gbp: add anonymous l3-out external interfaces 61/20361/6
authorBenoît Ganne <bganne@cisco.com>
Wed, 26 Jun 2019 11:36:51 +0000 (13:36 +0200)
committerNeale Ranns <nranns@cisco.com>
Tue, 2 Jul 2019 14:19:07 +0000 (14:19 +0000)
So far, GBP l3-out packets classification & policy relied on programmed
EP. All traffic to/from l3-out must go through a known EP.
This patch introduces a new feature where l3-out next-hops are only
known by their subnets (l3-out prefixes). As there are no longer known
EPs to program, an interface must be configured as external anonymous
l3-out. Packets classification & policy on this interface will rely on
the external subnets programmed in the BD VRF.
Note that contrary to all other interfaces in a GBP BD, external
anonymous l3-out interfaces have BD L2 learning turned on and rely on
ARP/ND.

Type: feature

Change-Id: Ieedb29dff4e967d08c4301e82d06bff450a63e5f
Signed-off-by: Benoît Ganne <bganne@cisco.com>
14 files changed:
src/plugins/gbp/gbp.api
src/plugins/gbp/gbp.h
src/plugins/gbp/gbp_api.c
src/plugins/gbp/gbp_api_print.h
src/plugins/gbp/gbp_classify.c
src/plugins/gbp/gbp_classify.h
src/plugins/gbp/gbp_classify_node.c
src/plugins/gbp/gbp_ext_itf.c
src/plugins/gbp/gbp_ext_itf.h
src/plugins/gbp/gbp_policy.c
src/plugins/gbp/gbp_policy_dpo.h
src/plugins/gbp/gbp_policy_node.c
src/vnet/l2/l2_input.h
src/vnet/l2/l2_output.h

index f6775e7..d1e483d 100644 (file)
@@ -419,6 +419,14 @@ define gbp_ext_itf_details
   vl_api_gbp_ext_itf_t ext_itf;
 };
 
+manual_print autoreply define gbp_ext_itf_anon_add_del
+{
+  u32 client_index;
+  u32 context;
+  u8  is_add;
+  vl_api_gbp_ext_itf_t ext_itf;
+};
+
 /*
  * Local Variables:
  * eval: (c-set-style "gnu")
index 35e02d2..e194a9d 100644 (file)
@@ -48,6 +48,15 @@ typedef struct
 
 extern gbp_main_t gbp_main;
 
+typedef enum gbp_policy_type_t_
+{
+  GBP_POLICY_PORT,
+  GBP_POLICY_MAC,
+  GBP_POLICY_LPM,
+  GBP_N_POLICY
+#define GBP_N_POLICY GBP_N_POLICY
+} gbp_policy_type_t;
+
 /**
  * Grouping of global data for the GBP source EPG classification feature
  */
@@ -56,7 +65,7 @@ typedef struct gbp_policy_main_t_
   /**
    * Next nodes for L2 output features
    */
-  u32 l2_output_feat_next[2][32];
+  u32 l2_output_feat_next[GBP_N_POLICY][32];
 } gbp_policy_main_t;
 
 extern gbp_policy_main_t gbp_policy_main;
index 8155a8f..010e308 100644 (file)
@@ -82,7 +82,8 @@
   _(GBP_CONTRACT_DUMP, gbp_contract_dump)                   \
   _(GBP_VXLAN_TUNNEL_ADD, gbp_vxlan_tunnel_add)             \
   _(GBP_VXLAN_TUNNEL_DEL, gbp_vxlan_tunnel_del)             \
-  _(GBP_VXLAN_TUNNEL_DUMP, gbp_vxlan_tunnel_dump)
+  _(GBP_VXLAN_TUNNEL_DUMP, gbp_vxlan_tunnel_dump)           \
+  _(GBP_EXT_ITF_ANON_ADD_DEL, gbp_ext_itf_anon_add_del)
 
 gbp_main_t gbp_main;
 
@@ -1146,6 +1147,34 @@ vl_api_gbp_vxlan_tunnel_dump_t_handler (vl_api_gbp_vxlan_tunnel_dump_t * mp)
   gbp_vxlan_walk (gbp_vxlan_tunnel_send_details, &ctx);
 }
 
