GBP: L3 out fixes

Change-Id: I0562d597fd45c7ddcb6db42cf17d3ffb569eb140
Signed-off-by: Neale Ranns <nranns@cisco.com>
diff --git a/test/test_gbp.py b/test/test_gbp.py
index cf9ad25..4d83284 100644
--- a/test/test_gbp.py
+++ b/test/test_gbp.py
@@ -5,7 +5,7 @@
 
 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_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
@@ -68,10 +68,6 @@
         return str(self.vmac)
 
     @property
-    def mac(self):
-        return self.itf.remote_mac
-
-    @property
     def ip4(self):
         return self._ip4
 
@@ -3197,7 +3193,12 @@
         #
         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()
 
         #
@@ -3210,15 +3211,44 @@
         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) /
@@ -3244,29 +3274,6 @@
             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
         #
         p = (Ether(src=self.pg7.remote_mac,
@@ -3283,11 +3290,41 @@
         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()
 
@@ -3311,9 +3348,40 @@
         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__':
diff --git a/test/vpp_l2.py b/test/vpp_l2.py
index 45b7d69..dc73e42 100644
--- a/test/vpp_l2.py
+++ b/test/vpp_l2.py
@@ -208,3 +208,32 @@
 
     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)