SPAN:add l2 mirror
added span feature nodes for l2-input / l2-output
Change-Id: Ib6e0ce60d0811901b6edd70209e6a4c4a35cd8ff
Signed-off-by: Eyal Bari <ebari@cisco.com>
diff --git a/src/vat/api_format.c b/src/vat/api_format.c
index 40eca8c..932e162 100644
--- a/src/vat/api_format.c
+++ b/src/vat/api_format.c
@@ -18082,6 +18082,7 @@
u32 dst_sw_if_index = ~0;
u8 state = 3;
int ret;
+ u8 is_l2 = 0;
while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
{
@@ -18104,6 +18105,8 @@
state = 2;
else if (unformat (i, "both"))
state = 3;
+ else if (unformat (i, "l2"))
+ is_l2 = 1;
else
break;
}
@@ -18113,6 +18116,7 @@
mp->sw_if_index_from = htonl (src_sw_if_index);
mp->sw_if_index_to = htonl (dst_sw_if_index);
mp->state = state;
+ mp->is_l2 = is_l2;
S (mp);
W (ret);
@@ -20044,7 +20048,7 @@
_(ipfix_classify_stream_dump, "") \
_(ipfix_classify_table_add_del, "table <table-index> ip4|ip6 [tcp|udp]") \
_(ipfix_classify_table_dump, "") \
-_(sw_interface_span_enable_disable, "[src <intfc> | src_sw_if_index <id>] [disable | [[dst <intfc> | dst_sw_if_index <id>] [both|rx|tx]]]") \
+_(sw_interface_span_enable_disable, "[l2] [src <intfc> | src_sw_if_index <id>] [disable | [[dst <intfc> | dst_sw_if_index <id>] [both|rx|tx]]]") \
_(sw_interface_span_dump, "") \
_(get_next_index, "node-name <node-name> next-node-name <node-name>") \
_(pg_create_interface, "if_id <nn>") \
diff --git a/src/vnet/l2/l2_input.h b/src/vnet/l2/l2_input.h
index 244ef44..e6b3bc7 100644
--- a/src/vnet/l2/l2_input.h
+++ b/src/vnet/l2/l2_input.h
@@ -102,7 +102,7 @@
/* L2 input features */
-/* Mappings from feature ID to graph node name */
+/* Mappings from feature ID to graph node name in reverse order */
#define foreach_l2input_feat \
_(DROP, "feature-bitmap-drop") \
_(XCONNECT, "l2-output") \
@@ -116,7 +116,8 @@
_(VPATH, "vpath-input-l2") \
_(ACL, "l2-input-acl") \
_(POLICER_CLAS, "l2-policer-classify") \
- _(INPUT_CLASSIFY, "l2-input-classify")
+ _(INPUT_CLASSIFY, "l2-input-classify") \
+ _(SPAN, "span-l2-input")
/* Feature bitmap positions */
typedef enum
diff --git a/src/vnet/l2/l2_output.c b/src/vnet/l2/l2_output.c
index b3537a3..fbee590 100644
--- a/src/vnet/l2/l2_output.c
+++ b/src/vnet/l2/l2_output.c
@@ -195,8 +195,6 @@
}
-static vlib_node_registration_t l2output_node;
-
static_always_inline uword
l2output_node_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
vlib_frame_t * frame, int do_trace)
@@ -477,7 +475,7 @@
}
/* *INDENT-OFF* */
-VLIB_REGISTER_NODE (l2output_node,static) = {
+VLIB_REGISTER_NODE (l2output_node) = {
.function = l2output_node_fn,
.name = "l2-output",
.vector_size = sizeof (u32),
@@ -495,6 +493,8 @@
[L2OUTPUT_NEXT_BAD_INTF] = "l2-output-bad-intf",
},
};
+
+VLIB_NODE_FUNCTION_MULTIARCH (l2output_node, l2output_node_fn);
/* *INDENT-ON* */
@@ -601,11 +601,12 @@
[0] = "error-drop",
},
};
+
+VLIB_NODE_FUNCTION_MULTIARCH (l2output_bad_intf_node, l2output_bad_intf_node_fn);
/* *INDENT-ON* */
-
-VLIB_NODE_FUNCTION_MULTIARCH (l2output_node, l2output_node_fn)
- clib_error_t *l2output_init (vlib_main_t * vm)
+static clib_error_t *
+l2output_init (vlib_main_t * vm)
{
l2output_main_t *mp = &l2output_main;
diff --git a/src/vnet/l2/l2_output.h b/src/vnet/l2/l2_output.h
index 6da3e30..a54b8d6 100644
--- a/src/vnet/l2/l2_output.h
+++ b/src/vnet/l2/l2_output.h
@@ -77,12 +77,14 @@
l2output_main_t l2output_main;
+extern vlib_node_registration_t l2output_node;
+
/* L2 output features */
-/* Mappings from feature ID to graph node name */
+/* Mappings from feature ID to graph node name in reverse order */
#define foreach_l2output_feat \
_(OUTPUT, "interface-output") \
- _(SPAN, "feature-bitmap-drop") \
+ _(SPAN, "span-l2-output") \
_(CFM, "feature-bitmap-drop") \
_(QOS, "feature-bitmap-drop") \
_(ACL, "l2-output-acl") \
@@ -103,6 +105,8 @@
L2OUTPUT_N_FEAT,
} l2output_feat_t;
+STATIC_ASSERT (L2OUTPUT_N_FEAT <= 32, "too many l2 output features");
+
/* Feature bit masks */
typedef enum
{
diff --git a/src/vnet/span/node.c b/src/vnet/span/node.c
index 3a461b0..9d83d4e 100644
--- a/src/vnet/span/node.c
+++ b/src/vnet/span/node.c
@@ -18,6 +18,9 @@
#include <vppinfra/error.h>
#include <vnet/span/span.h>
+#include <vnet/l2/l2_input.h>
+#include <vnet/l2/l2_output.h>
+#include <vnet/l2/feat_bitmap.h>
#include <vppinfra/error.h>
#include <vppinfra/elog.h>
@@ -59,21 +62,19 @@
static_always_inline void
span_mirror (vlib_main_t * vm, vlib_node_runtime_t * node, u32 sw_if_index0,
- vlib_buffer_t * b0, vlib_frame_t ** mirror_frames, int is_rx)
+ vlib_buffer_t * b0, vlib_frame_t ** mirror_frames,
+ vlib_rx_or_tx_t rxtx, span_feat_t sf)
{
vlib_buffer_t *c0;
span_main_t *sm = &span_main;
vnet_main_t *vnm = &vnet_main;
- span_interface_t *si0 = 0;
u32 *to_mirror_next = 0;
u32 i;
- si0 = vec_elt_at_index (sm->interfaces, sw_if_index0);
+ span_interface_t *si0 = vec_elt_at_index (sm->interfaces, sw_if_index0);
+ span_mirror_t *sm0 = &si0->mirror_rxtx[sf][rxtx];
- if (is_rx != 0 && si0->num_rx_mirror_ports == 0)
- return;
-
- if (is_rx == 0 && si0->num_tx_mirror_ports == 0)
+ if (sm0->num_mirror_ports == 0)
return;
/* Don't do it again */
@@ -81,10 +82,15 @@
return;
/* *INDENT-OFF* */
- clib_bitmap_foreach (i, is_rx ? si0->rx_mirror_ports : si0->tx_mirror_ports, (
+ clib_bitmap_foreach (i, sm0->mirror_ports, (
{
if (mirror_frames[i] == 0)
- mirror_frames[i] = vnet_get_frame_to_sw_interface (vnm, i);
+ {
+ if (sf == SPAN_FEAT_L2)
+ mirror_frames[i] = vlib_get_frame_to_node (vnm->vlib_main, l2output_node.index);
+ else
+ mirror_frames[i] = vnet_get_frame_to_sw_interface (vnm, i);
+ }
to_mirror_next = vlib_frame_vector_args (mirror_frames[i]);
to_mirror_next += mirror_frames[i]->n_vectors;
/* This can fail */
@@ -93,6 +99,8 @@
{
vnet_buffer (c0)->sw_if_index[VLIB_TX] = i;
c0->flags |= VNET_BUFFER_F_SPAN_CLONE;
+ if (sf == SPAN_FEAT_L2)
+ vnet_buffer (c0)->l2.feature_bitmap = L2OUTPUT_FEAT_OUTPUT;
to_mirror_next[0] = vlib_get_buffer_index (vm, c0);
mirror_frames[i]->n_vectors++;
if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
@@ -108,7 +116,8 @@
static_always_inline uword
span_node_inline_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
- vlib_frame_t * frame, int is_rx)
+ vlib_frame_t * frame, vlib_rx_or_tx_t rxtx,
+ span_feat_t sf)
{
span_main_t *sm = &span_main;
vnet_main_t *vnm = &vnet_main;
@@ -117,7 +126,6 @@
u32 next_index;
u32 sw_if_index;
static __thread vlib_frame_t **mirror_frames = 0;
- vlib_rx_or_tx_t rxtx = is_rx ? VLIB_RX : VLIB_TX;
from = vlib_frame_vector_args (frame);
n_left_from = frame->n_vectors;
@@ -156,11 +164,33 @@
sw_if_index0 = vnet_buffer (b0)->sw_if_index[rxtx];
sw_if_index1 = vnet_buffer (b1)->sw_if_index[rxtx];
- span_mirror (vm, node, sw_if_index0, b0, mirror_frames, is_rx);
- span_mirror (vm, node, sw_if_index1, b1, mirror_frames, is_rx);
+ span_mirror (vm, node, sw_if_index0, b0, mirror_frames, rxtx, sf);
+ span_mirror (vm, node, sw_if_index1, b1, mirror_frames, rxtx, sf);
- vnet_feature_next (sw_if_index0, &next0, b0);
- vnet_feature_next (sw_if_index1, &next1, b1);
+ switch (sf)
+ {
+ case SPAN_FEAT_L2:
+ if (rxtx == VLIB_RX)
+ {
+ next0 = vnet_l2_feature_next (b0, sm->l2_input_next,
+ L2INPUT_FEAT_SPAN);
+ next1 = vnet_l2_feature_next (b1, sm->l2_input_next,
+ L2INPUT_FEAT_SPAN);
+ }
+ else
+ {
+ next0 = vnet_l2_feature_next (b0, sm->l2_output_next,
+ L2OUTPUT_FEAT_SPAN);
+ next1 = vnet_l2_feature_next (b1, sm->l2_output_next,
+ L2OUTPUT_FEAT_SPAN);
+ }
+ break;
+ case SPAN_FEAT_DEVICE:
+ default:
+ vnet_feature_next (sw_if_index0, &next0, b0);
+ vnet_feature_next (sw_if_index1, &next1, b1);
+ break;
+ }
/* verify speculative enqueue, maybe switch current next frame */
vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
@@ -184,9 +214,23 @@
b0 = vlib_get_buffer (vm, bi0);
sw_if_index0 = vnet_buffer (b0)->sw_if_index[rxtx];
- span_mirror (vm, node, sw_if_index0, b0, mirror_frames, is_rx);
+ span_mirror (vm, node, sw_if_index0, b0, mirror_frames, rxtx, sf);
- vnet_feature_next (sw_if_index0, &next0, b0);
+ switch (sf)
+ {
+ case SPAN_FEAT_L2:
+ if (rxtx == VLIB_RX)
+ next0 = vnet_l2_feature_next (b0, sm->l2_input_next,
+ L2INPUT_FEAT_SPAN);
+ else
+ next0 = vnet_l2_feature_next (b0, sm->l2_output_next,
+ L2OUTPUT_FEAT_SPAN);
+ break;
+ case SPAN_FEAT_DEVICE:
+ default:
+ vnet_feature_next (sw_if_index0, &next0, b0);
+ break;
+ }
/* verify speculative enqueue, maybe switch current next frame */
vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
@@ -199,11 +243,14 @@
for (sw_if_index = 0; sw_if_index < vec_len (mirror_frames); sw_if_index++)
{
- if (mirror_frames[sw_if_index] == 0)
+ vlib_frame_t *f = mirror_frames[sw_if_index];
+ if (f == 0)
continue;
- vnet_put_frame_to_sw_interface (vnm, sw_if_index,
- mirror_frames[sw_if_index]);
+ if (sf == SPAN_FEAT_L2)
+ vlib_put_frame_to_node (vnm->vlib_main, l2output_node.index, f);
+ else
+ vnet_put_frame_to_sw_interface (vnm, sw_if_index, f);
mirror_frames[sw_if_index] = 0;
}
vlib_node_increment_counter (vm, span_node.index, SPAN_ERROR_HITS,
@@ -213,62 +260,103 @@
}
static uword
-span_input_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
- vlib_frame_t * frame)
+span_device_input_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
{
- return span_node_inline_fn (vm, node, frame, 1);
+ return span_node_inline_fn (vm, node, frame, VLIB_RX, SPAN_FEAT_DEVICE);
}
static uword
-span_output_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
- vlib_frame_t * frame)
+span_device_output_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
{
- return span_node_inline_fn (vm, node, frame, 0);
+ return span_node_inline_fn (vm, node, frame, VLIB_TX, SPAN_FEAT_DEVICE);
}
+static uword
+span_l2_input_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return span_node_inline_fn (vm, node, frame, VLIB_RX, SPAN_FEAT_L2);
+}
+
+static uword
+span_l2_output_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return span_node_inline_fn (vm, node, frame, VLIB_TX, SPAN_FEAT_L2);
+}
+
+#define span_node_defs \
+ .vector_size = sizeof (u32), \
+ .format_trace = format_span_trace, \
+ .type = VLIB_NODE_TYPE_INTERNAL, \
+ .n_errors = ARRAY_LEN(span_error_strings), \
+ .error_strings = span_error_strings, \
+ .n_next_nodes = 0, \
+ .next_nodes = { \
+ [0] = "error-drop" \
+ }
+
/* *INDENT-OFF* */
VLIB_REGISTER_NODE (span_input_node) = {
- .function = span_input_node_fn,
+ span_node_defs,
+ .function = span_device_input_node_fn,
.name = "span-input",
- .vector_size = sizeof (u32),
- .format_trace = format_span_trace,
- .type = VLIB_NODE_TYPE_INTERNAL,
-
- .n_errors = ARRAY_LEN(span_error_strings),
- .error_strings = span_error_strings,
-
- .n_next_nodes = 0,
-
- /* edit / add dispositions here */
- .next_nodes = {
- [0] = "error-drop",
- },
};
-VLIB_NODE_FUNCTION_MULTIARCH (span_input_node, span_input_node_fn)
+VLIB_NODE_FUNCTION_MULTIARCH (span_input_node, span_device_input_node_fn)
VLIB_REGISTER_NODE (span_output_node) = {
- .function = span_output_node_fn,
+ span_node_defs,
+ .function = span_device_output_node_fn,
.name = "span-output",
- .vector_size = sizeof (u32),
- .format_trace = format_span_trace,
- .type = VLIB_NODE_TYPE_INTERNAL,
-
- .n_errors = ARRAY_LEN(span_error_strings),
- .error_strings = span_error_strings,
-
- .n_next_nodes = 0,
-
- /* edit / add dispositions here */
- .next_nodes = {
- [0] = "error-drop",
- },
};
-VLIB_NODE_FUNCTION_MULTIARCH (span_output_node, span_output_node_fn)
+VLIB_NODE_FUNCTION_MULTIARCH (span_output_node, span_device_output_node_fn)
+VLIB_REGISTER_NODE (span_l2_input_node) = {
+ span_node_defs,
+ .function = span_l2_input_node_fn,
+ .name = "span-l2-input",
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (span_l2_input_node, span_l2_input_node_fn)
+
+VLIB_REGISTER_NODE (span_l2_output_node) = {
+ span_node_defs,
+ .function = span_l2_output_node_fn,
+ .name = "span-l2-output",
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (span_l2_output_node, span_l2_output_node_fn)
+
+clib_error_t *span_init (vlib_main_t * vm)
+{
+ span_main_t *sm = &span_main;
+
+ sm->vlib_main = vm;
+ sm->vnet_main = vnet_get_main ();
+
+ /* Initialize the feature next-node indexes */
+ feat_bitmap_init_next_nodes (vm,
+ span_l2_input_node.index,
+ L2INPUT_N_FEAT,
+ l2input_get_feat_names (),
+ sm->l2_input_next);
+
+ feat_bitmap_init_next_nodes (vm,
+ span_l2_output_node.index,
+ L2OUTPUT_N_FEAT,
+ l2output_get_feat_names (),
+ sm->l2_output_next);
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (span_init);
/* *INDENT-ON* */
+#undef span_node_defs
/*
* fd.io coding-style-patch-verification: ON
*
diff --git a/src/vnet/span/span.api b/src/vnet/span/span.api
index 914fd8d..2a762ac 100644
--- a/src/vnet/span/span.api
+++ b/src/vnet/span/span.api
@@ -27,6 +27,7 @@
u32 sw_if_index_from;
u32 sw_if_index_to;
u8 state;
+ u8 is_l2;
};
/** \brief SPAN dump request
diff --git a/src/vnet/span/span.c b/src/vnet/span/span.c
index c5b43e3..6ecd178 100644
--- a/src/vnet/span/span.c
+++ b/src/vnet/span/span.c
@@ -16,60 +16,81 @@
#include <vlib/vlib.h>
#include <vppinfra/error.h>
#include <vnet/feature/feature.h>
+#include <vnet/l2/l2_input.h>
+#include <vnet/l2/l2_output.h>
#include <vnet/span/span.h>
+typedef enum
+{
+ SPAN_DISABLE = 0,
+ SPAN_RX = 1,
+ SPAN_TX = 2,
+ SPAN_BOTH = SPAN_RX | SPAN_TX
+} span_state_t;
+
+static_always_inline u32
+span_dst_set (span_mirror_t * sm, u32 dst_sw_if_index, int enable)
+{
+ sm->mirror_ports =
+ clib_bitmap_set (sm->mirror_ports, dst_sw_if_index, enable);
+ u32 last = sm->num_mirror_ports;
+ sm->num_mirror_ports = clib_bitmap_count_set_bits (sm->mirror_ports);
+ return last;
+}
+
int
span_add_delete_entry (vlib_main_t * vm,
- u32 src_sw_if_index, u32 dst_sw_if_index, u8 state)
+ u32 src_sw_if_index, u32 dst_sw_if_index, u8 state,
+ span_feat_t sf)
{
span_main_t *sm = &span_main;
- span_interface_t *si;
- u32 new_num_rx_mirror_ports, new_num_tx_mirror_ports;
- if (state > 3)
+ if (state > SPAN_BOTH)
return VNET_API_ERROR_UNIMPLEMENTED;
if ((src_sw_if_index == ~0) || (dst_sw_if_index == ~0 && state > 0)
|| (src_sw_if_index == dst_sw_if_index))
return VNET_API_ERROR_INVALID_INTERFACE;
- vnet_sw_interface_t *sw_if;
-
- sw_if = vnet_get_sw_interface (vnet_get_main (), src_sw_if_index);
- if (sw_if->type == VNET_SW_INTERFACE_TYPE_SUB)
- return VNET_API_ERROR_UNIMPLEMENTED;
-
vec_validate_aligned (sm->interfaces, src_sw_if_index,
CLIB_CACHE_LINE_BYTES);
- si = vec_elt_at_index (sm->interfaces, src_sw_if_index);
- si->rx_mirror_ports = clib_bitmap_set (si->rx_mirror_ports, dst_sw_if_index,
- (state & 1) != 0);
- si->tx_mirror_ports = clib_bitmap_set (si->tx_mirror_ports, dst_sw_if_index,
- (state & 2) != 0);
+ span_interface_t *si = vec_elt_at_index (sm->interfaces, src_sw_if_index);
- new_num_rx_mirror_ports = clib_bitmap_count_set_bits (si->rx_mirror_ports);
- new_num_tx_mirror_ports = clib_bitmap_count_set_bits (si->tx_mirror_ports);
+ int rx = ! !(state & SPAN_RX);
+ int tx = ! !(state & SPAN_TX);
- if (new_num_rx_mirror_ports == 1 && si->num_rx_mirror_ports == 0)
- vnet_feature_enable_disable ("device-input", "span-input",
- src_sw_if_index, 1, 0, 0);
+ span_mirror_t *rxm = &si->mirror_rxtx[sf][VLIB_RX];
+ span_mirror_t *txm = &si->mirror_rxtx[sf][VLIB_TX];
- if (new_num_rx_mirror_ports == 0 && si->num_rx_mirror_ports == 1)
- vnet_feature_enable_disable ("device-input", "span-input",
- src_sw_if_index, 0, 0, 0);
+ u32 last_rx_ports_count = span_dst_set (rxm, dst_sw_if_index, rx);
+ u32 last_tx_ports_count = span_dst_set (txm, dst_sw_if_index, tx);
- if (new_num_rx_mirror_ports == 1 && si->num_rx_mirror_ports == 0)
- vnet_feature_enable_disable ("interface-output", "span-output",
- src_sw_if_index, 1, 0, 0);
+ int enable_rx = last_rx_ports_count == 0 && rxm->num_mirror_ports == 1;
+ int disable_rx = last_rx_ports_count == 1 && rxm->num_mirror_ports == 0;
+ int enable_tx = last_tx_ports_count == 0 && txm->num_mirror_ports == 1;
+ int disable_tx = last_tx_ports_count == 1 && txm->num_mirror_ports == 0;
- if (new_num_rx_mirror_ports == 0 && si->num_rx_mirror_ports == 1)
- vnet_feature_enable_disable ("interface-output", "span-output",
- src_sw_if_index, 0, 0, 0);
-
- si->num_rx_mirror_ports = new_num_rx_mirror_ports;
- si->num_tx_mirror_ports = new_num_tx_mirror_ports;
+ switch (sf)
+ {
+ case SPAN_FEAT_DEVICE:
+ if (enable_rx || disable_rx)
+ vnet_feature_enable_disable ("device-input", "span-input",
+ src_sw_if_index, rx, 0, 0);
+ if (enable_tx || disable_tx)
+ vnet_feature_enable_disable ("interface-output", "span-output",
+ src_sw_if_index, tx, 0, 0);
+ break;
+ case SPAN_FEAT_L2:
+ if (enable_rx || disable_rx)
+ l2input_intf_bitmap_enable (src_sw_if_index, L2INPUT_FEAT_SPAN, rx);
+ if (enable_tx || disable_tx)
+ l2output_intf_bitmap_enable (src_sw_if_index, L2OUTPUT_FEAT_SPAN, tx);
+ break;
+ default:
+ return VNET_API_ERROR_UNIMPLEMENTED;
+ }
if (dst_sw_if_index > sm->max_sw_if_index)
sm->max_sw_if_index = dst_sw_if_index;
@@ -85,7 +106,8 @@
span_main_t *sm = &span_main;
u32 src_sw_if_index = ~0;
u32 dst_sw_if_index = ~0;
- u8 state = 3;
+ u8 state = SPAN_BOTH;
+ span_feat_t sf = SPAN_FEAT_DEVICE;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
@@ -96,19 +118,21 @@
sm->vnet_main, &dst_sw_if_index))
;
else if (unformat (input, "disable"))
- state = 0;
+ state = SPAN_DISABLE;
else if (unformat (input, "rx"))
- state = 1;
+ state = SPAN_RX;
else if (unformat (input, "tx"))
- state = 2;
+ state = SPAN_TX;
else if (unformat (input, "both"))
- state = 3;
+ state = SPAN_BOTH;
+ else if (unformat (input, "l2"))
+ sf = SPAN_FEAT_L2;
else
break;
}
int rv =
- span_add_delete_entry (vm, src_sw_if_index, dst_sw_if_index, state);
+ span_add_delete_entry (vm, src_sw_if_index, dst_sw_if_index, state, sf);
if (rv == VNET_API_ERROR_INVALID_INTERFACE)
return clib_error_return (0, "Invalid interface");
return 0;
@@ -117,7 +141,7 @@
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (set_interface_span_command, static) = {
.path = "set interface span",
- .short_help = "set interface span <if-name> [disable | destination <if-name> [both|rx|tx]]",
+ .short_help = "set interface span <if-name> [l2] {disable | destination <if-name> [both|rx|tx]}",
.function = set_interface_span_command_fn,
};
/* *INDENT-ON* */
@@ -136,31 +160,44 @@
/* *INDENT-OFF* */
vec_foreach (si, sm->interfaces)
- if (si->num_rx_mirror_ports || si->num_tx_mirror_ports)
+ {
+ span_mirror_t * drxm = &si->mirror_rxtx[SPAN_FEAT_DEVICE][VLIB_RX];
+ span_mirror_t * dtxm = &si->mirror_rxtx[SPAN_FEAT_DEVICE][VLIB_TX];
+
+ span_mirror_t * lrxm = &si->mirror_rxtx[SPAN_FEAT_L2][VLIB_RX];
+ span_mirror_t * ltxm = &si->mirror_rxtx[SPAN_FEAT_L2][VLIB_TX];
+
+ if (drxm->num_mirror_ports || dtxm->num_mirror_ports ||
+ lrxm->num_mirror_ports || ltxm->num_mirror_ports)
{
- clib_bitmap_t *b;
u32 i;
- b = clib_bitmap_dup_or (si->rx_mirror_ports, si->tx_mirror_ports);
+ clib_bitmap_t *d = clib_bitmap_dup_or (drxm->mirror_ports, dtxm->mirror_ports);
+ clib_bitmap_t *l = clib_bitmap_dup_or (lrxm->mirror_ports, ltxm->mirror_ports);
+ clib_bitmap_t *b = clib_bitmap_dup_or (d, l);
if (header)
{
- vlib_cli_output (vm, "%-40s %s", "Source interface",
- "Mirror interface (direction)");
+ vlib_cli_output (vm, "%-20s %-20s %6s %6s", "Source", "Destination",
+ "Device", "L2");
header = 0;
}
s = format (s, "%U", format_vnet_sw_if_index_name, vnm,
si - sm->interfaces);
clib_bitmap_foreach (i, b, (
{
- int state;
- state = (clib_bitmap_get (si->rx_mirror_ports, i) +
- clib_bitmap_get (si->tx_mirror_ports, i) * 2);
+ int device = (clib_bitmap_get (drxm->mirror_ports, i) +
+ clib_bitmap_get (dtxm->mirror_ports, i) * 2);
+ int l2 = (clib_bitmap_get (lrxm->mirror_ports, i) +
+ clib_bitmap_get (ltxm->mirror_ports, i) * 2);
- vlib_cli_output (vm, "%-40v %U (%s)", s,
+ vlib_cli_output (vm, "%-20v %-20U (%6s) (%6s)", s,
format_vnet_sw_if_index_name, vnm, i,
- states[state]);
+ states[device], states[l2]);
vec_reset_length (s);
}));
clib_bitmap_free (b);
+ clib_bitmap_free (l);
+ clib_bitmap_free (d);
+ }
}
/* *INDENT-ON* */
vec_free (s);
@@ -175,19 +212,6 @@
};
/* *INDENT-ON* */
-static clib_error_t *
-span_init (vlib_main_t * vm)
-{
- span_main_t *sm = &span_main;
-
- sm->vlib_main = vm;
- sm->vnet_main = vnet_get_main ();
-
- return 0;
-}
-
-VLIB_INIT_FUNCTION (span_init);
-
/*
* fd.io coding-style-patch-verification: ON
*
diff --git a/src/vnet/span/span.h b/src/vnet/span/span.h
index a98b010..10de827 100644
--- a/src/vnet/span/span.h
+++ b/src/vnet/span/span.h
@@ -18,17 +18,32 @@
#include <vnet/vnet.h>
#include <vnet/ip/ip.h>
+#include <vnet/l2/l2_output.h>
+
+typedef enum
+{
+ SPAN_FEAT_DEVICE,
+ SPAN_FEAT_L2,
+ SPAN_FEAT_N
+} span_feat_t;
typedef struct
{
- clib_bitmap_t *rx_mirror_ports;
- clib_bitmap_t *tx_mirror_ports;
- u32 num_rx_mirror_ports;
- u32 num_tx_mirror_ports;
+ clib_bitmap_t *mirror_ports;
+ u32 num_mirror_ports;
+} span_mirror_t;
+
+typedef struct
+{
+ span_mirror_t mirror_rxtx[SPAN_FEAT_N][VLIB_N_RX_TX];
} span_interface_t;
typedef struct
{
+ /* l2 feature Next nodes */
+ u32 l2_input_next[32];
+ u32 l2_output_next[32];
+
/* per-interface vector of span instances */
span_interface_t *interfaces;
@@ -52,7 +67,7 @@
int
span_add_delete_entry (vlib_main_t * vm, u32 src_sw_if_index,
- u32 dst_sw_if_index, u8 is_add);
+ u32 dst_sw_if_index, u8 state, span_feat_t sf);
/*
* fd.io coding-style-patch-verification: ON
*
diff --git a/src/vnet/span/span_api.c b/src/vnet/span/span_api.c
index b456566..69fa8e9 100644
--- a/src/vnet/span/span_api.c
+++ b/src/vnet/span/span_api.c
@@ -56,7 +56,8 @@
vlib_main_t *vm = vlib_get_main ();
rv = span_add_delete_entry (vm, ntohl (mp->sw_if_index_from),
- ntohl (mp->sw_if_index_to), mp->state);
+ ntohl (mp->sw_if_index_to), mp->state,
+ mp->is_l2 ? SPAN_FEAT_L2 : SPAN_FEAT_DEVICE);
REPLY_MACRO (VL_API_SW_INTERFACE_SPAN_ENABLE_DISABLE_REPLY);
}
@@ -76,11 +77,14 @@
/* *INDENT-OFF* */
vec_foreach (si, sm->interfaces)
- if (si->num_rx_mirror_ports || si->num_tx_mirror_ports)
+ {
+ span_mirror_t * drxm = &si->mirror_rxtx[SPAN_FEAT_DEVICE][VLIB_RX];
+ span_mirror_t * dtxm = &si->mirror_rxtx[SPAN_FEAT_DEVICE][VLIB_TX];
+ if (drxm->num_mirror_ports || dtxm->num_mirror_ports)
{
clib_bitmap_t *b;
u32 i;
- b = clib_bitmap_dup_or (si->rx_mirror_ports, si->tx_mirror_ports);
+ b = clib_bitmap_dup_or (drxm->mirror_ports, dtxm->mirror_ports);
clib_bitmap_foreach (i, b, (
{
rmp = vl_msg_api_alloc (sizeof (*rmp));
@@ -90,13 +94,14 @@
rmp->sw_if_index_from = htonl (si - sm->interfaces);
rmp->sw_if_index_to = htonl (i);
- rmp->state = (u8) (clib_bitmap_get (si->rx_mirror_ports, i) +
- clib_bitmap_get (si->tx_mirror_ports, i) * 2);
+ rmp->state = (u8) (clib_bitmap_get (drxm->mirror_ports, i) +
+ clib_bitmap_get (dtxm->mirror_ports, i) * 2);
vl_msg_api_send_shmem (q, (u8 *) & rmp);
}));
clib_bitmap_free (b);
}
+ }
/* *INDENT-ON* */
}
diff --git a/src/vpp/api/custom_dump.c b/src/vpp/api/custom_dump.c
index 7f3a58d..55a362a 100644
--- a/src/vpp/api/custom_dump.c
+++ b/src/vpp/api/custom_dump.c
@@ -2227,6 +2227,9 @@
s = format (s, "src_sw_if_index %u ", ntohl (mp->sw_if_index_from));
s = format (s, "dst_sw_if_index %u ", ntohl (mp->sw_if_index_to));
+ if (mp->is_l2)
+ s = format (s, "l2 ");
+
switch (mp->state)
{
case 0:
diff --git a/test/test_span.py b/test/test_span.py
index d8b6525..f2529e8 100644
--- a/test/test_span.py
+++ b/test/test_span.py
@@ -3,63 +3,121 @@
import unittest
from scapy.packet import Raw
-from scapy.layers.l2 import Ether
+from scapy.layers.l2 import Ether, Dot1Q, GRE
from scapy.layers.inet import IP, UDP
+from scapy.layers.vxlan import VXLAN
from framework import VppTestCase, VppTestRunner
from util import Host, ppp
+from vpp_sub_interface import VppDot1QSubint, VppDot1ADSubint
+from vpp_gre_interface import VppGreInterface, VppGre6Interface
+from vpp_papi_provider import L2_VTR_OP
+from collections import namedtuple
+
+Tag = namedtuple('Tag', ['dot1', 'vlan'])
+DOT1AD = 0x88A8
+DOT1Q = 0x8100
class TestSpan(VppTestCase):
""" SPAN Test Case """
- # Test variables
- hosts_nr = 10 # Number of hosts
- pkts_per_burst = 257 # Number of packets per burst
-
@classmethod
def setUpClass(cls):
super(TestSpan, cls).setUpClass()
-
- def setUp(self):
- super(TestSpan, self).setUp()
-
+ # Test variables
+ cls.hosts_nr = 10 # Number of hosts
+ cls.pkts_per_burst = 257 # Number of packets per burst
# create 3 pg interfaces
- self.create_pg_interfaces(range(3))
+ cls.create_pg_interfaces(range(3))
+ cls.bd_id = 55
+ cls.sub_if = VppDot1QSubint(cls, cls.pg0, 100)
+ cls.dst_sub_if = VppDot1QSubint(cls, cls.pg2, 300)
+ cls.dst_sub_if.set_vtr(L2_VTR_OP.L2_POP_1, tag=300)
# packet flows mapping pg0 -> pg1, pg2 -> pg3, etc.
- self.flows = dict()
- self.flows[self.pg0] = [self.pg1]
+ cls.flows = dict()
+ cls.flows[cls.pg0] = [cls.pg1]
# packet sizes
- self.pg_if_packet_sizes = [64, 512] # , 1518, 9018]
+ cls.pg_if_packet_sizes = [64, 512] # , 1518, 9018]
- self.interfaces = list(self.pg_interfaces)
+ cls.interfaces = list(cls.pg_interfaces)
# Create host MAC and IPv4 lists
- # self.MY_MACS = dict()
- # self.MY_IP4S = dict()
- self.create_host_lists(TestSpan.hosts_nr)
-
- # Create bi-directional cross-connects between pg0 and pg1
- self.vapi.sw_interface_set_l2_xconnect(
- self.pg0.sw_if_index, self.pg1.sw_if_index, enable=1)
- self.vapi.sw_interface_set_l2_xconnect(
- self.pg1.sw_if_index, self.pg0.sw_if_index, enable=1)
+ # cls.MY_MACS = dict()
+ # cls.MY_IP4S = dict()
+ cls.create_host_lists(cls.hosts_nr)
# setup all interfaces
- for i in self.interfaces:
+ for i in cls.interfaces:
i.admin_up()
i.config_ip4()
i.resolve_arp()
- # Enable SPAN on pg0 (mirrored to pg2)
- self.vapi.sw_interface_span_enable_disable(
- self.pg0.sw_if_index, self.pg2.sw_if_index)
+ cls.vxlan = cls.vapi.vxlan_add_del_tunnel(
+ src_addr=cls.pg2.local_ip4n,
+ dst_addr=cls.pg2.remote_ip4n,
+ vni=1111,
+ is_add=1)
+
+ def setUp(self):
+ super(TestSpan, self).setUp()
+ self.reset_packet_infos()
def tearDown(self):
super(TestSpan, self).tearDown()
+ if not self.vpp_dead:
+ self.logger.info(self.vapi.ppcli("show interface span"))
+ def xconnect(self, a, b, is_add=1):
+ self.vapi.sw_interface_set_l2_xconnect(a, b, enable=is_add)
+ self.vapi.sw_interface_set_l2_xconnect(b, a, enable=is_add)
+
+ def bridge(self, sw_if_index, is_add=1):
+ self.vapi.sw_interface_set_l2_bridge(
+ sw_if_index, bd_id=self.bd_id, enable=is_add)
+
+ def _remove_tag(self, packet, vlan, tag_type):
+ self.assertEqual(packet.type, tag_type)
+ payload = packet.payload
+ self.assertEqual(payload.vlan, vlan)
+ inner_type = payload.type
+ payload = payload.payload
+ packet.remove_payload()
+ packet.add_payload(payload)
+ packet.type = inner_type
+
+ def remove_tags(self, packet, tags):
+ for t in tags:
+ self._remove_tag(packet, t.vlan, t.dot1)
+ return packet
+
+ def decap_gre(self, pkt):
+ """
+ Decapsulate the original payload frame by removing GRE header
+ """
+ self.assertEqual(pkt[Ether].src, self.pg2.local_mac)
+ self.assertEqual(pkt[Ether].dst, self.pg2.remote_mac)
+
+ self.assertEqual(pkt[IP].src, self.pg2.local_ip4)
+ self.assertEqual(pkt[IP].dst, self.pg2.remote_ip4)
+
+ return pkt[GRE].payload
+
+ def decap_vxlan(self, pkt):
+ """
+ Decapsulate the original payload frame by removing VXLAN header
+ """
+ self.assertEqual(pkt[Ether].src, self.pg2.local_mac)
+ self.assertEqual(pkt[Ether].dst, self.pg2.remote_mac)
+
+ self.assertEqual(pkt[IP].src, self.pg2.local_ip4)
+ self.assertEqual(pkt[IP].dst, self.pg2.remote_ip4)
+
+ return pkt[VXLAN].payload
+
+ @classmethod
def create_host_lists(self, count):
""" Method to create required number of MAC and IPv4 addresses.
Create required number of host MAC addresses and distribute them among
@@ -81,9 +139,9 @@
"172.17.1%02x.%u" % (pg_if.sw_if_index, j))
hosts.append(host)
- def create_stream(self, src_if, packet_sizes):
+ def create_stream(self, src_if, packet_sizes, do_dot1=False):
pkts = []
- for i in range(0, TestSpan.pkts_per_burst):
+ for i in range(0, self.pkts_per_burst):
dst_if = self.flows[src_if][0]
pkt_info = self.create_packet_info(src_if, dst_if)
payload = self.info_to_payload(pkt_info)
@@ -91,6 +149,8 @@
IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4) /
UDP(sport=1234, dport=1234) /
Raw(payload))
+ if do_dot1:
+ p = self.sub_if.add_dot1_layer(p)
pkt_info.data = p.copy()
size = packet_sizes[(i / 2) % len(packet_sizes)]
self.extend_packet(p, size)
@@ -161,8 +221,8 @@
"Port %u: Packet expected from source %u didn't"
" arrive" % (dst_sw_if_index, i.sw_if_index))
- def test_span(self):
- """ SPAN test
+ def test_device_span(self):
+ """ SPAN device rx mirror test
Test scenario:
1. config
@@ -173,10 +233,17 @@
burst of packets per interface
"""
+ # Create bi-directional cross-connects between pg0 and pg1
+ self.xconnect(self.pg0.sw_if_index, self.pg1.sw_if_index)
# Create incoming packet streams for packet-generator interfaces
pkts = self.create_stream(self.pg0, self.pg_if_packet_sizes)
self.pg0.add_stream(pkts)
+ # Enable SPAN on pg0 (mirrored to pg2)
+ self.vapi.sw_interface_span_enable_disable(
+ self.pg0.sw_if_index, self.pg2.sw_if_index)
+
+ self.logger.info(self.vapi.ppcli("show interface span"))
# Enable packet capturing and start packet sending
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
@@ -190,6 +257,225 @@
self.pg1.get_capture(),
self.pg2.get_capture(pg2_expected))
+ # Disable SPAN on pg0 (mirrored to pg2)
+ self.vapi.sw_interface_span_enable_disable(
+ self.pg0.sw_if_index, self.pg2.sw_if_index, state=0)
+ self.xconnect(self.pg0.sw_if_index, self.pg1.sw_if_index, is_add=0)
+
+ def test_span_l2_rx(self):
+ """ SPAN l2 rx mirror test """
+
+ self.sub_if.admin_up()
+
+ self.bridge(self.pg2.sw_if_index)
+ # Create bi-directional cross-connects between pg0 and pg1
+ self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
+ # Create incoming packet streams for packet-generator interfaces
+ pkts = self.create_stream(
+ self.pg0, self.pg_if_packet_sizes, do_dot1=True)
+ self.pg0.add_stream(pkts)
+
+ # Enable SPAN on pg0 (mirrored to pg2)
+ self.vapi.sw_interface_span_enable_disable(
+ self.sub_if.sw_if_index, self.pg2.sw_if_index, is_l2=1)
+
+ self.logger.info(self.vapi.ppcli("show interface span"))
+ # Enable packet capturing and start packet sending
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ # Verify packets outgoing packet streams on mirrored interface (pg2)
+ self.logger.info("Verifying capture on interfaces %s and %s" %
+ (self.pg1.name, self.pg2.name))
+ pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
+ pg1_pkts = self.pg1.get_capture()
+ pg2_pkts = self.pg2.get_capture(pg2_expected)
+ self.verify_capture(
+ self.pg1,
+ pg1_pkts,
+ pg2_pkts)
+
+ self.bridge(self.pg2.sw_if_index, is_add=0)
+ # Disable SPAN on pg0 (mirrored to pg2)
+ self.vapi.sw_interface_span_enable_disable(
+ self.sub_if.sw_if_index, self.pg2.sw_if_index, state=0, is_l2=1)
+ self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
+
+ def test_span_l2_rx_dst_vxlan(self):
+ """ SPAN l2 rx mirror into vxlan test """
+
+ self.sub_if.admin_up()
+ self.vapi.sw_interface_set_flags(self.vxlan.sw_if_index,
+ admin_up_down=1)
+
+ self.bridge(self.vxlan.sw_if_index, is_add=1)
+ # Create bi-directional cross-connects between pg0 and pg1
+ self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
+ # Create incoming packet streams for packet-generator interfaces
+ pkts = self.create_stream(
+ self.pg0, self.pg_if_packet_sizes, do_dot1=True)
+ self.pg0.add_stream(pkts)
+
+ # Enable SPAN on pg0 sub if (mirrored to vxlan)
+ self.vapi.sw_interface_span_enable_disable(
+ self.sub_if.sw_if_index, self.vxlan.sw_if_index, is_l2=1)
+
+ self.logger.info(self.vapi.ppcli("show interface span"))
+ # Enable packet capturing and start packet sending
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ # Verify packets outgoing packet streams on mirrored interface (pg2)
+ self.logger.info("Verifying capture on interfaces %s and %s" %
+ (self.pg1.name, self.pg2.name))
+ pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
+ pg1_pkts = self.pg1.get_capture()
+ pg2_pkts = [self.decap_vxlan(p)
+ for p in self.pg2.get_capture(pg2_expected)]
+ self.verify_capture(
+ self.pg1,
+ pg1_pkts,
+ pg2_pkts)
+
+ self.bridge(self.vxlan.sw_if_index, is_add=0)
+ # Disable SPAN on pg0 sub if (mirrored to vxlan)
+ self.vapi.sw_interface_span_enable_disable(
+ self.sub_if.sw_if_index, self.vxlan.sw_if_index, state=0, is_l2=1)
+ self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
+
+ def test_span_l2_rx_dst_gre_subif_vtr(self):
+ """ SPAN l2 rx mirror into gre-subif+vtr """
+
+ self.sub_if.admin_up()
+
+ gre_if = VppGreInterface(self, self.pg2.local_ip4,
+ self.pg2.remote_ip4,
+ is_teb=1)
+
+ gre_if.add_vpp_config()
+ gre_if.admin_up()
+
+ gre_sub_if = VppDot1QSubint(self, gre_if, 500)
+ gre_sub_if.set_vtr(L2_VTR_OP.L2_POP_1, tag=500)
+ gre_sub_if.admin_up()
+
+ self.bridge(gre_sub_if.sw_if_index)
+ # Create bi-directional cross-connects between pg0 and pg1
+ self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=1)
+
+ # Create incoming packet streams for packet-generator interfaces
+ pkts = self.create_stream(
+ self.pg0, self.pg_if_packet_sizes, do_dot1=True)
+ self.pg0.add_stream(pkts)
+
+ self.vapi.sw_interface_span_enable_disable(
+ self.sub_if.sw_if_index, gre_sub_if.sw_if_index, is_l2=1)
+
+ # Enable packet capturing and start packet sending
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ # Verify packets outgoing packet streams on mirrored interface (pg2)
+ self.logger.info("Verifying capture on interfaces %s and %s" %
+ (self.pg1.name, self.pg2.name))
+ pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
+ pg1_pkts = self.pg1.get_capture()
+ pg2_pkts = self.pg2.get_capture(pg2_expected)
+ pg2_decaped = [self.remove_tags(self.decap_gre(
+ p), [Tag(dot1=DOT1Q, vlan=500)]) for p in pg2_pkts]
+ self.verify_capture(
+ self.pg1,
+ pg1_pkts,
+ pg2_decaped)
+
+ self.bridge(gre_sub_if.sw_if_index, is_add=0)
+ # Disable SPAN on pg0 sub if
+ self.vapi.sw_interface_span_enable_disable(
+ self.sub_if.sw_if_index, gre_sub_if.sw_if_index, state=0,
+ is_l2=1)
+ gre_if.remove_vpp_config()
+ self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
+
+ def test_span_l2_rx_dst_vtr(self):
+ """ SPAN l2 rx mirror into subif+vtr """
+
+ self.sub_if.admin_up()
+ self.dst_sub_if.admin_up()
+
+ self.bridge(self.dst_sub_if.sw_if_index)
+ # Create bi-directional cross-connects between pg0 and pg1
+ self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=1)
+
+ # Create incoming packet streams for packet-generator interfaces
+ pkts = self.create_stream(
+ self.pg0, self.pg_if_packet_sizes, do_dot1=True)
+ self.pg0.add_stream(pkts)
+
+ self.vapi.sw_interface_span_enable_disable(
+ self.sub_if.sw_if_index, self.dst_sub_if.sw_if_index, is_l2=1)
+
+ # Enable packet capturing and start packet sending
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ # Verify packets outgoing packet streams on mirrored interface (pg2)
+ self.logger.info("Verifying capture on interfaces %s and %s" %
+ (self.pg1.name, self.pg2.name))
+ pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
+ pg1_pkts = self.pg1.get_capture()
+ pg2_pkts = self.pg2.get_capture(pg2_expected)
+ pg2_untagged = [self.remove_tags(p, [Tag(dot1=DOT1Q, vlan=300)])
+ for p in pg2_pkts]
+ self.verify_capture(
+ self.pg1,
+ pg1_pkts,
+ pg2_untagged)
+
+ self.bridge(self.dst_sub_if.sw_if_index, is_add=0)
+ # Disable SPAN on pg0 sub if (mirrored to vxlan)
+ self.vapi.sw_interface_span_enable_disable(
+ self.sub_if.sw_if_index, self.dst_sub_if.sw_if_index, state=0,
+ is_l2=1)
+ self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
+
+ def test_l2_tx_span(self):
+ """ SPAN l2 tx mirror test """
+
+ self.sub_if.admin_up()
+ self.bridge(self.pg2.sw_if_index)
+ # Create bi-directional cross-connects between pg0 and pg1
+ self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index)
+ # Create incoming packet streams for packet-generator interfaces
+ pkts = self.create_stream(
+ self.pg0, self.pg_if_packet_sizes, do_dot1=True)
+ self.pg0.add_stream(pkts)
+
+ # Enable SPAN on pg0 (mirrored to pg2)
+ self.vapi.sw_interface_span_enable_disable(
+ self.pg1.sw_if_index, self.pg2.sw_if_index, is_l2=1, state=2)
+
+ self.logger.info(self.vapi.ppcli("show interface span"))
+ # Enable packet capturing and start packet sending
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ # Verify packets outgoing packet streams on mirrored interface (pg2)
+ self.logger.info("Verifying capture on interfaces %s and %s" %
+ (self.pg1.name, self.pg2.name))
+ pg2_expected = self.get_packet_count_for_if_idx(self.pg1.sw_if_index)
+ pg1_pkts = self.pg1.get_capture()
+ pg2_pkts = self.pg2.get_capture(pg2_expected)
+ self.verify_capture(
+ self.pg1,
+ pg1_pkts,
+ pg2_pkts)
+
+ self.bridge(self.pg2.sw_if_index, is_add=0)
+ # Disable SPAN on pg0 (mirrored to pg2)
+ self.vapi.sw_interface_span_enable_disable(
+ self.pg1.sw_if_index, self.pg2.sw_if_index, state=0, is_l2=1)
+ self.xconnect(self.sub_if.sw_if_index, self.pg1.sw_if_index, is_add=0)
+
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)
diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py
index 11e16e4..204d9e3 100644
--- a/test/vpp_papi_provider.py
+++ b/test/vpp_papi_provider.py
@@ -847,17 +847,20 @@
)
def sw_interface_span_enable_disable(
- self, sw_if_index_from, sw_if_index_to, state=1):
+ self, sw_if_index_from, sw_if_index_to, state=1, is_l2=0):
"""
:param sw_if_index_from:
:param sw_if_index_to:
:param state:
+ :param is_l2:
"""
return self.api(self.papi.sw_interface_span_enable_disable,
{'sw_if_index_from': sw_if_index_from,
'sw_if_index_to': sw_if_index_to,
- 'state': state})
+ 'state': state,
+ 'is_l2': is_l2,
+ })
def gre_tunnel_add_del(self,
src_address,