blob: e4b73717241b2bb93354f72559d63da747644a23 [file] [log] [blame]
Damjan Mariona35cc142018-03-16 01:25:27 +01001/*
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 */
Chenmin Sun34bfa502020-07-27 17:40:17 +080015#include <stddef.h>
Damjan Mariona35cc142018-03-16 01:25:27 +010016
17#include <vnet/vnet.h>
18#include <vnet/devices/devices.h>
19#include <vnet/ip/ip.h>
20#include <vnet/ethernet/ethernet.h>
Chenmin Sun34bfa502020-07-27 17:40:17 +080021#include <vnet/ethernet/packet.h>
Damjan Mariona35cc142018-03-16 01:25:27 +010022#include <vnet/flow/flow.h>
23
24static format_function_t format_flow;
25
26uword
27unformat_ip_port_and_mask (unformat_input_t * input, va_list * args)
28{
29 ip_port_and_mask_t *pm = va_arg (*args, ip_port_and_mask_t *);
30 u32 port = 0, mask = 0;
31
32 if (unformat (input, "any"))
33 ;
34 else if (unformat (input, "%u/%u", &port, &mask))
35 ;
36 else if (unformat (input, "%u/0x%x", &port, &mask))
37 ;
38 else if (unformat (input, "%u", &port))
39 mask = 0xffff;
40 else
41 return 0;
42
43 if (port > 0xffff || mask > 0xffff)
44 return 0;
45
46 pm->port = port;
47 pm->mask = mask;
48 return 1;
49}
50
51u8 *
52format_ip_port_and_mask (u8 * s, va_list * args)
53{
54 ip_port_and_mask_t *pm = va_arg (*args, ip_port_and_mask_t *);
55
56 if (pm->port == 0 && pm->mask == 0)
57 return format (s, "any");
58
59 if (pm->mask == 0xffff)
60 return format (s, "%u", pm->port);
61
62 return format (s, "%u/0x%x", pm->port, pm->mask);
63}
64
Chenmin Sun34bfa502020-07-27 17:40:17 +080065uword
66unformat_ip_protocol_and_mask (unformat_input_t * input, va_list * args)
67{
68 ip_prot_and_mask_t *pm = va_arg (*args, ip_prot_and_mask_t *);
69 u32 prot = 0, mask = 0;
70
71 if (unformat (input, "any"))
72 ;
73 else if (unformat (input, "%U", unformat_ip_protocol, &prot))
74 mask = 0xFF;
75 else if (unformat (input, "%u", &prot))
76 mask = 0xFF;
77 else
78 return 0;
79
80 if (prot > 0XFF || mask > 0xFF)
81 return 0;
82
83 pm->prot = prot;
84 pm->mask = mask;
85 return 1;
86}
87
88u8 *
89format_ip_protocol_and_mask (u8 * s, va_list * args)
90{
91 ip_prot_and_mask_t *pm = va_arg (*args, ip_prot_and_mask_t *);
92
93 if (pm->prot == 0 && pm->mask == 0)
94 return format (s, "any");
95
96 return format (s, "%U", format_ip_protocol, pm->prot);
97}
98
Damjan Mariona35cc142018-03-16 01:25:27 +010099u8 *
100format_flow_error (u8 * s, va_list * args)
101{
102 int error = va_arg (*args, int);
103
104 if (error == 0)
105 return format (s, "no error");
106
107#define _(v,n,str) if (error == v) return format (s, #str);
108 foreach_flow_error;
109#undef _
110
111 return format (s, "unknown error (%d)", error);
112}
113
114u8 *
115format_flow_actions (u8 * s, va_list * args)
116{
117 u32 actions = va_arg (*args, u32);
118 u8 *t = 0;
119
120#define _(a, b, c) if (actions & (1 << a)) \
121 t = format (t, "%s%s", t ? " ":"", c);
122 foreach_flow_action
123#undef _
124 s = format (s, "%v", t);
125 vec_free (t);
126 return s;
127}
128
Eyal Barid3de7562018-05-31 11:30:16 +0300129u8 *
130format_flow_enabled_hw (u8 * s, va_list * args)
131{
132 u32 flow_index = va_arg (*args, u32);
133 vnet_flow_t *f = vnet_get_flow (flow_index);
134 if (f == 0)
135 return format (s, "not found");
136
137 u8 *t = 0;
138 u32 hw_if_index;
139 uword private_data;
140 vnet_main_t *vnm = vnet_get_main ();
Eyal Barid3de7562018-05-31 11:30:16 +0300141 hash_foreach (hw_if_index, private_data, f->private_data,
142 ({
143 t = format (t, "%s%U", t ? ", " : "",
144 format_vnet_hw_if_index_name, vnm, hw_if_index);
145 }));
Eyal Barid3de7562018-05-31 11:30:16 +0300146 s = format (s, "%v", t);
147 vec_free (t);
148 return s;
149}
150
Chenmin Sun34bfa502020-07-27 17:40:17 +0800151u8 *
152format_rss_function (u8 * s, va_list * args)
153{
154 vnet_rss_function_t func = va_arg (*args, vnet_rss_function_t);
155
156 if (0)
157 ;
158#undef _
159#define _(f, n) \
160 else if (func == VNET_RSS_FUNC_##f) \
161 return format (s, n);
162
163 foreach_rss_function
164#undef _
165 return format (s, "unknown");
166}
167
168u8 *
169format_rss_types (u8 * s, va_list * args)
170{
171 u64 type = va_arg (*args, u64);
172
173#undef _
174#define _(a,b,c) \
175 if (type & (1UL<<a)) \
176 s = format (s, "%s ", c);
177
178 foreach_flow_rss_types
179#undef _
180 return s;
181}
182
Damjan Mariona35cc142018-03-16 01:25:27 +0100183static const char *flow_type_strings[] = { 0,
184#define _(a,b,c) c,
185 foreach_flow_type
186#undef _
187};
188
189static clib_error_t *
190show_flow_entry (vlib_main_t * vm, unformat_input_t * input,
191 vlib_cli_command_t * cmd_arg)
192{
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;
198 vnet_flow_t *f;
199 uword private_data;
200 u32 index = ~0, hw_if_index;
201
202 if (!unformat_user (input, unformat_line_input, line_input))
203 goto no_args;
204
205 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
206 {
207 if (unformat (line_input, "index %u", &index))
208 ;
209 else
210 return clib_error_return (0, "parse error: '%U'",
211 format_unformat_error, line_input);
212 }
213
214 unformat_free (line_input);
215
216 if (index != ~0)
217 {
218 if ((f = vnet_get_flow (index)) == 0)
219 return clib_error_return (0, "no such flow");
220
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);
Ting Xu3a366822021-09-16 08:37:25 +0000224 if (f->type == VNET_FLOW_TYPE_GENERIC)
225 {
226 vlib_cli_output (vm, "%s: %s", "spec", f->generic.pattern.spec);
227 vlib_cli_output (vm, "%s: %s", "mask", f->generic.pattern.mask);
228 }
Damjan Mariona35cc142018-03-16 01:25:27 +0100229 hash_foreach (hw_if_index, private_data, f->private_data,
230 ({
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);
238 }));
Damjan Mariona35cc142018-03-16 01:25:27 +0100239 return 0;
240 }
241
242no_args:
Damjan Marionb2c31b62020-12-13 21:47:40 +0100243 pool_foreach (f, fm->global_flow_pool)
Damjan Mariona35cc142018-03-16 01:25:27 +0100244 {
245 vlib_cli_output (vm, "%U\n", format_flow, f);
Ting Xu3a366822021-09-16 08:37:25 +0000246 if (f->type == VNET_FLOW_TYPE_GENERIC)
247 {
248 vlib_cli_output (vm, "%s: %s", "spec", f->generic.pattern.spec);
249 vlib_cli_output (vm, "%s: %s", "mask", f->generic.pattern.mask);
250 }
Damjan Marionb2c31b62020-12-13 21:47:40 +0100251 }
Damjan Mariona35cc142018-03-16 01:25:27 +0100252
253 return 0;
254}
255
Damjan Mariona35cc142018-03-16 01:25:27 +0100256VLIB_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,
260};
Damjan Mariona35cc142018-03-16 01:25:27 +0100261
262static clib_error_t *
263show_flow_ranges (vlib_main_t * vm, unformat_input_t * input,
264 vlib_cli_command_t * cmd_arg)
265{
266 vnet_flow_main_t *fm = &flow_main;
267 vnet_flow_range_t *r = 0;
268
269 vlib_cli_output (vm, "%8s %8s %s", "Start", "Count", "Owner");
270
Damjan Mariona35cc142018-03-16 01:25:27 +0100271 vec_foreach (r, fm->ranges)
272 {
273 vlib_cli_output (vm, "%8u %8u %s", r->start, r->count, r->owner);
274 };
Damjan Mariona35cc142018-03-16 01:25:27 +0100275 return 0;
276}
277
Damjan Mariona35cc142018-03-16 01:25:27 +0100278VLIB_CLI_COMMAND (show_flow_ranges_command, static) = {
279 .path = "show flow ranges",
280 .short_help = "show flow ranges",
281 .function = show_flow_ranges,
282};
Damjan Mariona35cc142018-03-16 01:25:27 +0100283
284static clib_error_t *
285show_flow_interface (vlib_main_t * vm, unformat_input_t * input,
286 vlib_cli_command_t * cmd_arg)
287{
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;
293
294 if (unformat_user (input, unformat_line_input, line_input))
295 {
296 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
297 {
298 if (unformat (line_input, "%U",
299 unformat_vnet_hw_interface, vnm, &hw_if_index))
300 ;
301 else
302 return clib_error_return (0, "parse error: '%U'",
303 format_unformat_error, line_input);
304 }
305 unformat_free (line_input);
306 }
307
308 if (hw_if_index == ~0)
309 return clib_error_return (0, "please specify interface");
310
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");
315
316 vlib_cli_output (vm, "%U", dev_class->format_flow, hi->dev_instance, ~0, 0);
317 return 0;
318}
319
Damjan Mariona35cc142018-03-16 01:25:27 +0100320VLIB_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,
324};
Damjan Mariona35cc142018-03-16 01:25:27 +0100325
326static clib_error_t *
327test_flow (vlib_main_t * vm, unformat_input_t * input,
328 vlib_cli_command_t * cmd_arg)
329{
330 vnet_flow_t flow;
331 vnet_main_t *vnm = vnet_get_main ();
332 unformat_input_t _line_input, *line_input = &_line_input;
333 enum
334 {
335 FLOW_UNKNOWN_ACTION,
336 FLOW_ADD,
337 FLOW_DEL,
338 FLOW_ENABLE,
339 FLOW_DISABLE
340 } action = FLOW_UNKNOWN_ACTION;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800341 enum
342 {
343 FLOW_UNKNOWN_CLASS,
344 FLOW_ETHERNET_CLASS,
345 FLOW_IPV4_CLASS,
346 FLOW_IPV6_CLASS,
347 } flow_class = FLOW_UNKNOWN_CLASS;
348
Chenmin Sunbf85a982019-10-18 07:35:16 +0800349 u32 hw_if_index = ~0, flow_index = ~0;
Damjan Mariona35cc142018-03-16 01:25:27 +0100350 int rv;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800351 u32 teid = 0, session_id = 0, spi = 0;
Chenmin Sun91f102e2020-12-08 00:42:43 +0800352 u32 vni = 0;
Ting Xub95e6d42022-03-25 03:45:07 +0000353 u32 queue_start = 0, queue_end = 0;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800354 vnet_flow_type_t type = VNET_FLOW_TYPE_UNKNOWN;
Xinyao Cai99d3a402023-02-28 14:44:58 +0800355 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 = {};
Chenmin Sun4ff8d612019-12-15 01:16:45 +0800362 u16 eth_type;
Xinyao Cai99d3a402023-02-28 14:44:58 +0800363 bool inner_ip4_set = false, inner_ip6_set = false;
364 bool tcp_udp_port_set = false, inner_port_set = false;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800365 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;
Chenmin Sun24e2c502020-02-28 22:49:37 +0800370 u8 *rss_type[3] = { };
371 u8 *type_str = NULL;
Ting Xu3a366822021-09-16 08:37:25 +0000372 u8 *spec = NULL;
373 u8 *mask = NULL;
Damjan Mariona35cc142018-03-16 01:25:27 +0100374
Dave Barachb7b92992018-10-17 10:38:51 -0400375 clib_memset (&flow, 0, sizeof (vnet_flow_t));
Damjan Mariona35cc142018-03-16 01:25:27 +0100376 flow.index = ~0;
377 flow.actions = 0;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800378
Damjan Mariona35cc142018-03-16 01:25:27 +0100379 if (!unformat_user (input, unformat_line_input, line_input))
380 return 0;
381
382 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
383 {
384 if (unformat (line_input, "add"))
385 action = FLOW_ADD;
386 else if (unformat (line_input, "del"))
387 action = FLOW_DEL;
388 else if (unformat (line_input, "enable"))
389 action = FLOW_ENABLE;
390 else if (unformat (line_input, "disable"))
391 action = FLOW_DISABLE;
Ting Xu3a366822021-09-16 08:37:25 +0000392 else if (unformat (line_input, "spec %s", &spec))
393 ;
394 else if (unformat (line_input, "mask %s", &mask))
395 ;
Chenmin Sun4ff8d612019-12-15 01:16:45 +0800396 else if (unformat (line_input, "eth-type %U",
397 unformat_ethernet_type_host_byte_order, &eth_type))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800398 flow_class = FLOW_ETHERNET_CLASS;
Damjan Mariona35cc142018-03-16 01:25:27 +0100399 else if (unformat (line_input, "src-ip %U",
Chenmin Sunbf85a982019-10-18 07:35:16 +0800400 unformat_ip4_address_and_mask, &ip4s))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800401 flow_class = FLOW_IPV4_CLASS;
Damjan Mariona35cc142018-03-16 01:25:27 +0100402 else if (unformat (line_input, "dst-ip %U",
Chenmin Sunbf85a982019-10-18 07:35:16 +0800403 unformat_ip4_address_and_mask, &ip4d))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800404 flow_class = FLOW_IPV4_CLASS;
Xinyao Cai99d3a402023-02-28 14:44:58 +0800405 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;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800411 else if (unformat (line_input, "ip6-src-ip %U",
412 unformat_ip6_address_and_mask, &ip6s))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800413 flow_class = FLOW_IPV6_CLASS;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800414 else if (unformat (line_input, "ip6-dst-ip %U",
415 unformat_ip6_address_and_mask, &ip6d))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800416 flow_class = FLOW_IPV6_CLASS;
Xinyao Cai99d3a402023-02-28 14:44:58 +0800417 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;
Damjan Mariona35cc142018-03-16 01:25:27 +0100423 else if (unformat (line_input, "src-port %U", unformat_ip_port_and_mask,
Chenmin Sunbf85a982019-10-18 07:35:16 +0800424 &sport))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800425 tcp_udp_port_set = true;
Damjan Mariona35cc142018-03-16 01:25:27 +0100426 else if (unformat (line_input, "dst-port %U", unformat_ip_port_and_mask,
Chenmin Sunbf85a982019-10-18 07:35:16 +0800427 &dport))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800428 tcp_udp_port_set = true;
429 else
430 if (unformat
431 (line_input, "proto %U", unformat_ip_protocol_and_mask,
432 &protocol))
Chenmin Sunbf85a982019-10-18 07:35:16 +0800433 ;
Xinyao Cai99d3a402023-02-28 14:44:58 +0800434 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))
442 ;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800443 else if (unformat (line_input, "gtpc teid %u", &teid))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800444 gtpc_set = true;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800445 else if (unformat (line_input, "gtpu teid %u", &teid))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800446 gtpu_set = true;
447 else if (unformat (line_input, "vxlan vni %u", &vni))
448 vni_set = true;
Chenmin Sun8b43aaa2020-04-15 01:55:58 +0800449 else if (unformat (line_input, "session id %u", &session_id))
450 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800451 if (protocol.prot == IP_PROTOCOL_L2TP)
452 l2tpv3oip_set = true;
Chenmin Sun8b43aaa2020-04-15 01:55:58 +0800453 }
Chenmin Sund4c36662020-06-22 18:21:31 +0800454 else if (unformat (line_input, "spi %u", &spi))
455 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800456 if (protocol.prot == IP_PROTOCOL_IPSEC_ESP)
457 ipsec_esp_set = true;
458 else if (protocol.prot == IP_PROTOCOL_IPSEC_AH)
459 ipsec_ah_set = true;
Chenmin Sund4c36662020-06-22 18:21:31 +0800460 }
Damjan Mariona35cc142018-03-16 01:25:27 +0100461 else if (unformat (line_input, "index %u", &flow_index))
462 ;
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;
Chenmin Sune8c9f4f2019-10-15 20:36:16 +0800471 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;
Chenmin Sun24e2c502020-02-28 22:49:37 +0800476 else if (unformat (line_input, "rss function"))
477 {
478 if (0)
479 ;
480#undef _
Chenmin Sun34bfa502020-07-27 17:40:17 +0800481#define _(f, s) \
482 else if (unformat (line_input, s)) \
483 flow.rss_fun = VNET_RSS_FUNC_##f;
Chenmin Sun24e2c502020-02-28 22:49:37 +0800484
485 foreach_rss_function
486#undef _
487 else
488 {
489 return clib_error_return (0, "unknown input `%U'",
490 format_unformat_error, line_input);
491 }
492
493 flow.actions |= VNET_FLOW_ACTION_RSS;
494 }
495 else if (unformat (line_input, "rss types"))
496 {
497 rss_type[0] = NULL;
498 rss_type[1] = NULL;
499 rss_type[2] = NULL;
500 type_str = NULL;
501
502 if (unformat (line_input, "%s use %s and %s",
503 &rss_type[0], &rss_type[1], &rss_type[2]))
504 ;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800505 else if (unformat
506 (line_input, "%s use %s", &rss_type[0], &rss_type[1]))
Chenmin Sun24e2c502020-02-28 22:49:37 +0800507 ;
508 else if (unformat (line_input, "%s", &rss_type[0]))
509 ;
510
511#undef _
512#define _(a,b,c) \
Chenmin Sun34bfa502020-07-27 17:40:17 +0800513 else if (!clib_strcmp(c, (const char *)type_str)) \
514 flow.rss_types |= (1ULL<<a);
Chenmin Sun24e2c502020-02-28 22:49:37 +0800515
516#define check_rss_types(_str) \
Chenmin Sun34bfa502020-07-27 17:40:17 +0800517 if (_str != NULL) {\
518 type_str = _str;\
519 if (0) \
520 ; \
521 foreach_flow_rss_types \
522 else \
523 { \
524 return clib_error_return (0, "parse error: '%U'", \
525 format_unformat_error, line_input); \
526 } \
527 }
Chenmin Sun24e2c502020-02-28 22:49:37 +0800528
529 check_rss_types (rss_type[0])
530 check_rss_types (rss_type[1]) check_rss_types (rss_type[2])
531#undef _
532 flow.actions |= VNET_FLOW_ACTION_RSS;
533 }
Ting Xub95e6d42022-03-25 03:45:07 +0000534 else if (unformat (line_input, "rss queues"))
535 {
536 if (unformat (line_input, "%d to %d", &queue_start, &queue_end))
537 ;
538 else
539 {
540 return clib_error_return (0, "unknown input `%U'",
541 format_unformat_error, line_input);
542 }
543
544 flow.queue_index = queue_start;
545 flow.queue_num = queue_end - queue_start + 1;
546
547 flow.actions |= VNET_FLOW_ACTION_RSS;
548 }
Damjan Mariona35cc142018-03-16 01:25:27 +0100549 else if (unformat (line_input, "%U", unformat_vnet_hw_interface, vnm,
550 &hw_if_index))
551 ;
552 else
553 return clib_error_return (0, "parse error: '%U'",
554 format_unformat_error, line_input);
555 }
556
557 unformat_free (line_input);
558
559 if (hw_if_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE))
560 return clib_error_return (0, "Please specify interface name");
561
562 if (flow_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE ||
563 action == FLOW_DEL))
564 return clib_error_return (0, "Please specify flow index");
565
566 switch (action)
567 {
568 case FLOW_ADD:
Damjan Mariona35cc142018-03-16 01:25:27 +0100569 if (flow.actions == 0)
570 return clib_error_return (0, "Please specify at least one action");
Chenmin Sunbf85a982019-10-18 07:35:16 +0800571
572 /* Adjust the flow type */
Chenmin Sun34bfa502020-07-27 17:40:17 +0800573 switch (flow_class)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800574 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800575 case FLOW_ETHERNET_CLASS:
576 type = VNET_FLOW_TYPE_ETHERNET;
577 break;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800578
Chenmin Sun34bfa502020-07-27 17:40:17 +0800579 case FLOW_IPV4_CLASS:
580 if (gtpc_set)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800581 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800582 type = VNET_FLOW_TYPE_IP4_GTPC;
583 protocol.prot = IP_PROTOCOL_UDP;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800584 }
Chenmin Sun34bfa502020-07-27 17:40:17 +0800585 else if (gtpu_set)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800586 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800587 type = VNET_FLOW_TYPE_IP4_GTPU;
588 protocol.prot = IP_PROTOCOL_UDP;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800589 }
Chenmin Sun34bfa502020-07-27 17:40:17 +0800590 else if (vni_set)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800591 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800592 type = VNET_FLOW_TYPE_IP4_VXLAN;
593 protocol.prot = IP_PROTOCOL_UDP;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800594 }
Chenmin Sun34bfa502020-07-27 17:40:17 +0800595 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;
Xinyao Cai99d3a402023-02-28 14:44:58 +0800603 else if (inner_ip4_set)
604 {
605 if (inner_port_set)
606 type = VNET_FLOW_TYPE_IP4_IP4_N_TUPLE;
607 else
608 type = VNET_FLOW_TYPE_IP4_IP4;
609 protocol.prot = IP_PROTOCOL_IP_IN_IP;
610 }
611 else if (inner_ip6_set)
612 {
613 if (inner_port_set)
614 type = VNET_FLOW_TYPE_IP4_IP6_N_TUPLE;
615 else
616 type = VNET_FLOW_TYPE_IP4_IP6;
617 protocol.prot = IP_PROTOCOL_IPV6;
618 }
Chenmin Sun34bfa502020-07-27 17:40:17 +0800619 else
620 type = VNET_FLOW_TYPE_IP4;
621 break;
622 case FLOW_IPV6_CLASS:
623 if (tcp_udp_port_set)
624 type = VNET_FLOW_TYPE_IP6_N_TUPLE;
625 else if (vni_set)
626 type = VNET_FLOW_TYPE_IP6_VXLAN;
Xinyao Cai99d3a402023-02-28 14:44:58 +0800627 else if (inner_ip4_set)
628 {
629 if (inner_port_set)
630 type = VNET_FLOW_TYPE_IP6_IP4_N_TUPLE;
631 else
632 type = VNET_FLOW_TYPE_IP6_IP4;
633 protocol.prot = IP_PROTOCOL_IP_IN_IP;
634 }
635 else if (inner_ip6_set)
636 {
637 if (inner_port_set)
638 type = VNET_FLOW_TYPE_IP6_IP6_N_TUPLE;
639 else
640 type = VNET_FLOW_TYPE_IP6_IP6;
641 protocol.prot = IP_PROTOCOL_IPV6;
642 }
Chenmin Sun34bfa502020-07-27 17:40:17 +0800643 else
644 type = VNET_FLOW_TYPE_IP6;
645 break;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800646
Chenmin Sun34bfa502020-07-27 17:40:17 +0800647 default:
Ting Xu3a366822021-09-16 08:37:25 +0000648 if (spec && mask)
649 {
650 type = VNET_FLOW_TYPE_GENERIC;
651 break;
652 }
Chenmin Sun34bfa502020-07-27 17:40:17 +0800653 return clib_error_return (0,
654 "Please specify a supported flow type");
Chenmin Sunbf85a982019-10-18 07:35:16 +0800655 }
656
Chenmin Sun34bfa502020-07-27 17:40:17 +0800657 /* Assign specific field values per flow type */
658 if (flow_class == FLOW_ETHERNET_CLASS)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800659 {
Chenmin Sun4ff8d612019-12-15 01:16:45 +0800660 flow.ethernet.eth_hdr.type = eth_type;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800661 }
662 else if (flow_class == FLOW_IPV4_CLASS)
663 {
664 vnet_flow_ip4_t *ip4_ptr = &flow.ip4;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800665
Chenmin Sun34bfa502020-07-27 17:40:17 +0800666 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;
671
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
675 */
676 if (type == VNET_FLOW_TYPE_IP4)
677 ip4_ptr->protocol.mask = protocol.mask;
678
679 switch (protocol.prot)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800680 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800681 /* ip4-n-tuple */
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;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800686
Chenmin Sun34bfa502020-07-27 17:40:17 +0800687 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;
693 break;
694 case IP_PROTOCOL_L2TP:
695 flow.ip4_l2tpv3oip.session_id = session_id;
696 break;
697 case IP_PROTOCOL_IPSEC_ESP:
698 flow.ip4_ipsec_esp.spi = spi;
699 break;
700 case IP_PROTOCOL_IPSEC_AH:
701 flow.ip4_ipsec_esp.spi = spi;
702 break;
Xinyao Cai99d3a402023-02-28 14:44:58 +0800703 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)
709 {
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;
713 }
714 break;
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)
721 {
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;
725 }
726 break;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800727 default:
728 break;
729 }
730 }
731 else if (flow_class == FLOW_IPV6_CLASS)
732 {
733 vnet_flow_ip6_t *ip6_ptr = &flow.ip6;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800734
Chenmin Sunbf85a982019-10-18 07:35:16 +0800735 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));
Chenmin Sunbf85a982019-10-18 07:35:16 +0800739
Chenmin Sun34bfa502020-07-27 17:40:17 +0800740 ip6_ptr->protocol.prot = protocol.prot;
741
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
745 */
746 if (type == VNET_FLOW_TYPE_IP6)
747 ip6_ptr->protocol.mask = protocol.mask;
748
749 switch (protocol.prot)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800750 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800751 /* ip6-n-tuple */
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;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800756
Chenmin Sun34bfa502020-07-27 17:40:17 +0800757 if (type == VNET_FLOW_TYPE_IP6_VXLAN)
758 flow.ip6_vxlan.vni = vni;
759 break;
Xinyao Cai99d3a402023-02-28 14:44:58 +0800760 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)
766 {
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;
770 }
771 break;
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)
778 {
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;
782 }
783 break;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800784 default:
785 break;
786 }
Chenmin Sunbf85a982019-10-18 07:35:16 +0800787 }
Ting Xu3a366822021-09-16 08:37:25 +0000788 if (type == VNET_FLOW_TYPE_GENERIC)
789 {
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));
794 }
Chenmin Sunbf85a982019-10-18 07:35:16 +0800795
796 flow.type = type;
Damjan Mariona35cc142018-03-16 01:25:27 +0100797 rv = vnet_flow_add (vnm, &flow, &flow_index);
Chenmin Sunbf85a982019-10-18 07:35:16 +0800798 if (!rv)
Chenmin Sun34bfa502020-07-27 17:40:17 +0800799 vlib_cli_output (vm, "flow %u added", flow_index);
Chenmin Sunbf85a982019-10-18 07:35:16 +0800800
Damjan Mariona35cc142018-03-16 01:25:27 +0100801 break;
802 case FLOW_DEL:
803 rv = vnet_flow_del (vnm, flow_index);
804 break;
805 case FLOW_ENABLE:
806 rv = vnet_flow_enable (vnm, flow_index, hw_if_index);
807 break;
808 case FLOW_DISABLE:
809 rv = vnet_flow_disable (vnm, flow_index, hw_if_index);
810 break;
811 default:
812 return clib_error_return (0, "please specify action (add, del, enable,"
813 " disable)");
814 }
815
816 if (rv < 0)
817 return clib_error_return (0, "flow error: %U", format_flow_error, rv);
Chenmin Sun34bfa502020-07-27 17:40:17 +0800818
Damjan Mariona35cc142018-03-16 01:25:27 +0100819 return 0;
820}
821
Damjan Mariona35cc142018-03-16 01:25:27 +0100822VLIB_CLI_COMMAND (test_flow_command, static) = {
Ting Xu3a366822021-09-16 08:37:25 +0000823 .path = "test flow",
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] "
Ting Xub95e6d42022-03-25 03:45:07 +0000834 "[rss function <name>] [rss types <flow type>]"
835 "[rss queues <queue_start> to <queue_end>]",
Ting Xu3a366822021-09-16 08:37:25 +0000836 .function = test_flow,
Damjan Mariona35cc142018-03-16 01:25:27 +0100837};
Damjan Mariona35cc142018-03-16 01:25:27 +0100838
Damjan Mariona35cc142018-03-16 01:25:27 +0100839static u8 *
840format_flow_match_element (u8 * s, va_list * args)
841{
842 char *type = va_arg (*args, char *);
843 void *ptr = va_arg (*args, void *);
844
845 if (strncmp (type, "u8", 2) == 0)
846 return format (s, "%d", *(u8 *) ptr);
847
848 if (strncmp (type, "u16", 3) == 0)
849 return format (s, "%d", *(u16 *) ptr);
850
851 if (strncmp (type, "u32", 3) == 0)
852 return format (s, "%d", *(u32 *) ptr);
853
Chenmin Sun34bfa502020-07-27 17:40:17 +0800854 if (strncmp (type, "ethernet_header_t", 13) == 0)
855 {
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);
862 }
863
Damjan Mariona35cc142018-03-16 01:25:27 +0100864 if (strncmp (type, "ip4_address_t", 13) == 0)
865 return format (s, "%U", format_ip4_address, ptr);
866
867 if (strncmp (type, "ip4_address_and_mask_t", 13) == 0)
868 return format (s, "%U", format_ip4_address_and_mask, ptr);
869
870 if (strncmp (type, "ip6_address_t", 13) == 0)
871 return format (s, "%U", format_ip6_address, ptr);
872
873 if (strncmp (type, "ip6_address_and_mask_t", 13) == 0)
874 return format (s, "%U", format_ip6_address_and_mask, ptr);
875
Chenmin Sun34bfa502020-07-27 17:40:17 +0800876 if (strncmp (type, "ip_prot_and_mask_t", 13) == 0)
877 return format (s, "%U", format_ip_protocol_and_mask, ptr);
Damjan Mariona35cc142018-03-16 01:25:27 +0100878
879 if (strncmp (type, "ip_port_and_mask_t", 18) == 0)
880 return format (s, "%U", format_ip_port_and_mask, ptr);
881
882 s = format (s, "unknown type '%s'", type);
883 return s;
884}
885
886#define _fe(a,b) s2 = format (s2, "%s%s %U", s2 ? ", ":"", #b, \
887 format_flow_match_element, #a, &f->b);
888#define _(a,b,c) \
889u8 * format_flow_match_##b (u8 * s, va_list * args) \
890{ \
891 vnet_flow_##b##_t *f = __builtin_va_arg (*args, vnet_flow_##b##_t *); \
892 u8 *s2 = 0; \
893foreach_flow_entry_##b \
894 s = format (s, "%v", s2);; \
895 vec_free (s2); \
896return s; \
897}
898foreach_flow_type
899#undef _
900#undef _fe
901static u8 *
902format_flow_match (u8 * s, va_list * args)
903{
904 vnet_flow_t *f = va_arg (*args, vnet_flow_t *);
905
906#define _(a,b,c) \
907 if (f->type == VNET_FLOW_TYPE_##a) \
908 return format (s, "%U", format_flow_match_##b, &f->b);
909 foreach_flow_type;
910#undef _
911
912 return s;
913}
914
915static u8 *
916format_flow (u8 * s, va_list * args)
917{
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);
921 u8 *t = 0;
922
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);
930
Chenmin Sun34bfa502020-07-27 17:40:17 +0800931 if (f->actions & VNET_FLOW_ACTION_DROP)
932 t = format (t, "%sdrop", t ? ", " : "");
933
Damjan Mariona35cc142018-03-16 01:25:27 +0100934 if (f->actions & VNET_FLOW_ACTION_MARK)
935 t = format (t, "%smark %u", t ? ", " : "", f->mark_flow_id);
936
Chenmin Sun34bfa502020-07-27 17:40:17 +0800937 if (f->actions & VNET_FLOW_ACTION_REDIRECT_TO_QUEUE)
938 t =
939 format (t, "%sredirect-to-queue %u", t ? ", " : "", f->redirect_queue);
940
Damjan Mariona35cc142018-03-16 01:25:27 +0100941 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);
944
945 if (f->actions & VNET_FLOW_ACTION_BUFFER_ADVANCE)
946 t = format (t, "%sbuffer-advance %d", t ? ", " : "", f->buffer_advance);
947
Chenmin Sun34bfa502020-07-27 17:40:17 +0800948 if (f->actions & VNET_FLOW_ACTION_RSS)
949 {
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);
954 }
955
Damjan Mariona35cc142018-03-16 01:25:27 +0100956 if (t)
957 {
958 s = format (s, "\n%U%v", format_white_space, indent + 4, t);
959 vec_free (t);
960 }
961
962 return s;
963}
964
965/*
966 * fd.io coding-style-patch-verification: ON
967 *
968 * Local Variables:
969 * eval: (c-set-style "gnu")
970 * End:
971 */