+static void
+vl_api_gbp_ext_itf_anon_add_del_t_handler (vl_api_gbp_ext_itf_anon_add_del_t *
+                                          mp)
+{
+  vl_api_gbp_ext_itf_anon_add_del_reply_t *rmp;
+  u32 sw_if_index = ~0;
+  vl_api_gbp_ext_itf_t *ext_itf;
+  int rv = 0;
+
+  ext_itf = &mp->ext_itf;
+  if (ext_itf)
+    sw_if_index = ntohl (ext_itf->sw_if_index);
+
+  if (!vnet_sw_if_index_is_api_valid (sw_if_index))
+    goto bad_sw_if_index;
+
+  if (mp->is_add)
+    rv = gbp_ext_itf_anon_add (sw_if_index,
+                              ntohl (ext_itf->bd_id),
+                              ntohl (ext_itf->rd_id));
+  else
+    rv = gbp_ext_itf_anon_delete (sw_if_index);
+
+  BAD_SW_IF_INDEX_LABEL;
+
+  REPLY_MACRO (VL_API_GBP_EXT_ITF_ANON_ADD_DEL_REPLY + GBP_MSG_BASE);
+}
+
 /*
  * gbp_api_hookup
  * Add vpe's API message handlers to the table.
index 67cd30c..b3afc94 100644 (file)
@@ -340,6 +340,29 @@ vl_api_gbp_ext_itf_add_del_t_print (vl_api_gbp_ext_itf_add_del_t * a,
   return handle;
 }
 
+static inline void *
+vl_api_gbp_ext_itf_anon_add_del_t_print (vl_api_gbp_ext_itf_anon_add_del_t *
+                                        a, void *handle)
+{
+  u8 *s = 0;
+
+  s = format (s, "SCRIPT: gbp_ext_itf_anon_add_del ");
+  if (a->is_add)
+    s = format (s, "add ");
+  else
+    s = format (s, "del ");
+
+  s = format (s, "sw_if_index %d ", ntohl (a->ext_itf.sw_if_index));
+  s = format (s, "bd_id %d ", ntohl (a->ext_itf.bd_id));
+  s = format (s, "rd_id %d ", ntohl (a->ext_itf.rd_id));
+
+  s = format (s, "\n");
+
+  PRINT_S;
+
+  return handle;
+}
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index 5735f35..255db25 100644 (file)
@@ -49,6 +49,14 @@ gbp_src_classify_init (vlib_main_t * vm)
                               l2input_get_feat_names (),
                               em->l2_input_feat_next[GBP_SRC_CLASSIFY_LPM]);
 
+  node = vlib_get_node_by_name (vm, (u8 *) "l2-gbp-lpm-anon-classify");
+  feat_bitmap_init_next_nodes (vm,
+                              node->index,
+                              L2INPUT_N_FEAT,
+                              l2input_get_feat_names (),
+                              em->l2_input_feat_next
+                              [GBP_SRC_CLASSIFY_LPM_ANON]);
+
   return 0;
 }
 
index c0c1fd5..ca7db94 100644 (file)
 #define __GBP_CLASSIFY_H__
 
 #include <plugins/gbp/gbp.h>
+#include <vnet/ethernet/arp_packet.h>
 
 typedef enum gbp_src_classify_type_t_
 {
   GBP_SRC_CLASSIFY_NULL,
   GBP_SRC_CLASSIFY_PORT,
   GBP_SRC_CLASSIFY_LPM,
+  GBP_SRC_CLASSIFY_LPM_ANON,
+  GBP_SRC_N_CLASSIFY
+#define GBP_SRC_N_CLASSIFY GBP_SRC_N_CLASSIFY
 } gbp_src_classify_type_t;
 
-#define GBP_SRC_N_CLASSIFY (GBP_SRC_CLASSIFY_LPM + 1)
-
 /**
  * Grouping of global data for the GBP source EPG classification feature
  */
@@ -42,6 +44,45 @@ typedef struct gbp_src_classify_main_t_
 
 extern gbp_src_classify_main_t gbp_src_classify_main;
 
+enum gbp_classify_get_ip_way
+{
+  GBP_CLASSIFY_GET_IP_SRC = 0,
+  GBP_CLASSIFY_GET_IP_DST = 1
+};
+
+static_always_inline dpo_proto_t
+gbp_classify_get_ip_address (const ethernet_header_t * eh0,
+                            const ip4_address_t ** ip4,
+                            const ip6_address_t ** ip6,
+                            const enum gbp_classify_get_ip_way way)
+{
+  u16 etype = clib_net_to_host_u16 (eh0->type);
+  const void *l3h0 = eh0 + 1;
+
+  if (ETHERNET_TYPE_VLAN == etype)
+    {
+      const ethernet_vlan_header_t *vh0 =
+       (ethernet_vlan_header_t *) (eh0 + 1);
+      etype = clib_net_to_host_u16 (vh0->type);
+      l3h0 = vh0 + 1;
+    }
+
+  switch (etype)
+    {
+    case ETHERNET_TYPE_IP4:
+      *ip4 = &(&((const ip4_header_t *) l3h0)->src_address)[way];
+      return DPO_PROTO_IP4;
+    case ETHERNET_TYPE_IP6:
+      *ip6 = &(&((const ip6_header_t *) l3h0)->src_address)[way];
+      return DPO_PROTO_IP6;
+    case ETHERNET_TYPE_ARP:
+      *ip4 = &((ethernet_arp_header_t *) l3h0)->ip4_over_ethernet[way].ip4;
+      return DPO_PROTO_IP4;
+    }
+
+  return DPO_PROTO_NONE;
+}
+
 #endif
 
 /*
index 9ad2b06..a2058a2 100644 (file)
@@ -279,36 +279,6 @@ typedef enum gbp_lpm_classify_next_t_
   GPB_LPM_CLASSIFY_DROP,
 } gbp_lpm_classify_next_t;
 
-always_inline void
-gbp_classify_get_src_ip_address (const ethernet_header_t * eh0,
-                                const ip4_address_t ** ip4,
-                                const ip6_address_t ** ip6)
-{
-  u16 etype = clib_net_to_host_u16 (eh0->type);
-  const void *l3h0 = eh0 + 1;
-
-  if (ETHERNET_TYPE_VLAN == etype)
-    {
-      const ethernet_vlan_header_t *vh0 =
-       (ethernet_vlan_header_t *) (eh0 + 1);
-      etype = clib_net_to_host_u16 (vh0->type);
-      l3h0 = vh0 + 1;
-    }
-
-  switch (etype)
-    {
-    case ETHERNET_TYPE_IP4:
-      *ip4 = &((const ip4_header_t *) l3h0)->src_address;
-      break;
-    case ETHERNET_TYPE_IP6:
-      *ip6 = &((const ip6_header_t *) l3h0)->src_address;
-      break;
-    case ETHERNET_TYPE_ARP:
-      *ip4 = &((ethernet_arp_header_t *) l3h0)->ip4_over_ethernet[0].ip4;
-      break;
-    }
-}
-
 /**
  * per-packet trace data
  */
