docs: Use newer Ubuntu LTS in tutorial
[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 #include <stddef.h>
16
17 #include <vnet/vnet.h>
18 #include <vnet/devices/devices.h>
19 #include <vnet/ip/ip.h>
20 #include <vnet/ethernet/ethernet.h>
21 #include <vnet/ethernet/packet.h>
22 #include <vnet/flow/flow.h>
23
24 static format_function_t format_flow;
25
26 uword
27 unformat_ip_port_and_mask (unformat_input_t * input, va_list * args)
28 {
29   ip_port_and_mask_t *pm = va_arg (*args, ip_port_and_mask_t *);
30   u32 port = 0, mask = 0;
31
32   if (unformat (input, "any"))
33     ;
34   else if (unformat (input, "%u/%u", &port, &mask))
35     ;
36   else if (unformat (input, "%u/0x%x", &port, &mask))
37     ;
38   else if (unformat (input, "%u", &port))
39     mask = 0xffff;
40   else
41     return 0;
42
43   if (port > 0xffff || mask > 0xffff)
44     return 0;
45
46   pm->port = port;
47   pm->mask = mask;
48   return 1;
49 }
50
51 u8 *
52 format_ip_port_and_mask (u8 * s, va_list * args)
53 {
54   ip_port_and_mask_t *pm = va_arg (*args, ip_port_and_mask_t *);
55
56   if (pm->port == 0 && pm->mask == 0)
57     return format (s, "any");
58
59   if (pm->mask == 0xffff)
60     return format (s, "%u", pm->port);
61
62   return format (s, "%u/0x%x", pm->port, pm->mask);
63 }
64
65 uword
66 unformat_ip_protocol_and_mask (unformat_input_t * input, va_list * args)
67 {
68   ip_prot_and_mask_t *pm = va_arg (*args, ip_prot_and_mask_t *);
69   u32 prot = 0, mask = 0;
70
71   if (unformat (input, "any"))
72     ;
73   else if (unformat (input, "%U", unformat_ip_protocol, &prot))
74     mask = 0xFF;
75   else if (unformat (input, "%u", &prot))
76     mask = 0xFF;
77   else
78     return 0;
79
80   if (prot > 0XFF || mask > 0xFF)
81     return 0;
82
83   pm->prot = prot;
84   pm->mask = mask;
85   return 1;
86 }
87
88 u8 *
89 format_ip_protocol_and_mask (u8 * s, va_list * args)
90 {
91   ip_prot_and_mask_t *pm = va_arg (*args, ip_prot_and_mask_t *);
92
93   if (pm->prot == 0 && pm->mask == 0)
94     return format (s, "any");
95
96   return format (s, "%U", format_ip_protocol, pm->prot);
97 }
98
99 u8 *
100 format_flow_error (u8 * s, va_list * args)
101 {
102   int error = va_arg (*args, int);
103
104   if (error == 0)
105     return format (s, "no error");
106
107 #define _(v,n,str) if (error == v) return format (s, #str);
108   foreach_flow_error;
109 #undef _
110
111   return format (s, "unknown error (%d)", error);
112 }
113
114 u8 *
115 format_flow_actions (u8 * s, va_list * args)
116 {
117   u32 actions = va_arg (*args, u32);
118   u8 *t = 0;
119
120 #define _(a, b, c) if (actions & (1 << a)) \
121   t = format (t, "%s%s", t ? " ":"", c);
122   foreach_flow_action
123 #undef _
124     s = format (s, "%v", t);
125   vec_free (t);
126   return s;
127 }
128
129 u8 *
130 format_flow_enabled_hw (u8 * s, va_list * args)
131 {
132   u32 flow_index = va_arg (*args, u32);
133   vnet_flow_t *f = vnet_get_flow (flow_index);
134   if (f == 0)
135     return format (s, "not found");
136
137   u8 *t = 0;
138   u32 hw_if_index;
139   uword private_data;
140   vnet_main_t *vnm = vnet_get_main ();
141   hash_foreach (hw_if_index, private_data, f->private_data,
142     ({
143      t = format (t, "%s%U", t ? ", " : "",
144                  format_vnet_hw_if_index_name, vnm, hw_if_index);
145      }));
146   s = format (s, "%v", t);
147   vec_free (t);
148   return s;
149 }
150
151 u8 *
152 format_rss_function (u8 * s, va_list * args)
153 {
154   vnet_rss_function_t func = va_arg (*args, vnet_rss_function_t);
155
156   if (0)
157     ;
158 #undef _
159 #define _(f, n) \
160       else if (func == VNET_RSS_FUNC_##f) \
161         return format (s, n);
162
163   foreach_rss_function
164 #undef _
165     return format (s, "unknown");
166 }
167
168 u8 *
169 format_rss_types (u8 * s, va_list * args)
170 {
171   u64 type = va_arg (*args, u64);
172
173 #undef _
174 #define _(a,b,c)     \
175   if (type & (1UL<<a)) \
176     s = format (s, "%s ", c);
177
178   foreach_flow_rss_types
179 #undef _
180     return s;
181 }
182
183 static const char *flow_type_strings[] = { 0,
184 #define _(a,b,c) c,
185   foreach_flow_type
186 #undef _
187 };
188
189 static clib_error_t *
190 show_flow_entry (vlib_main_t * vm, unformat_input_t * input,
191                  vlib_cli_command_t * cmd_arg)
192 {
193   vnet_main_t *vnm = vnet_get_main ();
194   vnet_flow_main_t *fm = &flow_main;
195   unformat_input_t _line_input, *line_input = &_line_input;
196   vnet_hw_interface_t *hi;
197   vnet_device_class_t *dev_class;
198   vnet_flow_t *f;
199   uword private_data;
200   u32 index = ~0, hw_if_index;
201
202   if (!unformat_user (input, unformat_line_input, line_input))
203     goto no_args;
204
205   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
206     {
207       if (unformat (line_input, "index %u", &index))
208         ;
209       else
210         return clib_error_return (0, "parse error: '%U'",
211                                   format_unformat_error, line_input);
212     }
213
214   unformat_free (line_input);
215
216   if (index != ~0)
217     {
218       if ((f = vnet_get_flow (index)) == 0)
219         return clib_error_return (0, "no such flow");
220
221       vlib_cli_output (vm, "%-10s: %u", "index", f->index);
222       vlib_cli_output (vm, "%-10s: %s", "type", flow_type_strings[f->type]);
223       vlib_cli_output (vm, "%-10s: %U", "match", format_flow, f);
224       if (f->type == VNET_FLOW_TYPE_GENERIC)
225         {
226           vlib_cli_output (vm, "%s: %s", "spec", f->generic.pattern.spec);
227           vlib_cli_output (vm, "%s: %s", "mask", f->generic.pattern.mask);
228         }
229       hash_foreach (hw_if_index, private_data, f->private_data,
230         ({
231          hi = vnet_get_hw_interface (vnm, hw_if_index);
232           dev_class = vnet_get_device_class (vnm, hi->dev_class_index);
233           vlib_cli_output (vm,  "interface %U\n",
234                            format_vnet_hw_if_index_name, vnm, hw_if_index);
235           if (dev_class->format_flow)
236             vlib_cli_output (vm,  "  %U\n", dev_class->format_flow,
237                              hi->dev_instance, f->index, private_data);
238          }));
239       return 0;
240     }
241
242 no_args:
243   pool_foreach (f, fm->global_flow_pool)
244     {
245       vlib_cli_output (vm, "%U\n", format_flow, f);
246       if (f->type == VNET_FLOW_TYPE_GENERIC)
247         {
248           vlib_cli_output (vm, "%s: %s", "spec", f->generic.pattern.spec);
249           vlib_cli_output (vm, "%s: %s", "mask", f->generic.pattern.mask);
250         }
251     }
252
253   return 0;
254 }
255
256 VLIB_CLI_COMMAND (show_flow_entry_command, static) = {
257     .path = "show flow entry",
258     .short_help = "show flow entry [index <index>]",
259     .function = show_flow_entry,
260 };
261
262 static clib_error_t *
263 show_flow_ranges (vlib_main_t * vm, unformat_input_t * input,
264                   vlib_cli_command_t * cmd_arg)
265 {
266   vnet_flow_main_t *fm = &flow_main;
267   vnet_flow_range_t *r = 0;
268
269   vlib_cli_output (vm, "%8s  %8s  %s", "Start", "Count", "Owner");
270
271   vec_foreach (r, fm->ranges)
272     {
273       vlib_cli_output (vm, "%8u  %8u  %s", r->start, r->count, r->owner);
274     };
275   return 0;
276 }
277
278 VLIB_CLI_COMMAND (show_flow_ranges_command, static) = {
279     .path = "show flow ranges",
280     .short_help = "show flow ranges",
281     .function = show_flow_ranges,
282 };
283
284 static clib_error_t *
285 show_flow_interface (vlib_main_t * vm, unformat_input_t * input,
286                      vlib_cli_command_t * cmd_arg)
287 {
288   vnet_main_t *vnm = vnet_get_main ();
289   vnet_hw_interface_t *hi;
290   vnet_device_class_t *dev_class;
291   unformat_input_t _line_input, *line_input = &_line_input;
292   u32 hw_if_index = ~0;
293
294   if (unformat_user (input, unformat_line_input, line_input))
295     {
296       while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
297         {
298           if (unformat (line_input, "%U",
299                         unformat_vnet_hw_interface, vnm, &hw_if_index))
300             ;
301           else
302             return clib_error_return (0, "parse error: '%U'",
303                                       format_unformat_error, line_input);
304         }
305       unformat_free (line_input);
306     }
307
308   if (hw_if_index == ~0)
309     return clib_error_return (0, "please specify interface");
310
311   hi = vnet_get_hw_interface (vnm, hw_if_index);
312   dev_class = vnet_get_device_class (vnm, hi->dev_class_index);
313   if (dev_class->format_flow == 0)
314     return clib_error_return (0, "not supported");
315
316   vlib_cli_output (vm, "%U", dev_class->format_flow, hi->dev_instance, ~0, 0);
317   return 0;
318 }
319
320 VLIB_CLI_COMMAND (show_flow_interface_command, static) = {
321     .path = "show flow interface",
322     .short_help = "show flow interface <interface name>",
323     .function = show_flow_interface,
324 };
325
326 static clib_error_t *
327 test_flow (vlib_main_t * vm, unformat_input_t * input,
328            vlib_cli_command_t * cmd_arg)
329 {
330   vnet_flow_t flow;
331   vnet_main_t *vnm = vnet_get_main ();
332   unformat_input_t _line_input, *line_input = &_line_input;
333   enum
334   {
335     FLOW_UNKNOWN_ACTION,
336     FLOW_ADD,
337     FLOW_DEL,
338     FLOW_ENABLE,
339     FLOW_DISABLE
340   } action = FLOW_UNKNOWN_ACTION;
341   enum
342   {
343     FLOW_UNKNOWN_CLASS,
344     FLOW_ETHERNET_CLASS,
345     FLOW_IPV4_CLASS,
346     FLOW_IPV6_CLASS,
347   } flow_class = FLOW_UNKNOWN_CLASS;
348
349   u32 hw_if_index = ~0, flow_index = ~0;
350   int rv;
351   u32 teid = 0, session_id = 0, spi = 0;
352   u32 vni = 0;
353   u32 queue_start = 0, queue_end = 0;
354   vnet_flow_type_t type = VNET_FLOW_TYPE_UNKNOWN;
355   ip4_address_and_mask_t ip4s = {}, in_ip4s = {};
356   ip4_address_and_mask_t ip4d = {}, in_ip4d = {};
357   ip6_address_and_mask_t ip6s = {}, in_ip6s = {};
358   ip6_address_and_mask_t ip6d = {}, in_ip6d = {};
359   ip_port_and_mask_t sport = {}, in_sport = {};
360   ip_port_and_mask_t dport = {}, in_dport = {};
361   ip_prot_and_mask_t protocol = {}, in_proto = {};
362   u16 eth_type;
363   bool inner_ip4_set = false, inner_ip6_set = false;
364   bool tcp_udp_port_set = false, inner_port_set = false;
365   bool gtpc_set = false;
366   bool gtpu_set = false;
367   bool vni_set = false;
368   bool l2tpv3oip_set = false;
369   bool ipsec_esp_set = false, ipsec_ah_set = false;
370   u8 *rss_type[3] = { };
371   u8 *type_str = NULL;
372   u8 *spec = NULL;
373   u8 *mask = NULL;
374
375   clib_memset (&flow, 0, sizeof (vnet_flow_t));
376   flow.index = ~0;
377   flow.actions = 0;
378
379   if (!unformat_user (input, unformat_line_input, line_input))
380     return 0;
381
382   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
383     {
384       if (unformat (line_input, "add"))
385         action = FLOW_ADD;
386       else if (unformat (line_input, "del"))
387         action = FLOW_DEL;
388       else if (unformat (line_input, "enable"))
389         action = FLOW_ENABLE;
390       else if (unformat (line_input, "disable"))
391         action = FLOW_DISABLE;
392       else if (unformat (line_input, "spec %s", &spec))
393         ;
394       else if (unformat (line_input, "mask %s", &mask))
395         ;
396       else if (unformat (line_input, "eth-type %U",
397                          unformat_ethernet_type_host_byte_order, &eth_type))
398         flow_class = FLOW_ETHERNET_CLASS;
399       else if (unformat (line_input, "src-ip %U",
400                          unformat_ip4_address_and_mask, &ip4s))
401         flow_class = FLOW_IPV4_CLASS;
402       else if (unformat (line_input, "dst-ip %U",
403                          unformat_ip4_address_and_mask, &ip4d))
404         flow_class = FLOW_IPV4_CLASS;
405       else if (unformat (line_input, "in-src-ip %U",
406                          unformat_ip4_address_and_mask, &in_ip4s))
407         inner_ip4_set = true;
408       else if (unformat (line_input, "in-dst-ip %U",
409                          unformat_ip4_address_and_mask, &in_ip4d))
410         inner_ip4_set = true;
411       else if (unformat (line_input, "ip6-src-ip %U",
412                          unformat_ip6_address_and_mask, &ip6s))
413         flow_class = FLOW_IPV6_CLASS;
414       else if (unformat (line_input, "ip6-dst-ip %U",
415                          unformat_ip6_address_and_mask, &ip6d))
416         flow_class = FLOW_IPV6_CLASS;
417       else if (unformat (line_input, "in-ip6-src-ip %U",
418                          unformat_ip6_address_and_mask, &in_ip6s))
419         inner_ip6_set = true;
420       else if (unformat (line_input, "in-ip6-dst-ip %U",
421                          unformat_ip6_address_and_mask, &in_ip6d))
422         inner_ip6_set = true;
423       else if (unformat (line_input, "src-port %U", unformat_ip_port_and_mask,
424                          &sport))
425         tcp_udp_port_set = true;
426       else if (unformat (line_input, "dst-port %U", unformat_ip_port_and_mask,
427                          &dport))
428         tcp_udp_port_set = true;
429       else
430         if (unformat
431             (line_input, "proto %U", unformat_ip_protocol_and_mask,
432              &protocol))
433         ;
434       else if (unformat (line_input, "in-src-port %U",
435                          unformat_ip_port_and_mask, &in_sport))
436         inner_port_set = true;
437       else if (unformat (line_input, "in-dst-port %U",
438                          unformat_ip_port_and_mask, &in_dport))
439         inner_port_set = true;
440       else if (unformat (line_input, "in-proto %U",
441                          unformat_ip_protocol_and_mask, &in_proto))
442         ;
443       else if (unformat (line_input, "gtpc teid %u", &teid))
444         gtpc_set = true;
445       else if (unformat (line_input, "gtpu teid %u", &teid))
446         gtpu_set = true;
447       else if (unformat (line_input, "vxlan vni %u", &vni))
448         vni_set = true;
449       else if (unformat (line_input, "session id %u", &session_id))
450         {
451           if (protocol.prot == IP_PROTOCOL_L2TP)
452             l2tpv3oip_set = true;
453         }
454       else if (unformat (line_input, "spi %u", &spi))
455         {
456           if (protocol.prot == IP_PROTOCOL_IPSEC_ESP)
457             ipsec_esp_set = true;
458           else if (protocol.prot == IP_PROTOCOL_IPSEC_AH)
459             ipsec_ah_set = true;
460         }
461       else if (unformat (line_input, "index %u", &flow_index))
462         ;
463       else if (unformat (line_input, "next-node %U", unformat_vlib_node, vm,
464                          &flow.redirect_node_index))
465         flow.actions |= VNET_FLOW_ACTION_REDIRECT_TO_NODE;
466       else if (unformat (line_input, "mark %d", &flow.mark_flow_id))
467         flow.actions |= VNET_FLOW_ACTION_MARK;
468       else if (unformat (line_input, "buffer-advance %d",
469                          &flow.buffer_advance))
470         flow.actions |= VNET_FLOW_ACTION_BUFFER_ADVANCE;
471       else if (unformat (line_input, "redirect-to-queue %d",
472                          &flow.redirect_queue))
473         flow.actions |= VNET_FLOW_ACTION_REDIRECT_TO_QUEUE;
474       else if (unformat (line_input, "drop"))
475         flow.actions |= VNET_FLOW_ACTION_DROP;
476       else if (unformat (line_input, "rss function"))
477         {
478           if (0)
479             ;
480 #undef _
481 #define _(f, s) \
482       else if (unformat (line_input, s)) \
483       flow.rss_fun = VNET_RSS_FUNC_##f;
484
485           foreach_rss_function
486 #undef _
487             else
488             {
489               return clib_error_return (0, "unknown input `%U'",
490                                         format_unformat_error, line_input);
491             }
492
493           flow.actions |= VNET_FLOW_ACTION_RSS;
494         }
495       else if (unformat (line_input, "rss types"))
496         {
497           rss_type[0] = NULL;
498           rss_type[1] = NULL;
499           rss_type[2] = NULL;
500           type_str = NULL;
501
502           if (unformat (line_input, "%s use %s and %s",
503                         &rss_type[0], &rss_type[1], &rss_type[2]))
504             ;
505           else if (unformat
506                    (line_input, "%s use %s", &rss_type[0], &rss_type[1]))
507             ;
508           else if (unformat (line_input, "%s", &rss_type[0]))
509             ;
510
511 #undef _
512 #define _(a,b,c)     \
513       else if (!clib_strcmp(c, (const char *)type_str)) \
514         flow.rss_types |= (1ULL<<a);
515
516 #define check_rss_types(_str)     \
517       if (_str != NULL) {\
518         type_str = _str;\
519         if (0) \
520           ; \
521         foreach_flow_rss_types \
522         else \
523         { \
524           return clib_error_return (0, "parse error: '%U'", \
525           format_unformat_error, line_input); \
526         } \
527       }
528
529           check_rss_types (rss_type[0])
530             check_rss_types (rss_type[1]) check_rss_types (rss_type[2])
531 #undef _
532             flow.actions |= VNET_FLOW_ACTION_RSS;
533         }
534       else if (unformat (line_input, "rss queues"))
535         {
536           if (unformat (line_input, "%d to %d", &queue_start, &queue_end))
537             ;
538           else
539             {
540               return clib_error_return (0, "unknown input `%U'",
541                                         format_unformat_error, line_input);
542             }
543
544           flow.queue_index = queue_start;
545           flow.queue_num = queue_end - queue_start + 1;
546
547           flow.actions |= VNET_FLOW_ACTION_RSS;
548         }
549       else if (unformat (line_input, "%U", unformat_vnet_hw_interface, vnm,
550                          &hw_if_index))
551         ;
552       else
553         return clib_error_return (0, "parse error: '%U'",
554                                   format_unformat_error, line_input);
555     }
556
557   unformat_free (line_input);
558
559   if (hw_if_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE))
560     return clib_error_return (0, "Please specify interface name");
561
562   if (flow_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE ||
563                            action == FLOW_DEL))
564     return clib_error_return (0, "Please specify flow index");
565
566   switch (action)
567     {
568     case FLOW_ADD:
569       if (flow.actions == 0)
570         return clib_error_return (0, "Please specify at least one action");
571
572       /* Adjust the flow type */
573       switch (flow_class)
574         {
575         case FLOW_ETHERNET_CLASS:
576           type = VNET_FLOW_TYPE_ETHERNET;
577           break;
578
579         case FLOW_IPV4_CLASS:
580           if (gtpc_set)
581             {
582               type = VNET_FLOW_TYPE_IP4_GTPC;
583               protocol.prot = IP_PROTOCOL_UDP;
584             }
585           else if (gtpu_set)
586             {
587               type = VNET_FLOW_TYPE_IP4_GTPU;
588               protocol.prot = IP_PROTOCOL_UDP;
589             }
590           else if (vni_set)
591             {
592               type = VNET_FLOW_TYPE_IP4_VXLAN;
593               protocol.prot = IP_PROTOCOL_UDP;
594             }
595           else if (l2tpv3oip_set)
596             type = VNET_FLOW_TYPE_IP4_L2TPV3OIP;
597           else if (ipsec_esp_set)
598             type = VNET_FLOW_TYPE_IP4_IPSEC_ESP;
599           else if (ipsec_ah_set)
600             type = VNET_FLOW_TYPE_IP4_IPSEC_AH;
601           else if (tcp_udp_port_set)
602             type = VNET_FLOW_TYPE_IP4_N_TUPLE;
603           else if (inner_ip4_set)
604             {
605               if (inner_port_set)
606                 type = VNET_FLOW_TYPE_IP4_IP4_N_TUPLE;
607               else
608                 type = VNET_FLOW_TYPE_IP4_IP4;
609               protocol.prot = IP_PROTOCOL_IP_IN_IP;
610             }
611           else if (inner_ip6_set)
612             {
613               if (inner_port_set)
614                 type = VNET_FLOW_TYPE_IP4_IP6_N_TUPLE;
615               else
616                 type = VNET_FLOW_TYPE_IP4_IP6;
617               protocol.prot = IP_PROTOCOL_IPV6;
618             }
619           else
620             type = VNET_FLOW_TYPE_IP4;
621           break;
622         case FLOW_IPV6_CLASS:
623           if (tcp_udp_port_set)
624             type = VNET_FLOW_TYPE_IP6_N_TUPLE;
625           else if (vni_set)
626             type = VNET_FLOW_TYPE_IP6_VXLAN;
627           else if (inner_ip4_set)
628             {
629               if (inner_port_set)
630                 type = VNET_FLOW_TYPE_IP6_IP4_N_TUPLE;
631               else
632                 type = VNET_FLOW_TYPE_IP6_IP4;
633               protocol.prot = IP_PROTOCOL_IP_IN_IP;
634             }
635           else if (inner_ip6_set)
636             {
637               if (inner_port_set)
638                 type = VNET_FLOW_TYPE_IP6_IP6_N_TUPLE;
639               else
640                 type = VNET_FLOW_TYPE_IP6_IP6;
641               protocol.prot = IP_PROTOCOL_IPV6;
642             }
643           else
644             type = VNET_FLOW_TYPE_IP6;
645           break;
646
647         default:
648           if (spec && mask)
649             {
650               type = VNET_FLOW_TYPE_GENERIC;
651               break;
652             }
653           return clib_error_return (0,
654                                     "Please specify a supported flow type");
655         }
656
657       /* Assign specific field values per flow type */
658       if (flow_class == FLOW_ETHERNET_CLASS)
659         {
660           flow.ethernet.eth_hdr.type = eth_type;
661         }
662       else if (flow_class == FLOW_IPV4_CLASS)
663         {
664           vnet_flow_ip4_t *ip4_ptr = &flow.ip4;
665
666           clib_memcpy (&ip4_ptr->src_addr, &ip4s,
667                        sizeof (ip4_address_and_mask_t));
668           clib_memcpy (&ip4_ptr->dst_addr, &ip4d,
669                        sizeof (ip4_address_and_mask_t));
670           ip4_ptr->protocol.prot = protocol.prot;
671
672           /* In this cli, we use the protocol.mask only when the flow type is
673            * VNET_FLOW_TYPE_IP4/IP6. For other cases, the IP protocol is just
674            * used to identify the next layer type: e.g. UDP/TCP or IPSEC_ESP
675            */
676           if (type == VNET_FLOW_TYPE_IP4)
677             ip4_ptr->protocol.mask = protocol.mask;
678
679           switch (protocol.prot)
680             {
681               /* ip4-n-tuple */
682             case IP_PROTOCOL_TCP:
683             case IP_PROTOCOL_UDP:
684               flow.ip4_n_tuple.src_port = sport;
685               flow.ip4_n_tuple.dst_port = dport;
686
687               if (type == VNET_FLOW_TYPE_IP4_GTPC)
688                 flow.ip4_gtpc.teid = teid;
689               else if (type == VNET_FLOW_TYPE_IP4_GTPU)
690                 flow.ip4_gtpu.teid = teid;
691               else if (type == VNET_FLOW_TYPE_IP4_VXLAN)
692                 flow.ip4_vxlan.vni = vni;
693               break;
694             case IP_PROTOCOL_L2TP:
695               flow.ip4_l2tpv3oip.session_id = session_id;
696               break;
697             case IP_PROTOCOL_IPSEC_ESP:
698               flow.ip4_ipsec_esp.spi = spi;
699               break;
700             case IP_PROTOCOL_IPSEC_AH:
701               flow.ip4_ipsec_esp.spi = spi;
702               break;
703             case IP_PROTOCOL_IP_IN_IP:
704               clib_memcpy (&flow.ip4_ip4.in_src_addr, &in_ip4s,
705                            sizeof (ip4_address_and_mask_t));
706               clib_memcpy (&flow.ip4_ip4.in_dst_addr, &in_ip4d,
707                            sizeof (ip4_address_and_mask_t));
708               if (type == VNET_FLOW_TYPE_IP4_IP4_N_TUPLE)
709                 {
710                   flow.ip4_ip4.in_protocol.prot = in_proto.prot;
711                   flow.ip4_ip4_n_tuple.in_src_port = in_sport;
712                   flow.ip4_ip4_n_tuple.in_dst_port = in_dport;
713                 }
714               break;
715             case IP_PROTOCOL_IPV6:
716               clib_memcpy (&flow.ip4_ip6.in_src_addr, &in_ip6s,
717                            sizeof (ip6_address_and_mask_t));
718               clib_memcpy (&flow.ip4_ip6.in_dst_addr, &in_ip6d,
719                            sizeof (ip6_address_and_mask_t));
720               if (type == VNET_FLOW_TYPE_IP4_IP6_N_TUPLE)
721                 {
722                   flow.ip4_ip6.in_protocol.prot = in_proto.prot;
723                   flow.ip4_ip6_n_tuple.in_src_port = in_sport;
724                   flow.ip4_ip6_n_tuple.in_dst_port = in_dport;
725                 }
726               break;
727             default:
728               break;
729             }
730         }
731       else if (flow_class == FLOW_IPV6_CLASS)
732         {
733           vnet_flow_ip6_t *ip6_ptr = &flow.ip6;
734
735           clib_memcpy (&flow.ip6_n_tuple.src_addr, &ip6s,
736                        sizeof (ip6_address_and_mask_t));
737           clib_memcpy (&flow.ip6_n_tuple.dst_addr, &ip6d,
738                        sizeof (ip6_address_and_mask_t));
739
740           ip6_ptr->protocol.prot = protocol.prot;
741
742           /* In this cli, we use the protocol.mask only when the flow type is
743            * VNET_FLOW_TYPE_IP4/IP6. For other cases, the IP protocol is just
744            * used to identify the next layer type: e.g. UDP/TCP or IPSEC_ESP
745            */
746           if (type == VNET_FLOW_TYPE_IP6)
747             ip6_ptr->protocol.mask = protocol.mask;
748
749           switch (protocol.prot)
750             {
751               /* ip6-n-tuple */
752             case IP_PROTOCOL_TCP:
753             case IP_PROTOCOL_UDP:
754               flow.ip6_n_tuple.src_port = sport;
755               flow.ip6_n_tuple.dst_port = dport;
756
757               if (type == VNET_FLOW_TYPE_IP6_VXLAN)
758                 flow.ip6_vxlan.vni = vni;
759               break;
760             case IP_PROTOCOL_IP_IN_IP:
761               clib_memcpy (&flow.ip6_ip4.in_src_addr, &in_ip4s,
762                            sizeof (ip4_address_and_mask_t));
763               clib_memcpy (&flow.ip6_ip4.in_dst_addr, &in_ip4d,
764                            sizeof (ip4_address_and_mask_t));
765               if (type == VNET_FLOW_TYPE_IP6_IP4_N_TUPLE)
766                 {
767                   flow.ip6_ip4.in_protocol.prot = in_proto.prot;
768                   flow.ip6_ip4_n_tuple.in_src_port = in_sport;
769                   flow.ip6_ip4_n_tuple.in_dst_port = in_dport;
770                 }
771               break;
772             case IP_PROTOCOL_IPV6:
773               clib_memcpy (&flow.ip6_ip6.in_src_addr, &in_ip6s,
774                            sizeof (ip6_address_and_mask_t));
775               clib_memcpy (&flow.ip6_ip6.in_dst_addr, &in_ip6d,
776                            sizeof (ip6_address_and_mask_t));
777               if (type == VNET_FLOW_TYPE_IP6_IP6_N_TUPLE)
778                 {
779                   flow.ip6_ip6.in_protocol.prot = in_proto.prot;
780                   flow.ip6_ip6_n_tuple.in_src_port = in_sport;
781                   flow.ip6_ip6_n_tuple.in_dst_port = in_dport;
782                 }
783               break;
784             default:
785               break;
786             }
787         }
788       if (type == VNET_FLOW_TYPE_GENERIC)
789         {
790           clib_memcpy (flow.generic.pattern.spec, spec,
791                        sizeof (flow.generic.pattern.spec));
792           clib_memcpy (flow.generic.pattern.mask, mask,
793                        sizeof (flow.generic.pattern.mask));
794         }
795
796       flow.type = type;
797       rv = vnet_flow_add (vnm, &flow, &flow_index);
798       if (!rv)
799         vlib_cli_output (vm, "flow %u added", flow_index);
800
801       break;
802     case FLOW_DEL:
803       rv = vnet_flow_del (vnm, flow_index);
804       break;
805     case FLOW_ENABLE:
806       rv = vnet_flow_enable (vnm, flow_index, hw_if_index);
807       break;
808     case FLOW_DISABLE:
809       rv = vnet_flow_disable (vnm, flow_index, hw_if_index);
810       break;
811     default:
812       return clib_error_return (0, "please specify action (add, del, enable,"
813                                 " disable)");
814     }
815
816   if (rv < 0)
817     return clib_error_return (0, "flow error: %U", format_flow_error, rv);
818
819   return 0;
820 }
821
822 VLIB_CLI_COMMAND (test_flow_command, static) = {
823   .path = "test flow",
824   .short_help = "test flow [add|del|enable|disable] [index <id>] "
825                 "[src-ip <ip-addr/mask>] [dst-ip <ip-addr/mask>] "
826                 "[ip6-src-ip <ip-addr/mask>] [ip6-dst-ip <ip-addr/mask>] "
827                 "[src-port <port/mask>] [dst-port <port/mask>] "
828                 "[proto <ip-proto>] "
829                 "[gtpc teid <teid>] [gtpu teid <teid>] [vxlan <vni>] "
830                 "[session id <session>] [spi <spi>]"
831                 "[spec <spec string>] [mask <mask string>]"
832                 "[next-node <node>] [mark <id>] [buffer-advance <len>] "
833                 "[redirect-to-queue <queue>] [drop] "
834                 "[rss function <name>] [rss types <flow type>]"
835                 "[rss queues <queue_start> to <queue_end>]",
836   .function = test_flow,
837 };
838
839 static u8 *
840 format_flow_match_element (u8 * s, va_list * args)
841 {
842   char *type = va_arg (*args, char *);
843   void *ptr = va_arg (*args, void *);
844
845   if (strncmp (type, "u8", 2) == 0)
846     return format (s, "%d", *(u8 *) ptr);
847
848   if (strncmp (type, "u16", 3) == 0)
849     return format (s, "%d", *(u16 *) ptr);
850
851   if (strncmp (type, "u32", 3) == 0)
852     return format (s, "%d", *(u32 *) ptr);
853
854   if (strncmp (type, "ethernet_header_t", 13) == 0)
855     {
856       ethernet_max_header_t m;
857       memset (&m, 0, sizeof (m));
858       m.ethernet = *(ethernet_header_t *) ptr;
859       /* convert the ethernet type to net order */
860       m.ethernet.type = clib_host_to_net_u16 (m.ethernet.type);
861       return format (s, "%U", format_ethernet_header, &m);
862     }
863
864   if (strncmp (type, "ip4_address_t", 13) == 0)
865     return format (s, "%U", format_ip4_address, ptr);
866
867   if (strncmp (type, "ip4_address_and_mask_t", 13) == 0)
868     return format (s, "%U", format_ip4_address_and_mask, ptr);
869
870   if (strncmp (type, "ip6_address_t", 13) == 0)
871     return format (s, "%U", format_ip6_address, ptr);
872
873   if (strncmp (type, "ip6_address_and_mask_t", 13) == 0)
874     return format (s, "%U", format_ip6_address_and_mask, ptr);
875
876   if (strncmp (type, "ip_prot_and_mask_t", 13) == 0)
877     return format (s, "%U", format_ip_protocol_and_mask, ptr);
878
879   if (strncmp (type, "ip_port_and_mask_t", 18) == 0)
880     return format (s, "%U", format_ip_port_and_mask, ptr);
881
882   s = format (s, "unknown type '%s'", type);
883   return s;
884 }
885
886 #define _fe(a,b) s2 = format (s2, "%s%s %U", s2 ? ", ":"", #b, \
887                               format_flow_match_element, #a, &f->b);
888 #define _(a,b,c) \
889 u8 * format_flow_match_##b (u8 * s, va_list * args)                     \
890 {                                                                       \
891   vnet_flow_##b##_t *f = __builtin_va_arg (*args, vnet_flow_##b##_t *); \
892   u8 *s2 = 0; \
893 foreach_flow_entry_##b \
894   s = format (s, "%v", s2);; \
895   vec_free (s2); \
896 return s; \
897 }
898 foreach_flow_type
899 #undef _
900 #undef _fe
901 static u8 *
902 format_flow_match (u8 * s, va_list * args)
903 {
904   vnet_flow_t *f = va_arg (*args, vnet_flow_t *);
905
906 #define _(a,b,c) \
907   if (f->type == VNET_FLOW_TYPE_##a) \
908     return format (s, "%U", format_flow_match_##b, &f->b);
909   foreach_flow_type;
910 #undef _
911
912   return s;
913 }
914
915 static u8 *
916 format_flow (u8 * s, va_list * args)
917 {
918   vlib_main_t *vm = vlib_get_main ();
919   vnet_flow_t *f = va_arg (*args, vnet_flow_t *);
920   u32 indent = format_get_indent (s);
921   u8 *t = 0;
922
923   s = format (s, "flow-index %u type %s active %u",
924               f->index, flow_type_strings[f->type],
925               hash_elts (f->private_data)),
926     s = format (s, "\n%Umatch: %U", format_white_space, indent + 2,
927                 format_flow_match, f);
928   s = format (s, "\n%Uaction: %U", format_white_space, indent + 2,
929               format_flow_actions, f->actions);
930
931   if (f->actions & VNET_FLOW_ACTION_DROP)
932     t = format (t, "%sdrop", t ? ", " : "");
933
934   if (f->actions & VNET_FLOW_ACTION_MARK)
935     t = format (t, "%smark %u", t ? ", " : "", f->mark_flow_id);
936
937   if (f->actions & VNET_FLOW_ACTION_REDIRECT_TO_QUEUE)
938     t =
939       format (t, "%sredirect-to-queue %u", t ? ", " : "", f->redirect_queue);
940
941   if (f->actions & VNET_FLOW_ACTION_REDIRECT_TO_NODE)
942     t = format (t, "%snext-node %U", t ? ", " : "",
943                 format_vlib_node_name, vm, f->redirect_node_index);
944
945   if (f->actions & VNET_FLOW_ACTION_BUFFER_ADVANCE)
946     t = format (t, "%sbuffer-advance %d", t ? ", " : "", f->buffer_advance);
947
948   if (f->actions & VNET_FLOW_ACTION_RSS)
949     {
950       t = format (t, "%srss function %U", t ? ", " : "",
951                   format_rss_function, f->rss_fun);
952       t = format (t, "%srss types %U", t ? ", " : "",
953                   format_rss_types, f->rss_types);
954     }
955
956   if (t)
957     {
958       s = format (s, "\n%U%v", format_white_space, indent + 4, t);
959       vec_free (t);
960     }
961
962   return s;
963 }
964
965 /*
966  * fd.io coding-style-patch-verification: ON
967  *
968  * Local Variables:
969  * eval: (c-set-style "gnu")
970  * End:
971  */