c11 safe string handling support
[vpp.git] / src / vnet / flow / flow_cli.c
1 /*
2  * Copyright (c) 2018 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 <vnet/vnet.h>
17 #include <vnet/devices/devices.h>
18 #include <vnet/ip/ip.h>
19 #include <vnet/ethernet/ethernet.h>
20 #include <vnet/flow/flow.h>
21
22 static format_function_t format_flow;
23
24 uword
25 unformat_ip_port_and_mask (unformat_input_t * input, va_list * args)
26 {
27   ip_port_and_mask_t *pm = va_arg (*args, ip_port_and_mask_t *);
28   u32 port = 0, mask = 0;
29
30   if (unformat (input, "any"))
31     ;
32   else if (unformat (input, "%u/%u", &port, &mask))
33     ;
34   else if (unformat (input, "%u/0x%x", &port, &mask))
35     ;
36   else if (unformat (input, "%u", &port))
37     mask = 0xffff;
38   else
39     return 0;
40
41   if (port > 0xffff || mask > 0xffff)
42     return 0;
43
44   pm->port = port;
45   pm->mask = mask;
46   return 1;
47 }
48
49 u8 *
50 format_ip_port_and_mask (u8 * s, va_list * args)
51 {
52   ip_port_and_mask_t *pm = va_arg (*args, ip_port_and_mask_t *);
53
54   if (pm->port == 0 && pm->mask == 0)
55     return format (s, "any");
56
57   if (pm->mask == 0xffff)
58     return format (s, "%u", pm->port);
59
60   return format (s, "%u/0x%x", pm->port, pm->mask);
61 }
62
63 u8 *
64 format_flow_error (u8 * s, va_list * args)
65 {
66   int error = va_arg (*args, int);
67
68   if (error == 0)
69     return format (s, "no error");
70
71 #define _(v,n,str) if (error == v) return format (s, #str);
72   foreach_flow_error;
73 #undef _
74
75   return format (s, "unknown error (%d)", error);
76 }
77
78 u8 *
79 format_flow_actions (u8 * s, va_list * args)
80 {
81   u32 actions = va_arg (*args, u32);
82   u8 *t = 0;
83
84 #define _(a, b, c) if (actions & (1 << a)) \
85   t = format (t, "%s%s", t ? " ":"", c);
86   foreach_flow_action
87 #undef _
88     s = format (s, "%v", t);
89   vec_free (t);
90   return s;
91 }
92
93 u8 *
94 format_flow_enabled_hw (u8 * s, va_list * args)
95 {
96   u32 flow_index = va_arg (*args, u32);
97   vnet_flow_t *f = vnet_get_flow (flow_index);
98   if (f == 0)
99     return format (s, "not found");
100
101   u8 *t = 0;
102   u32 hw_if_index;
103   uword private_data;
104   vnet_main_t *vnm = vnet_get_main ();
105   /* *INDENT-OFF* */
106   hash_foreach (hw_if_index, private_data, f->private_data,
107     ({
108      t = format (t, "%s%U", t ? ", " : "",
109                  format_vnet_hw_if_index_name, vnm, hw_if_index);
110      }));
111   /* *INDENT-ON* */
112   s = format (s, "%v", t);
113   vec_free (t);
114   return s;
115 }
116
117 static const char *flow_type_strings[] = { 0,
118 #define _(a,b,c) c,
119   foreach_flow_type
120 #undef _
121 };
122
123 static clib_error_t *
124 show_flow_entry (vlib_main_t * vm, unformat_input_t * input,
125                  vlib_cli_command_t * cmd_arg)
126 {
127   vnet_main_t *vnm = vnet_get_main ();
128   vnet_flow_main_t *fm = &flow_main;
129   unformat_input_t _line_input, *line_input = &_line_input;
130   vnet_hw_interface_t *hi;
131   vnet_device_class_t *dev_class;
132   vnet_flow_t *f;
133   uword private_data;
134   u32 index = ~0, hw_if_index;
135
136   if (!unformat_user (input, unformat_line_input, line_input))
137     goto no_args;
138
139   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
140     {
141       if (unformat (line_input, "index %u", &index))
142         ;
143       else
144         return clib_error_return (0, "parse error: '%U'",
145                                   format_unformat_error, line_input);
146     }
147
148   unformat_free (line_input);
149
150   if (index != ~0)
151     {
152       if ((f = vnet_get_flow (index)) == 0)
153         return clib_error_return (0, "no such flow");
154
155       vlib_cli_output (vm, "%-10s: %u", "index", f->index);
156       vlib_cli_output (vm, "%-10s: %s", "type", flow_type_strings[f->type]);
157       vlib_cli_output (vm, "%-10s: %U", "match", format_flow, f);
158       /* *INDENT-OFF* */
159       hash_foreach (hw_if_index, private_data, f->private_data,
160         ({
161          hi = vnet_get_hw_interface (vnm, hw_if_index);
162           dev_class = vnet_get_device_class (vnm, hi->dev_class_index);
163           vlib_cli_output (vm,  "interface %U\n",
164                            format_vnet_hw_if_index_name, vnm, hw_if_index);
165           if (dev_class->format_flow)
166             vlib_cli_output (vm,  "  %U\n", dev_class->format_flow,
167                              hi->dev_instance, f->index, private_data);
168          }));
169       /* *INDENT-ON* */
170       return 0;
171     }
172
173 no_args:
174   /* *INDENT-OFF* */
175   pool_foreach (f, fm->global_flow_pool,
176     {
177       vlib_cli_output (vm, "%U\n", format_flow, f);
178     });
179   /* *INDENT-ON* */
180
181   return 0;
182 }
183
184 /* *INDENT-OFF* */
185 VLIB_CLI_COMMAND (show_flow_entry_command, static) = {
186     .path = "show flow entry",
187     .short_help = "show flow entry [index <index>]",
188     .function = show_flow_entry,
189 };
190 /* *INDENT-ON* */
191
192 static clib_error_t *
193 show_flow_ranges (vlib_main_t * vm, unformat_input_t * input,
194                   vlib_cli_command_t * cmd_arg)
195 {
196   vnet_flow_main_t *fm = &flow_main;
197   vnet_flow_range_t *r = 0;
198
199   vlib_cli_output (vm, "%8s  %8s  %s", "Start", "Count", "Owner");
200
201   /* *INDENT-OFF* */
202   vec_foreach (r, fm->ranges)
203     {
204       vlib_cli_output (vm, "%8u  %8u  %s", r->start, r->count, r->owner);
205     };
206   /* *INDENT-ON* */
207   return 0;
208 }
209
210 /* *INDENT-OFF* */
211 VLIB_CLI_COMMAND (show_flow_ranges_command, static) = {
212     .path = "show flow ranges",
213     .short_help = "show flow ranges",
214     .function = show_flow_ranges,
215 };
216 /* *INDENT-ON* */
217
218 static clib_error_t *
219 show_flow_interface (vlib_main_t * vm, unformat_input_t * input,
220                      vlib_cli_command_t * cmd_arg)
221 {
222   vnet_main_t *vnm = vnet_get_main ();
223   vnet_hw_interface_t *hi;
224   vnet_device_class_t *dev_class;
225   unformat_input_t _line_input, *line_input = &_line_input;
226   u32 hw_if_index = ~0;
227
228   if (unformat_user (input, unformat_line_input, line_input))
229     {
230       while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
231         {
232           if (unformat (line_input, "%U",
233                         unformat_vnet_hw_interface, vnm, &hw_if_index))
234             ;
235           else
236             return clib_error_return (0, "parse error: '%U'",
237                                       format_unformat_error, line_input);
238         }
239       unformat_free (line_input);
240     }
241
242   if (hw_if_index == ~0)
243     return clib_error_return (0, "please specify interface");
244
245   hi = vnet_get_hw_interface (vnm, hw_if_index);
246   dev_class = vnet_get_device_class (vnm, hi->dev_class_index);
247   if (dev_class->format_flow == 0)
248     return clib_error_return (0, "not supported");
249
250   vlib_cli_output (vm, "%U", dev_class->format_flow, hi->dev_instance, ~0, 0);
251   return 0;
252 }
253
254 /* *INDENT-OFF* */
255 VLIB_CLI_COMMAND (show_flow_interface_command, static) = {
256     .path = "show flow interface",
257     .short_help = "show flow interface <interface name>",
258     .function = show_flow_interface,
259 };
260 /* *INDENT-ON* */
261
262 static clib_error_t *
263 test_flow (vlib_main_t * vm, unformat_input_t * input,
264            vlib_cli_command_t * cmd_arg)
265 {
266   vnet_flow_t flow;
267   vnet_main_t *vnm = vnet_get_main ();
268   unformat_input_t _line_input, *line_input = &_line_input;
269   enum
270   {
271     FLOW_UNKNOWN_ACTION,
272     FLOW_ADD,
273     FLOW_DEL,
274     FLOW_ENABLE,
275     FLOW_DISABLE
276   } action = FLOW_UNKNOWN_ACTION;
277   u32 hw_if_index = ~0, tmp, flow_index = ~0;
278   int rv;
279   u8 prot;
280
281   clib_memset (&flow, 0, sizeof (vnet_flow_t));
282   flow.index = ~0;
283   flow.actions = 0;
284   flow.ip4_n_tuple.protocol = ~0;
285   if (!unformat_user (input, unformat_line_input, line_input))
286     return 0;
287
288   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
289     {
290       if (unformat (line_input, "add"))
291         action = FLOW_ADD;
292       else if (unformat (line_input, "del"))
293         action = FLOW_DEL;
294       else if (unformat (line_input, "enable"))
295         action = FLOW_ENABLE;
296       else if (unformat (line_input, "disable"))
297         action = FLOW_DISABLE;
298       else if (unformat (line_input, "src-ip %U",
299                          unformat_ip4_address_and_mask,
300                          &flow.ip4_n_tuple.src_addr))
301         ;
302       else if (unformat (line_input, "dst-ip %U",
303                          unformat_ip4_address_and_mask,
304                          &flow.ip4_n_tuple.dst_addr))
305         ;
306       else if (unformat (line_input, "src-port %U", unformat_ip_port_and_mask,
307                          &flow.ip4_n_tuple.src_port))
308         ;
309       else if (unformat (line_input, "dst-port %U", unformat_ip_port_and_mask,
310                          &flow.ip4_n_tuple.dst_port))
311         ;
312       else if (unformat (line_input, "proto %U", unformat_ip_protocol, &prot))
313         flow.ip4_n_tuple.protocol = prot;
314       else if (unformat (line_input, "proto %u", &tmp))
315         flow.ip4_n_tuple.protocol = tmp;
316       else if (unformat (line_input, "index %u", &flow_index))
317         ;
318       else if (unformat (line_input, "next-node %U", unformat_vlib_node, vm,
319                          &flow.redirect_node_index))
320         flow.actions |= VNET_FLOW_ACTION_REDIRECT_TO_NODE;
321       else if (unformat (line_input, "mark %d", &flow.mark_flow_id))
322         flow.actions |= VNET_FLOW_ACTION_MARK;
323       else if (unformat (line_input, "buffer-advance %d",
324                          &flow.buffer_advance))
325         flow.actions |= VNET_FLOW_ACTION_BUFFER_ADVANCE;
326       else if (unformat (line_input, "%U", unformat_vnet_hw_interface, vnm,
327                          &hw_if_index))
328         ;
329       else
330         return clib_error_return (0, "parse error: '%U'",
331                                   format_unformat_error, line_input);
332     }
333
334   unformat_free (line_input);
335
336   if (hw_if_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE))
337     return clib_error_return (0, "Please specify interface name");
338
339   if (flow_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE ||
340                            action == FLOW_DEL))
341     return clib_error_return (0, "Please specify flow index");
342
343   switch (action)
344     {
345     case FLOW_ADD:
346       if (flow.ip4_n_tuple.protocol == (ip_protocol_t) ~ 0)
347         return clib_error_return (0, "Please specify ip protocol");
348
349       if (flow.actions == 0)
350         return clib_error_return (0, "Please specify at least one action");
351       flow.type = VNET_FLOW_TYPE_IP4_N_TUPLE;
352       rv = vnet_flow_add (vnm, &flow, &flow_index);
353       break;
354     case FLOW_DEL:
355       rv = vnet_flow_del (vnm, flow_index);
356       break;
357     case FLOW_ENABLE:
358       rv = vnet_flow_enable (vnm, flow_index, hw_if_index);
359       break;
360     case FLOW_DISABLE:
361       rv = vnet_flow_disable (vnm, flow_index, hw_if_index);
362       break;
363     default:
364       return clib_error_return (0, "please specify action (add, del, enable,"
365                                 " disable)");
366     }
367
368   if (rv < 0)
369     return clib_error_return (0, "flow error: %U", format_flow_error, rv);
370   return 0;
371 }
372
373 /* *INDENT-OFF* */
374 VLIB_CLI_COMMAND (test_flow_command, static) = {
375     .path = "test flow",
376     .short_help = "test flow add [src-ip <ip-addr/mask>] [dst-ip "
377       "<ip-addr/mask>] [src-port <port/mask>] [dst-port <port/mask>] "
378       "[proto <ip-proto>",
379     .function = test_flow,
380 };
381 /* *INDENT-ON* */
382
383
384 static u8 *
385 format_flow_match_element (u8 * s, va_list * args)
386 {
387   char *type = va_arg (*args, char *);
388   void *ptr = va_arg (*args, void *);
389
390   if (strncmp (type, "u8", 2) == 0)
391     return format (s, "%d", *(u8 *) ptr);
392
393   if (strncmp (type, "u16", 3) == 0)
394     return format (s, "%d", *(u16 *) ptr);
395
396   if (strncmp (type, "u32", 3) == 0)
397     return format (s, "%d", *(u32 *) ptr);
398
399   if (strncmp (type, "ip4_address_t", 13) == 0)
400     return format (s, "%U", format_ip4_address, ptr);
401
402   if (strncmp (type, "ip4_address_and_mask_t", 13) == 0)
403     return format (s, "%U", format_ip4_address_and_mask, ptr);
404
405   if (strncmp (type, "ip6_address_t", 13) == 0)
406     return format (s, "%U", format_ip6_address, ptr);
407
408   if (strncmp (type, "ip6_address_and_mask_t", 13) == 0)
409     return format (s, "%U", format_ip6_address_and_mask, ptr);
410
411   if (strncmp (type, "ip_protocol_t", 13) == 0)
412     return format (s, "%U", format_ip_protocol, *(ip_protocol_t *) ptr);
413
414   if (strncmp (type, "ip_port_and_mask_t", 18) == 0)
415     return format (s, "%U", format_ip_port_and_mask, ptr);
416
417   s = format (s, "unknown type '%s'", type);
418   return s;
419 }
420
421 #define _fe(a,b) s2 = format (s2, "%s%s %U", s2 ? ", ":"", #b, \
422                               format_flow_match_element, #a, &f->b);
423 #define _(a,b,c) \
424 u8 * format_flow_match_##b (u8 * s, va_list * args)                     \
425 {                                                                       \
426   vnet_flow_##b##_t *f = __builtin_va_arg (*args, vnet_flow_##b##_t *); \
427   u8 *s2 = 0; \
428 foreach_flow_entry_##b \
429   s = format (s, "%v", s2);; \
430   vec_free (s2); \
431 return s; \
432 }
433 foreach_flow_type
434 #undef _
435 #undef _fe
436 static u8 *
437 format_flow_match (u8 * s, va_list * args)
438 {
439   vnet_flow_t *f = va_arg (*args, vnet_flow_t *);
440
441 #define _(a,b,c) \
442   if (f->type == VNET_FLOW_TYPE_##a) \
443     return format (s, "%U", format_flow_match_##b, &f->b);
444   foreach_flow_type;
445 #undef _
446
447   return s;
448 }
449
450 static u8 *
451 format_flow (u8 * s, va_list * args)
452 {
453   vlib_main_t *vm = vlib_get_main ();
454   vnet_flow_t *f = va_arg (*args, vnet_flow_t *);
455   u32 indent = format_get_indent (s);
456   u8 *t = 0;
457
458   s = format (s, "flow-index %u type %s active %u",
459               f->index, flow_type_strings[f->type],
460               hash_elts (f->private_data)),
461     s = format (s, "\n%Umatch: %U", format_white_space, indent + 2,
462                 format_flow_match, f);
463   s = format (s, "\n%Uaction: %U", format_white_space, indent + 2,
464               format_flow_actions, f->actions);
465
466   if (f->actions & VNET_FLOW_ACTION_MARK)
467     t = format (t, "%smark %u", t ? ", " : "", f->mark_flow_id);
468
469   if (f->actions & VNET_FLOW_ACTION_REDIRECT_TO_NODE)
470     t = format (t, "%snext-node %U", t ? ", " : "",
471                 format_vlib_node_name, vm, f->redirect_node_index);
472
473   if (f->actions & VNET_FLOW_ACTION_BUFFER_ADVANCE)
474     t = format (t, "%sbuffer-advance %d", t ? ", " : "", f->buffer_advance);
475
476   if (t)
477     {
478       s = format (s, "\n%U%v", format_white_space, indent + 4, t);
479       vec_free (t);
480     }
481
482   return s;
483 }
484
485 /*
486  * fd.io coding-style-patch-verification: ON
487  *
488  * Local Variables:
489  * eval: (c-set-style "gnu")
490  * End:
491  */