aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPavel Kotucek <pkotucek@cisco.com>2016-08-26 16:11:36 +0200
committerJohn Lo <loj@cisco.com>2016-10-04 17:15:16 +0000
commit95300d19152877dca8dfbd574dc6da50620125e8 (patch)
treee645d4dd4a83c37f7b407042132ebf4fbaf0582c
parentac09280947dcf035f51cb394b88de3c7b66a6320 (diff)
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 <pkotucek@cisco.com>
-rw-r--r--vnet/vnet/ethernet/format.c87
-rw-r--r--vnet/vnet/ethernet/packet.h39
-rw-r--r--vnet/vnet/ethernet/types.def1
-rw-r--r--vnet/vnet/l2/l2_input_vtr.c99
-rw-r--r--vnet/vnet/l2/l2_output.c143
-rw-r--r--vnet/vnet/l2/l2_output.h5
-rw-r--r--vnet/vnet/l2/l2_vtr.c165
-rw-r--r--vnet/vnet/l2/l2_vtr.h67
-rw-r--r--vpp-api-test/vat/api_format.c99
-rw-r--r--vpp/vpp-api/api.c39
-rw-r--r--vpp/vpp-api/custom_dump.c26
-rw-r--r--vpp/vpp-api/vpe.api35
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
@@ -88,9 +88,35 @@ format_ethernet_vlan_tci (u8 * s, va_list * va)
}
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 = &ethernet_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 <interface> [disable | pop | push | translate_pbb_stag <outer_tag> dmac <address> smac <address> s_id <nn> [b_vlanid <nn>]]
+ */
+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 <outer_tag>\n"
+ "dmac <address> smac <address> s_id <nn> [b_vlanid <nn>]]");
+ 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 <address> smac <address> s_id <nn> [b_vlanid <nn>]");
+ 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 <interface> [disable | pop | push | translate_pbb_stag <outer_tag> dmac <address> smac <address> s_id <nn> [b_vlanid <nn>]]",
+ .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 <addr> dst <addr> local_sa <sa-id> remote_sa <sa-id> [del]") \
_(ipsec_gre_tunnel_dump, "[sw_if_index <nn>]") \
-_(delete_subif,"sub_sw_if_index <nn> sub_if_id <nn>")
+_(delete_subif,"sub_sw_if_index <nn> sub_if_id <nn>") \
+_(l2_interface_pbb_tag_rewrite, \
+ "<intfc> | sw_if_index <nn> \n" \
+ "[disable | push | pop | translate_pbb_stag <outer_tag>] \n" \
+ "dmac <mac> smac <mac> sid <nn> [vlanid <nn>]")
/* 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;
+};