From 95300d19152877dca8dfbd574dc6da50620125e8 Mon Sep 17 00:00:00 2001 From: Pavel Kotucek Date: Fri, 26 Aug 2016 16:11:36 +0200 Subject: 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 ++++++++++++++++++ 8 files changed, 503 insertions(+), 103 deletions(-) (limited to 'vnet') 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. -- cgit 1.2.3-korg