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