flow: add generic flow pattern for 5G flow 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   vnet_flow_type_t type = VNET_FLOW_TYPE_UNKNOWN;
368   ip4_address_and_mask_t ip4s = { };
369   ip4_address_and_mask_t ip4d = { };
370   ip6_address_and_mask_t ip6s = { };
371   ip6_address_and_mask_t ip6d = { };
372   ip_port_and_mask_t sport = { };
373   ip_port_and_mask_t dport = { };
374   ip_prot_and_mask_t protocol = { };
375   u16 eth_type;
376   bool tcp_udp_port_set = false;
377   bool gtpc_set = false;
378   bool gtpu_set = false;
379   bool vni_set = false;
380   bool l2tpv3oip_set = false;
381   bool ipsec_esp_set = false, ipsec_ah_set = false;
382   u8 *rss_type[3] = { };
383   u8 *type_str = NULL;
384   u8 *spec = NULL;
385   u8 *mask = NULL;
386
387   clib_memset (&flow, 0, sizeof (vnet_flow_t));
388   flow.index = ~0;
389   flow.actions = 0;
390
391   if (!unformat_user (input, unformat_line_input, line_input))
392     return 0;
393
394   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
395     {
396       if (unformat (line_input, "add"))
397         action = FLOW_ADD;
398       else if (unformat (line_input, "del"))
399         action = FLOW_DEL;
400       else if (unformat (line_input, "enable"))
401         action = FLOW_ENABLE;
402       else if (unformat (line_input, "disable"))
403         action = FLOW_DISABLE;
404       else if (unformat (line_input, "spec %s", &spec))
405         ;
406       else if (unformat (line_input, "mask %s", &mask))
407         ;
408       else if (unformat (line_input, "eth-type %U",
409                          unformat_ethernet_type_host_byte_order, &eth_type))
410         flow_class = FLOW_ETHERNET_CLASS;
411       else if (unformat (line_input, "src-ip %U",
412                          unformat_ip4_address_and_mask, &ip4s))
413         flow_class = FLOW_IPV4_CLASS;
414       else if (unformat (line_input, "dst-ip %U",
415                          unformat_ip4_address_and_mask, &ip4d))
416         flow_class = FLOW_IPV4_CLASS;
417       else if (unformat (line_input, "ip6-src-ip %U",
418                          unformat_ip6_address_and_mask, &ip6s))
419         flow_class = FLOW_IPV6_CLASS;
420       else if (unformat (line_input, "ip6-dst-ip %U",
421                          unformat_ip6_address_and_mask, &ip6d))
422         flow_class = FLOW_IPV6_CLASS;
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, "gtpc teid %u", &teid))
435         gtpc_set = true;
436       else if (unformat (line_input, "gtpu teid %u", &teid))
437         gtpu_set = true;
438       else if (unformat (line_input, "vxlan vni %u", &vni))
439         vni_set = true;
440       else if (unformat (line_input, "session id %u", &session_id))
441         {
442           if (protocol.prot == IP_PROTOCOL_L2TP)
443             l2tpv3oip_set = true;
444         }
445       else if (unformat (line_input, "spi %u", &spi))
446         {
447           if (protocol.prot == IP_PROTOCOL_IPSEC_ESP)
448             ipsec_esp_set = true;
449           else if (protocol.prot == IP_PROTOCOL_IPSEC_AH)
450             ipsec_ah_set = true;
451         }
452       else if (unformat (line_input, "index %u", &flow_index))
453         ;
454       else if (unformat (line_input, "next-node %U", unformat_vlib_node, vm,
455                          &flow.redirect_node_index))
456         flow.actions |= VNET_FLOW_ACTION_REDIRECT_TO_NODE;
457       else if (unformat (line_input, "mark %d", &flow.mark_flow_id))
458         flow.actions |= VNET_FLOW_ACTION_MARK;
459       else if (unformat (line_input, "buffer-advance %d",
460                          &flow.buffer_advance))
461         flow.actions |= VNET_FLOW_ACTION_BUFFER_ADVANCE;
462       else if (unformat (line_input, "redirect-to-queue %d",
463                          &flow.redirect_queue))
464         flow.actions |= VNET_FLOW_ACTION_REDIRECT_TO_QUEUE;
465       else if (unformat (line_input, "drop"))
466         flow.actions |= VNET_FLOW_ACTION_DROP;
467       else if (unformat (line_input, "rss function"))
468         {
469           if (0)
470             ;
471 #undef _
472 #define _(f, s) \
473       else if (unformat (line_input, s)) \
474       flow.rss_fun = VNET_RSS_FUNC_##f;
475
476           foreach_rss_function
477 #undef _
478             else
479             {
480               return clib_error_return (0, "unknown input `%U'",
481                                         format_unformat_error, line_input);
482             }
483
484           flow.actions |= VNET_FLOW_ACTION_RSS;
485         }
486       else if (unformat (line_input, "rss types"))
487         {
488           rss_type[0] = NULL;
489           rss_type[1] = NULL;
490           rss_type[2] = NULL;
491           type_str = NULL;
492
493           if (unformat (line_input, "%s use %s and %s",
494                         &rss_type[0], &rss_type[1], &rss_type[2]))
495             ;
496           else if (unformat
497                    (line_input, "%s use %s", &rss_type[0], &rss_type[1]))
498             ;
499           else if (unformat (line_input, "%s", &rss_type[0]))
500             ;
501
502 #undef _
503 #define _(a,b,c)     \
504       else if (!clib_strcmp(c, (const char *)type_str)) \
505         flow.rss_types |= (1ULL<<a);
506
507 #define check_rss_types(_str)     \
508       if (_str != NULL) {\
509         type_str = _str;\
510         if (0) \
511           ; \
512         foreach_flow_rss_types \
513         else \
514         { \
515           return clib_error_return (0, "parse error: '%U'", \
516           format_unformat_error, line_input); \
517         } \
518       }
519
520           check_rss_types (rss_type[0])
521             check_rss_types (rss_type[1]) check_rss_types (rss_type[2])
522 #undef _
523             flow.actions |= VNET_FLOW_ACTION_RSS;
524         }
525       else if (unformat (line_input, "%U", unformat_vnet_hw_interface, vnm,
526                          &hw_if_index))
527         ;
528       else
529         return clib_error_return (0, "parse error: '%U'",
530                                   format_unformat_error, line_input);
531     }
532
533   unformat_free (line_input);
534
535   if (hw_if_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE))
536     return clib_error_return (0, "Please specify interface name");
537
538   if (flow_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE ||
539                            action == FLOW_DEL))
540     return clib_error_return (0, "Please specify flow index");
541
542   switch (action)
543     {
544     case FLOW_ADD:
545       if (flow.actions == 0)
546         return clib_error_return (0, "Please specify at least one action");
547
548       /* Adjust the flow type */
549       switch (flow_class)
550         {
551         case FLOW_ETHERNET_CLASS:
552           type = VNET_FLOW_TYPE_ETHERNET;
553           break;
554
555         case FLOW_IPV4_CLASS:
556           if (gtpc_set)
557             {
558               type = VNET_FLOW_TYPE_IP4_GTPC;
559               protocol.prot = IP_PROTOCOL_UDP;
560             }
561           else if (gtpu_set)
562             {
563               type = VNET_FLOW_TYPE_IP4_GTPU;
564               protocol.prot = IP_PROTOCOL_UDP;
565             }
566           else if (vni_set)
567             {
568               type = VNET_FLOW_TYPE_IP4_VXLAN;
569               protocol.prot = IP_PROTOCOL_UDP;
570             }
571           else if (l2tpv3oip_set)
572             type = VNET_FLOW_TYPE_IP4_L2TPV3OIP;
573           else if (ipsec_esp_set)
574             type = VNET_FLOW_TYPE_IP4_IPSEC_ESP;
575           else if (ipsec_ah_set)
576             type = VNET_FLOW_TYPE_IP4_IPSEC_AH;
577           else if (tcp_udp_port_set)
578             type = VNET_FLOW_TYPE_IP4_N_TUPLE;
579           else
580             type = VNET_FLOW_TYPE_IP4;
581           break;
582         case FLOW_IPV6_CLASS:
583           if (tcp_udp_port_set)
584             type = VNET_FLOW_TYPE_IP6_N_TUPLE;
585           else if (vni_set)
586             type = VNET_FLOW_TYPE_IP6_VXLAN;
587           else
588             type = VNET_FLOW_TYPE_IP6;
589           break;
590
591         default:
592           if (spec && mask)
593             {
594               type = VNET_FLOW_TYPE_GENERIC;
595               break;
596             }
597           return clib_error_return (0,
598                                     "Please specify a supported flow type");
599         }
600
601       /* Assign specific field values per flow type */
602       if (flow_class == FLOW_ETHERNET_CLASS)
603         {
604           flow.ethernet.eth_hdr.type = eth_type;
605         }
606       else if (flow_class == FLOW_IPV4_CLASS)
607         {
608           vnet_flow_ip4_t *ip4_ptr = &flow.ip4;
609
610           clib_memcpy (&ip4_ptr->src_addr, &ip4s,
611                        sizeof (ip4_address_and_mask_t));
612           clib_memcpy (&ip4_ptr->dst_addr, &ip4d,
613                        sizeof (ip4_address_and_mask_t));
614           ip4_ptr->protocol.prot = protocol.prot;
615
616           /* In this cli, we use the protocol.mask only when the flow type is
617            * VNET_FLOW_TYPE_IP4/IP6. For other cases, the IP protocol is just
618            * used to identify the next layer type: e.g. UDP/TCP or IPSEC_ESP
619            */
620           if (type == VNET_FLOW_TYPE_IP4)
621             ip4_ptr->protocol.mask = protocol.mask;
622
623           switch (protocol.prot)
624             {
625               /* ip4-n-tuple */
626             case IP_PROTOCOL_TCP:
627             case IP_PROTOCOL_UDP:
628               flow.ip4_n_tuple.src_port = sport;
629               flow.ip4_n_tuple.dst_port = dport;
630
631               if (type == VNET_FLOW_TYPE_IP4_GTPC)
632                 flow.ip4_gtpc.teid = teid;
633               else if (type == VNET_FLOW_TYPE_IP4_GTPU)
634                 flow.ip4_gtpu.teid = teid;
635               else if (type == VNET_FLOW_TYPE_IP4_VXLAN)
636                 flow.ip4_vxlan.vni = vni;
637               break;
638             case IP_PROTOCOL_L2TP:
639               flow.ip4_l2tpv3oip.session_id = session_id;
640               break;
641             case IP_PROTOCOL_IPSEC_ESP:
642               flow.ip4_ipsec_esp.spi = spi;
643               break;
644             case IP_PROTOCOL_IPSEC_AH:
645               flow.ip4_ipsec_esp.spi = spi;
646               break;
647             default:
648               break;
649             }
650         }
651       else if (flow_class == FLOW_IPV6_CLASS)
652         {
653           vnet_flow_ip6_t *ip6_ptr = &flow.ip6;
654
655           clib_memcpy (&flow.ip6_n_tuple.src_addr, &ip6s,
656                        sizeof (ip6_address_and_mask_t));
657           clib_memcpy (&flow.ip6_n_tuple.dst_addr, &ip6d,
658                        sizeof (ip6_address_and_mask_t));
659
660           ip6_ptr->protocol.prot = protocol.prot;
661
662           /* In this cli, we use the protocol.mask only when the flow type is
663            * VNET_FLOW_TYPE_IP4/IP6. For other cases, the IP protocol is just
664            * used to identify the next layer type: e.g. UDP/TCP or IPSEC_ESP
665            */
666           if (type == VNET_FLOW_TYPE_IP6)
667             ip6_ptr->protocol.mask = protocol.mask;
668
669           switch (protocol.prot)
670             {
671               /* ip6-n-tuple */
672             case IP_PROTOCOL_TCP:
673             case IP_PROTOCOL_UDP:
674               flow.ip6_n_tuple.src_port = sport;
675               flow.ip6_n_tuple.dst_port = dport;
676
677               if (type == VNET_FLOW_TYPE_IP6_VXLAN)
678                 flow.ip6_vxlan.vni = vni;
679               break;
680             default:
681               break;
682             }
683         }
684       if (type == VNET_FLOW_TYPE_GENERIC)
685         {
686           clib_memcpy (flow.generic.pattern.spec, spec,
687                        sizeof (flow.generic.pattern.spec));
688           clib_memcpy (flow.generic.pattern.mask, mask,
689                        sizeof (flow.generic.pattern.mask));
690         }
691
692       flow.type = type;
693       rv = vnet_flow_add (vnm, &flow, &flow_index);
694       if (!rv)
695         vlib_cli_output (vm, "flow %u added", flow_index);
696
697       break;
698     case FLOW_DEL:
699       rv = vnet_flow_del (vnm, flow_index);
700       break;
701     case FLOW_ENABLE:
702       rv = vnet_flow_enable (vnm, flow_index, hw_if_index);
703       break;
704     case FLOW_DISABLE:
705       rv = vnet_flow_disable (vnm, flow_index, hw_if_index);
706       break;
707     default:
708       return clib_error_return (0, "please specify action (add, del, enable,"
709                                 " disable)");
710     }
711
712   if (rv < 0)
713     return clib_error_return (0, "flow error: %U", format_flow_error, rv);
714
715   return 0;
716 }
717
718 /* *INDENT-OFF* */
719 VLIB_CLI_COMMAND (test_flow_command, static) = {
720   .path = "test flow",
721   .short_help = "test flow [add|del|enable|disable] [index <id>] "
722                 "[src-ip <ip-addr/mask>] [dst-ip <ip-addr/mask>] "
723                 "[ip6-src-ip <ip-addr/mask>] [ip6-dst-ip <ip-addr/mask>] "
724                 "[src-port <port/mask>] [dst-port <port/mask>] "
725                 "[proto <ip-proto>] "
726                 "[gtpc teid <teid>] [gtpu teid <teid>] [vxlan <vni>] "
727                 "[session id <session>] [spi <spi>]"
728                 "[spec <spec string>] [mask <mask string>]"
729                 "[next-node <node>] [mark <id>] [buffer-advance <len>] "
730                 "[redirect-to-queue <queue>] [drop] "
731                 "[rss function <name>] [rss types <flow type>]",
732   .function = test_flow,
733 };
734 /* *INDENT-ON* */
735
736 static u8 *
737 format_flow_match_element (u8 * s, va_list * args)
738 {
739   char *type = va_arg (*args, char *);
740   void *ptr = va_arg (*args, void *);
741
742   if (strncmp (type, "u8", 2) == 0)
743     return format (s, "%d", *(u8 *) ptr);
744
745   if (strncmp (type, "u16", 3) == 0)
746     return format (s, "%d", *(u16 *) ptr);
747
748   if (strncmp (type, "u32", 3) == 0)
749     return format (s, "%d", *(u32 *) ptr);
750
751   if (strncmp (type, "ethernet_header_t", 13) == 0)
752     {
753       ethernet_max_header_t m;
754       memset (&m, 0, sizeof (m));
755       m.ethernet = *(ethernet_header_t *) ptr;
756       /* convert the ethernet type to net order */
757       m.ethernet.type = clib_host_to_net_u16 (m.ethernet.type);
758       return format (s, "%U", format_ethernet_header, &m);
759     }
760
761   if (strncmp (type, "ip4_address_t", 13) == 0)
762     return format (s, "%U", format_ip4_address, ptr);
763
764   if (strncmp (type, "ip4_address_and_mask_t", 13) == 0)
765     return format (s, "%U", format_ip4_address_and_mask, ptr);
766
767   if (strncmp (type, "ip6_address_t", 13) == 0)
768     return format (s, "%U", format_ip6_address, ptr);
769
770   if (strncmp (type, "ip6_address_and_mask_t", 13) == 0)
771     return format (s, "%U", format_ip6_address_and_mask, ptr);
772
773   if (strncmp (type, "ip_prot_and_mask_t", 13) == 0)
774     return format (s, "%U", format_ip_protocol_and_mask, ptr);
775
776   if (strncmp (type, "ip_port_and_mask_t", 18) == 0)
777     return format (s, "%U", format_ip_port_and_mask, ptr);
778
779   s = format (s, "unknown type '%s'", type);
780   return s;
781 }
782
783 #define _fe(a,b) s2 = format (s2, "%s%s %U", s2 ? ", ":"", #b, \
784                               format_flow_match_element, #a, &f->b);
785 #define _(a,b,c) \
786 u8 * format_flow_match_##b (u8 * s, va_list * args)                     \
787 {                                                                       \
788   vnet_flow_##b##_t *f = __builtin_va_arg (*args, vnet_flow_##b##_t *); \
789   u8 *s2 = 0; \
790 foreach_flow_entry_##b \
791   s = format (s, "%v", s2);; \
792   vec_free (s2); \
793 return s; \
794 }
795 foreach_flow_type
796 #undef _
797 #undef _fe
798 static u8 *
799 format_flow_match (u8 * s, va_list * args)
800 {
801   vnet_flow_t *f = va_arg (*args, vnet_flow_t *);
802
803 #define _(a,b,c) \
804   if (f->type == VNET_FLOW_TYPE_##a) \
805     return format (s, "%U", format_flow_match_##b, &f->b);
806   foreach_flow_type;
807 #undef _
808
809   return s;
810 }
811
812 static u8 *
813 format_flow (u8 * s, va_list * args)
814 {
815   vlib_main_t *vm = vlib_get_main ();
816   vnet_flow_t *f = va_arg (*args, vnet_flow_t *);
817   u32 indent = format_get_indent (s);
818   u8 *t = 0;
819
820   s = format (s, "flow-index %u type %s active %u",
821               f->index, flow_type_strings[f->type],
822               hash_elts (f->private_data)),
823     s = format (s, "\n%Umatch: %U", format_white_space, indent + 2,
824                 format_flow_match, f);
825   s = format (s, "\n%Uaction: %U", format_white_space, indent + 2,
826               format_flow_actions, f->actions);
827
828   if (f->actions & VNET_FLOW_ACTION_DROP)
829     t = format (t, "%sdrop", t ? ", " : "");
830
831   if (f->actions & VNET_FLOW_ACTION_MARK)
832     t = format (t, "%smark %u", t ? ", " : "", f->mark_flow_id);
833
834   if (f->actions & VNET_FLOW_ACTION_REDIRECT_TO_QUEUE)
835     t =
836       format (t, "%sredirect-to-queue %u", t ? ", " : "", f->redirect_queue);
837
838   if (f->actions & VNET_FLOW_ACTION_REDIRECT_TO_NODE)
839     t = format (t, "%snext-node %U", t ? ", " : "",
840                 format_vlib_node_name, vm, f->redirect_node_index);
841
842   if (f->actions & VNET_FLOW_ACTION_BUFFER_ADVANCE)
843     t = format (t, "%sbuffer-advance %d", t ? ", " : "", f->buffer_advance);
844
845   if (f->actions & VNET_FLOW_ACTION_RSS)
846     {
847       t = format (t, "%srss function %U", t ? ", " : "",
848                   format_rss_function, f->rss_fun);
849       t = format (t, "%srss types %U", t ? ", " : "",
850                   format_rss_types, f->rss_types);
851     }
852
853   if (t)
854     {
855       s = format (s, "\n%U%v", format_white_space, indent + 4, t);
856       vec_free (t);
857     }
858
859   return s;
860 }
861
862 /*
863  * fd.io coding-style-patch-verification: ON
864  *
865  * Local Variables:
866  * eval: (c-set-style "gnu")
867  * End:
868  */