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