blob: bf343b0d550a80ab40f072b926b106604c700b62 [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 ();
141 /* *INDENT-OFF* */
142 hash_foreach (hw_if_index, private_data, f->private_data,
143 ({
144 t = format (t, "%s%U", t ? ", " : "",
145 format_vnet_hw_if_index_name, vnm, hw_if_index);
146 }));
147 /* *INDENT-ON* */
148 s = format (s, "%v", t);
149 vec_free (t);
150 return s;
151}
152
Chenmin Sun34bfa502020-07-27 17:40:17 +0800153u8 *
154format_rss_function (u8 * s, va_list * args)
155{
156 vnet_rss_function_t func = va_arg (*args, vnet_rss_function_t);
157
158 if (0)
159 ;
160#undef _
161#define _(f, n) \
162 else if (func == VNET_RSS_FUNC_##f) \
163 return format (s, n);
164
165 foreach_rss_function
166#undef _
167 return format (s, "unknown");
168}
169
170u8 *
171format_rss_types (u8 * s, va_list * args)
172{
173 u64 type = va_arg (*args, u64);
174
175#undef _
176#define _(a,b,c) \
177 if (type & (1UL<<a)) \
178 s = format (s, "%s ", c);
179
180 foreach_flow_rss_types
181#undef _
182 return s;
183}
184
Damjan Mariona35cc142018-03-16 01:25:27 +0100185static const char *flow_type_strings[] = { 0,
186#define _(a,b,c) c,
187 foreach_flow_type
188#undef _
189};
190
191static clib_error_t *
192show_flow_entry (vlib_main_t * vm, unformat_input_t * input,
193 vlib_cli_command_t * cmd_arg)
194{
195 vnet_main_t *vnm = vnet_get_main ();
196 vnet_flow_main_t *fm = &flow_main;
197 unformat_input_t _line_input, *line_input = &_line_input;
198 vnet_hw_interface_t *hi;
199 vnet_device_class_t *dev_class;
200 vnet_flow_t *f;
201 uword private_data;
202 u32 index = ~0, hw_if_index;
203
204 if (!unformat_user (input, unformat_line_input, line_input))
205 goto no_args;
206
207 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
208 {
209 if (unformat (line_input, "index %u", &index))
210 ;
211 else
212 return clib_error_return (0, "parse error: '%U'",
213 format_unformat_error, line_input);
214 }
215
216 unformat_free (line_input);
217
218 if (index != ~0)
219 {
220 if ((f = vnet_get_flow (index)) == 0)
221 return clib_error_return (0, "no such flow");
222
223 vlib_cli_output (vm, "%-10s: %u", "index", f->index);
224 vlib_cli_output (vm, "%-10s: %s", "type", flow_type_strings[f->type]);
225 vlib_cli_output (vm, "%-10s: %U", "match", format_flow, f);
Ting Xu3a366822021-09-16 08:37:25 +0000226 if (f->type == VNET_FLOW_TYPE_GENERIC)
227 {
228 vlib_cli_output (vm, "%s: %s", "spec", f->generic.pattern.spec);
229 vlib_cli_output (vm, "%s: %s", "mask", f->generic.pattern.mask);
230 }
Damjan Mariona35cc142018-03-16 01:25:27 +0100231 /* *INDENT-OFF* */
232 hash_foreach (hw_if_index, private_data, f->private_data,
233 ({
234 hi = vnet_get_hw_interface (vnm, hw_if_index);
235 dev_class = vnet_get_device_class (vnm, hi->dev_class_index);
236 vlib_cli_output (vm, "interface %U\n",
237 format_vnet_hw_if_index_name, vnm, hw_if_index);
238 if (dev_class->format_flow)
239 vlib_cli_output (vm, " %U\n", dev_class->format_flow,
240 hi->dev_instance, f->index, private_data);
241 }));
242 /* *INDENT-ON* */
243 return 0;
244 }
245
246no_args:
247 /* *INDENT-OFF* */
Damjan Marionb2c31b62020-12-13 21:47:40 +0100248 pool_foreach (f, fm->global_flow_pool)
Damjan Mariona35cc142018-03-16 01:25:27 +0100249 {
250 vlib_cli_output (vm, "%U\n", format_flow, f);
Ting Xu3a366822021-09-16 08:37:25 +0000251 if (f->type == VNET_FLOW_TYPE_GENERIC)
252 {
253 vlib_cli_output (vm, "%s: %s", "spec", f->generic.pattern.spec);
254 vlib_cli_output (vm, "%s: %s", "mask", f->generic.pattern.mask);
255 }
Damjan Marionb2c31b62020-12-13 21:47:40 +0100256 }
Damjan Mariona35cc142018-03-16 01:25:27 +0100257 /* *INDENT-ON* */
258
259 return 0;
260}
261
262/* *INDENT-OFF* */
263VLIB_CLI_COMMAND (show_flow_entry_command, static) = {
264 .path = "show flow entry",
265 .short_help = "show flow entry [index <index>]",
266 .function = show_flow_entry,
267};
268/* *INDENT-ON* */
269
270static clib_error_t *
271show_flow_ranges (vlib_main_t * vm, unformat_input_t * input,
272 vlib_cli_command_t * cmd_arg)
273{
274 vnet_flow_main_t *fm = &flow_main;
275 vnet_flow_range_t *r = 0;
276
277 vlib_cli_output (vm, "%8s %8s %s", "Start", "Count", "Owner");
278
279 /* *INDENT-OFF* */
280 vec_foreach (r, fm->ranges)
281 {
282 vlib_cli_output (vm, "%8u %8u %s", r->start, r->count, r->owner);
283 };
284 /* *INDENT-ON* */
285 return 0;
286}
287
288/* *INDENT-OFF* */
289VLIB_CLI_COMMAND (show_flow_ranges_command, static) = {
290 .path = "show flow ranges",
291 .short_help = "show flow ranges",
292 .function = show_flow_ranges,
293};
294/* *INDENT-ON* */
295
296static clib_error_t *
297show_flow_interface (vlib_main_t * vm, unformat_input_t * input,
298 vlib_cli_command_t * cmd_arg)
299{
300 vnet_main_t *vnm = vnet_get_main ();
301 vnet_hw_interface_t *hi;
302 vnet_device_class_t *dev_class;
303 unformat_input_t _line_input, *line_input = &_line_input;
304 u32 hw_if_index = ~0;
305
306 if (unformat_user (input, unformat_line_input, line_input))
307 {
308 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
309 {
310 if (unformat (line_input, "%U",
311 unformat_vnet_hw_interface, vnm, &hw_if_index))
312 ;
313 else
314 return clib_error_return (0, "parse error: '%U'",
315 format_unformat_error, line_input);
316 }
317 unformat_free (line_input);
318 }
319
320 if (hw_if_index == ~0)
321 return clib_error_return (0, "please specify interface");
322
323 hi = vnet_get_hw_interface (vnm, hw_if_index);
324 dev_class = vnet_get_device_class (vnm, hi->dev_class_index);
325 if (dev_class->format_flow == 0)
326 return clib_error_return (0, "not supported");
327
328 vlib_cli_output (vm, "%U", dev_class->format_flow, hi->dev_instance, ~0, 0);
329 return 0;
330}
331
332/* *INDENT-OFF* */
333VLIB_CLI_COMMAND (show_flow_interface_command, static) = {
334 .path = "show flow interface",
335 .short_help = "show flow interface <interface name>",
336 .function = show_flow_interface,
337};
338/* *INDENT-ON* */
339
340static clib_error_t *
341test_flow (vlib_main_t * vm, unformat_input_t * input,
342 vlib_cli_command_t * cmd_arg)
343{
344 vnet_flow_t flow;
345 vnet_main_t *vnm = vnet_get_main ();
346 unformat_input_t _line_input, *line_input = &_line_input;
347 enum
348 {
349 FLOW_UNKNOWN_ACTION,
350 FLOW_ADD,
351 FLOW_DEL,
352 FLOW_ENABLE,
353 FLOW_DISABLE
354 } action = FLOW_UNKNOWN_ACTION;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800355 enum
356 {
357 FLOW_UNKNOWN_CLASS,
358 FLOW_ETHERNET_CLASS,
359 FLOW_IPV4_CLASS,
360 FLOW_IPV6_CLASS,
361 } flow_class = FLOW_UNKNOWN_CLASS;
362
Chenmin Sunbf85a982019-10-18 07:35:16 +0800363 u32 hw_if_index = ~0, flow_index = ~0;
Damjan Mariona35cc142018-03-16 01:25:27 +0100364 int rv;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800365 u32 teid = 0, session_id = 0, spi = 0;
Chenmin Sun91f102e2020-12-08 00:42:43 +0800366 u32 vni = 0;
Ting Xub95e6d42022-03-25 03:45:07 +0000367 u32 queue_start = 0, queue_end = 0;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800368 vnet_flow_type_t type = VNET_FLOW_TYPE_UNKNOWN;
Xinyao Cai99d3a402023-02-28 14:44:58 +0800369 ip4_address_and_mask_t ip4s = {}, in_ip4s = {};
370 ip4_address_and_mask_t ip4d = {}, in_ip4d = {};
371 ip6_address_and_mask_t ip6s = {}, in_ip6s = {};
372 ip6_address_and_mask_t ip6d = {}, in_ip6d = {};
373 ip_port_and_mask_t sport = {}, in_sport = {};
374 ip_port_and_mask_t dport = {}, in_dport = {};
375 ip_prot_and_mask_t protocol = {}, in_proto = {};
Chenmin Sun4ff8d612019-12-15 01:16:45 +0800376 u16 eth_type;
Xinyao Cai99d3a402023-02-28 14:44:58 +0800377 bool inner_ip4_set = false, inner_ip6_set = false;
378 bool tcp_udp_port_set = false, inner_port_set = false;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800379 bool gtpc_set = false;
380 bool gtpu_set = false;
381 bool vni_set = false;
382 bool l2tpv3oip_set = false;
383 bool ipsec_esp_set = false, ipsec_ah_set = false;
Chenmin Sun24e2c502020-02-28 22:49:37 +0800384 u8 *rss_type[3] = { };
385 u8 *type_str = NULL;
Ting Xu3a366822021-09-16 08:37:25 +0000386 u8 *spec = NULL;
387 u8 *mask = NULL;
Damjan Mariona35cc142018-03-16 01:25:27 +0100388
Dave Barachb7b92992018-10-17 10:38:51 -0400389 clib_memset (&flow, 0, sizeof (vnet_flow_t));
Damjan Mariona35cc142018-03-16 01:25:27 +0100390 flow.index = ~0;
391 flow.actions = 0;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800392
Damjan Mariona35cc142018-03-16 01:25:27 +0100393 if (!unformat_user (input, unformat_line_input, line_input))
394 return 0;
395
396 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
397 {
398 if (unformat (line_input, "add"))
399 action = FLOW_ADD;
400 else if (unformat (line_input, "del"))
401 action = FLOW_DEL;
402 else if (unformat (line_input, "enable"))
403 action = FLOW_ENABLE;
404 else if (unformat (line_input, "disable"))
405 action = FLOW_DISABLE;
Ting Xu3a366822021-09-16 08:37:25 +0000406 else if (unformat (line_input, "spec %s", &spec))
407 ;
408 else if (unformat (line_input, "mask %s", &mask))
409 ;
Chenmin Sun4ff8d612019-12-15 01:16:45 +0800410 else if (unformat (line_input, "eth-type %U",
411 unformat_ethernet_type_host_byte_order, &eth_type))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800412 flow_class = FLOW_ETHERNET_CLASS;
Damjan Mariona35cc142018-03-16 01:25:27 +0100413 else if (unformat (line_input, "src-ip %U",
Chenmin Sunbf85a982019-10-18 07:35:16 +0800414 unformat_ip4_address_and_mask, &ip4s))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800415 flow_class = FLOW_IPV4_CLASS;
Damjan Mariona35cc142018-03-16 01:25:27 +0100416 else if (unformat (line_input, "dst-ip %U",
Chenmin Sunbf85a982019-10-18 07:35:16 +0800417 unformat_ip4_address_and_mask, &ip4d))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800418 flow_class = FLOW_IPV4_CLASS;
Xinyao Cai99d3a402023-02-28 14:44:58 +0800419 else if (unformat (line_input, "in-src-ip %U",
420 unformat_ip4_address_and_mask, &in_ip4s))
421 inner_ip4_set = true;
422 else if (unformat (line_input, "in-dst-ip %U",
423 unformat_ip4_address_and_mask, &in_ip4d))
424 inner_ip4_set = true;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800425 else if (unformat (line_input, "ip6-src-ip %U",
426 unformat_ip6_address_and_mask, &ip6s))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800427 flow_class = FLOW_IPV6_CLASS;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800428 else if (unformat (line_input, "ip6-dst-ip %U",
429 unformat_ip6_address_and_mask, &ip6d))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800430 flow_class = FLOW_IPV6_CLASS;
Xinyao Cai99d3a402023-02-28 14:44:58 +0800431 else if (unformat (line_input, "in-ip6-src-ip %U",
432 unformat_ip6_address_and_mask, &in_ip6s))
433 inner_ip6_set = true;
434 else if (unformat (line_input, "in-ip6-dst-ip %U",
435 unformat_ip6_address_and_mask, &in_ip6d))
436 inner_ip6_set = true;
Damjan Mariona35cc142018-03-16 01:25:27 +0100437 else if (unformat (line_input, "src-port %U", unformat_ip_port_and_mask,
Chenmin Sunbf85a982019-10-18 07:35:16 +0800438 &sport))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800439 tcp_udp_port_set = true;
Damjan Mariona35cc142018-03-16 01:25:27 +0100440 else if (unformat (line_input, "dst-port %U", unformat_ip_port_and_mask,
Chenmin Sunbf85a982019-10-18 07:35:16 +0800441 &dport))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800442 tcp_udp_port_set = true;
443 else
444 if (unformat
445 (line_input, "proto %U", unformat_ip_protocol_and_mask,
446 &protocol))
Chenmin Sunbf85a982019-10-18 07:35:16 +0800447 ;
Xinyao Cai99d3a402023-02-28 14:44:58 +0800448 else if (unformat (line_input, "in-src-port %U",
449 unformat_ip_port_and_mask, &in_sport))
450 inner_port_set = true;
451 else if (unformat (line_input, "in-dst-port %U",
452 unformat_ip_port_and_mask, &in_dport))
453 inner_port_set = true;
454 else if (unformat (line_input, "in-proto %U",
455 unformat_ip_protocol_and_mask, &in_proto))
456 ;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800457 else if (unformat (line_input, "gtpc teid %u", &teid))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800458 gtpc_set = true;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800459 else if (unformat (line_input, "gtpu teid %u", &teid))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800460 gtpu_set = true;
461 else if (unformat (line_input, "vxlan vni %u", &vni))
462 vni_set = true;
Chenmin Sun8b43aaa2020-04-15 01:55:58 +0800463 else if (unformat (line_input, "session id %u", &session_id))
464 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800465 if (protocol.prot == IP_PROTOCOL_L2TP)
466 l2tpv3oip_set = true;
Chenmin Sun8b43aaa2020-04-15 01:55:58 +0800467 }
Chenmin Sund4c36662020-06-22 18:21:31 +0800468 else if (unformat (line_input, "spi %u", &spi))
469 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800470 if (protocol.prot == IP_PROTOCOL_IPSEC_ESP)
471 ipsec_esp_set = true;
472 else if (protocol.prot == IP_PROTOCOL_IPSEC_AH)
473 ipsec_ah_set = true;
Chenmin Sund4c36662020-06-22 18:21:31 +0800474 }
Damjan Mariona35cc142018-03-16 01:25:27 +0100475 else if (unformat (line_input, "index %u", &flow_index))
476 ;
477 else if (unformat (line_input, "next-node %U", unformat_vlib_node, vm,
478 &flow.redirect_node_index))
479 flow.actions |= VNET_FLOW_ACTION_REDIRECT_TO_NODE;
480 else if (unformat (line_input, "mark %d", &flow.mark_flow_id))
481 flow.actions |= VNET_FLOW_ACTION_MARK;
482 else if (unformat (line_input, "buffer-advance %d",
483 &flow.buffer_advance))
484 flow.actions |= VNET_FLOW_ACTION_BUFFER_ADVANCE;
Chenmin Sune8c9f4f2019-10-15 20:36:16 +0800485 else if (unformat (line_input, "redirect-to-queue %d",
486 &flow.redirect_queue))
487 flow.actions |= VNET_FLOW_ACTION_REDIRECT_TO_QUEUE;
488 else if (unformat (line_input, "drop"))
489 flow.actions |= VNET_FLOW_ACTION_DROP;
Chenmin Sun24e2c502020-02-28 22:49:37 +0800490 else if (unformat (line_input, "rss function"))
491 {
492 if (0)
493 ;
494#undef _
Chenmin Sun34bfa502020-07-27 17:40:17 +0800495#define _(f, s) \
496 else if (unformat (line_input, s)) \
497 flow.rss_fun = VNET_RSS_FUNC_##f;
Chenmin Sun24e2c502020-02-28 22:49:37 +0800498
499 foreach_rss_function
500#undef _
501 else
502 {
503 return clib_error_return (0, "unknown input `%U'",
504 format_unformat_error, line_input);
505 }
506
507 flow.actions |= VNET_FLOW_ACTION_RSS;
508 }
509 else if (unformat (line_input, "rss types"))
510 {
511 rss_type[0] = NULL;
512 rss_type[1] = NULL;
513 rss_type[2] = NULL;
514 type_str = NULL;
515
516 if (unformat (line_input, "%s use %s and %s",
517 &rss_type[0], &rss_type[1], &rss_type[2]))
518 ;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800519 else if (unformat
520 (line_input, "%s use %s", &rss_type[0], &rss_type[1]))
Chenmin Sun24e2c502020-02-28 22:49:37 +0800521 ;
522 else if (unformat (line_input, "%s", &rss_type[0]))
523 ;
524
525#undef _
526#define _(a,b,c) \
Chenmin Sun34bfa502020-07-27 17:40:17 +0800527 else if (!clib_strcmp(c, (const char *)type_str)) \
528 flow.rss_types |= (1ULL<<a);
Chenmin Sun24e2c502020-02-28 22:49:37 +0800529
530#define check_rss_types(_str) \
Chenmin Sun34bfa502020-07-27 17:40:17 +0800531 if (_str != NULL) {\
532 type_str = _str;\
533 if (0) \
534 ; \
535 foreach_flow_rss_types \
536 else \
537 { \
538 return clib_error_return (0, "parse error: '%U'", \
539 format_unformat_error, line_input); \
540 } \
541 }
Chenmin Sun24e2c502020-02-28 22:49:37 +0800542
543 check_rss_types (rss_type[0])
544 check_rss_types (rss_type[1]) check_rss_types (rss_type[2])
545#undef _
546 flow.actions |= VNET_FLOW_ACTION_RSS;
547 }
Ting Xub95e6d42022-03-25 03:45:07 +0000548 else if (unformat (line_input, "rss queues"))
549 {
550 if (unformat (line_input, "%d to %d", &queue_start, &queue_end))
551 ;
552 else
553 {
554 return clib_error_return (0, "unknown input `%U'",
555 format_unformat_error, line_input);
556 }
557
558 flow.queue_index = queue_start;
559 flow.queue_num = queue_end - queue_start + 1;
560
561 flow.actions |= VNET_FLOW_ACTION_RSS;
562 }
Damjan Mariona35cc142018-03-16 01:25:27 +0100563 else if (unformat (line_input, "%U", unformat_vnet_hw_interface, vnm,
564 &hw_if_index))
565 ;
566 else
567 return clib_error_return (0, "parse error: '%U'",
568 format_unformat_error, line_input);
569 }
570
571 unformat_free (line_input);
572
573 if (hw_if_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE))
574 return clib_error_return (0, "Please specify interface name");
575
576 if (flow_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE ||
577 action == FLOW_DEL))
578 return clib_error_return (0, "Please specify flow index");
579
580 switch (action)
581 {
582 case FLOW_ADD:
Damjan Mariona35cc142018-03-16 01:25:27 +0100583 if (flow.actions == 0)
584 return clib_error_return (0, "Please specify at least one action");
Chenmin Sunbf85a982019-10-18 07:35:16 +0800585
586 /* Adjust the flow type */
Chenmin Sun34bfa502020-07-27 17:40:17 +0800587 switch (flow_class)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800588 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800589 case FLOW_ETHERNET_CLASS:
590 type = VNET_FLOW_TYPE_ETHERNET;
591 break;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800592
Chenmin Sun34bfa502020-07-27 17:40:17 +0800593 case FLOW_IPV4_CLASS:
594 if (gtpc_set)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800595 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800596 type = VNET_FLOW_TYPE_IP4_GTPC;
597 protocol.prot = IP_PROTOCOL_UDP;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800598 }
Chenmin Sun34bfa502020-07-27 17:40:17 +0800599 else if (gtpu_set)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800600 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800601 type = VNET_FLOW_TYPE_IP4_GTPU;
602 protocol.prot = IP_PROTOCOL_UDP;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800603 }
Chenmin Sun34bfa502020-07-27 17:40:17 +0800604 else if (vni_set)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800605 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800606 type = VNET_FLOW_TYPE_IP4_VXLAN;
607 protocol.prot = IP_PROTOCOL_UDP;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800608 }
Chenmin Sun34bfa502020-07-27 17:40:17 +0800609 else if (l2tpv3oip_set)
610 type = VNET_FLOW_TYPE_IP4_L2TPV3OIP;
611 else if (ipsec_esp_set)
612 type = VNET_FLOW_TYPE_IP4_IPSEC_ESP;
613 else if (ipsec_ah_set)
614 type = VNET_FLOW_TYPE_IP4_IPSEC_AH;
615 else if (tcp_udp_port_set)
616 type = VNET_FLOW_TYPE_IP4_N_TUPLE;
Xinyao Cai99d3a402023-02-28 14:44:58 +0800617 else if (inner_ip4_set)
618 {
619 if (inner_port_set)
620 type = VNET_FLOW_TYPE_IP4_IP4_N_TUPLE;
621 else
622 type = VNET_FLOW_TYPE_IP4_IP4;
623 protocol.prot = IP_PROTOCOL_IP_IN_IP;
624 }
625 else if (inner_ip6_set)
626 {
627 if (inner_port_set)
628 type = VNET_FLOW_TYPE_IP4_IP6_N_TUPLE;
629 else
630 type = VNET_FLOW_TYPE_IP4_IP6;
631 protocol.prot = IP_PROTOCOL_IPV6;
632 }
Chenmin Sun34bfa502020-07-27 17:40:17 +0800633 else
634 type = VNET_FLOW_TYPE_IP4;
635 break;
636 case FLOW_IPV6_CLASS:
637 if (tcp_udp_port_set)
638 type = VNET_FLOW_TYPE_IP6_N_TUPLE;
639 else if (vni_set)
640 type = VNET_FLOW_TYPE_IP6_VXLAN;
Xinyao Cai99d3a402023-02-28 14:44:58 +0800641 else if (inner_ip4_set)
642 {
643 if (inner_port_set)
644 type = VNET_FLOW_TYPE_IP6_IP4_N_TUPLE;
645 else
646 type = VNET_FLOW_TYPE_IP6_IP4;
647 protocol.prot = IP_PROTOCOL_IP_IN_IP;
648 }
649 else if (inner_ip6_set)
650 {
651 if (inner_port_set)
652 type = VNET_FLOW_TYPE_IP6_IP6_N_TUPLE;
653 else
654 type = VNET_FLOW_TYPE_IP6_IP6;
655 protocol.prot = IP_PROTOCOL_IPV6;
656 }
Chenmin Sun34bfa502020-07-27 17:40:17 +0800657 else
658 type = VNET_FLOW_TYPE_IP6;
659 break;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800660
Chenmin Sun34bfa502020-07-27 17:40:17 +0800661 default:
Ting Xu3a366822021-09-16 08:37:25 +0000662 if (spec && mask)
663 {
664 type = VNET_FLOW_TYPE_GENERIC;
665 break;
666 }
Chenmin Sun34bfa502020-07-27 17:40:17 +0800667 return clib_error_return (0,
668 "Please specify a supported flow type");
Chenmin Sunbf85a982019-10-18 07:35:16 +0800669 }
670
Chenmin Sun34bfa502020-07-27 17:40:17 +0800671 /* Assign specific field values per flow type */
672 if (flow_class == FLOW_ETHERNET_CLASS)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800673 {
Chenmin Sun4ff8d612019-12-15 01:16:45 +0800674 flow.ethernet.eth_hdr.type = eth_type;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800675 }
676 else if (flow_class == FLOW_IPV4_CLASS)
677 {
678 vnet_flow_ip4_t *ip4_ptr = &flow.ip4;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800679
Chenmin Sun34bfa502020-07-27 17:40:17 +0800680 clib_memcpy (&ip4_ptr->src_addr, &ip4s,
681 sizeof (ip4_address_and_mask_t));
682 clib_memcpy (&ip4_ptr->dst_addr, &ip4d,
683 sizeof (ip4_address_and_mask_t));
684 ip4_ptr->protocol.prot = protocol.prot;
685
686 /* In this cli, we use the protocol.mask only when the flow type is
687 * VNET_FLOW_TYPE_IP4/IP6. For other cases, the IP protocol is just
688 * used to identify the next layer type: e.g. UDP/TCP or IPSEC_ESP
689 */
690 if (type == VNET_FLOW_TYPE_IP4)
691 ip4_ptr->protocol.mask = protocol.mask;
692
693 switch (protocol.prot)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800694 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800695 /* ip4-n-tuple */
696 case IP_PROTOCOL_TCP:
697 case IP_PROTOCOL_UDP:
698 flow.ip4_n_tuple.src_port = sport;
699 flow.ip4_n_tuple.dst_port = dport;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800700
Chenmin Sun34bfa502020-07-27 17:40:17 +0800701 if (type == VNET_FLOW_TYPE_IP4_GTPC)
702 flow.ip4_gtpc.teid = teid;
703 else if (type == VNET_FLOW_TYPE_IP4_GTPU)
704 flow.ip4_gtpu.teid = teid;
705 else if (type == VNET_FLOW_TYPE_IP4_VXLAN)
706 flow.ip4_vxlan.vni = vni;
707 break;
708 case IP_PROTOCOL_L2TP:
709 flow.ip4_l2tpv3oip.session_id = session_id;
710 break;
711 case IP_PROTOCOL_IPSEC_ESP:
712 flow.ip4_ipsec_esp.spi = spi;
713 break;
714 case IP_PROTOCOL_IPSEC_AH:
715 flow.ip4_ipsec_esp.spi = spi;
716 break;
Xinyao Cai99d3a402023-02-28 14:44:58 +0800717 case IP_PROTOCOL_IP_IN_IP:
718 clib_memcpy (&flow.ip4_ip4.in_src_addr, &in_ip4s,
719 sizeof (ip4_address_and_mask_t));
720 clib_memcpy (&flow.ip4_ip4.in_dst_addr, &in_ip4d,
721 sizeof (ip4_address_and_mask_t));
722 if (type == VNET_FLOW_TYPE_IP4_IP4_N_TUPLE)
723 {
724 flow.ip4_ip4.in_protocol.prot = in_proto.prot;
725 flow.ip4_ip4_n_tuple.in_src_port = in_sport;
726 flow.ip4_ip4_n_tuple.in_dst_port = in_dport;
727 }
728 break;
729 case IP_PROTOCOL_IPV6:
730 clib_memcpy (&flow.ip4_ip6.in_src_addr, &in_ip6s,
731 sizeof (ip6_address_and_mask_t));
732 clib_memcpy (&flow.ip4_ip6.in_dst_addr, &in_ip6d,
733 sizeof (ip6_address_and_mask_t));
734 if (type == VNET_FLOW_TYPE_IP4_IP6_N_TUPLE)
735 {
736 flow.ip4_ip6.in_protocol.prot = in_proto.prot;
737 flow.ip4_ip6_n_tuple.in_src_port = in_sport;
738 flow.ip4_ip6_n_tuple.in_dst_port = in_dport;
739 }
740 break;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800741 default:
742 break;
743 }
744 }
745 else if (flow_class == FLOW_IPV6_CLASS)
746 {
747 vnet_flow_ip6_t *ip6_ptr = &flow.ip6;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800748
Chenmin Sunbf85a982019-10-18 07:35:16 +0800749 clib_memcpy (&flow.ip6_n_tuple.src_addr, &ip6s,
750 sizeof (ip6_address_and_mask_t));
751 clib_memcpy (&flow.ip6_n_tuple.dst_addr, &ip6d,
752 sizeof (ip6_address_and_mask_t));
Chenmin Sunbf85a982019-10-18 07:35:16 +0800753
Chenmin Sun34bfa502020-07-27 17:40:17 +0800754 ip6_ptr->protocol.prot = protocol.prot;
755
756 /* In this cli, we use the protocol.mask only when the flow type is
757 * VNET_FLOW_TYPE_IP4/IP6. For other cases, the IP protocol is just
758 * used to identify the next layer type: e.g. UDP/TCP or IPSEC_ESP
759 */
760 if (type == VNET_FLOW_TYPE_IP6)
761 ip6_ptr->protocol.mask = protocol.mask;
762
763 switch (protocol.prot)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800764 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800765 /* ip6-n-tuple */
766 case IP_PROTOCOL_TCP:
767 case IP_PROTOCOL_UDP:
768 flow.ip6_n_tuple.src_port = sport;
769 flow.ip6_n_tuple.dst_port = dport;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800770
Chenmin Sun34bfa502020-07-27 17:40:17 +0800771 if (type == VNET_FLOW_TYPE_IP6_VXLAN)
772 flow.ip6_vxlan.vni = vni;
773 break;
Xinyao Cai99d3a402023-02-28 14:44:58 +0800774 case IP_PROTOCOL_IP_IN_IP:
775 clib_memcpy (&flow.ip6_ip4.in_src_addr, &in_ip4s,
776 sizeof (ip4_address_and_mask_t));
777 clib_memcpy (&flow.ip6_ip4.in_dst_addr, &in_ip4d,
778 sizeof (ip4_address_and_mask_t));
779 if (type == VNET_FLOW_TYPE_IP6_IP4_N_TUPLE)
780 {
781 flow.ip6_ip4.in_protocol.prot = in_proto.prot;
782 flow.ip6_ip4_n_tuple.in_src_port = in_sport;
783 flow.ip6_ip4_n_tuple.in_dst_port = in_dport;
784 }
785 break;
786 case IP_PROTOCOL_IPV6:
787 clib_memcpy (&flow.ip6_ip6.in_src_addr, &in_ip6s,
788 sizeof (ip6_address_and_mask_t));
789 clib_memcpy (&flow.ip6_ip6.in_dst_addr, &in_ip6d,
790 sizeof (ip6_address_and_mask_t));
791 if (type == VNET_FLOW_TYPE_IP6_IP6_N_TUPLE)
792 {
793 flow.ip6_ip6.in_protocol.prot = in_proto.prot;
794 flow.ip6_ip6_n_tuple.in_src_port = in_sport;
795 flow.ip6_ip6_n_tuple.in_dst_port = in_dport;
796 }
797 break;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800798 default:
799 break;
800 }
Chenmin Sunbf85a982019-10-18 07:35:16 +0800801 }
Ting Xu3a366822021-09-16 08:37:25 +0000802 if (type == VNET_FLOW_TYPE_GENERIC)
803 {
804 clib_memcpy (flow.generic.pattern.spec, spec,
805 sizeof (flow.generic.pattern.spec));
806 clib_memcpy (flow.generic.pattern.mask, mask,
807 sizeof (flow.generic.pattern.mask));
808 }
Chenmin Sunbf85a982019-10-18 07:35:16 +0800809
810 flow.type = type;
Damjan Mariona35cc142018-03-16 01:25:27 +0100811 rv = vnet_flow_add (vnm, &flow, &flow_index);
Chenmin Sunbf85a982019-10-18 07:35:16 +0800812 if (!rv)
Chenmin Sun34bfa502020-07-27 17:40:17 +0800813 vlib_cli_output (vm, "flow %u added", flow_index);
Chenmin Sunbf85a982019-10-18 07:35:16 +0800814
Damjan Mariona35cc142018-03-16 01:25:27 +0100815 break;
816 case FLOW_DEL:
817 rv = vnet_flow_del (vnm, flow_index);
818 break;
819 case FLOW_ENABLE:
820 rv = vnet_flow_enable (vnm, flow_index, hw_if_index);
821 break;
822 case FLOW_DISABLE:
823 rv = vnet_flow_disable (vnm, flow_index, hw_if_index);
824 break;
825 default:
826 return clib_error_return (0, "please specify action (add, del, enable,"
827 " disable)");
828 }
829
830 if (rv < 0)
831 return clib_error_return (0, "flow error: %U", format_flow_error, rv);
Chenmin Sun34bfa502020-07-27 17:40:17 +0800832
Damjan Mariona35cc142018-03-16 01:25:27 +0100833 return 0;
834}
835
836/* *INDENT-OFF* */
837VLIB_CLI_COMMAND (test_flow_command, static) = {
Ting Xu3a366822021-09-16 08:37:25 +0000838 .path = "test flow",
839 .short_help = "test flow [add|del|enable|disable] [index <id>] "
840 "[src-ip <ip-addr/mask>] [dst-ip <ip-addr/mask>] "
841 "[ip6-src-ip <ip-addr/mask>] [ip6-dst-ip <ip-addr/mask>] "
842 "[src-port <port/mask>] [dst-port <port/mask>] "
843 "[proto <ip-proto>] "
844 "[gtpc teid <teid>] [gtpu teid <teid>] [vxlan <vni>] "
845 "[session id <session>] [spi <spi>]"
846 "[spec <spec string>] [mask <mask string>]"
847 "[next-node <node>] [mark <id>] [buffer-advance <len>] "
848 "[redirect-to-queue <queue>] [drop] "
Ting Xub95e6d42022-03-25 03:45:07 +0000849 "[rss function <name>] [rss types <flow type>]"
850 "[rss queues <queue_start> to <queue_end>]",
Ting Xu3a366822021-09-16 08:37:25 +0000851 .function = test_flow,
Damjan Mariona35cc142018-03-16 01:25:27 +0100852};
853/* *INDENT-ON* */
854
Damjan Mariona35cc142018-03-16 01:25:27 +0100855static u8 *
856format_flow_match_element (u8 * s, va_list * args)
857{
858 char *type = va_arg (*args, char *);
859 void *ptr = va_arg (*args, void *);
860
861 if (strncmp (type, "u8", 2) == 0)
862 return format (s, "%d", *(u8 *) ptr);
863
864 if (strncmp (type, "u16", 3) == 0)
865 return format (s, "%d", *(u16 *) ptr);
866
867 if (strncmp (type, "u32", 3) == 0)
868 return format (s, "%d", *(u32 *) ptr);
869
Chenmin Sun34bfa502020-07-27 17:40:17 +0800870 if (strncmp (type, "ethernet_header_t", 13) == 0)
871 {
872 ethernet_max_header_t m;
873 memset (&m, 0, sizeof (m));
874 m.ethernet = *(ethernet_header_t *) ptr;
875 /* convert the ethernet type to net order */
876 m.ethernet.type = clib_host_to_net_u16 (m.ethernet.type);
877 return format (s, "%U", format_ethernet_header, &m);
878 }
879
Damjan Mariona35cc142018-03-16 01:25:27 +0100880 if (strncmp (type, "ip4_address_t", 13) == 0)
881 return format (s, "%U", format_ip4_address, ptr);
882
883 if (strncmp (type, "ip4_address_and_mask_t", 13) == 0)
884 return format (s, "%U", format_ip4_address_and_mask, ptr);
885
886 if (strncmp (type, "ip6_address_t", 13) == 0)
887 return format (s, "%U", format_ip6_address, ptr);
888
889 if (strncmp (type, "ip6_address_and_mask_t", 13) == 0)
890 return format (s, "%U", format_ip6_address_and_mask, ptr);
891
Chenmin Sun34bfa502020-07-27 17:40:17 +0800892 if (strncmp (type, "ip_prot_and_mask_t", 13) == 0)
893 return format (s, "%U", format_ip_protocol_and_mask, ptr);
Damjan Mariona35cc142018-03-16 01:25:27 +0100894
895 if (strncmp (type, "ip_port_and_mask_t", 18) == 0)
896 return format (s, "%U", format_ip_port_and_mask, ptr);
897
898 s = format (s, "unknown type '%s'", type);
899 return s;
900}
901
902#define _fe(a,b) s2 = format (s2, "%s%s %U", s2 ? ", ":"", #b, \
903 format_flow_match_element, #a, &f->b);
904#define _(a,b,c) \
905u8 * format_flow_match_##b (u8 * s, va_list * args) \
906{ \
907 vnet_flow_##b##_t *f = __builtin_va_arg (*args, vnet_flow_##b##_t *); \
908 u8 *s2 = 0; \
909foreach_flow_entry_##b \
910 s = format (s, "%v", s2);; \
911 vec_free (s2); \
912return s; \
913}
914foreach_flow_type
915#undef _
916#undef _fe
917static u8 *
918format_flow_match (u8 * s, va_list * args)
919{
920 vnet_flow_t *f = va_arg (*args, vnet_flow_t *);
921
922#define _(a,b,c) \
923 if (f->type == VNET_FLOW_TYPE_##a) \
924 return format (s, "%U", format_flow_match_##b, &f->b);
925 foreach_flow_type;
926#undef _
927
928 return s;
929}
930
931static u8 *
932format_flow (u8 * s, va_list * args)
933{
934 vlib_main_t *vm = vlib_get_main ();
935 vnet_flow_t *f = va_arg (*args, vnet_flow_t *);
936 u32 indent = format_get_indent (s);
937 u8 *t = 0;
938
939 s = format (s, "flow-index %u type %s active %u",
940 f->index, flow_type_strings[f->type],
941 hash_elts (f->private_data)),
942 s = format (s, "\n%Umatch: %U", format_white_space, indent + 2,
943 format_flow_match, f);
944 s = format (s, "\n%Uaction: %U", format_white_space, indent + 2,
945 format_flow_actions, f->actions);
946
Chenmin Sun34bfa502020-07-27 17:40:17 +0800947 if (f->actions & VNET_FLOW_ACTION_DROP)
948 t = format (t, "%sdrop", t ? ", " : "");
949
Damjan Mariona35cc142018-03-16 01:25:27 +0100950 if (f->actions & VNET_FLOW_ACTION_MARK)
951 t = format (t, "%smark %u", t ? ", " : "", f->mark_flow_id);
952
Chenmin Sun34bfa502020-07-27 17:40:17 +0800953 if (f->actions & VNET_FLOW_ACTION_REDIRECT_TO_QUEUE)
954 t =
955 format (t, "%sredirect-to-queue %u", t ? ", " : "", f->redirect_queue);
956
Damjan Mariona35cc142018-03-16 01:25:27 +0100957 if (f->actions & VNET_FLOW_ACTION_REDIRECT_TO_NODE)
958 t = format (t, "%snext-node %U", t ? ", " : "",
959 format_vlib_node_name, vm, f->redirect_node_index);
960
961 if (f->actions & VNET_FLOW_ACTION_BUFFER_ADVANCE)
962 t = format (t, "%sbuffer-advance %d", t ? ", " : "", f->buffer_advance);
963
Chenmin Sun34bfa502020-07-27 17:40:17 +0800964 if (f->actions & VNET_FLOW_ACTION_RSS)
965 {
966 t = format (t, "%srss function %U", t ? ", " : "",
967 format_rss_function, f->rss_fun);
968 t = format (t, "%srss types %U", t ? ", " : "",
969 format_rss_types, f->rss_types);
970 }
971
Damjan Mariona35cc142018-03-16 01:25:27 +0100972 if (t)
973 {
974 s = format (s, "\n%U%v", format_white_space, indent + 4, t);
975 vec_free (t);
976 }
977
978 return s;
979}
980
981/*
982 * fd.io coding-style-patch-verification: ON
983 *
984 * Local Variables:
985 * eval: (c-set-style "gnu")
986 * End:
987 */