VPP-355: add PBB (802.1ah) tag rewrite 20/2520/12
authorPavel Kotucek <pkotucek@cisco.com>
Fri, 26 Aug 2016 14:11:36 +0000 (16:11 +0200)
committerJohn Lo <loj@cisco.com>
Tue, 4 Oct 2016 17:15:16 +0000 (17:15 +0000)
- 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>
12 files changed:
vnet/vnet/ethernet/format.c
vnet/vnet/ethernet/packet.h
vnet/vnet/ethernet/types.def
vnet/vnet/l2/l2_input_vtr.c
vnet/vnet/l2/l2_output.c
vnet/vnet/l2/l2_output.h
vnet/vnet/l2/l2_vtr.c
vnet/vnet/l2/l2_vtr.h
vpp-api-test/vat/api_format.c
vpp/vpp-api/api.c
vpp/vpp-api/custom_dump.c
vpp/vpp-api/vpe.api

index 1b67127..4edef5a 100644 (file)
@@ -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 = &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;
index b50f930..964cf63 100644 (file)
@@ -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 */
 
 /*
index 37c97f6..643f315 100644 (file)
@@ -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)
index 47b1211..60a3963 100644 (file)
@@ -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)
index f03796e..b84501a 100644 (file)
@@ -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
index 6a846f3..2e04914 100644 (file)
@@ -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;
 
 
index 9cb0a7e..95a4f15 100644 (file)
@@ -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
index 5e41c6d..893b227 100644 (file)
@@ -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.
index 12e8a95..7dc7cea 100644 (file)
@@ -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                                    \
index 5698e36..2fdf526 100644 (file)
@@ -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)                                                \
index dc0dfa1..4689776 100644 (file)
@@ -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)
 {
index d42eee8..6924aef 100644 (file)
@@ -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;
+};