@@ -333,6 +303,13 @@ format_gbp_lpm_classify_trace (u8 * s, va_list * args)
   return s;
 }
 
+enum gbp_lpm_type
+{
+  GBP_LPM_RECIRC,
+  GBP_LPM_EPG,
+  GBP_LPM_ANON
+};
+
 /*
  * Determine the SRC EPG from a LPM
  */
@@ -340,7 +317,8 @@ always_inline uword
 gbp_lpm_classify_inline (vlib_main_t * vm,
                         vlib_node_runtime_t * node,
                         vlib_frame_t * frame,
-                        dpo_proto_t dproto, u8 is_recirc)
+                        const dpo_proto_t dproto,
+                        const enum gbp_lpm_type type)
 {
   gbp_src_classify_main_t *gscm = &gbp_src_classify_main;
   u32 n_left_from, *from, *to_next;
@@ -366,8 +344,6 @@ gbp_lpm_classify_inline (vlib_main_t * vm,
          const ip4_address_t *ip4_0;
          const ip6_address_t *ip6_0;
          const gbp_recirc_t *gr0;
-         const dpo_id_t *dpo0;
-         load_balance_t *lb0;
          vlib_buffer_t *b0;
          sclass_t sclass0;
 
@@ -397,10 +373,11 @@ gbp_lpm_classify_inline (vlib_main_t * vm,
          else if (DPO_PROTO_ETHERNET == dproto)
            {
              eh0 = vlib_buffer_get_current (b0);
-             gbp_classify_get_src_ip_address (eh0, &ip4_0, &ip6_0);
+             gbp_classify_get_ip_address (eh0, &ip4_0, &ip6_0,
+                                          GBP_CLASSIFY_GET_IP_SRC);
            }
 
-         if (is_recirc)
+         if (GBP_LPM_RECIRC == type)
            {
              gr0 = gbp_recirc_get (sw_if_index0);
              fib_index0 = gr0->gr_fib_index[dproto];
@@ -417,96 +394,105 @@ gbp_lpm_classify_inline (vlib_main_t * vm,
                  goto trace;
                }
 
-             ge0 = gbp_endpoint_find_mac (eh0->src_address,
-                                          vnet_buffer (b0)->l2.bd_index);
-
-             if (NULL == ge0)
+             if (GBP_LPM_ANON == type)
                {
-                 /* packet must have come from an EP's mac */
-                 sclass0 = SCLASS_INVALID;
-                 goto trace;
-               }
-
-             fib_index0 = ge0->ge_fwd.gef_fib_index;
-
-             if (~0 == fib_index0)
-               {
-                 sclass0 = SCLASS_INVALID;
-                 goto trace;
-               }
-
-             if (ip4_0)
-               {
-                 ge_lpm0 = gbp_endpoint_find_ip4 (ip4_0, fib_index0);
-               }
-             else if (ip6_0)
-               {
-                 ge_lpm0 = gbp_endpoint_find_ip6 (ip6_0, fib_index0);
+                 /*
+                  * anonymous LPM classification: only honour LPM as no EP
+                  * were programmed
+                  */
+                 gbp_ext_itf_t *gei = gbp_ext_itf_get (sw_if_index0);
+                 if (ip4_0)
+                   fib_index0 = gei->gx_fib_index[DPO_PROTO_IP4];
+                 else if (ip6_0)
+                   fib_index0 = gei->gx_fib_index[DPO_PROTO_IP6];
+                 else
+                   {
+                     /* not IP so no LPM classify possible */
+                     sclass0 = SCLASS_INVALID;
+                     next0 = GPB_LPM_CLASSIFY_DROP;
+                     goto trace;
+                   }
+                 next0 = vnet_l2_feature_next
+                   (b0, gscm->l2_input_feat_next[GBP_SRC_CLASSIFY_LPM_ANON],
+                    L2INPUT_FEAT_GBP_LPM_ANON_CLASSIFY);
                }
              else
                {
-                 ge_lpm0 = NULL;
-               }
+                 /*
+                  * not an anonymous LPM classification: check it comes from
+                  * an EP, and use EP RD info
+                  */
+                 ge0 = gbp_endpoint_find_mac (eh0->src_address,
+                                              vnet_buffer (b0)->l2.bd_index);
 
-             next0 = vnet_l2_feature_next
-               (b0, gscm->l2_input_feat_next[GBP_SRC_CLASSIFY_LPM],
-                L2INPUT_FEAT_GBP_LPM_CLASSIFY);
+                 if (NULL == ge0)
+                   {
+                     /* packet must have come from an EP's mac */
+                     sclass0 = SCLASS_INVALID;
+                     goto trace;
+                   }
 
-             /*
-              * if we found the EP by IP lookup, it must be from the EP
-              * not a network behind it
-              */
-             if (NULL != ge_lpm0)
-               {
-                 if (PREDICT_FALSE (ge0 != ge_lpm0))
+                 fib_index0 = ge0->ge_fwd.gef_fib_index;
+
+                 if (~0 == fib_index0)
                    {
-                     /* an EP spoofing another EP */
                      sclass0 = SCLASS_INVALID;
-                     next0 = GPB_LPM_CLASSIFY_DROP;
+                     goto trace;
+                   }
+
+                 if (ip4_0)
+                   {
+                     ge_lpm0 = gbp_endpoint_find_ip4 (ip4_0, fib_index0);
+                   }
+                 else if (ip6_0)
+                   {
+                     ge_lpm0 = gbp_endpoint_find_ip6 (ip6_0, fib_index0);
                    }
                  else
                    {
-                     sclass0 = ge0->ge_fwd.gef_sclass;
+                     ge_lpm0 = NULL;
+                   }
+
+                 next0 = vnet_l2_feature_next
+                   (b0, gscm->l2_input_feat_next[GBP_SRC_CLASSIFY_LPM],
+                    L2INPUT_FEAT_GBP_LPM_CLASSIFY);
+
+                 /*
+                  * if we found the EP by IP lookup, it must be from the EP
+                  * not a network behind it
+                  */
+                 if (NULL != ge_lpm0)
+                   {
+                     if (PREDICT_FALSE (ge0 != ge_lpm0))
+                       {
+                         /* an EP spoofing another EP */
+                         sclass0 = SCLASS_INVALID;
+                         next0 = GPB_LPM_CLASSIFY_DROP;
+                       }
+                     else
+                       {
+                         sclass0 = ge0->ge_fwd.gef_sclass;
+                       }
+                     goto trace;
                    }
-                 goto trace;
                }
            }
 
-         if (ip4_0)
-           {
-             lbi0 = ip4_fib_forwarding_lookup (fib_index0, ip4_0);
-           }
-         else if (ip6_0)
+         gpd0 = gbp_classify_get_gpd (ip4_0, ip6_0, fib_index0);
+         if (0 == gpd0)
            {
-             lbi0 =
-               ip6_fib_table_fwding_lookup (&ip6_main, fib_index0, ip6_0);
-           }
-         else
-           {
-             /* not IP so no LPM classify possible */
+             /* could not classify => drop */
              sclass0 = SCLASS_INVALID;
              next0 = GPB_LPM_CLASSIFY_DROP;
              goto trace;
            }
-         lb0 = load_balance_get (lbi0);
-         dpo0 = load_balance_get_bucket_i (lb0, 0);
+
+         sclass0 = gpd0->gpd_sclass;
 
          /* all packets from an external network should not be learned by the
           * reciever. so set the Do-not-learn bit here */
          vnet_buffer2 (b0)->gbp.flags = VXLAN_GBP_GPFLAGS_D;
 
-         if (gbp_policy_dpo_type == dpo0->dpoi_type)
-           {
-             gpd0 = gbp_policy_dpo_get (dpo0->dpoi_index);
-             sclass0 = gpd0->gpd_sclass;
-           }
-         else
-           {
-             /* could not classify => drop */
-             sclass0 = SCLASS_INVALID;
-             goto trace;
-           }
-
        trace:
          vnet_buffer2 (b0)->gbp.sclass = sclass0;
 
@@ -537,21 +523,32 @@ VLIB_NODE_FN (gbp_ip4_lpm_classify_node) (vlib_main_t * vm,
                                          vlib_node_runtime_t * node,
                                          vlib_frame_t * frame)
 {
-  return (gbp_lpm_classify_inline (vm, node, frame, DPO_PROTO_IP4, 1));
+  return (gbp_lpm_classify_inline
+         (vm, node, frame, DPO_PROTO_IP4, GBP_LPM_RECIRC));
 }
 
 VLIB_NODE_FN (gbp_ip6_lpm_classify_node) (vlib_main_t * vm,
                                          vlib_node_runtime_t * node,
                                          vlib_frame_t * frame)
 {
-  return (gbp_lpm_classify_inline (vm, node, frame, DPO_PROTO_IP6, 1));
+  return (gbp_lpm_classify_inline
+         (vm, node, frame, DPO_PROTO_IP6, GBP_LPM_RECIRC));
 }
 
 VLIB_NODE_FN (gbp_l2_lpm_classify_node) (vlib_main_t * vm,
                                         vlib_node_runtime_t * node,
                                         vlib_frame_t * frame)
 {
-  return (gbp_lpm_classify_inline (vm, node, frame, DPO_PROTO_ETHERNET, 0));
+  return (gbp_lpm_classify_inline
+         (vm, node, frame, DPO_PROTO_ETHERNET, GBP_LPM_EPG));
+}
+
+VLIB_NODE_FN (gbp_l2_lpm_anon_classify_node) (vlib_main_t * vm,
+                                             vlib_node_runtime_t * node,
+                                             vlib_frame_t * frame)
+{
+  return (gbp_lpm_classify_inline
+         (vm, node, frame, DPO_PROTO_ETHERNET, GBP_LPM_ANON));
 }
 
 /* *INDENT-OFF* */
@@ -594,6 +591,19 @@ VLIB_REGISTER_NODE (gbp_l2_lpm_classify_node) = {
   },
 };
 
+VLIB_REGISTER_NODE (gbp_l2_lpm_anon_classify_node) = {
+  .name = "l2-gbp-lpm-anon-classify",
+  .vector_size = sizeof (u32),
+  .format_trace = format_gbp_lpm_classify_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = 0,
+  .n_next_nodes = 1,
+  .next_nodes = {
+    [GPB_LPM_CLASSIFY_DROP] = "error-drop"
+  },
+};
+
 VNET_FEATURE_INIT (gbp_ip4_lpm_classify_feat_node, static) =
 {
   .arc_name = "ip4-unicast",
index be2d614..89bcb3d 100644 (file)
@@ -130,6 +130,120 @@ gbp_ext_itf_delete (u32 sw_if_index)
   return (VNET_API_ERROR_NO_SUCH_ENTRY);
 }
 
+int
+gbp_ext_itf_anon_add (u32 sw_if_index, u32 bd_id, u32 rd_id)
+{
+  int rv = gbp_ext_itf_add (sw_if_index, bd_id, rd_id);
+  if (rv)
+    return rv;
+  /* add interface to the BD */
+  index_t itf = gbp_itf_add_and_lock (sw_if_index, bd_id);
+  /* setup GBP L2 features on this interface */
+  gbp_itf_set_l2_input_feature (itf, 0,
+                               L2INPUT_FEAT_GBP_LPM_ANON_CLASSIFY |
+                               L2INPUT_FEAT_LEARN);
+  gbp_itf_set_l2_output_feature (itf, 0, L2OUTPUT_FEAT_GBP_POLICY_LPM);
+  return 0;
+}
+
+int
+gbp_ext_itf_anon_delete (u32 sw_if_index)
+{
+  int rv = gbp_ext_itf_delete (sw_if_index);
+  if (rv)
+    return rv;
+  gbp_itf_unlock (sw_if_index);
+  return 0;
+}
+
+static clib_error_t *
+gbp_ext_itf_add_del_cli (vlib_main_t * vm,
+                        unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  unformat_input_t _line_input, *line_input = &_line_input;
+  u32 sw_if_index = ~0, bd_id = ~0, rd_id = ~0;
+  int add = 1, anon = 0;
+  int rv;
+
+  /* Get a line of input. */
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "del"))
+       add = 0;
+      else
+       if (unformat
+           (line_input, "%U", unformat_vnet_sw_interface, vnet_get_main (),
+            &sw_if_index))
+       ;
+      else if (unformat (line_input, "bd %d", &bd_id))
+       ;
+      else if (unformat (line_input, "rd %d", &rd_id))
+       ;
+      else if (unformat (line_input, "anon-l3-out"))
+       anon = 1;
+      else
+       return clib_error_return (0, "unknown input `%U'",
+                                 format_unformat_error, line_input);
+    }
+  unformat_free (line_input);
+
+  if (~0 == sw_if_index)
+    return clib_error_return (0, "interface must be specified");
+
+  if (add)
+    {
+      if (~0 == bd_id)
+       return clib_error_return (0, "BD-ID must be specified");
+      if (~0 == rd_id)
+       return clib_error_return (0, "RD-ID must be specified");
+      if (anon)
+       rv = gbp_ext_itf_anon_add (sw_if_index, bd_id, rd_id);
+      else
+       rv = gbp_ext_itf_add (sw_if_index, bd_id, rd_id);
+    }
+  else
+    {
+      if (anon)
+       rv = gbp_ext_itf_anon_delete (sw_if_index);
+      else
+       rv = gbp_ext_itf_delete (sw_if_index);
+    }
+
+  switch (rv)
+    {
+    case 0:
+      return 0;
+    case VNET_API_ERROR_ENTRY_ALREADY_EXISTS:
+      return clib_error_return (0, "interface already exists");
+    case VNET_API_ERROR_NO_SUCH_ENTRY: /* fallthrough */
+    case VNET_API_ERROR_INVALID_SW_IF_INDEX:
+      return clib_error_return (0, "unknown interface");
+    default:
+      return clib_error_return (0, "error %d", rv);
+    }
+
+  /* never reached */
+  return 0;
+}
+
+/*?
+ * Add Group Based Interface as anonymous L3out interface
+ *
+ * @cliexpar
+ * @cliexstart{gbp interface [del] anon-l3out <interface> bd <ID>}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_itf_anon_l3out_add_del_node, static) = {
+  .path = "gbp ext-itf",
+  .short_help = "gbp ext-itf [del] <interface> bd <ID> rd <ID> [anon-l3-out]\n",
+  .function = gbp_ext_itf_add_del_cli,
+};
+/* *INDENT-ON* */
+
 void
 gbp_ext_itf_walk (gbp_ext_itf_cb_t cb, void *ctx)
 {
index dafcd08..d1b0f98 100644 (file)
@@ -52,6 +52,9 @@ typedef struct gpb_ext_itf_t_
 extern int gbp_ext_itf_add (u32 sw_if_index, u32 bd_id, u32 rd_id);
 extern int gbp_ext_itf_delete (u32 sw_if_index);
 
+extern int gbp_ext_itf_anon_add (u32 sw_if_index, u32 bd_id, u32 rd_id);
+extern int gbp_ext_itf_anon_delete (u32 sw_if_index);
+
 extern u8 *format_gbp_ext_itf (u8 * s, va_list * args);
 
 typedef walk_rc_t (*gbp_ext_itf_cb_t) (gbp_ext_itf_t * gbpe, void *ctx);
index fbdf394..0f26701 100644 (file)
@@ -24,21 +24,27 @@ gbp_policy_init (vlib_main_t * vm)
   gbp_policy_main_t *gpm = &gbp_policy_main;
   clib_error_t *error = 0;
 
-  vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "gbp-policy-port");
-
   /* Initialize the feature next-node indexes */
