aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeale Ranns <neale.ranns@cisco.com>2018-07-04 10:24:24 -0700
committerDave Barach <openvpp@barachs.net>2018-07-07 16:06:34 +0000
commit17ff3c1fa5687255a118c53223fa2cd49132d929 (patch)
tree874a3fbf5bed5408f9f9c84546a3b95df62ead79
parent65784c1602c7c8171effd00384f65f546d93a13b (diff)
Pipes
A pipe resembles a unix pipe. Each end of the pipe is a full VPP interface. pipes can be used for e.g. packet recirculation, inter-BD, etc. Change-Id: I185bb9fb43dd233ff45da63ac1b85ae2e1ceca16 Signed-off-by: Neale Ranns <neale.ranns@cisco.com>
-rw-r--r--src/vnet.am13
-rw-r--r--src/vnet/devices/pipe/pipe.api90
-rw-r--r--src/vnet/devices/pipe/pipe.c802
-rw-r--r--src/vnet/devices/pipe/pipe.h72
-rw-r--r--src/vnet/devices/pipe/pipe_api.c177
-rw-r--r--src/vnet/ethernet/interface.c7
-rwxr-xr-xsrc/vnet/ethernet/node.c11
-rw-r--r--src/vnet/interface.c26
-rw-r--r--src/vnet/interface.h1
-rw-r--r--src/vnet/interface_funcs.h18
-rw-r--r--src/vnet/ip/ip6_neighbor.c1
-rw-r--r--src/vnet/vnet_all_api_h.h1
-rw-r--r--test/test_pipe.py255
-rw-r--r--test/vpp_papi_provider.py12
14 files changed, 1479 insertions, 7 deletions
diff --git a/src/vnet.am b/src/vnet.am
index 0a9cb5ffee4..d6a5d524e53 100644
--- a/src/vnet.am
+++ b/src/vnet.am
@@ -945,6 +945,19 @@ nobase_include_HEADERS += \
API_FILES += vnet/devices/tap/tapv2.api
########################################
+# tap interface (with virtio backend)
+########################################
+
+libvnet_la_SOURCES += \
+ vnet/devices/pipe/pipe_api.c \
+ vnet/devices/pipe/pipe.c
+
+nobase_include_HEADERS += \
+ vnet/devices/pipe/pipe.h
+
+API_FILES += vnet/devices/pipe/pipe.api
+
+########################################
# session managmeent
########################################
diff --git a/src/vnet/devices/pipe/pipe.api b/src/vnet/devices/pipe/pipe.api
new file mode 100644
index 00000000000..d3dfd16bcd4
--- /dev/null
+++ b/src/vnet/devices/pipe/pipe.api
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** \file
+
+ This file defines vpe control-plane API messages for
+ the Linux kernel PIPE device driver
+*/
+
+option version = "1.0.0";
+
+/** \brief Initialize a new pipe interface with the given paramters
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param is_specified - if non-0, a specific user_instance is being requested
+ @param user_instance - requested instance, ~0 => dynamically allocate
+*/
+define pipe_create
+{
+ u32 client_index;
+ u32 context;
+ u8 is_specified;
+ u32 user_instance;
+};
+
+/** \brief Reply for pipe create reply
+ @param context - returned sender context, to match reply w/ request
+ @param retval - return code
+ @param parent_sw_if_index - software index allocated for the new pipe parent interface
+ Use the parent interface for link up/down and to delete
+ @param pipe_sw_if_index - the two SW indicies that form the ends of the pipe.
+*/
+define pipe_create_reply
+{
+ u32 context;
+ i32 retval;
+ u32 parent_sw_if_index;
+ u32 pipe_sw_if_index[2];
+};
+
+/** \brief Delete pipe interface
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param parnet_sw_if_index - interface index of existing pipe interface
+*/
+autoreply define pipe_delete
+{
+ u32 client_index;
+ u32 context;
+ u32 parent_sw_if_index;
+};
+
+/** \brief Dump pipe interfaces request */
+define pipe_dump
+{
+ u32 client_index;
+ u32 context;
+};
+
+/** \brief Reply for pipe dump request
+ @param sw_if_index - software index of pipe interface
+ @param parent_sw_if_index - software index allocated for the pipe parent interface
+ @param pipe_sw_if_index - the two SW indicies that form the ends of the pipe.
+ @param instance - instance allocated
+*/
+define pipe_details
+{
+ u32 context;
+ u32 parent_sw_if_index;
+ u32 pipe_sw_if_index[2];
+ u32 instance;
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/devices/pipe/pipe.c b/src/vnet/devices/pipe/pipe.c
new file mode 100644
index 00000000000..29f54e19917
--- /dev/null
+++ b/src/vnet/devices/pipe/pipe.c
@@ -0,0 +1,802 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/devices/pipe/pipe.h>
+
+#include <vppinfra/sparse_vec.h>
+
+/**
+ * @file
+ * @brief Pipe Interfaces.
+ *
+ * A pipe interface, like the UNIX pipe, is a pair of interfaces
+ * that are joined.
+ */
+const static pipe_t PIPE_INVALID = {
+ .sw_if_index = ~0,
+ .subint = {0},
+};
+
+/**
+ * Various 'module' lavel variables
+ */
+typedef struct pipe_main_t_
+{
+ /**
+ * Allocated pipe instances
+ */
+ uword *instances;
+
+ /**
+ * the per-swif-index array of pipes. Each end of the pipe is stored againt
+ * its respective sw_if_index
+ */
+ pipe_t *pipes;
+} pipe_main_t;
+
+static pipe_main_t pipe_main;
+
+/*
+ * The pipe rewrite is the same size as an ethernet header (since it
+ * is an ethernet interface and the DP is optimised for writing
+ * sizeof(ethernet_header_t) rewirtes. Hwoever, there are no MAC addresses
+ * since pipes don't have them.
+ */
+static u8 *
+pipe_build_rewrite (vnet_main_t * vnm,
+ u32 sw_if_index,
+ vnet_link_t link_type, const void *dst_address)
+{
+ ethernet_header_t *h;
+ ethernet_type_t type;
+ u8 *rewrite = NULL;
+
+ switch (link_type)
+ {
+#define _(a,b) case VNET_LINK_##a: type = ETHERNET_TYPE_##b; break
+ _(IP4, IP4);
+ _(IP6, IP6);
+ _(MPLS, MPLS);
+ _(ARP, ARP);
+#undef _
+ default:
+ return NULL;
+ }
+
+ vec_validate (rewrite, sizeof (ethernet_header_t));
+
+ h = (ethernet_header_t *) rewrite;
+ h->type = clib_host_to_net_u16 (type);
+
+ return (rewrite);
+}
+
+/* *INDENT-OFF* */
+VNET_HW_INTERFACE_CLASS (pipe_hw_interface_class) = {
+ .name = "Pipe",
+ .build_rewrite = pipe_build_rewrite,
+ .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
+};
+/* *INDENT-ON* */
+
+pipe_t *
+pipe_get (u32 sw_if_index)
+{
+ vec_validate_init_empty (pipe_main.pipes, sw_if_index, PIPE_INVALID);
+
+ return (&pipe_main.pipes[sw_if_index]);
+}
+
+uword
+unformat_pipe_interface (unformat_input_t * input, va_list * args)
+{
+ vnet_main_t *vnm = va_arg (*args, vnet_main_t *);
+ u32 *result = va_arg (*args, u32 *);
+ u32 hw_if_index;
+ ethernet_main_t *em = &ethernet_main;
+ ethernet_interface_t *eif;
+
+ if (!unformat_user (input, unformat_vnet_hw_interface, vnm, &hw_if_index))
+ return 0;
+
+ eif = ethernet_get_interface (em, hw_if_index);
+ if (eif)
+ {
+ *result = hw_if_index;
+ return 1;
+ }
+ return 0;
+}
+
+#define VNET_PIPE_TX_NEXT_ETHERNET_INPUT VNET_INTERFACE_TX_N_NEXT
+
+/*
+ * The TX function bounces the packets back to pipe-rx with the TX interface
+ * swapped to the RX.
+ */
+static uword
+pipe_tx (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ u32 n_left_from, n_left_to_next, n_copy, *from, *to_next;
+ u32 next_index = VNET_PIPE_TX_NEXT_ETHERNET_INPUT;
+ u32 i, sw_if_index = 0;
+ u32 n_pkts = 0, n_bytes = 0;
+ u32 thread_index = vm->thread_index;
+ vnet_main_t *vnm = vnet_get_main ();
+ vnet_interface_main_t *im = &vnm->interface_main;
+ vlib_buffer_t *b;
+ pipe_t *pipe;
+
+ n_left_from = frame->n_vectors;
+ from = vlib_frame_args (frame);
+
+ while (n_left_from > 0)
+ {
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ n_copy = clib_min (n_left_from, n_left_to_next);
+
+ clib_memcpy (to_next, from, n_copy * sizeof (from[0]));
+ n_left_to_next -= n_copy;
+ n_left_from -= n_copy;
+ i = 0;
+ while (i < n_copy)
+ {
+ b = vlib_get_buffer (vm, from[i]);
+ sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_TX];
+
+ pipe = &pipe_main.pipes[sw_if_index];
+ // Set up RX index to be recv'd by the other end of the pipe
+ vnet_buffer (b)->sw_if_index[VLIB_RX] = pipe->sw_if_index;
+ vnet_buffer (b)->sw_if_index[VLIB_TX] = ~0;
+
+ i++;
+ n_pkts++;
+ n_bytes += vlib_buffer_length_in_chain (vm, b);
+ }
+ from += n_copy;
+
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+
+ /* increment TX interface stat */
+ vlib_increment_combined_counter (im->combined_sw_if_counters +
+ VNET_INTERFACE_COUNTER_TX,
+ thread_index, sw_if_index, n_pkts,
+ n_bytes);
+ }
+
+ return n_left_from;
+}
+
+static u8 *
+format_pipe_name (u8 * s, va_list * args)
+{
+ u32 dev_instance = va_arg (*args, u32);
+ return format (s, "pipe%d", dev_instance);
+}
+
+static clib_error_t *
+pipe_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
+{
+ vnet_hw_interface_t *hi;
+ u32 id, sw_if_index;
+
+ u32 hw_flags = ((flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ?
+ VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
+ vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
+
+ /* *INDENT-OFF* */
+ hi = vnet_get_hw_interface (vnm, hw_if_index);
+ hash_foreach (id, sw_if_index, hi->sub_interface_sw_if_index_by_id,
+ ({
+ vnet_sw_interface_set_flags (vnm, sw_if_index, flags);
+ }));
+ /* *INDENT-ON* */
+
+ return (NULL);
+}
+
+/* *INDENT-OFF* */
+VNET_DEVICE_CLASS (pipe_device_class) = {
+ .name = "Pipe",
+ .format_device_name = format_pipe_name,
+ .tx_function = pipe_tx,
+ .admin_up_down_function = pipe_admin_up_down,
+};
+/* *INDENT-ON* */
+
+#define foreach_pipe_rx_next \
+ _ (DROP, "error-drop")
+
+typedef enum pipe_rx_next_t_
+{
+#define _(s,n) PIPE_RX_NEXT_##s,
+ foreach_pipe_rx_next
+#undef _
+ PIPE_RX_N_NEXT,
+} pipe_rx_next_t;
+
+typedef struct pipe_rx_trace_t_
+{
+ u8 packet_data[32];
+} pipe_rx_trace_t;
+
+static u8 *
+format_pipe_rx_trace (u8 * s, va_list * va)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
+ pipe_rx_trace_t *t = va_arg (*va, pipe_rx_trace_t *);
+
+ s = format (s, "%U", format_ethernet_header, t->packet_data);
+
+ return s;
+}
+
+/*
+ * The pipe-rx node is a sibling of ethernet-input so steal it's
+ * next node mechanism
+ */
+static_always_inline void
+pipe_determine_next_node (ethernet_main_t * em,
+ u32 is_l20,
+ u32 type0,
+ vlib_buffer_t * b0, pipe_rx_next_t * next0)
+{
+ if (is_l20)
+ {
+ *next0 = em->l2_next;
+ }
+ else if (type0 == ETHERNET_TYPE_IP4)
+ {
+ *next0 = em->l3_next.input_next_ip4;
+ }
+ else if (type0 == ETHERNET_TYPE_IP6)
+ {
+ *next0 = em->l3_next.input_next_ip6;
+ }
+ else if (type0 == ETHERNET_TYPE_MPLS)
+ {
+ *next0 = em->l3_next.input_next_mpls;
+
+ }
+ else if (em->redirect_l3)
+ {
+ // L3 Redirect is on, the cached common next nodes will be
+ // pointing to the redirect node, catch the uncommon types here
+ *next0 = em->redirect_l3_next;
+ }
+ else
+ {
+ // uncommon ethertype, check table
+ u32 i0;
+ i0 = sparse_vec_index (em->l3_next.input_next_by_type, type0);
+ *next0 = vec_elt (em->l3_next.input_next_by_type, i0);
+
+ // The table is not populated with LLC values, so check that now.
+ if (type0 < 0x600)
+ {
+ *next0 = PIPE_RX_NEXT_DROP;
+ }
+ }
+}
+
+static_always_inline uword
+pipe_rx (vlib_main_t * vm,
+ vlib_node_runtime_t * node, vlib_frame_t * from_frame)
+{
+ u32 n_left_from, next_index, *from, *to_next;
+ u32 n_left_to_next;
+
+ from = vlib_frame_vector_args (from_frame);
+ n_left_from = from_frame->n_vectors;
+
+ if (node->flags & VLIB_NODE_FLAG_TRACE)
+ vlib_trace_frame_buffers_only (vm, node,
+ from,
+ n_left_from,
+ sizeof (from[0]),
+ sizeof (pipe_rx_trace_t));
+
+ next_index = node->cached_next_index;
+
+ while (n_left_from > 0)
+ {
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from >= 4 && n_left_to_next >= 2)
+ {
+ u32 bi0, sw_if_index0, bi1, sw_if_index1;
+ pipe_rx_next_t next0, next1;
+ ethernet_header_t *e0, *e1;
+ vlib_buffer_t *b0, *b1;
+ pipe_t *pipe0, *pipe1;
+ u8 is_l20, is_l21;
+ u16 type0, type1;
+
+ // Prefetch next iteration
+ {
+ vlib_buffer_t *p2, *p3;
+
+ p2 = vlib_get_buffer (vm, from[2]);
+ p3 = vlib_get_buffer (vm, from[3]);
+ vlib_prefetch_buffer_header (p2, STORE);
+ vlib_prefetch_buffer_header (p3, STORE);
+ CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, LOAD);
+ CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, LOAD);
+ }
+
+ bi0 = from[0];
+ to_next[0] = bi0;
+ bi1 = from[1];
+ to_next[1] = bi1;
+ from += 2;
+ to_next += 2;
+ n_left_from -= 2;
+ n_left_to_next -= 2;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ b1 = vlib_get_buffer (vm, bi1);
+
+ e0 = vlib_buffer_get_current (b0);
+ e1 = vlib_buffer_get_current (b1);
+ sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
+ type0 = clib_net_to_host_u16 (e0->type);
+ type1 = clib_net_to_host_u16 (e1->type);
+ pipe0 = &pipe_main.pipes[sw_if_index0];
+ pipe1 = &pipe_main.pipes[sw_if_index1];
+
+ vnet_buffer (b0)->l3_hdr_offset =
+ vnet_buffer (b0)->l2_hdr_offset + vnet_buffer (b0)->l2.l2_len;
+ vnet_buffer (b1)->l3_hdr_offset =
+ vnet_buffer (b1)->l2_hdr_offset + vnet_buffer (b1)->l2.l2_len;
+ b0->flags |= VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
+ b1->flags |= VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
+
+ is_l20 = pipe0->subint.flags & SUBINT_CONFIG_L2;
+ is_l21 = pipe1->subint.flags & SUBINT_CONFIG_L2;
+ pipe_determine_next_node (&ethernet_main, is_l20, type0, b0,
+ &next0);
+ pipe_determine_next_node (&ethernet_main, is_l21, type1, b1,
+ &next1);
+
+ if (!is_l20)
+ vlib_buffer_advance (b0, sizeof (ethernet_header_t));
+ else
+ {
+ u32 eth_start = vnet_buffer (b0)->l2_hdr_offset;
+ vnet_buffer (b0)->l2.l2_len = b0->current_data - eth_start;
+ }
+ if (!is_l21)
+ vlib_buffer_advance (b1, sizeof (ethernet_header_t));
+ else
+ {
+ u32 eth_start = vnet_buffer (b1)->l2_hdr_offset;
+ vnet_buffer (b1)->l2.l2_len = b1->current_data - eth_start;
+ }
+
+ vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, bi1, next0, next0);
+ }
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ u32 bi0, sw_if_index0;
+ vlib_buffer_t *b0;
+ pipe_rx_next_t next0;
+ ethernet_header_t *e0;
+ pipe_t *pipe0;
+ u16 type0;
+ u8 is_l20;
+
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+
+ e0 = vlib_buffer_get_current (b0);
+ sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ type0 = clib_net_to_host_u16 (e0->type);
+ pipe0 = &pipe_main.pipes[sw_if_index0];
+
+ vnet_buffer (b0)->l3_hdr_offset =
+ vnet_buffer (b0)->l2_hdr_offset + vnet_buffer (b0)->l2.l2_len;
+ b0->flags |= VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
+
+ is_l20 = pipe0->subint.flags & SUBINT_CONFIG_L2;
+ pipe_determine_next_node (&ethernet_main, is_l20, type0, b0,
+ &next0);
+
+ if (!is_l20)
+ vlib_buffer_advance (b0, sizeof (ethernet_header_t));
+ else
+ {
+ u32 eth_start = vnet_buffer (b0)->l2_hdr_offset;
+ vnet_buffer (b0)->l2.l2_len = b0->current_data - eth_start;
+ }
+
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, next0);
+ }
+
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ return from_frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (pipe_rx_node) = {
+ .function = pipe_rx,
+ .name = "pipe-rx",
+ /* Takes a vector of packets. */
+ .vector_size = sizeof (u32),
+ .format_trace = format_pipe_rx_trace,
+
+ .sibling_of = "ethernet-input",
+};
+/* *INDENT-ON* */
+
+/*
+ * Maintain a bitmap of allocated pipe instance numbers.
+ */
+#define PIPE_MAX_INSTANCE (16 * 1024)
+
+static u32
+pipe_instance_alloc (u8 is_specified, u32 want)
+{
+ /*
+ * Check for dynamically allocaetd instance number.
+ */
+ if (!is_specified)
+ {
+ u32 bit;
+
+ bit = clib_bitmap_first_clear (pipe_main.instances);
+ if (bit >= PIPE_MAX_INSTANCE)
+ {
+ return ~0;
+ }
+ pipe_main.instances = clib_bitmap_set (pipe_main.instances, bit, 1);
+ return bit;
+ }
+
+ /*
+ * In range?
+ */
+ if (want >= PIPE_MAX_INSTANCE)
+ {
+ return ~0;
+ }
+
+ /*
+ * Already in use?
+ */
+ if (clib_bitmap_get (pipe_main.instances, want))
+ {
+ return ~0;
+ }
+
+ /*
+ * Grant allocation request.
+ */
+ pipe_main.instances = clib_bitmap_set (pipe_main.instances, want, 1);
+
+ return want;
+}
+
+static int
+pipe_instance_free (u32 instance)
+{
+ if (instance >= PIPE_MAX_INSTANCE)
+ {
+ return -1;
+ }
+
+ if (clib_bitmap_get (pipe_main.instances, instance) == 0)
+ {
+ return -1;
+ }
+
+ pipe_main.instances = clib_bitmap_set (pipe_main.instances, instance, 0);
+ return 0;
+}
+
+static clib_error_t *
+pipe_create_sub_interface (vnet_hw_interface_t * hi,
+ uint32_t sub_id, u32 * sw_if_index)
+{
+ vnet_sw_interface_t template;
+
+ memset (&template, 0, sizeof (template));
+ template.type = VNET_SW_INTERFACE_TYPE_PIPE;
+ template.flood_class = VNET_FLOOD_CLASS_NORMAL;
+ template.sup_sw_if_index = hi->sw_if_index;
+ template.sub.id = sub_id;
+
+ return (vnet_create_sw_interface (vnet_get_main (),
+ &template, sw_if_index));
+}
+
+int
+vnet_create_pipe_interface (u8 is_specified,
+ u32 user_instance,
+ u32 * parent_sw_if_index, u32 pipe_sw_if_index[2])
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ vlib_main_t *vm = vlib_get_main ();
+ uint8_t address[6] = {
+ [0] = 0x22,
+ [1] = 0x22,
+ };
+ vnet_hw_interface_t *hi;
+ clib_error_t *error;
+ u32 hw_if_index;
+ u32 instance;
+ u32 slot;
+ int rv = 0;
+
+ ASSERT (parent_sw_if_index);
+
+ memset (address, 0, sizeof (address));
+
+ /*
+ * Allocate a pipe instance. Either select one dynamically
+ * or try to use the desired user_instance number.
+ */
+ instance = pipe_instance_alloc (is_specified, user_instance);
+ if (instance == ~0)
+ {
+ return VNET_API_ERROR_INVALID_REGISTRATION;
+ }
+
+ /*
+ * Default MAC address (0000:0000:0000 + instance) is allocated
+ */
+ address[5] = instance;
+
+ error = ethernet_register_interface (vnm, pipe_device_class.index,
+ instance, address, &hw_if_index,
+ /* flag change */ 0);
+
+ if (error)
+ {
+ rv = VNET_API_ERROR_INVALID_REGISTRATION;
+ goto oops;
+ }
+
+ hi = vnet_get_hw_interface (vnm, hw_if_index);
+ *parent_sw_if_index = hi->sw_if_index;
+ slot = vlib_node_add_named_next_with_slot (vm, hi->tx_node_index,
+ "pipe-rx",
+ VNET_PIPE_TX_NEXT_ETHERNET_INPUT);
+ ASSERT (slot == VNET_PIPE_TX_NEXT_ETHERNET_INPUT);
+
+ /*
+ * create two sub-interfaces, one for each end of the pipe.
+ */
+ error = pipe_create_sub_interface (hi, 0, &pipe_sw_if_index[0]);
+
+ if (error)
+ goto oops;
+
+ error = pipe_create_sub_interface (hi, 1, &pipe_sw_if_index[1]);
+
+ if (error)
+ goto oops;
+
+ hash_set (hi->sub_interface_sw_if_index_by_id, 0, pipe_sw_if_index[0]);
+ hash_set (hi->sub_interface_sw_if_index_by_id, 1, pipe_sw_if_index[1]);
+
+ vec_validate_init_empty (pipe_main.pipes, pipe_sw_if_index[0],
+ PIPE_INVALID);
+ vec_validate_init_empty (pipe_main.pipes, pipe_sw_if_index[1],
+ PIPE_INVALID);
+
+ pipe_main.pipes[pipe_sw_if_index[0]].sw_if_index = pipe_sw_if_index[1];
+ pipe_main.pipes[pipe_sw_if_index[1]].sw_if_index = pipe_sw_if_index[0];
+
+ return 0;
+
+oops:
+ clib_error_report (error);
+ return rv;
+}
+
+typedef struct pipe_hw_walk_ctx_t_
+{
+ pipe_cb_fn_t cb;
+ void *ctx;
+} pipe_hw_walk_ctx_t;
+
+static walk_rc_t
+pipe_hw_walk (vnet_main_t * vnm, u32 hw_if_index, void *args)
+{
+ vnet_hw_interface_t *hi;
+ pipe_hw_walk_ctx_t *ctx;
+
+ ctx = args;
+ hi = vnet_get_hw_interface (vnm, hw_if_index);
+
+ if (hi->dev_class_index == pipe_device_class.index)
+ {
+ u32 pipe_sw_if_index[2], id, sw_if_index;
+
+ /* *INDENT-OFF* */
+ hash_foreach (id, sw_if_index, hi->sub_interface_sw_if_index_by_id,
+ ({
+ ASSERT(id < 2);
+ pipe_sw_if_index[id] = sw_if_index;
+ }));
+ /* *INDENT-ON* */
+
+ ctx->cb (hi->sw_if_index, pipe_sw_if_index, hi->dev_instance, ctx->ctx);
+ }
+
+ return (WALK_CONTINUE);
+}
+
+void
+pipe_walk (pipe_cb_fn_t fn, void *ctx)
+{
+ pipe_hw_walk_ctx_t wctx = {
+ .cb = fn,
+ .ctx = ctx,
+ };
+
+ ASSERT (fn);
+
+ vnet_hw_interface_walk (vnet_get_main (), pipe_hw_walk, &wctx);
+}
+
+static clib_error_t *
+create_pipe_interfaces (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ int rv;
+ u32 sw_if_index;
+ u32 pipe_sw_if_index[2];
+ u8 is_specified = 0;
+ u32 user_instance = 0;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "instance %d", &user_instance))
+ is_specified = 1;
+ else
+ break;
+ }
+
+ rv = vnet_create_pipe_interface (is_specified, user_instance,
+ &sw_if_index, pipe_sw_if_index);
+
+ if (rv)
+ return clib_error_return (0, "vnet_create_pipe_interface failed");
+
+ vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name,
+ vnet_get_main (), sw_if_index);
+ return 0;
+}
+
+/*?
+ * Create a pipe interface.
+ *
+ * @cliexpar
+ * The following two command syntaxes are equivalent:
+ * @cliexcmd{pipe create-interface [mac <mac-addr>] [instance <instance>]}
+ * Example of how to create a pipe interface:
+ * @cliexcmd{pipe create}
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (pipe_create_interface_command, static) = {
+ .path = "pipe create",
+ .short_help = "pipe create [instance <instance>]",
+ .function = create_pipe_interfaces,
+};
+/* *INDENT-ON* */
+
+int
+vnet_delete_pipe_interface (u32 sw_if_index)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ vnet_sw_interface_t *si;
+ vnet_hw_interface_t *hi;
+ u32 instance, id;
+ u32 hw_if_index;
+
+ if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+
+ si = vnet_get_sw_interface (vnm, sw_if_index);
+ hw_if_index = si->hw_if_index;
+ hi = vnet_get_hw_interface (vnm, hw_if_index);
+ instance = hi->dev_instance;
+
+ if (pipe_instance_free (instance) < 0)
+ {
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+ }
+
+ /* *INDENT-OFF* */
+ hash_foreach (id, sw_if_index, hi->sub_interface_sw_if_index_by_id,
+ ({
+ vnet_delete_sub_interface(sw_if_index);
+ pipe_main.pipes[sw_if_index] = PIPE_INVALID;
+ }));
+ /* *INDENT-ON* */
+
+ ethernet_delete_interface (vnm, hw_if_index);
+
+ return 0;
+}
+
+static clib_error_t *
+delete_pipe_interfaces (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ u32 sw_if_index = ~0;
+ int rv;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "%U",
+ unformat_vnet_sw_interface, vnm, &sw_if_index))
+ ;
+ else
+ break;
+ }
+
+ if (sw_if_index == ~0)
+ return clib_error_return (0, "interface not specified");
+
+ rv = vnet_delete_pipe_interface (sw_if_index);
+
+ if (rv)
+ return clib_error_return (0, "vnet_delete_pipe_interface failed");
+
+ return 0;
+}
+
+/*?
+ * Delete a pipe interface.
+ *
+ * @cliexpar
+ * The following two command syntaxes are equivalent:
+ * @cliexcmd{pipe delete intfc <interface>}
+ * Example of how to delete a pipe interface:
+ * @cliexcmd{pipe delete-interface intfc loop0}
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (pipe_delete_interface_command, static) = {
+ .path = "pipe delete",
+ .short_help = "pipe delete <interface>",
+ .function = delete_pipe_interfaces,
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/devices/pipe/pipe.h b/src/vnet/devices/pipe/pipe.h
new file mode 100644
index 00000000000..ef72934c3ba
--- /dev/null
+++ b/src/vnet/devices/pipe/pipe.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __PIPE_H__
+#define __PIPE_H__
+
+#include <vnet/ethernet/ethernet.h>
+
+/**
+ * represenation of a pipe interface
+ */
+typedef struct pipe_t_
+{
+ /** the SW if_index of the other end of the pipe */
+ u32 sw_if_index;
+
+ /** Sub-interface config */
+ subint_config_t subint;
+} pipe_t;
+
+/**
+ * Create a new pipe interface
+ *
+ * @param is_specified Has the user speficied a desired instance number
+ * @param user_instance The user's desired instnace
+ * @param parent_sw_index OUT the created parent interface
+ * @param pipe_sw_if_index OUT the ends of the pipe
+ */
+extern int vnet_create_pipe_interface (u8 is_specified,
+ u32 user_instance,
+ u32 * parent_sw_if_index,
+ u32 pipe_sw_if_index[2]);
+extern int vnet_delete_pipe_interface (u32 parent_sw_if_index);
+
+/**
+ * Get the pipe instnace based on one end
+ */
+extern pipe_t *pipe_get (u32 sw_if_index);
+
+/**
+ * Call back function when walking all the pipes
+ */
+typedef walk_rc_t (*pipe_cb_fn_t) (u32 parent_sw_if_index,
+ u32 pipe_sw_if_index[2],
+ u32 instance, void *ctx);
+
+/**
+ * Walk all the of pipe interfaces
+ */
+extern void pipe_walk (pipe_cb_fn_t fn, void *ctx);
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/devices/pipe/pipe_api.c b/src/vnet/devices/pipe/pipe_api.c
new file mode 100644
index 00000000000..46bbea5f275
--- /dev/null
+++ b/src/vnet/devices/pipe/pipe_api.c
@@ -0,0 +1,177 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+ */
+
+#include <vnet/vnet.h>
+#include <vlibmemory/api.h>
+
+#include <vnet/devices/pipe/pipe.h>
+#include <vnet/vnet_msg_enum.h>
+
+#define vl_typedefs /* define message structures */
+#include <vnet/vnet_all_api_h.h>
+#undef vl_typedefs
+
+#define vl_endianfun /* define message structures */
+#include <vnet/vnet_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
+#define vl_printfun
+#include <vnet/vnet_all_api_h.h>
+#undef vl_printfun
+
+#include <vlibapi/api_helper_macros.h>
+vpe_api_main_t vpe_api_main;
+
+#define foreach_vpe_api_msg \
+ _(PIPE_CREATE, pipe_create) \
+ _(PIPE_DELETE, pipe_delete) \
+ _(PIPE_DUMP, pipe_dump)
+
+static void
+vl_api_pipe_create_t_handler (vl_api_pipe_create_t * mp)
+{
+ vl_api_pipe_create_reply_t *rmp;
+ u32 parent_sw_if_index;
+ u32 pipe_sw_if_index[2];
+ int rv;
+ u8 is_specified = mp->is_specified;
+ u32 user_instance = ntohl (mp->user_instance);
+
+ rv = vnet_create_pipe_interface (is_specified, user_instance,
+ &parent_sw_if_index, pipe_sw_if_index);
+
+ /* *INDENT-OFF* */
+ REPLY_MACRO2(VL_API_PIPE_CREATE_REPLY,
+ ({
+ rmp->parent_sw_if_index = ntohl (parent_sw_if_index);
+ rmp->pipe_sw_if_index[0] = ntohl (pipe_sw_if_index[0]);
+ rmp->pipe_sw_if_index[1] = ntohl (pipe_sw_if_index[1]);
+ }));
+ /* *INDENT-ON* */
+}
+
+static void
+vl_api_pipe_delete_t_handler (vl_api_pipe_delete_t * mp)
+{
+ vl_api_pipe_delete_reply_t *rmp;
+ int rv;
+
+ rv = vnet_delete_pipe_interface (ntohl (mp->parent_sw_if_index));
+
+ REPLY_MACRO (VL_API_PIPE_DELETE_REPLY);
+}
+
+typedef struct pipe_dump_walk_t_
+{
+ vl_api_registration_t *reg;
+ u32 context;
+} pipe_dump_walk_t;
+
+static walk_rc_t
+pipe_send_details (u32 parent_sw_if_index,
+ u32 pipe_sw_if_index[2], u32 instance, void *args)
+{
+ pipe_dump_walk_t *ctx = args;
+ vl_api_pipe_details_t *mp;
+
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ if (!mp)
+ return (WALK_STOP);
+
+ mp->_vl_msg_id = ntohs (VL_API_PIPE_DETAILS);
+ mp->context = ctx->context;
+
+ mp->instance = ntohl (instance);
+ mp->parent_sw_if_index = ntohl (parent_sw_if_index);
+ mp->pipe_sw_if_index[0] = ntohl (pipe_sw_if_index[0]);
+ mp->pipe_sw_if_index[1] = ntohl (pipe_sw_if_index[1]);
+
+ vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+ return (WALK_CONTINUE);
+}
+
+static void
+vl_api_pipe_dump_t_handler (vl_api_pipe_dump_t * mp)
+{
+ vl_api_registration_t *reg;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ pipe_dump_walk_t ctx = {
+ .reg = reg,
+ .context = mp->context,
+ };
+
+ pipe_walk (pipe_send_details, &ctx);
+}
+
+/*
+ * vpe_api_hookup
+ * Add vpe's API message handlers to the table.
+ * vlib has alread mapped shared memory and
+ * added the client registration handlers.
+ * See .../vlib-api/vlibmemory/memclnt_vlib.c:memclnt_process()
+ */
+#define vl_msg_name_crc_list
+#include <vnet/devices/pipe/pipe.api.h>
+#undef vl_msg_name_crc_list
+
+static void
+setup_message_id_table (api_main_t * am)
+{
+#define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
+ foreach_vl_msg_name_crc_pipe;
+#undef _
+}
+
+static clib_error_t *
+pipe_api_hookup (vlib_main_t * vm)
+{
+ api_main_t *am = &api_main;
+
+#define _(N,n) \
+ vl_msg_api_set_handlers(VL_API_##N, #n, \
+ vl_api_##n##_t_handler, \
+ vl_noop_handler, \
+ vl_api_##n##_t_endian, \
+ vl_api_##n##_t_print, \
+ sizeof(vl_api_##n##_t), 1);
+ foreach_vpe_api_msg;
+#undef _
+
+ /*
+ * Set up the (msg_name, crc, message-id) table
+ */
+ setup_message_id_table (am);
+
+ return 0;
+}
+
+VLIB_API_INIT_FUNCTION (pipe_api_hookup);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ethernet/interface.c b/src/vnet/ethernet/interface.c
index b3ea983e50d..e39ae7b989e 100644
--- a/src/vnet/ethernet/interface.c
+++ b/src/vnet/ethernet/interface.c
@@ -91,7 +91,8 @@ ethernet_build_rewrite (vnet_main_t * vnm,
u8 *rewrite = NULL;
u8 is_p2p = 0;
- if (sub_sw->type == VNET_SW_INTERFACE_TYPE_P2P)
+ if ((sub_sw->type == VNET_SW_INTERFACE_TYPE_P2P) ||
+ (sub_sw->type == VNET_SW_INTERFACE_TYPE_PIPE))
is_p2p = 1;
if (sub_sw != sup_sw)
{
@@ -197,7 +198,8 @@ ethernet_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, u32 ai)
adj = adj_get (ai);
vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index);
- if (si->type == VNET_SW_INTERFACE_TYPE_P2P)
+ if ((si->type == VNET_SW_INTERFACE_TYPE_P2P) ||
+ (si->type == VNET_SW_INTERFACE_TYPE_PIPE))
{
default_update_adjacency (vnm, sw_if_index, ai);
}
@@ -748,6 +750,7 @@ vnet_delete_sub_interface (u32 sw_if_index)
si = vnet_get_sw_interface (vnm, sw_if_index);
if (si->type == VNET_SW_INTERFACE_TYPE_SUB ||
+ si->type == VNET_SW_INTERFACE_TYPE_PIPE ||
si->type == VNET_SW_INTERFACE_TYPE_P2P)
{
vnet_interface_main_t *im = &vnm->interface_main;
diff --git a/src/vnet/ethernet/node.c b/src/vnet/ethernet/node.c
index 55d8285f81e..86676673ed3 100755
--- a/src/vnet/ethernet/node.c
+++ b/src/vnet/ethernet/node.c
@@ -41,6 +41,7 @@
#include <vnet/pg/pg.h>
#include <vnet/ethernet/ethernet.h>
#include <vnet/ethernet/p2p_ethernet.h>
+#include <vnet/devices/pipe/pipe.h>
#include <vppinfra/sparse_vec.h>
#include <vnet/l2/l2_bvi.h>
@@ -838,6 +839,14 @@ ethernet_sw_interface_get_config (vnet_main_t * vnm,
subint = vec_elt_at_index (p2pm->p2p_subif_pool, si->p2p.pool_index);
*flags = SUBINT_CONFIG_P2P;
}
+ else if (si->type == VNET_SW_INTERFACE_TYPE_PIPE)
+ {
+ pipe_t *pipe;
+
+ pipe = pipe_get (sw_if_index);
+ subint = &pipe->subint;
+ *flags = SUBINT_CONFIG_P2P;
+ }
else if (si->sub.eth.flags.default_sub)
{
subint = &main_intf->default_subint;
@@ -1127,7 +1136,7 @@ ethernet_sw_interface_add_del (vnet_main_t * vnm,
}
else
{
- // Note that config is L3 by defaulty
+ // Note that config is L3 by default
subint->flags = SUBINT_CONFIG_VALID | match_flags;
subint->sw_if_index = ~0; // because interfaces are initially down
}
diff --git a/src/vnet/interface.c b/src/vnet/interface.c
index 34ad292a728..a9346a2b072 100644
--- a/src/vnet/interface.c
+++ b/src/vnet/interface.c
@@ -1042,8 +1042,9 @@ vnet_hw_interface_walk_sw (vnet_main_t * vnm,
u32 id, sw_if_index;
hi = vnet_get_hw_interface (vnm, hw_if_index);
- /* the super first, then the and sub interfaces */
- fn (vnm, hi->sw_if_index, ctx);
+ /* the super first, then the sub interfaces */
+ if (WALK_STOP == fn (vnm, hi->sw_if_index, ctx))
+ return;
/* *INDENT-OFF* */
hash_foreach (id, sw_if_index,
@@ -1056,6 +1057,24 @@ vnet_hw_interface_walk_sw (vnet_main_t * vnm,
}
void
+vnet_hw_interface_walk (vnet_main_t * vnm,
+ vnet_hw_interface_walk_t fn, void *ctx)
+{
+ vnet_interface_main_t *im;
+ vnet_hw_interface_t *hi;
+
+ im = &vnm->interface_main;
+
+ /* *INDENT-OFF* */
+ pool_foreach (hi, im->hw_interfaces,
+ ({
+ if (WALK_STOP == fn(vnm, hi->hw_if_index, ctx))
+ break;
+ }));
+ /* *INDENT-ON* */
+}
+
+void
vnet_sw_interface_walk (vnet_main_t * vnm,
vnet_sw_interface_walk_t fn, void *ctx)
{
@@ -1257,7 +1276,8 @@ int
vnet_sw_interface_is_p2p (vnet_main_t * vnm, u32 sw_if_index)
{
vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index);
- if (si->type == VNET_SW_INTERFACE_TYPE_P2P)
+ if ((si->type == VNET_SW_INTERFACE_TYPE_P2P) ||
+ (si->type == VNET_SW_INTERFACE_TYPE_PIPE))
return 1;
vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
diff --git a/src/vnet/interface.h b/src/vnet/interface.h
index 1181c341a0c..f82cf9d2485 100644
--- a/src/vnet/interface.h
+++ b/src/vnet/interface.h
@@ -578,6 +578,7 @@ typedef enum
/* A sub-interface. */
VNET_SW_INTERFACE_TYPE_SUB,
VNET_SW_INTERFACE_TYPE_P2P,
+ VNET_SW_INTERFACE_TYPE_PIPE,
} vnet_sw_interface_type_t;
typedef struct
diff --git a/src/vnet/interface_funcs.h b/src/vnet/interface_funcs.h
index c4dd3b72e51..08af7f26151 100644
--- a/src/vnet/interface_funcs.h
+++ b/src/vnet/interface_funcs.h
@@ -82,6 +82,7 @@ vnet_get_sup_sw_interface (vnet_main_t * vnm, u32 sw_if_index)
{
vnet_sw_interface_t *sw = vnet_get_sw_interface (vnm, sw_if_index);
if (sw->type == VNET_SW_INTERFACE_TYPE_SUB ||
+ sw->type == VNET_SW_INTERFACE_TYPE_PIPE ||
sw->type == VNET_SW_INTERFACE_TYPE_P2P)
sw = vnet_get_sw_interface (vnm, sw->sup_sw_if_index);
return sw;
@@ -91,7 +92,8 @@ always_inline vnet_hw_interface_t *
vnet_get_sup_hw_interface (vnet_main_t * vnm, u32 sw_if_index)
{
vnet_sw_interface_t *sw = vnet_get_sup_sw_interface (vnm, sw_if_index);
- ASSERT (sw->type == VNET_SW_INTERFACE_TYPE_HARDWARE);
+ ASSERT ((sw->type == VNET_SW_INTERFACE_TYPE_HARDWARE) ||
+ (sw->type == VNET_SW_INTERFACE_TYPE_PIPE));
return vnet_get_hw_interface (vnm, sw->hw_if_index);
}
@@ -185,6 +187,20 @@ typedef walk_rc_t (*vnet_sw_interface_walk_t) (vnet_main_t * vnm,
void vnet_sw_interface_walk (vnet_main_t * vnm,
vnet_sw_interface_walk_t fn, void *ctx);
+
+/**
+ * Call back walk type for walking all HW indices
+ */
+typedef walk_rc_t (*vnet_hw_interface_walk_t) (vnet_main_t * vnm,
+ u32 hw_if_index, void *ctx);
+
+/**
+ * @brief
+ * Walk all the HW interface
+ */
+void vnet_hw_interface_walk (vnet_main_t * vnm,
+ vnet_hw_interface_walk_t fn, void *ctx);
+
/* Register a hardware interface instance. */
u32 vnet_register_interface (vnet_main_t * vnm,
u32 dev_class_index,
diff --git a/src/vnet/ip/ip6_neighbor.c b/src/vnet/ip/ip6_neighbor.c
index 6d91224c0b4..87bda439ae5 100644
--- a/src/vnet/ip/ip6_neighbor.c
+++ b/src/vnet/ip/ip6_neighbor.c
@@ -4190,6 +4190,7 @@ enable_ip6_interface (vlib_main_t * vm, u32 sw_if_index)
sw_if0 = vnet_get_sw_interface (vnm, sw_if_index);
if (sw_if0->type == VNET_SW_INTERFACE_TYPE_SUB ||
+ sw_if0->type == VNET_SW_INTERFACE_TYPE_PIPE ||
sw_if0->type == VNET_SW_INTERFACE_TYPE_P2P)
{
/* make up an interface id */
diff --git a/src/vnet/vnet_all_api_h.h b/src/vnet/vnet_all_api_h.h
index 06b76a5e926..f07be653e20 100644
--- a/src/vnet/vnet_all_api_h.h
+++ b/src/vnet/vnet_all_api_h.h
@@ -74,6 +74,7 @@
#include <vnet/qos/qos.api.h>
#include <vnet/dhcp/dhcp6_pd_client_cp.api.h>
#include <vnet/dhcp/dhcp6_ia_na_client_cp.api.h>
+#include <vnet/devices/pipe/pipe.api.h>
/*
* fd.io coding-style-patch-verification: ON
diff --git a/test/test_pipe.py b/test/test_pipe.py
new file mode 100644
index 00000000000..a89c1e90236
--- /dev/null
+++ b/test/test_pipe.py
@@ -0,0 +1,255 @@
+#!/usr/bin/env python
+from socket import AF_INET, AF_INET6, inet_pton
+
+from framework import VppTestCase, VppTestRunner
+from vpp_interface import VppInterface
+from vpp_ip_route import VppIpTable, VppIpRoute, VppRoutePath
+
+from scapy.packet import Raw
+from scapy.layers.l2 import Ether
+from scapy.layers.inet import IP, UDP
+
+
+class VppPipe(VppInterface):
+ """
+ VPP Pipe
+ """
+
+ @property
+ def east(self):
+ return self.result.pipe_sw_if_index[1]
+
+ @property
+ def west(self):
+ return self.result.pipe_sw_if_index[0]
+
+ def __init__(self, test, instance=0xffffffff):
+ super(VppPipe, self).__init__(test)
+ self._test = test
+ self.instance = instance
+
+ def add_vpp_config(self):
+ self.result = self._test.vapi.pipe_create(
+ 0 if self.instance == 0xffffffff else 1,
+ self.instance)
+ self.set_sw_if_index(self.result.parent_sw_if_index)
+
+ def remove_vpp_config(self):
+ self._test.vapi.pipe_delete(
+ self.result.parent_sw_if_index)
+
+ def __str__(self):
+ return self.object_id()
+
+ def object_id(self):
+ return "pipe-%d" % (self._sw_if_index)
+
+ def query_vpp_config(self):
+ pipes = self._test.vapi.pipe_dump()
+ for p in pipes:
+ if p.parent_sw_if_index == self.result.parent_sw_if_index:
+ return True
+ return False
+
+ def set_unnumbered(self, ip_sw_if_index, is_add=True):
+ res = self._test.vapi.sw_interface_set_unnumbered(
+ self.east, ip_sw_if_index, is_add)
+ res = self._test.vapi.sw_interface_set_unnumbered(
+ self.west, ip_sw_if_index, is_add)
+
+
+class TestPipe(VppTestCase):
+ """ Pipes """
+
+ def setUp(self):
+ super(TestPipe, self).setUp()
+
+ self.create_pg_interfaces(range(4))
+
+ for i in self.pg_interfaces:
+ i.admin_up()
+
+ def tearDown(self):
+ for i in self.pg_interfaces:
+ i.admin_down()
+
+ super(TestPipe, self).tearDown()
+
+ def test_pipe(self):
+ """ Pipes """
+
+ pipes = []
+ pipes.append(VppPipe(self))
+ pipes.append(VppPipe(self, 10))
+
+ for p in pipes:
+ p.add_vpp_config()
+ p.admin_up()
+
+ #
+ # L2 cross-connect pipe0 east with pg0 and west with pg1
+ #
+ self.vapi.sw_interface_set_l2_xconnect(self.pg0.sw_if_index,
+ pipes[0].east,
+ enable=1)
+ self.vapi.sw_interface_set_l2_xconnect(pipes[0].east,
+ self.pg0.sw_if_index,
+ enable=1)
+ self.vapi.sw_interface_set_l2_xconnect(self.pg1.sw_if_index,
+ pipes[0].west,
+ enable=1)
+ self.vapi.sw_interface_set_l2_xconnect(pipes[0].west,
+ self.pg1.sw_if_index,
+ enable=1)
+
+ # test bi-drectional L2 flow pg0<->pg1
+ p = (Ether(src=self.pg0.remote_mac,
+ dst=self.pg1.remote_mac) /
+ IP(src="1.1.1.1",
+ dst="1.1.1.2") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+
+ self.send_and_expect(self.pg0, p * 65, self.pg1)
+ self.send_and_expect(self.pg1, p * 65, self.pg0)
+
+ #
+ # Attach ACL to ensure features are run on the pipe
+ #
+ rule_1 = ({'is_permit': 0,
+ 'is_ipv6': 0,
+ 'proto': 17,
+ 'srcport_or_icmptype_first': 1234,
+ 'srcport_or_icmptype_last': 1234,
+ 'src_ip_prefix_len': 32,
+ 'src_ip_addr': inet_pton(AF_INET, "1.1.1.1"),
+ 'dstport_or_icmpcode_first': 1234,
+ 'dstport_or_icmpcode_last': 1234,
+ 'dst_ip_prefix_len': 32,
+ 'dst_ip_addr': inet_pton(AF_INET, "1.1.1.2")})
+ acl = self.vapi.acl_add_replace(acl_index=4294967295,
+ r=[rule_1])
+
+ # Apply the ACL on the pipe on output
+ self.vapi.acl_interface_set_acl_list(pipes[0].east,
+ 0,
+ [acl.acl_index])
+ self.send_and_assert_no_replies(self.pg0, p * 65)
+ self.send_and_expect(self.pg1, p * 65, self.pg0)
+
+ # remove from output and apply on input
+ self.vapi.acl_interface_set_acl_list(pipes[0].east,
+ 0,
+ [])
+ self.vapi.acl_interface_set_acl_list(pipes[0].west,
+ 1,
+ [acl.acl_index])
+ self.send_and_assert_no_replies(self.pg0, p * 65)
+ self.send_and_expect(self.pg1, p * 65, self.pg0)
+ self.vapi.acl_interface_set_acl_list(pipes[0].west,
+ 0,
+ [])
+ self.send_and_expect(self.pg0, p * 65, self.pg1)
+ self.send_and_expect(self.pg1, p * 65, self.pg0)
+
+ #
+ # L3 routes in two separate tables so a pipe can be used to L3
+ # x-connect
+ #
+ tables = []
+ tables.append(VppIpTable(self, 1))
+ tables.append(VppIpTable(self, 2))
+
+ for t in tables:
+ t.add_vpp_config()
+
+ self.pg2.set_table_ip4(1)
+ self.pg2.config_ip4()
+ self.pg2.resolve_arp()
+ self.pg3.set_table_ip4(2)
+ self.pg3.config_ip4()
+ self.pg3.resolve_arp()
+
+ routes = []
+ routes.append(VppIpRoute(self, "1.1.1.1", 32,
+ [VppRoutePath(self.pg3.remote_ip4,
+ self.pg3.sw_if_index)],
+ table_id=2))
+ routes.append(VppIpRoute(self, "1.1.1.1", 32,
+ [VppRoutePath("0.0.0.0", pipes[1].east)],
+ table_id=1))
+ routes.append(VppIpRoute(self, "1.1.1.2", 32,
+ [VppRoutePath("0.0.0.0", pipes[1].west)],
+ table_id=2))
+ routes.append(VppIpRoute(self, "1.1.1.2", 32,
+ [VppRoutePath(self.pg2.remote_ip4,
+ self.pg2.sw_if_index)],
+ table_id=1))
+
+ for r in routes:
+ r.add_vpp_config()
+
+ p_east = (Ether(src=self.pg2.remote_mac,
+ dst=self.pg2.local_mac) /
+ IP(src="1.1.1.2",
+ dst="1.1.1.1") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+
+ # bind the pipe ends to the correct tables
+ self.vapi.sw_interface_set_table(pipes[1].west, 0, 2)
+ self.vapi.sw_interface_set_table(pipes[1].east, 0, 1)
+
+ # IP is not enabled on the pipes at this point
+ self.send_and_assert_no_replies(self.pg2, p_east * 65)
+
+ # IP enable the Pipes by making them unnumbered
+ pipes[0].set_unnumbered(self.pg2.sw_if_index)
+ pipes[1].set_unnumbered(self.pg3.sw_if_index)
+
+ self.send_and_expect(self.pg2, p_east * 65, self.pg3)
+
+ # and the return path
+ p_west = (Ether(src=self.pg3.remote_mac,
+ dst=self.pg3.local_mac) /
+ IP(src="1.1.1.1",
+ dst="1.1.1.2") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+ self.send_and_expect(self.pg3, p_west * 65, self.pg2)
+
+ #
+ # Use ACLs to test features run on the Pipes
+ #
+ self.vapi.acl_interface_set_acl_list(pipes[1].east,
+ 0,
+ [acl.acl_index])
+ self.send_and_assert_no_replies(self.pg2, p_east * 65)
+ self.send_and_expect(self.pg3, p_west * 65, self.pg2)
+
+ # remove from output and apply on input
+ self.vapi.acl_interface_set_acl_list(pipes[1].east,
+ 0,
+ [])
+ self.vapi.acl_interface_set_acl_list(pipes[1].west,
+ 1,
+ [acl.acl_index])
+ self.send_and_assert_no_replies(self.pg2, p_east * 65)
+ self.send_and_expect(self.pg3, p_west * 65, self.pg2)
+ self.vapi.acl_interface_set_acl_list(pipes[1].west,
+ 0,
+ [])
+ self.send_and_expect(self.pg2, p_east * 65, self.pg3)
+ self.send_and_expect(self.pg3, p_west * 65, self.pg2)
+
+ # cleanup (so the tables delete)
+ self.pg2.unconfig_ip4()
+ self.pg2.set_table_ip4(0)
+ self.pg3.unconfig_ip4()
+ self.pg3.set_table_ip4(0)
+ self.vapi.sw_interface_set_table(pipes[1].west, 0, 0)
+ self.vapi.sw_interface_set_table(pipes[1].east, 0, 0)
+
+
+if __name__ == '__main__':
+ unittest.main(testRunner=VppTestRunner)
diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py
index 32c8eebef01..2d4b44764f3 100644
--- a/test/vpp_papi_provider.py
+++ b/test/vpp_papi_provider.py
@@ -3650,3 +3650,15 @@ class VppPapiProvider(object):
def abf_itf_attach_dump(self):
return self.api(
self.papi.abf_itf_attach_dump, {})
+
+ def pipe_create(self, is_specified, user_instance):
+ return self.api(self.papi.pipe_create,
+ {'is_specified': is_specified,
+ 'user_instance': user_instance})
+
+ def pipe_delete(self, parent_sw_if_index):
+ return self.api(self.papi.pipe_delete,
+ {'parent_sw_if_index': parent_sw_if_index})
+
+ def pipe_dump(self):
+ return self.api(self.papi.pipe_dump, {})