GBP: L3 out fixes 13/18213/4
authorNeale Ranns <nranns@cisco.com>
Tue, 12 Mar 2019 09:34:07 +0000 (02:34 -0700)
committerNeale Ranns <nranns@cisco.com>
Tue, 12 Mar 2019 16:50:23 +0000 (16:50 +0000)
Change-Id: I0562d597fd45c7ddcb6db42cf17d3ffb569eb140
Signed-off-by: Neale Ranns <nranns@cisco.com>
src/plugins/gbp/gbp_classify_node.c
src/plugins/gbp/gbp_endpoint.c
src/plugins/gbp/gbp_endpoint.h
src/plugins/gbp/gbp_ext_itf.c
src/plugins/gbp/gbp_itf.c
test/test_gbp.py
test/vpp_l2.py

index 1b2cb0a..58b1f7a 100644 (file)
@@ -308,6 +308,28 @@ ethertype_to_dpo_proto (const ethernet_header_t * eh0)
   return (DPO_PROTO_NONE);
 }
 
+/**
+ * per-packet trace data
+ */
+typedef struct gbp_lpm_classify_trace_t_
+{
+  sclass_t sclass;
+  index_t lbi;
+} gbp_lpm_classify_trace_t;
+
+/* packet trace format function */
+static u8 *
+format_gbp_lpm_classify_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  gbp_lpm_classify_trace_t *t = va_arg (*args, gbp_lpm_classify_trace_t *);
+
+  s = format (s, "sclass:%d lb:%d", t->sclass, t->lbi);
+
+  return s;
+}
+
 /*
  * Determine the SRC EPG from a LPM
  */
