blob: 7dd68677c3b36ae2006e95144d44f10e46d8683b [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);
226 /* *INDENT-OFF* */
227 hash_foreach (hw_if_index, private_data, f->private_data,
228 ({
229 hi = vnet_get_hw_interface (vnm, hw_if_index);
230 dev_class = vnet_get_device_class (vnm, hi->dev_class_index);
231 vlib_cli_output (vm, "interface %U\n",
232 format_vnet_hw_if_index_name, vnm, hw_if_index);
233 if (dev_class->format_flow)
234 vlib_cli_output (vm, " %U\n", dev_class->format_flow,
235 hi->dev_instance, f->index, private_data);
236 }));
237 /* *INDENT-ON* */
238 return 0;
239 }
240
241no_args:
242 /* *INDENT-OFF* */
243 pool_foreach (f, fm->global_flow_pool,
244 {
245 vlib_cli_output (vm, "%U\n", format_flow, f);
246 });
247 /* *INDENT-ON* */
248
249 return 0;
250}
251
252/* *INDENT-OFF* */
253VLIB_CLI_COMMAND (show_flow_entry_command, static) = {
254 .path = "show flow entry",
255 .short_help = "show flow entry [index <index>]",
256 .function = show_flow_entry,
257};
258/* *INDENT-ON* */
259
260static clib_error_t *
261show_flow_ranges (vlib_main_t * vm, unformat_input_t * input,
262 vlib_cli_command_t * cmd_arg)
263{
264 vnet_flow_main_t *fm = &flow_main;
265 vnet_flow_range_t *r = 0;
266
267 vlib_cli_output (vm, "%8s %8s %s", "Start", "Count", "Owner");
268
269 /* *INDENT-OFF* */
270 vec_foreach (r, fm->ranges)
271 {
272 vlib_cli_output (vm, "%8u %8u %s", r->start, r->count, r->owner);
273 };
274 /* *INDENT-ON* */
275 return 0;
276}
277
278/* *INDENT-OFF* */
279VLIB_CLI_COMMAND (show_flow_ranges_command, static) = {
280 .path = "show flow ranges",
281 .short_help = "show flow ranges",
282 .function = show_flow_ranges,
283};
284/* *INDENT-ON* */
285
286static clib_error_t *
287show_flow_interface (vlib_main_t * vm, unformat_input_t * input,
288 vlib_cli_command_t * cmd_arg)
289{
290 vnet_main_t *vnm = vnet_get_main ();
291 vnet_hw_interface_t *hi;
292 vnet_device_class_t *dev_class;
293 unformat_input_t _line_input, *line_input = &_line_input;
294 u32 hw_if_index = ~0;
295
296 if (unformat_user (input, unformat_line_input, line_input))
297 {
298 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
299 {
300 if (unformat (line_input, "%U",
301 unformat_vnet_hw_interface, vnm, &hw_if_index))
302 ;
303 else
304 return clib_error_return (0, "parse error: '%U'",
305 format_unformat_error, line_input);
306 }
307 unformat_free (line_input);
308 }
309
310 if (hw_if_index == ~0)
311 return clib_error_return (0, "please specify interface");
312
313 hi = vnet_get_hw_interface (vnm, hw_if_index);
314 dev_class = vnet_get_device_class (vnm, hi->dev_class_index);
315 if (dev_class->format_flow == 0)
316 return clib_error_return (0, "not supported");
317
318 vlib_cli_output (vm, "%U", dev_class->format_flow, hi->dev_instance, ~0, 0);
319 return 0;
320}
321
322/* *INDENT-OFF* */
323VLIB_CLI_COMMAND (show_flow_interface_command, static) = {
324 .path = "show flow interface",
325 .short_help = "show flow interface <interface name>",
326 .function = show_flow_interface,
327};
328/* *INDENT-ON* */
329
330static clib_error_t *
331test_flow (vlib_main_t * vm, unformat_input_t * input,
332 vlib_cli_command_t * cmd_arg)
333{
334 vnet_flow_t flow;
335 vnet_main_t *vnm = vnet_get_main ();
336 unformat_input_t _line_input, *line_input = &_line_input;
337 enum
338 {
339 FLOW_UNKNOWN_ACTION,
340 FLOW_ADD,
341 FLOW_DEL,
342 FLOW_ENABLE,
343 FLOW_DISABLE
344 } action = FLOW_UNKNOWN_ACTION;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800345 enum
346 {
347 FLOW_UNKNOWN_CLASS,
348 FLOW_ETHERNET_CLASS,
349 FLOW_IPV4_CLASS,
350 FLOW_IPV6_CLASS,
351 } flow_class = FLOW_UNKNOWN_CLASS;
352
Chenmin Sunbf85a982019-10-18 07:35:16 +0800353 u32 hw_if_index = ~0, flow_index = ~0;
Damjan Mariona35cc142018-03-16 01:25:27 +0100354 int rv;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800355 u32 teid = 0, session_id = 0, spi = 0;
356 u16 vni = 0;
357 vnet_flow_type_t type = VNET_FLOW_TYPE_UNKNOWN;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800358 ip4_address_and_mask_t ip4s = { };
359 ip4_address_and_mask_t ip4d = { };
Chenmin Sunbf85a982019-10-18 07:35:16 +0800360 ip6_address_and_mask_t ip6s = { };
361 ip6_address_and_mask_t ip6d = { };
Chenmin Sunbf85a982019-10-18 07:35:16 +0800362 ip_port_and_mask_t sport = { };
363 ip_port_and_mask_t dport = { };
Chenmin Sun34bfa502020-07-27 17:40:17 +0800364 ip_prot_and_mask_t protocol = { };
Chenmin Sun4ff8d612019-12-15 01:16:45 +0800365 u16 eth_type;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800366 bool tcp_udp_port_set = false;
367 bool gtpc_set = false;
368 bool gtpu_set = false;
369 bool vni_set = false;
370 bool l2tpv3oip_set = false;
371 bool ipsec_esp_set = false, ipsec_ah_set = false;
Chenmin Sun24e2c502020-02-28 22:49:37 +0800372 u8 *rss_type[3] = { };
373 u8 *type_str = 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;
Chenmin Sun4ff8d612019-12-15 01:16:45 +0800392 else if (unformat (line_input, "eth-type %U",
393 unformat_ethernet_type_host_byte_order, &eth_type))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800394 flow_class = FLOW_ETHERNET_CLASS;
Damjan Mariona35cc142018-03-16 01:25:27 +0100395 else if (unformat (line_input, "src-ip %U",
Chenmin Sunbf85a982019-10-18 07:35:16 +0800396 unformat_ip4_address_and_mask, &ip4s))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800397 flow_class = FLOW_IPV4_CLASS;
Damjan Mariona35cc142018-03-16 01:25:27 +0100398 else if (unformat (line_input, "dst-ip %U",
Chenmin Sunbf85a982019-10-18 07:35:16 +0800399 unformat_ip4_address_and_mask, &ip4d))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800400 flow_class = FLOW_IPV4_CLASS;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800401 else if (unformat (line_input, "ip6-src-ip %U",
402 unformat_ip6_address_and_mask, &ip6s))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800403 flow_class = FLOW_IPV6_CLASS;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800404 else if (unformat (line_input, "ip6-dst-ip %U",
405 unformat_ip6_address_and_mask, &ip6d))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800406 flow_class = FLOW_IPV6_CLASS;
Damjan Mariona35cc142018-03-16 01:25:27 +0100407 else if (unformat (line_input, "src-port %U", unformat_ip_port_and_mask,
Chenmin Sunbf85a982019-10-18 07:35:16 +0800408 &sport))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800409 tcp_udp_port_set = true;
Damjan Mariona35cc142018-03-16 01:25:27 +0100410 else if (unformat (line_input, "dst-port %U", unformat_ip_port_and_mask,
Chenmin Sunbf85a982019-10-18 07:35:16 +0800411 &dport))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800412 tcp_udp_port_set = true;
413 else
414 if (unformat
415 (line_input, "proto %U", unformat_ip_protocol_and_mask,
416 &protocol))
Chenmin Sunbf85a982019-10-18 07:35:16 +0800417 ;
418 else if (unformat (line_input, "gtpc teid %u", &teid))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800419 gtpc_set = true;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800420 else if (unformat (line_input, "gtpu teid %u", &teid))
Chenmin Sun34bfa502020-07-27 17:40:17 +0800421 gtpu_set = true;
422 else if (unformat (line_input, "vxlan vni %u", &vni))
423 vni_set = true;
Chenmin Sun8b43aaa2020-04-15 01:55:58 +0800424 else if (unformat (line_input, "session id %u", &session_id))
425 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800426 if (protocol.prot == IP_PROTOCOL_L2TP)
427 l2tpv3oip_set = true;
Chenmin Sun8b43aaa2020-04-15 01:55:58 +0800428 }
Chenmin Sund4c36662020-06-22 18:21:31 +0800429 else if (unformat (line_input, "spi %u", &spi))
430 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800431 if (protocol.prot == IP_PROTOCOL_IPSEC_ESP)
432 ipsec_esp_set = true;
433 else if (protocol.prot == IP_PROTOCOL_IPSEC_AH)
434 ipsec_ah_set = true;
Chenmin Sund4c36662020-06-22 18:21:31 +0800435 }
Damjan Mariona35cc142018-03-16 01:25:27 +0100436 else if (unformat (line_input, "index %u", &flow_index))
437 ;
438 else if (unformat (line_input, "next-node %U", unformat_vlib_node, vm,
439 &flow.redirect_node_index))
440 flow.actions |= VNET_FLOW_ACTION_REDIRECT_TO_NODE;
441 else if (unformat (line_input, "mark %d", &flow.mark_flow_id))
442 flow.actions |= VNET_FLOW_ACTION_MARK;
443 else if (unformat (line_input, "buffer-advance %d",
444 &flow.buffer_advance))
445 flow.actions |= VNET_FLOW_ACTION_BUFFER_ADVANCE;
Chenmin Sune8c9f4f2019-10-15 20:36:16 +0800446 else if (unformat (line_input, "redirect-to-queue %d",
447 &flow.redirect_queue))
448 flow.actions |= VNET_FLOW_ACTION_REDIRECT_TO_QUEUE;
449 else if (unformat (line_input, "drop"))
450 flow.actions |= VNET_FLOW_ACTION_DROP;
Chenmin Sun24e2c502020-02-28 22:49:37 +0800451 else if (unformat (line_input, "rss function"))
452 {
453 if (0)
454 ;
455#undef _
Chenmin Sun34bfa502020-07-27 17:40:17 +0800456#define _(f, s) \
457 else if (unformat (line_input, s)) \
458 flow.rss_fun = VNET_RSS_FUNC_##f;
Chenmin Sun24e2c502020-02-28 22:49:37 +0800459
460 foreach_rss_function
461#undef _
462 else
463 {
464 return clib_error_return (0, "unknown input `%U'",
465 format_unformat_error, line_input);
466 }
467
468 flow.actions |= VNET_FLOW_ACTION_RSS;
469 }
470 else if (unformat (line_input, "rss types"))
471 {
472 rss_type[0] = NULL;
473 rss_type[1] = NULL;
474 rss_type[2] = NULL;
475 type_str = NULL;
476
477 if (unformat (line_input, "%s use %s and %s",
478 &rss_type[0], &rss_type[1], &rss_type[2]))
479 ;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800480 else if (unformat
481 (line_input, "%s use %s", &rss_type[0], &rss_type[1]))
Chenmin Sun24e2c502020-02-28 22:49:37 +0800482 ;
483 else if (unformat (line_input, "%s", &rss_type[0]))
484 ;
485
486#undef _
487#define _(a,b,c) \
Chenmin Sun34bfa502020-07-27 17:40:17 +0800488 else if (!clib_strcmp(c, (const char *)type_str)) \
489 flow.rss_types |= (1ULL<<a);
Chenmin Sun24e2c502020-02-28 22:49:37 +0800490
491#define check_rss_types(_str) \
Chenmin Sun34bfa502020-07-27 17:40:17 +0800492 if (_str != NULL) {\
493 type_str = _str;\
494 if (0) \
495 ; \
496 foreach_flow_rss_types \
497 else \
498 { \
499 return clib_error_return (0, "parse error: '%U'", \
500 format_unformat_error, line_input); \
501 } \
502 }
Chenmin Sun24e2c502020-02-28 22:49:37 +0800503
504 check_rss_types (rss_type[0])
505 check_rss_types (rss_type[1]) check_rss_types (rss_type[2])
506#undef _
507 flow.actions |= VNET_FLOW_ACTION_RSS;
508 }
Damjan Mariona35cc142018-03-16 01:25:27 +0100509 else if (unformat (line_input, "%U", unformat_vnet_hw_interface, vnm,
510 &hw_if_index))
511 ;
512 else
513 return clib_error_return (0, "parse error: '%U'",
514 format_unformat_error, line_input);
515 }
516
517 unformat_free (line_input);
518
519 if (hw_if_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE))
520 return clib_error_return (0, "Please specify interface name");
521
522 if (flow_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE ||
523 action == FLOW_DEL))
524 return clib_error_return (0, "Please specify flow index");
525
526 switch (action)
527 {
528 case FLOW_ADD:
Damjan Mariona35cc142018-03-16 01:25:27 +0100529 if (flow.actions == 0)
530 return clib_error_return (0, "Please specify at least one action");
Chenmin Sunbf85a982019-10-18 07:35:16 +0800531
532 /* Adjust the flow type */
Chenmin Sun34bfa502020-07-27 17:40:17 +0800533 switch (flow_class)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800534 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800535 case FLOW_ETHERNET_CLASS:
536 type = VNET_FLOW_TYPE_ETHERNET;
537 break;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800538
Chenmin Sun34bfa502020-07-27 17:40:17 +0800539 case FLOW_IPV4_CLASS:
540 if (gtpc_set)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800541 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800542 type = VNET_FLOW_TYPE_IP4_GTPC;
543 protocol.prot = IP_PROTOCOL_UDP;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800544 }
Chenmin Sun34bfa502020-07-27 17:40:17 +0800545 else if (gtpu_set)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800546 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800547 type = VNET_FLOW_TYPE_IP4_GTPU;
548 protocol.prot = IP_PROTOCOL_UDP;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800549 }
Chenmin Sun34bfa502020-07-27 17:40:17 +0800550 else if (vni_set)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800551 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800552 type = VNET_FLOW_TYPE_IP4_VXLAN;
553 protocol.prot = IP_PROTOCOL_UDP;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800554 }
Chenmin Sun34bfa502020-07-27 17:40:17 +0800555 else if (l2tpv3oip_set)
556 type = VNET_FLOW_TYPE_IP4_L2TPV3OIP;
557 else if (ipsec_esp_set)
558 type = VNET_FLOW_TYPE_IP4_IPSEC_ESP;
559 else if (ipsec_ah_set)
560 type = VNET_FLOW_TYPE_IP4_IPSEC_AH;
561 else if (tcp_udp_port_set)
562 type = VNET_FLOW_TYPE_IP4_N_TUPLE;
563 else
564 type = VNET_FLOW_TYPE_IP4;
565 break;
566 case FLOW_IPV6_CLASS:
567 if (tcp_udp_port_set)
568 type = VNET_FLOW_TYPE_IP6_N_TUPLE;
569 else if (vni_set)
570 type = VNET_FLOW_TYPE_IP6_VXLAN;
571 else
572 type = VNET_FLOW_TYPE_IP6;
573 break;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800574
Chenmin Sun34bfa502020-07-27 17:40:17 +0800575 default:
576 return clib_error_return (0,
577 "Please specify a supported flow type");
Chenmin Sunbf85a982019-10-18 07:35:16 +0800578 }
579
Chenmin Sun34bfa502020-07-27 17:40:17 +0800580 /* Assign specific field values per flow type */
581 if (flow_class == FLOW_ETHERNET_CLASS)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800582 {
Chenmin Sun4ff8d612019-12-15 01:16:45 +0800583 flow.ethernet.eth_hdr.type = eth_type;
Chenmin Sun34bfa502020-07-27 17:40:17 +0800584 }
585 else if (flow_class == FLOW_IPV4_CLASS)
586 {
587 vnet_flow_ip4_t *ip4_ptr = &flow.ip4;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800588
Chenmin Sun34bfa502020-07-27 17:40:17 +0800589 clib_memcpy (&ip4_ptr->src_addr, &ip4s,
590 sizeof (ip4_address_and_mask_t));
591 clib_memcpy (&ip4_ptr->dst_addr, &ip4d,
592 sizeof (ip4_address_and_mask_t));
593 ip4_ptr->protocol.prot = protocol.prot;
594
595 /* In this cli, we use the protocol.mask only when the flow type is
596 * VNET_FLOW_TYPE_IP4/IP6. For other cases, the IP protocol is just
597 * used to identify the next layer type: e.g. UDP/TCP or IPSEC_ESP
598 */
599 if (type == VNET_FLOW_TYPE_IP4)
600 ip4_ptr->protocol.mask = protocol.mask;
601
602 switch (protocol.prot)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800603 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800604 /* ip4-n-tuple */
605 case IP_PROTOCOL_TCP:
606 case IP_PROTOCOL_UDP:
607 flow.ip4_n_tuple.src_port = sport;
608 flow.ip4_n_tuple.dst_port = dport;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800609
Chenmin Sun34bfa502020-07-27 17:40:17 +0800610 if (type == VNET_FLOW_TYPE_IP4_GTPC)
611 flow.ip4_gtpc.teid = teid;
612 else if (type == VNET_FLOW_TYPE_IP4_GTPU)
613 flow.ip4_gtpu.teid = teid;
614 else if (type == VNET_FLOW_TYPE_IP4_VXLAN)
615 flow.ip4_vxlan.vni = vni;
616 break;
617 case IP_PROTOCOL_L2TP:
618 flow.ip4_l2tpv3oip.session_id = session_id;
619 break;
620 case IP_PROTOCOL_IPSEC_ESP:
621 flow.ip4_ipsec_esp.spi = spi;
622 break;
623 case IP_PROTOCOL_IPSEC_AH:
624 flow.ip4_ipsec_esp.spi = spi;
625 break;
626 default:
627 break;
628 }
629 }
630 else if (flow_class == FLOW_IPV6_CLASS)
631 {
632 vnet_flow_ip6_t *ip6_ptr = &flow.ip6;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800633
Chenmin Sunbf85a982019-10-18 07:35:16 +0800634 clib_memcpy (&flow.ip6_n_tuple.src_addr, &ip6s,
635 sizeof (ip6_address_and_mask_t));
636 clib_memcpy (&flow.ip6_n_tuple.dst_addr, &ip6d,
637 sizeof (ip6_address_and_mask_t));
Chenmin Sunbf85a982019-10-18 07:35:16 +0800638
Chenmin Sun34bfa502020-07-27 17:40:17 +0800639 ip6_ptr->protocol.prot = protocol.prot;
640
641 /* In this cli, we use the protocol.mask only when the flow type is
642 * VNET_FLOW_TYPE_IP4/IP6. For other cases, the IP protocol is just
643 * used to identify the next layer type: e.g. UDP/TCP or IPSEC_ESP
644 */
645 if (type == VNET_FLOW_TYPE_IP6)
646 ip6_ptr->protocol.mask = protocol.mask;
647
648 switch (protocol.prot)
Chenmin Sunbf85a982019-10-18 07:35:16 +0800649 {
Chenmin Sun34bfa502020-07-27 17:40:17 +0800650 /* ip6-n-tuple */
651 case IP_PROTOCOL_TCP:
652 case IP_PROTOCOL_UDP:
653 flow.ip6_n_tuple.src_port = sport;
654 flow.ip6_n_tuple.dst_port = dport;
Chenmin Sunbf85a982019-10-18 07:35:16 +0800655
Chenmin Sun34bfa502020-07-27 17:40:17 +0800656 if (type == VNET_FLOW_TYPE_IP6_VXLAN)
657 flow.ip6_vxlan.vni = vni;
658 break;
659 default:
660 break;
661 }
Chenmin Sunbf85a982019-10-18 07:35:16 +0800662 }
663
664 flow.type = type;
Damjan Mariona35cc142018-03-16 01:25:27 +0100665 rv = vnet_flow_add (vnm, &flow, &flow_index);
Chenmin Sunbf85a982019-10-18 07:35:16 +0800666 if (!rv)
Chenmin Sun34bfa502020-07-27 17:40:17 +0800667 vlib_cli_output (vm, "flow %u added", flow_index);
Chenmin Sunbf85a982019-10-18 07:35:16 +0800668
Damjan Mariona35cc142018-03-16 01:25:27 +0100669 break;
670 case FLOW_DEL:
671 rv = vnet_flow_del (vnm, flow_index);
672 break;
673 case FLOW_ENABLE:
674 rv = vnet_flow_enable (vnm, flow_index, hw_if_index);
675 break;
676 case FLOW_DISABLE:
677 rv = vnet_flow_disable (vnm, flow_index, hw_if_index);
678 break;
679 default:
680 return clib_error_return (0, "please specify action (add, del, enable,"
681 " disable)");
682 }
683
684 if (rv < 0)
685 return clib_error_return (0, "flow error: %U", format_flow_error, rv);
Chenmin Sun34bfa502020-07-27 17:40:17 +0800686
Damjan Mariona35cc142018-03-16 01:25:27 +0100687 return 0;
688}
689
690/* *INDENT-OFF* */
691VLIB_CLI_COMMAND (test_flow_command, static) = {
692 .path = "test flow",
Chenmin Sun34bfa502020-07-27 17:40:17 +0800693 .short_help = "test flow [add|del|enable|disable] [index <id>] "
694 "[src-ip <ip-addr/mask>] [dst-ip <ip-addr/mask>] "
695 "[ip6-src-ip <ip-addr/mask>] [ip6-dst-ip <ip-addr/mask>] "
696 "[src-port <port/mask>] [dst-port <port/mask>] "
697 "[proto <ip-proto>] "
698 "[gtpc teid <teid>] [gtpu teid <teid>] [vxlan <vni>] "
699 "[session id <session>] [spi <spi>]"
700 "[next-node <node>] [mark <id>] [buffer-advance <len>] "
701 "[redirect-to-queue <queue>] [drop] "
702 "[rss function <name>] [rss types <flow type>]",
Damjan Mariona35cc142018-03-16 01:25:27 +0100703 .function = test_flow,
704};
705/* *INDENT-ON* */
706
Damjan Mariona35cc142018-03-16 01:25:27 +0100707static u8 *
708format_flow_match_element (u8 * s, va_list * args)
709{
710 char *type = va_arg (*args, char *);
711 void *ptr = va_arg (*args, void *);
712
713 if (strncmp (type, "u8", 2) == 0)
714 return format (s, "%d", *(u8 *) ptr);
715
716 if (strncmp (type, "u16", 3) == 0)
717 return format (s, "%d", *(u16 *) ptr);
718
719 if (strncmp (type, "u32", 3) == 0)
720 return format (s, "%d", *(u32 *) ptr);
721
Chenmin Sun34bfa502020-07-27 17:40:17 +0800722 if (strncmp (type, "ethernet_header_t", 13) == 0)
723 {
724 ethernet_max_header_t m;
725 memset (&m, 0, sizeof (m));
726 m.ethernet = *(ethernet_header_t *) ptr;
727 /* convert the ethernet type to net order */
728 m.ethernet.type = clib_host_to_net_u16 (m.ethernet.type);
729 return format (s, "%U", format_ethernet_header, &m);
730 }
731
Damjan Mariona35cc142018-03-16 01:25:27 +0100732 if (strncmp (type, "ip4_address_t", 13) == 0)
733 return format (s, "%U", format_ip4_address, ptr);
734
735 if (strncmp (type, "ip4_address_and_mask_t", 13) == 0)
736 return format (s, "%U", format_ip4_address_and_mask, ptr);
737
738 if (strncmp (type, "ip6_address_t", 13) == 0)
739 return format (s, "%U", format_ip6_address, ptr);
740
741 if (strncmp (type, "ip6_address_and_mask_t", 13) == 0)
742 return format (s, "%U", format_ip6_address_and_mask, ptr);
743
Chenmin Sun34bfa502020-07-27 17:40:17 +0800744 if (strncmp (type, "ip_prot_and_mask_t", 13) == 0)
745 return format (s, "%U", format_ip_protocol_and_mask, ptr);
Damjan Mariona35cc142018-03-16 01:25:27 +0100746
747 if (strncmp (type, "ip_port_and_mask_t", 18) == 0)
748 return format (s, "%U", format_ip_port_and_mask, ptr);
749
750 s = format (s, "unknown type '%s'", type);
751 return s;
752}
753
754#define _fe(a,b) s2 = format (s2, "%s%s %U", s2 ? ", ":"", #b, \
755 format_flow_match_element, #a, &f->b);
756#define _(a,b,c) \
757u8 * format_flow_match_##b (u8 * s, va_list * args) \
758{ \
759 vnet_flow_##b##_t *f = __builtin_va_arg (*args, vnet_flow_##b##_t *); \
760 u8 *s2 = 0; \
761foreach_flow_entry_##b \
762 s = format (s, "%v", s2);; \
763 vec_free (s2); \
764return s; \
765}
766foreach_flow_type
767#undef _
768#undef _fe
769static u8 *
770format_flow_match (u8 * s, va_list * args)
771{
772 vnet_flow_t *f = va_arg (*args, vnet_flow_t *);
773
774#define _(a,b,c) \
775 if (f->type == VNET_FLOW_TYPE_##a) \
776 return format (s, "%U", format_flow_match_##b, &f->b);
777 foreach_flow_type;
778#undef _
779
780 return s;
781}
782
783static u8 *
784format_flow (u8 * s, va_list * args)
785{
786 vlib_main_t *vm = vlib_get_main ();
787 vnet_flow_t *f = va_arg (*args, vnet_flow_t *);
788 u32 indent = format_get_indent (s);
789 u8 *t = 0;
790
791 s = format (s, "flow-index %u type %s active %u",
792 f->index, flow_type_strings[f->type],
793 hash_elts (f->private_data)),
794 s = format (s, "\n%Umatch: %U", format_white_space, indent + 2,
795 format_flow_match, f);
796 s = format (s, "\n%Uaction: %U", format_white_space, indent + 2,
797 format_flow_actions, f->actions);
798
Chenmin Sun34bfa502020-07-27 17:40:17 +0800799 if (f->actions & VNET_FLOW_ACTION_DROP)
800 t = format (t, "%sdrop", t ? ", " : "");
801
Damjan Mariona35cc142018-03-16 01:25:27 +0100802 if (f->actions & VNET_FLOW_ACTION_MARK)
803 t = format (t, "%smark %u", t ? ", " : "", f->mark_flow_id);
804
Chenmin Sun34bfa502020-07-27 17:40:17 +0800805 if (f->actions & VNET_FLOW_ACTION_REDIRECT_TO_QUEUE)
806 t =
807 format (t, "%sredirect-to-queue %u", t ? ", " : "", f->redirect_queue);
808
Damjan Mariona35cc142018-03-16 01:25:27 +0100809 if (f->actions & VNET_FLOW_ACTION_REDIRECT_TO_NODE)
810 t = format (t, "%snext-node %U", t ? ", " : "",
811 format_vlib_node_name, vm, f->redirect_node_index);
812
813 if (f->actions & VNET_FLOW_ACTION_BUFFER_ADVANCE)
814 t = format (t, "%sbuffer-advance %d", t ? ", " : "", f->buffer_advance);
815
Chenmin Sun34bfa502020-07-27 17:40:17 +0800816 if (f->actions & VNET_FLOW_ACTION_RSS)
817 {
818 t = format (t, "%srss function %U", t ? ", " : "",
819 format_rss_function, f->rss_fun);
820 t = format (t, "%srss types %U", t ? ", " : "",
821 format_rss_types, f->rss_types);
822 }
823
Damjan Mariona35cc142018-03-16 01:25:27 +0100824 if (t)
825 {
826 s = format (s, "\n%U%v", format_white_space, indent + 4, t);
827 vec_free (t);
828 }
829
830 return s;
831}
832
833/*
834 * fd.io coding-style-patch-verification: ON
835 *
836 * Local Variables:
837 * eval: (c-set-style "gnu")
838 * End:
839 */