blob: 26b01970b6c6261f6fd61d447582b9dbe4c05222 [file] [log] [blame]
Neale Ranns17ff3c12018-07-04 10:24:24 -07001/*
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
16#include <vnet/devices/pipe/pipe.h>
17
18#include <vppinfra/sparse_vec.h>
19
20/**
21 * @file
22 * @brief Pipe Interfaces.
23 *
Paul Vinciguerra97c998c2019-10-29 16:11:09 -040024 * A pipe interface, like the UNIX pipe, is a pair of vpp interfaces
Neale Ranns17ff3c12018-07-04 10:24:24 -070025 * that are joined.
26 */
27const static pipe_t PIPE_INVALID = {
28 .sw_if_index = ~0,
29 .subint = {0},
30};
31
32/**
Paul Vinciguerra97c998c2019-10-29 16:11:09 -040033 * Various 'module' level variables
Neale Ranns17ff3c12018-07-04 10:24:24 -070034 */
35typedef struct pipe_main_t_
36{
37 /**
38 * Allocated pipe instances
39 */
40 uword *instances;
41
42 /**
Paul Vinciguerra97c998c2019-10-29 16:11:09 -040043 * the per-swif-index array of pipes. Each end of the pipe is stored against
Neale Ranns17ff3c12018-07-04 10:24:24 -070044 * its respective sw_if_index
45 */
46 pipe_t *pipes;
47} pipe_main_t;
48
49static pipe_main_t pipe_main;
50
51/*
52 * The pipe rewrite is the same size as an ethernet header (since it
53 * is an ethernet interface and the DP is optimised for writing
Paul Vinciguerra97c998c2019-10-29 16:11:09 -040054 * sizeof(ethernet_header_t) rewrites. However, there are no MAC addresses
Neale Ranns17ff3c12018-07-04 10:24:24 -070055 * since pipes don't have them.
56 */
57static u8 *
58pipe_build_rewrite (vnet_main_t * vnm,
59 u32 sw_if_index,
60 vnet_link_t link_type, const void *dst_address)
61{
62 ethernet_header_t *h;
63 ethernet_type_t type;
64 u8 *rewrite = NULL;
65
66 switch (link_type)
67 {
68#define _(a,b) case VNET_LINK_##a: type = ETHERNET_TYPE_##b; break
69 _(IP4, IP4);
70 _(IP6, IP6);
71 _(MPLS, MPLS);
72 _(ARP, ARP);
73#undef _
74 default:
75 return NULL;
76 }
77
78 vec_validate (rewrite, sizeof (ethernet_header_t));
79
80 h = (ethernet_header_t *) rewrite;
81 h->type = clib_host_to_net_u16 (type);
82
83 return (rewrite);
84}
85
86/* *INDENT-OFF* */
87VNET_HW_INTERFACE_CLASS (pipe_hw_interface_class) = {
88 .name = "Pipe",
89 .build_rewrite = pipe_build_rewrite,
90 .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
91};
92/* *INDENT-ON* */
93
94pipe_t *
95pipe_get (u32 sw_if_index)
96{
97 vec_validate_init_empty (pipe_main.pipes, sw_if_index, PIPE_INVALID);
98
99 return (&pipe_main.pipes[sw_if_index]);
100}
101
102uword
103unformat_pipe_interface (unformat_input_t * input, va_list * args)
104{
105 vnet_main_t *vnm = va_arg (*args, vnet_main_t *);
106 u32 *result = va_arg (*args, u32 *);
107 u32 hw_if_index;
108 ethernet_main_t *em = &ethernet_main;
109 ethernet_interface_t *eif;
110
111 if (!unformat_user (input, unformat_vnet_hw_interface, vnm, &hw_if_index))
112 return 0;
113
114 eif = ethernet_get_interface (em, hw_if_index);
115 if (eif)
116 {
117 *result = hw_if_index;
118 return 1;
119 }
120 return 0;
121}
122
123#define VNET_PIPE_TX_NEXT_ETHERNET_INPUT VNET_INTERFACE_TX_N_NEXT
124
125/*
126 * The TX function bounces the packets back to pipe-rx with the TX interface
127 * swapped to the RX.
128 */
129static uword
130pipe_tx (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
131{
132 u32 n_left_from, n_left_to_next, n_copy, *from, *to_next;
133 u32 next_index = VNET_PIPE_TX_NEXT_ETHERNET_INPUT;
Damjan Marionad8b37d2021-11-05 19:59:09 +0100134 u32 i, sw_if_index = 0, n_pkts = 0;
Neale Ranns17ff3c12018-07-04 10:24:24 -0700135 vlib_buffer_t *b;
136 pipe_t *pipe;
137
138 n_left_from = frame->n_vectors;
Damjan Mariona3d59862018-11-10 10:23:00 +0100139 from = vlib_frame_vector_args (frame);
Neale Ranns17ff3c12018-07-04 10:24:24 -0700140
141 while (n_left_from > 0)
142 {
143 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
144
145 n_copy = clib_min (n_left_from, n_left_to_next);
146
Dave Barach178cf492018-11-13 16:34:13 -0500147 clib_memcpy_fast (to_next, from, n_copy * sizeof (from[0]));
Neale Ranns17ff3c12018-07-04 10:24:24 -0700148 n_left_to_next -= n_copy;
149 n_left_from -= n_copy;
150 i = 0;
151 while (i < n_copy)
152 {
153 b = vlib_get_buffer (vm, from[i]);
154 sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_TX];
155
156 pipe = &pipe_main.pipes[sw_if_index];
157 // Set up RX index to be recv'd by the other end of the pipe
158 vnet_buffer (b)->sw_if_index[VLIB_RX] = pipe->sw_if_index;
159 vnet_buffer (b)->sw_if_index[VLIB_TX] = ~0;
160
161 i++;
162 n_pkts++;
Neale Ranns17ff3c12018-07-04 10:24:24 -0700163 }
164 from += n_copy;
165
166 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
Neale Ranns17ff3c12018-07-04 10:24:24 -0700167 }
168
Neale Rannsf16e9a52021-02-25 19:09:24 +0000169 return frame->n_vectors;
Neale Ranns17ff3c12018-07-04 10:24:24 -0700170}
171
172static u8 *
173format_pipe_name (u8 * s, va_list * args)
174{
175 u32 dev_instance = va_arg (*args, u32);
176 return format (s, "pipe%d", dev_instance);
177}
178
179static clib_error_t *
180pipe_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
181{
182 vnet_hw_interface_t *hi;
183 u32 id, sw_if_index;
184
185 u32 hw_flags = ((flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ?
186 VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
187 vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
188
189 /* *INDENT-OFF* */
190 hi = vnet_get_hw_interface (vnm, hw_if_index);
191 hash_foreach (id, sw_if_index, hi->sub_interface_sw_if_index_by_id,
192 ({
193 vnet_sw_interface_set_flags (vnm, sw_if_index, flags);
194 }));
195 /* *INDENT-ON* */
196
197 return (NULL);
198}
199
200/* *INDENT-OFF* */
201VNET_DEVICE_CLASS (pipe_device_class) = {
202 .name = "Pipe",
203 .format_device_name = format_pipe_name,
204 .tx_function = pipe_tx,
205 .admin_up_down_function = pipe_admin_up_down,
206};
207/* *INDENT-ON* */
208
209#define foreach_pipe_rx_next \
210 _ (DROP, "error-drop")
211
212typedef enum pipe_rx_next_t_
213{
214#define _(s,n) PIPE_RX_NEXT_##s,
215 foreach_pipe_rx_next
216#undef _
217 PIPE_RX_N_NEXT,
218} pipe_rx_next_t;
219
220typedef struct pipe_rx_trace_t_
221{
222 u8 packet_data[32];
223} pipe_rx_trace_t;
224
225static u8 *
226format_pipe_rx_trace (u8 * s, va_list * va)
227{
228 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
229 CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
230 pipe_rx_trace_t *t = va_arg (*va, pipe_rx_trace_t *);
231
232 s = format (s, "%U", format_ethernet_header, t->packet_data);
233
234 return s;
235}
236
237/*
238 * The pipe-rx node is a sibling of ethernet-input so steal it's
239 * next node mechanism
240 */
241static_always_inline void
242pipe_determine_next_node (ethernet_main_t * em,
243 u32 is_l20,
244 u32 type0,
245 vlib_buffer_t * b0, pipe_rx_next_t * next0)
246{
247 if (is_l20)
248 {
249 *next0 = em->l2_next;
250 }
251 else if (type0 == ETHERNET_TYPE_IP4)
252 {
253 *next0 = em->l3_next.input_next_ip4;
254 }
255 else if (type0 == ETHERNET_TYPE_IP6)
256 {
257 *next0 = em->l3_next.input_next_ip6;
258 }
259 else if (type0 == ETHERNET_TYPE_MPLS)
260 {
261 *next0 = em->l3_next.input_next_mpls;
262
263 }
264 else if (em->redirect_l3)
265 {
266 // L3 Redirect is on, the cached common next nodes will be
267 // pointing to the redirect node, catch the uncommon types here
268 *next0 = em->redirect_l3_next;
269 }
270 else
271 {
272 // uncommon ethertype, check table
273 u32 i0;
274 i0 = sparse_vec_index (em->l3_next.input_next_by_type, type0);
275 *next0 = vec_elt (em->l3_next.input_next_by_type, i0);
276
277 // The table is not populated with LLC values, so check that now.
278 if (type0 < 0x600)
279 {
280 *next0 = PIPE_RX_NEXT_DROP;
281 }
282 }
283}
284
285static_always_inline uword
286pipe_rx (vlib_main_t * vm,
287 vlib_node_runtime_t * node, vlib_frame_t * from_frame)
288{
289 u32 n_left_from, next_index, *from, *to_next;
290 u32 n_left_to_next;
291
292 from = vlib_frame_vector_args (from_frame);
293 n_left_from = from_frame->n_vectors;
294
295 if (node->flags & VLIB_NODE_FLAG_TRACE)
296 vlib_trace_frame_buffers_only (vm, node,
297 from,
298 n_left_from,
299 sizeof (from[0]),
300 sizeof (pipe_rx_trace_t));
301
302 next_index = node->cached_next_index;
303
304 while (n_left_from > 0)
305 {
306 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
307
308 while (n_left_from >= 4 && n_left_to_next >= 2)
309 {
310 u32 bi0, sw_if_index0, bi1, sw_if_index1;
311 pipe_rx_next_t next0, next1;
312 ethernet_header_t *e0, *e1;
313 vlib_buffer_t *b0, *b1;
314 pipe_t *pipe0, *pipe1;
315 u8 is_l20, is_l21;
316 u16 type0, type1;
317
318 // Prefetch next iteration
319 {
320 vlib_buffer_t *p2, *p3;
321
322 p2 = vlib_get_buffer (vm, from[2]);
323 p3 = vlib_get_buffer (vm, from[3]);
324 vlib_prefetch_buffer_header (p2, STORE);
325 vlib_prefetch_buffer_header (p3, STORE);
Damjan Marionaf7fb042021-07-15 11:54:41 +0200326 clib_prefetch_load (p2->data);
327 clib_prefetch_load (p3->data);
Neale Ranns17ff3c12018-07-04 10:24:24 -0700328 }
329
330 bi0 = from[0];
331 to_next[0] = bi0;
332 bi1 = from[1];
333 to_next[1] = bi1;
334 from += 2;
335 to_next += 2;
336 n_left_from -= 2;
337 n_left_to_next -= 2;
338
339 b0 = vlib_get_buffer (vm, bi0);
340 b1 = vlib_get_buffer (vm, bi1);
341
342 e0 = vlib_buffer_get_current (b0);
343 e1 = vlib_buffer_get_current (b1);
344 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
345 sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
346 type0 = clib_net_to_host_u16 (e0->type);
347 type1 = clib_net_to_host_u16 (e1->type);
348 pipe0 = &pipe_main.pipes[sw_if_index0];
349 pipe1 = &pipe_main.pipes[sw_if_index1];
350
Andrew Yourtchenko270055a2018-10-16 12:04:51 +0200351 vnet_buffer (b0)->l2_hdr_offset = b0->current_data;
352 vnet_buffer (b1)->l2_hdr_offset = b1->current_data;
353
Neale Ranns17ff3c12018-07-04 10:24:24 -0700354 vnet_buffer (b0)->l3_hdr_offset =
Andrew Yourtchenko270055a2018-10-16 12:04:51 +0200355 vnet_buffer (b0)->l2_hdr_offset + sizeof (ethernet_header_t);
Neale Ranns17ff3c12018-07-04 10:24:24 -0700356 vnet_buffer (b1)->l3_hdr_offset =
Andrew Yourtchenko270055a2018-10-16 12:04:51 +0200357 vnet_buffer (b1)->l2_hdr_offset + sizeof (ethernet_header_t);
358 b0->flags |=
359 VNET_BUFFER_F_L2_HDR_OFFSET_VALID |
360 VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
361 b1->flags |=
362 VNET_BUFFER_F_L2_HDR_OFFSET_VALID |
363 VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
Neale Ranns17ff3c12018-07-04 10:24:24 -0700364
365 is_l20 = pipe0->subint.flags & SUBINT_CONFIG_L2;
366 is_l21 = pipe1->subint.flags & SUBINT_CONFIG_L2;
Andrew Yourtchenko270055a2018-10-16 12:04:51 +0200367
368 /*
369 * from discussion with Neale - we do not support the tagged traffic.
370 * So assume a simple ethernet header
371 */
372 vnet_buffer (b0)->l2.l2_len = sizeof (ethernet_header_t);
373 vnet_buffer (b1)->l2.l2_len = sizeof (ethernet_header_t);
374 vlib_buffer_advance (b0, is_l20 ? 0 : sizeof (ethernet_header_t));
375 vlib_buffer_advance (b1, is_l21 ? 0 : sizeof (ethernet_header_t));
376
Neale Ranns17ff3c12018-07-04 10:24:24 -0700377 pipe_determine_next_node (&ethernet_main, is_l20, type0, b0,
378 &next0);
379 pipe_determine_next_node (&ethernet_main, is_l21, type1, b1,
380 &next1);
381
Neale Ranns17ff3c12018-07-04 10:24:24 -0700382 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
383 to_next, n_left_to_next,
Lijian Zhangee4efa72018-08-13 16:34:21 +0800384 bi0, bi1, next0, next1);
Neale Ranns17ff3c12018-07-04 10:24:24 -0700385 }
386 while (n_left_from > 0 && n_left_to_next > 0)
387 {
388 u32 bi0, sw_if_index0;
389 vlib_buffer_t *b0;
390 pipe_rx_next_t next0;
391 ethernet_header_t *e0;
392 pipe_t *pipe0;
393 u16 type0;
394 u8 is_l20;
395
396 bi0 = from[0];
397 to_next[0] = bi0;
398 from += 1;
399 to_next += 1;
400 n_left_from -= 1;
401 n_left_to_next -= 1;
402
403 b0 = vlib_get_buffer (vm, bi0);
404
405 e0 = vlib_buffer_get_current (b0);
406 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
407 type0 = clib_net_to_host_u16 (e0->type);
408 pipe0 = &pipe_main.pipes[sw_if_index0];
409
Andrew Yourtchenko270055a2018-10-16 12:04:51 +0200410 vnet_buffer (b0)->l2_hdr_offset = b0->current_data;
Neale Ranns17ff3c12018-07-04 10:24:24 -0700411 vnet_buffer (b0)->l3_hdr_offset =
Andrew Yourtchenko270055a2018-10-16 12:04:51 +0200412 vnet_buffer (b0)->l2_hdr_offset + sizeof (ethernet_header_t);
413 b0->flags |=
414 VNET_BUFFER_F_L2_HDR_OFFSET_VALID |
415 VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
Neale Ranns17ff3c12018-07-04 10:24:24 -0700416
417 is_l20 = pipe0->subint.flags & SUBINT_CONFIG_L2;
Andrew Yourtchenko270055a2018-10-16 12:04:51 +0200418
419 vnet_buffer (b0)->l2.l2_len = sizeof (ethernet_header_t);
420 vlib_buffer_advance (b0, is_l20 ? 0 : sizeof (ethernet_header_t));
421
Neale Ranns17ff3c12018-07-04 10:24:24 -0700422 pipe_determine_next_node (&ethernet_main, is_l20, type0, b0,
423 &next0);
424
Neale Ranns17ff3c12018-07-04 10:24:24 -0700425 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
426 to_next, n_left_to_next,
427 bi0, next0);
428 }
429
430 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
431 }
432
433 return from_frame->n_vectors;
434}
435
436/* *INDENT-OFF* */
437VLIB_REGISTER_NODE (pipe_rx_node) = {
438 .function = pipe_rx,
439 .name = "pipe-rx",
440 /* Takes a vector of packets. */
441 .vector_size = sizeof (u32),
442 .format_trace = format_pipe_rx_trace,
443
444 .sibling_of = "ethernet-input",
445};
446/* *INDENT-ON* */
447
448/*
449 * Maintain a bitmap of allocated pipe instance numbers.
450 */
451#define PIPE_MAX_INSTANCE (16 * 1024)
452
453static u32
454pipe_instance_alloc (u8 is_specified, u32 want)
455{
456 /*
457 * Check for dynamically allocaetd instance number.
458 */
459 if (!is_specified)
460 {
461 u32 bit;
462
463 bit = clib_bitmap_first_clear (pipe_main.instances);
464 if (bit >= PIPE_MAX_INSTANCE)
465 {
466 return ~0;
467 }
468 pipe_main.instances = clib_bitmap_set (pipe_main.instances, bit, 1);
469 return bit;
470 }
471
472 /*
473 * In range?
474 */
475 if (want >= PIPE_MAX_INSTANCE)
476 {
477 return ~0;
478 }
479
480 /*
481 * Already in use?
482 */
483 if (clib_bitmap_get (pipe_main.instances, want))
484 {
485 return ~0;
486 }
487
488 /*
489 * Grant allocation request.
490 */
491 pipe_main.instances = clib_bitmap_set (pipe_main.instances, want, 1);
492
493 return want;
494}
495
496static int
497pipe_instance_free (u32 instance)
498{
499 if (instance >= PIPE_MAX_INSTANCE)
500 {
501 return -1;
502 }
503
504 if (clib_bitmap_get (pipe_main.instances, instance) == 0)
505 {
506 return -1;
507 }
508
509 pipe_main.instances = clib_bitmap_set (pipe_main.instances, instance, 0);
510 return 0;
511}
512
513static clib_error_t *
514pipe_create_sub_interface (vnet_hw_interface_t * hi,
Neale Rannsa1ff01e2018-07-09 09:56:32 -0700515 u32 sub_id, u32 * sw_if_index)
Neale Ranns17ff3c12018-07-04 10:24:24 -0700516{
517 vnet_sw_interface_t template;
518
Dave Barachb7b92992018-10-17 10:38:51 -0400519 clib_memset (&template, 0, sizeof (template));
Neale Ranns17ff3c12018-07-04 10:24:24 -0700520 template.type = VNET_SW_INTERFACE_TYPE_PIPE;
521 template.flood_class = VNET_FLOOD_CLASS_NORMAL;
522 template.sup_sw_if_index = hi->sw_if_index;
523 template.sub.id = sub_id;
524
525 return (vnet_create_sw_interface (vnet_get_main (),
526 &template, sw_if_index));
527}
528
529int
530vnet_create_pipe_interface (u8 is_specified,
531 u32 user_instance,
532 u32 * parent_sw_if_index, u32 pipe_sw_if_index[2])
533{
534 vnet_main_t *vnm = vnet_get_main ();
535 vlib_main_t *vm = vlib_get_main ();
Damjan Marion5c954c42022-01-06 20:36:14 +0100536 vnet_eth_interface_registration_t eir = {};
Neale Rannsa1ff01e2018-07-09 09:56:32 -0700537 u8 address[6] = {
Neale Ranns17ff3c12018-07-04 10:24:24 -0700538 [0] = 0x22,
539 [1] = 0x22,
540 };
541 vnet_hw_interface_t *hi;
542 clib_error_t *error;
543 u32 hw_if_index;
544 u32 instance;
545 u32 slot;
546 int rv = 0;
547
548 ASSERT (parent_sw_if_index);
549
Dave Barachb7b92992018-10-17 10:38:51 -0400550 clib_memset (address, 0, sizeof (address));
Neale Ranns17ff3c12018-07-04 10:24:24 -0700551
552 /*
553 * Allocate a pipe instance. Either select one dynamically
554 * or try to use the desired user_instance number.
555 */
556 instance = pipe_instance_alloc (is_specified, user_instance);
557 if (instance == ~0)
558 {
559 return VNET_API_ERROR_INVALID_REGISTRATION;
560 }
561
562 /*
563 * Default MAC address (0000:0000:0000 + instance) is allocated
564 */
565 address[5] = instance;
566
Damjan Marion5c954c42022-01-06 20:36:14 +0100567 eir.dev_class_index = pipe_device_class.index;
568 eir.dev_instance = instance;
569 eir.address = address;
570 hw_if_index = vnet_eth_register_interface (vnm, &eir);
Neale Ranns17ff3c12018-07-04 10:24:24 -0700571
572 hi = vnet_get_hw_interface (vnm, hw_if_index);
573 *parent_sw_if_index = hi->sw_if_index;
574 slot = vlib_node_add_named_next_with_slot (vm, hi->tx_node_index,
575 "pipe-rx",
576 VNET_PIPE_TX_NEXT_ETHERNET_INPUT);
577 ASSERT (slot == VNET_PIPE_TX_NEXT_ETHERNET_INPUT);
578
579 /*
580 * create two sub-interfaces, one for each end of the pipe.
581 */
582 error = pipe_create_sub_interface (hi, 0, &pipe_sw_if_index[0]);
583
584 if (error)
585 goto oops;
586
587 error = pipe_create_sub_interface (hi, 1, &pipe_sw_if_index[1]);
588
589 if (error)
590 goto oops;
591
592 hash_set (hi->sub_interface_sw_if_index_by_id, 0, pipe_sw_if_index[0]);
593 hash_set (hi->sub_interface_sw_if_index_by_id, 1, pipe_sw_if_index[1]);
594
595 vec_validate_init_empty (pipe_main.pipes, pipe_sw_if_index[0],
596 PIPE_INVALID);
597 vec_validate_init_empty (pipe_main.pipes, pipe_sw_if_index[1],
598 PIPE_INVALID);
599
600 pipe_main.pipes[pipe_sw_if_index[0]].sw_if_index = pipe_sw_if_index[1];
601 pipe_main.pipes[pipe_sw_if_index[1]].sw_if_index = pipe_sw_if_index[0];
602
603 return 0;
604
605oops:
606 clib_error_report (error);
607 return rv;
608}
609
610typedef struct pipe_hw_walk_ctx_t_
611{
612 pipe_cb_fn_t cb;
613 void *ctx;
614} pipe_hw_walk_ctx_t;
615
616static walk_rc_t
617pipe_hw_walk (vnet_main_t * vnm, u32 hw_if_index, void *args)
618{
619 vnet_hw_interface_t *hi;
620 pipe_hw_walk_ctx_t *ctx;
621
622 ctx = args;
623 hi = vnet_get_hw_interface (vnm, hw_if_index);
624
625 if (hi->dev_class_index == pipe_device_class.index)
626 {
627 u32 pipe_sw_if_index[2], id, sw_if_index;
628
629 /* *INDENT-OFF* */
630 hash_foreach (id, sw_if_index, hi->sub_interface_sw_if_index_by_id,
631 ({
632 ASSERT(id < 2);
633 pipe_sw_if_index[id] = sw_if_index;
634 }));
635 /* *INDENT-ON* */
636
637 ctx->cb (hi->sw_if_index, pipe_sw_if_index, hi->dev_instance, ctx->ctx);
638 }
639
640 return (WALK_CONTINUE);
641}
642
643void
644pipe_walk (pipe_cb_fn_t fn, void *ctx)
645{
646 pipe_hw_walk_ctx_t wctx = {
647 .cb = fn,
648 .ctx = ctx,
649 };
650
651 ASSERT (fn);
652
653 vnet_hw_interface_walk (vnet_get_main (), pipe_hw_walk, &wctx);
654}
655
656static clib_error_t *
657create_pipe_interfaces (vlib_main_t * vm,
658 unformat_input_t * input, vlib_cli_command_t * cmd)
659{
660 int rv;
661 u32 sw_if_index;
662 u32 pipe_sw_if_index[2];
663 u8 is_specified = 0;
664 u32 user_instance = 0;
665
666 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
667 {
668 if (unformat (input, "instance %d", &user_instance))
669 is_specified = 1;
670 else
671 break;
672 }
673
674 rv = vnet_create_pipe_interface (is_specified, user_instance,
675 &sw_if_index, pipe_sw_if_index);
676
677 if (rv)
678 return clib_error_return (0, "vnet_create_pipe_interface failed");
679
680 vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name,
681 vnet_get_main (), sw_if_index);
682 return 0;
683}
684
685/*?
686 * Create a pipe interface.
687 *
688 * @cliexpar
689 * The following two command syntaxes are equivalent:
690 * @cliexcmd{pipe create-interface [mac <mac-addr>] [instance <instance>]}
691 * Example of how to create a pipe interface:
692 * @cliexcmd{pipe create}
693 ?*/
694/* *INDENT-OFF* */
695VLIB_CLI_COMMAND (pipe_create_interface_command, static) = {
696 .path = "pipe create",
697 .short_help = "pipe create [instance <instance>]",
698 .function = create_pipe_interfaces,
699};
700/* *INDENT-ON* */
701
702int
703vnet_delete_pipe_interface (u32 sw_if_index)
704{
705 vnet_main_t *vnm = vnet_get_main ();
706 vnet_sw_interface_t *si;
707 vnet_hw_interface_t *hi;
708 u32 instance, id;
709 u32 hw_if_index;
710
711 if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
712 return VNET_API_ERROR_INVALID_SW_IF_INDEX;
713
714 si = vnet_get_sw_interface (vnm, sw_if_index);
715 hw_if_index = si->hw_if_index;
716 hi = vnet_get_hw_interface (vnm, hw_if_index);
717 instance = hi->dev_instance;
718
719 if (pipe_instance_free (instance) < 0)
720 {
721 return VNET_API_ERROR_INVALID_SW_IF_INDEX;
722 }
723
724 /* *INDENT-OFF* */
725 hash_foreach (id, sw_if_index, hi->sub_interface_sw_if_index_by_id,
726 ({
727 vnet_delete_sub_interface(sw_if_index);
728 pipe_main.pipes[sw_if_index] = PIPE_INVALID;
729 }));
730 /* *INDENT-ON* */
731
732 ethernet_delete_interface (vnm, hw_if_index);
733
734 return 0;
735}
736
737static clib_error_t *
738delete_pipe_interfaces (vlib_main_t * vm,
739 unformat_input_t * input, vlib_cli_command_t * cmd)
740{
741 vnet_main_t *vnm = vnet_get_main ();
742 u32 sw_if_index = ~0;
743 int rv;
744
745 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
746 {
747 if (unformat (input, "%U",
748 unformat_vnet_sw_interface, vnm, &sw_if_index))
749 ;
750 else
751 break;
752 }
753
754 if (sw_if_index == ~0)
755 return clib_error_return (0, "interface not specified");
756
757 rv = vnet_delete_pipe_interface (sw_if_index);
758
759 if (rv)
760 return clib_error_return (0, "vnet_delete_pipe_interface failed");
761
762 return 0;
763}
764
765/*?
766 * Delete a pipe interface.
767 *
768 * @cliexpar
769 * The following two command syntaxes are equivalent:
770 * @cliexcmd{pipe delete intfc <interface>}
771 * Example of how to delete a pipe interface:
772 * @cliexcmd{pipe delete-interface intfc loop0}
773 ?*/
774/* *INDENT-OFF* */
775VLIB_CLI_COMMAND (pipe_delete_interface_command, static) = {
776 .path = "pipe delete",
777 .short_help = "pipe delete <interface>",
778 .function = delete_pipe_interfaces,
779};
780/* *INDENT-ON* */
781
782/*
783 * fd.io coding-style-patch-verification: ON
784 *
785 * Local Variables:
786 * eval: (c-set-style "gnu")
787 * End:
788 */