@@ -335,8 +357,9 @@ gbp_lpm_classify_inline (vlib_main_t * vm,
        {
          u32 bi0, sw_if_index0, fib_index0, lbi0;
          gbp_lpm_classify_next_t next0;
+         const ethernet_header_t *eh0;
          const gbp_policy_dpo_t *gpd0;
-         const gbp_ext_itf_t *gx0;
+         const gbp_endpoint_t *ge0;
          const gbp_recirc_t *gr0;
          const dpo_id_t *dpo0;
          load_balance_t *lb0;
@@ -355,6 +378,8 @@ gbp_lpm_classify_inline (vlib_main_t * vm,
          ip6_0 = NULL;
          next0 = GPB_LPM_CLASSIFY_DROP;
 
+         lbi0 = ~0;
+         eh0 = NULL;
          b0 = vlib_get_buffer (vm, bi0);
 
          sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
@@ -366,8 +391,6 @@ gbp_lpm_classify_inline (vlib_main_t * vm,
            ip6_0 = vlib_buffer_get_current (b0);
          else if (DPO_PROTO_ETHERNET == dproto)
            {
-             const ethernet_header_t *eh0;
-
              eh0 = vlib_buffer_get_current (b0);
 
              dproto = ethertype_to_dpo_proto (eh0);
@@ -393,17 +416,61 @@ gbp_lpm_classify_inline (vlib_main_t * vm,
            {
              gr0 = gbp_recirc_get (sw_if_index0);
              fib_index0 = gr0->gr_fib_index[dproto];
+             ge0 = NULL;
 
              vnet_feature_next (&next0, b0);
            }
          else
            {
-             gx0 = gbp_ext_itf_get (sw_if_index0);
-             fib_index0 = gx0->gx_fib_index[dproto];
+             if (NULL == eh0)
+               {
+                 /* packet should be l2 */
+                 sclass0 = SCLASS_INVALID;
+                 goto trace;
+               }
+
+             ge0 = gbp_endpoint_find_mac (eh0->src_address,
+                                          vnet_buffer (b0)->l2.bd_index);
+
+             if (NULL == ge0)
+               {
+                 /* 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 (DPO_PROTO_IP4 == dproto)
+               {
+                 ge0 =
+                   gbp_endpoint_find_ip4 (&ip4_0->src_address, fib_index0);
+               }
+             else if (DPO_PROTO_IP6 == dproto)
+               {
+                 ge0 =
+                   gbp_endpoint_find_ip6 (&ip6_0->src_address, fib_index0);
+               }
 
              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 != ge0)
+               {
+                 sclass0 = ge0->ge_fwd.gef_sclass;
+                 goto trace;
+               }
            }
 
          if (DPO_PROTO_IP4 == dproto)
@@ -420,6 +487,7 @@ gbp_lpm_classify_inline (vlib_main_t * vm,
            {
              /* not IP so no LPM classify possible */
              sclass0 = SCLASS_INVALID;
+             next0 = GPB_LPM_CLASSIFY_DROP;
              goto trace;
            }
          lb0 = load_balance_get (lbi0);
@@ -434,7 +502,7 @@ gbp_lpm_classify_inline (vlib_main_t * vm,
            {
              /* could not classify => drop */
              sclass0 = SCLASS_INVALID;
-             next0 = GPB_LPM_CLASSIFY_DROP;
+             goto trace;
            }
 
        trace:
@@ -442,9 +510,10 @@ gbp_lpm_classify_inline (vlib_main_t * vm,
 
          if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
            {
-             gbp_classify_trace_t *t =
+             gbp_lpm_classify_trace_t *t =
                vlib_add_trace (vm, node, b0, sizeof (*t));
              t->sclass = sclass0;
+             t->lbi = lbi0;
            }
 
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
@@ -483,7 +552,7 @@ VLIB_NODE_FN (gbp_l2_lpm_classify_node) (vlib_main_t * vm,
 VLIB_REGISTER_NODE (gbp_ip4_lpm_classify_node) = {
   .name = "ip4-gbp-lpm-classify",
   .vector_size = sizeof (u32),
-  .format_trace = format_gbp_classify_trace,
+  .format_trace = format_gbp_lpm_classify_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
 
   .n_errors = 0,
@@ -496,7 +565,7 @@ VLIB_REGISTER_NODE (gbp_ip4_lpm_classify_node) = {
 VLIB_REGISTER_NODE (gbp_ip6_lpm_classify_node) = {
   .name = "ip6-gbp-lpm-classify",
   .vector_size = sizeof (u32),
-  .format_trace = format_gbp_classify_trace,
+  .format_trace = format_gbp_lpm_classify_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
 
   .n_errors = 0,
@@ -509,7 +578,7 @@ VLIB_REGISTER_NODE (gbp_ip6_lpm_classify_node) = {
 VLIB_REGISTER_NODE (gbp_l2_lpm_classify_node) = {
   .name = "l2-gbp-lpm-classify",
   .vector_size = sizeof (u32),
-  .format_trace = format_gbp_classify_trace,
+  .format_trace = format_gbp_lpm_classify_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
 
   .n_errors = 0,
index 36e0050..2472199 100644 (file)
@@ -254,7 +254,8 @@ gbp_endpoint_alloc (const ip46_address_t * ips,
   fib_node_init (&ge->ge_node, gbp_endpoint_fib_type);
   gei = gbp_endpoint_index (ge);
   ge->ge_key.gek_gbd =
-    ge->ge_key.gek_grd = ge->ge_fwd.gef_itf = INDEX_INVALID;
+    ge->ge_key.gek_grd =
+    ge->ge_fwd.gef_itf = ge->ge_fwd.gef_fib_index = INDEX_INVALID;
   ge->ge_last_time = vlib_time_now (vlib_get_main ());
   ge->ge_key.gek_gbd = gbp_bridge_domain_index (gbd);
 
@@ -602,7 +603,7 @@ gbb_endpoint_fwd_reset (gbp_endpoint_t * ge)
     {
       l2fib_del_entry (ge->ge_key.gek_mac.bytes,
                       gbd->gb_bd_index, gef->gef_itf);
-      gbp_itf_set_l2_input_feature (gef->gef_itf, gei, (L2INPUT_FEAT_NONE));
+      gbp_itf_set_l2_input_feature (gef->gef_itf, gei, L2INPUT_FEAT_NONE);
       gbp_itf_set_l2_output_feature (gef->gef_itf, gei, L2OUTPUT_FEAT_NONE);
 
       gbp_itf_unlock (gef->gef_itf);
@@ -681,6 +682,7 @@ gbb_endpoint_fwd_recalc (gbp_endpoint_t * ge)
     rewrite = NULL;
     grd = gbp_route_domain_get (ge->ge_key.gek_grd);
     fib_index = grd->grd_fib_index[pfx->fp_proto];
+    gef->gef_fib_index = fib_index;
 
     bd_add_del_ip_mac (gbd->gb_bd_index, fib_proto_to_ip46 (pfx->fp_proto),
                       &pfx->fp_addr, &ge->ge_key.gek_mac, 1);
@@ -786,7 +788,12 @@ gbb_endpoint_fwd_recalc (gbp_endpoint_t * ge)
       }
   }
 
-  if (gbp_endpoint_is_local (ge) && !gbp_endpoint_is_external (ge))
+  if (gbp_endpoint_is_external (ge))
+    {
+      gbp_itf_set_l2_input_feature (gef->gef_itf, gei,
+                                   L2INPUT_FEAT_GBP_LPM_CLASSIFY);
+    }
+  else if (gbp_endpoint_is_local (ge))
     {
       /*
        * non-remote endpoints (i.e. those not arriving on iVXLAN
index aba1f9d..a0d354a 100644 (file)
@@ -168,6 +168,11 @@ typedef struct gbp_endpoint_fwd_t_
    */
   sclass_t gef_sclass;
 
+  /**
+   * FIB index the EP is in
+   */
+  u32 gef_fib_index;
+
   gbp_endpoint_flags_t gef_flags;
 } gbp_endpoint_fwd_t;
 
index 16cdaa8..be2d614 100644 (file)
@@ -60,7 +60,6 @@ gbp_ext_itf_add (u32 sw_if_index, u32 bd_id, u32 rd_id)
 
   if (INDEX_INVALID == gxi)
     {
-      gbp_bridge_domain_t *gb;
       gbp_route_domain_t *gr;
       fib_protocol_t fproto;
       index_t gbi, gri;
@@ -81,11 +80,11 @@ gbp_ext_itf_add (u32 sw_if_index, u32 bd_id, u32 rd_id)
       pool_get_zero (gbp_ext_itf_pool, gx);
       gxi = gx - gbp_ext_itf_pool;
 
-      gb = gbp_bridge_domain_get (gbi);
       gr = gbp_route_domain_get (gri);
 
       gx->gx_bd = gbi;
       gx->gx_rd = gri;
+      gx->gx_itf = sw_if_index;
 
       FOR_EACH_FIB_IP_PROTOCOL (fproto)
       {
@@ -93,10 +92,6 @@ gbp_ext_itf_add (u32 sw_if_index, u32 bd_id, u32 rd_id)
          gr->grd_fib_index[fib_proto_to_dpo (fproto)];
       }
 
-      gx->gx_itf = gbp_itf_add_and_lock (sw_if_index, gb->gb_bd_index);
-      gbp_itf_set_l2_input_feature (gx->gx_itf, (gxi | GBP_EXT_ITF_ID),
-                                   L2INPUT_FEAT_GBP_LPM_CLASSIFY);
-
       gbp_ext_itf_db[sw_if_index] = gxi;
 
       GBP_EXT_ITF_DBG ("add: %U", format_gbp_ext_itf, gx);
@@ -124,11 +119,6 @@ gbp_ext_itf_delete (u32 sw_if_index)
 
       GBP_EXT_ITF_DBG ("del: %U", format_gbp_ext_itf, gx);
 
-      gbp_itf_set_l2_input_feature (gx->gx_itf,
-                                   (gxi | GBP_EXT_ITF_ID),
-                                   L2INPUT_FEAT_NONE);
-      gbp_itf_unlock (gx->gx_itf);
-
       gbp_route_domain_unlock (gx->gx_rd);
       gbp_bridge_domain_unlock (gx->gx_bd);
 
index 7cfe9da..4944f27 100644 (file)
@@ -194,15 +194,48 @@ format_gbp_itf (u8 * s, va_list * args)
 
   gi = gbp_itf_get (gii);
 
-  s = format (s, "%U locks:%d input-feats:%U output-feats:%U",
+  s = format (s, "%U locks:%d bd-index:%d input-feats:%U output-feats:%U",
              format_vnet_sw_if_index_name, vnet_get_main (),
              gi->gi_sw_if_index, gi->gi_locks,
+             gi->gi_bd_index,
              format_l2_input_features, gi->gi_l2_input_fb, 0,
              format_l2_output_features, gi->gi_l2_output_fb, 0);
 
   return (s);
 }
 
+static clib_error_t *
+gbp_itf_show (vlib_main_t * vm,
+             unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  u32 gii;
+
+  vlib_cli_output (vm, "Interfaces:");
+
+  vec_foreach_index (gii, gbp_itfs)
+  {
+    vlib_cli_output (vm, "  [%d] %U", gii, format_gbp_itf, gii);
+  }
+
+  return (NULL);
+}
+
+/*?
+ * Show Group Based Interfaces
+ *
+ * @cliexpar
+ * @cliexstart{show gbp contract}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_contract_show_node, static) = {
+  .path = "show gbp interface",
+  .short_help = "show gbp interface\n",
+  .function = gbp_itf_show,
+};
+/* *INDENT-ON* */
+
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index cf9ad25..4d83284 100644 (file)
@@ -5,7 +5,7 @@ import unittest
 
 from scapy.packet import Raw
 from scapy.layers.l2 import Ether, ARP, Dot1Q
-from scapy.layers.inet import IP, UDP
+from scapy.layers.inet import IP, UDP, ICMP
 from scapy.layers.inet6 import IPv6, ICMPv6ND_NS,  ICMPv6NDOptSrcLLAddr, \
     ICMPv6ND_NA
 from scapy.utils6 import in6_getnsma, in6_getnsmac
@@ -19,7 +19,7 @@ from vpp_interface import VppInterface
 from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable, \
     VppIpInterfaceAddress, VppIpInterfaceBind, find_route
 from vpp_l2 import VppBridgeDomain, VppBridgeDomainPort, \
-    VppBridgeDomainArpEntry, VppL2FibEntry, find_bridge_domain_port
+    VppBridgeDomainArpEntry, VppL2FibEntry, find_bridge_domain_port, VppL2Vtr
 from vpp_sub_interface import VppDot1QSubint
 from vpp_ip import VppIpAddress, VppIpPrefix
 from vpp_papi import VppEnum, MACAddress
@@ -67,10 +67,6 @@ class VppGbpEndpoint(VppObject):
     def mac(self):
         return str(self.vmac)
 
-    @property
-    def mac(self):
-        return self.itf.remote_mac
-
     @property
     def ip4(self):
         return self._ip4
@@ -3197,7 +3193,12 @@ class TestGBP(VppTestCase):
         #
         vlan_100 = VppDot1QSubint(self, self.pg0, 100)
         vlan_100.admin_up()
-        ext_itf = VppGbpExtItf(self, vlan_100, bd1, rd1)
+        VppL2Vtr(self, vlan_100, L2_VTR_OP.L2_POP_1).add_vpp_config()
+        vlan_101 = VppDot1QSubint(self, self.pg0, 101)
+        vlan_101.admin_up()
+        VppL2Vtr(self, vlan_101, L2_VTR_OP.L2_POP_1).add_vpp_config()
+
+        ext_itf = VppGbpExtItf(self, self.loop0, bd1, rd1)
         ext_itf.add_vpp_config()
 
         #
@@ -3209,16 +3210,45 @@ class TestGBP(VppTestCase):
             self.pg2.local_ip4)
         vx_tun_l3.add_vpp_config()
 
+        #
+        # External Endpoints
+        #
+        eep1 = VppGbpEndpoint(self, vlan_100,
+                              epg_220, None,
+                              "10.0.0.1", "11.0.0.1",
+                              "2001:10::1", "3001::1",
+                              ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL)
+        eep1.add_vpp_config()
+        eep2 = VppGbpEndpoint(self, vlan_101,
+                              epg_220, None,
+                              "10.0.0.2", "11.0.0.2",
+                              "2001:10::2", "3001::2",
+                              ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL)
+        eep2.add_vpp_config()
+
+        #
+        # A remote endpoint
+        #
+        rep = VppGbpEndpoint(self, vx_tun_l3,
+                             epg_220, None,
+                             "10.0.0.101", "11.0.0.101",
+                             "2001:10::101", "3001::101",
+                             ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
+                             self.pg7.local_ip4,
+                             self.pg7.remote_ip4,
+                             mac=None)
+        rep.add_vpp_config()
+
         #
         # packets destined to unknown addresses in the BVI's subnet
         # are ARP'd for
         #
-        p4 = (Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
+        p4 = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
               Dot1Q(vlan=100) /
               IP(src="10.0.0.1", dst="10.0.0.88") /
               UDP(sport=1234, dport=1234) /
               Raw('\xa5' * 100))
-        p6 = (Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
+        p6 = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
               Dot1Q(vlan=100) /
               IPv6(src="2001:10::1", dst="2001:10::88") /
               UDP(sport=1234, dport=1234) /
@@ -3243,29 +3273,6 @@ class TestGBP(VppTestCase):
 
             self.assertTrue(inner.haslayer(ARP))
 
-        #
-        # An external Endpoint
-        #
-        eep = VppGbpEndpoint(self, vlan_100,
-                             epg_220, None,
-                             "10.0.0.1", "11.0.0.1",
-                             "2001:10::1", "3001::1",
-                             ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL)
-        eep.add_vpp_config()
-
-        #
-        # A remote endpoint
-        #
-        rep = VppGbpEndpoint(self, vx_tun_l3,
-                             epg_220, None,
-                             "10.0.0.101", "11.0.0.101",
-                             "2001:10::101", "3001::101",
-                             ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
-                             self.pg7.local_ip4,
-                             self.pg7.remote_ip4,
-                             mac=None)
-        rep.add_vpp_config()
-
         #
         # remote to external
         #
@@ -3282,12 +3289,42 @@ class TestGBP(VppTestCase):
 
         rxs = self.send_and_expect(self.pg7, p * 1, self.pg0)
 
+        #
+        # local EP pings router
+        #
+        p = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
+             Dot1Q(vlan=100) /
+             IP(src=eep1.ip4.address, dst="10.0.0.128") /
+             ICMP(type='echo-request'))
+
+        rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
+
+        for rx in rxs:
+            self.assertEqual(rx[Ether].src, str(self.router_mac))
+            self.assertEqual(rx[Ether].dst, eep1.mac)
+            self.assertEqual(rx[Dot1Q].vlan, 100)
+
+        #
+        # local EP pings other local EP
+        #
+        p = (Ether(src=eep1.mac, dst=eep2.mac) /
+             Dot1Q(vlan=100) /
+             IP(src=eep1.ip4.address, dst=eep2.ip4.address) /
+             ICMP(type='echo-request'))
+
+        rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
+
+        for rx in rxs:
+            self.assertEqual(rx[Ether].src, eep1.mac)
+            self.assertEqual(rx[Ether].dst, eep2.mac)
+            self.assertEqual(rx[Dot1Q].vlan, 101)
+
         #
         # A subnet reachable through the external EP
         #
         ip_220 = VppIpRoute(self, "10.220.0.0", 24,
-                            [VppRoutePath(eep.ip4.address,
-                                          eep.epg.bvi.sw_if_index)],
+                            [VppRoutePath(eep1.ip4.address,
+                                          eep1.epg.bvi.sw_if_index)],
                             table_id=t4.table_id)
         ip_220.add_vpp_config()
 
@@ -3310,10 +3347,41 @@ class TestGBP(VppTestCase):
 
         rxs = self.send_and_expect(self.pg7, p * 1, self.pg0)
 
+        #
+        # A subnet reachable through the external EP
+        #
+        ip_221 = VppIpRoute(self, "10.221.0.0", 24,
+                            [VppRoutePath(eep2.ip4.address,
+                                          eep2.epg.bvi.sw_if_index)],
+                            table_id=t4.table_id)
+        ip_221.add_vpp_config()
+
+        l3o_221 = VppGbpSubnet(
+            self, rd1, "10.221.0.0", 24,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+            epg=epg_220)
+        l3o_221.add_vpp_config()
+
+        #
+        # ping between hosts in remote subnets
+        #
+        p = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
+             Dot1Q(vlan=100) /
+             IP(src="10.220.0.1", dst="10.221.0.1") /
+             ICMP(type='echo-request'))
+
+        rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
+
+        for rx in rxs:
+            self.assertEqual(rx[Ether].src, str(self.router_mac))
+            self.assertEqual(rx[Ether].dst, eep2.mac)
+            self.assertEqual(rx[Dot1Q].vlan, 101)
+
         #
         # cleanup
         #
         self.pg7.unconfig_ip4()
+        vlan_100.set_vtr(L2_VTR_OP.L2_DISABLED)
 
 
 if __name__ == '__main__':
index 45b7d69..dc73e42 100644 (file)
@@ -208,3 +208,32 @@ class VppL2FibEntry(VppObject):
 
     def object_id(self):
         return "L2-Fib-Entry-%s-%s-%s" % (self.bd, self.mac, self.itf)
+
+
+class VppL2Vtr(VppObject):
+
+    def __init__(self, test, itf, op):
+        self._test = test
+        self.itf = itf
+        self.op = op
+
+    def add_vpp_config(self):
+        self.itf.set_vtr(self.op)
+        self._test.registry.register(self, self._test.logger)
+
+    def remove_vpp_config(self):
+        self.itf.set_vtr(L2_VTR_OP.L2_DISABLED)
+
+    def query_vpp_config(self):
+        ds = self._test.vapi.sw_interface_dump()
+        d = self.itf.get_interface_config_from_dump(ds)
+
+        if d is not None:
+            return (d.vtr_op == self.op)
+        return False
+
+    def __str__(self):
+        return self.object_id()
+
+    def object_id(self):
+        return "L2-vtr-%s-%d" % (str(self.itf), self.op)