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