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