aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/plugins/srv6-ad/ad.c272
-rw-r--r--src/plugins/srv6-ad/ad.h8
-rw-r--r--src/plugins/srv6-ad/node.c165
-rw-r--r--test/test_srv6_ad.py174
4 files changed, 518 insertions, 101 deletions
diff --git a/src/plugins/srv6-ad/ad.c b/src/plugins/srv6-ad/ad.c
index ae7a6c93f5b..64bf66d8482 100644
--- a/src/plugins/srv6-ad/ad.c
+++ b/src/plugins/srv6-ad/ad.c
@@ -24,6 +24,11 @@
#include <vpp/app/version.h>
#include <srv6-ad/ad.h>
+#define SID_CREATE_IFACE_FEATURE_ERROR -1
+#define SID_CREATE_INVALID_IFACE_TYPE -3
+#define SID_CREATE_INVALID_IFACE_INDEX -4
+#define SID_CREATE_INVALID_ADJ_INDEX -5
+
unsigned char function_name[] = "SRv6-AD-plugin";
unsigned char keyword_str[] = "End.AD";
unsigned char def_str[] =
@@ -45,16 +50,22 @@ srv6_ad_localsid_creation_fn (ip6_sr_localsid_t * localsid)
/* Retrieve the adjacency corresponding to the (OIF, next_hop) */
adj_index_t nh_adj_index = ADJ_INDEX_INVALID;
- if (ls_mem->ip_version == DA_IP4)
- nh_adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP4,
- VNET_LINK_IP4, &ls_mem->nh_addr,
- ls_mem->sw_if_index_out);
- else if (ls_mem->ip_version == DA_IP6)
- nh_adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP6,
- VNET_LINK_IP6, &ls_mem->nh_addr,
- ls_mem->sw_if_index_out);
- if (nh_adj_index == ADJ_INDEX_INVALID)
- return -5;
+ if (ls_mem->inner_type != AD_TYPE_L2)
+ {
+ if (ls_mem->inner_type == AD_TYPE_IP4)
+ nh_adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP4,
+ VNET_LINK_IP4, &ls_mem->nh_addr,
+ ls_mem->sw_if_index_out);
+ else if (ls_mem->inner_type == AD_TYPE_IP6)
+ nh_adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP6,
+ VNET_LINK_IP6, &ls_mem->nh_addr,
+ ls_mem->sw_if_index_out);
+ if (nh_adj_index == ADJ_INDEX_INVALID)
+ {
+ clib_mem_free (ls_mem);
+ return SID_CREATE_INVALID_ADJ_INDEX;
+ }
+ }
ls_mem->nh_adj = nh_adj_index;
@@ -64,52 +75,90 @@ srv6_ad_localsid_creation_fn (ip6_sr_localsid_t * localsid)
/* Sanitise the SW_IF_INDEX */
if (pool_is_free_index (sm->vnet_main->interface_main.sw_interfaces,
ls_mem->sw_if_index_in))
- return -3;
+ {
+ adj_unlock (ls_mem->nh_adj);
+ clib_mem_free (ls_mem);
+ return SID_CREATE_INVALID_IFACE_INDEX;
+ }
vnet_sw_interface_t *sw = vnet_get_sw_interface (sm->vnet_main,
ls_mem->sw_if_index_in);
if (sw->type != VNET_SW_INTERFACE_TYPE_HARDWARE)
- return -3;
+ {
+ adj_unlock (ls_mem->nh_adj);
+ clib_mem_free (ls_mem);
+ return SID_CREATE_INVALID_IFACE_TYPE;
+ }
- int ret = -1;
- if (ls_mem->ip_version == DA_IP4)
+ if (ls_mem->inner_type == AD_TYPE_L2)
{
- ret = vnet_feature_enable_disable ("ip4-unicast", "srv6-ad4-rewrite",
- ls_mem->sw_if_index_in, 1, 0, 0);
+ /* Enable End.AD2 rewrite node for this interface */
+ int ret =
+ vnet_feature_enable_disable ("device-input", "srv6-ad2-rewrite",
+ ls_mem->sw_if_index_in, 1, 0, 0);
if (ret != 0)
- return -1;
+ {
+ clib_mem_free (ls_mem);
+ return SID_CREATE_IFACE_FEATURE_ERROR;
+ }
+
+ /* Set interface in promiscuous mode */
+ vnet_main_t *vnm = vnet_get_main ();
+ ethernet_set_flags (vnm, ls_mem->sw_if_index_in,
+ ETHERNET_INTERFACE_FLAG_ACCEPT_ALL);
- /* FIB API calls - Recursive route through the BindingSID */
- if (ls_mem->sw_if_index_in < vec_len (sm->sw_iface_localsid4))
+ /* Associate local SID index to this interface (resize vector if needed) */
+ if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid2))
{
- sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = localsid_index;
+ vec_resize (sm->sw_iface_localsid2,
+ (pool_len (sm->vnet_main->interface_main.sw_interfaces)
+ - vec_len (sm->sw_iface_localsid2)));
}
- else
+ sm->sw_iface_localsid2[ls_mem->sw_if_index_in] = localsid_index;
+ }
+ else if (ls_mem->inner_type == AD_TYPE_IP4)
+ {
+ /* Enable End.AD4 rewrite node for this interface */
+ int ret =
+ vnet_feature_enable_disable ("ip4-unicast", "srv6-ad4-rewrite",
+ ls_mem->sw_if_index_in, 1, 0, 0);
+ if (ret != 0)
+ {
+ adj_unlock (ls_mem->nh_adj);
+ clib_mem_free (ls_mem);
+ return SID_CREATE_IFACE_FEATURE_ERROR;
+ }
+
+ /* Associate local SID index to this interface (resize vector if needed) */
+ if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid4))
{
vec_resize (sm->sw_iface_localsid4,
(pool_len (sm->vnet_main->interface_main.sw_interfaces)
- vec_len (sm->sw_iface_localsid4)));
- sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = localsid_index;
}
+ sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = localsid_index;
}
- else if (ls_mem->ip_version == DA_IP6)
+ else if (ls_mem->inner_type == AD_TYPE_IP6)
{
- ret = vnet_feature_enable_disable ("ip6-unicast", "srv6-ad6-rewrite",
- ls_mem->sw_if_index_in, 1, 0, 0);
+ /* Enable End.AD6 rewrite node for this interface */
+ int ret =
+ vnet_feature_enable_disable ("ip6-unicast", "srv6-ad6-rewrite",
+ ls_mem->sw_if_index_in, 1, 0, 0);
if (ret != 0)
- return -1;
-
- if (ls_mem->sw_if_index_in < vec_len (sm->sw_iface_localsid6))
{
- sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = localsid_index;
+ adj_unlock (ls_mem->nh_adj);
+ clib_mem_free (ls_mem);
+ return SID_CREATE_IFACE_FEATURE_ERROR;
}
- else
+
+ /* Associate local SID index to this interface (resize vector if needed) */
+ if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid6))
{
vec_resize (sm->sw_iface_localsid6,
(pool_len (sm->vnet_main->interface_main.sw_interfaces)
- vec_len (sm->sw_iface_localsid6)));
- sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = localsid_index;
}
+ sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = localsid_index;
}
ls_mem->rw_len = 0;
@@ -135,38 +184,56 @@ srv6_ad_localsid_removal_fn (ip6_sr_localsid_t * localsid)
srv6_ad_main_t *sm = &srv6_ad_main;
srv6_ad_localsid_t *ls_mem = localsid->plugin_mem;
- int ret = -1;
- if (ls_mem->ip_version == DA_IP4)
+ if (ls_mem->inner_type == AD_TYPE_L2)
{
- /* Remove hardware indirection (from sr_steering.c:137) */
- ret = vnet_feature_enable_disable ("ip4-unicast", "srv6-ad4-rewrite",
- ls_mem->sw_if_index_in, 0, 0, 0);
+ /* Disable End.AD2 rewrite node for this interface */
+ int ret =
+ vnet_feature_enable_disable ("device-input", "srv6-ad2-rewrite",
+ ls_mem->sw_if_index_in, 0, 0, 0);
if (ret != 0)
return -1;
- /* Remove local SID pointer from interface table (from sr_steering.c:139) */
+ /* Disable promiscuous mode on the interface */
+ vnet_main_t *vnm = vnet_get_main ();
+ ethernet_set_flags (vnm, ls_mem->sw_if_index_in, 0);
+
+ /* Remove local SID index from interface table */
+ sm->sw_iface_localsid2[ls_mem->sw_if_index_in] = ~(u32) 0;
+ }
+ else if (ls_mem->inner_type == AD_TYPE_IP4)
+ {
+ /* Disable End.AD4 rewrite node for this interface */
+ int ret =
+ vnet_feature_enable_disable ("ip4-unicast", "srv6-ad4-rewrite",
+ ls_mem->sw_if_index_in, 0, 0, 0);
+ if (ret != 0)
+ return -1;
+
+ /* Remove local SID pointer from interface table */
sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = ~(u32) 0;
}
- else if (ls_mem->ip_version == DA_IP6)
+ else if (ls_mem->inner_type == AD_TYPE_IP6)
{
- /* Remove hardware indirection (from sr_steering.c:137) */
- ret = vnet_feature_enable_disable ("ip6-unicast", "srv6-ad6-rewrite",
- ls_mem->sw_if_index_in, 0, 0, 0);
+ /* Disable End.AD6 rewrite node for this interface */
+ int ret =
+ vnet_feature_enable_disable ("ip6-unicast", "srv6-ad6-rewrite",
+ ls_mem->sw_if_index_in, 0, 0, 0);
if (ret != 0)
return -1;
- /* Remove local SID pointer from interface table (from sr_steering.c:139) */
+ /* Remove local SID pointer from interface table */
sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = ~(u32) 0;
}
- /* Unlock (OIF, NHOP) adjacency (from sr_localsid.c:103) */
+ /* Unlock (OIF, NHOP) adjacency */
adj_unlock (ls_mem->nh_adj);
/* Delete SID entry */
pool_put (sm->sids, pool_elt_at_index (sm->sids, ls_mem->index));
/* Clean up local SID memory */
+ vec_free (ls_mem->rewrite);
clib_mem_free (localsid->plugin_mem);
return 0;
@@ -186,20 +253,20 @@ format_srv6_ad_localsid (u8 * s, va_list * args)
vnet_main_t *vnm = vnet_get_main ();
srv6_ad_main_t *sm = &srv6_ad_main;
- if (ls_mem->ip_version == DA_IP4)
+ if (ls_mem->inner_type == AD_TYPE_IP4)
{
s =
- format (s, "Next-hop:\t%U\n", format_ip4_address,
+ format (s, "Next-hop:\t%U\n\t", format_ip4_address,
&ls_mem->nh_addr.ip4);
}
- else
+ else if (ls_mem->inner_type == AD_TYPE_IP6)
{
s =
- format (s, "Next-hop:\t%U\n", format_ip6_address,
+ format (s, "Next-hop:\t%U\n\t", format_ip6_address,
&ls_mem->nh_addr.ip6);
}
- s = format (s, "\tOutgoing iface:\t%U\n", format_vnet_sw_if_index_name, vnm,
+ s = format (s, "Outgoing iface:\t%U\n", format_vnet_sw_if_index_name, vnm,
ls_mem->sw_if_index_out);
s = format (s, "\tIncoming iface:\t%U\n", format_vnet_sw_if_index_name, vnm,
ls_mem->sw_if_index_in);
@@ -231,55 +298,75 @@ unformat_srv6_ad_localsid (unformat_input_t * input, va_list * args)
vnet_main_t *vnm = vnet_get_main ();
+ u8 inner_type = AD_TYPE_L2;
ip46_address_t nh_addr;
u32 sw_if_index_out;
u32 sw_if_index_in;
- if (unformat (input, "end.ad nh %U oif %U iif %U",
- unformat_ip4_address, &nh_addr.ip4,
- unformat_vnet_sw_interface, vnm, &sw_if_index_out,
- unformat_vnet_sw_interface, vnm, &sw_if_index_in))
+ u8 params = 0;
+#define PARAM_AD_NH (1 << 0)
+#define PARAM_AD_OIF (1 << 1)
+#define PARAM_AD_IIF (1 << 2)
+
+ if (!unformat (input, "end.ad"))
+ return 0;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
- /* Allocate a portion of memory */
- ls_mem = clib_mem_alloc_aligned_at_offset (sizeof *ls_mem, 0, 0, 1);
-
- /* Set to zero the memory */
- memset (ls_mem, 0, sizeof *ls_mem);
-
- /* Our brand-new car is ready */
- ls_mem->ip_version = DA_IP4;
- clib_memcpy (&ls_mem->nh_addr.ip4, &nh_addr.ip4,
- sizeof (ip4_address_t));
- ls_mem->sw_if_index_out = sw_if_index_out;
- ls_mem->sw_if_index_in = sw_if_index_in;
-
- /* Dont forget to add it to the localsid */
- *plugin_mem_p = ls_mem;
- return 1;
+ if (!(params & PARAM_AD_NH) && unformat (input, "nh %U",
+ unformat_ip4_address,
+ &nh_addr.ip4))
+ {
+ inner_type = AD_TYPE_IP4;
+ params |= PARAM_AD_NH;
+ }
+ if (!(params & PARAM_AD_NH) && unformat (input, "nh %U",
+ unformat_ip6_address,
+ &nh_addr.ip6))
+ {
+ inner_type = AD_TYPE_IP6;
+ params |= PARAM_AD_NH;
+ }
+ else if (!(params & PARAM_AD_OIF) && unformat (input, "oif %U",
+ unformat_vnet_sw_interface,
+ vnm, &sw_if_index_out))
+ {
+ params |= PARAM_AD_OIF;
+ }
+ else if (!(params & PARAM_AD_IIF) && unformat (input, "iif %U",
+ unformat_vnet_sw_interface,
+ vnm, &sw_if_index_in))
+ {
+ params |= PARAM_AD_IIF;
+ }
+ else
+ {
+ break;
+ }
}
- else if (unformat (input, "end.ad nh %U oif %U iif %U",
- unformat_ip6_address, &nh_addr.ip6,
- unformat_vnet_sw_interface, vnm, &sw_if_index_out,
- unformat_vnet_sw_interface, vnm, &sw_if_index_in))
+
+ /* Make sure that all parameters are supplied */
+ u8 params_chk = (PARAM_AD_OIF | PARAM_AD_IIF);
+ if ((params & params_chk) != params_chk)
{
- /* Allocate a portion of memory */
- ls_mem = clib_mem_alloc_aligned_at_offset (sizeof *ls_mem, 0, 0, 1);
-
- /* Set to zero the memory */
- memset (ls_mem, 0, sizeof *ls_mem);
-
- /* Our brand-new car is ready */
- ls_mem->ip_version = DA_IP6;
- clib_memcpy (&ls_mem->nh_addr.ip6, &nh_addr.ip6,
- sizeof (ip6_address_t));
- ls_mem->sw_if_index_out = sw_if_index_out;
- ls_mem->sw_if_index_in = sw_if_index_in;
-
- /* Dont forget to add it to the localsid */
- *plugin_mem_p = ls_mem;
- return 1;
+ return 0;
}
- return 0;
+
+ /* Allocate and initialize memory block for local SID parameters */
+ ls_mem = clib_mem_alloc_aligned_at_offset (sizeof *ls_mem, 0, 0, 1);
+ memset (ls_mem, 0, sizeof *ls_mem);
+ *plugin_mem_p = ls_mem;
+
+ /* Set local SID parameters */
+ ls_mem->inner_type = inner_type;
+ if (inner_type == AD_TYPE_IP4)
+ ls_mem->nh_addr.ip4 = nh_addr.ip4;
+ else if (inner_type == AD_TYPE_IP6)
+ ls_mem->nh_addr.ip6 = nh_addr.ip6;
+ ls_mem->sw_if_index_out = sw_if_index_out;
+ ls_mem->sw_if_index_in = sw_if_index_in;
+
+ return 1;
}
/*************************/
@@ -351,6 +438,13 @@ srv6_ad_init (vlib_main_t * vm)
}
/* *INDENT-OFF* */
+VNET_FEATURE_INIT (srv6_ad2_rewrite, static) =
+{
+ .arc_name = "device-input",
+ .node_name = "srv6-ad2-rewrite",
+ .runs_before = VNET_FEATURES ("ethernet-input"),
+};
+
VNET_FEATURE_INIT (srv6_ad4_rewrite, static) =
{
.arc_name = "ip4-unicast",
diff --git a/src/plugins/srv6-ad/ad.h b/src/plugins/srv6-ad/ad.h
index 945e8125235..ff00f84aac1 100644
--- a/src/plugins/srv6-ad/ad.h
+++ b/src/plugins/srv6-ad/ad.h
@@ -23,8 +23,9 @@
#include <vppinfra/error.h>
#include <vppinfra/elog.h>
-#define DA_IP4 4
-#define DA_IP6 6
+#define AD_TYPE_L2 2
+#define AD_TYPE_IP4 4
+#define AD_TYPE_IP6 6
/*
* This is the memory that will be stored per each localsid
@@ -35,7 +36,7 @@ typedef struct
ip46_address_t nh_addr; /**< Proxied device address */
u32 sw_if_index_out; /**< Outgoing iface to proxied dev. */
u32 nh_adj; /**< Adjacency index for out. iface */
- u8 ip_version;
+ u8 inner_type;
u32 sw_if_index_in; /**< Incoming iface from proxied dev. */
u32 rw_len; /**< Number of bits to be rewritten */
@@ -55,6 +56,7 @@ typedef struct
u32 srv6_localsid_behavior_id; /**< SRv6 LocalSID behavior number */
+ u32 *sw_iface_localsid2; /**< Retrieve local SID from iface */
u32 *sw_iface_localsid4; /**< Retrieve local SID from iface */
u32 *sw_iface_localsid6; /**< Retrieve local SID from iface */
diff --git a/src/plugins/srv6-ad/node.c b/src/plugins/srv6-ad/node.c
index e6afe42650a..776d283dbe7 100644
--- a/src/plugins/srv6-ad/node.c
+++ b/src/plugins/srv6-ad/node.c
@@ -92,6 +92,7 @@ typedef enum
SRV6_AD_LOCALSID_NEXT_ERROR,
SRV6_AD_LOCALSID_NEXT_REWRITE4,
SRV6_AD_LOCALSID_NEXT_REWRITE6,
+ SRV6_AD_LOCALSID_NEXT_INTERFACE,
SRV6_AD_LOCALSID_N_NEXT,
} srv6_ad_localsid_next_t;
@@ -149,9 +150,10 @@ end_ad_processing (vlib_buffer_t * b0,
next_ext_header = ip6_ext_next_header (next_ext_header);
}
- /* Make sure next header is IP */
- if (PREDICT_FALSE
- (next_hdr != IP_PROTOCOL_IPV6 && next_hdr != IP_PROTOCOL_IP_IN_IP))
+ /* Make sure next header is valid */
+ if (PREDICT_FALSE (next_hdr != IP_PROTOCOL_IPV6 &&
+ next_hdr != IP_PROTOCOL_IP_IN_IP &&
+ next_hdr != IP_PROTOCOL_IP6_NONXT))
{
return;
}
@@ -170,13 +172,23 @@ end_ad_processing (vlib_buffer_t * b0,
/* Remove IP header and extensions */
vlib_buffer_advance (b0, total_size);
- /* Set Xconnect adjacency to VNF */
- vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ls0_mem->nh_adj;
+ if (next_hdr == IP_PROTOCOL_IP6_NONXT)
+ {
+ /* Set output interface */
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = ls0_mem->sw_if_index_out;
+
+ /* Set next node to interface-output */
+ *next0 = SRV6_AD_LOCALSID_NEXT_INTERFACE;
+ }
+ else
+ {
+ /* Set Xconnect adjacency to VNF */
+ vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ls0_mem->nh_adj;
- if (ls0_mem->ip_version == DA_IP4)
- *next0 = SRV6_AD_LOCALSID_NEXT_REWRITE4;
- else if (ls0_mem->ip_version == DA_IP6)
- *next0 = SRV6_AD_LOCALSID_NEXT_REWRITE6;
+ /* Set next node to ip-rewrite */
+ *next0 = (next_hdr == IP_PROTOCOL_IPV6) ?
+ SRV6_AD_LOCALSID_NEXT_REWRITE6 : SRV6_AD_LOCALSID_NEXT_REWRITE4;
+ }
}
/**
@@ -269,6 +281,7 @@ VLIB_REGISTER_NODE (srv6_ad_localsid_node) = {
.next_nodes = {
[SRV6_AD_LOCALSID_NEXT_REWRITE4] = "ip4-rewrite",
[SRV6_AD_LOCALSID_NEXT_REWRITE6] = "ip6-rewrite",
+ [SRV6_AD_LOCALSID_NEXT_INTERFACE] = "interface-output",
[SRV6_AD_LOCALSID_NEXT_ERROR] = "error-drop",
},
};
@@ -281,6 +294,140 @@ VLIB_REGISTER_NODE (srv6_ad_localsid_node) = {
* @brief Graph node for applying a SR policy into an IPv6 packet. Encapsulation
*/
static uword
+srv6_ad2_rewrite_fn (vlib_main_t * vm,
+ vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ ip6_sr_main_t *srm = &sr_main;
+ srv6_ad_main_t *sm = &srv6_ad_main;
+ u32 n_left_from, next_index, *from, *to_next;
+ u32 cnt_packets = 0;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ /* TODO: Dual/quad loop */
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ u32 bi0;
+ vlib_buffer_t *b0;
+ ethernet_header_t *en0;
+ ip6_header_t *ip0 = 0;
+ ip6_sr_localsid_t *ls0;
+ srv6_ad_localsid_t *ls0_mem;
+ u32 next0 = SRV6_AD_REWRITE_NEXT_LOOKUP;
+
+ 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);
+ en0 = vlib_buffer_get_current (b0);
+ ls0 = pool_elt_at_index (srm->localsids,
+ sm->sw_iface_localsid2[vnet_buffer
+ (b0)->sw_if_index
+ [VLIB_RX]]);
+ ls0_mem = ls0->plugin_mem;
+
+ if (PREDICT_FALSE (ls0_mem == NULL || ls0_mem->rewrite == NULL))
+ {
+ next0 = SRV6_AD_REWRITE_NEXT_ERROR;
+ b0->error = node->errors[SRV6_AD_REWRITE_COUNTER_NO_RW];
+ }
+ else
+ {
+ ASSERT (VLIB_BUFFER_PRE_DATA_SIZE >=
+ (ls0_mem->rw_len + b0->current_data));
+
+ clib_memcpy (((u8 *) en0) - ls0_mem->rw_len,
+ ls0_mem->rewrite, ls0_mem->rw_len);
+ vlib_buffer_advance (b0, -(word) ls0_mem->rw_len);
+
+ ip0 = vlib_buffer_get_current (b0);
+
+ ip0->payload_length =
+ clib_host_to_net_u16 (b0->current_length -
+ sizeof (ip6_header_t));
+ }
+
+ if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE) &&
+ PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ srv6_ad_rewrite_trace_t *tr =
+ vlib_add_trace (vm, node, b0, sizeof *tr);
+ tr->error = 0;
+
+ if (next0 == SRV6_AD_REWRITE_NEXT_ERROR)
+ {
+ tr->error = 1;
+ }
+ else
+ {
+ clib_memcpy (tr->src.as_u8, ip0->src_address.as_u8,
+ sizeof tr->src.as_u8);
+ clib_memcpy (tr->dst.as_u8, ip0->dst_address.as_u8,
+ sizeof tr->dst.as_u8);
+ }
+ }
+
+ /* Increment per-SID AD rewrite counters */
+ vlib_increment_combined_counter (((next0 ==
+ SRV6_AD_LOCALSID_NEXT_ERROR) ?
+ &(sm->invalid_counters) :
+ &(sm->valid_counters)),
+ vm->thread_index, ls0_mem->index,
+ 1, vlib_buffer_length_in_chain (vm,
+ b0));
+
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+ n_left_to_next, bi0, next0);
+
+ cnt_packets++;
+ }
+
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ /* Update counters */
+ vlib_node_increment_counter (vm, srv6_ad4_rewrite_node.index,
+ SRV6_AD_REWRITE_COUNTER_PROCESSED,
+ cnt_packets);
+
+ return frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (srv6_ad2_rewrite_node) = {
+ .function = srv6_ad2_rewrite_fn,
+ .name = "srv6-ad2-rewrite",
+ .vector_size = sizeof (u32),
+ .format_trace = format_srv6_ad_rewrite_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_errors = SRV6_AD_REWRITE_N_COUNTERS,
+ .error_strings = srv6_ad_rewrite_counter_strings,
+ .n_next_nodes = SRV6_AD_REWRITE_N_NEXT,
+ .next_nodes = {
+ [SRV6_AD_REWRITE_NEXT_LOOKUP] = "ip6-lookup",
+ [SRV6_AD_REWRITE_NEXT_ERROR] = "error-drop",
+ },
+};
+/* *INDENT-ON* */
+
+
+/**
+ * @brief Graph node for applying a SR policy into an IPv6 packet. Encapsulation
+ */
+static uword
srv6_ad4_rewrite_fn (vlib_main_t * vm,
vlib_node_runtime_t * node, vlib_frame_t * frame)
{
diff --git a/test/test_srv6_ad.py b/test/test_srv6_ad.py
index 13979ba13a2..75d9d12da48 100644
--- a/test/test_srv6_ad.py
+++ b/test/test_srv6_ad.py
@@ -408,6 +408,136 @@ class TestSRv6(VppTestCase):
self.logger.debug("packet verification: SUCCESS")
+ def test_SRv6_End_AD_L2(self):
+ """ Test SRv6 End.AD behavior with L2 traffic.
+ """
+ self.src_addr = 'a0::'
+ self.sid_list = ['a1::', 'a2::a4', 'a3::']
+ self.test_sid_index = 1
+
+ # send traffic to one destination interface
+ # source and destination interfaces are IPv6 only
+ self.setup_interfaces(ipv6=[True, False])
+
+ # configure route to next segment
+ route = VppIpRoute(self, self.sid_list[self.test_sid_index + 1], 128,
+ [VppRoutePath(self.pg0.remote_ip6,
+ self.pg0.sw_if_index,
+ proto=DpoProto.DPO_PROTO_IP6)],
+ is_ip6=1)
+ route.add_vpp_config()
+
+ # configure SRv6 localSID behavior
+ cli_str = "sr localsid address " + \
+ self.sid_list[self.test_sid_index] + \
+ " behavior end.ad" + \
+ " oif " + self.pg1.name + \
+ " iif " + self.pg1.name
+ self.vapi.cli(cli_str)
+
+ # log the localsids
+ self.logger.debug(self.vapi.cli("show sr localsid"))
+
+ # send one packet per packet size
+ count = len(self.pg_packet_sizes)
+
+ # prepare L2 in SRv6 headers
+ packet_header1 = self.create_packet_header_IPv6_SRH_L2(
+ srcaddr=self.src_addr,
+ sidlist=self.sid_list[::-1],
+ segleft=len(self.sid_list) - self.test_sid_index - 1,
+ vlan=0)
+
+ # generate packets (pg0->pg1)
+ pkts1 = self.create_stream(self.pg0, self.pg1, packet_header1,
+ self.pg_packet_sizes, count)
+
+ # send packets and verify received packets
+ self.send_and_verify_pkts(self.pg0, pkts1, self.pg1,
+ self.compare_rx_tx_packet_End_AD_L2_out)
+
+ # log the localsid counters
+ self.logger.info(self.vapi.cli("show sr localsid"))
+
+ # prepare L2 header for returning packets
+ packet_header2 = self.create_packet_header_L2()
+
+ # generate returning packets (pg1->pg0)
+ pkts2 = self.create_stream(self.pg1, self.pg0, packet_header2,
+ self.pg_packet_sizes, count)
+
+ # send packets and verify received packets
+ self.send_and_verify_pkts(self.pg1, pkts2, self.pg0,
+ self.compare_rx_tx_packet_End_AD_L2_in)
+
+ # log the localsid counters
+ self.logger.info(self.vapi.cli("show sr localsid"))
+
+ # remove SRv6 localSIDs
+ cli_str = "sr localsid del address " + \
+ self.sid_list[self.test_sid_index]
+ self.vapi.cli(cli_str)
+
+ # cleanup interfaces
+ self.teardown_interfaces()
+
+ def compare_rx_tx_packet_End_AD_L2_out(self, tx_pkt, rx_pkt):
+ """ Compare input and output packet after passing End.AD with L2
+
+ :param tx_pkt: transmitted packet
+ :param rx_pkt: received packet
+ """
+
+ # get IPv4 header of rx'ed packet
+ rx_eth = rx_pkt.getlayer(Ether)
+
+ tx_ip = tx_pkt.getlayer(IPv6)
+ # we can't just get the 2nd Ether layer
+ # get the Raw content and dissect it as Ether
+ tx_eth1 = Ether(str(tx_pkt[Raw]))
+
+ # verify if rx'ed packet has no SRH
+ self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
+
+ # the whole rx_eth pkt should be equal to tx_eth1
+ self.assertEqual(rx_eth, tx_eth1)
+
+ self.logger.debug("packet verification: SUCCESS")
+
+ def compare_rx_tx_packet_End_AD_L2_in(self, tx_pkt, rx_pkt):
+ """ Compare input and output packet after passing End.AD
+
+ :param tx_pkt: transmitted packet
+ :param rx_pkt: received packet
+ """
+
+ ####
+ # get first (outer) IPv6 header of rx'ed packet
+ rx_ip = rx_pkt.getlayer(IPv6)
+ # received ip.src should be equal to SR Policy source
+ self.assertEqual(rx_ip.src, self.src_addr)
+ # received ip.dst should be equal to expected sidlist next segment
+ self.assertEqual(rx_ip.dst, self.sid_list[self.test_sid_index + 1])
+
+ # rx'ed packet should have SRH
+ self.assertTrue(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
+
+ # get SRH
+ rx_srh = rx_pkt.getlayer(IPv6ExtHdrSegmentRouting)
+ # rx'ed seglist should be equal to SID-list in reversed order
+ self.assertEqual(rx_srh.addresses, self.sid_list[::-1])
+ # segleft should be equal to previous segleft value minus 1
+ self.assertEqual(rx_srh.segleft,
+ len(self.sid_list) - self.test_sid_index - 2)
+ # lastentry should be equal to the SID-list length minus 1
+ self.assertEqual(rx_srh.lastentry, len(self.sid_list) - 1)
+
+ # the whole rx'ed pkt beyond SRH should be equal to tx'ed pkt
+ tx_ether = tx_pkt.getlayer(Ether)
+ self.assertEqual(Ether(str(rx_srh.payload)), tx_ether)
+
+ self.logger.debug("packet verification: SUCCESS")
+
def create_stream(self, src_if, dst_if, packet_header, packet_sizes,
count):
"""Create SRv6 input packet stream for defined interface.
@@ -547,6 +677,50 @@ class TestSRv6(VppTestCase):
UDP(sport=1234, dport=1234)
return p
+ def create_packet_header_L2(self, vlan=0):
+ """Create packet header: L2 header
+
+ :param vlan: if vlan!=0 then add 802.1q header
+ """
+ # Note: the dst addr ('00:55:44:33:22:11') is used in
+ # the compare function compare_rx_tx_packet_T_Encaps_L2
+ # to detect presence of L2 in SRH payload
+ p = Ether(src='00:11:22:33:44:55', dst='00:55:44:33:22:11')
+ etype = 0x8137 # IPX
+ if vlan:
+ # add 802.1q layer
+ p /= Dot1Q(vlan=vlan, type=etype)
+ else:
+ p.type = etype
+ return p
+
+ def create_packet_header_IPv6_SRH_L2(self, srcaddr, sidlist, segleft,
+ vlan=0):
+ """Create packet header: L2 encapsulated in SRv6:
+ IPv6 header with SRH, L2
+
+ :param int srcaddr: IPv6 source address
+ :param list sidlist: segment list of outer IPv6 SRH
+ :param int segleft: segments-left field of outer IPv6 SRH
+ :param vlan: L2 vlan; if vlan!=0 then add 802.1q header
+
+ IPv6 source address is set to srcaddr
+ IPv6 destination address is set to sidlist[segleft]
+ """
+ eth = Ether(src='00:11:22:33:44:55', dst='00:55:44:33:22:11')
+ etype = 0x8137 # IPX
+ if vlan:
+ # add 802.1q layer
+ eth /= Dot1Q(vlan=vlan, type=etype)
+ else:
+ eth.type = etype
+
+ p = IPv6(src=srcaddr, dst=sidlist[segleft]) / \
+ IPv6ExtHdrSegmentRouting(addresses=sidlist,
+ segleft=segleft, nh=59) / \
+ eth
+ return p
+
def get_payload_info(self, packet):
""" Extract the payload_info from the packet
"""