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