+  vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "gbp-policy-port");
   feat_bitmap_init_next_nodes (vm,
                               node->index,
                               L2OUTPUT_N_FEAT,
                               l2output_get_feat_names (),
-                              gpm->l2_output_feat_next[1]);
+                              gpm->l2_output_feat_next[GBP_POLICY_PORT]);
 
   node = vlib_get_node_by_name (vm, (u8 *) "gbp-policy-mac");
   feat_bitmap_init_next_nodes (vm,
                               node->index,
                               L2OUTPUT_N_FEAT,
                               l2output_get_feat_names (),
-                              gpm->l2_output_feat_next[0]);
+                              gpm->l2_output_feat_next[GBP_POLICY_MAC]);
+
+  node = vlib_get_node_by_name (vm, (u8 *) "gbp-policy-lpm");
+  feat_bitmap_init_next_nodes (vm,
+                              node->index,
+                              L2OUTPUT_N_FEAT,
+                              l2output_get_feat_names (),
+                              gpm->l2_output_feat_next[GBP_POLICY_LPM]);
 
   return error;
 }
index 6b4f8c5..1dc11ab 100644 (file)
@@ -17,6 +17,9 @@
 #define __GBP_POLICY_DPO_H__
 
 #include <vnet/dpo/dpo.h>
