From 95300d19152877dca8dfbd574dc6da50620125e8 Mon Sep 17 00:00:00 2001 From: Pavel Kotucek Date: Fri, 26 Aug 2016 16:11:36 +0200 Subject: [PATCH] VPP-355: add PBB (802.1ah) tag rewrite - new API/CLI to define pbb tag rewrite on interface - encapsulation/decapsulation of PBB tags - tracing of PBB header - PBB tag rewrite operations Change-Id: I538b3025a8b2e41cdeed9f10fea94bbcd28b5f5f Signed-off-by: Pavel Kotucek --- vnet/vnet/ethernet/format.c | 87 ++++++++++++++++------ vnet/vnet/ethernet/packet.h | 39 ++++++++++ vnet/vnet/ethernet/types.def | 1 + vnet/vnet/l2/l2_input_vtr.c | 99 +++++++++++++++++++------ vnet/vnet/l2/l2_output.c | 143 +++++++++++++++++++++++------------- vnet/vnet/l2/l2_output.h | 5 ++ vnet/vnet/l2/l2_vtr.c | 165 +++++++++++++++++++++++++++++++++++++++++- vnet/vnet/l2/l2_vtr.h | 67 +++++++++++++++++ vpp-api-test/vat/api_format.c | 99 ++++++++++++++++++++++++- vpp/vpp-api/api.c | 39 +++++++++- vpp/vpp-api/custom_dump.c | 26 ++++++- vpp/vpp-api/vpe.api | 35 +++++++++ 12 files changed, 697 insertions(+), 108 deletions(-) diff --git a/vnet/vnet/ethernet/format.c b/vnet/vnet/ethernet/format.c index 1b6712773e9..4edef5adbeb 100644 --- a/vnet/vnet/ethernet/format.c +++ b/vnet/vnet/ethernet/format.c @@ -87,10 +87,36 @@ format_ethernet_vlan_tci (u8 * s, va_list * va) return s; } +u8 * +format_ethernet_pbb (u8 * s, va_list * va) +{ + u32 b_tag = va_arg (*va, u32); + u32 i_tag = va_arg (*va, u32); + u32 vid = (b_tag & 0xfff); + u32 bdei = (b_tag >> 12) & 1; + u32 bpcp = (b_tag >> 13); + u32 sid = (i_tag & 0xffffff); + u8 ires = (i_tag >> 24) & 3; + u8 iuca = (i_tag >> 27) & 1; + u8 idei = (i_tag >> 28) & 1; + u8 ipcp = (i_tag >> 29); + + s = + format (s, "B_tag %04X (vid %d, dei %d, pcp %d), ", b_tag, vid, bdei, + bpcp); + s = + format (s, "I_tag %08X (sid %d, res %d, dei %d, pcp %d)", i_tag, sid, + ires, iuca, idei, ipcp); + + return s; +} + u8 * format_ethernet_header_with_length (u8 * s, va_list * args) { - ethernet_max_header_t *m = va_arg (*args, ethernet_max_header_t *); + ethernet_pbb_header_packed_t *ph = + va_arg (*args, ethernet_pbb_header_packed_t *); + ethernet_max_header_t *m = (ethernet_max_header_t *) ph; u32 max_header_bytes = va_arg (*args, u32); ethernet_main_t *em = ðernet_main; ethernet_header_t *e = &m->ethernet; @@ -100,12 +126,15 @@ format_ethernet_header_with_length (u8 * s, va_list * args) u32 n_vlan = 0, i, header_bytes; uword indent; - while ((type == ETHERNET_TYPE_VLAN || type == ETHERNET_TYPE_DOT1AD) - && n_vlan < ARRAY_LEN (m->vlan)) + while ((type == ETHERNET_TYPE_VLAN || type == ETHERNET_TYPE_DOT1AD + || type == ETHERNET_TYPE_DOT1AH) && n_vlan < ARRAY_LEN (m->vlan)) { vlan_type[n_vlan] = type; - v = m->vlan + n_vlan; - type = clib_net_to_host_u16 (v->type); + if (type != ETHERNET_TYPE_DOT1AH) + { + v = m->vlan + n_vlan; + type = clib_net_to_host_u16 (v->type); + } n_vlan++; } @@ -120,28 +149,38 @@ format_ethernet_header_with_length (u8 * s, va_list * args) format_ethernet_address, e->src_address, format_ethernet_address, e->dst_address); - for (i = 0; i < n_vlan; i++) + if (type != ETHERNET_TYPE_DOT1AH) { - u32 v = clib_net_to_host_u16 (m->vlan[i].priority_cfi_and_id); - if (*vlan_type == ETHERNET_TYPE_VLAN) - s = format (s, " 802.1q vlan %U", format_ethernet_vlan_tci, v); - else - s = format (s, " 802.1ad vlan %U", format_ethernet_vlan_tci, v); - } + for (i = 0; i < n_vlan; i++) + { + u32 v = clib_net_to_host_u16 (m->vlan[i].priority_cfi_and_id); + if (*vlan_type == ETHERNET_TYPE_VLAN) + s = format (s, " 802.1q vlan %U", format_ethernet_vlan_tci, v); + else + s = format (s, " 802.1ad vlan %U", format_ethernet_vlan_tci, v); + } - if (max_header_bytes != 0 && header_bytes < max_header_bytes) + if (max_header_bytes != 0 && header_bytes < max_header_bytes) + { + ethernet_type_info_t *ti; + vlib_node_t *node = 0; + + ti = ethernet_get_type_info (em, type); + if (ti && ti->node_index != ~0) + node = vlib_get_node (em->vlib_main, ti->node_index); + if (node && node->format_buffer) + s = format (s, "\n%U%U", + format_white_space, indent, + node->format_buffer, (void *) m + header_bytes, + max_header_bytes - header_bytes); + } + } + else { - ethernet_type_info_t *ti; - vlib_node_t *node = 0; - - ti = ethernet_get_type_info (em, type); - if (ti && ti->node_index != ~0) - node = vlib_get_node (em->vlib_main, ti->node_index); - if (node && node->format_buffer) - s = format (s, "\n%U%U", - format_white_space, indent, - node->format_buffer, (void *) m + header_bytes, - max_header_bytes - header_bytes); + s = format (s, "\n%UPBB header : %U", format_white_space, indent, + format_ethernet_pbb, + clib_net_to_host_u16 (ph->priority_dei_id), + clib_net_to_host_u32 (ph->priority_dei_uca_res_sid)); } return s; diff --git a/vnet/vnet/ethernet/packet.h b/vnet/vnet/ethernet/packet.h index b50f9303f23..964cf638101 100644 --- a/vnet/vnet/ethernet/packet.h +++ b/vnet/vnet/ethernet/packet.h @@ -102,6 +102,45 @@ typedef struct u16 priority_cfi_and_id; } ethernet_vlan_header_tv_t; +/* PBB header with B-TAG - backbone VLAN indicator and I-TAG - service encapsulation */ +typedef struct +{ + /* Backbone source/destination address. */ + u8 b_dst_address[6]; + u8 b_src_address[6]; + + /* B-tag */ + u16 b_type; + /* 3 bit priority, 1 bit DEI and 12 bit vlan id */ + u16 priority_dei_id; + + /* I-tag */ + u16 i_type; + /* 3 bit priority, 1 bit DEI, 1 bit UCA, 3 bit RES and 24 bit I_SID (service identifier) */ + u32 priority_dei_uca_res_sid; + +#define ETHERNET_N_PBB (1 << 24) +} ethernet_pbb_header_t; + +/* *INDENT-OFF* */ +typedef CLIB_PACKED (struct +{ + /* Backbone source/destination address. */ + u8 b_dst_address[6]; + u8 b_src_address[6]; + + /* B-tag */ + u16 b_type; + /* 3 bit priority, 1 bit DEI and 12 bit vlan id */ + u16 priority_dei_id; + + /* I-tag */ + u16 i_type; + /* 3 bit priority, 1 bit DEI, 1 bit UCA, 3 bit RES and 24 bit I_SID (service identifier) */ + u32 priority_dei_uca_res_sid; +}) ethernet_pbb_header_packed_t; +/* *INDENT-ON* */ + #endif /* included_ethernet_packet_h */ /* diff --git a/vnet/vnet/ethernet/types.def b/vnet/vnet/ethernet/types.def index 37c97f62cdb..643f3152a85 100644 --- a/vnet/vnet/ethernet/types.def +++ b/vnet/vnet/ethernet/types.def @@ -101,6 +101,7 @@ ethernet_type (0x88AE, BRDWALK) ethernet_type (0x88B7, 802_OUI_EXTENDED) ethernet_type (0x88c7, 802_11I_PRE_AUTHENTICATION) ethernet_type (0x88cc, 802_1_LLDP) +ethernet_type (0x88e7, DOT1AH) ethernet_type (0x894f, VPATH_3) ethernet_type (0x9000, LOOPBACK) ethernet_type (0x9021, RTNET_MAC) diff --git a/vnet/vnet/l2/l2_input_vtr.c b/vnet/vnet/l2/l2_input_vtr.c index 47b12115071..60a39631e87 100644 --- a/vnet/vnet/l2/l2_input_vtr.c +++ b/vnet/vnet/l2/l2_input_vtr.c @@ -178,24 +178,59 @@ l2_invtr_node_fn (vlib_main_t * vm, next1 = feat_bitmap_get_next_node_index (msm->feat_next_node_index, feature_bitmap1); - /* perform the tag rewrite on two packets */ - if (l2_vtr_process - (b0, - &(vec_elt_at_index - (l2output_main.configs, sw_if_index0)->input_vtr))) + l2_output_config_t *config0; + l2_output_config_t *config1; + config0 = vec_elt_at_index (l2output_main.configs, sw_if_index0); + config1 = vec_elt_at_index (l2output_main.configs, sw_if_index1); + + if (PREDICT_FALSE (config0->out_vtr_flag)) { - /* Drop packet */ - next0 = L2_INVTR_NEXT_DROP; - b0->error = node->errors[L2_INVTR_ERROR_DROP]; + if (config0->output_vtr.push_and_pop_bytes) + { + /* perform the tag rewrite on two packets */ + if (l2_vtr_process + (b0, + &(vec_elt_at_index + (l2output_main.configs, sw_if_index0)->input_vtr))) + { + /* Drop packet */ + next0 = L2_INVTR_NEXT_DROP; + b0->error = node->errors[L2_INVTR_ERROR_DROP]; + } + } + else if (config0->output_pbb_vtr.push_and_pop_bytes) + { + if (l2_pbb_process (b0, &(config0->input_pbb_vtr))) + { + /* Drop packet */ + next0 = L2_INVTR_NEXT_DROP; + b0->error = node->errors[L2_INVTR_ERROR_DROP]; + } + } } - if (l2_vtr_process - (b1, - &(vec_elt_at_index - (l2output_main.configs, sw_if_index1)->input_vtr))) + if (PREDICT_FALSE (config1->out_vtr_flag)) { - /* Drop packet */ - next1 = L2_INVTR_NEXT_DROP; - b1->error = node->errors[L2_INVTR_ERROR_DROP]; + if (config1->output_vtr.push_and_pop_bytes) + { + if (l2_vtr_process + (b1, + &(vec_elt_at_index + (l2output_main.configs, sw_if_index1)->input_vtr))) + { + /* Drop packet */ + next1 = L2_INVTR_NEXT_DROP; + b1->error = node->errors[L2_INVTR_ERROR_DROP]; + } + } + else if (config1->output_pbb_vtr.push_and_pop_bytes) + { + if (l2_pbb_process (b1, &(config1->input_pbb_vtr))) + { + /* Drop packet */ + next1 = L2_INVTR_NEXT_DROP; + b1->error = node->errors[L2_INVTR_ERROR_DROP]; + } + } } if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) @@ -262,15 +297,33 @@ l2_invtr_node_fn (vlib_main_t * vm, next0 = feat_bitmap_get_next_node_index (msm->feat_next_node_index, feature_bitmap0); - /* perform the tag rewrite on one packet */ - if (l2_vtr_process - (b0, - &(vec_elt_at_index - (l2output_main.configs, sw_if_index0)->input_vtr))) + l2_output_config_t *config0; + config0 = vec_elt_at_index (l2output_main.configs, sw_if_index0); + + if (PREDICT_FALSE (config0->out_vtr_flag)) { - /* Drop packet */ - next0 = L2_INVTR_NEXT_DROP; - b0->error = node->errors[L2_INVTR_ERROR_DROP]; + if (config0->output_vtr.push_and_pop_bytes) + { + /* perform the tag rewrite on one packet */ + if (l2_vtr_process + (b0, + &(vec_elt_at_index + (l2output_main.configs, sw_if_index0)->input_vtr))) + { + /* Drop packet */ + next0 = L2_INVTR_NEXT_DROP; + b0->error = node->errors[L2_INVTR_ERROR_DROP]; + } + } + else if (config0->output_pbb_vtr.push_and_pop_bytes) + { + if (l2_pbb_process (b0, &(config0->input_pbb_vtr))) + { + /* Drop packet */ + next0 = L2_INVTR_NEXT_DROP; + b0->error = node->errors[L2_INVTR_ERROR_DROP]; + } + } } if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) diff --git a/vnet/vnet/l2/l2_output.c b/vnet/vnet/l2/l2_output.c index f03796ebf76..b84501aa691 100644 --- a/vnet/vnet/l2/l2_output.c +++ b/vnet/vnet/l2/l2_output.c @@ -232,51 +232,77 @@ l2output_node_fn (vlib_main_t * vm, &msm->next_nodes, b1, sw_if_index1, feature_bitmap1, &next1); - /* - * Perform output vlan tag rewrite and the pre-vtr EFP filter check. - * The EFP Filter only needs to be run if there is an output VTR - * configured. The flag for the post-vtr EFP Filter node is used - * to trigger the pre-vtr check as well. - */ - - if (PREDICT_FALSE (config0->output_vtr.push_and_pop_bytes)) + if (PREDICT_FALSE (config0->out_vtr_flag)) { /* Perform pre-vtr EFP filter check if configured */ - u32 failed1 = (feature_bitmap0 & L2OUTPUT_FEAT_EFP_FILTER) && - (l2_efp_filter_process (b0, &(config0->input_vtr))); - u32 failed2 = l2_vtr_process (b0, &(config0->output_vtr)); - - if (PREDICT_FALSE (failed1 | failed2)) + if (config0->output_vtr.push_and_pop_bytes) { - next0 = L2OUTPUT_NEXT_DROP; - if (failed2) + /* + * Perform output vlan tag rewrite and the pre-vtr EFP filter check. + * The EFP Filter only needs to be run if there is an output VTR + * configured. The flag for the post-vtr EFP Filter node is used + * to trigger the pre-vtr check as well. + */ + u32 failed1 = (feature_bitmap0 & L2OUTPUT_FEAT_EFP_FILTER) + && (l2_efp_filter_process (b0, &(config0->input_vtr))); + u32 failed2 = l2_vtr_process (b0, &(config0->output_vtr)); + + if (PREDICT_FALSE (failed1 | failed2)) { - b0->error = node->errors[L2OUTPUT_ERROR_VTR_DROP]; + next0 = L2OUTPUT_NEXT_DROP; + if (failed2) + { + b0->error = node->errors[L2OUTPUT_ERROR_VTR_DROP]; + } + if (failed1) + { + b0->error = node->errors[L2OUTPUT_ERROR_EFP_DROP]; + } } - if (failed1) + } + // perform the PBB rewrite + else if (config0->output_pbb_vtr.push_and_pop_bytes) + { + u32 failed = + l2_pbb_process (b0, &(config0->output_pbb_vtr)); + if (PREDICT_FALSE (failed)) { - b0->error = node->errors[L2OUTPUT_ERROR_EFP_DROP]; + next0 = L2OUTPUT_NEXT_DROP; + b0->error = node->errors[L2OUTPUT_ERROR_VTR_DROP]; } } } - - if (PREDICT_FALSE (config1->output_vtr.push_and_pop_bytes)) + if (PREDICT_FALSE (config1->out_vtr_flag)) { /* Perform pre-vtr EFP filter check if configured */ - u32 failed1 = (feature_bitmap1 & L2OUTPUT_FEAT_EFP_FILTER) && - (l2_efp_filter_process (b1, &(config1->input_vtr))); - u32 failed2 = l2_vtr_process (b1, &(config1->output_vtr)); - - if (PREDICT_FALSE (failed1 | failed2)) + if (config1->output_vtr.push_and_pop_bytes) { - next1 = L2OUTPUT_NEXT_DROP; - if (failed2) + u32 failed1 = (feature_bitmap1 & L2OUTPUT_FEAT_EFP_FILTER) + && (l2_efp_filter_process (b1, &(config1->input_vtr))); + u32 failed2 = l2_vtr_process (b1, &(config1->output_vtr)); + + if (PREDICT_FALSE (failed1 | failed2)) { - b1->error = node->errors[L2OUTPUT_ERROR_VTR_DROP]; + next1 = L2OUTPUT_NEXT_DROP; + if (failed2) + { + b1->error = node->errors[L2OUTPUT_ERROR_VTR_DROP]; + } + if (failed1) + { + b1->error = node->errors[L2OUTPUT_ERROR_EFP_DROP]; + } } - if (failed1) + } + // perform the PBB rewrite + else if (config1->output_pbb_vtr.push_and_pop_bytes) + { + u32 failed = + l2_pbb_process (b0, &(config1->output_pbb_vtr)); + if (PREDICT_FALSE (failed)) { - b1->error = node->errors[L2OUTPUT_ERROR_EFP_DROP]; + next1 = L2OUTPUT_NEXT_DROP; + b1->error = node->errors[L2OUTPUT_ERROR_VTR_DROP]; } } } @@ -342,8 +368,8 @@ l2output_node_fn (vlib_main_t * vm, clib_memcpy (t->dst, h0->dst_address, 6); } - em->counters[node_counter_base_index + L2OUTPUT_ERROR_L2OUTPUT] += - 1; + em->counters[node_counter_base_index + + L2OUTPUT_ERROR_L2OUTPUT] += 1; /* Get config for the output interface */ config0 = vec_elt_at_index (msm->configs, sw_if_index0); @@ -364,34 +390,47 @@ l2output_node_fn (vlib_main_t * vm, &msm->next_nodes, b0, sw_if_index0, feature_bitmap0, &next0); - /* - * Perform output vlan tag rewrite and the pre-vtr EFP filter check. - * The EFP Filter only needs to be run if there is an output VTR - * configured. The flag for the post-vtr EFP Filter node is used - * to trigger the pre-vtr check as well. - */ - - if (config0->output_vtr.push_and_pop_bytes) + if (PREDICT_FALSE (config0->out_vtr_flag)) { - /* Perform pre-vtr EFP filter check if configured */ - u32 failed1 = (feature_bitmap0 & L2OUTPUT_FEAT_EFP_FILTER) && - (l2_efp_filter_process (b0, &(config0->input_vtr))); - u32 failed2 = l2_vtr_process (b0, &(config0->output_vtr)); - - if (PREDICT_FALSE (failed1 | failed2)) + /* + * Perform output vlan tag rewrite and the pre-vtr EFP filter check. + * The EFP Filter only needs to be run if there is an output VTR + * configured. The flag for the post-vtr EFP Filter node is used + * to trigger the pre-vtr check as well. + */ + + if (config0->output_vtr.push_and_pop_bytes) { - next0 = L2OUTPUT_NEXT_DROP; - if (failed2) + /* Perform pre-vtr EFP filter check if configured */ + u32 failed1 = (feature_bitmap0 & L2OUTPUT_FEAT_EFP_FILTER) + && (l2_efp_filter_process (b0, &(config0->input_vtr))); + u32 failed2 = l2_vtr_process (b0, &(config0->output_vtr)); + + if (PREDICT_FALSE (failed1 | failed2)) { - b0->error = node->errors[L2OUTPUT_ERROR_VTR_DROP]; + next0 = L2OUTPUT_NEXT_DROP; + if (failed2) + { + b0->error = node->errors[L2OUTPUT_ERROR_VTR_DROP]; + } + if (failed1) + { + b0->error = node->errors[L2OUTPUT_ERROR_EFP_DROP]; + } } - if (failed1) + } + // perform the PBB rewrite + else if (config0->output_pbb_vtr.push_and_pop_bytes) + { + u32 failed = + l2_pbb_process (b0, &(config0->output_pbb_vtr)); + if (PREDICT_FALSE (failed)) { - b0->error = node->errors[L2OUTPUT_ERROR_EFP_DROP]; + next0 = L2OUTPUT_NEXT_DROP; + b0->error = node->errors[L2OUTPUT_ERROR_VTR_DROP]; } } } - /* Perform the split horizon check */ if (PREDICT_FALSE (split_horizon_violation diff --git a/vnet/vnet/l2/l2_output.h b/vnet/vnet/l2/l2_output.h index 6a846f377ba..2e049148951 100644 --- a/vnet/vnet/l2/l2_output.h +++ b/vnet/vnet/l2/l2_output.h @@ -37,6 +37,8 @@ typedef struct */ vtr_config_t input_vtr; vtr_config_t output_vtr; + ptr_config_t input_pbb_vtr; + ptr_config_t output_pbb_vtr; /* some of these flags may get integrated into the feature bitmap */ u8 fwd_enable; @@ -45,6 +47,9 @@ typedef struct /* split horizon group */ u8 shg; + /* flag for output vtr operation */ + u8 out_vtr_flag; + } l2_output_config_t; diff --git a/vnet/vnet/l2/l2_vtr.c b/vnet/vnet/l2/l2_vtr.c index 9cb0a7e9da3..95a4f15700a 100644 --- a/vnet/vnet/l2/l2_vtr.c +++ b/vnet/vnet/l2/l2_vtr.c @@ -50,6 +50,83 @@ l2_vtr_init (vlib_main_t * vm) VLIB_INIT_FUNCTION (l2_vtr_init); +u32 +l2pbb_configure (vlib_main_t * vlib_main, + vnet_main_t * vnet_main, u32 sw_if_index, u32 vtr_op, + u8 * b_dmac, u8 * b_smac, + u16 b_vlanid, u32 i_sid, u16 vlan_outer_tag) +{ + u32 error = 0; + u32 enable = 0; + + l2_output_config_t *config = 0; + vnet_hw_interface_t *hi; + hi = vnet_get_sup_hw_interface (vnet_main, sw_if_index); + + if (!hi) + { + error = VNET_API_ERROR_INVALID_INTERFACE; + goto done; + } + + // Config for this interface should be already initialized + ptr_config_t *in_config; + ptr_config_t *out_config; + config = vec_elt_at_index (l2output_main.configs, sw_if_index); + in_config = &(config->input_pbb_vtr); + out_config = &(config->output_pbb_vtr); + + in_config->pop_bytes = 0; + in_config->push_bytes = 0; + out_config->pop_bytes = 0; + out_config->push_bytes = 0; + enable = (vtr_op != L2_VTR_DISABLED); + + if (!enable) + goto done; + + if (vtr_op == L2_VTR_POP_2) + { + in_config->pop_bytes = sizeof (ethernet_pbb_header_packed_t); + } + else if (vtr_op == L2_VTR_PUSH_2) + { + clib_memcpy (in_config->macs_tags.b_dst_address, b_dmac, + sizeof (in_config->macs_tags.b_dst_address)); + clib_memcpy (in_config->macs_tags.b_src_address, b_smac, + sizeof (in_config->macs_tags.b_src_address)); + in_config->macs_tags.b_type = + clib_net_to_host_u16 (ETHERNET_TYPE_DOT1AD); + in_config->macs_tags.priority_dei_id = + clib_net_to_host_u16 (b_vlanid & 0xFFF); + in_config->macs_tags.i_type = + clib_net_to_host_u16 (ETHERNET_TYPE_DOT1AH); + in_config->macs_tags.priority_dei_uca_res_sid = + clib_net_to_host_u32 (i_sid & 0xFFFFF); + in_config->push_bytes = sizeof (ethernet_pbb_header_packed_t); + } + else if (vtr_op == L2_VTR_TRANSLATE_2_2) + { + /* TODO after PoC */ + } + + /* + * Construct the output tag-rewrite config + * + * The push/pop values are always reversed + */ + out_config->raw_data = in_config->raw_data; + out_config->pop_bytes = in_config->push_bytes; + out_config->push_bytes = in_config->pop_bytes; + +done: + l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_VTR, enable); + if (config) + config->out_vtr_flag = (u8) enable; + + /* output vtr enable is checked explicitly in l2_output */ + return error; +} /** * Configure vtag tag rewrite on the given interface. @@ -64,6 +141,7 @@ l2vtr_configure (vlib_main_t * vlib_main, vnet_main_t * vnet_main, u32 sw_if_ind vnet_sw_interface_t *si; u32 hw_no_tags; u32 error = 0; + l2_output_config_t *config; vtr_config_t *in_config; vtr_config_t *out_config; u32 enable; @@ -80,10 +158,9 @@ l2vtr_configure (vlib_main_t * vlib_main, vnet_main_t * vnet_main, u32 sw_if_ind /* Init the config for this interface */ vec_validate (l2output_main.configs, sw_if_index); - in_config = - &(vec_elt_at_index (l2output_main.configs, sw_if_index)->input_vtr); - out_config = - &(vec_elt_at_index (l2output_main.configs, sw_if_index)->output_vtr); + config = vec_elt_at_index (l2output_main.configs, sw_if_index); + in_config = &(config->input_vtr); + out_config = &(config->output_vtr); in_config->raw_tags = 0; out_config->raw_tags = 0; @@ -257,6 +334,7 @@ l2vtr_configure (vlib_main_t * vlib_main, vnet_main_t * vnet_main, u32 sw_if_ind /* set the interface enable flags */ enable = (vtr_op != L2_VTR_DISABLED); + config->out_vtr_flag = (u8) enable; l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_VTR, enable); /* output vtr enable is checked explicitly in l2_output */ @@ -603,6 +681,85 @@ VLIB_CLI_COMMAND (int_l2_vtr_cli, static) = { }; /* *INDENT-ON* */ +/** + * Set subinterface pbb vtr enable/disable. + * The CLI format is: + * set interface l2 pbb-tag-rewrite [disable | pop | push | translate_pbb_stag dmac
smac
s_id [b_vlanid ]] + */ +static clib_error_t * +int_l2_pbb_vtr (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vnet_main_t *vnm = vnet_get_main (); + clib_error_t *error = 0; + u32 sw_if_index, tmp; + u32 vtr_op = L2_VTR_DISABLED; + u32 outer_tag = 0; + u8 dmac[6]; + u8 smac[6]; + u8 dmac_set = 0, smac_set = 0; + u16 b_vlanid = 0; + u32 s_id = ~0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat_user + (input, unformat_vnet_sw_interface, vnm, &sw_if_index)) + ; + else if (unformat (input, "disable")) + vtr_op = L2_VTR_DISABLED; + else if (vtr_op == L2_VTR_DISABLED && unformat (input, "pop")) + vtr_op = L2_VTR_POP_2; + else if (vtr_op == L2_VTR_DISABLED && unformat (input, "push")) + vtr_op = L2_VTR_PUSH_2; + else if (vtr_op == L2_VTR_DISABLED + && unformat (input, "translate_pbb_stag %d", &outer_tag)) + vtr_op = L2_VTR_TRANSLATE_2_1; + else if (unformat (input, "dmac %U", unformat_ethernet_address, dmac)) + dmac_set = 1; + else if (unformat (input, "smac %U", unformat_ethernet_address, smac)) + smac_set = 1; + else if (unformat (input, "b_vlanid %d", &tmp)) + b_vlanid = tmp; + else if (unformat (input, "s_id %d", &s_id)) + ; + else + { + error = clib_error_return (0, + "expecting [disable | pop | push | translate_pbb_stag \n" + "dmac
smac
s_id [b_vlanid ]]"); + goto done; + } + } + + if ((vtr_op == L2_VTR_PUSH_2 || vtr_op == L2_VTR_TRANSLATE_2_1) + && (!dmac_set || !smac_set || s_id == ~0)) + { + error = clib_error_return (0, + "expecting dmac
smac
s_id [b_vlanid ]"); + goto done; + } + + if (l2pbb_configure + (vm, vnm, sw_if_index, vtr_op, dmac, smac, b_vlanid, s_id, outer_tag)) + { + error = + clib_error_return (0, + "pbb tag rewrite is not compatible with interface"); + goto done; + } + +done: + return error; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (int_l2_pbb_vtr_cli, static) = { + .path = "set interface l2 pbb-tag-rewrite", + .short_help = "set interface l2 pbb-tag-rewrite [disable | pop | push | translate_pbb_stag dmac
smac
s_id [b_vlanid ]]", + .function = int_l2_pbb_vtr, +}; +/* *INDENT-ON* */ /* * fd.io coding-style-patch-verification: ON diff --git a/vnet/vnet/l2/l2_vtr.h b/vnet/vnet/l2/l2_vtr.h index 5e41c6d91ad..893b2272b04 100644 --- a/vnet/vnet/l2/l2_vtr.h +++ b/vnet/vnet/l2/l2_vtr.h @@ -171,6 +171,73 @@ l2_efp_filter_process (vlib_buffer_t * b0, vtr_config_t * in_config) return (packet_tags & tag_mask) != in_config->raw_tags; } +typedef struct +{ + union + { + ethernet_pbb_header_t macs_tags; + struct + { + u64 data1; + u64 data2; + u16 data3; + u32 data4; + } raw_data; + }; + union + { + struct + { + u8 push_bytes; /* number of bytes to push pbb tags */ + u8 pop_bytes; /* number of bytes to pop pbb tags */ + }; + u16 push_and_pop_bytes; /* if 0 then the feature is disabled */ + }; +} ptr_config_t; + +always_inline u32 +l2_pbb_process (vlib_buffer_t * b0, ptr_config_t * config) +{ + u8 *eth = vlib_buffer_get_current (b0); + + if (config->pop_bytes > 0) + { + ethernet_pbb_header_packed_t *ph = (ethernet_pbb_header_packed_t *) eth; + + // drop packet without PBB header or with wrong I-tag or B-tag + if (clib_net_to_host_u16 (ph->priority_dei_id) != + clib_net_to_host_u16 (config->macs_tags.priority_dei_id) + || clib_net_to_host_u32 (ph->priority_dei_uca_res_sid) != + clib_net_to_host_u32 (config->macs_tags.priority_dei_uca_res_sid)) + return 1; + + eth += config->pop_bytes; + } + + if (config->push_bytes > 0) + { + eth -= config->push_bytes; + // copy the B-DA (6B), B-SA (6B), B-TAG (4B), I-TAG (6B) + *((u64 *) eth) = config->raw_data.data1; + *((u64 *) (eth + 8)) = config->raw_data.data2; + *((u16 *) (eth + 16)) = config->raw_data.data3; + *((u32 *) (eth + 18)) = config->raw_data.data4; + } + + /* Update l2_len */ + vnet_buffer (b0)->l2.l2_len += + (word) config->push_bytes - (word) config->pop_bytes; + /* Update packet len */ + vlib_buffer_advance (b0, + (word) config->pop_bytes - (word) config->push_bytes); + + return 0; +} + +u32 l2pbb_configure (vlib_main_t * vlib_main, + vnet_main_t * vnet_main, u32 sw_if_index, u32 vtr_op, + u8 * b_dmac, u8 * b_smac, + u16 b_vlanid, u32 i_sid, u16 vlan_outer_tag); /** * Configure vtag tag rewrite on the given interface. diff --git a/vpp-api-test/vat/api_format.c b/vpp-api-test/vat/api_format.c index 12e8a9565aa..7dc7ceab792 100644 --- a/vpp-api-test/vat/api_format.c +++ b/vpp-api-test/vat/api_format.c @@ -3481,7 +3481,8 @@ _(pg_capture_reply) \ _(pg_enable_disable_reply) \ _(ip_source_and_port_range_check_add_del_reply) \ _(ip_source_and_port_range_check_interface_add_del_reply)\ -_(delete_subif_reply) +_(delete_subif_reply) \ +_(l2_interface_pbb_tag_rewrite_reply) #define _(n) \ static void vl_api_##n##_t_handler \ @@ -3714,7 +3715,8 @@ _(IP_SOURCE_AND_PORT_RANGE_CHECK_INTERFACE_ADD_DEL_REPLY, \ ip_source_and_port_range_check_interface_add_del_reply) \ _(IPSEC_GRE_ADD_DEL_TUNNEL_REPLY, ipsec_gre_add_del_tunnel_reply) \ _(IPSEC_GRE_TUNNEL_DETAILS, ipsec_gre_tunnel_details) \ -_(DELETE_SUBIF_REPLY, delete_subif_reply) +_(DELETE_SUBIF_REPLY, delete_subif_reply) \ +_(L2_INTERFACE_PBB_TAG_REWRITE_REPLY, l2_interface_pbb_tag_rewrite_reply) /* M: construct, but don't yet send a message */ @@ -15216,6 +15218,93 @@ api_delete_subif (vat_main_t * vam) W; } +#define foreach_pbb_vtr_op \ +_("disable", L2_VTR_DISABLED) \ +_("pop", L2_VTR_POP_2) \ +_("push", L2_VTR_PUSH_2) + +static int +api_l2_interface_pbb_tag_rewrite (vat_main_t * vam) +{ + unformat_input_t *i = vam->input; + vl_api_l2_interface_pbb_tag_rewrite_t *mp; + f64 timeout; + u32 sw_if_index = ~0, vtr_op = ~0; + u16 outer_tag = ~0; + u8 dmac[6], smac[6]; + u8 dmac_set = 0, smac_set = 0; + u16 vlanid = 0; + u32 sid = ~0; + u32 tmp; + + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index)) + ; + else if (unformat (i, "sw_if_index %d", &sw_if_index)) + ; + else if (unformat (i, "vtr_op %d", &vtr_op)) + ; +#define _(n,v) else if (unformat(i, n)) {vtr_op = v;} + foreach_pbb_vtr_op +#undef _ + else if (unformat (i, "translate_pbb_stag")) + { + if (unformat (i, "%d", &tmp)) + { + vtr_op = L2_VTR_TRANSLATE_2_1; + outer_tag = tmp; + } + else + { + errmsg + ("translate_pbb_stag operation requires outer tag definition\n"); + return -99; + } + } + else if (unformat (i, "dmac %U", unformat_ethernet_address, dmac)) + dmac_set++; + else if (unformat (i, "smac %U", unformat_ethernet_address, smac)) + smac_set++; + else if (unformat (i, "sid %d", &sid)) + ; + else if (unformat (i, "vlanid %d", &tmp)) + vlanid = tmp; + else + { + clib_warning ("parse error '%U'", format_unformat_error, i); + return -99; + } + } + + if ((sw_if_index == ~0) || (vtr_op == ~0)) + { + errmsg ("missing sw_if_index or vtr operation\n"); + return -99; + } + if (((vtr_op == L2_VTR_PUSH_2) || (vtr_op == L2_VTR_TRANSLATE_2_2)) + && ((dmac_set == 0) || (smac_set == 0) || (sid == ~0))) + { + errmsg + ("push and translate_qinq operations require dmac, smac, sid and optionally vlanid\n"); + return -99; + } + + M (L2_INTERFACE_PBB_TAG_REWRITE, l2_interface_pbb_tag_rewrite); + mp->sw_if_index = ntohl (sw_if_index); + mp->vtr_op = ntohl (vtr_op); + mp->outer_tag = ntohs (outer_tag); + clib_memcpy (mp->b_dmac, dmac, sizeof (dmac)); + clib_memcpy (mp->b_smac, smac, sizeof (smac)); + mp->b_vlanid = ntohs (vlanid); + mp->i_sid = ntohl (sid); + + S; + W; + /* NOTREACHED */ + return 0; +} + static int q_or_quit (vat_main_t * vam) { @@ -15808,7 +15897,11 @@ _(ip_source_and_port_range_check_interface_add_del, \ _(ipsec_gre_add_del_tunnel, \ "src dst local_sa remote_sa [del]") \ _(ipsec_gre_tunnel_dump, "[sw_if_index ]") \ -_(delete_subif,"sub_sw_if_index sub_if_id ") +_(delete_subif,"sub_sw_if_index sub_if_id ") \ +_(l2_interface_pbb_tag_rewrite, \ + " | sw_if_index \n" \ + "[disable | push | pop | translate_pbb_stag ] \n" \ + "dmac smac sid [vlanid ]") /* List of command functions, CLI names map directly to functions */ #define foreach_cli_function \ diff --git a/vpp/vpp-api/api.c b/vpp/vpp-api/api.c index 5698e369df7..2fdf5267d41 100644 --- a/vpp/vpp-api/api.c +++ b/vpp/vpp-api/api.c @@ -422,7 +422,8 @@ _(IP_SOURCE_AND_PORT_RANGE_CHECK_INTERFACE_ADD_DEL, \ ip_source_and_port_range_check_interface_add_del) \ _(IPSEC_GRE_ADD_DEL_TUNNEL, ipsec_gre_add_del_tunnel) \ _(IPSEC_GRE_TUNNEL_DUMP, ipsec_gre_tunnel_dump) \ -_(DELETE_SUBIF, delete_subif) +_(DELETE_SUBIF, delete_subif) \ +_(L2_INTERFACE_PBB_TAG_REWRITE, l2_interface_pbb_tag_rewrite) #define QUOTE_(x) #x #define QUOTE(x) QUOTE_(x) @@ -8467,6 +8468,42 @@ vl_api_delete_subif_t_handler (vl_api_delete_subif_t * mp) REPLY_MACRO (VL_API_DELETE_SUBIF_REPLY); } +static void + vl_api_l2_interface_pbb_tag_rewrite_t_handler + (vl_api_l2_interface_pbb_tag_rewrite_t * mp) +{ + vl_api_l2_interface_pbb_tag_rewrite_reply_t *rmp; + vnet_main_t *vnm = vnet_get_main (); + vlib_main_t *vm = vlib_get_main (); + u32 vtr_op; + int rv = 0; + + VALIDATE_SW_IF_INDEX (mp); + + vtr_op = ntohl (mp->vtr_op); + + switch (vtr_op) + { + case L2_VTR_DISABLED: + case L2_VTR_PUSH_2: + case L2_VTR_POP_2: + case L2_VTR_TRANSLATE_2_1: + break; + + default: + rv = VNET_API_ERROR_INVALID_VALUE; + goto bad_sw_if_index; + } + + rv = l2pbb_configure (vm, vnm, ntohl (mp->sw_if_index), vtr_op, + mp->b_dmac, mp->b_smac, ntohs (mp->b_vlanid), + ntohl (mp->i_sid), ntohs (mp->outer_tag)); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_L2_INTERFACE_PBB_TAG_REWRITE_REPLY); +} + #define BOUNCE_HANDLER(nn) \ static void vl_api_##nn##_t_handler ( \ vl_api_##nn##_t *mp) \ diff --git a/vpp/vpp-api/custom_dump.c b/vpp/vpp-api/custom_dump.c index dc0dfa1621b..4689776ac58 100644 --- a/vpp/vpp-api/custom_dump.c +++ b/vpp/vpp-api/custom_dump.c @@ -2748,6 +2748,29 @@ static void *vl_api_ipsec_gre_tunnel_dump_t_print FINISH; } +static void *vl_api_l2_interface_pbb_tag_rewrite_t_print + (vl_api_l2_interface_pbb_tag_rewrite_t * mp, void *handle) +{ + u8 *s; + u32 vtr_op = ntohl (mp->vtr_op); + + s = format (0, "SCRIPT: l2_interface_pbb_tag_rewrite "); + + s = format (s, "sw_if_index %d ", ntohl (mp->sw_if_index)); + s = format (s, "vtr_op %d ", vtr_op); + if (vtr_op != L2_VTR_DISABLED && vtr_op != L2_VTR_POP_2) + { + if (vtr_op == L2_VTR_TRANSLATE_2_2) + s = format (s, "%d ", ntohs (mp->outer_tag)); + s = format (s, "dmac %U ", format_ethernet_address, &mp->b_dmac); + s = format (s, "smac %U ", format_ethernet_address, &mp->b_smac); + s = format (s, "sid %d ", ntohl (mp->i_sid)); + s = format (s, "vlanid %d ", ntohs (mp->b_vlanid)); + } + + FINISH; +} + #define foreach_custom_print_no_arg_function \ _(lisp_eid_table_vni_dump) \ _(lisp_map_resolver_dump) \ @@ -2908,7 +2931,8 @@ _(LISP_LOCATOR_SET_DUMP, lisp_locator_set_dump) \ _(LISP_LOCATOR_DUMP, lisp_locator_dump) \ _(IPSEC_GRE_ADD_DEL_TUNNEL, ipsec_gre_add_del_tunnel) \ _(IPSEC_GRE_TUNNEL_DUMP, ipsec_gre_tunnel_dump) \ -_(DELETE_SUBIF, delete_subif) +_(DELETE_SUBIF, delete_subif) \ +_(L2_INTERFACE_PBB_TAG_REWRITE, l2_interface_pbb_tag_rewrite) void vl_msg_api_custom_dump_configure (api_main_t * am) { diff --git a/vpp/vpp-api/vpe.api b/vpp/vpp-api/vpe.api index d42eee877f2..6924aef9587 100644 --- a/vpp/vpp-api/vpe.api +++ b/vpp/vpp-api/vpe.api @@ -5119,3 +5119,38 @@ define sw_interface_set_dpdk_hqos_tctbl_reply { u32 context; i32 retval; }; + +/** \brief L2 interface pbb tag rewrite configure request + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param sw_if_index - interface the operation is applied to + @param vtr_op - Choose from l2_vtr_op_t enum values + @param inner_tag - needed for translate_qinq vtr op only + @param outer_tag - needed for translate_qinq vtr op only + @param b_dmac - B-tag remote mac address, needed for any push or translate_qinq vtr op + @param b_smac - B-tag local mac address, needed for any push or translate qinq vtr op + @param b_vlanid - B-tag vlanid, needed for any push or translate qinq vtr op + @param i_sid - I-tag service id, needed for any push or translate qinq vtr op +*/ +define l2_interface_pbb_tag_rewrite +{ + u32 client_index; + u32 context; + u32 sw_if_index; + u32 vtr_op; + u16 outer_tag; + u8 b_dmac[6]; + u8 b_smac[6]; + u16 b_vlanid; + u32 i_sid; +}; + +/** \brief L2 interface pbb tag rewrite response + @param context - sender context, to match reply w/ request + @param retval - return code for the request +*/ +define l2_interface_pbb_tag_rewrite_reply +{ + u32 context; + i32 retval; +}; -- 2.16.6