Line data Source code
1 : /*
2 : * Copyright (c) 2018 Cisco and/or its affiliates.
3 : * Licensed under the Apache License, Version 2.0 (the "License");
4 : * you may not use this file except in compliance with the License.
5 : * You may obtain a copy of the License at:
6 : *
7 : * http://www.apache.org/licenses/LICENSE-2.0
8 : *
9 : * Unless required by applicable law or agreed to in writing, software
10 : * distributed under the License is distributed on an "AS IS" BASIS,
11 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : * See the License for the specific language governing permissions and
13 : * limitations under the License.
14 : */
15 : #include <stddef.h>
16 :
17 : #include <vnet/vnet.h>
18 : #include <vnet/devices/devices.h>
19 : #include <vnet/ip/ip.h>
20 : #include <vnet/ethernet/ethernet.h>
21 : #include <vnet/ethernet/packet.h>
22 : #include <vnet/flow/flow.h>
23 :
24 : static format_function_t format_flow;
25 :
26 : uword
27 0 : unformat_ip_port_and_mask (unformat_input_t * input, va_list * args)
28 : {
29 0 : ip_port_and_mask_t *pm = va_arg (*args, ip_port_and_mask_t *);
30 0 : u32 port = 0, mask = 0;
31 :
32 0 : if (unformat (input, "any"))
33 : ;
34 0 : else if (unformat (input, "%u/%u", &port, &mask))
35 : ;
36 0 : else if (unformat (input, "%u/0x%x", &port, &mask))
37 : ;
38 0 : else if (unformat (input, "%u", &port))
39 0 : mask = 0xffff;
40 : else
41 0 : return 0;
42 :
43 0 : if (port > 0xffff || mask > 0xffff)
44 0 : return 0;
45 :
46 0 : pm->port = port;
47 0 : pm->mask = mask;
48 0 : return 1;
49 : }
50 :
51 : u8 *
52 0 : format_ip_port_and_mask (u8 * s, va_list * args)
53 : {
54 0 : ip_port_and_mask_t *pm = va_arg (*args, ip_port_and_mask_t *);
55 :
56 0 : if (pm->port == 0 && pm->mask == 0)
57 0 : return format (s, "any");
58 :
59 0 : if (pm->mask == 0xffff)
60 0 : return format (s, "%u", pm->port);
61 :
62 0 : return format (s, "%u/0x%x", pm->port, pm->mask);
63 : }
64 :
65 : uword
66 0 : unformat_ip_protocol_and_mask (unformat_input_t * input, va_list * args)
67 : {
68 0 : ip_prot_and_mask_t *pm = va_arg (*args, ip_prot_and_mask_t *);
69 0 : u32 prot = 0, mask = 0;
70 :
71 0 : if (unformat (input, "any"))
72 : ;
73 0 : else if (unformat (input, "%U", unformat_ip_protocol, &prot))
74 0 : mask = 0xFF;
75 0 : else if (unformat (input, "%u", &prot))
76 0 : mask = 0xFF;
77 : else
78 0 : return 0;
79 :
80 0 : if (prot > 0XFF || mask > 0xFF)
81 0 : return 0;
82 :
83 0 : pm->prot = prot;
84 0 : pm->mask = mask;
85 0 : return 1;
86 : }
87 :
88 : u8 *
89 0 : format_ip_protocol_and_mask (u8 * s, va_list * args)
90 : {
91 0 : ip_prot_and_mask_t *pm = va_arg (*args, ip_prot_and_mask_t *);
92 :
93 0 : if (pm->prot == 0 && pm->mask == 0)
94 0 : return format (s, "any");
95 :
96 0 : return format (s, "%U", format_ip_protocol, pm->prot);
97 : }
98 :
99 : u8 *
100 0 : format_flow_error (u8 * s, va_list * args)
101 : {
102 0 : int error = va_arg (*args, int);
103 :
104 0 : if (error == 0)
105 0 : return format (s, "no error");
106 :
107 : #define _(v,n,str) if (error == v) return format (s, #str);
108 0 : foreach_flow_error;
109 : #undef _
110 :
111 0 : return format (s, "unknown error (%d)", error);
112 : }
113 :
114 : u8 *
115 0 : format_flow_actions (u8 * s, va_list * args)
116 : {
117 0 : u32 actions = va_arg (*args, u32);
118 0 : u8 *t = 0;
119 :
120 : #define _(a, b, c) if (actions & (1 << a)) \
121 : t = format (t, "%s%s", t ? " ":"", c);
122 0 : foreach_flow_action
123 : #undef _
124 0 : s = format (s, "%v", t);
125 0 : vec_free (t);
126 0 : return s;
127 : }
128 :
129 : u8 *
130 0 : format_flow_enabled_hw (u8 * s, va_list * args)
131 : {
132 0 : u32 flow_index = va_arg (*args, u32);
133 0 : vnet_flow_t *f = vnet_get_flow (flow_index);
134 0 : if (f == 0)
135 0 : return format (s, "not found");
136 :
137 0 : u8 *t = 0;
138 : u32 hw_if_index;
139 : uword private_data;
140 0 : vnet_main_t *vnm = vnet_get_main ();
141 : /* *INDENT-OFF* */
142 0 : 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 0 : s = format (s, "%v", t);
149 0 : vec_free (t);
150 0 : return s;
151 : }
152 :
153 : u8 *
154 0 : format_rss_function (u8 * s, va_list * args)
155 : {
156 0 : 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 0 : foreach_rss_function
166 : #undef _
167 0 : return format (s, "unknown");
168 : }
169 :
170 : u8 *
171 0 : format_rss_types (u8 * s, va_list * args)
172 : {
173 0 : 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 0 : foreach_flow_rss_types
181 : #undef _
182 0 : return s;
183 : }
184 :
185 : static const char *flow_type_strings[] = { 0,
186 : #define _(a,b,c) c,
187 : foreach_flow_type
188 : #undef _
189 : };
190 :
191 : static clib_error_t *
192 0 : show_flow_entry (vlib_main_t * vm, unformat_input_t * input,
193 : vlib_cli_command_t * cmd_arg)
194 : {
195 0 : vnet_main_t *vnm = vnet_get_main ();
196 0 : vnet_flow_main_t *fm = &flow_main;
197 0 : 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 0 : u32 index = ~0, hw_if_index;
203 :
204 0 : if (!unformat_user (input, unformat_line_input, line_input))
205 0 : goto no_args;
206 :
207 0 : while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
208 : {
209 0 : if (unformat (line_input, "index %u", &index))
210 : ;
211 : else
212 0 : return clib_error_return (0, "parse error: '%U'",
213 : format_unformat_error, line_input);
214 : }
215 :
216 0 : unformat_free (line_input);
217 :
218 0 : if (index != ~0)
219 : {
220 0 : if ((f = vnet_get_flow (index)) == 0)
221 0 : return clib_error_return (0, "no such flow");
222 :
223 0 : vlib_cli_output (vm, "%-10s: %u", "index", f->index);
224 0 : vlib_cli_output (vm, "%-10s: %s", "type", flow_type_strings[f->type]);
225 0 : vlib_cli_output (vm, "%-10s: %U", "match", format_flow, f);
226 0 : if (f->type == VNET_FLOW_TYPE_GENERIC)
227 : {
228 0 : vlib_cli_output (vm, "%s: %s", "spec", f->generic.pattern.spec);
229 0 : vlib_cli_output (vm, "%s: %s", "mask", f->generic.pattern.mask);
230 : }
231 : /* *INDENT-OFF* */
232 0 : 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 0 : return 0;
244 : }
245 :
246 0 : no_args:
247 : /* *INDENT-OFF* */
248 0 : pool_foreach (f, fm->global_flow_pool)
249 : {
250 0 : vlib_cli_output (vm, "%U\n", format_flow, f);
251 0 : if (f->type == VNET_FLOW_TYPE_GENERIC)
252 : {
253 0 : vlib_cli_output (vm, "%s: %s", "spec", f->generic.pattern.spec);
254 0 : vlib_cli_output (vm, "%s: %s", "mask", f->generic.pattern.mask);
255 : }
256 : }
257 : /* *INDENT-ON* */
258 :
259 0 : return 0;
260 : }
261 :
262 : /* *INDENT-OFF* */
263 272887 : VLIB_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 :
270 : static clib_error_t *
271 0 : show_flow_ranges (vlib_main_t * vm, unformat_input_t * input,
272 : vlib_cli_command_t * cmd_arg)
273 : {
274 0 : vnet_flow_main_t *fm = &flow_main;
275 0 : vnet_flow_range_t *r = 0;
276 :
277 0 : vlib_cli_output (vm, "%8s %8s %s", "Start", "Count", "Owner");
278 :
279 : /* *INDENT-OFF* */
280 0 : vec_foreach (r, fm->ranges)
281 : {
282 0 : vlib_cli_output (vm, "%8u %8u %s", r->start, r->count, r->owner);
283 : };
284 : /* *INDENT-ON* */
285 0 : return 0;
286 : }
287 :
288 : /* *INDENT-OFF* */
289 272887 : VLIB_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 :
296 : static clib_error_t *
297 0 : show_flow_interface (vlib_main_t * vm, unformat_input_t * input,
298 : vlib_cli_command_t * cmd_arg)
299 : {
300 0 : vnet_main_t *vnm = vnet_get_main ();
301 : vnet_hw_interface_t *hi;
302 : vnet_device_class_t *dev_class;
303 0 : unformat_input_t _line_input, *line_input = &_line_input;
304 0 : u32 hw_if_index = ~0;
305 :
306 0 : if (unformat_user (input, unformat_line_input, line_input))
307 : {
308 0 : while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
309 : {
310 0 : if (unformat (line_input, "%U",
311 : unformat_vnet_hw_interface, vnm, &hw_if_index))
312 : ;
313 : else
314 0 : return clib_error_return (0, "parse error: '%U'",
315 : format_unformat_error, line_input);
316 : }
317 0 : unformat_free (line_input);
318 : }
319 :
320 0 : if (hw_if_index == ~0)
321 0 : return clib_error_return (0, "please specify interface");
322 :
323 0 : hi = vnet_get_hw_interface (vnm, hw_if_index);
324 0 : dev_class = vnet_get_device_class (vnm, hi->dev_class_index);
325 0 : if (dev_class->format_flow == 0)
326 0 : return clib_error_return (0, "not supported");
327 :
328 0 : vlib_cli_output (vm, "%U", dev_class->format_flow, hi->dev_instance, ~0, 0);
329 0 : return 0;
330 : }
331 :
332 : /* *INDENT-OFF* */
333 272887 : VLIB_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 :
340 : static clib_error_t *
341 0 : test_flow (vlib_main_t * vm, unformat_input_t * input,
342 : vlib_cli_command_t * cmd_arg)
343 : {
344 : vnet_flow_t flow;
345 0 : vnet_main_t *vnm = vnet_get_main ();
346 0 : 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 0 : } action = FLOW_UNKNOWN_ACTION;
355 : enum
356 : {
357 : FLOW_UNKNOWN_CLASS,
358 : FLOW_ETHERNET_CLASS,
359 : FLOW_IPV4_CLASS,
360 : FLOW_IPV6_CLASS,
361 0 : } flow_class = FLOW_UNKNOWN_CLASS;
362 :
363 0 : u32 hw_if_index = ~0, flow_index = ~0;
364 : int rv;
365 0 : u32 teid = 0, session_id = 0, spi = 0;
366 0 : u32 vni = 0;
367 0 : u32 queue_start = 0, queue_end = 0;
368 0 : vnet_flow_type_t type = VNET_FLOW_TYPE_UNKNOWN;
369 0 : ip4_address_and_mask_t ip4s = {}, in_ip4s = {};
370 0 : ip4_address_and_mask_t ip4d = {}, in_ip4d = {};
371 0 : ip6_address_and_mask_t ip6s = {}, in_ip6s = {};
372 0 : ip6_address_and_mask_t ip6d = {}, in_ip6d = {};
373 0 : ip_port_and_mask_t sport = {}, in_sport = {};
374 0 : ip_port_and_mask_t dport = {}, in_dport = {};
375 0 : ip_prot_and_mask_t protocol = {}, in_proto = {};
376 : u16 eth_type;
377 0 : bool inner_ip4_set = false, inner_ip6_set = false;
378 0 : bool tcp_udp_port_set = false, inner_port_set = false;
379 0 : bool gtpc_set = false;
380 0 : bool gtpu_set = false;
381 0 : bool vni_set = false;
382 0 : bool l2tpv3oip_set = false;
383 0 : bool ipsec_esp_set = false, ipsec_ah_set = false;
384 0 : u8 *rss_type[3] = { };
385 0 : u8 *type_str = NULL;
386 0 : u8 *spec = NULL;
387 0 : u8 *mask = NULL;
388 :
389 0 : clib_memset (&flow, 0, sizeof (vnet_flow_t));
390 0 : flow.index = ~0;
391 0 : flow.actions = 0;
392 :
393 0 : if (!unformat_user (input, unformat_line_input, line_input))
394 0 : return 0;
395 :
396 0 : while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
397 : {
398 0 : if (unformat (line_input, "add"))
399 0 : action = FLOW_ADD;
400 0 : else if (unformat (line_input, "del"))
401 0 : action = FLOW_DEL;
402 0 : else if (unformat (line_input, "enable"))
403 0 : action = FLOW_ENABLE;
404 0 : else if (unformat (line_input, "disable"))
405 0 : action = FLOW_DISABLE;
406 0 : else if (unformat (line_input, "spec %s", &spec))
407 : ;
408 0 : else if (unformat (line_input, "mask %s", &mask))
409 : ;
410 0 : else if (unformat (line_input, "eth-type %U",
411 : unformat_ethernet_type_host_byte_order, ð_type))
412 0 : flow_class = FLOW_ETHERNET_CLASS;
413 0 : else if (unformat (line_input, "src-ip %U",
414 : unformat_ip4_address_and_mask, &ip4s))
415 0 : flow_class = FLOW_IPV4_CLASS;
416 0 : else if (unformat (line_input, "dst-ip %U",
417 : unformat_ip4_address_and_mask, &ip4d))
418 0 : flow_class = FLOW_IPV4_CLASS;
419 0 : else if (unformat (line_input, "in-src-ip %U",
420 : unformat_ip4_address_and_mask, &in_ip4s))
421 0 : inner_ip4_set = true;
422 0 : else if (unformat (line_input, "in-dst-ip %U",
423 : unformat_ip4_address_and_mask, &in_ip4d))
424 0 : inner_ip4_set = true;
425 0 : else if (unformat (line_input, "ip6-src-ip %U",
426 : unformat_ip6_address_and_mask, &ip6s))
427 0 : flow_class = FLOW_IPV6_CLASS;
428 0 : else if (unformat (line_input, "ip6-dst-ip %U",
429 : unformat_ip6_address_and_mask, &ip6d))
430 0 : flow_class = FLOW_IPV6_CLASS;
431 0 : else if (unformat (line_input, "in-ip6-src-ip %U",
432 : unformat_ip6_address_and_mask, &in_ip6s))
433 0 : inner_ip6_set = true;
434 0 : else if (unformat (line_input, "in-ip6-dst-ip %U",
435 : unformat_ip6_address_and_mask, &in_ip6d))
436 0 : inner_ip6_set = true;
437 0 : else if (unformat (line_input, "src-port %U", unformat_ip_port_and_mask,
438 : &sport))
439 0 : tcp_udp_port_set = true;
440 0 : else if (unformat (line_input, "dst-port %U", unformat_ip_port_and_mask,
441 : &dport))
442 0 : tcp_udp_port_set = true;
443 : else
444 0 : if (unformat
445 : (line_input, "proto %U", unformat_ip_protocol_and_mask,
446 : &protocol))
447 : ;
448 0 : else if (unformat (line_input, "in-src-port %U",
449 : unformat_ip_port_and_mask, &in_sport))
450 0 : inner_port_set = true;
451 0 : else if (unformat (line_input, "in-dst-port %U",
452 : unformat_ip_port_and_mask, &in_dport))
453 0 : inner_port_set = true;
454 0 : else if (unformat (line_input, "in-proto %U",
455 : unformat_ip_protocol_and_mask, &in_proto))
456 : ;
457 0 : else if (unformat (line_input, "gtpc teid %u", &teid))
458 0 : gtpc_set = true;
459 0 : else if (unformat (line_input, "gtpu teid %u", &teid))
460 0 : gtpu_set = true;
461 0 : else if (unformat (line_input, "vxlan vni %u", &vni))
462 0 : vni_set = true;
463 0 : else if (unformat (line_input, "session id %u", &session_id))
464 : {
465 0 : if (protocol.prot == IP_PROTOCOL_L2TP)
466 0 : l2tpv3oip_set = true;
467 : }
468 0 : else if (unformat (line_input, "spi %u", &spi))
469 : {
470 0 : if (protocol.prot == IP_PROTOCOL_IPSEC_ESP)
471 0 : ipsec_esp_set = true;
472 0 : else if (protocol.prot == IP_PROTOCOL_IPSEC_AH)
473 0 : ipsec_ah_set = true;
474 : }
475 0 : else if (unformat (line_input, "index %u", &flow_index))
476 : ;
477 0 : else if (unformat (line_input, "next-node %U", unformat_vlib_node, vm,
478 : &flow.redirect_node_index))
479 0 : flow.actions |= VNET_FLOW_ACTION_REDIRECT_TO_NODE;
480 0 : else if (unformat (line_input, "mark %d", &flow.mark_flow_id))
481 0 : flow.actions |= VNET_FLOW_ACTION_MARK;
482 0 : else if (unformat (line_input, "buffer-advance %d",
483 : &flow.buffer_advance))
484 0 : flow.actions |= VNET_FLOW_ACTION_BUFFER_ADVANCE;
485 0 : else if (unformat (line_input, "redirect-to-queue %d",
486 : &flow.redirect_queue))
487 0 : flow.actions |= VNET_FLOW_ACTION_REDIRECT_TO_QUEUE;
488 0 : else if (unformat (line_input, "drop"))
489 0 : flow.actions |= VNET_FLOW_ACTION_DROP;
490 0 : else if (unformat (line_input, "rss function"))
491 : {
492 : if (0)
493 : ;
494 : #undef _
495 : #define _(f, s) \
496 : else if (unformat (line_input, s)) \
497 : flow.rss_fun = VNET_RSS_FUNC_##f;
498 :
499 0 : foreach_rss_function
500 : #undef _
501 : else
502 : {
503 0 : return clib_error_return (0, "unknown input `%U'",
504 : format_unformat_error, line_input);
505 : }
506 :
507 0 : flow.actions |= VNET_FLOW_ACTION_RSS;
508 : }
509 0 : else if (unformat (line_input, "rss types"))
510 : {
511 0 : rss_type[0] = NULL;
512 0 : rss_type[1] = NULL;
513 0 : rss_type[2] = NULL;
514 0 : type_str = NULL;
515 :
516 0 : if (unformat (line_input, "%s use %s and %s",
517 : &rss_type[0], &rss_type[1], &rss_type[2]))
518 : ;
519 0 : else if (unformat
520 : (line_input, "%s use %s", &rss_type[0], &rss_type[1]))
521 : ;
522 0 : else if (unformat (line_input, "%s", &rss_type[0]))
523 : ;
524 :
525 : #undef _
526 : #define _(a,b,c) \
527 : else if (!clib_strcmp(c, (const char *)type_str)) \
528 : flow.rss_types |= (1ULL<<a);
529 :
530 : #define check_rss_types(_str) \
531 : 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 : }
542 :
543 0 : check_rss_types (rss_type[0])
544 0 : check_rss_types (rss_type[1]) check_rss_types (rss_type[2])
545 : #undef _
546 0 : flow.actions |= VNET_FLOW_ACTION_RSS;
547 : }
548 0 : else if (unformat (line_input, "rss queues"))
549 : {
550 0 : if (unformat (line_input, "%d to %d", &queue_start, &queue_end))
551 : ;
552 : else
553 : {
554 0 : return clib_error_return (0, "unknown input `%U'",
555 : format_unformat_error, line_input);
556 : }
557 :
558 0 : flow.queue_index = queue_start;
559 0 : flow.queue_num = queue_end - queue_start + 1;
560 :
561 0 : flow.actions |= VNET_FLOW_ACTION_RSS;
562 : }
563 0 : else if (unformat (line_input, "%U", unformat_vnet_hw_interface, vnm,
564 : &hw_if_index))
565 : ;
566 : else
567 0 : return clib_error_return (0, "parse error: '%U'",
568 : format_unformat_error, line_input);
569 : }
570 :
571 0 : unformat_free (line_input);
572 :
573 0 : if (hw_if_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE))
574 0 : return clib_error_return (0, "Please specify interface name");
575 :
576 0 : if (flow_index == ~0 && (action == FLOW_ENABLE || action == FLOW_DISABLE ||
577 : action == FLOW_DEL))
578 0 : return clib_error_return (0, "Please specify flow index");
579 :
580 0 : switch (action)
581 : {
582 0 : case FLOW_ADD:
583 0 : if (flow.actions == 0)
584 0 : return clib_error_return (0, "Please specify at least one action");
585 :
586 : /* Adjust the flow type */
587 : switch (flow_class)
588 : {
589 0 : case FLOW_ETHERNET_CLASS:
590 0 : type = VNET_FLOW_TYPE_ETHERNET;
591 0 : break;
592 :
593 0 : case FLOW_IPV4_CLASS:
594 0 : if (gtpc_set)
595 : {
596 0 : type = VNET_FLOW_TYPE_IP4_GTPC;
597 0 : protocol.prot = IP_PROTOCOL_UDP;
598 : }
599 0 : else if (gtpu_set)
600 : {
601 0 : type = VNET_FLOW_TYPE_IP4_GTPU;
602 0 : protocol.prot = IP_PROTOCOL_UDP;
603 : }
604 0 : else if (vni_set)
605 : {
606 0 : type = VNET_FLOW_TYPE_IP4_VXLAN;
607 0 : protocol.prot = IP_PROTOCOL_UDP;
608 : }
609 0 : else if (l2tpv3oip_set)
610 0 : type = VNET_FLOW_TYPE_IP4_L2TPV3OIP;
611 0 : else if (ipsec_esp_set)
612 0 : type = VNET_FLOW_TYPE_IP4_IPSEC_ESP;
613 0 : else if (ipsec_ah_set)
614 0 : type = VNET_FLOW_TYPE_IP4_IPSEC_AH;
615 0 : else if (tcp_udp_port_set)
616 0 : type = VNET_FLOW_TYPE_IP4_N_TUPLE;
617 0 : else if (inner_ip4_set)
618 : {
619 0 : if (inner_port_set)
620 0 : type = VNET_FLOW_TYPE_IP4_IP4_N_TUPLE;
621 : else
622 0 : type = VNET_FLOW_TYPE_IP4_IP4;
623 0 : protocol.prot = IP_PROTOCOL_IP_IN_IP;
624 : }
625 0 : else if (inner_ip6_set)
626 : {
627 0 : if (inner_port_set)
628 0 : type = VNET_FLOW_TYPE_IP4_IP6_N_TUPLE;
629 : else
630 0 : type = VNET_FLOW_TYPE_IP4_IP6;
631 0 : protocol.prot = IP_PROTOCOL_IPV6;
632 : }
633 : else
634 0 : type = VNET_FLOW_TYPE_IP4;
635 0 : break;
636 0 : case FLOW_IPV6_CLASS:
637 0 : if (tcp_udp_port_set)
638 0 : type = VNET_FLOW_TYPE_IP6_N_TUPLE;
639 0 : else if (vni_set)
640 0 : type = VNET_FLOW_TYPE_IP6_VXLAN;
641 0 : else if (inner_ip4_set)
642 : {
643 0 : if (inner_port_set)
644 0 : type = VNET_FLOW_TYPE_IP6_IP4_N_TUPLE;
645 : else
646 0 : type = VNET_FLOW_TYPE_IP6_IP4;
647 0 : protocol.prot = IP_PROTOCOL_IP_IN_IP;
648 : }
649 0 : else if (inner_ip6_set)
650 : {
651 0 : if (inner_port_set)
652 0 : type = VNET_FLOW_TYPE_IP6_IP6_N_TUPLE;
653 : else
654 0 : type = VNET_FLOW_TYPE_IP6_IP6;
655 0 : protocol.prot = IP_PROTOCOL_IPV6;
656 : }
657 : else
658 0 : type = VNET_FLOW_TYPE_IP6;
659 0 : break;
660 :
661 0 : default:
662 0 : if (spec && mask)
663 : {
664 0 : type = VNET_FLOW_TYPE_GENERIC;
665 0 : break;
666 : }
667 0 : return clib_error_return (0,
668 : "Please specify a supported flow type");
669 : }
670 :
671 : /* Assign specific field values per flow type */
672 0 : if (flow_class == FLOW_ETHERNET_CLASS)
673 : {
674 0 : flow.ethernet.eth_hdr.type = eth_type;
675 : }
676 0 : else if (flow_class == FLOW_IPV4_CLASS)
677 : {
678 0 : vnet_flow_ip4_t *ip4_ptr = &flow.ip4;
679 :
680 0 : clib_memcpy (&ip4_ptr->src_addr, &ip4s,
681 : sizeof (ip4_address_and_mask_t));
682 0 : clib_memcpy (&ip4_ptr->dst_addr, &ip4d,
683 : sizeof (ip4_address_and_mask_t));
684 0 : 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 0 : if (type == VNET_FLOW_TYPE_IP4)
691 0 : ip4_ptr->protocol.mask = protocol.mask;
692 :
693 0 : switch (protocol.prot)
694 : {
695 : /* ip4-n-tuple */
696 0 : case IP_PROTOCOL_TCP:
697 : case IP_PROTOCOL_UDP:
698 0 : flow.ip4_n_tuple.src_port = sport;
699 0 : flow.ip4_n_tuple.dst_port = dport;
700 :
701 0 : if (type == VNET_FLOW_TYPE_IP4_GTPC)
702 0 : flow.ip4_gtpc.teid = teid;
703 0 : else if (type == VNET_FLOW_TYPE_IP4_GTPU)
704 0 : flow.ip4_gtpu.teid = teid;
705 0 : else if (type == VNET_FLOW_TYPE_IP4_VXLAN)
706 0 : flow.ip4_vxlan.vni = vni;
707 0 : break;
708 0 : case IP_PROTOCOL_L2TP:
709 0 : flow.ip4_l2tpv3oip.session_id = session_id;
710 0 : break;
711 0 : case IP_PROTOCOL_IPSEC_ESP:
712 0 : flow.ip4_ipsec_esp.spi = spi;
713 0 : break;
714 0 : case IP_PROTOCOL_IPSEC_AH:
715 0 : flow.ip4_ipsec_esp.spi = spi;
716 0 : break;
717 0 : case IP_PROTOCOL_IP_IN_IP:
718 0 : clib_memcpy (&flow.ip4_ip4.in_src_addr, &in_ip4s,
719 : sizeof (ip4_address_and_mask_t));
720 0 : clib_memcpy (&flow.ip4_ip4.in_dst_addr, &in_ip4d,
721 : sizeof (ip4_address_and_mask_t));
722 0 : if (type == VNET_FLOW_TYPE_IP4_IP4_N_TUPLE)
723 : {
724 0 : flow.ip4_ip4.in_protocol.prot = in_proto.prot;
725 0 : flow.ip4_ip4_n_tuple.in_src_port = in_sport;
726 0 : flow.ip4_ip4_n_tuple.in_dst_port = in_dport;
727 : }
728 0 : break;
729 0 : case IP_PROTOCOL_IPV6:
730 0 : clib_memcpy (&flow.ip4_ip6.in_src_addr, &in_ip6s,
731 : sizeof (ip6_address_and_mask_t));
732 0 : clib_memcpy (&flow.ip4_ip6.in_dst_addr, &in_ip6d,
733 : sizeof (ip6_address_and_mask_t));
734 0 : if (type == VNET_FLOW_TYPE_IP4_IP6_N_TUPLE)
735 : {
736 0 : flow.ip4_ip6.in_protocol.prot = in_proto.prot;
737 0 : flow.ip4_ip6_n_tuple.in_src_port = in_sport;
738 0 : flow.ip4_ip6_n_tuple.in_dst_port = in_dport;
739 : }
740 0 : break;
741 0 : default:
742 0 : break;
743 : }
744 : }
745 0 : else if (flow_class == FLOW_IPV6_CLASS)
746 : {
747 0 : vnet_flow_ip6_t *ip6_ptr = &flow.ip6;
748 :
749 0 : clib_memcpy (&flow.ip6_n_tuple.src_addr, &ip6s,
750 : sizeof (ip6_address_and_mask_t));
751 0 : clib_memcpy (&flow.ip6_n_tuple.dst_addr, &ip6d,
752 : sizeof (ip6_address_and_mask_t));
753 :
754 0 : 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 0 : if (type == VNET_FLOW_TYPE_IP6)
761 0 : ip6_ptr->protocol.mask = protocol.mask;
762 :
763 0 : switch (protocol.prot)
764 : {
765 : /* ip6-n-tuple */
766 0 : case IP_PROTOCOL_TCP:
767 : case IP_PROTOCOL_UDP:
768 0 : flow.ip6_n_tuple.src_port = sport;
769 0 : flow.ip6_n_tuple.dst_port = dport;
770 :
771 0 : if (type == VNET_FLOW_TYPE_IP6_VXLAN)
772 0 : flow.ip6_vxlan.vni = vni;
773 0 : break;
774 0 : case IP_PROTOCOL_IP_IN_IP:
775 0 : clib_memcpy (&flow.ip6_ip4.in_src_addr, &in_ip4s,
776 : sizeof (ip4_address_and_mask_t));
777 0 : clib_memcpy (&flow.ip6_ip4.in_dst_addr, &in_ip4d,
778 : sizeof (ip4_address_and_mask_t));
779 0 : if (type == VNET_FLOW_TYPE_IP6_IP4_N_TUPLE)
780 : {
781 0 : flow.ip6_ip4.in_protocol.prot = in_proto.prot;
782 0 : flow.ip6_ip4_n_tuple.in_src_port = in_sport;
783 0 : flow.ip6_ip4_n_tuple.in_dst_port = in_dport;
784 : }
785 0 : break;
786 0 : case IP_PROTOCOL_IPV6:
787 0 : clib_memcpy (&flow.ip6_ip6.in_src_addr, &in_ip6s,
788 : sizeof (ip6_address_and_mask_t));
789 0 : clib_memcpy (&flow.ip6_ip6.in_dst_addr, &in_ip6d,
790 : sizeof (ip6_address_and_mask_t));
791 0 : if (type == VNET_FLOW_TYPE_IP6_IP6_N_TUPLE)
792 : {
793 0 : flow.ip6_ip6.in_protocol.prot = in_proto.prot;
794 0 : flow.ip6_ip6_n_tuple.in_src_port = in_sport;
795 0 : flow.ip6_ip6_n_tuple.in_dst_port = in_dport;
796 : }
797 0 : break;
798 0 : default:
799 0 : break;
800 : }
801 0 : }
802 0 : if (type == VNET_FLOW_TYPE_GENERIC)
803 : {
804 0 : clib_memcpy (flow.generic.pattern.spec, spec,
805 : sizeof (flow.generic.pattern.spec));
806 0 : clib_memcpy (flow.generic.pattern.mask, mask,
807 : sizeof (flow.generic.pattern.mask));
808 : }
809 :
810 0 : flow.type = type;
811 0 : rv = vnet_flow_add (vnm, &flow, &flow_index);
812 0 : if (!rv)
813 0 : vlib_cli_output (vm, "flow %u added", flow_index);
814 :
815 0 : break;
816 0 : case FLOW_DEL:
817 0 : rv = vnet_flow_del (vnm, flow_index);
818 0 : break;
819 0 : case FLOW_ENABLE:
820 0 : rv = vnet_flow_enable (vnm, flow_index, hw_if_index);
821 0 : break;
822 0 : case FLOW_DISABLE:
823 0 : rv = vnet_flow_disable (vnm, flow_index, hw_if_index);
824 0 : break;
825 0 : default:
826 0 : return clib_error_return (0, "please specify action (add, del, enable,"
827 : " disable)");
828 : }
829 :
830 0 : if (rv < 0)
831 0 : return clib_error_return (0, "flow error: %U", format_flow_error, rv);
832 :
833 0 : return 0;
834 : }
835 :
836 : /* *INDENT-OFF* */
837 272887 : VLIB_CLI_COMMAND (test_flow_command, static) = {
838 : .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] "
849 : "[rss function <name>] [rss types <flow type>]"
850 : "[rss queues <queue_start> to <queue_end>]",
851 : .function = test_flow,
852 : };
853 : /* *INDENT-ON* */
854 :
855 : static u8 *
856 0 : format_flow_match_element (u8 * s, va_list * args)
857 : {
858 0 : char *type = va_arg (*args, char *);
859 0 : void *ptr = va_arg (*args, void *);
860 :
861 0 : if (strncmp (type, "u8", 2) == 0)
862 0 : return format (s, "%d", *(u8 *) ptr);
863 :
864 0 : if (strncmp (type, "u16", 3) == 0)
865 0 : return format (s, "%d", *(u16 *) ptr);
866 :
867 0 : if (strncmp (type, "u32", 3) == 0)
868 0 : return format (s, "%d", *(u32 *) ptr);
869 :
870 0 : if (strncmp (type, "ethernet_header_t", 13) == 0)
871 : {
872 : ethernet_max_header_t m;
873 0 : memset (&m, 0, sizeof (m));
874 0 : m.ethernet = *(ethernet_header_t *) ptr;
875 : /* convert the ethernet type to net order */
876 0 : m.ethernet.type = clib_host_to_net_u16 (m.ethernet.type);
877 0 : return format (s, "%U", format_ethernet_header, &m);
878 : }
879 :
880 0 : if (strncmp (type, "ip4_address_t", 13) == 0)
881 0 : return format (s, "%U", format_ip4_address, ptr);
882 :
883 0 : if (strncmp (type, "ip4_address_and_mask_t", 13) == 0)
884 0 : return format (s, "%U", format_ip4_address_and_mask, ptr);
885 :
886 0 : if (strncmp (type, "ip6_address_t", 13) == 0)
887 0 : return format (s, "%U", format_ip6_address, ptr);
888 :
889 0 : if (strncmp (type, "ip6_address_and_mask_t", 13) == 0)
890 0 : return format (s, "%U", format_ip6_address_and_mask, ptr);
891 :
892 0 : if (strncmp (type, "ip_prot_and_mask_t", 13) == 0)
893 0 : return format (s, "%U", format_ip_protocol_and_mask, ptr);
894 :
895 0 : if (strncmp (type, "ip_port_and_mask_t", 18) == 0)
896 0 : return format (s, "%U", format_ip_port_and_mask, ptr);
897 :
898 0 : s = format (s, "unknown type '%s'", type);
899 0 : 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) \
905 : u8 * 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; \
909 : foreach_flow_entry_##b \
910 : s = format (s, "%v", s2);; \
911 : vec_free (s2); \
912 : return s; \
913 : }
914 0 : foreach_flow_type
915 : #undef _
916 : #undef _fe
917 : static u8 *
918 0 : format_flow_match (u8 * s, va_list * args)
919 : {
920 0 : 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 0 : foreach_flow_type;
926 : #undef _
927 :
928 0 : return s;
929 : }
930 :
931 : static u8 *
932 0 : format_flow (u8 * s, va_list * args)
933 : {
934 0 : vlib_main_t *vm = vlib_get_main ();
935 0 : vnet_flow_t *f = va_arg (*args, vnet_flow_t *);
936 0 : u32 indent = format_get_indent (s);
937 0 : u8 *t = 0;
938 :
939 0 : s = format (s, "flow-index %u type %s active %u",
940 0 : f->index, flow_type_strings[f->type],
941 0 : hash_elts (f->private_data)),
942 0 : s = format (s, "\n%Umatch: %U", format_white_space, indent + 2,
943 : format_flow_match, f);
944 0 : s = format (s, "\n%Uaction: %U", format_white_space, indent + 2,
945 : format_flow_actions, f->actions);
946 :
947 0 : if (f->actions & VNET_FLOW_ACTION_DROP)
948 0 : t = format (t, "%sdrop", t ? ", " : "");
949 :
950 0 : if (f->actions & VNET_FLOW_ACTION_MARK)
951 0 : t = format (t, "%smark %u", t ? ", " : "", f->mark_flow_id);
952 :
953 0 : if (f->actions & VNET_FLOW_ACTION_REDIRECT_TO_QUEUE)
954 0 : t =
955 0 : format (t, "%sredirect-to-queue %u", t ? ", " : "", f->redirect_queue);
956 :
957 0 : if (f->actions & VNET_FLOW_ACTION_REDIRECT_TO_NODE)
958 0 : t = format (t, "%snext-node %U", t ? ", " : "",
959 : format_vlib_node_name, vm, f->redirect_node_index);
960 :
961 0 : if (f->actions & VNET_FLOW_ACTION_BUFFER_ADVANCE)
962 0 : t = format (t, "%sbuffer-advance %d", t ? ", " : "", f->buffer_advance);
963 :
964 0 : if (f->actions & VNET_FLOW_ACTION_RSS)
965 : {
966 0 : t = format (t, "%srss function %U", t ? ", " : "",
967 0 : format_rss_function, f->rss_fun);
968 0 : t = format (t, "%srss types %U", t ? ", " : "",
969 : format_rss_types, f->rss_types);
970 : }
971 :
972 0 : if (t)
973 : {
974 0 : s = format (s, "\n%U%v", format_white_space, indent + 4, t);
975 0 : vec_free (t);
976 : }
977 :
978 0 : 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 : */
|