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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include <vnet/devices/pipe/pipe.h>
18 #include <vppinfra/sparse_vec.h>
22 * @brief Pipe Interfaces.
24 * A pipe interface, like the UNIX pipe, is a pair of vpp interfaces
27 const static pipe_t PIPE_INVALID = {
33 * Various 'module' level variables
35 typedef struct pipe_main_t_
38 * Allocated pipe instances
43 * the per-swif-index array of pipes. Each end of the pipe is stored against
44 * its respective sw_if_index
49 static pipe_main_t pipe_main;
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
54 * sizeof(ethernet_header_t) rewrites. However, there are no MAC addresses
55 * since pipes don't have them.
58 pipe_build_rewrite (vnet_main_t * vnm,
60 vnet_link_t link_type, const void *dst_address)
68 #define _(a,b) case VNET_LINK_##a: type = ETHERNET_TYPE_##b; break
78 vec_validate (rewrite, sizeof (ethernet_header_t));
80 h = (ethernet_header_t *) rewrite;
81 h->type = clib_host_to_net_u16 (type);
87 VNET_HW_INTERFACE_CLASS (pipe_hw_interface_class) = {
89 .build_rewrite = pipe_build_rewrite,
90 .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
95 pipe_get (u32 sw_if_index)
97 vec_validate_init_empty (pipe_main.pipes, sw_if_index, PIPE_INVALID);
99 return (&pipe_main.pipes[sw_if_index]);
103 unformat_pipe_interface (unformat_input_t * input, va_list * args)
105 vnet_main_t *vnm = va_arg (*args, vnet_main_t *);
106 u32 *result = va_arg (*args, u32 *);
108 ethernet_main_t *em = ðernet_main;
109 ethernet_interface_t *eif;
111 if (!unformat_user (input, unformat_vnet_hw_interface, vnm, &hw_if_index))
114 eif = ethernet_get_interface (em, hw_if_index);
117 *result = hw_if_index;
123 #define VNET_PIPE_TX_NEXT_ETHERNET_INPUT VNET_INTERFACE_TX_N_NEXT
126 * The TX function bounces the packets back to pipe-rx with the TX interface
130 pipe_tx (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
132 u32 n_left_from, n_left_to_next, n_copy, *from, *to_next;
133 u32 next_index = VNET_PIPE_TX_NEXT_ETHERNET_INPUT;
134 u32 i, sw_if_index = 0, n_pkts = 0, n_bytes = 0;
138 n_left_from = frame->n_vectors;
139 from = vlib_frame_vector_args (frame);
141 while (n_left_from > 0)
143 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
145 n_copy = clib_min (n_left_from, n_left_to_next);
147 clib_memcpy_fast (to_next, from, n_copy * sizeof (from[0]));
148 n_left_to_next -= n_copy;
149 n_left_from -= n_copy;
153 b = vlib_get_buffer (vm, from[i]);
154 sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_TX];
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;
163 n_bytes += vlib_buffer_length_in_chain (vm, b);
167 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
174 format_pipe_name (u8 * s, va_list * args)
176 u32 dev_instance = va_arg (*args, u32);
177 return format (s, "pipe%d", dev_instance);
180 static clib_error_t *
181 pipe_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
183 vnet_hw_interface_t *hi;
186 u32 hw_flags = ((flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ?
187 VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
188 vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
191 hi = vnet_get_hw_interface (vnm, hw_if_index);
192 hash_foreach (id, sw_if_index, hi->sub_interface_sw_if_index_by_id,
194 vnet_sw_interface_set_flags (vnm, sw_if_index, flags);
202 VNET_DEVICE_CLASS (pipe_device_class) = {
204 .format_device_name = format_pipe_name,
205 .tx_function = pipe_tx,
206 .admin_up_down_function = pipe_admin_up_down,
210 #define foreach_pipe_rx_next \
211 _ (DROP, "error-drop")
213 typedef enum pipe_rx_next_t_
215 #define _(s,n) PIPE_RX_NEXT_##s,
221 typedef struct pipe_rx_trace_t_
227 format_pipe_rx_trace (u8 * s, va_list * va)
229 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
230 CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
231 pipe_rx_trace_t *t = va_arg (*va, pipe_rx_trace_t *);
233 s = format (s, "%U", format_ethernet_header, t->packet_data);
239 * The pipe-rx node is a sibling of ethernet-input so steal it's
240 * next node mechanism
242 static_always_inline void
243 pipe_determine_next_node (ethernet_main_t * em,
246 vlib_buffer_t * b0, pipe_rx_next_t * next0)
250 *next0 = em->l2_next;
252 else if (type0 == ETHERNET_TYPE_IP4)
254 *next0 = em->l3_next.input_next_ip4;
256 else if (type0 == ETHERNET_TYPE_IP6)
258 *next0 = em->l3_next.input_next_ip6;
260 else if (type0 == ETHERNET_TYPE_MPLS)
262 *next0 = em->l3_next.input_next_mpls;
265 else if (em->redirect_l3)
267 // L3 Redirect is on, the cached common next nodes will be
268 // pointing to the redirect node, catch the uncommon types here
269 *next0 = em->redirect_l3_next;
273 // uncommon ethertype, check table
275 i0 = sparse_vec_index (em->l3_next.input_next_by_type, type0);
276 *next0 = vec_elt (em->l3_next.input_next_by_type, i0);
278 // The table is not populated with LLC values, so check that now.
281 *next0 = PIPE_RX_NEXT_DROP;
286 static_always_inline uword
287 pipe_rx (vlib_main_t * vm,
288 vlib_node_runtime_t * node, vlib_frame_t * from_frame)
290 u32 n_left_from, next_index, *from, *to_next;
293 from = vlib_frame_vector_args (from_frame);
294 n_left_from = from_frame->n_vectors;
296 if (node->flags & VLIB_NODE_FLAG_TRACE)
297 vlib_trace_frame_buffers_only (vm, node,
301 sizeof (pipe_rx_trace_t));
303 next_index = node->cached_next_index;
305 while (n_left_from > 0)
307 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
309 while (n_left_from >= 4 && n_left_to_next >= 2)
311 u32 bi0, sw_if_index0, bi1, sw_if_index1;
312 pipe_rx_next_t next0, next1;
313 ethernet_header_t *e0, *e1;
314 vlib_buffer_t *b0, *b1;
315 pipe_t *pipe0, *pipe1;
319 // Prefetch next iteration
321 vlib_buffer_t *p2, *p3;
323 p2 = vlib_get_buffer (vm, from[2]);
324 p3 = vlib_get_buffer (vm, from[3]);
325 vlib_prefetch_buffer_header (p2, STORE);
326 vlib_prefetch_buffer_header (p3, STORE);
327 CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, LOAD);
328 CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, LOAD);
340 b0 = vlib_get_buffer (vm, bi0);
341 b1 = vlib_get_buffer (vm, bi1);
343 e0 = vlib_buffer_get_current (b0);
344 e1 = vlib_buffer_get_current (b1);
345 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
346 sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
347 type0 = clib_net_to_host_u16 (e0->type);
348 type1 = clib_net_to_host_u16 (e1->type);
349 pipe0 = &pipe_main.pipes[sw_if_index0];
350 pipe1 = &pipe_main.pipes[sw_if_index1];
352 vnet_buffer (b0)->l2_hdr_offset = b0->current_data;
353 vnet_buffer (b1)->l2_hdr_offset = b1->current_data;
355 vnet_buffer (b0)->l3_hdr_offset =
356 vnet_buffer (b0)->l2_hdr_offset + sizeof (ethernet_header_t);
357 vnet_buffer (b1)->l3_hdr_offset =
358 vnet_buffer (b1)->l2_hdr_offset + sizeof (ethernet_header_t);
360 VNET_BUFFER_F_L2_HDR_OFFSET_VALID |
361 VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
363 VNET_BUFFER_F_L2_HDR_OFFSET_VALID |
364 VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
366 is_l20 = pipe0->subint.flags & SUBINT_CONFIG_L2;
367 is_l21 = pipe1->subint.flags & SUBINT_CONFIG_L2;
370 * from discussion with Neale - we do not support the tagged traffic.
371 * So assume a simple ethernet header
373 vnet_buffer (b0)->l2.l2_len = sizeof (ethernet_header_t);
374 vnet_buffer (b1)->l2.l2_len = sizeof (ethernet_header_t);
375 vlib_buffer_advance (b0, is_l20 ? 0 : sizeof (ethernet_header_t));
376 vlib_buffer_advance (b1, is_l21 ? 0 : sizeof (ethernet_header_t));
378 pipe_determine_next_node (ðernet_main, is_l20, type0, b0,
380 pipe_determine_next_node (ðernet_main, is_l21, type1, b1,
383 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
384 to_next, n_left_to_next,
385 bi0, bi1, next0, next1);
387 while (n_left_from > 0 && n_left_to_next > 0)
389 u32 bi0, sw_if_index0;
391 pipe_rx_next_t next0;
392 ethernet_header_t *e0;
404 b0 = vlib_get_buffer (vm, bi0);
406 e0 = vlib_buffer_get_current (b0);
407 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
408 type0 = clib_net_to_host_u16 (e0->type);
409 pipe0 = &pipe_main.pipes[sw_if_index0];
411 vnet_buffer (b0)->l2_hdr_offset = b0->current_data;
412 vnet_buffer (b0)->l3_hdr_offset =
413 vnet_buffer (b0)->l2_hdr_offset + sizeof (ethernet_header_t);
415 VNET_BUFFER_F_L2_HDR_OFFSET_VALID |
416 VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
418 is_l20 = pipe0->subint.flags & SUBINT_CONFIG_L2;
420 vnet_buffer (b0)->l2.l2_len = sizeof (ethernet_header_t);
421 vlib_buffer_advance (b0, is_l20 ? 0 : sizeof (ethernet_header_t));
423 pipe_determine_next_node (ðernet_main, is_l20, type0, b0,
426 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
427 to_next, n_left_to_next,
431 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
434 return from_frame->n_vectors;
438 VLIB_REGISTER_NODE (pipe_rx_node) = {
441 /* Takes a vector of packets. */
442 .vector_size = sizeof (u32),
443 .format_trace = format_pipe_rx_trace,
445 .sibling_of = "ethernet-input",
450 * Maintain a bitmap of allocated pipe instance numbers.
452 #define PIPE_MAX_INSTANCE (16 * 1024)
455 pipe_instance_alloc (u8 is_specified, u32 want)
458 * Check for dynamically allocaetd instance number.
464 bit = clib_bitmap_first_clear (pipe_main.instances);
465 if (bit >= PIPE_MAX_INSTANCE)
469 pipe_main.instances = clib_bitmap_set (pipe_main.instances, bit, 1);
476 if (want >= PIPE_MAX_INSTANCE)
484 if (clib_bitmap_get (pipe_main.instances, want))
490 * Grant allocation request.
492 pipe_main.instances = clib_bitmap_set (pipe_main.instances, want, 1);
498 pipe_instance_free (u32 instance)
500 if (instance >= PIPE_MAX_INSTANCE)
505 if (clib_bitmap_get (pipe_main.instances, instance) == 0)
510 pipe_main.instances = clib_bitmap_set (pipe_main.instances, instance, 0);
514 static clib_error_t *
515 pipe_create_sub_interface (vnet_hw_interface_t * hi,
516 u32 sub_id, u32 * sw_if_index)
518 vnet_sw_interface_t template;
520 clib_memset (&template, 0, sizeof (template));
521 template.type = VNET_SW_INTERFACE_TYPE_PIPE;
522 template.flood_class = VNET_FLOOD_CLASS_NORMAL;
523 template.sup_sw_if_index = hi->sw_if_index;
524 template.sub.id = sub_id;
526 return (vnet_create_sw_interface (vnet_get_main (),
527 &template, sw_if_index));
531 vnet_create_pipe_interface (u8 is_specified,
533 u32 * parent_sw_if_index, u32 pipe_sw_if_index[2])
535 vnet_main_t *vnm = vnet_get_main ();
536 vlib_main_t *vm = vlib_get_main ();
541 vnet_hw_interface_t *hi;
548 ASSERT (parent_sw_if_index);
550 clib_memset (address, 0, sizeof (address));
553 * Allocate a pipe instance. Either select one dynamically
554 * or try to use the desired user_instance number.
556 instance = pipe_instance_alloc (is_specified, user_instance);
559 return VNET_API_ERROR_INVALID_REGISTRATION;
563 * Default MAC address (0000:0000:0000 + instance) is allocated
565 address[5] = instance;
567 error = ethernet_register_interface (vnm, pipe_device_class.index,
568 instance, address, &hw_if_index,
569 /* flag change */ 0);
573 rv = VNET_API_ERROR_INVALID_REGISTRATION;
577 hi = vnet_get_hw_interface (vnm, hw_if_index);
578 *parent_sw_if_index = hi->sw_if_index;
579 slot = vlib_node_add_named_next_with_slot (vm, hi->tx_node_index,
581 VNET_PIPE_TX_NEXT_ETHERNET_INPUT);
582 ASSERT (slot == VNET_PIPE_TX_NEXT_ETHERNET_INPUT);
585 * create two sub-interfaces, one for each end of the pipe.
587 error = pipe_create_sub_interface (hi, 0, &pipe_sw_if_index[0]);
592 error = pipe_create_sub_interface (hi, 1, &pipe_sw_if_index[1]);
597 hash_set (hi->sub_interface_sw_if_index_by_id, 0, pipe_sw_if_index[0]);
598 hash_set (hi->sub_interface_sw_if_index_by_id, 1, pipe_sw_if_index[1]);
600 vec_validate_init_empty (pipe_main.pipes, pipe_sw_if_index[0],
602 vec_validate_init_empty (pipe_main.pipes, pipe_sw_if_index[1],
605 pipe_main.pipes[pipe_sw_if_index[0]].sw_if_index = pipe_sw_if_index[1];
606 pipe_main.pipes[pipe_sw_if_index[1]].sw_if_index = pipe_sw_if_index[0];
611 clib_error_report (error);
615 typedef struct pipe_hw_walk_ctx_t_
619 } pipe_hw_walk_ctx_t;
622 pipe_hw_walk (vnet_main_t * vnm, u32 hw_if_index, void *args)
624 vnet_hw_interface_t *hi;
625 pipe_hw_walk_ctx_t *ctx;
628 hi = vnet_get_hw_interface (vnm, hw_if_index);
630 if (hi->dev_class_index == pipe_device_class.index)
632 u32 pipe_sw_if_index[2], id, sw_if_index;
635 hash_foreach (id, sw_if_index, hi->sub_interface_sw_if_index_by_id,
638 pipe_sw_if_index[id] = sw_if_index;
642 ctx->cb (hi->sw_if_index, pipe_sw_if_index, hi->dev_instance, ctx->ctx);
645 return (WALK_CONTINUE);
649 pipe_walk (pipe_cb_fn_t fn, void *ctx)
651 pipe_hw_walk_ctx_t wctx = {
658 vnet_hw_interface_walk (vnet_get_main (), pipe_hw_walk, &wctx);
661 static clib_error_t *
662 create_pipe_interfaces (vlib_main_t * vm,
663 unformat_input_t * input, vlib_cli_command_t * cmd)
667 u32 pipe_sw_if_index[2];
669 u32 user_instance = 0;
671 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
673 if (unformat (input, "instance %d", &user_instance))
679 rv = vnet_create_pipe_interface (is_specified, user_instance,
680 &sw_if_index, pipe_sw_if_index);
683 return clib_error_return (0, "vnet_create_pipe_interface failed");
685 vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name,
686 vnet_get_main (), sw_if_index);
691 * Create a pipe interface.
694 * The following two command syntaxes are equivalent:
695 * @cliexcmd{pipe create-interface [mac <mac-addr>] [instance <instance>]}
696 * Example of how to create a pipe interface:
697 * @cliexcmd{pipe create}
700 VLIB_CLI_COMMAND (pipe_create_interface_command, static) = {
701 .path = "pipe create",
702 .short_help = "pipe create [instance <instance>]",
703 .function = create_pipe_interfaces,
708 vnet_delete_pipe_interface (u32 sw_if_index)
710 vnet_main_t *vnm = vnet_get_main ();
711 vnet_sw_interface_t *si;
712 vnet_hw_interface_t *hi;
716 if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
717 return VNET_API_ERROR_INVALID_SW_IF_INDEX;
719 si = vnet_get_sw_interface (vnm, sw_if_index);
720 hw_if_index = si->hw_if_index;
721 hi = vnet_get_hw_interface (vnm, hw_if_index);
722 instance = hi->dev_instance;
724 if (pipe_instance_free (instance) < 0)
726 return VNET_API_ERROR_INVALID_SW_IF_INDEX;
730 hash_foreach (id, sw_if_index, hi->sub_interface_sw_if_index_by_id,
732 vnet_delete_sub_interface(sw_if_index);
733 pipe_main.pipes[sw_if_index] = PIPE_INVALID;
737 ethernet_delete_interface (vnm, hw_if_index);
742 static clib_error_t *
743 delete_pipe_interfaces (vlib_main_t * vm,
744 unformat_input_t * input, vlib_cli_command_t * cmd)
746 vnet_main_t *vnm = vnet_get_main ();
747 u32 sw_if_index = ~0;
750 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
752 if (unformat (input, "%U",
753 unformat_vnet_sw_interface, vnm, &sw_if_index))
759 if (sw_if_index == ~0)
760 return clib_error_return (0, "interface not specified");
762 rv = vnet_delete_pipe_interface (sw_if_index);
765 return clib_error_return (0, "vnet_delete_pipe_interface failed");
771 * Delete a pipe interface.
774 * The following two command syntaxes are equivalent:
775 * @cliexcmd{pipe delete intfc <interface>}
776 * Example of how to delete a pipe interface:
777 * @cliexcmd{pipe delete-interface intfc loop0}
780 VLIB_CLI_COMMAND (pipe_delete_interface_command, static) = {
781 .path = "pipe delete",
782 .short_help = "pipe delete <interface>",
783 .function = delete_pipe_interfaces,
788 * fd.io coding-style-patch-verification: ON
791 * eval: (c-set-style "gnu")