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