nat: pnat copy and clear byte instructions
[vpp.git] / src / plugins / nat / pnat / pnat_cli.c
1 /*
2  * Copyright (c) 2021 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <stdbool.h>
17 #include <vlib/vlib.h>
18 #include <vnet/feature/feature.h>
19 #include <vnet/ip/ip.h>
20 #include <vnet/ip/ip4.h>
21 #include <vnet/ip/ip4_packet.h>
22 #include <vppinfra/clib_error.h>
23 #include <vppinfra/pool.h>
24 #include "pnat.h"
25
26 /*
27  * This file contains the handlers for the (unsupported) VPP debug CLI.
28  */
29 u8 *format_pnat_match_tuple(u8 *s, va_list *args) {
30     pnat_match_tuple_t *t = va_arg(*args, pnat_match_tuple_t *);
31     s = format(s, "{");
32     if (t->mask & PNAT_SA)
33         s = format(s, "%U", format_ip4_address, &t->src);
34     else
35         s = format(s, "*");
36     if (t->mask & PNAT_SPORT)
37         s = format(s, ":%u,", t->sport);
38     else
39         s = format(s, ":*,");
40     if (t->proto > 0)
41         s = format(s, "%U,", format_ip_protocol, t->proto);
42     else
43         s = format(s, "*,");
44     if (t->mask & PNAT_DA)
45         s = format(s, "%U", format_ip4_address, &t->dst);
46     else
47         s = format(s, "*");
48     if (t->mask & PNAT_DPORT)
49         s = format(s, ":%u", t->dport);
50     else
51         s = format(s, ":*");
52     s = format(s, "}");
53     return s;
54 }
55 u8 *format_pnat_rewrite_tuple(u8 *s, va_list *args) {
56     pnat_rewrite_tuple_t *t = va_arg(*args, pnat_rewrite_tuple_t *);
57     s = format(s, "{");
58     if (t->mask & PNAT_SA)
59         s = format(s, "%U", format_ip4_address, &t->src);
60     else
61         s = format(s, "*");
62     if (t->mask & PNAT_SPORT)
63         s = format(s, ":%u,", t->sport);
64     else
65         s = format(s, ":*,");
66     if (t->mask & PNAT_DA)
67         s = format(s, "%U", format_ip4_address, &t->dst);
68     else
69         s = format(s, "*");
70     if (t->mask & PNAT_DPORT)
71         s = format(s, ":%u", t->dport);
72     else
73         s = format(s, ":*");
74     if (t->mask & PNAT_COPY_BYTE)
75         s = format(s, " copy byte@[%d->%d]", t->from_offset, t->to_offset);
76     if (t->mask & PNAT_CLEAR_BYTE)
77         s = format(s, " clear byte@[%d]", t->clear_offset);
78     s = format(s, "}");
79     return s;
80 }
81
82 u8 *format_pnat_translation(u8 *s, va_list *args) {
83     u32 index = va_arg(*args, u32);
84     pnat_translation_t *t = va_arg(*args, pnat_translation_t *);
85     s = format(s, "[%d] match: %U rewrite: %U", index, format_pnat_match_tuple,
86                &t->match, format_pnat_rewrite_tuple, &t->rewrite);
87     return s;
88 }
89
90 static u8 *format_pnat_mask(u8 *s, va_list *args) {
91     pnat_mask_t t = va_arg(*args, pnat_mask_t);
92     if (t & PNAT_SA)
93         s = format(s, "SA ");
94     if (t & PNAT_SPORT)
95         s = format(s, "SP ");
96     if (t & PNAT_DA)
97         s = format(s, "DA ");
98     if (t & PNAT_DPORT)
99         s = format(s, "DP");
100     return s;
101 }
102
103 static u8 *format_pnat_interface(u8 *s, va_list *args) {
104     pnat_interface_t *interface = va_arg(*args, pnat_interface_t *);
105     s = format(s, "sw_if_index: %d", interface->sw_if_index);
106     if (interface->enabled[PNAT_IP4_INPUT]) {
107         s = format(s, " input mask: %U", format_pnat_mask,
108                    interface->lookup_mask[PNAT_IP4_INPUT]);
109     }
110     if (interface->enabled[PNAT_IP4_OUTPUT]) {
111         s = format(s, " output mask: %U", format_pnat_mask,
112                    interface->lookup_mask[PNAT_IP4_OUTPUT]);
113     }
114     return s;
115 }
116
117 uword unformat_pnat_match_tuple(unformat_input_t *input, va_list *args) {
118     pnat_match_tuple_t *t = va_arg(*args, pnat_match_tuple_t *);
119     u32 dport, sport;
120     while (1) {
121         if (unformat(input, "src %U", unformat_ip4_address, &t->src))
122             t->mask |= PNAT_SA;
123         else if (unformat(input, "dst %U", unformat_ip4_address, &t->dst))
124             t->mask |= PNAT_DA;
125         else if (unformat(input, "sport %d", &sport)) {
126             if (sport == 0 || sport > 65535)
127                 return 0;
128             t->mask |= PNAT_SPORT;
129             t->sport = sport;
130         } else if (unformat(input, "dport %d", &dport)) {
131             if (dport == 0 || dport > 65535)
132                 return 0;
133             t->mask |= PNAT_DPORT;
134             t->dport = dport;
135         } else if (unformat(input, "proto %U", unformat_ip_protocol, &t->proto))
136             ;
137         else
138             break;
139     }
140     return 1;
141 }
142
143 uword unformat_pnat_rewrite_tuple(unformat_input_t *input, va_list *args) {
144     pnat_rewrite_tuple_t *t = va_arg(*args, pnat_rewrite_tuple_t *);
145     u32 dport, sport;
146     u32 to_offset, from_offset, clear_offset;
147
148     while (1) {
149         if (unformat(input, "src %U", unformat_ip4_address, &t->src))
150             t->mask |= PNAT_SA;
151         else if (unformat(input, "dst %U", unformat_ip4_address, &t->dst))
152             t->mask |= PNAT_DA;
153         else if (unformat(input, "sport %d", &sport)) {
154             if (sport == 0 || sport > 65535)
155                 return 0;
156             t->mask |= PNAT_SPORT;
157             t->sport = sport;
158         } else if (unformat(input, "dport %d", &dport)) {
159             if (dport == 0 || dport > 65535)
160                 return 0;
161             t->mask |= PNAT_DPORT;
162             t->dport = dport;
163         } else if (unformat(input, "copy-byte-at-offset %d %d", &from_offset,
164                             &to_offset)) {
165             if (from_offset == to_offset || to_offset > 255 ||
166                 from_offset > 255)
167                 return 0;
168             t->mask |= PNAT_COPY_BYTE;
169             t->from_offset = from_offset;
170             t->to_offset = to_offset;
171         } else if (unformat(input, "clear-byte-at-offset %d", &clear_offset)) {
172             if (clear_offset > 255)
173                 return 0;
174             t->mask |= PNAT_CLEAR_BYTE;
175             t->clear_offset = clear_offset;
176         } else
177             break;
178     }
179     return 1;
180 }
181
182 static clib_error_t *set_pnat_translation_command_fn(vlib_main_t *vm,
183                                                      unformat_input_t *input,
184                                                      vlib_cli_command_t *cmd) {
185     unformat_input_t _line_input, *line_input = &_line_input;
186     clib_error_t *error = 0;
187     bool in = false, out = false;
188     bool match_set = false, rewrite_set = false;
189     bool add = true;
190     u32 sw_if_index = ~0;
191     pnat_match_tuple_t match = {0};
192     pnat_rewrite_tuple_t rewrite = {0};
193
194     /* Get a line of input. */
195     if (!unformat_user(input, unformat_line_input, line_input))
196         return 0;
197
198     while (unformat_check_input(line_input) != UNFORMAT_END_OF_INPUT) {
199         if (unformat(line_input, "match %U", unformat_pnat_match_tuple, &match))
200             match_set = true;
201         else if (unformat(line_input, "rewrite %U", unformat_pnat_rewrite_tuple,
202                           &rewrite))
203             rewrite_set = true;
204         else if (unformat(line_input, "interface %U",
205                           unformat_vnet_sw_interface, vnet_get_main(),
206                           &sw_if_index))
207             ;
208         else if (unformat(line_input, "in")) {
209             in = true;
210         } else if (unformat(line_input, "out")) {
211             out = true;
212         } else if (unformat(line_input, "del")) {
213             add = false;
214         } else {
215             error = clib_error_return(0, "unknown input `%U'",
216                                       format_unformat_error, line_input);
217             goto done;
218         }
219     }
220     if (sw_if_index == ~0) {
221         error = clib_error_return(0, "interface is required `%U'",
222                                   format_unformat_error, line_input);
223         goto done;
224     }
225     if ((in && out) || (!in && !out)) {
226         error = clib_error_return(0, "in or out is required `%U'",
227                                   format_unformat_error, line_input);
228         goto done;
229     }
230     if (!match_set) {
231         error = clib_error_return(0, "missing parameter: match `%U'",
232                                   format_unformat_error, line_input);
233         goto done;
234     }
235     if (!rewrite_set) {
236         error = clib_error_return(0, "missing parameter: rewrite `%U'",
237                                   format_unformat_error, line_input);
238         goto done;
239     }
240
241     if ((match.dport || match.sport) &&
242         (match.proto != 17 && match.proto != 6)) {
243         error = clib_error_return(0, "missing protocol (TCP|UDP): match `%U'",
244                                   format_unformat_error, line_input);
245         goto done;
246     }
247     pnat_attachment_point_t attachment = in ? PNAT_IP4_INPUT : PNAT_IP4_OUTPUT;
248
249     if (add) {
250         u32 binding_index;
251         int rv = pnat_binding_add(&match, &rewrite, &binding_index);
252         if (rv) {
253             error = clib_error_return(0, "Adding binding failed %d", rv);
254             goto done;
255         }
256         rv = pnat_binding_attach(sw_if_index, attachment, binding_index);
257         if (rv) {
258             pnat_binding_del(binding_index);
259             error = clib_error_return(
260                 0, "Attaching binding to interface failed %d", rv);
261             goto done;
262         }
263     } else {
264         /* Lookup binding and lookup interface if both exists proceed with
265          * delete */
266         u32 binding_index = pnat_flow_lookup(sw_if_index, attachment, &match);
267         if (binding_index == ~0) {
268             error = clib_error_return(0, "Binding does not exist");
269             goto done;
270         }
271         pnat_attachment_point_t attachment =
272             in ? PNAT_IP4_INPUT : PNAT_IP4_OUTPUT;
273         int rv = pnat_binding_detach(sw_if_index, attachment, binding_index);
274         if (rv) {
275             error = clib_error_return(0, "Detaching binding failed %d %d",
276                                       binding_index, rv);
277             goto done;
278         }
279         rv = pnat_binding_del(binding_index);
280         if (rv) {
281             error = clib_error_return(0, "Deleting translation failed %d %d",
282                                       binding_index, rv);
283             goto done;
284         }
285     }
286
287 done:
288     unformat_free(line_input);
289
290     return error;
291 }
292
293 VLIB_CLI_COMMAND(set_pnat_translation_command, static) = {
294     .path = "set pnat translation",
295     .short_help = "set pnat translation interface <name> match <5-tuple> "
296                   "rewrite <tuple> {in|out} [del]",
297     .function = set_pnat_translation_command_fn,
298 };
299
300 static clib_error_t *
301 show_pnat_translations_command_fn(vlib_main_t *vm, unformat_input_t *input,
302                                   vlib_cli_command_t *cmd) {
303     pnat_main_t *pm = &pnat_main;
304     pnat_translation_t *s;
305     clib_error_t *error = 0;
306
307     /* Get a line of input. */
308     pool_foreach(s, pm->translations) {
309         vlib_cli_output(vm, "%U", format_pnat_translation, s - pm->translations,
310                         s);
311     }
312     return error;
313 }
314
315 VLIB_CLI_COMMAND(show_pnat_translations_command, static) = {
316     .path = "show pnat translations",
317     .short_help = "show pnat translations",
318     .function = show_pnat_translations_command_fn,
319 };
320
321 static clib_error_t *show_pnat_interfaces_command_fn(vlib_main_t *vm,
322                                                      unformat_input_t *input,
323                                                      vlib_cli_command_t *cmd) {
324     pnat_main_t *pm = &pnat_main;
325     pnat_interface_t *interface;
326     clib_error_t *error = 0;
327
328     /* Get a line of input. */
329     pool_foreach(interface, pm->interfaces) {
330         vlib_cli_output(vm, "%U", format_pnat_interface, interface);
331     }
332     return error;
333 }
334
335 VLIB_CLI_COMMAND(show_pnat_interfaces_command, static) = {
336     .path = "show pnat interfaces",
337     .short_help = "show pnat interfaces",
338     .function = show_pnat_interfaces_command_fn,
339 };