+#include <vnet/dpo/load_balance.h>
+#include <vnet/fib/ip4_fib.h>
+#include <vnet/fib/ip6_fib.h>
 
 /**
  * @brief
@@ -81,6 +84,32 @@ gbp_policy_dpo_get (index_t index)
   return (pool_elt_at_index (gbp_policy_dpo_pool, index));
 }
 
+static_always_inline const gbp_policy_dpo_t *
+gbp_classify_get_gpd (const ip4_address_t * ip4, const ip6_address_t * ip6,
+                     const u32 fib_index)
+{
+  const gbp_policy_dpo_t *gpd;
+  const dpo_id_t *dpo;
+  const load_balance_t *lb;
+  u32 lbi;
+
+  if (ip4)
+    lbi = ip4_fib_forwarding_lookup (fib_index, ip4);
+  else if (ip6)
+    lbi = ip6_fib_table_fwding_lookup (&ip6_main, fib_index, ip6);
+  else
+    return 0;
+
+  lb = load_balance_get (lbi);
+  dpo = load_balance_get_bucket_i (lb, 0);
+
+  if (dpo->dpoi_type != gbp_policy_dpo_type)
+    return 0;
+
+  gpd = gbp_policy_dpo_get (dpo->dpoi_index);
+  return gpd;
+}
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index 26f7e9b..10f5956 100644 (file)
  */
 
 #include <plugins/gbp/gbp.h>
