From: Francois Clad Date: Wed, 4 Jul 2018 13:35:29 +0000 (+0200) Subject: srv6-ad: Adding support for L2 traffic X-Git-Tag: v18.10-rc1~387 X-Git-Url: https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commitdiff_plain;h=429542476578e947399b2f09f9e8dd5aa1ac0951 srv6-ad: Adding support for L2 traffic Change-Id: I3c14555113c2b58f3be629600051beb7f6716cfe Signed-off-by: Francois Clad --- 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 #include +#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 #include -#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", }, }; @@ -277,6 +290,140 @@ VLIB_REGISTER_NODE (srv6_ad_localsid_node) = { /******************************* Rewriting 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 */ 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 """