flow: add RSS support
[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
16 #include <vnet/vnet.h>
17 #include <vnet/devices/devices.h>
18 #include <vnet/ip/ip.h>
19 #include <vnet/ethernet/ethernet.h>
20 #include <vnet/flow/flow.h>
21
22 static format_function_t format_flow;
23
24 uword
25 unformat_ip_port_and_mask (unformat_input_t * input, va_list * args)
26 {
27   ip_port_and_mask_t *pm = va_arg (*args, ip_port_and_mask_t *);
28   u32 port = 0, mask = 0;
29
30   if (unformat (input, "any"))
31     ;
32   else if (unformat (input, "%u/%u", &port, &mask))
33     ;
34   else if (unformat (input, "%u/0x%x", &port, &mask))
35     ;
36   else if (unformat (input, "%u", &port))
37     mask = 0xffff;
38   else
39     return 0;
40
41   if (port > 0xffff || mask > 0xffff)
42     return 0;
43
44   pm->port = port;
45   pm->mask = mask;
46   return 1;
47 }
48
49 u8 *
50 format_ip_port_and_mask (u8 * s, va_list * args)
51 {
52   ip_port_and_mask_t *pm = va_arg (*args, ip_port_and_mask_t *);
53
54   if (pm->port == 0 && pm->mask == 0)
55     return format (s, "any");
56
57   if (pm->mask == 0xffff)
58     return format (s, "%u", pm->port);
59
60   return format (s, "%u/0x%x", pm->port, pm->mask);
61 }
62
63 u8 *
64 format_flow_error (u8 * s, va_list * args)
65 {
66   int error = va_arg (*args, int);
67
68   if (error == 0)
69     return format (s, "no error");
70
71 #define _(v,n,str) if (error == v) return format (s, #str);
72   foreach_flow_error;
73 #undef _
74
75   return format (s, "unknown error (%d)", error);
76 }
77
78 u8 *
79 format_flow_actions (u8 * s, va_list * args)
80 {
81   u32 actions = va_arg (*args, u32);
82   u8 *t = 0;
83
84 #define _(a, b, c) if (actions & (1 << a)) \
85   t = format (t, "%s%s", t ? " ":"", c);
86   foreach_flow_action
87 #undef _
88     s = format (s, "%v", t);
89   vec_free (t);
90   return s;
91 }
92
93 u8 *
94 format_flow_enabled_hw (u8 * s, va_list * args)
95 {
96   u32 flow_index = va_arg (*args, u32);
97   vnet_flow_t *f = vnet_get_flow (flow_index);
98   if (f == 0)
99     return format (s, "not found");
100
101   u8 *t = 0;
102   u32 hw_if_index;
103   uword private_data;
104   vnet_main_t *vnm = vnet_get_main ();
105   /* *INDENT-OFF* */
106   hash_foreach (hw_if_index, private_data, f->private_data,
107     ({
108      t = format (t, "%s%U", t ? ", " : "",
109                  format_vnet_hw_if_index_name, vnm, hw_if_index);
110      }));
111   /* *INDENT-ON* */
112   s = format (s, "%v", t);
113   vec_free (t);
114   return s;
115 }
116
117 static const char *flow_type_strings[] = { 0,
118 #define _(a,b,c) c,
119   foreach_flow_type
120 #undef _
121 };
122
123 static clib_error_t *
124 show_flow_entry (vlib_main_t * vm, unformat_input_t * input,
125                  vlib_cli_command_t * cmd_arg)
126 {
127   vnet_main_t *vnm = vnet_get_main ();
128   vnet_flow_main_t *fm = &flow_main;
129   unformat_input_t _line_input, *line_input = &_line_input;
130   vnet_hw_interface_t *hi;
131   vnet_device_class_t *dev_class;
132   vnet_flow_t *f;
133   uword private_data;
134   u32 index = ~0, hw_if_index;
135
136   if (!unformat_user (input, unformat_line_input, line_input))
137     goto no_args;
138
139   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
140     {
141       if (unformat (line_input, "index %u", &index))
142         ;
143       else
144         return clib_error_return (0, "parse error: '%U'",
145                                   format_unformat_error, line_input);
146     }
147
148   unformat_free (line_input);
149
150   if (index != ~0)
151     {
152       if ((f = vnet_get_flow (index)) == 0)
153         return clib_error_return (0, "no such flow");
154
155       vlib_cli_output (vm, "%-10s: %u", "index", f->index);
156       vlib_cli_output (vm, "%-10s: %s", "type", flow_type_strings[f->type]);
157       vlib_cli_output (vm, "%-10s: %U", "match", format_flow, f);
158       /* *INDENT-OFF* */
159       hash_foreach (hw_if_index, private_data, f->private_data,
160         ({
161          hi = vnet_get_hw_interface (vnm, hw_if_index);
162           dev_class = vnet_get_device_class (vnm, hi->dev_class_index);
163           vlib_cli_output (vm,  "interface %U\n",
164                            format_vnet_hw_if_index_name, vnm, hw_if_index);
165           if (dev_class->format_flow)
166             vlib_cli_output (vm,  "  %U\n", dev_class->format_flow,
167                              hi->dev_instance, f->index, private_data);
168          }));
169       /* *INDENT-ON* */
170       return 0;
171     }
172
173 no_args:
174   /* *INDENT-OFF* */
175   pool_foreach (f, fm->global_flow_pool,
176     {
177       vlib_cli_output (vm, "%U\n", format_flow, f);
178     });
179   /* *INDENT-ON* */
180
181   return 0;
182 }
183
184 /* *INDENT-OFF* */
185 VLIB_CLI_COMMAND (show_flow_entry_command, static) = {
186     .path = "show flow entry",
187     .short_help = "show flow entry [index <index>]",
188     .function = show_flow_entry,
189 };
190 /* *INDENT-ON* */
191
192 static clib_error_t *
193 show_flow_ranges (vlib_main_t * vm, unformat_input_t * input,
194                   vlib_cli_command_t * cmd_arg)
195 {
196   vnet_flow_main_t *fm = &flow_main;
197   vnet_flow_range_t *r = 0;
198
199   vlib_cli_output (vm, "%8s  %8s  %s", "Start", "Count", "Owner");
200
201   /* *INDENT-OFF* */
202   vec_foreach (r, fm->ranges)
203     {
204       vlib_cli_output (vm, "%8u  %8u  %s", r->start, r->count, r->owner);
205     };
206   /* *INDENT-ON* */
207   return 0;
208 }
209
210 /* *INDENT-OFF* */
211 VLIB_CLI_COMMAND (show_flow_ranges_command, static) = {
212     .path = "show flow ranges",
213     .short_help = "show flow ranges",
214     .function = show_flow_ranges,
215 };
216 /* *INDENT-ON* */
217
218 static clib_error_t *
219 show_flow_interface (vlib_main_t * vm, unformat_input_t * input,
220                      vlib_cli_command_t * cmd_arg)
221 {
222   vnet_main_t *vnm = vnet_get_main ();
223   vnet_hw_interface_t *hi;
224   vnet_device_class_t *dev_class;
225   unformat_input_t _line_input, *line_input = &_line_input;
226   u32 hw_if_index = ~0;
227
228   if (unformat_user (input, unformat_line_input, line_input))
229     {
230       while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
231         {
232           if (unformat (line_input, "%U",
233                         unformat_vnet_hw_interface, vnm, &hw_if_index))
234             ;
235           else
236             return clib_error_return (0, "parse error: '%U'",
237                                       format_unformat_error, line_input);
238         }
239       unformat_free (line_input);
240     }
241
242   if (hw_if_index == ~0)
243     return clib_error_return (0, "please specify interface");
244
245   hi = vnet_get_hw_interface (vnm, hw_if_index);
246   dev_class = vnet_get_device_class (vnm, hi->dev_class_index);
247   if (dev_class->format_flow == 0)
248     return clib_error_return (0, "not supported");
249
250   vlib_cli_output (vm, "%U", dev_class->format_flow, hi->dev_instance, ~0, 0);
251   return 0;
252 }
253
254 /* *INDENT-OFF* */
255 VLIB_CLI_COMMAND (show_flow_interface_command, static) = {
256     .path = "show flow interface",
257     .short_help = "show flow interface <interface name>",
258     .function = show_flow_interface,
259 };
260 /* *INDENT-ON* */
261
262 static clib_error_t *
263 test_flow (vlib_main_t * vm, unformat_input_t * input,
264            vlib_cli_command_t * cmd_arg)
265 {
266   vnet_flow_t flow;
267   vnet_main_t *vnm = vnet_get_main ();
268   unformat_input_t _line_input, *line_input = &_line_input;
269   enum
270   {
271     FLOW_UNKNOWN_ACTION,
272     FLOW_ADD,
273     FLOW_DEL,
274     FLOW_ENABLE,
275     FLOW_DISABLE
276   } action = FLOW_UNKNOWN_ACTION;
277   u32 hw_if_index = ~0, flow_index = ~0;
278   int rv;
279   u32 prot = 0, teid = 0;
280   vnet_flow_type_t type = VNET_FLOW_TYPE_IP4_N_TUPLE;
281   bool is_gtpc_set = false;
282   bool is_gtpu_set = false;
283   vnet_flow_type_t outer_type = VNET_FLOW_TYPE_UNKNOWN;
284   vnet_flow_type_t inner_type = VNET_FLOW_TYPE_UNKNOWN;
285   bool outer_ip4_set = false, inner_ip4_set = false;
286   bool outer_ip6_set = false, inner_ip6_set = false;
287   ip4_address_and_mask_t ip4s = { };
288   ip4_address_and_mask_t ip4d = { };
289   ip4_address_and_mask_t inner_ip4s = { };
290   ip4_address_and_mask_t inner_ip4d = { };
291   ip6_address_and_mask_t ip6s = { };
292   ip6_address_and_mask_t ip6d = { };
293   ip6_address_and_mask_t inner_ip6s = { };
294   ip6_address_and_mask_t inner_ip6d = { };
295   ip_port_and_mask_t sport = { };
296   ip_port_and_mask_t dport = { };
297   u16 eth_type;
298   bool ethernet_set = false;
299   u8 *rss_type[3] = { };
300   u8 *type_str = NULL;
301
302   clib_memset (&flow, 0, sizeof (vnet_flow_t));
303   flow.index = ~0;
304   flow.actions = 0;
305   flow.ip4_n_tuple.protocol = ~0;
306   if (!unformat_user (input, unformat_line_input, line_input))
307     return 0;
308
309   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
310     {
311       if (unformat (line_input, "add"))
312         action = FLOW_ADD;
313       else if (unformat (line_input, "del"))
314         action = FLOW_DEL;
315       else if (unformat (line_input, "enable"))
316         action = FLOW_ENABLE;
317       else if (unformat (line_input, "disable"))
318         action = FLOW_DISABLE;
319       else if (unformat (line_input, "eth-type %U",
320                          unformat_ethernet_type_host_byte_order, &eth_type))
321         ethernet_set = true;
322       else if (unformat (line_input, "src-ip %U",
323                          unformat_ip4_address_and_mask, &ip4s))
324         outer_ip4_set = true;
325       else if (unformat (line_input, "dst-ip %U",
326                          unformat_ip4_address_and_mask, &ip4d))
327         outer_ip4_set = true;
328       else if (unformat (line_input, "ip6-src-ip %U",
329                          unformat_ip6_address_and_mask, &ip6s))
330         outer_ip6_set = true;
331       else if (unformat (line_input, "ip6-dst-ip %U",
332                          unformat_ip6_address_and_mask, &ip6d))
333         outer_ip6_set = true;
334       else if (unformat (line_input, "inner-src-ip %U",
335                          unformat_ip4_address_and_mask, &inner_ip4s))
336         inner_ip4_set = true;
337       else if (unformat (line_input, "inner-dst-ip %U",
338                          unformat_ip4_address_and_mask, &inner_ip4d))
339         inner_ip4_set = true;
340       else if (unformat (line_input, "inner-ip6-src-ip %U",
341                          unformat_ip6_address_and_mask, &inner_ip6s))
342         inner_ip6_set = true;
343       else if (unformat (line_input, "inner-ip6-dst-ip %U",
344                          unformat_ip6_address_and_mask, &inner_ip6d))
345         inner_ip6_set = true;
346       else if (unformat (line_input, "src-port %U", unformat_ip_port_and_mask,
347                          &sport))
348         ;
349       else if (unformat (line_input, "dst-port %U", unformat_ip_port_and_mask,
350                          &dport))
351         ;
352       else if (unformat (line_input, "proto %U", unformat_ip_protocol, &prot))
353         ;
354       else if (unformat (line_input, "proto %u", &prot))
355         ;
356       else if (unformat (line_input, "gtpc teid %u", &teid))
357         is_gtpc_set = true;
358       else if (unformat (line_input, "gtpu teid %u", &teid))
359         is_gtpu_set = true;
360       else if (unformat (line_input, "index %u", &flow_index))
361         ;
362       else if (unformat (line_input, "next-node %U", unformat_vlib_node, vm,
363                          &flow.redirect_node_index))
364         flow.actions |= VNET_FLOW_ACTION_REDIRECT_TO_NODE;
365       else if (unformat (line_input, "mark %d", &flow.mark_flow_id))
366         flow.actions |= VNET_FLOW_ACTION_MARK;
367       else if (unformat (line_input, "buffer-advance %d",
368                          &flow.buffer_advance))
369         flow.actions |= VNET_FLOW_ACTION_BUFFER_ADVANCE;
370       else if (unformat (line_input, "redirect-to-queue %d",
371                          &flow.redirect_queue))
372         flow.actions |= VNET_FLOW_ACTION_REDIRECT_TO_QUEUE;
373       else if (unformat (line_input, "drop"))
374         flow.actions |= VNET_FLOW_ACTION_DROP;
375       else if (unformat (line_input, "rss function"))
376         {
377           if (0)
378             ;
379 #undef _
380 #define _(f, s)                                 \
381         else if (unformat (line_input, s)) \
382           flow.rss_fun = VNET_RSS_FUNC_##f;
383
384           foreach_rss_function
385 #undef _
386             else
387             {
388               return clib_error_return (0, "unknown input `%U'",
389                                         format_unformat_error, line_input);
390             }
391
392           flow.actions |= VNET_FLOW_ACTION_RSS;
393         }
394       else if (unformat (line_input, "rss types"))
395         {
396           rss_type[0] = NULL;
397           rss_type[1] = NULL;
398           rss_type[2] = NULL;
399           type_str = NULL;
400
401           if (unformat (line_input, "%s use %s and %s",
402                         &rss_type[0], &rss_type[1], &rss_type[2]))
403             ;
404           else
405             if (unformat
406                 (line_input, "%s use %s", &rss_type[0], &rss_type[1]))
407             ;
408           else if (unformat (line_input, "%s", &rss_type[0]))
409             ;
410
411 #undef _
412 #define _(a,b,c)     \
413         else if (!clib_strcmp(c, (const char *)type_str)) \
414           flow.rss_types |= (1ULL<<a);
415
416 #define check_rss_types(_str)     \
417         if (_str != NULL) {\
418           type_str = _str;\
419           if (0) \
420                   ; \
421           foreach_flow_rss_types \
422           else \
423           { \
424             return clib_error_return (0, "parse error: '%U'", \
425               format_unformat_error, line_input); \
426           } \
427         }
428
429           check_rss_types (rss_type[0])
430             check_rss_types (rss_type[1]) check_rss_types (rss_type[2])
431 #undef _
432             flow.actions |= VNET_FLOW_ACTION_RSS;
433         }
434       else if (unformat (line_input, "%U", unformat_vnet_hw_interface, vnm,
435                          &hw_if_index))
436         ;
437       else
438         return clib_error_return (0, "parse error: '%U'",
439                                   format_unformat_error, line_input);
440     }
441
442   unformat_free (line_input);
443
444   if (hw_if_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE))
445     return clib_error_return (0, "Please specify interface name");
446
447   if (flow_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE ||
448                            action == FLOW_DEL))
449     return clib_error_return (0, "Please specify flow index");
450
451   switch (action)
452     {
453     case FLOW_ADD:
454       if (flow.actions == 0)
455         return clib_error_return (0, "Please specify at least one action");
456
457       /* Adjust the flow type */
458       if (ethernet_set == true)
459         outer_type = VNET_FLOW_TYPE_ETHERNET;
460       if (outer_ip4_set == true)
461         outer_type = VNET_FLOW_TYPE_IP4_N_TUPLE;
462       else if (outer_ip6_set == true)
463         outer_type = VNET_FLOW_TYPE_IP6_N_TUPLE;
464       if (inner_ip4_set == true)
465         inner_type = VNET_FLOW_TYPE_IP4_N_TUPLE;
466       else if (inner_ip6_set == true)
467         inner_type = VNET_FLOW_TYPE_IP6_N_TUPLE;
468
469       if (outer_type == VNET_FLOW_TYPE_UNKNOWN)
470         return clib_error_return (0, "Please specify a supported flow type");
471
472       if (outer_type == VNET_FLOW_TYPE_ETHERNET)
473         type = VNET_FLOW_TYPE_ETHERNET;
474       else if (outer_type == VNET_FLOW_TYPE_IP4_N_TUPLE)
475         {
476           type = VNET_FLOW_TYPE_IP4_N_TUPLE;
477
478           if (inner_type == VNET_FLOW_TYPE_UNKNOWN)
479             {
480               if (is_gtpc_set)
481                 type = VNET_FLOW_TYPE_IP4_GTPC;
482               else if (is_gtpu_set)
483                 type = VNET_FLOW_TYPE_IP4_GTPU;
484             }
485           else if (inner_type == VNET_FLOW_TYPE_IP4_N_TUPLE)
486             {
487               if (is_gtpu_set)
488                 type = VNET_FLOW_TYPE_IP4_GTPU_IP4;
489             }
490           else if (inner_type == VNET_FLOW_TYPE_IP6_N_TUPLE)
491             {
492               if (is_gtpu_set)
493                 type = VNET_FLOW_TYPE_IP4_GTPU_IP6;
494             }
495         }
496       else if (outer_type == VNET_FLOW_TYPE_IP6_N_TUPLE)
497         {
498           type = VNET_FLOW_TYPE_IP6_N_TUPLE;
499
500           if (inner_type == VNET_FLOW_TYPE_UNKNOWN)
501             {
502               if (is_gtpc_set)
503                 type = VNET_FLOW_TYPE_IP6_GTPC;
504               else if (is_gtpu_set)
505                 type = VNET_FLOW_TYPE_IP6_GTPU;
506             }
507           else if (inner_type == VNET_FLOW_TYPE_IP4_N_TUPLE)
508             {
509               if (is_gtpu_set)
510                 type = VNET_FLOW_TYPE_IP6_GTPU_IP4;
511             }
512           else if (inner_type == VNET_FLOW_TYPE_IP6_N_TUPLE)
513             {
514               if (is_gtpu_set)
515                 type = VNET_FLOW_TYPE_IP6_GTPU_IP6;
516             }
517         }
518
519       //assign specific field values per flow type
520       switch (type)
521         {
522         case VNET_FLOW_TYPE_ETHERNET:
523           memset (&flow.ethernet, 0, sizeof (flow.ethernet));
524           flow.ethernet.eth_hdr.type = eth_type;
525           break;
526
527         case VNET_FLOW_TYPE_IP4_N_TUPLE:
528         case VNET_FLOW_TYPE_IP4_GTPC:
529         case VNET_FLOW_TYPE_IP4_GTPU:
530         case VNET_FLOW_TYPE_IP4_GTPU_IP4:
531         case VNET_FLOW_TYPE_IP4_GTPU_IP6:
532           clib_memcpy (&flow.ip4_n_tuple.src_addr, &ip4s,
533                        sizeof (ip4_address_and_mask_t));
534           clib_memcpy (&flow.ip4_n_tuple.dst_addr, &ip4d,
535                        sizeof (ip4_address_and_mask_t));
536           clib_memcpy (&flow.ip4_n_tuple.src_port, &sport,
537                        sizeof (ip_port_and_mask_t));
538           clib_memcpy (&flow.ip4_n_tuple.dst_port, &dport,
539                        sizeof (ip_port_and_mask_t));
540           flow.ip4_n_tuple.protocol = prot;
541
542           if (type == VNET_FLOW_TYPE_IP4_GTPC)
543             flow.ip4_gtpc.teid = teid;
544           else if (type == VNET_FLOW_TYPE_IP4_GTPU)
545             flow.ip4_gtpu.teid = teid;
546           else if (type == VNET_FLOW_TYPE_IP4_GTPU_IP4)
547             {
548               flow.ip4_gtpu_ip4.teid = teid;
549               clib_memcpy (&flow.ip4_gtpu_ip4.inner_src_addr, &inner_ip4s,
550                            sizeof (ip4_address_and_mask_t));
551               clib_memcpy (&flow.ip4_gtpu_ip4.inner_dst_addr, &inner_ip4d,
552                            sizeof (ip4_address_and_mask_t));
553             }
554           else if (type == VNET_FLOW_TYPE_IP4_GTPU_IP6)
555             {
556               flow.ip4_gtpu_ip6.teid = teid;
557               clib_memcpy (&flow.ip4_gtpu_ip6.inner_src_addr, &inner_ip6s,
558                            sizeof (ip6_address_and_mask_t));
559               clib_memcpy (&flow.ip4_gtpu_ip6.inner_dst_addr, &inner_ip6d,
560                            sizeof (ip6_address_and_mask_t));
561             }
562
563           if (flow.ip4_n_tuple.protocol == (ip_protocol_t) ~ 0)
564             return clib_error_return (0, "Please specify ip protocol");
565           if ((type != VNET_FLOW_TYPE_IP4_N_TUPLE) &&
566               (flow.ip4_n_tuple.protocol != IP_PROTOCOL_UDP))
567             return clib_error_return (0,
568                                       "For GTP related flow, ip protocol must be UDP");
569           break;
570
571         case VNET_FLOW_TYPE_IP6_N_TUPLE:
572         case VNET_FLOW_TYPE_IP6_GTPC:
573         case VNET_FLOW_TYPE_IP6_GTPU:
574         case VNET_FLOW_TYPE_IP6_GTPU_IP4:
575         case VNET_FLOW_TYPE_IP6_GTPU_IP6:
576           clib_memcpy (&flow.ip6_n_tuple.src_addr, &ip6s,
577                        sizeof (ip6_address_and_mask_t));
578           clib_memcpy (&flow.ip6_n_tuple.dst_addr, &ip6d,
579                        sizeof (ip6_address_and_mask_t));
580           clib_memcpy (&flow.ip6_n_tuple.src_port, &sport,
581                        sizeof (ip_port_and_mask_t));
582           clib_memcpy (&flow.ip6_n_tuple.dst_port, &dport,
583                        sizeof (ip_port_and_mask_t));
584           flow.ip6_n_tuple.protocol = prot;
585
586           if (type == VNET_FLOW_TYPE_IP6_GTPC)
587             flow.ip6_gtpc.teid = teid;
588           else if (type == VNET_FLOW_TYPE_IP6_GTPU)
589             flow.ip6_gtpu.teid = teid;
590           else if (type == VNET_FLOW_TYPE_IP6_GTPU_IP4)
591             {
592               flow.ip6_gtpu_ip4.teid = teid;
593               clib_memcpy (&flow.ip6_gtpu_ip4.inner_src_addr, &inner_ip4s,
594                            sizeof (ip4_address_and_mask_t));
595               clib_memcpy (&flow.ip6_gtpu_ip4.inner_dst_addr, &inner_ip4d,
596                            sizeof (ip4_address_and_mask_t));
597             }
598           else if (type == VNET_FLOW_TYPE_IP6_GTPU_IP6)
599             {
600               flow.ip6_gtpu_ip6.teid = teid;
601               clib_memcpy (&flow.ip6_gtpu_ip6.inner_src_addr, &inner_ip6s,
602                            sizeof (ip6_address_and_mask_t));
603               clib_memcpy (&flow.ip6_gtpu_ip6.inner_dst_addr, &inner_ip6d,
604                            sizeof (ip6_address_and_mask_t));
605             }
606
607           if (flow.ip6_n_tuple.protocol == (ip_protocol_t) ~ 0)
608             return clib_error_return (0, "Please specify ip protocol");
609           if ((type != VNET_FLOW_TYPE_IP6_N_TUPLE) &&
610               (flow.ip6_n_tuple.protocol != IP_PROTOCOL_UDP))
611             return clib_error_return (0,
612                                       "For GTP related flow, ip protocol must be UDP");
613           break;
614
615         default:
616           break;
617         }
618
619       flow.type = type;
620       rv = vnet_flow_add (vnm, &flow, &flow_index);
621       if (!rv)
622         printf ("flow %u added\n", flow_index);
623
624       break;
625     case FLOW_DEL:
626       rv = vnet_flow_del (vnm, flow_index);
627       break;
628     case FLOW_ENABLE:
629       rv = vnet_flow_enable (vnm, flow_index, hw_if_index);
630       break;
631     case FLOW_DISABLE:
632       rv = vnet_flow_disable (vnm, flow_index, hw_if_index);
633       break;
634     default:
635       return clib_error_return (0, "please specify action (add, del, enable,"
636                                 " disable)");
637     }
638
639   if (rv < 0)
640     return clib_error_return (0, "flow error: %U", format_flow_error, rv);
641   return 0;
642 }
643
644 /* *INDENT-OFF* */
645 VLIB_CLI_COMMAND (test_flow_command, static) = {
646     .path = "test flow",
647     .short_help = "test flow add [src-ip <ip-addr/mask>] [dst-ip "
648       "<ip-addr/mask>] [src-port <port/mask>] [dst-port <port/mask>] "
649       "[proto <ip-proto>",
650     .function = test_flow,
651 };
652 /* *INDENT-ON* */
653
654
655 static u8 *
656 format_flow_match_element (u8 * s, va_list * args)
657 {
658   char *type = va_arg (*args, char *);
659   void *ptr = va_arg (*args, void *);
660
661   if (strncmp (type, "u8", 2) == 0)
662     return format (s, "%d", *(u8 *) ptr);
663
664   if (strncmp (type, "u16", 3) == 0)
665     return format (s, "%d", *(u16 *) ptr);
666
667   if (strncmp (type, "u32", 3) == 0)
668     return format (s, "%d", *(u32 *) ptr);
669
670   if (strncmp (type, "ip4_address_t", 13) == 0)
671     return format (s, "%U", format_ip4_address, ptr);
672
673   if (strncmp (type, "ip4_address_and_mask_t", 13) == 0)
674     return format (s, "%U", format_ip4_address_and_mask, ptr);
675
676   if (strncmp (type, "ip6_address_t", 13) == 0)
677     return format (s, "%U", format_ip6_address, ptr);
678
679   if (strncmp (type, "ip6_address_and_mask_t", 13) == 0)
680     return format (s, "%U", format_ip6_address_and_mask, ptr);
681
682   if (strncmp (type, "ip_protocol_t", 13) == 0)
683     return format (s, "%U", format_ip_protocol, *(ip_protocol_t *) ptr);
684
685   if (strncmp (type, "ip_port_and_mask_t", 18) == 0)
686     return format (s, "%U", format_ip_port_and_mask, ptr);
687
688   s = format (s, "unknown type '%s'", type);
689   return s;
690 }
691
692 #define _fe(a,b) s2 = format (s2, "%s%s %U", s2 ? ", ":"", #b, \
693                               format_flow_match_element, #a, &f->b);
694 #define _(a,b,c) \
695 u8 * format_flow_match_##b (u8 * s, va_list * args)                     \
696 {                                                                       \
697   vnet_flow_##b##_t *f = __builtin_va_arg (*args, vnet_flow_##b##_t *); \
698   u8 *s2 = 0; \
699 foreach_flow_entry_##b \
700   s = format (s, "%v", s2);; \
701   vec_free (s2); \
702 return s; \
703 }
704 foreach_flow_type
705 #undef _
706 #undef _fe
707 static u8 *
708 format_flow_match (u8 * s, va_list * args)
709 {
710   vnet_flow_t *f = va_arg (*args, vnet_flow_t *);
711
712 #define _(a,b,c) \
713   if (f->type == VNET_FLOW_TYPE_##a) \
714     return format (s, "%U", format_flow_match_##b, &f->b);
715   foreach_flow_type;
716 #undef _
717
718   return s;
719 }
720
721 static u8 *
722 format_flow (u8 * s, va_list * args)
723 {
724   vlib_main_t *vm = vlib_get_main ();
725   vnet_flow_t *f = va_arg (*args, vnet_flow_t *);
726   u32 indent = format_get_indent (s);
727   u8 *t = 0;
728
729   s = format (s, "flow-index %u type %s active %u",
730               f->index, flow_type_strings[f->type],
731               hash_elts (f->private_data)),
732     s = format (s, "\n%Umatch: %U", format_white_space, indent + 2,
733                 format_flow_match, f);
734   s = format (s, "\n%Uaction: %U", format_white_space, indent + 2,
735               format_flow_actions, f->actions);
736
737   if (f->actions & VNET_FLOW_ACTION_MARK)
738     t = format (t, "%smark %u", t ? ", " : "", f->mark_flow_id);
739
740   if (f->actions & VNET_FLOW_ACTION_REDIRECT_TO_NODE)
741     t = format (t, "%snext-node %U", t ? ", " : "",
742                 format_vlib_node_name, vm, f->redirect_node_index);
743
744   if (f->actions & VNET_FLOW_ACTION_BUFFER_ADVANCE)
745     t = format (t, "%sbuffer-advance %d", t ? ", " : "", f->buffer_advance);
746
747   if (t)
748     {
749       s = format (s, "\n%U%v", format_white_space, indent + 4, t);
750       vec_free (t);
751     }
752
753   return s;
754 }
755
756 /*
757  * fd.io coding-style-patch-verification: ON
758  *
759  * Local Variables:
760  * eval: (c-set-style "gnu")
761  * End:
762  */