srv6-as: Adding support for L2 traffic
Change-Id: I72978c5957cb1acf154c9de7ad153092bac37785 Signed-off-by: Francois Clad <fclad@cisco.com>
This commit is contained in:
Francois Clad
committed by
Damjan Marion
parent
0928da903f
commit
b02f3b7b8a
@ -121,18 +121,21 @@ srv6_as_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)
|
||||
if (ls_mem->inner_type != AS_TYPE_L2)
|
||||
{
|
||||
free_ls_mem (ls_mem);
|
||||
return SID_CREATE_INVALID_ADJ_INDEX;
|
||||
if (ls_mem->inner_type == AS_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 == AS_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)
|
||||
{
|
||||
free_ls_mem (ls_mem);
|
||||
return SID_CREATE_INVALID_ADJ_INDEX;
|
||||
}
|
||||
}
|
||||
|
||||
ls_mem->nh_adj = nh_adj_index;
|
||||
@ -159,7 +162,37 @@ srv6_as_localsid_creation_fn (ip6_sr_localsid_t * localsid)
|
||||
return SID_CREATE_INVALID_IFACE_TYPE;
|
||||
}
|
||||
|
||||
if (ls_mem->ip_version == DA_IP4)
|
||||
if (ls_mem->inner_type == AS_TYPE_L2)
|
||||
{
|
||||
/* Enable End.AS2 rewrite node for this interface */
|
||||
int ret =
|
||||
vnet_feature_enable_disable ("device-input", "srv6-as2-rewrite",
|
||||
ls_mem->sw_if_index_in, 1, 0, 0);
|
||||
if (ret != 0)
|
||||
{
|
||||
free_ls_mem (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);
|
||||
|
||||
/* Prepare rewrite string */
|
||||
ls_mem->rewrite = prepare_rewrite (ls_mem->src_addr, ls_mem->sid_list,
|
||||
IP_PROTOCOL_IP6_NONXT);
|
||||
|
||||
/* Associate local SID index to this interface (resize vector if needed) */
|
||||
if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid2))
|
||||
{
|
||||
vec_resize (sm->sw_iface_localsid2,
|
||||
(pool_len (sm->vnet_main->interface_main.sw_interfaces)
|
||||
- vec_len (sm->sw_iface_localsid2)));
|
||||
}
|
||||
sm->sw_iface_localsid2[ls_mem->sw_if_index_in] = localsid_index;
|
||||
}
|
||||
else if (ls_mem->inner_type == AS_TYPE_IP4)
|
||||
{
|
||||
/* Enable End.AS4 rewrite node for this interface */
|
||||
int ret =
|
||||
@ -185,7 +218,7 @@ srv6_as_localsid_creation_fn (ip6_sr_localsid_t * localsid)
|
||||
}
|
||||
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 == AS_TYPE_IP6)
|
||||
{
|
||||
/* Enable End.AS6 rewrite node for this interface */
|
||||
int ret =
|
||||
@ -233,7 +266,23 @@ srv6_as_localsid_removal_fn (ip6_sr_localsid_t * localsid)
|
||||
srv6_as_main_t *sm = &srv6_as_main;
|
||||
srv6_as_localsid_t *ls_mem = localsid->plugin_mem;
|
||||
|
||||
if (ls_mem->ip_version == DA_IP4)
|
||||
if (ls_mem->inner_type == AS_TYPE_L2)
|
||||
{
|
||||
/* Disable End.AS2 rewrite node for this interface */
|
||||
int ret;
|
||||
ret = vnet_feature_enable_disable ("device-input", "srv6-as2-rewrite",
|
||||
ls_mem->sw_if_index_in, 0, 0, 0);
|
||||
if (ret != 0)
|
||||
return -1;
|
||||
|
||||
/* 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 == AS_TYPE_IP4)
|
||||
{
|
||||
/* Disable End.AS4 rewrite node for this interface */
|
||||
int ret;
|
||||
@ -245,7 +294,7 @@ srv6_as_localsid_removal_fn (ip6_sr_localsid_t * localsid)
|
||||
/* Remove local SID index 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 == AS_TYPE_IP6)
|
||||
{
|
||||
/* Disable End.AS6 rewrite node for this interface */
|
||||
int ret;
|
||||
@ -285,20 +334,20 @@ format_srv6_as_localsid (u8 * s, va_list * args)
|
||||
vnet_main_t *vnm = vnet_get_main ();
|
||||
srv6_as_main_t *sm = &srv6_as_main;
|
||||
|
||||
if (ls_mem->ip_version == DA_IP4)
|
||||
if (ls_mem->inner_type == AS_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 == AS_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);
|
||||
@ -342,7 +391,7 @@ unformat_srv6_as_localsid (unformat_input_t * input, va_list * args)
|
||||
|
||||
vnet_main_t *vnm = vnet_get_main ();
|
||||
|
||||
u8 ip_version = 0;
|
||||
u8 inner_type = AS_TYPE_L2;
|
||||
ip46_address_t nh_addr;
|
||||
u32 sw_if_index_out;
|
||||
u32 sw_if_index_in;
|
||||
@ -365,14 +414,14 @@ unformat_srv6_as_localsid (unformat_input_t * input, va_list * args)
|
||||
unformat_ip4_address,
|
||||
&nh_addr.ip4))
|
||||
{
|
||||
ip_version = DA_IP4;
|
||||
inner_type = AS_TYPE_IP4;
|
||||
params |= PARAM_AS_NH;
|
||||
}
|
||||
if (!(params & PARAM_AS_NH) && unformat (input, "nh %U",
|
||||
unformat_ip6_address,
|
||||
&nh_addr.ip6))
|
||||
{
|
||||
ip_version = DA_IP6;
|
||||
inner_type = AS_TYPE_IP6;
|
||||
params |= PARAM_AS_NH;
|
||||
}
|
||||
else if (!(params & PARAM_AS_OIF) && unformat (input, "oif %U",
|
||||
@ -404,7 +453,7 @@ unformat_srv6_as_localsid (unformat_input_t * input, va_list * args)
|
||||
}
|
||||
|
||||
/* Make sure that all parameters are supplied */
|
||||
u8 params_chk = (PARAM_AS_NH | PARAM_AS_OIF | PARAM_AS_IIF | PARAM_AS_SRC);
|
||||
u8 params_chk = (PARAM_AS_OIF | PARAM_AS_IIF | PARAM_AS_SRC);
|
||||
if ((params & params_chk) != params_chk || sid_list == NULL)
|
||||
{
|
||||
vec_free (sid_list);
|
||||
@ -417,10 +466,10 @@ unformat_srv6_as_localsid (unformat_input_t * input, va_list * args)
|
||||
*plugin_mem_p = ls_mem;
|
||||
|
||||
/* Set local SID parameters */
|
||||
ls_mem->ip_version = ip_version;
|
||||
if (ip_version == DA_IP4)
|
||||
ls_mem->inner_type = inner_type;
|
||||
if (inner_type == AS_TYPE_IP4)
|
||||
ls_mem->nh_addr.ip4 = nh_addr.ip4;
|
||||
else
|
||||
else if (inner_type == AS_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;
|
||||
@ -499,6 +548,13 @@ srv6_as_init (vlib_main_t * vm)
|
||||
}
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
VNET_FEATURE_INIT (srv6_as2_rewrite, static) =
|
||||
{
|
||||
.arc_name = "device-input",
|
||||
.node_name = "srv6-as2-rewrite",
|
||||
.runs_before = VNET_FEATURES ("ethernet-input"),
|
||||
};
|
||||
|
||||
VNET_FEATURE_INIT (srv6_as4_rewrite, static) =
|
||||
{
|
||||
.arc_name = "ip4-unicast",
|
||||
|
@ -23,8 +23,9 @@
|
||||
#include <vppinfra/error.h>
|
||||
#include <vppinfra/elog.h>
|
||||
|
||||
#define DA_IP4 4
|
||||
#define DA_IP6 6
|
||||
#define AS_TYPE_L2 2
|
||||
#define AS_TYPE_IP4 4
|
||||
#define AS_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. */
|
||||
u8 *rewrite; /**< Headers to be rewritten */
|
||||
@ -57,6 +58,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 */
|
||||
|
||||
|
@ -92,6 +92,7 @@ typedef enum
|
||||
SRV6_AS_LOCALSID_NEXT_ERROR,
|
||||
SRV6_AS_LOCALSID_NEXT_REWRITE4,
|
||||
SRV6_AS_LOCALSID_NEXT_REWRITE6,
|
||||
SRV6_AS_LOCALSID_NEXT_INTERFACE,
|
||||
SRV6_AS_LOCALSID_N_NEXT,
|
||||
} srv6_as_localsid_next_t;
|
||||
|
||||
@ -129,9 +130,10 @@ end_as_processing (vlib_buffer_t * b0,
|
||||
ext_hdr = ip6_ext_next_header (ext_hdr);
|
||||
}
|
||||
|
||||
/* Make sure next header is IP */
|
||||
/* Make sure next header is valid */
|
||||
if (PREDICT_FALSE (hdr_type != IP_PROTOCOL_IPV6 &&
|
||||
hdr_type != IP_PROTOCOL_IP_IN_IP))
|
||||
hdr_type != IP_PROTOCOL_IP_IN_IP &&
|
||||
hdr_type != IP_PROTOCOL_IP6_NONXT))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -139,13 +141,23 @@ end_as_processing (vlib_buffer_t * b0,
|
||||
/* Remove IP header and extensions */
|
||||
vlib_buffer_advance (b0, encap_len);
|
||||
|
||||
/* Set Xconnect adjacency to VNF */
|
||||
vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ls0_mem->nh_adj;
|
||||
if (hdr_type == IP_PROTOCOL_IP6_NONXT)
|
||||
{
|
||||
/* Set output interface */
|
||||
vnet_buffer (b0)->sw_if_index[VLIB_TX] = ls0_mem->sw_if_index_out;
|
||||
|
||||
if (ls0_mem->ip_version == DA_IP4)
|
||||
*next0 = SRV6_AS_LOCALSID_NEXT_REWRITE4;
|
||||
else if (ls0_mem->ip_version == DA_IP6)
|
||||
*next0 = SRV6_AS_LOCALSID_NEXT_REWRITE6;
|
||||
/* Set next node to interface-output */
|
||||
*next0 = SRV6_AS_LOCALSID_NEXT_INTERFACE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set Xconnect adjacency to VNF */
|
||||
vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ls0_mem->nh_adj;
|
||||
|
||||
/* Set next node to ip-rewrite */
|
||||
*next0 = (hdr_type == IP_PROTOCOL_IPV6) ?
|
||||
SRV6_AS_LOCALSID_NEXT_REWRITE6 : SRV6_AS_LOCALSID_NEXT_REWRITE4;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -236,6 +248,7 @@ VLIB_REGISTER_NODE (srv6_as_localsid_node) = {
|
||||
.next_nodes = {
|
||||
[SRV6_AS_LOCALSID_NEXT_REWRITE4] = "ip4-rewrite",
|
||||
[SRV6_AS_LOCALSID_NEXT_REWRITE6] = "ip6-rewrite",
|
||||
[SRV6_AS_LOCALSID_NEXT_INTERFACE] = "interface-output",
|
||||
[SRV6_AS_LOCALSID_NEXT_ERROR] = "error-drop",
|
||||
},
|
||||
};
|
||||
@ -244,6 +257,140 @@ VLIB_REGISTER_NODE (srv6_as_localsid_node) = {
|
||||
|
||||
/******************************* Rewriting node *******************************/
|
||||
|
||||
/**
|
||||
* @brief Graph node for applying a SR policy into an IPv6 packet. Encapsulation
|
||||
*/
|
||||
static uword
|
||||
srv6_as2_rewrite_fn (vlib_main_t * vm,
|
||||
vlib_node_runtime_t * node, vlib_frame_t * frame)
|
||||
{
|
||||
ip6_sr_main_t *srm = &sr_main;
|
||||
srv6_as_main_t *sm = &srv6_as_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_as_localsid_t *ls0_mem;
|
||||
u32 next0 = SRV6_AS_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_AS_REWRITE_NEXT_ERROR;
|
||||
b0->error = node->errors[SRV6_AS_REWRITE_COUNTER_NO_RW];
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT (VLIB_BUFFER_PRE_DATA_SIZE >=
|
||||
(vec_len (ls0_mem->rewrite) + b0->current_data));
|
||||
|
||||
clib_memcpy (((u8 *) en0) - vec_len (ls0_mem->rewrite),
|
||||
ls0_mem->rewrite, vec_len (ls0_mem->rewrite));
|
||||
vlib_buffer_advance (b0, -(word) vec_len (ls0_mem->rewrite));
|
||||
|
||||
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_as_rewrite_trace_t *tr =
|
||||
vlib_add_trace (vm, node, b0, sizeof *tr);
|
||||
tr->error = 0;
|
||||
|
||||
if (next0 == SRV6_AS_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 AS rewrite counters */
|
||||
vlib_increment_combined_counter (((next0 ==
|
||||
SRV6_AS_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_as4_rewrite_node.index,
|
||||
SRV6_AS_REWRITE_COUNTER_PROCESSED,
|
||||
cnt_packets);
|
||||
|
||||
return frame->n_vectors;
|
||||
}
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
VLIB_REGISTER_NODE (srv6_as2_rewrite_node) = {
|
||||
.function = srv6_as2_rewrite_fn,
|
||||
.name = "srv6-as2-rewrite",
|
||||
.vector_size = sizeof (u32),
|
||||
.format_trace = format_srv6_as_rewrite_trace,
|
||||
.type = VLIB_NODE_TYPE_INTERNAL,
|
||||
.n_errors = SRV6_AS_REWRITE_N_COUNTERS,
|
||||
.error_strings = srv6_as_rewrite_counter_strings,
|
||||
.n_next_nodes = SRV6_AS_REWRITE_N_NEXT,
|
||||
.next_nodes = {
|
||||
[SRV6_AS_REWRITE_NEXT_LOOKUP] = "ip6-lookup",
|
||||
[SRV6_AS_REWRITE_NEXT_ERROR] = "error-drop",
|
||||
},
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
|
||||
/**
|
||||
* @brief Graph node for applying a SR policy into an IPv6 packet. Encapsulation
|
||||
*/
|
||||
|
199
test/test_srv6_as.py
Normal file → Executable file
199
test/test_srv6_as.py
Normal file → Executable file
@ -162,6 +162,93 @@ class TestSRv6(VppTestCase):
|
||||
test_sid_index=0,
|
||||
rewrite_src_addr='a1::')
|
||||
|
||||
def test_SRv6_End_AS_L2_noSRH(self):
|
||||
""" Test SRv6 End.AS behavior with L2 traffic and no SRH rewrite.
|
||||
"""
|
||||
self.run_SRv6_End_AS_L2(
|
||||
sid_list=['a1::', 'a2::a6', 'a3::'],
|
||||
test_sid_index=1,
|
||||
rewrite_src_addr='a2::')
|
||||
|
||||
def test_SRv6_End_AS_L2_SRH(self):
|
||||
""" Test SRv6 End.AS behavior with L2 traffic and SRH rewrite.
|
||||
"""
|
||||
self.run_SRv6_End_AS_L2(
|
||||
sid_list=['a1::a6', 'a2::', 'a3::'],
|
||||
test_sid_index=0,
|
||||
rewrite_src_addr='a1::')
|
||||
|
||||
def run_SRv6_End_AS_L2(self, sid_list, test_sid_index, rewrite_src_addr):
|
||||
""" Run SRv6 End.AS test with L2 traffic.
|
||||
"""
|
||||
self.rewrite_src_addr = rewrite_src_addr
|
||||
self.rewrite_sid_list = sid_list[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, sid_list[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 " + sid_list[test_sid_index] \
|
||||
+ " behavior end.as" \
|
||||
+ " oif " + self.pg1.name \
|
||||
+ " iif " + self.pg1.name \
|
||||
+ " src " + self.rewrite_src_addr
|
||||
for s in self.rewrite_sid_list:
|
||||
cli_str += " next " + s
|
||||
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(
|
||||
sidlist=sid_list[::-1],
|
||||
segleft=len(sid_list) - 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_AS_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_AS_L2_in)
|
||||
|
||||
# log the localsid counters
|
||||
self.logger.info(self.vapi.cli("show sr localsid"))
|
||||
|
||||
# remove SRv6 localSIDs
|
||||
self.vapi.cli("sr localsid del address " + sid_list[test_sid_index])
|
||||
|
||||
# cleanup interfaces
|
||||
self.teardown_interfaces()
|
||||
|
||||
def run_SRv6_End_AS_IPv6(self, sid_list, test_sid_index, rewrite_src_addr):
|
||||
""" Run SRv6 End.AS test with IPv6 traffic.
|
||||
"""
|
||||
@ -407,6 +494,53 @@ class TestSRv6(VppTestCase):
|
||||
|
||||
self.logger.debug("packet verification: SUCCESS")
|
||||
|
||||
def compare_rx_tx_packet_End_AS_L2_in(self, tx_pkt, rx_pkt):
|
||||
""" Compare input and output packet after passing End.AS
|
||||
|
||||
: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)
|
||||
rx_srh = None
|
||||
|
||||
tx_ether = tx_pkt.getlayer(Ether)
|
||||
|
||||
# expected segment-list (SRH order)
|
||||
tx_seglist = self.rewrite_sid_list[::-1]
|
||||
|
||||
# received ip.src should be equal to SR Policy source
|
||||
self.assertEqual(rx_ip.src, self.rewrite_src_addr)
|
||||
# received ip.dst should be equal to expected sidlist[lastentry]
|
||||
self.assertEqual(rx_ip.dst, tx_seglist[-1])
|
||||
|
||||
if len(tx_seglist) > 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 seglist
|
||||
self.assertEqual(rx_srh.addresses, tx_seglist)
|
||||
# segleft should be equal to size seglist-1
|
||||
self.assertEqual(rx_srh.segleft, len(tx_seglist)-1)
|
||||
# segleft should be equal to lastentry
|
||||
self.assertEqual(rx_srh.segleft, rx_srh.lastentry)
|
||||
# nh should be "No Next Header" (59)
|
||||
self.assertEqual(rx_srh.nh, 59)
|
||||
# get payload
|
||||
payload = rx_srh.payload
|
||||
else:
|
||||
# rx'ed packet should NOT have SRH
|
||||
self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
|
||||
# get payload
|
||||
payload = rx_ip.payload
|
||||
|
||||
# the whole rx'ed pkt beyond SRH should be equal to tx'ed pkt
|
||||
self.assertEqual(Ether(str(payload)), tx_ether)
|
||||
|
||||
self.logger.debug("packet verification: SUCCESS")
|
||||
|
||||
def compare_rx_tx_packet_End_AS_IPv6_out(self, tx_pkt, rx_pkt):
|
||||
""" Compare input and output packet after passing End.AS with IPv6
|
||||
|
||||
@ -462,6 +596,29 @@ class TestSRv6(VppTestCase):
|
||||
|
||||
self.logger.debug("packet verification: SUCCESS")
|
||||
|
||||
def compare_rx_tx_packet_End_AS_L2_out(self, tx_pkt, rx_pkt):
|
||||
""" Compare input and output packet after passing End.AS 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 create_stream(self, src_if, dst_if, packet_header, packet_sizes,
|
||||
count):
|
||||
"""Create SRv6 input packet stream for defined interface.
|
||||
@ -604,6 +761,48 @@ 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, sidlist, segleft, vlan=0):
|
||||
"""Create packet header: L2 encapsulated in SRv6:
|
||||
IPv6 header with SRH, L2
|
||||
|
||||
: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
|
||||
|
||||
Outer IPv6 destination address is set to sidlist[segleft]
|
||||
IPv6 source address is 1234::1
|
||||
"""
|
||||
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='1234::1', 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
|
||||
"""
|
||||
|
Reference in New Issue
Block a user