flow: add l2tpv3oip flow
[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, session_id = 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   bool is_l2tpv3oip_set = false;
284   vnet_flow_type_t outer_type = VNET_FLOW_TYPE_UNKNOWN;
285   vnet_flow_type_t inner_type = VNET_FLOW_TYPE_UNKNOWN;
286   bool outer_ip4_set = false, inner_ip4_set = false;
287   bool outer_ip6_set = false, inner_ip6_set = false;
288   ip4_address_and_mask_t ip4s = { };
289   ip4_address_and_mask_t ip4d = { };
290   ip4_address_and_mask_t inner_ip4s = { };
291   ip4_address_and_mask_t inner_ip4d = { };
292   ip6_address_and_mask_t ip6s = { };
293   ip6_address_and_mask_t ip6d = { };
294   ip6_address_and_mask_t inner_ip6s = { };
295   ip6_address_and_mask_t inner_ip6d = { };
296   ip_port_and_mask_t sport = { };
297   ip_port_and_mask_t dport = { };
298   u16 eth_type;
299   bool ethernet_set = false;
300   u8 *rss_type[3] = { };
301   u8 *type_str = NULL;
302
303   clib_memset (&flow, 0, sizeof (vnet_flow_t));
304   flow.index = ~0;
305   flow.actions = 0;
306   flow.ip4_n_tuple.protocol = ~0;
307   if (!unformat_user (input, unformat_line_input, line_input))
308     return 0;
309
310   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
311     {
312       if (unformat (line_input, "add"))
313         action = FLOW_ADD;
314       else if (unformat (line_input, "del"))
315         action = FLOW_DEL;
316       else if (unformat (line_input, "enable"))
317         action = FLOW_ENABLE;
318       else if (unformat (line_input, "disable"))
319         action = FLOW_DISABLE;
320       else if (unformat (line_input, "eth-type %U",
321                          unformat_ethernet_type_host_byte_order, &eth_type))
322         ethernet_set = true;
323       else if (unformat (line_input, "src-ip %U",
324                          unformat_ip4_address_and_mask, &ip4s))
325         outer_ip4_set = true;
326       else if (unformat (line_input, "dst-ip %U",
327                          unformat_ip4_address_and_mask, &ip4d))
328         outer_ip4_set = true;
329       else if (unformat (line_input, "ip6-src-ip %U",
330                          unformat_ip6_address_and_mask, &ip6s))
331         outer_ip6_set = true;
332       else if (unformat (line_input, "ip6-dst-ip %U",
333                          unformat_ip6_address_and_mask, &ip6d))
334         outer_ip6_set = true;
335       else if (unformat (line_input, "inner-src-ip %U",
336                          unformat_ip4_address_and_mask, &inner_ip4s))
337         inner_ip4_set = true;
338       else if (unformat (line_input, "inner-dst-ip %U",
339                          unformat_ip4_address_and_mask, &inner_ip4d))
340         inner_ip4_set = true;
341       else if (unformat (line_input, "inner-ip6-src-ip %U",
342                          unformat_ip6_address_and_mask, &inner_ip6s))
343         inner_ip6_set = true;
344       else if (unformat (line_input, "inner-ip6-dst-ip %U",
345                          unformat_ip6_address_and_mask, &inner_ip6d))
346         inner_ip6_set = true;
347       else if (unformat (line_input, "src-port %U", unformat_ip_port_and_mask,
348                          &sport))
349         ;
350       else if (unformat (line_input, "dst-port %U", unformat_ip_port_and_mask,
351                          &dport))
352         ;
353       else if (unformat (line_input, "proto %U", unformat_ip_protocol, &prot))
354         ;
355       else if (unformat (line_input, "proto %u", &prot))
356         ;
357       else if (unformat (line_input, "gtpc teid %u", &teid))
358         is_gtpc_set = true;
359       else if (unformat (line_input, "gtpu teid %u", &teid))
360         is_gtpu_set = true;
361       else if (unformat (line_input, "session id %u", &session_id))
362         {
363           if (prot == IP_PROTOCOL_L2TP)
364             is_l2tpv3oip_set = true;
365         }
366       else if (unformat (line_input, "index %u", &flow_index))
367         ;
368       else if (unformat (line_input, "next-node %U", unformat_vlib_node, vm,
369                          &flow.redirect_node_index))
370         flow.actions |= VNET_FLOW_ACTION_REDIRECT_TO_NODE;
371       else if (unformat (line_input, "mark %d", &flow.mark_flow_id))
372         flow.actions |= VNET_FLOW_ACTION_MARK;
373       else if (unformat (line_input, "buffer-advance %d",
374                          &flow.buffer_advance))
375         flow.actions |= VNET_FLOW_ACTION_BUFFER_ADVANCE;
376       else if (unformat (line_input, "redirect-to-queue %d",
377                          &flow.redirect_queue))
378         flow.actions |= VNET_FLOW_ACTION_REDIRECT_TO_QUEUE;
379       else if (unformat (line_input, "drop"))
380         flow.actions |= VNET_FLOW_ACTION_DROP;
381       else if (unformat (line_input, "rss function"))
382         {
383           if (0)
384             ;
385 #undef _
386 #define _(f, s)                                 \
387         else if (unformat (line_input, s)) \
388           flow.rss_fun = VNET_RSS_FUNC_##f;
389
390           foreach_rss_function
391 #undef _
392             else
393             {
394               return clib_error_return (0, "unknown input `%U'",
395                                         format_unformat_error, line_input);
396             }
397
398           flow.actions |= VNET_FLOW_ACTION_RSS;
399         }
400       else if (unformat (line_input, "rss types"))
401         {
402           rss_type[0] = NULL;
403           rss_type[1] = NULL;
404           rss_type[2] = NULL;
405           type_str = NULL;
406
407           if (unformat (line_input, "%s use %s and %s",
408                         &rss_type[0], &rss_type[1], &rss_type[2]))
409             ;
410           else
411             if (unformat
412                 (line_input, "%s use %s", &rss_type[0], &rss_type[1]))
413             ;
414           else if (unformat (line_input, "%s", &rss_type[0]))
415             ;
416
417 #undef _
418 #define _(a,b,c)     \
419         else if (!clib_strcmp(c, (const char *)type_str)) \
420           flow.rss_types |= (1ULL<<a);
421
422 #define check_rss_types(_str)     \
423         if (_str != NULL) {\
424           type_str = _str;\
425           if (0) \
426                   ; \
427           foreach_flow_rss_types \
428           else \
429           { \
430             return clib_error_return (0, "parse error: '%U'", \
431               format_unformat_error, line_input); \
432           } \
433         }
434
435           check_rss_types (rss_type[0])
436             check_rss_types (rss_type[1]) check_rss_types (rss_type[2])
437 #undef _
438             flow.actions |= VNET_FLOW_ACTION_RSS;
439         }
440       else if (unformat (line_input, "%U", unformat_vnet_hw_interface, vnm,
441                          &hw_if_index))
442         ;
443       else
444         return clib_error_return (0, "parse error: '%U'",
445                                   format_unformat_error, line_input);
446     }
447
448   unformat_free (line_input);
449
450   if (hw_if_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE))
451     return clib_error_return (0, "Please specify interface name");
452
453   if (flow_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE ||
454                            action == FLOW_DEL))
455     return clib_error_return (0, "Please specify flow index");
456
457   switch (action)
458     {
459     case FLOW_ADD:
460       if (flow.actions == 0)
461         return clib_error_return (0, "Please specify at least one action");
462
463       /* Adjust the flow type */
464       if (ethernet_set == true)
465         outer_type = VNET_FLOW_TYPE_ETHERNET;
466       if (outer_ip4_set == true)
467         outer_type = VNET_FLOW_TYPE_IP4_N_TUPLE;
468       else if (outer_ip6_set == true)
469         outer_type = VNET_FLOW_TYPE_IP6_N_TUPLE;
470       if (inner_ip4_set == true)
471         inner_type = VNET_FLOW_TYPE_IP4_N_TUPLE;
472       else if (inner_ip6_set == true)
473         inner_type = VNET_FLOW_TYPE_IP6_N_TUPLE;
474
475       if (outer_type == VNET_FLOW_TYPE_UNKNOWN)
476         return clib_error_return (0, "Please specify a supported flow type");
477
478       if (outer_type == VNET_FLOW_TYPE_ETHERNET)
479         type = VNET_FLOW_TYPE_ETHERNET;
480       else if (outer_type == VNET_FLOW_TYPE_IP4_N_TUPLE)
481         {
482           type = VNET_FLOW_TYPE_IP4_N_TUPLE;
483
484           if (inner_type == VNET_FLOW_TYPE_UNKNOWN)
485             {
486               if (is_gtpc_set)
487                 type = VNET_FLOW_TYPE_IP4_GTPC;
488               else if (is_gtpu_set)
489                 type = VNET_FLOW_TYPE_IP4_GTPU;
490               else if (is_l2tpv3oip_set)
491                 type = VNET_FLOW_TYPE_IP4_L2TPV3OIP;
492             }
493           else if (inner_type == VNET_FLOW_TYPE_IP4_N_TUPLE)
494             {
495               if (is_gtpu_set)
496                 type = VNET_FLOW_TYPE_IP4_GTPU_IP4;
497             }
498           else if (inner_type == VNET_FLOW_TYPE_IP6_N_TUPLE)
499             {
500               if (is_gtpu_set)
501                 type = VNET_FLOW_TYPE_IP4_GTPU_IP6;
502             }
503         }
504       else if (outer_type == VNET_FLOW_TYPE_IP6_N_TUPLE)
505         {
506           type = VNET_FLOW_TYPE_IP6_N_TUPLE;
507
508           if (inner_type == VNET_FLOW_TYPE_UNKNOWN)
509             {
510               if (is_gtpc_set)
511                 type = VNET_FLOW_TYPE_IP6_GTPC;
512               else if (is_gtpu_set)
513                 type = VNET_FLOW_TYPE_IP6_GTPU;
514             }
515           else if (inner_type == VNET_FLOW_TYPE_IP4_N_TUPLE)
516             {
517               if (is_gtpu_set)
518                 type = VNET_FLOW_TYPE_IP6_GTPU_IP4;
519             }
520           else if (inner_type == VNET_FLOW_TYPE_IP6_N_TUPLE)
521             {
522               if (is_gtpu_set)
523                 type = VNET_FLOW_TYPE_IP6_GTPU_IP6;
524             }
525         }
526
527       //assign specific field values per flow type
528       switch (type)
529         {
530         case VNET_FLOW_TYPE_ETHERNET:
531           memset (&flow.ethernet, 0, sizeof (flow.ethernet));
532           flow.ethernet.eth_hdr.type = eth_type;
533           break;
534         case VNET_FLOW_TYPE_IP4_L2TPV3OIP:
535           clib_memcpy (&flow.ip4_l2tpv3oip.src_addr, &ip4s,
536                        sizeof (ip4_address_and_mask_t));
537           clib_memcpy (&flow.ip4_l2tpv3oip.dst_addr, &ip4d,
538                        sizeof (ip4_address_and_mask_t));
539           flow.ip4_l2tpv3oip.protocol = prot;
540           flow.ip4_l2tpv3oip.session_id = session_id;
541           break;
542         case VNET_FLOW_TYPE_IP4_N_TUPLE:
543         case VNET_FLOW_TYPE_IP4_GTPC:
544         case VNET_FLOW_TYPE_IP4_GTPU:
545         case VNET_FLOW_TYPE_IP4_GTPU_IP4:
546         case VNET_FLOW_TYPE_IP4_GTPU_IP6:
547           clib_memcpy (&flow.ip4_n_tuple.src_addr, &ip4s,
548                        sizeof (ip4_address_and_mask_t));
549           clib_memcpy (&flow.ip4_n_tuple.dst_addr, &ip4d,
550                        sizeof (ip4_address_and_mask_t));
551           clib_memcpy (&flow.ip4_n_tuple.src_port, &sport,
552                        sizeof (ip_port_and_mask_t));
553           clib_memcpy (&flow.ip4_n_tuple.dst_port, &dport,
554                        sizeof (ip_port_and_mask_t));
555           flow.ip4_n_tuple.protocol = prot;
556
557           if (type == VNET_FLOW_TYPE_IP4_GTPC)
558             flow.ip4_gtpc.teid = teid;
559           else if (type == VNET_FLOW_TYPE_IP4_GTPU)
560             flow.ip4_gtpu.teid = teid;
561           else if (type == VNET_FLOW_TYPE_IP4_GTPU_IP4)
562             {
563               flow.ip4_gtpu_ip4.teid = teid;
564               clib_memcpy (&flow.ip4_gtpu_ip4.inner_src_addr, &inner_ip4s,
565                            sizeof (ip4_address_and_mask_t));
566               clib_memcpy (&flow.ip4_gtpu_ip4.inner_dst_addr, &inner_ip4d,
567                            sizeof (ip4_address_and_mask_t));
568             }
569           else if (type == VNET_FLOW_TYPE_IP4_GTPU_IP6)
570             {
571               flow.ip4_gtpu_ip6.teid = teid;
572               clib_memcpy (&flow.ip4_gtpu_ip6.inner_src_addr, &inner_ip6s,
573                            sizeof (ip6_address_and_mask_t));
574               clib_memcpy (&flow.ip4_gtpu_ip6.inner_dst_addr, &inner_ip6d,
575                            sizeof (ip6_address_and_mask_t));
576             }
577
578           if (flow.ip4_n_tuple.protocol == (ip_protocol_t) ~ 0)
579             return clib_error_return (0, "Please specify ip protocol");
580           if ((type != VNET_FLOW_TYPE_IP4_N_TUPLE) &&
581               (flow.ip4_n_tuple.protocol != IP_PROTOCOL_UDP))
582             return clib_error_return (0,
583                                       "For GTP related flow, ip protocol must be UDP");
584           break;
585
586         case VNET_FLOW_TYPE_IP6_N_TUPLE:
587         case VNET_FLOW_TYPE_IP6_GTPC:
588         case VNET_FLOW_TYPE_IP6_GTPU:
589         case VNET_FLOW_TYPE_IP6_GTPU_IP4:
590         case VNET_FLOW_TYPE_IP6_GTPU_IP6:
591           clib_memcpy (&flow.ip6_n_tuple.src_addr, &ip6s,
592                        sizeof (ip6_address_and_mask_t));
593           clib_memcpy (&flow.ip6_n_tuple.dst_addr, &ip6d,
594                        sizeof (ip6_address_and_mask_t));
595           clib_memcpy (&flow.ip6_n_tuple.src_port, &sport,
596                        sizeof (ip_port_and_mask_t));
597           clib_memcpy (&flow.ip6_n_tuple.dst_port, &dport,
598                        sizeof (ip_port_and_mask_t));
599           flow.ip6_n_tuple.protocol = prot;
600
601           if (type == VNET_FLOW_TYPE_IP6_GTPC)
602             flow.ip6_gtpc.teid = teid;
603           else if (type == VNET_FLOW_TYPE_IP6_GTPU)
604             flow.ip6_gtpu.teid = teid;
605           else if (type == VNET_FLOW_TYPE_IP6_GTPU_IP4)
606             {
607               flow.ip6_gtpu_ip4.teid = teid;
608               clib_memcpy (&flow.ip6_gtpu_ip4.inner_src_addr, &inner_ip4s,
609                            sizeof (ip4_address_and_mask_t));
610               clib_memcpy (&flow.ip6_gtpu_ip4.inner_dst_addr, &inner_ip4d,
611                            sizeof (ip4_address_and_mask_t));
612             }
613           else if (type == VNET_FLOW_TYPE_IP6_GTPU_IP6)
614             {
615               flow.ip6_gtpu_ip6.teid = teid;
616               clib_memcpy (&flow.ip6_gtpu_ip6.inner_src_addr, &inner_ip6s,
617                            sizeof (ip6_address_and_mask_t));
618               clib_memcpy (&flow.ip6_gtpu_ip6.inner_dst_addr, &inner_ip6d,
619                            sizeof (ip6_address_and_mask_t));
620             }
621
622           if (flow.ip6_n_tuple.protocol == (ip_protocol_t) ~ 0)
623             return clib_error_return (0, "Please specify ip protocol");
624           if ((type != VNET_FLOW_TYPE_IP6_N_TUPLE) &&
625               (flow.ip6_n_tuple.protocol != IP_PROTOCOL_UDP))
626             return clib_error_return (0,
627                                       "For GTP related flow, ip protocol must be UDP");
628           break;
629
630         default:
631           break;
632         }
633
634       flow.type = type;
635       rv = vnet_flow_add (vnm, &flow, &flow_index);
636       if (!rv)
637         printf ("flow %u added\n", flow_index);
638
639       break;
640     case FLOW_DEL:
641       rv = vnet_flow_del (vnm, flow_index);
642       break;
643     case FLOW_ENABLE:
644       rv = vnet_flow_enable (vnm, flow_index, hw_if_index);
645       break;
646     case FLOW_DISABLE:
647       rv = vnet_flow_disable (vnm, flow_index, hw_if_index);
648       break;
649     default:
650       return clib_error_return (0, "please specify action (add, del, enable,"
651                                 " disable)");
652     }
653
654   if (rv < 0)
655     return clib_error_return (0, "flow error: %U", format_flow_error, rv);
656   return 0;
657 }
658
659 /* *INDENT-OFF* */
660 VLIB_CLI_COMMAND (test_flow_command, static) = {
661     .path = "test flow",
662     .short_help = "test flow add [src-ip <ip-addr/mask>] [dst-ip "
663       "<ip-addr/mask>] [src-port <port/mask>] [dst-port <port/mask>] "
664       "[proto <ip-proto>",
665     .function = test_flow,
666 };
667 /* *INDENT-ON* */
668
669
670 static u8 *
671 format_flow_match_element (u8 * s, va_list * args)
672 {
673   char *type = va_arg (*args, char *);
674   void *ptr = va_arg (*args, void *);
675
676   if (strncmp (type, "u8", 2) == 0)
677     return format (s, "%d", *(u8 *) ptr);
678
679   if (strncmp (type, "u16", 3) == 0)
680     return format (s, "%d", *(u16 *) ptr);
681
682   if (strncmp (type, "u32", 3) == 0)
683     return format (s, "%d", *(u32 *) ptr);
684
685   if (strncmp (type, "ip4_address_t", 13) == 0)
686     return format (s, "%U", format_ip4_address, ptr);
687
688   if (strncmp (type, "ip4_address_and_mask_t", 13) == 0)
689     return format (s, "%U", format_ip4_address_and_mask, ptr);
690
691   if (strncmp (type, "ip6_address_t", 13) == 0)
692     return format (s, "%U", format_ip6_address, ptr);
693
694   if (strncmp (type, "ip6_address_and_mask_t", 13) == 0)
695     return format (s, "%U", format_ip6_address_and_mask, ptr);
696
697   if (strncmp (type, "ip_protocol_t", 13) == 0)
698     return format (s, "%U", format_ip_protocol, *(ip_protocol_t *) ptr);
699
700   if (strncmp (type, "ip_port_and_mask_t", 18) == 0)
701     return format (s, "%U", format_ip_port_and_mask, ptr);
702
703   s = format (s, "unknown type '%s'", type);
704   return s;
705 }
706
707 #define _fe(a,b) s2 = format (s2, "%s%s %U", s2 ? ", ":"", #b, \
708                               format_flow_match_element, #a, &f->b);
709 #define _(a,b,c) \
710 u8 * format_flow_match_##b (u8 * s, va_list * args)                     \
711 {                                                                       \
712   vnet_flow_##b##_t *f = __builtin_va_arg (*args, vnet_flow_##b##_t *); \
713   u8 *s2 = 0; \
714 foreach_flow_entry_##b \
715   s = format (s, "%v", s2);; \
716   vec_free (s2); \
717 return s; \
718 }
719 foreach_flow_type
720 #undef _
721 #undef _fe
722 static u8 *
723 format_flow_match (u8 * s, va_list * args)
724 {
725   vnet_flow_t *f = va_arg (*args, vnet_flow_t *);
726
727 #define _(a,b,c) \
728   if (f->type == VNET_FLOW_TYPE_##a) \
729     return format (s, "%U", format_flow_match_##b, &f->b);
730   foreach_flow_type;
731 #undef _
732
733   return s;
734 }
735
736 static u8 *
737 format_flow (u8 * s, va_list * args)
738 {
739   vlib_main_t *vm = vlib_get_main ();
740   vnet_flow_t *f = va_arg (*args, vnet_flow_t *);
741   u32 indent = format_get_indent (s);
742   u8 *t = 0;
743
744   s = format (s, "flow-index %u type %s active %u",
745               f->index, flow_type_strings[f->type],
746               hash_elts (f->private_data)),
747     s = format (s, "\n%Umatch: %U", format_white_space, indent + 2,
748                 format_flow_match, f);
749   s = format (s, "\n%Uaction: %U", format_white_space, indent + 2,
750               format_flow_actions, f->actions);
751
752   if (f->actions & VNET_FLOW_ACTION_MARK)
753     t = format (t, "%smark %u", t ? ", " : "", f->mark_flow_id);
754
755   if (f->actions & VNET_FLOW_ACTION_REDIRECT_TO_NODE)
756     t = format (t, "%snext-node %U", t ? ", " : "",
757                 format_vlib_node_name, vm, f->redirect_node_index);
758
759   if (f->actions & VNET_FLOW_ACTION_BUFFER_ADVANCE)
760     t = format (t, "%sbuffer-advance %d", t ? ", " : "", f->buffer_advance);
761
762   if (t)
763     {
764       s = format (s, "\n%U%v", format_white_space, indent + 4, t);
765       vec_free (t);
766     }
767
768   return s;
769 }
770
771 /*
772  * fd.io coding-style-patch-verification: ON
773  *
774  * Local Variables:
775  * eval: (c-set-style "gnu")
776  * End:
777  */