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