+#include <plugins/gbp/gbp_classify.h>
 #include <plugins/gbp/gbp_policy_dpo.h>
 #include <plugins/gbp/gbp_bridge_domain.h>
+#include <plugins/gbp/gbp_ext_itf.h>
 
 #include <vnet/vxlan-gbp/vxlan_gbp_packet.h>
 #include <vnet/vxlan-gbp/vxlan_gbp.h>
@@ -109,10 +111,34 @@ gbp_policy_is_ethertype_allowed (const gbp_contract_t * gc0, u16 ethertype)
   return (0);
 }
 
+static_always_inline gbp_policy_next_t
+gbp_policy_l2_feature_next (gbp_policy_main_t * gpm, vlib_buffer_t * b,
+                           const gbp_policy_type_t type)
+{
+  u32 feat_bit;
+
+  switch (type)
+    {
+    case GBP_POLICY_PORT:
+      feat_bit = L2OUTPUT_FEAT_GBP_POLICY_PORT;
+      break;
+    case GBP_POLICY_MAC:
+      feat_bit = L2OUTPUT_FEAT_GBP_POLICY_MAC;
+      break;
+    case GBP_POLICY_LPM:
+      feat_bit = L2OUTPUT_FEAT_GBP_POLICY_LPM;
+      break;
+    default:
+      return GBP_POLICY_NEXT_DROP;
+    }
+
+  return vnet_l2_feature_next (b, gpm->l2_output_feat_next[type], feat_bit);
+}
+
 static uword
 gbp_policy_inline (vlib_main_t * vm,
                   vlib_node_runtime_t * node,
-                  vlib_frame_t * frame, u8 is_port_based)
+                  vlib_frame_t * frame, const gbp_policy_type_t type)
 {
   gbp_main_t *gm = &gbp_main;
   gbp_policy_main_t *gpm = &gbp_policy_main;
@@ -174,12 +200,7 @@ gbp_policy_inline (vlib_main_t * vm,
           */
          if (vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_A)
            {
-             next0 = vnet_l2_feature_next (b0,
-                                           gpm->l2_output_feat_next
-                                           [is_port_based],
-                                           (is_port_based ?
-                                            L2OUTPUT_FEAT_GBP_POLICY_PORT :
-                                            L2OUTPUT_FEAT_GBP_POLICY_MAC));
+             next0 = gbp_policy_l2_feature_next (gpm, b0, type);
              n_allow_a_bit++;
              key0.as_u64 = ~0;
              goto trace;
@@ -188,19 +209,39 @@ gbp_policy_inline (vlib_main_t * vm,
          /*
           * determine the src and dst EPG
           */
-         if (is_port_based)
-           ge0 = gbp_endpoint_find_itf (sw_if_index0);
-         else
-           ge0 = gbp_endpoint_find_mac (h0->dst_address,
-                                        vnet_buffer (b0)->l2.bd_index);
 
-         if (NULL != ge0)
+         key0.gck_dst = SCLASS_INVALID;
+
+         if (GBP_POLICY_LPM == type)
            {
-             key0.gck_dst = ge0->ge_fwd.gef_sclass;
-             key0.gck_scope =
-               gbp_bridge_domain_get_scope (vnet_buffer (b0)->l2.bd_index);
+             const ip4_address_t *ip4 = 0;
+             const ip6_address_t *ip6 = 0;
+             const dpo_proto_t proto =
+               gbp_classify_get_ip_address (h0, &ip4, &ip6,
+                                            GBP_CLASSIFY_GET_IP_DST);
+             if (PREDICT_TRUE (DPO_PROTO_NONE != proto))
+               {
+                 const gbp_ext_itf_t *ext_itf =
+                   gbp_ext_itf_get (sw_if_index0);
+                 const gbp_policy_dpo_t *gpd =
+                   gbp_classify_get_gpd (ip4, ip6,
+                                         ext_itf->gx_fib_index[proto]);
+                 if (gpd)
+                   key0.gck_dst = gpd->gpd_sclass;
+               }
            }
          else
+           {
+             if (GBP_POLICY_PORT == type)
+               ge0 = gbp_endpoint_find_itf (sw_if_index0);
+             else
+               ge0 = gbp_endpoint_find_mac (h0->dst_address,
+                                            vnet_buffer (b0)->l2.bd_index);
+             if (NULL != ge0)
+               key0.gck_dst = ge0->ge_fwd.gef_sclass;
+           }
+
+         if (SCLASS_INVALID == key0.gck_dst)
            {
              /* If you cannot determine the destination EP then drop */
              b0->error = node->errors[GBP_POLICY_ERROR_DROP_NO_DCLASS];
@@ -215,13 +256,7 @@ gbp_policy_inline (vlib_main_t * vm,
                  /*
                   * intra-epg allowed
                   */
-                 next0 =
-                   vnet_l2_feature_next (b0,
-                                         gpm->l2_output_feat_next
-                                         [is_port_based],
-                                         (is_port_based ?
-                                          L2OUTPUT_FEAT_GBP_POLICY_PORT :
-                                          L2OUTPUT_FEAT_GBP_POLICY_MAC));
+                 next0 = gbp_policy_l2_feature_next (gpm, b0, type);
                  vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A;
                  n_allow_intra++;
                }
@@ -230,18 +265,15 @@ gbp_policy_inline (vlib_main_t * vm,
                  /*
                   * sclass or dclass 1 allowed
                   */
-                 next0 =
-                   vnet_l2_feature_next (b0,
-                                         gpm->l2_output_feat_next
-                                         [is_port_based],
-                                         (is_port_based ?
-                                          L2OUTPUT_FEAT_GBP_POLICY_PORT :
-                                          L2OUTPUT_FEAT_GBP_POLICY_MAC));
+                 next0 = gbp_policy_l2_feature_next (gpm, b0, type);
                  vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A;
                  n_allow_sclass_1++;
                }
              else
                {
+                 key0.gck_scope =
+                   gbp_bridge_domain_get_scope (vnet_buffer (b0)->
+                                                l2.bd_index);
                  gci0 = gbp_contract_find (&key0);
 
                  if (INDEX_INVALID != gci0)
@@ -321,13 +353,9 @@ gbp_policy_inline (vlib_main_t * vm,
                              switch (gu->gu_action)
                                {
                                case GBP_RULE_PERMIT:
-                                 next0 = vnet_l2_feature_next
-                                   (b0,
-                                    gpm->l2_output_feat_next
-                                    [is_port_based],
-                                    (is_port_based ?
-                                     L2OUTPUT_FEAT_GBP_POLICY_PORT :
-                                     L2OUTPUT_FEAT_GBP_POLICY_MAC));
+                                 next0 =
+                                   gbp_policy_l2_feature_next (gpm, b0,
+                                                               type);
                                  break;
                                case GBP_RULE_DENY:
                                  next0 = GBP_POLICY_NEXT_DROP;
@@ -368,12 +396,7 @@ gbp_policy_inline (vlib_main_t * vm,
               * the src EPG is not set when the packet arrives on an EPG
               * uplink interface and we do not need to apply policy
               */
-             next0 =
-               vnet_l2_feature_next (b0,
-                                     gpm->l2_output_feat_next[is_port_based],
-                                     (is_port_based ?
-                                      L2OUTPUT_FEAT_GBP_POLICY_PORT :
-                                      L2OUTPUT_FEAT_GBP_POLICY_MAC));
+             next0 = gbp_policy_l2_feature_next (gpm, b0, type);
            }
 
        trace:
@@ -413,14 +436,21 @@ VLIB_NODE_FN (gbp_policy_port_node) (vlib_main_t * vm,
                                     vlib_node_runtime_t * node,
                                     vlib_frame_t * frame)
 {
-  return (gbp_policy_inline (vm, node, frame, 1));
+  return (gbp_policy_inline (vm, node, frame, GBP_POLICY_PORT));
 }
 
 VLIB_NODE_FN (gbp_policy_mac_node) (vlib_main_t * vm,
                                    vlib_node_runtime_t * node,
                                    vlib_frame_t * frame)
 {
-  return (gbp_policy_inline (vm, node, frame, 0));
+  return (gbp_policy_inline (vm, node, frame, GBP_POLICY_MAC));
+}
+
+VLIB_NODE_FN (gbp_policy_lpm_node) (vlib_main_t * vm,
+                                   vlib_node_runtime_t * node,
+                                   vlib_frame_t * frame)
+{
+  return (gbp_policy_inline (vm, node, frame, GBP_POLICY_LPM));
 }
 
 /* packet trace format function */
@@ -470,6 +500,21 @@ VLIB_REGISTER_NODE (gbp_policy_mac_node) = {
   },
 };
 
+VLIB_REGISTER_NODE (gbp_policy_lpm_node) = {
+  .name = "gbp-policy-lpm",
+  .vector_size = sizeof (u32),
+  .format_trace = format_gbp_policy_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = ARRAY_LEN(gbp_policy_error_strings),
+  .error_strings = gbp_policy_error_strings,
+
+  .n_next_nodes = GBP_POLICY_N_NEXT,
+  .next_nodes = {
+    [GBP_POLICY_NEXT_DROP] = "error-drop",
+  },
+};
+
 /* *INDENT-ON* */
 
 /*
index ce9a7d5..677186b 100644 (file)
@@ -112,6 +112,7 @@ l2input_bd_config (u32 bd_index)
  _(LEARN,         "l2-learn")                   \
  _(L2_EMULATION,  "l2-emulation")               \
  _(GBP_LEARN,     "gbp-learn-l2")               \
+ _(GBP_LPM_ANON_CLASSIFY, "l2-gbp-lpm-anon-classify") \
  _(GBP_NULL_CLASSIFY, "gbp-null-classify")      \
  _(GBP_SRC_CLASSIFY,  "gbp-src-classify")       \
  _(GBP_LPM_CLASSIFY,  "l2-gbp-lpm-classify")    \
index 74d2829..1cc1e73 100644 (file)
@@ -81,6 +81,7 @@ extern vlib_node_registration_t l2output_node;
 #define foreach_l2output_feat \
  _(OUTPUT,            "interface-output")           \
  _(SPAN,              "span-l2-output")             \
+ _(GBP_POLICY_LPM,    "gbp-policy-lpm")            \
  _(GBP_POLICY_PORT,   "gbp-policy-port")            \
  _(GBP_POLICY_MAC,    "gbp-policy-mac")             \
  _(CFM,               "feature-bitmap-drop")        \