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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <vnet/vnet.h>
18 #include <vnet/devices/devices.h>
19 #include <vnet/ip/ip.h>
20 #include <vnet/ethernet/ethernet.h>
21 #include <vnet/ethernet/packet.h>
22 #include <vnet/flow/flow.h>
24 static format_function_t format_flow;
27 unformat_ip_port_and_mask (unformat_input_t * input, va_list * args)
29 ip_port_and_mask_t *pm = va_arg (*args, ip_port_and_mask_t *);
30 u32 port = 0, mask = 0;
32 if (unformat (input, "any"))
34 else if (unformat (input, "%u/%u", &port, &mask))
36 else if (unformat (input, "%u/0x%x", &port, &mask))
38 else if (unformat (input, "%u", &port))
43 if (port > 0xffff || mask > 0xffff)
52 format_ip_port_and_mask (u8 * s, va_list * args)
54 ip_port_and_mask_t *pm = va_arg (*args, ip_port_and_mask_t *);
56 if (pm->port == 0 && pm->mask == 0)
57 return format (s, "any");
59 if (pm->mask == 0xffff)
60 return format (s, "%u", pm->port);
62 return format (s, "%u/0x%x", pm->port, pm->mask);
66 unformat_ip_protocol_and_mask (unformat_input_t * input, va_list * args)
68 ip_prot_and_mask_t *pm = va_arg (*args, ip_prot_and_mask_t *);
69 u32 prot = 0, mask = 0;
71 if (unformat (input, "any"))
73 else if (unformat (input, "%U", unformat_ip_protocol, &prot))
75 else if (unformat (input, "%u", &prot))
80 if (prot > 0XFF || mask > 0xFF)
89 format_ip_protocol_and_mask (u8 * s, va_list * args)
91 ip_prot_and_mask_t *pm = va_arg (*args, ip_prot_and_mask_t *);
93 if (pm->prot == 0 && pm->mask == 0)
94 return format (s, "any");
96 return format (s, "%U", format_ip_protocol, pm->prot);
100 format_flow_error (u8 * s, va_list * args)
102 int error = va_arg (*args, int);
105 return format (s, "no error");
107 #define _(v,n,str) if (error == v) return format (s, #str);
111 return format (s, "unknown error (%d)", error);
115 format_flow_actions (u8 * s, va_list * args)
117 u32 actions = va_arg (*args, u32);
120 #define _(a, b, c) if (actions & (1 << a)) \
121 t = format (t, "%s%s", t ? " ":"", c);
124 s = format (s, "%v", t);
130 format_flow_enabled_hw (u8 * s, va_list * args)
132 u32 flow_index = va_arg (*args, u32);
133 vnet_flow_t *f = vnet_get_flow (flow_index);
135 return format (s, "not found");
140 vnet_main_t *vnm = vnet_get_main ();
141 hash_foreach (hw_if_index, private_data, f->private_data,
143 t = format (t, "%s%U", t ? ", " : "",
144 format_vnet_hw_if_index_name, vnm, hw_if_index);
146 s = format (s, "%v", t);
152 format_rss_function (u8 * s, va_list * args)
154 vnet_rss_function_t func = va_arg (*args, vnet_rss_function_t);
160 else if (func == VNET_RSS_FUNC_##f) \
161 return format (s, n);
165 return format (s, "unknown");
169 format_rss_types (u8 * s, va_list * args)
171 u64 type = va_arg (*args, u64);
175 if (type & (1UL<<a)) \
176 s = format (s, "%s ", c);
178 foreach_flow_rss_types
183 static const char *flow_type_strings[] = { 0,
189 static clib_error_t *
190 show_flow_entry (vlib_main_t * vm, unformat_input_t * input,
191 vlib_cli_command_t * cmd_arg)
193 vnet_main_t *vnm = vnet_get_main ();
194 vnet_flow_main_t *fm = &flow_main;
195 unformat_input_t _line_input, *line_input = &_line_input;
196 vnet_hw_interface_t *hi;
197 vnet_device_class_t *dev_class;
200 u32 index = ~0, hw_if_index;
202 if (!unformat_user (input, unformat_line_input, line_input))
205 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
207 if (unformat (line_input, "index %u", &index))
210 return clib_error_return (0, "parse error: '%U'",
211 format_unformat_error, line_input);
214 unformat_free (line_input);
218 if ((f = vnet_get_flow (index)) == 0)
219 return clib_error_return (0, "no such flow");
221 vlib_cli_output (vm, "%-10s: %u", "index", f->index);
222 vlib_cli_output (vm, "%-10s: %s", "type", flow_type_strings[f->type]);
223 vlib_cli_output (vm, "%-10s: %U", "match", format_flow, f);
224 if (f->type == VNET_FLOW_TYPE_GENERIC)
226 vlib_cli_output (vm, "%s: %s", "spec", f->generic.pattern.spec);
227 vlib_cli_output (vm, "%s: %s", "mask", f->generic.pattern.mask);
229 hash_foreach (hw_if_index, private_data, f->private_data,
231 hi = vnet_get_hw_interface (vnm, hw_if_index);
232 dev_class = vnet_get_device_class (vnm, hi->dev_class_index);
233 vlib_cli_output (vm, "interface %U\n",
234 format_vnet_hw_if_index_name, vnm, hw_if_index);
235 if (dev_class->format_flow)
236 vlib_cli_output (vm, " %U\n", dev_class->format_flow,
237 hi->dev_instance, f->index, private_data);
243 pool_foreach (f, fm->global_flow_pool)
245 vlib_cli_output (vm, "%U\n", format_flow, f);
246 if (f->type == VNET_FLOW_TYPE_GENERIC)
248 vlib_cli_output (vm, "%s: %s", "spec", f->generic.pattern.spec);
249 vlib_cli_output (vm, "%s: %s", "mask", f->generic.pattern.mask);
256 VLIB_CLI_COMMAND (show_flow_entry_command, static) = {
257 .path = "show flow entry",
258 .short_help = "show flow entry [index <index>]",
259 .function = show_flow_entry,
262 static clib_error_t *
263 show_flow_ranges (vlib_main_t * vm, unformat_input_t * input,
264 vlib_cli_command_t * cmd_arg)
266 vnet_flow_main_t *fm = &flow_main;
267 vnet_flow_range_t *r = 0;
269 vlib_cli_output (vm, "%8s %8s %s", "Start", "Count", "Owner");
271 vec_foreach (r, fm->ranges)
273 vlib_cli_output (vm, "%8u %8u %s", r->start, r->count, r->owner);
278 VLIB_CLI_COMMAND (show_flow_ranges_command, static) = {
279 .path = "show flow ranges",
280 .short_help = "show flow ranges",
281 .function = show_flow_ranges,
284 static clib_error_t *
285 show_flow_interface (vlib_main_t * vm, unformat_input_t * input,
286 vlib_cli_command_t * cmd_arg)
288 vnet_main_t *vnm = vnet_get_main ();
289 vnet_hw_interface_t *hi;
290 vnet_device_class_t *dev_class;
291 unformat_input_t _line_input, *line_input = &_line_input;
292 u32 hw_if_index = ~0;
294 if (unformat_user (input, unformat_line_input, line_input))
296 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
298 if (unformat (line_input, "%U",
299 unformat_vnet_hw_interface, vnm, &hw_if_index))
302 return clib_error_return (0, "parse error: '%U'",
303 format_unformat_error, line_input);
305 unformat_free (line_input);
308 if (hw_if_index == ~0)
309 return clib_error_return (0, "please specify interface");
311 hi = vnet_get_hw_interface (vnm, hw_if_index);
312 dev_class = vnet_get_device_class (vnm, hi->dev_class_index);
313 if (dev_class->format_flow == 0)
314 return clib_error_return (0, "not supported");
316 vlib_cli_output (vm, "%U", dev_class->format_flow, hi->dev_instance, ~0, 0);
320 VLIB_CLI_COMMAND (show_flow_interface_command, static) = {
321 .path = "show flow interface",
322 .short_help = "show flow interface <interface name>",
323 .function = show_flow_interface,
326 static clib_error_t *
327 test_flow (vlib_main_t * vm, unformat_input_t * input,
328 vlib_cli_command_t * cmd_arg)
331 vnet_main_t *vnm = vnet_get_main ();
332 unformat_input_t _line_input, *line_input = &_line_input;
340 } action = FLOW_UNKNOWN_ACTION;
347 } flow_class = FLOW_UNKNOWN_CLASS;
349 u32 hw_if_index = ~0, flow_index = ~0;
351 u32 teid = 0, session_id = 0, spi = 0;
353 u32 queue_start = 0, queue_end = 0;
354 vnet_flow_type_t type = VNET_FLOW_TYPE_UNKNOWN;
355 ip4_address_and_mask_t ip4s = {}, in_ip4s = {};
356 ip4_address_and_mask_t ip4d = {}, in_ip4d = {};
357 ip6_address_and_mask_t ip6s = {}, in_ip6s = {};
358 ip6_address_and_mask_t ip6d = {}, in_ip6d = {};
359 ip_port_and_mask_t sport = {}, in_sport = {};
360 ip_port_and_mask_t dport = {}, in_dport = {};
361 ip_prot_and_mask_t protocol = {}, in_proto = {};
363 bool inner_ip4_set = false, inner_ip6_set = false;
364 bool tcp_udp_port_set = false, inner_port_set = false;
365 bool gtpc_set = false;
366 bool gtpu_set = false;
367 bool vni_set = false;
368 bool l2tpv3oip_set = false;
369 bool ipsec_esp_set = false, ipsec_ah_set = false;
370 u8 *rss_type[3] = { };
375 clib_memset (&flow, 0, sizeof (vnet_flow_t));
379 if (!unformat_user (input, unformat_line_input, line_input))
382 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
384 if (unformat (line_input, "add"))
386 else if (unformat (line_input, "del"))
388 else if (unformat (line_input, "enable"))
389 action = FLOW_ENABLE;
390 else if (unformat (line_input, "disable"))
391 action = FLOW_DISABLE;
392 else if (unformat (line_input, "spec %s", &spec))
394 else if (unformat (line_input, "mask %s", &mask))
396 else if (unformat (line_input, "eth-type %U",
397 unformat_ethernet_type_host_byte_order, ð_type))
398 flow_class = FLOW_ETHERNET_CLASS;
399 else if (unformat (line_input, "src-ip %U",
400 unformat_ip4_address_and_mask, &ip4s))
401 flow_class = FLOW_IPV4_CLASS;
402 else if (unformat (line_input, "dst-ip %U",
403 unformat_ip4_address_and_mask, &ip4d))
404 flow_class = FLOW_IPV4_CLASS;
405 else if (unformat (line_input, "in-src-ip %U",
406 unformat_ip4_address_and_mask, &in_ip4s))
407 inner_ip4_set = true;
408 else if (unformat (line_input, "in-dst-ip %U",
409 unformat_ip4_address_and_mask, &in_ip4d))
410 inner_ip4_set = true;
411 else if (unformat (line_input, "ip6-src-ip %U",
412 unformat_ip6_address_and_mask, &ip6s))
413 flow_class = FLOW_IPV6_CLASS;
414 else if (unformat (line_input, "ip6-dst-ip %U",
415 unformat_ip6_address_and_mask, &ip6d))
416 flow_class = FLOW_IPV6_CLASS;
417 else if (unformat (line_input, "in-ip6-src-ip %U",
418 unformat_ip6_address_and_mask, &in_ip6s))
419 inner_ip6_set = true;
420 else if (unformat (line_input, "in-ip6-dst-ip %U",
421 unformat_ip6_address_and_mask, &in_ip6d))
422 inner_ip6_set = true;
423 else if (unformat (line_input, "src-port %U", unformat_ip_port_and_mask,
425 tcp_udp_port_set = true;
426 else if (unformat (line_input, "dst-port %U", unformat_ip_port_and_mask,
428 tcp_udp_port_set = true;
431 (line_input, "proto %U", unformat_ip_protocol_and_mask,
434 else if (unformat (line_input, "in-src-port %U",
435 unformat_ip_port_and_mask, &in_sport))
436 inner_port_set = true;
437 else if (unformat (line_input, "in-dst-port %U",
438 unformat_ip_port_and_mask, &in_dport))
439 inner_port_set = true;
440 else if (unformat (line_input, "in-proto %U",
441 unformat_ip_protocol_and_mask, &in_proto))
443 else if (unformat (line_input, "gtpc teid %u", &teid))
445 else if (unformat (line_input, "gtpu teid %u", &teid))
447 else if (unformat (line_input, "vxlan vni %u", &vni))
449 else if (unformat (line_input, "session id %u", &session_id))
451 if (protocol.prot == IP_PROTOCOL_L2TP)
452 l2tpv3oip_set = true;
454 else if (unformat (line_input, "spi %u", &spi))
456 if (protocol.prot == IP_PROTOCOL_IPSEC_ESP)
457 ipsec_esp_set = true;
458 else if (protocol.prot == IP_PROTOCOL_IPSEC_AH)
461 else if (unformat (line_input, "index %u", &flow_index))
463 else if (unformat (line_input, "next-node %U", unformat_vlib_node, vm,
464 &flow.redirect_node_index))
465 flow.actions |= VNET_FLOW_ACTION_REDIRECT_TO_NODE;
466 else if (unformat (line_input, "mark %d", &flow.mark_flow_id))
467 flow.actions |= VNET_FLOW_ACTION_MARK;
468 else if (unformat (line_input, "buffer-advance %d",
469 &flow.buffer_advance))
470 flow.actions |= VNET_FLOW_ACTION_BUFFER_ADVANCE;
471 else if (unformat (line_input, "redirect-to-queue %d",
472 &flow.redirect_queue))
473 flow.actions |= VNET_FLOW_ACTION_REDIRECT_TO_QUEUE;
474 else if (unformat (line_input, "drop"))
475 flow.actions |= VNET_FLOW_ACTION_DROP;
476 else if (unformat (line_input, "rss function"))
482 else if (unformat (line_input, s)) \
483 flow.rss_fun = VNET_RSS_FUNC_##f;
489 return clib_error_return (0, "unknown input `%U'",
490 format_unformat_error, line_input);
493 flow.actions |= VNET_FLOW_ACTION_RSS;
495 else if (unformat (line_input, "rss types"))
502 if (unformat (line_input, "%s use %s and %s",
503 &rss_type[0], &rss_type[1], &rss_type[2]))
506 (line_input, "%s use %s", &rss_type[0], &rss_type[1]))
508 else if (unformat (line_input, "%s", &rss_type[0]))
513 else if (!clib_strcmp(c, (const char *)type_str)) \
514 flow.rss_types |= (1ULL<<a);
516 #define check_rss_types(_str) \
521 foreach_flow_rss_types \
524 return clib_error_return (0, "parse error: '%U'", \
525 format_unformat_error, line_input); \
529 check_rss_types (rss_type[0])
530 check_rss_types (rss_type[1]) check_rss_types (rss_type[2])
532 flow.actions |= VNET_FLOW_ACTION_RSS;
534 else if (unformat (line_input, "rss queues"))
536 if (unformat (line_input, "%d to %d", &queue_start, &queue_end))
540 return clib_error_return (0, "unknown input `%U'",
541 format_unformat_error, line_input);
544 flow.queue_index = queue_start;
545 flow.queue_num = queue_end - queue_start + 1;
547 flow.actions |= VNET_FLOW_ACTION_RSS;
549 else if (unformat (line_input, "%U", unformat_vnet_hw_interface, vnm,
553 return clib_error_return (0, "parse error: '%U'",
554 format_unformat_error, line_input);
557 unformat_free (line_input);
559 if (hw_if_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE))
560 return clib_error_return (0, "Please specify interface name");
562 if (flow_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE ||
564 return clib_error_return (0, "Please specify flow index");
569 if (flow.actions == 0)
570 return clib_error_return (0, "Please specify at least one action");
572 /* Adjust the flow type */
575 case FLOW_ETHERNET_CLASS:
576 type = VNET_FLOW_TYPE_ETHERNET;
579 case FLOW_IPV4_CLASS:
582 type = VNET_FLOW_TYPE_IP4_GTPC;
583 protocol.prot = IP_PROTOCOL_UDP;
587 type = VNET_FLOW_TYPE_IP4_GTPU;
588 protocol.prot = IP_PROTOCOL_UDP;
592 type = VNET_FLOW_TYPE_IP4_VXLAN;
593 protocol.prot = IP_PROTOCOL_UDP;
595 else if (l2tpv3oip_set)
596 type = VNET_FLOW_TYPE_IP4_L2TPV3OIP;
597 else if (ipsec_esp_set)
598 type = VNET_FLOW_TYPE_IP4_IPSEC_ESP;
599 else if (ipsec_ah_set)
600 type = VNET_FLOW_TYPE_IP4_IPSEC_AH;
601 else if (tcp_udp_port_set)
602 type = VNET_FLOW_TYPE_IP4_N_TUPLE;
603 else if (inner_ip4_set)
606 type = VNET_FLOW_TYPE_IP4_IP4_N_TUPLE;
608 type = VNET_FLOW_TYPE_IP4_IP4;
609 protocol.prot = IP_PROTOCOL_IP_IN_IP;
611 else if (inner_ip6_set)
614 type = VNET_FLOW_TYPE_IP4_IP6_N_TUPLE;
616 type = VNET_FLOW_TYPE_IP4_IP6;
617 protocol.prot = IP_PROTOCOL_IPV6;
620 type = VNET_FLOW_TYPE_IP4;
622 case FLOW_IPV6_CLASS:
623 if (tcp_udp_port_set)
624 type = VNET_FLOW_TYPE_IP6_N_TUPLE;
626 type = VNET_FLOW_TYPE_IP6_VXLAN;
627 else if (inner_ip4_set)
630 type = VNET_FLOW_TYPE_IP6_IP4_N_TUPLE;
632 type = VNET_FLOW_TYPE_IP6_IP4;
633 protocol.prot = IP_PROTOCOL_IP_IN_IP;
635 else if (inner_ip6_set)
638 type = VNET_FLOW_TYPE_IP6_IP6_N_TUPLE;
640 type = VNET_FLOW_TYPE_IP6_IP6;
641 protocol.prot = IP_PROTOCOL_IPV6;
644 type = VNET_FLOW_TYPE_IP6;
650 type = VNET_FLOW_TYPE_GENERIC;
653 return clib_error_return (0,
654 "Please specify a supported flow type");
657 /* Assign specific field values per flow type */
658 if (flow_class == FLOW_ETHERNET_CLASS)
660 flow.ethernet.eth_hdr.type = eth_type;
662 else if (flow_class == FLOW_IPV4_CLASS)
664 vnet_flow_ip4_t *ip4_ptr = &flow.ip4;
666 clib_memcpy (&ip4_ptr->src_addr, &ip4s,
667 sizeof (ip4_address_and_mask_t));
668 clib_memcpy (&ip4_ptr->dst_addr, &ip4d,
669 sizeof (ip4_address_and_mask_t));
670 ip4_ptr->protocol.prot = protocol.prot;
672 /* In this cli, we use the protocol.mask only when the flow type is
673 * VNET_FLOW_TYPE_IP4/IP6. For other cases, the IP protocol is just
674 * used to identify the next layer type: e.g. UDP/TCP or IPSEC_ESP
676 if (type == VNET_FLOW_TYPE_IP4)
677 ip4_ptr->protocol.mask = protocol.mask;
679 switch (protocol.prot)
682 case IP_PROTOCOL_TCP:
683 case IP_PROTOCOL_UDP:
684 flow.ip4_n_tuple.src_port = sport;
685 flow.ip4_n_tuple.dst_port = dport;
687 if (type == VNET_FLOW_TYPE_IP4_GTPC)
688 flow.ip4_gtpc.teid = teid;
689 else if (type == VNET_FLOW_TYPE_IP4_GTPU)
690 flow.ip4_gtpu.teid = teid;
691 else if (type == VNET_FLOW_TYPE_IP4_VXLAN)
692 flow.ip4_vxlan.vni = vni;
694 case IP_PROTOCOL_L2TP:
695 flow.ip4_l2tpv3oip.session_id = session_id;
697 case IP_PROTOCOL_IPSEC_ESP:
698 flow.ip4_ipsec_esp.spi = spi;
700 case IP_PROTOCOL_IPSEC_AH:
701 flow.ip4_ipsec_esp.spi = spi;
703 case IP_PROTOCOL_IP_IN_IP:
704 clib_memcpy (&flow.ip4_ip4.in_src_addr, &in_ip4s,
705 sizeof (ip4_address_and_mask_t));
706 clib_memcpy (&flow.ip4_ip4.in_dst_addr, &in_ip4d,
707 sizeof (ip4_address_and_mask_t));
708 if (type == VNET_FLOW_TYPE_IP4_IP4_N_TUPLE)
710 flow.ip4_ip4.in_protocol.prot = in_proto.prot;
711 flow.ip4_ip4_n_tuple.in_src_port = in_sport;
712 flow.ip4_ip4_n_tuple.in_dst_port = in_dport;
715 case IP_PROTOCOL_IPV6:
716 clib_memcpy (&flow.ip4_ip6.in_src_addr, &in_ip6s,
717 sizeof (ip6_address_and_mask_t));
718 clib_memcpy (&flow.ip4_ip6.in_dst_addr, &in_ip6d,
719 sizeof (ip6_address_and_mask_t));
720 if (type == VNET_FLOW_TYPE_IP4_IP6_N_TUPLE)
722 flow.ip4_ip6.in_protocol.prot = in_proto.prot;
723 flow.ip4_ip6_n_tuple.in_src_port = in_sport;
724 flow.ip4_ip6_n_tuple.in_dst_port = in_dport;
731 else if (flow_class == FLOW_IPV6_CLASS)
733 vnet_flow_ip6_t *ip6_ptr = &flow.ip6;
735 clib_memcpy (&flow.ip6_n_tuple.src_addr, &ip6s,
736 sizeof (ip6_address_and_mask_t));
737 clib_memcpy (&flow.ip6_n_tuple.dst_addr, &ip6d,
738 sizeof (ip6_address_and_mask_t));
740 ip6_ptr->protocol.prot = protocol.prot;
742 /* In this cli, we use the protocol.mask only when the flow type is
743 * VNET_FLOW_TYPE_IP4/IP6. For other cases, the IP protocol is just
744 * used to identify the next layer type: e.g. UDP/TCP or IPSEC_ESP
746 if (type == VNET_FLOW_TYPE_IP6)
747 ip6_ptr->protocol.mask = protocol.mask;
749 switch (protocol.prot)
752 case IP_PROTOCOL_TCP:
753 case IP_PROTOCOL_UDP:
754 flow.ip6_n_tuple.src_port = sport;
755 flow.ip6_n_tuple.dst_port = dport;
757 if (type == VNET_FLOW_TYPE_IP6_VXLAN)
758 flow.ip6_vxlan.vni = vni;
760 case IP_PROTOCOL_IP_IN_IP:
761 clib_memcpy (&flow.ip6_ip4.in_src_addr, &in_ip4s,
762 sizeof (ip4_address_and_mask_t));
763 clib_memcpy (&flow.ip6_ip4.in_dst_addr, &in_ip4d,
764 sizeof (ip4_address_and_mask_t));
765 if (type == VNET_FLOW_TYPE_IP6_IP4_N_TUPLE)
767 flow.ip6_ip4.in_protocol.prot = in_proto.prot;
768 flow.ip6_ip4_n_tuple.in_src_port = in_sport;
769 flow.ip6_ip4_n_tuple.in_dst_port = in_dport;
772 case IP_PROTOCOL_IPV6:
773 clib_memcpy (&flow.ip6_ip6.in_src_addr, &in_ip6s,
774 sizeof (ip6_address_and_mask_t));
775 clib_memcpy (&flow.ip6_ip6.in_dst_addr, &in_ip6d,
776 sizeof (ip6_address_and_mask_t));
777 if (type == VNET_FLOW_TYPE_IP6_IP6_N_TUPLE)
779 flow.ip6_ip6.in_protocol.prot = in_proto.prot;
780 flow.ip6_ip6_n_tuple.in_src_port = in_sport;
781 flow.ip6_ip6_n_tuple.in_dst_port = in_dport;
788 if (type == VNET_FLOW_TYPE_GENERIC)
790 clib_memcpy (flow.generic.pattern.spec, spec,
791 sizeof (flow.generic.pattern.spec));
792 clib_memcpy (flow.generic.pattern.mask, mask,
793 sizeof (flow.generic.pattern.mask));
797 rv = vnet_flow_add (vnm, &flow, &flow_index);
799 vlib_cli_output (vm, "flow %u added", flow_index);
803 rv = vnet_flow_del (vnm, flow_index);
806 rv = vnet_flow_enable (vnm, flow_index, hw_if_index);
809 rv = vnet_flow_disable (vnm, flow_index, hw_if_index);
812 return clib_error_return (0, "please specify action (add, del, enable,"
817 return clib_error_return (0, "flow error: %U", format_flow_error, rv);
822 VLIB_CLI_COMMAND (test_flow_command, static) = {
824 .short_help = "test flow [add|del|enable|disable] [index <id>] "
825 "[src-ip <ip-addr/mask>] [dst-ip <ip-addr/mask>] "
826 "[ip6-src-ip <ip-addr/mask>] [ip6-dst-ip <ip-addr/mask>] "
827 "[src-port <port/mask>] [dst-port <port/mask>] "
828 "[proto <ip-proto>] "
829 "[gtpc teid <teid>] [gtpu teid <teid>] [vxlan <vni>] "
830 "[session id <session>] [spi <spi>]"
831 "[spec <spec string>] [mask <mask string>]"
832 "[next-node <node>] [mark <id>] [buffer-advance <len>] "
833 "[redirect-to-queue <queue>] [drop] "
834 "[rss function <name>] [rss types <flow type>]"
835 "[rss queues <queue_start> to <queue_end>]",
836 .function = test_flow,
840 format_flow_match_element (u8 * s, va_list * args)
842 char *type = va_arg (*args, char *);
843 void *ptr = va_arg (*args, void *);
845 if (strncmp (type, "u8", 2) == 0)
846 return format (s, "%d", *(u8 *) ptr);
848 if (strncmp (type, "u16", 3) == 0)
849 return format (s, "%d", *(u16 *) ptr);
851 if (strncmp (type, "u32", 3) == 0)
852 return format (s, "%d", *(u32 *) ptr);
854 if (strncmp (type, "ethernet_header_t", 13) == 0)
856 ethernet_max_header_t m;
857 memset (&m, 0, sizeof (m));
858 m.ethernet = *(ethernet_header_t *) ptr;
859 /* convert the ethernet type to net order */
860 m.ethernet.type = clib_host_to_net_u16 (m.ethernet.type);
861 return format (s, "%U", format_ethernet_header, &m);
864 if (strncmp (type, "ip4_address_t", 13) == 0)
865 return format (s, "%U", format_ip4_address, ptr);
867 if (strncmp (type, "ip4_address_and_mask_t", 13) == 0)
868 return format (s, "%U", format_ip4_address_and_mask, ptr);
870 if (strncmp (type, "ip6_address_t", 13) == 0)
871 return format (s, "%U", format_ip6_address, ptr);
873 if (strncmp (type, "ip6_address_and_mask_t", 13) == 0)
874 return format (s, "%U", format_ip6_address_and_mask, ptr);
876 if (strncmp (type, "ip_prot_and_mask_t", 13) == 0)
877 return format (s, "%U", format_ip_protocol_and_mask, ptr);
879 if (strncmp (type, "ip_port_and_mask_t", 18) == 0)
880 return format (s, "%U", format_ip_port_and_mask, ptr);
882 s = format (s, "unknown type '%s'", type);
886 #define _fe(a,b) s2 = format (s2, "%s%s %U", s2 ? ", ":"", #b, \
887 format_flow_match_element, #a, &f->b);
889 u8 * format_flow_match_##b (u8 * s, va_list * args) \
891 vnet_flow_##b##_t *f = __builtin_va_arg (*args, vnet_flow_##b##_t *); \
893 foreach_flow_entry_##b \
894 s = format (s, "%v", s2);; \
902 format_flow_match (u8 * s, va_list * args)
904 vnet_flow_t *f = va_arg (*args, vnet_flow_t *);
907 if (f->type == VNET_FLOW_TYPE_##a) \
908 return format (s, "%U", format_flow_match_##b, &f->b);
916 format_flow (u8 * s, va_list * args)
918 vlib_main_t *vm = vlib_get_main ();
919 vnet_flow_t *f = va_arg (*args, vnet_flow_t *);
920 u32 indent = format_get_indent (s);
923 s = format (s, "flow-index %u type %s active %u",
924 f->index, flow_type_strings[f->type],
925 hash_elts (f->private_data)),
926 s = format (s, "\n%Umatch: %U", format_white_space, indent + 2,
927 format_flow_match, f);
928 s = format (s, "\n%Uaction: %U", format_white_space, indent + 2,
929 format_flow_actions, f->actions);
931 if (f->actions & VNET_FLOW_ACTION_DROP)
932 t = format (t, "%sdrop", t ? ", " : "");
934 if (f->actions & VNET_FLOW_ACTION_MARK)
935 t = format (t, "%smark %u", t ? ", " : "", f->mark_flow_id);
937 if (f->actions & VNET_FLOW_ACTION_REDIRECT_TO_QUEUE)
939 format (t, "%sredirect-to-queue %u", t ? ", " : "", f->redirect_queue);
941 if (f->actions & VNET_FLOW_ACTION_REDIRECT_TO_NODE)
942 t = format (t, "%snext-node %U", t ? ", " : "",
943 format_vlib_node_name, vm, f->redirect_node_index);
945 if (f->actions & VNET_FLOW_ACTION_BUFFER_ADVANCE)
946 t = format (t, "%sbuffer-advance %d", t ? ", " : "", f->buffer_advance);
948 if (f->actions & VNET_FLOW_ACTION_RSS)
950 t = format (t, "%srss function %U", t ? ", " : "",
951 format_rss_function, f->rss_fun);
952 t = format (t, "%srss types %U", t ? ", " : "",
953 format_rss_types, f->rss_types);
958 s = format (s, "\n%U%v", format_white_space, indent + 4, t);
966 * fd.io coding-style-patch-verification: ON
969 * eval: (c-set-style "gnu")