Typos. A bunch of typos I've been collecting.
[vpp.git] / test / test_gbp.py
index 68bbfe4..74eb424 100644 (file)
@@ -1,41 +1,39 @@
 #!/usr/bin/env python
 
+from socket import AF_INET, AF_INET6
 import unittest
 
-from framework import VppTestCase, VppTestRunner
-from vpp_object import VppObject
-from vpp_neighbor import VppNeighbor
-from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable, \
-    VppIpInterfaceAddress, VppIpInterfaceBind, find_route
-from vpp_l2 import VppBridgeDomain, VppBridgeDomainPort, \
-    VppBridgeDomainArpEntry, VppL2FibEntry, find_bridge_domain_port
-from vpp_vxlan_gbp_tunnel import *
-from vpp_sub_interface import VppDot1QSubint
-
-from vpp_ip import *
-from vpp_mac import *
-from vpp_papi_provider import L2_PORT_TYPE
-from vpp_papi import VppEnum
-
 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
 from scapy.layers.vxlan import VXLAN
-
-from socket import AF_INET, AF_INET6
+from scapy.data import ETH_P_IP, ETH_P_IPV6
 from scapy.utils import inet_pton, inet_ntop
-from util import mactobinary
+
+from framework import VppTestCase, VppTestRunner
+from vpp_object import VppObject
+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, VppL2Vtr
+from vpp_sub_interface import VppDot1QSubint
+from vpp_ip import VppIpAddress, VppIpPrefix
+from vpp_papi import VppEnum, MACAddress
 from vpp_papi_provider import L2_VTR_OP
+from vpp_vxlan_gbp_tunnel import find_vxlan_gbp_tunnel, INDEX_INVALID, \
+    VppVxlanGbpTunnel
+from vpp_neighbor import VppNeighbor
 
 
 def find_gbp_endpoint(test, sw_if_index=None, ip=None, mac=None):
     if ip:
         vip = VppIpAddress(ip)
     if mac:
-        vmac = VppMacAddress(mac)
+        vmac = MACAddress(mac)
 
     eps = test.vapi.gbp_endpoint_dump()
 
@@ -48,7 +46,7 @@ def find_gbp_endpoint(test, sw_if_index=None, ip=None, mac=None):
                 if vip == eip:
                     return True
         if mac:
-            if vmac == ep.endpoint.mac:
+            if vmac.packed == ep.endpoint.mac:
                 return True
     return False
 
@@ -66,17 +64,9 @@ class VppGbpEndpoint(VppObject):
     GBP Endpoint
     """
 
-    @property
-    def bin_mac(self):
-        return self.vmac.bytes
-
     @property
     def mac(self):
-        return self.vmac.address
-
-    @property
-    def mac(self):
-        return self.itf.remote_mac
+        return str(self.vmac)
 
     @property
     def ip4(self):
@@ -118,9 +108,9 @@ class VppGbpEndpoint(VppObject):
         self._fip6 = VppIpAddress(fip6)
 
         if mac:
-            self.vmac = VppMacAddress(self.itf.remote_mac)
+            self.vmac = MACAddress(self.itf.remote_mac)
         else:
-            self.vmac = VppMacAddress("00:00:00:00:00:00")
+            self.vmac = MACAddress("00:00:00:00:00:00")
 
         self.flags = flags
         self.tun_src = VppIpAddress(tun_src)
@@ -130,8 +120,8 @@ class VppGbpEndpoint(VppObject):
         res = self._test.vapi.gbp_endpoint_add(
             self.itf.sw_if_index,
             [self.ip4.encode(), self.ip6.encode()],
-            self.vmac.encode(),
-            self.epg.epg,
+            self.vmac.packed,
+            self.epg.sclass,
             self.flags,
             self.tun_src.encode(),
             self.tun_dst.encode())
@@ -148,7 +138,7 @@ class VppGbpEndpoint(VppObject):
         return "gbp-endpoint:[%d==%d:%s:%d]" % (self.handle,
                                                 self.itf.sw_if_index,
                                                 self.ip4.address,
-                                                self.epg.epg)
+                                                self.epg.sclass)
 
     def query_vpp_config(self):
         return find_gbp_endpoint(self._test,
@@ -171,7 +161,7 @@ class VppGbpRecirc(VppObject):
         self._test.vapi.gbp_recirc_add_del(
             1,
             self.recirc.sw_if_index,
-            self.epg.epg,
+            self.epg.sclass,
             self.is_ext)
         self._test.registry.register(self, self._test.logger)
 
@@ -179,7 +169,7 @@ class VppGbpRecirc(VppObject):
         self._test.vapi.gbp_recirc_add_del(
             0,
             self.recirc.sw_if_index,
-            self.epg.epg,
+            self.epg.sclass,
             self.is_ext)
 
     def __str__(self):
@@ -196,18 +186,58 @@ class VppGbpRecirc(VppObject):
         return False
 
 
+class VppGbpExtItf(VppObject):
+    """
+    GBP ExtItfulation Interface
+    """
+
+    def __init__(self, test, itf, bd, rd):
+        self._test = test
+        self.itf = itf
+        self.bd = bd
+        self.rd = rd
+
+    def add_vpp_config(self):
+        self._test.vapi.gbp_ext_itf_add_del(
+            1,
+            self.itf.sw_if_index,
+            self.bd.bd_id,
+            self.rd.rd_id)
+        self._test.registry.register(self, self._test.logger)
+
+    def remove_vpp_config(self):
+        self._test.vapi.gbp_ext_itf_add_del(
+            0,
+            self.itf.sw_if_index,
+            self.bd.bd_id,
+            self.rd.rd_id)
+
+    def __str__(self):
+        return self.object_id()
+
+    def object_id(self):
+        return "gbp-ext-itf:[%d]" % (self.itf.sw_if_index)
+
+    def query_vpp_config(self):
+        rs = self._test.vapi.gbp_ext_itf_dump()
+        for r in rs:
+            if r.ext_itf.sw_if_index == self.itf.sw_if_index:
+                return True
+        return False
+
+
 class VppGbpSubnet(VppObject):
     """
     GBP Subnet
     """
     def __init__(self, test, rd, address, address_len,
-                 type, sw_if_index=None, epg=None):
+                 type, sw_if_index=None, sclass=None):
         self._test = test
         self.rd_id = rd.rd_id
         self.prefix = VppIpPrefix(address, address_len)
         self.type = type
         self.sw_if_index = sw_if_index
-        self.epg = epg
+        self.sclass = sclass
 
     def add_vpp_config(self):
         self._test.vapi.gbp_subnet_add_del(
@@ -216,7 +246,7 @@ class VppGbpSubnet(VppObject):
             self.prefix.encode(),
             self.type,
             sw_if_index=self.sw_if_index if self.sw_if_index else 0xffffffff,
-            epg_id=self.epg if self.epg else 0xffff)
+            sclass=self.sclass if self.sclass else 0xffff)
         self._test.registry.register(self, self._test.logger)
 
     def remove_vpp_config(self):
@@ -242,44 +272,58 @@ class VppGbpSubnet(VppObject):
         return False
 
 
+class VppGbpEndpointRetention(object):
+    def __init__(self, remote_ep_timeout=0xffffffff):
+        self.remote_ep_timeout = remote_ep_timeout
+
+    def encode(self):
+        return {'remote_ep_timeout': self.remote_ep_timeout}
+
+
 class VppGbpEndpointGroup(VppObject):
     """
     GBP Endpoint Group
     """
 
-    def __init__(self, test, epg, rd, bd, uplink,
-                 bvi, bvi_ip4, bvi_ip6=None):
+    def __init__(self, test, vnid, sclass, rd, bd, uplink,
+                 bvi, bvi_ip4, bvi_ip6=None,
+                 retention=VppGbpEndpointRetention()):
         self._test = test
         self.uplink = uplink
         self.bvi = bvi
         self.bvi_ip4 = VppIpAddress(bvi_ip4)
         self.bvi_ip6 = VppIpAddress(bvi_ip6)
-        self.epg = epg
+        self.vnid = vnid
         self.bd = bd
         self.rd = rd
+        self.sclass = sclass
+        if 0 == self.sclass:
+            self.sclass = 0xffff
+        self.retention = retention
 
     def add_vpp_config(self):
         self._test.vapi.gbp_endpoint_group_add(
-            self.epg,
+            self.vnid,
+            self.sclass,
             self.bd.bd.bd_id,
             self.rd.rd_id,
-            self.uplink.sw_if_index if self.uplink else INDEX_INVALID)
+            self.uplink.sw_if_index if self.uplink else INDEX_INVALID,
+            self.retention.encode())
         self._test.registry.register(self, self._test.logger)
 
     def remove_vpp_config(self):
-        self._test.vapi.gbp_endpoint_group_del(
-            self.epg)
+        self._test.vapi.gbp_endpoint_group_del(self.sclass)
 
     def __str__(self):
         return self.object_id()
 
     def object_id(self):
-        return "gbp-endpoint-group:[%d]" % (self.epg)
+        return "gbp-endpoint-group:[%d]" % (self.vnid)
 
     def query_vpp_config(self):
         epgs = self._test.vapi.gbp_endpoint_group_dump()
         for epg in epgs:
-            if epg.epg.epg_id == self.epg:
+            if epg.epg.vnid == self.vnid:
                 return True
         return False
 
@@ -289,10 +333,12 @@ class VppGbpBridgeDomain(VppObject):
     GBP Bridge Domain
     """
 
-    def __init__(self, test, bd, bvi, uu_flood=None, learn=True):
+    def __init__(self, test, bd, bvi, uu_fwd=None,
+                 bm_flood=None, learn=True):
         self._test = test
         self.bvi = bvi
-        self.uu_flood = uu_flood
+        self.uu_fwd = uu_fwd
+        self.bm_flood = bm_flood
         self.bd = bd
 
         e = VppEnum.vl_api_gbp_bridge_domain_flags_t
@@ -306,7 +352,8 @@ class VppGbpBridgeDomain(VppObject):
             self.bd.bd_id,
             self.learn,
             self.bvi.sw_if_index,
-            self.uu_flood.sw_if_index if self.uu_flood else INDEX_INVALID)
+            self.uu_fwd.sw_if_index if self.uu_fwd else INDEX_INVALID,
+            self.bm_flood.sw_if_index if self.bm_flood else INDEX_INVALID)
         self._test.registry.register(self, self._test.logger)
 
     def remove_vpp_config(self):
@@ -374,7 +421,7 @@ class VppGbpContractNextHop():
 
     def encode(self):
         return {'ip': self.ip.encode(),
-                'mac': self.mac.encode(),
+                'mac': self.mac.packed,
                 'bd_id': self.bd.bd.bd_id,
                 'rd_id': self.rd.rd_id}
 
@@ -403,12 +450,16 @@ class VppGbpContract(VppObject):
     GBP Contract
     """
 
-    def __init__(self, test, src_epg, dst_epg, acl_index, rules=[]):
+    def __init__(self, test, sclass, dclass, acl_index,
+                 rules, allowed_ethertypes):
         self._test = test
         self.acl_index = acl_index
-        self.src_epg = src_epg
-        self.dst_epg = dst_epg
+        self.sclass = sclass
+        self.dclass = dclass
         self.rules = rules
+        self.allowed_ethertypes = allowed_ethertypes
+        while (len(self.allowed_ethertypes) < 16):
+            self.allowed_ethertypes.append(0)
 
     def add_vpp_config(self):
         rules = []
@@ -416,33 +467,35 @@ class VppGbpContract(VppObject):
             rules.append(r.encode())
         self._test.vapi.gbp_contract_add_del(
             1,
-            self.src_epg,
-            self.dst_epg,
+            self.sclass,
+            self.dclass,
             self.acl_index,
-            rules)
+            rules,
+            self.allowed_ethertypes)
         self._test.registry.register(self, self._test.logger)
 
     def remove_vpp_config(self):
         self._test.vapi.gbp_contract_add_del(
             0,
-            self.src_epg,
-            self.dst_epg,
+            self.sclass,
+            self.dclass,
             self.acl_index,
-            [])
+            [],
+            self.allowed_ethertypes)
 
     def __str__(self):
         return self.object_id()
 
     def object_id(self):
-        return "gbp-contract:[%d:%s:%d]" % (self.src_epg,
-                                            self.dst_epg,
+        return "gbp-contract:[%d:%s:%d]" % (self.sclass,
+                                            self.dclass,
                                             self.acl_index)
 
     def query_vpp_config(self):
         cs = self._test.vapi.gbp_contract_dump()
         for c in cs:
-            if c.contract.src_epg == self.src_epg \
-               and c.contract.dst_epg == self.dst_epg:
+            if c.contract.sclass == self.sclass \
+               and c.contract.dclass == self.dclass:
                 return True
         return False
 
@@ -452,18 +505,20 @@ class VppGbpVxlanTunnel(VppInterface):
     GBP VXLAN tunnel
     """
 
-    def __init__(self, test, vni, bd_rd_id, mode):
+    def __init__(self, test, vni, bd_rd_id, mode, src):
         super(VppGbpVxlanTunnel, self).__init__(test)
         self._test = test
         self.vni = vni
         self.bd_rd_id = bd_rd_id
         self.mode = mode
+        self.src = src
 
     def add_vpp_config(self):
         r = self._test.vapi.gbp_vxlan_tunnel_add(
             self.vni,
             self.bd_rd_id,
-            self.mode)
+            self.mode,
+            self.src)
         self.set_sw_if_index(r.sw_if_index)
         self._test.registry.register(self, self._test.logger)
 
@@ -474,7 +529,7 @@ class VppGbpVxlanTunnel(VppInterface):
         return self.object_id()
 
     def object_id(self):
-        return "gbp-vxlan:%d" % (self.vni)
+        return "gbp-vxlan:%d" % (self.sw_if_index)
 
     def query_vpp_config(self):
         return find_gbp_vxlan(self._test, self.vni)
@@ -490,8 +545,8 @@ class VppGbpAcl(VppObject):
         self.acl_index = 4294967295
 
     def create_rule(self, is_ipv6=0, permit_deny=0, proto=-1,
-                    s_prefix=0, s_ip='\x00\x00\x00\x00', sport_from=0,
-                    sport_to=65535, d_prefix=0, d_ip='\x00\x00\x00\x00',
+                    s_prefix=0, s_ip=b'\x00\x00\x00\x00', sport_from=0,
+                    sport_to=65535, d_prefix=0, d_ip=b'\x00\x00\x00\x00',
                     dport_from=0, dport_to=65535):
         if proto == -1 or proto == 0:
             sport_to = 0
@@ -514,7 +569,7 @@ class VppGbpAcl(VppObject):
 
         reply = self._test.vapi.acl_add_replace(self.acl_index,
                                                 r=rules,
-                                                tag='GBPTest')
+                                                tag=b'GBPTest')
         self.acl_index = reply.acl_index
         return self.acl_index
 
@@ -544,7 +599,7 @@ class TestGBP(VppTestCase):
         self.create_pg_interfaces(range(9))
         self.create_loopback_interfaces(8)
 
-        self.router_mac = VppMacAddress("00:11:22:33:44:55")
+        self.router_mac = MACAddress("00:11:22:33:44:55")
 
         for i in self.pg_interfaces:
             i.admin_up()
@@ -631,7 +686,7 @@ class TestGBP(VppTestCase):
         rx = self.send_and_expect(src, tx, dst)
 
         for r in rx:
-            self.assertEqual(r[Ether].src, self.router_mac.address)
+            self.assertEqual(r[Ether].src, str(self.router_mac))
             self.assertEqual(r[Ether].dst, dst.remote_mac)
             self.assertEqual(r[IP].dst, dst_ip)
             self.assertEqual(r[IP].src, src_ip)
@@ -641,7 +696,7 @@ class TestGBP(VppTestCase):
         rx = self.send_and_expect(src, tx, dst)
 
         for r in rx:
-            self.assertEqual(r[Ether].src, self.router_mac.address)
+            self.assertEqual(r[Ether].src, str(self.router_mac))
             self.assertEqual(r[Ether].dst, dst.remote_mac)
             self.assertEqual(r[IPv6].dst, dst_ip)
             self.assertEqual(r[IPv6].src, src_ip)
@@ -650,6 +705,8 @@ class TestGBP(VppTestCase):
     def test_gbp(self):
         """ Group Based Policy """
 
+        ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
+
         #
         # Bridge Domains
         #
@@ -691,36 +748,26 @@ class TestGBP(VppTestCase):
         # 3 EPGs, 2 of which share a BD.
         # 2 NAT EPGs, one for floating-IP subnets, the other for internet
         #
-        epgs = [VppGbpEndpointGroup(self, 220, rd0, gbd1, self.pg4,
-                                    self.loop0,
-                                    "10.0.0.128",
-                                    "2001:10::128"),
-                VppGbpEndpointGroup(self, 221, rd0, gbd1, self.pg5,
-                                    self.loop0,
-                                    "10.0.1.128",
-                                    "2001:10:1::128"),
-                VppGbpEndpointGroup(self, 222, rd0, gbd2, self.pg6,
-                                    self.loop1,
-                                    "10.0.2.128",
-                                    "2001:10:2::128"),
-                VppGbpEndpointGroup(self, 333, rd20, gbd20, self.pg7,
-                                    self.loop2,
-                                    "11.0.0.128",
-                                    "3001::128"),
-                VppGbpEndpointGroup(self, 444, rd20, gbd20, self.pg8,
-                                    self.loop2,
-                                    "11.0.0.129",
-                                    "3001::129")]
-        recircs = [VppGbpRecirc(self, epgs[0],
-                                self.loop3),
-                   VppGbpRecirc(self, epgs[1],
-                                self.loop4),
-                   VppGbpRecirc(self, epgs[2],
-                                self.loop5),
-                   VppGbpRecirc(self, epgs[3],
-                                self.loop6, is_ext=True),
-                   VppGbpRecirc(self, epgs[4],
-                                self.loop7, is_ext=True)]
+        epgs = [VppGbpEndpointGroup(self, 220, 1220, rd0, gbd1,
+                                    self.pg4, self.loop0,
+                                    "10.0.0.128", "2001:10::128"),
+                VppGbpEndpointGroup(self, 221, 1221, rd0, gbd1,
+                                    self.pg5, self.loop0,
+                                    "10.0.1.128", "2001:10:1::128"),
+                VppGbpEndpointGroup(self, 222, 1222, rd0, gbd2,
+                                    self.pg6, self.loop1,
+                                    "10.0.2.128", "2001:10:2::128"),
+                VppGbpEndpointGroup(self, 333, 1333, rd20, gbd20,
+                                    self.pg7, self.loop2,
+                                    "11.0.0.128", "3001::128"),
+                VppGbpEndpointGroup(self, 444, 1444, rd20, gbd20,
+                                    self.pg8, self.loop2,
+                                    "11.0.0.129", "3001::129")]
+        recircs = [VppGbpRecirc(self, epgs[0], self.loop3),
+                   VppGbpRecirc(self, epgs[1], self.loop4),
+                   VppGbpRecirc(self, epgs[2], self.loop5),
+                   VppGbpRecirc(self, epgs[3], self.loop6, is_ext=True),
+                   VppGbpRecirc(self, epgs[4], self.loop7, is_ext=True)]
 
         epg_nat = epgs[3]
         recirc_nat = recircs[3]
@@ -755,7 +802,7 @@ class TestGBP(VppTestCase):
                 VppIpInterfaceBind(self, epg.bvi, epg.rd.t6).add_vpp_config()
                 self.vapi.sw_interface_set_mac_address(
                     epg.bvi.sw_if_index,
-                    self.router_mac.bytes)
+                    self.router_mac.packed)
 
                 # The BVIs are NAT inside interfaces
                 self.vapi.nat44_interface_add_del_feature(epg.bvi.sw_if_index,
@@ -776,10 +823,10 @@ class TestGBP(VppTestCase):
 
             # add the BD ARP termination entry for BVI IP
             epg.bd_arp_ip4 = VppBridgeDomainArpEntry(self, epg.bd.bd,
-                                                     self.router_mac.address,
+                                                     str(self.router_mac),
                                                      epg.bvi_ip4)
             epg.bd_arp_ip6 = VppBridgeDomainArpEntry(self, epg.bd.bd,
-                                                     self.router_mac.address,
+                                                     str(self.router_mac),
                                                      epg.bvi_ip6)
             epg.bd_arp_ip4.add_vpp_config()
             epg.bd_arp_ip6.add_vpp_config()
@@ -794,8 +841,6 @@ class TestGBP(VppTestCase):
             VppIpInterfaceBind(self, recirc.recirc,
                                recirc.epg.rd.t6).add_vpp_config()
 
-            self.vapi.sw_interface_set_l2_emulation(
-                recirc.recirc.sw_if_index)
             self.vapi.nat44_interface_add_del_feature(
                 recirc.recirc.sw_if_index,
                 is_inside=0,
@@ -937,13 +982,13 @@ class TestGBP(VppTestCase):
         # packets to non-local L3 destinations dropped
         #
         pkt_intra_epg_220_ip4 = (Ether(src=self.pg0.remote_mac,
-                                       dst=self.router_mac.address) /
+                                       dst=str(self.router_mac)) /
                                  IP(src=eps[0].ip4.address,
                                     dst="10.0.0.99") /
                                  UDP(sport=1234, dport=1234) /
                                  Raw('\xa5' * 100))
         pkt_inter_epg_222_ip4 = (Ether(src=self.pg0.remote_mac,
-                                       dst=self.router_mac.address) /
+                                       dst=str(self.router_mac)) /
                                  IP(src=eps[0].ip4.address,
                                     dst="10.0.1.99") /
                                  UDP(sport=1234, dport=1234) /
@@ -952,7 +997,7 @@ class TestGBP(VppTestCase):
         self.send_and_assert_no_replies(self.pg0, pkt_intra_epg_220_ip4 * 65)
 
         pkt_inter_epg_222_ip6 = (Ether(src=self.pg0.remote_mac,
-                                       dst=self.router_mac.address) /
+                                       dst=str(self.router_mac)) /
                                  IPv6(src=eps[0].ip6.address,
                                       dst="2001:10::99") /
                                  UDP(sport=1234, dport=1234) /
@@ -1064,7 +1109,7 @@ class TestGBP(VppTestCase):
         self.send_and_expect_bridged(self.pg0, pkt_intra_epg * 65, self.pg1)
 
         #
-        # in the abscense of policy, endpoints in the different EPG
+        # in the absence of policy, endpoints in the different EPG
         # cannot communicate
         #
         pkt_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac,
@@ -1080,7 +1125,7 @@ class TestGBP(VppTestCase):
                                     UDP(sport=1234, dport=1234) /
                                     Raw('\xa5' * 100))
         pkt_inter_epg_220_to_222 = (Ether(src=self.pg0.remote_mac,
-                                          dst=self.router_mac.address) /
+                                          dst=str(self.router_mac)) /
                                     IP(src=eps[0].ip4.address,
                                        dst=eps[3].ip4.address) /
                                     UDP(sport=1234, dport=1234) /
@@ -1099,13 +1144,14 @@ class TestGBP(VppTestCase):
         rule2 = acl.create_rule(is_ipv6=1, permit_deny=1, proto=17)
         acl_index = acl.add_vpp_config([rule, rule2])
         c1 = VppGbpContract(
-            self, 220, 221, acl_index,
+            self, epgs[0].sclass, epgs[1].sclass, acl_index,
             [VppGbpContractRule(
                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
                 []),
              VppGbpContractRule(
                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
-                 [])])
+                 [])],
+            [ETH_P_IP, ETH_P_IPV6])
         c1.add_vpp_config()
 
         self.send_and_expect_bridged(eps[0].itf,
@@ -1118,14 +1164,14 @@ class TestGBP(VppTestCase):
         # contract for the return direction
         #
         c2 = VppGbpContract(
-            self, 221, 220, acl_index,
+            self, epgs[1].sclass, epgs[0].sclass, acl_index,
             [VppGbpContractRule(
                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
                 []),
              VppGbpContractRule(
                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
-                 [])])
-
+                 [])],
+            [ETH_P_IP, ETH_P_IPV6])
         c2.add_vpp_config()
 
         self.send_and_expect_bridged(eps[0].itf,
@@ -1135,6 +1181,15 @@ class TestGBP(VppTestCase):
                                      pkt_inter_epg_221_to_220 * 65,
                                      eps[0].itf)
 
+        #
+        # the contract does not allow non-IP
+        #
+        pkt_non_ip_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac,
+                                                 dst=self.pg2.remote_mac) /
+                                           ARP())
+        self.send_and_assert_no_replies(eps[0].itf,
+                                        pkt_non_ip_inter_epg_220_to_221 * 17)
+
         #
         # check that inter group is still disabled for the groups
         # not in the contract.
@@ -1146,14 +1201,14 @@ class TestGBP(VppTestCase):
         # A uni-directional contract from EPG 220 -> 222 'L3 routed'
         #
         c3 = VppGbpContract(
-            self, 220, 222, acl_index,
+            self, epgs[0].sclass, epgs[2].sclass, acl_index,
             [VppGbpContractRule(
                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
                 []),
              VppGbpContractRule(
                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
-                 [])])
-
+                 [])],
+            [ETH_P_IP, ETH_P_IPV6])
         c3.add_vpp_config()
 
         self.logger.info(self.vapi.cli("sh gbp contract"))
@@ -1161,7 +1216,7 @@ class TestGBP(VppTestCase):
         self.send_and_expect_routed(eps[0].itf,
                                     pkt_inter_epg_220_to_222 * 65,
                                     eps[3].itf,
-                                    self.router_mac.address)
+                                    str(self.router_mac))
 
         #
         # remove both contracts, traffic stops in both directions
@@ -1188,33 +1243,33 @@ class TestGBP(VppTestCase):
             self, rd0, "0.0.0.0", 0,
             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
             sw_if_index=recirc_nat.recirc.sw_if_index,
-            epg=epg_nat.epg)
+            sclass=epg_nat.sclass)
         se2 = VppGbpSubnet(
             self, rd0, "11.0.0.0", 8,
             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
             sw_if_index=recirc_nat.recirc.sw_if_index,
-            epg=epg_nat.epg)
+            sclass=epg_nat.sclass)
         se16 = VppGbpSubnet(
             self, rd0, "::", 0,
             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
             sw_if_index=recirc_nat.recirc.sw_if_index,
-            epg=epg_nat.epg)
+            sclass=epg_nat.sclass)
         # in the NAT RD an external subnet via the NAT EPG's uplink
         se3 = VppGbpSubnet(
             self, rd20, "0.0.0.0", 0,
             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
             sw_if_index=epg_nat.uplink.sw_if_index,
-            epg=epg_nat.epg)
+            sclass=epg_nat.sclass)
         se36 = VppGbpSubnet(
             self, rd20, "::", 0,
             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
             sw_if_index=epg_nat.uplink.sw_if_index,
-            epg=epg_nat.epg)
+            sclass=epg_nat.sclass)
         se4 = VppGbpSubnet(
             self, rd20, "11.0.0.0", 8,
             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
             sw_if_index=epg_nat.uplink.sw_if_index,
-            epg=epg_nat.epg)
+            sclass=epg_nat.sclass)
         se1.add_vpp_config()
         se2.add_vpp_config()
         se16.add_vpp_config()
@@ -1229,10 +1284,10 @@ class TestGBP(VppTestCase):
                                        eps[0].fip6))
 
         #
-        # From an EP to an outside addess: IN2OUT
+        # From an EP to an outside address: IN2OUT
         #
         pkt_inter_epg_220_to_global = (Ether(src=self.pg0.remote_mac,
-                                             dst=self.router_mac.address) /
+                                             dst=str(self.router_mac)) /
                                        IP(src=eps[0].ip4.address,
                                           dst="1.1.1.1") /
                                        UDP(sport=1234, dport=1234) /
@@ -1251,14 +1306,14 @@ class TestGBP(VppTestCase):
 
         acl_index2 = acl2.add_vpp_config([rule, rule2])
         c4 = VppGbpContract(
-            self, 220, 333, acl_index2,
+            self, epgs[0].sclass, epgs[3].sclass, acl_index2,
             [VppGbpContractRule(
                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
                 []),
              VppGbpContractRule(
                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
-                 [])])
-
+                 [])],
+            [ETH_P_IP, ETH_P_IPV6])
         c4.add_vpp_config()
 
         self.send_and_expect_natted(eps[0].itf,
@@ -1267,7 +1322,7 @@ class TestGBP(VppTestCase):
                                     eps[0].fip4.address)
 
         pkt_inter_epg_220_to_global = (Ether(src=self.pg0.remote_mac,
-                                             dst=self.router_mac.address) /
+                                             dst=str(self.router_mac)) /
                                        IPv6(src=eps[0].ip6.address,
                                             dst="6001::1") /
                                        UDP(sport=1234, dport=1234) /
@@ -1281,7 +1336,7 @@ class TestGBP(VppTestCase):
         #
         # From a global address to an EP: OUT2IN
         #
-        pkt_inter_epg_220_from_global = (Ether(src=self.router_mac.address,
+        pkt_inter_epg_220_from_global = (Ether(src=str(self.router_mac),
                                                dst=self.pg0.remote_mac) /
                                          IP(dst=eps[0].fip4.address,
                                             src="1.1.1.1") /
@@ -1292,14 +1347,14 @@ class TestGBP(VppTestCase):
                                         pkt_inter_epg_220_from_global * 65)
 
         c5 = VppGbpContract(
-            self, 333, 220, acl_index2,
+            self, epgs[3].sclass, epgs[0].sclass, acl_index2,
             [VppGbpContractRule(
                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
                 []),
              VppGbpContractRule(
                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
-                 [])])
-
+                 [])],
+            [ETH_P_IP, ETH_P_IPV6])
         c5.add_vpp_config()
 
         self.send_and_expect_unnatted(self.pg7,
@@ -1307,7 +1362,7 @@ class TestGBP(VppTestCase):
                                       eps[0].itf,
                                       eps[0].ip4.address)
 
-        pkt_inter_epg_220_from_global = (Ether(src=self.router_mac.address,
+        pkt_inter_epg_220_from_global = (Ether(src=str(self.router_mac),
                                                dst=self.pg0.remote_mac) /
                                          IPv6(dst=eps[0].fip6.address,
                                               src="6001::1") /
@@ -1324,7 +1379,7 @@ class TestGBP(VppTestCase):
         #  IN2OUT2IN
         #
         pkt_intra_epg_220_global = (Ether(src=self.pg0.remote_mac,
-                                          dst=self.router_mac.address) /
+                                          dst=str(self.router_mac)) /
                                     IP(src=eps[0].ip4.address,
                                        dst=eps[1].fip4.address) /
                                     UDP(sport=1234, dport=1234) /
@@ -1337,7 +1392,7 @@ class TestGBP(VppTestCase):
                                            eps[1].ip4.address)
 
         pkt_intra_epg_220_global = (Ether(src=self.pg0.remote_mac,
-                                          dst=self.router_mac.address) /
+                                          dst=str(self.router_mac)) /
                                     IPv6(src=eps[0].ip6.address,
                                          dst=eps[1].fip6.address) /
                                     UDP(sport=1234, dport=1234) /
@@ -1375,8 +1430,6 @@ class TestGBP(VppTestCase):
                                                   is_add=0)
 
         for recirc in recircs:
-            self.vapi.sw_interface_set_l2_emulation(
-                recirc.recirc.sw_if_index, enable=0)
             self.vapi.nat44_interface_add_del_feature(
                 recirc.recirc.sw_if_index,
                 is_inside=0,
@@ -1386,9 +1439,20 @@ class TestGBP(VppTestCase):
                 is_inside=0,
                 is_add=0)
 
+    def wait_for_ep_timeout(self, sw_if_index=None, ip=None, mac=None,
+                            n_tries=100, s_time=1):
+        while (n_tries):
+            if not find_gbp_endpoint(self, sw_if_index, ip, mac):
+                return True
+            n_tries = n_tries - 1
+            self.sleep(s_time)
+        self.assertFalse(find_gbp_endpoint(self, sw_if_index, ip, mac))
+        return False
+
     def test_gbp_learn_l2(self):
         """ GBP L2 Endpoint Learning """
 
+        ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
         learnt = [{'mac': '00:00:11:11:11:01',
                    'ip': '10.0.0.1',
                    'ip6': '2001:10::2'},
@@ -1396,12 +1460,6 @@ class TestGBP(VppTestCase):
                    'ip': '10.0.0.2',
                    'ip6': '2001:10::3'}]
 
-        #
-        # lower the inactive threshold so these tests pass in a
-        # reasonable amount of time
-        #
-        self.vapi.gbp_endpoint_learn_set_inactive_threshold(1)
-
         #
         # IP tables
         #
@@ -1427,12 +1485,20 @@ class TestGBP(VppTestCase):
         self.pg4.config_ip4()
         self.pg4.resolve_arp()
 
+        #
+        # Add a mcast destination VXLAN-GBP tunnel for B&M traffic
+        #
+        tun_bm = VppVxlanGbpTunnel(self, self.pg4.local_ip4,
+                                   "239.1.1.1", 88,
+                                   mcast_itf=self.pg4)
+        tun_bm.add_vpp_config()
+
         #
         # a GBP bridge domain with a BVI and a UU-flood interface
         #
         bd1 = VppBridgeDomain(self, 1)
         bd1.add_vpp_config()
-        gbd1 = VppGbpBridgeDomain(self, bd1, self.loop0, self.pg3)
+        gbd1 = VppGbpBridgeDomain(self, bd1, self.loop0, self.pg3, tun_bm)
         gbd1.add_vpp_config()
 
         self.logger.info(self.vapi.cli("sh bridge 1 detail"))
@@ -1445,24 +1511,27 @@ class TestGBP(VppTestCase):
         #
         # The Endpoint-group in which we are learning endpoints
         #
-        epg_220 = VppGbpEndpointGroup(self, 220, rd1, gbd1,
+        epg_220 = VppGbpEndpointGroup(self, 220, 112, rd1, gbd1,
                                       None, self.loop0,
                                       "10.0.0.128",
-                                      "2001:10::128")
+                                      "2001:10::128",
+                                      VppGbpEndpointRetention(2))
         epg_220.add_vpp_config()
-        epg_330 = VppGbpEndpointGroup(self, 330, rd1, gbd1,
+        epg_330 = VppGbpEndpointGroup(self, 330, 113, rd1, gbd1,
                                       None, self.loop1,
                                       "10.0.1.128",
-                                      "2001:11::128")
+                                      "2001:11::128",
+                                      VppGbpEndpointRetention(2))
         epg_330.add_vpp_config()
 
         #
         # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint
-        # leanring enabled
+        # learning enabled
         #
         vx_tun_l2_1 = VppGbpVxlanTunnel(
             self, 99, bd1.bd_id,
-            VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L2)
+            VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L2,
+            self.pg2.local_ip4)
         vx_tun_l2_1.add_vpp_config()
 
         #
@@ -1477,7 +1546,7 @@ class TestGBP(VppTestCase):
 
         self.assertTrue(find_route(self, ep.ip4.address, 32, table_id=1))
 
-        # a packet with an sclass from an unknwon EPG
+        # a packet with an sclass from an unknown EPG
         p = (Ether(src=self.pg2.remote_mac,
                    dst=self.pg2.local_mac) /
              IP(src=self.pg2.remote_hosts[0].ip4,
@@ -1501,18 +1570,21 @@ class TestGBP(VppTestCase):
                                                self.pg2.remote_hosts[0].ip4,
                                                99))
 
-        # epg is not learnt, becasue the EPG is unknwon
+        # epg is not learnt, because the EPG is unknwon
         self.assertEqual(len(self.vapi.gbp_endpoint_dump()), 1)
 
+        #
+        # Learn new EPs from IP packets
+        #
         for ii, l in enumerate(learnt):
-            # a packet with an sclass from a knwon EPG
+            # a packet with an sclass from a known EPG
             # arriving on an unknown TEP
             p = (Ether(src=self.pg2.remote_mac,
                        dst=self.pg2.local_mac) /
                  IP(src=self.pg2.remote_hosts[1].ip4,
                     dst=self.pg2.local_ip4) /
                  UDP(sport=1234, dport=48879) /
-                 VXLAN(vni=99, gpid=220, flags=0x88) /
+                 VXLAN(vni=99, gpid=112, flags=0x88) /
                  Ether(src=l['mac'], dst=ep.mac) /
                  IP(src=l['ip'], dst=ep.ip4.address) /
                  UDP(sport=1234, dport=1234) /
@@ -1541,33 +1613,116 @@ class TestGBP(VppTestCase):
 
         self.logger.info(self.vapi.cli("show gbp endpoint"))
         self.logger.info(self.vapi.cli("show gbp vxlan"))
-        self.logger.info(self.vapi.cli("show vxlan-gbp tunnel"))
+        self.logger.info(self.vapi.cli("show ip mfib"))
 
         #
         # If we sleep for the threshold time, the learnt endpoints should
         # age out
         #
-        self.sleep(2)
         for l in learnt:
-            self.assertFalse(find_gbp_endpoint(self,
-                                               tep1_sw_if_index,
-                                               mac=l['mac']))
+            self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index,
+                                     mac=l['mac'])
+
+        #
+        # Learn new EPs from GARP packets received on the BD's mcast tunnel
+        #
+        for ii, l in enumerate(learnt):
+            # a packet with an sclass from a known EPG
+            # arriving on an unknown TEP
+            p = (Ether(src=self.pg2.remote_mac,
+                       dst=self.pg2.local_mac) /
+                 IP(src=self.pg2.remote_hosts[1].ip4,
+                    dst="239.1.1.1") /
+                 UDP(sport=1234, dport=48879) /
+                 VXLAN(vni=88, gpid=112, flags=0x88) /
+                 Ether(src=l['mac'], dst="ff:ff:ff:ff:ff:ff") /
+                 ARP(op="who-has",
+                     psrc=l['ip'], pdst=l['ip'],
+                     hwsrc=l['mac'], hwdst="ff:ff:ff:ff:ff:ff"))
+
+            rx = self.send_and_expect(self.pg4, [p], self.pg0)
+
+            # the new TEP
+            tep1_sw_if_index = find_vxlan_gbp_tunnel(
+                self,
+                self.pg2.local_ip4,
+                self.pg2.remote_hosts[1].ip4,
+                99)
+            self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
+
+            #
+            # the EP is learnt via the learnt TEP
+            # both from its MAC and its IP
+            #
+            self.assertTrue(find_gbp_endpoint(self,
+                                              vx_tun_l2_1.sw_if_index,
+                                              mac=l['mac']))
+            self.assertTrue(find_gbp_endpoint(self,
+                                              vx_tun_l2_1.sw_if_index,
+                                              ip=l['ip']))
+
+        #
+        # wait for the learnt endpoints to age out
+        #
+        for l in learnt:
+            self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index,
+                                     mac=l['mac'])
+
+        #
+        # Learn new EPs from L2 packets
+        #
+        for ii, l in enumerate(learnt):
+            # a packet with an sclass from a known EPG
+            # arriving on an unknown TEP
+            p = (Ether(src=self.pg2.remote_mac,
+                       dst=self.pg2.local_mac) /
+                 IP(src=self.pg2.remote_hosts[1].ip4,
+                    dst=self.pg2.local_ip4) /
+                 UDP(sport=1234, dport=48879) /
+                 VXLAN(vni=99, gpid=112, flags=0x88) /
+                 Ether(src=l['mac'], dst=ep.mac) /
+                 Raw('\xa5' * 100))
+
+            rx = self.send_and_expect(self.pg2, [p], self.pg0)
+
+            # the new TEP
+            tep1_sw_if_index = find_vxlan_gbp_tunnel(
+                self,
+                self.pg2.local_ip4,
+                self.pg2.remote_hosts[1].ip4,
+                99)
+            self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
+
+            #
+            # the EP is learnt via the learnt TEP
+            # both from its MAC and its IP
+            #
+            self.assertTrue(find_gbp_endpoint(self,
+                                              vx_tun_l2_1.sw_if_index,
+                                              mac=l['mac']))
 
         self.logger.info(self.vapi.cli("show gbp endpoint"))
         self.logger.info(self.vapi.cli("show gbp vxlan"))
         self.logger.info(self.vapi.cli("show vxlan-gbp tunnel"))
 
+        #
+        # wait for the learnt endpoints to age out
+        #
+        for l in learnt:
+            self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index,
+                                     mac=l['mac'])
+
         #
         # repeat. the do not learn bit is set so the EPs are not learnt
         #
         for l in learnt:
-            # a packet with an sclass from a knwon EPG
+            # a packet with an sclass from a known EPG
             p = (Ether(src=self.pg2.remote_mac,
                        dst=self.pg2.local_mac) /
                  IP(src=self.pg2.remote_hosts[1].ip4,
                     dst=self.pg2.local_ip4) /
                  UDP(sport=1234, dport=48879) /
-                 VXLAN(vni=99, gpid=220, flags=0x88, gpflags="D") /
+                 VXLAN(vni=99, gpid=112, flags=0x88, gpflags="D") /
                  Ether(src=l['mac'], dst=ep.mac) /
                  IP(src=l['ip'], dst=ep.ip4.address) /
                  UDP(sport=1234, dport=1234) /
@@ -1584,13 +1739,13 @@ class TestGBP(VppTestCase):
         # repeat
         #
         for l in learnt:
-            # a packet with an sclass from a knwon EPG
+            # a packet with an sclass from a known EPG
             p = (Ether(src=self.pg2.remote_mac,
                        dst=self.pg2.local_mac) /
                  IP(src=self.pg2.remote_hosts[1].ip4,
                     dst=self.pg2.local_ip4) /
                  UDP(sport=1234, dport=48879) /
-                 VXLAN(vni=99, gpid=220, flags=0x88) /
+                 VXLAN(vni=99, gpid=112, flags=0x88) /
                  Ether(src=l['mac'], dst=ep.mac) /
                  IP(src=l['ip'], dst=ep.ip4.address) /
                  UDP(sport=1234, dport=1234) /
@@ -1619,18 +1774,16 @@ class TestGBP(VppTestCase):
                 self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
                 self.assertEqual(rx[UDP].dport, 48879)
                 # the UDP source port is a random value for hashing
-                self.assertEqual(rx[VXLAN].gpid, 220)
+                self.assertEqual(rx[VXLAN].gpid, 112)
                 self.assertEqual(rx[VXLAN].vni, 99)
                 self.assertTrue(rx[VXLAN].flags.G)
                 self.assertTrue(rx[VXLAN].flags.Instance)
                 self.assertTrue(rx[VXLAN].gpflags.A)
                 self.assertFalse(rx[VXLAN].gpflags.D)
 
-        self.sleep(2)
         for l in learnt:
-            self.assertFalse(find_gbp_endpoint(self,
-                                               vx_tun_l2_1.sw_if_index,
-                                               mac=l['mac']))
+            self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index,
+                                     mac=l['mac'])
 
         #
         # repeat in the other EPG
@@ -1638,13 +1791,13 @@ class TestGBP(VppTestCase):
         # so the packet is cleared for delivery
         #
         for l in learnt:
-            # a packet with an sclass from a knwon EPG
+            # a packet with an sclass from a known EPG
             p = (Ether(src=self.pg2.remote_mac,
                        dst=self.pg2.local_mac) /
                  IP(src=self.pg2.remote_hosts[1].ip4,
                     dst=self.pg2.local_ip4) /
                  UDP(sport=1234, dport=48879) /
-                 VXLAN(vni=99, gpid=330, flags=0x88, gpflags='A') /
+                 VXLAN(vni=99, gpid=113, flags=0x88, gpflags='A') /
                  Ether(src=l['mac'], dst=ep.mac) /
                  IP(src=l['ip'], dst=ep.ip4.address) /
                  UDP(sport=1234, dport=1234) /
@@ -1671,13 +1824,13 @@ class TestGBP(VppTestCase):
         # refresh the entries after the check for no replies above
         #
         for l in learnt:
-            # a packet with an sclass from a knwon EPG
+            # a packet with an sclass from a known EPG
             p = (Ether(src=self.pg2.remote_mac,
                        dst=self.pg2.local_mac) /
                  IP(src=self.pg2.remote_hosts[1].ip4,
                     dst=self.pg2.local_ip4) /
                  UDP(sport=1234, dport=48879) /
-                 VXLAN(vni=99, gpid=330, flags=0x88, gpflags='A') /
+                 VXLAN(vni=99, gpid=113, flags=0x88, gpflags='A') /
                  Ether(src=l['mac'], dst=ep.mac) /
                  IP(src=l['ip'], dst=ep.ip4.address) /
                  UDP(sport=1234, dport=1234) /
@@ -1697,14 +1850,14 @@ class TestGBP(VppTestCase):
         rule2 = acl.create_rule(is_ipv6=1, permit_deny=1, proto=17)
         acl_index = acl.add_vpp_config([rule, rule2])
         c1 = VppGbpContract(
-            self, 220, 330, acl_index,
+            self, epg_220.sclass, epg_330.sclass, acl_index,
             [VppGbpContractRule(
                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
                 []),
              VppGbpContractRule(
                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
-                 [])])
-
+                 [])],
+            [ETH_P_IP, ETH_P_IPV6])
         c1.add_vpp_config()
 
         for l in learnt:
@@ -1724,18 +1877,7 @@ class TestGBP(VppTestCase):
                 IP(dst="10.0.0.133", src=ep.ip4.address) /
                 UDP(sport=1234, dport=1234) /
                 Raw('\xa5' * 100))
-        rxs = self.send_and_expect(ep.itf, [p_uu], gbd1.uu_flood)
-
-        #
-        # Add a mcast destination VXLAN-GBP tunnel for B&M traffic
-        #
-        tun_bm = VppVxlanGbpTunnel(self, self.pg4.local_ip4,
-                                   "239.1.1.1", 88,
-                                   mcast_itf=self.pg4)
-        tun_bm.add_vpp_config()
-        bp_bm = VppBridgeDomainPort(self, bd1, tun_bm,
-                                    port_type=L2_PORT_TYPE.NORMAL)
-        bp_bm.add_vpp_config()
+        rxs = self.send_and_expect(ep.itf, [p_uu], gbd1.uu_fwd)
 
         self.logger.info(self.vapi.cli("sh bridge 1 detail"))
 
@@ -1745,17 +1887,29 @@ class TestGBP(VppTestCase):
                 Raw('\xa5' * 100))
         rxs = self.send_and_expect_only(ep.itf, [p_bm], tun_bm.mcast_itf)
 
+        for rx in rxs:
+            self.assertEqual(rx[IP].src, self.pg4.local_ip4)
+            self.assertEqual(rx[IP].dst, "239.1.1.1")
+            self.assertEqual(rx[UDP].dport, 48879)
+            # the UDP source port is a random value for hashing
+            self.assertEqual(rx[VXLAN].gpid, 112)
+            self.assertEqual(rx[VXLAN].vni, 88)
+            self.assertTrue(rx[VXLAN].flags.G)
+            self.assertTrue(rx[VXLAN].flags.Instance)
+            self.assertFalse(rx[VXLAN].gpflags.A)
+            self.assertFalse(rx[VXLAN].gpflags.D)
+
         #
         # Check v6 Endpoints
         #
         for l in learnt:
-            # a packet with an sclass from a knwon EPG
+            # a packet with an sclass from a known EPG
             p = (Ether(src=self.pg2.remote_mac,
                        dst=self.pg2.local_mac) /
                  IP(src=self.pg2.remote_hosts[1].ip4,
                     dst=self.pg2.local_ip4) /
                  UDP(sport=1234, dport=48879) /
-                 VXLAN(vni=99, gpid=330, flags=0x88, gpflags='A') /
+                 VXLAN(vni=99, gpid=113, flags=0x88, gpflags='A') /
                  Ether(src=l['mac'], dst=ep.mac) /
                  IPv6(src=l['ip6'], dst=ep.ip6.address) /
                  UDP(sport=1234, dport=1234) /
@@ -1775,11 +1929,9 @@ class TestGBP(VppTestCase):
         #
         # clean up
         #
-        self.sleep(2)
         for l in learnt:
-            self.assertFalse(find_gbp_endpoint(self,
-                                               vx_tun_l2_1.sw_if_index,
-                                               mac=l['mac']))
+            self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index,
+                                     mac=l['mac'])
 
         self.pg2.unconfig_ip4()
         self.pg3.unconfig_ip4()
@@ -1791,6 +1943,7 @@ class TestGBP(VppTestCase):
     def test_gbp_learn_vlan_l2(self):
         """ GBP L2 Endpoint w/ VLANs"""
 
+        ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
         learnt = [{'mac': '00:00:11:11:11:01',
                    'ip': '10.0.0.1',
                    'ip6': '2001:10::2'},
@@ -1798,12 +1951,6 @@ class TestGBP(VppTestCase):
                    'ip': '10.0.0.2',
                    'ip6': '2001:10::3'}]
 
-        #
-        # lower the inactive threshold so these tests pass in a
-        # reasonable amount of time
-        #
-        self.vapi.gbp_endpoint_learn_set_inactive_threshold(1)
-
         #
         # IP tables
         #
@@ -1830,9 +1977,9 @@ class TestGBP(VppTestCase):
         #
         vlan_11 = VppDot1QSubint(self, self.pg0, 11)
         vlan_11.admin_up()
-        self.vapi.sw_interface_set_l2_tag_rewrite(vlan_11.sw_if_index,
-                                                  L2_VTR_OP.L2_POP_1,
-                                                  11)
+        self.vapi.l2_interface_vlan_tag_rewrite(
+            sw_if_index=vlan_11.sw_if_index, vtr_op=L2_VTR_OP.L2_POP_1,
+            push_dot1q=11)
 
         bd_uu_fwd = VppVxlanGbpTunnel(self, self.pg3.local_ip4,
                                       self.pg3.remote_ip4, 116)
@@ -1859,19 +2006,21 @@ class TestGBP(VppTestCase):
         #
         # The Endpoint-group in which we are learning endpoints
         #
-        epg_220 = VppGbpEndpointGroup(self, 220, rd1, gbd1,
+        epg_220 = VppGbpEndpointGroup(self, 220, 441, rd1, gbd1,
                                       None, self.loop0,
                                       "10.0.0.128",
-                                      "2001:10::128")
+                                      "2001:10::128",
+                                      VppGbpEndpointRetention(2))
         epg_220.add_vpp_config()
 
         #
         # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint
-        # leanring enabled
+        # learning enabled
         #
         vx_tun_l2_1 = VppGbpVxlanTunnel(
             self, 99, bd1.bd_id,
-            VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L2)
+            VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L2,
+            self.pg2.local_ip4)
         vx_tun_l2_1.add_vpp_config()
 
         #
@@ -1890,14 +2039,14 @@ class TestGBP(VppTestCase):
         # Send to the static EP
         #
         for ii, l in enumerate(learnt):
-            # a packet with an sclass from a knwon EPG
+            # a packet with an sclass from a known EPG
             # arriving on an unknown TEP
             p = (Ether(src=self.pg2.remote_mac,
                        dst=self.pg2.local_mac) /
                  IP(src=self.pg2.remote_hosts[1].ip4,
                     dst=self.pg2.local_ip4) /
                  UDP(sport=1234, dport=48879) /
-                 VXLAN(vni=99, gpid=220, flags=0x88) /
+                 VXLAN(vni=99, gpid=441, flags=0x88) /
                  Ether(src=l['mac'], dst=ep.mac) /
                  IP(src=l['ip'], dst=ep.ip4.address) /
                  UDP(sport=1234, dport=1234) /
@@ -1945,7 +2094,7 @@ class TestGBP(VppTestCase):
                 self.assertEqual(rx[IP].dst, self.pg3.remote_ip4)
                 self.assertEqual(rx[UDP].dport, 48879)
                 # the UDP source port is a random value for hashing
-                self.assertEqual(rx[VXLAN].gpid, 220)
+                self.assertEqual(rx[VXLAN].gpid, 441)
                 self.assertEqual(rx[VXLAN].vni, 116)
                 self.assertTrue(rx[VXLAN].flags.G)
                 self.assertTrue(rx[VXLAN].flags.Instance)
@@ -1960,6 +2109,7 @@ class TestGBP(VppTestCase):
 
         self.vapi.cli("set logging class gbp debug")
 
+        ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
         routed_dst_mac = "00:0c:0c:0c:0c:0c"
         routed_src_mac = "00:22:bd:f8:19:ff"
 
@@ -1970,12 +2120,6 @@ class TestGBP(VppTestCase):
                    'ip': '10.0.1.3',
                    'ip6': '2001:10::3'}]
 
-        #
-        # lower the inactive threshold so these tests pass in a
-        # reasonable amount of time
-        #
-        self.vapi.gbp_endpoint_learn_set_inactive_threshold(1)
-
         #
         # IP tables
         #
@@ -1994,7 +2138,7 @@ class TestGBP(VppTestCase):
         rd1 = VppGbpRouteDomain(self, 2, t4, t6, tun_ip4_uu, tun_ip6_uu)
         rd1.add_vpp_config()
 
-        self.loop0.set_mac(self.router_mac.address)
+        self.loop0.set_mac(self.router_mac)
 
         #
         # Bind the BVI to the RD
@@ -2038,19 +2182,21 @@ class TestGBP(VppTestCase):
         #
         # The Endpoint-group in which we are learning endpoints
         #
-        epg_220 = VppGbpEndpointGroup(self, 220, rd1, gbd1,
+        epg_220 = VppGbpEndpointGroup(self, 220, 441, rd1, gbd1,
                                       None, self.loop0,
                                       "10.0.0.128",
-                                      "2001:10::128")
+                                      "2001:10::128",
+                                      VppGbpEndpointRetention(2))
         epg_220.add_vpp_config()
 
         #
         # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint
-        # leanring enabled
+        # learning enabled
         #
         vx_tun_l3 = VppGbpVxlanTunnel(
             self, 101, rd1.rd_id,
-            VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3)
+            VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
+            self.pg2.local_ip4)
         vx_tun_l3.add_vpp_config()
 
         #
@@ -2067,14 +2213,14 @@ class TestGBP(VppTestCase):
         # learn some remote IPv4 EPs
         #
         for ii, l in enumerate(learnt):
-            # a packet with an sclass from a knwon EPG
+            # a packet with an sclass from a known EPG
             # arriving on an unknown TEP
             p = (Ether(src=self.pg2.remote_mac,
                        dst=self.pg2.local_mac) /
                  IP(src=self.pg2.remote_hosts[1].ip4,
                     dst=self.pg2.local_ip4) /
                  UDP(sport=1234, dport=48879) /
-                 VXLAN(vni=101, gpid=220, flags=0x88) /
+                 VXLAN(vni=101, gpid=441, flags=0x88) /
                  Ether(src=l['mac'], dst="00:00:00:11:11:11") /
                  IP(src=l['ip'], dst=ep.ip4.address) /
                  UDP(sport=1234, dport=1234) /
@@ -2111,7 +2257,7 @@ class TestGBP(VppTestCase):
                 self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
                 self.assertEqual(rx[UDP].dport, 48879)
                 # the UDP source port is a random value for hashing
-                self.assertEqual(rx[VXLAN].gpid, 220)
+                self.assertEqual(rx[VXLAN].gpid, 441)
                 self.assertEqual(rx[VXLAN].vni, 101)
                 self.assertTrue(rx[VXLAN].flags.G)
                 self.assertTrue(rx[VXLAN].flags.Instance)
@@ -2125,7 +2271,6 @@ class TestGBP(VppTestCase):
                 self.assertEqual(inner[IP].src, ep.ip4.address)
                 self.assertEqual(inner[IP].dst, l['ip'])
 
-        self.sleep(2)
         for l in learnt:
             self.assertFalse(find_gbp_endpoint(self,
                                                tep1_sw_if_index,
@@ -2135,14 +2280,14 @@ class TestGBP(VppTestCase):
         # learn some remote IPv6 EPs
         #
         for ii, l in enumerate(learnt):
-            # a packet with an sclass from a knwon EPG
+            # a packet with an sclass from a known EPG
             # arriving on an unknown TEP
             p = (Ether(src=self.pg2.remote_mac,
                        dst=self.pg2.local_mac) /
                  IP(src=self.pg2.remote_hosts[1].ip4,
                     dst=self.pg2.local_ip4) /
                  UDP(sport=1234, dport=48879) /
-                 VXLAN(vni=101, gpid=220, flags=0x88) /
+                 VXLAN(vni=101, gpid=441, flags=0x88) /
                  Ether(src=l['mac'], dst="00:00:00:11:11:11") /
                  IPv6(src=l['ip6'], dst=ep.ip6.address) /
                  UDP(sport=1234, dport=1234) /
@@ -2185,7 +2330,7 @@ class TestGBP(VppTestCase):
                 self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
                 self.assertEqual(rx[UDP].dport, 48879)
                 # the UDP source port is a random value for hashing
-                self.assertEqual(rx[VXLAN].gpid, 220)
+                self.assertEqual(rx[VXLAN].gpid, 441)
                 self.assertEqual(rx[VXLAN].vni, 101)
                 self.assertTrue(rx[VXLAN].flags.G)
                 self.assertTrue(rx[VXLAN].flags.Instance)
@@ -2200,11 +2345,8 @@ class TestGBP(VppTestCase):
                 self.assertEqual(inner[IPv6].dst, l['ip6'])
 
         self.logger.info(self.vapi.cli("sh gbp endpoint"))
-        self.sleep(2)
         for l in learnt:
-            self.assertFalse(find_gbp_endpoint(self,
-                                               tep1_sw_if_index,
-                                               ip=l['ip']))
+            self.wait_for_ep_timeout(ip=l['ip'])
 
         #
         # Static sends to unknown EP with no route
@@ -2218,7 +2360,7 @@ class TestGBP(VppTestCase):
 
         #
         # Add a route to static EP's v4 and v6 subnet
-        #  packets should be send on the v4/v6 uu=fwd interface resp.
+        #  packets should be sent on the v4/v6 uu=fwd interface resp.
         #
         se_10_24 = VppGbpSubnet(
             self, rd1, "10.0.0.0", 24,
@@ -2236,7 +2378,7 @@ class TestGBP(VppTestCase):
             self.assertEqual(rx[IP].dst, self.pg4.remote_ip4)
             self.assertEqual(rx[UDP].dport, 48879)
             # the UDP source port is a random value for hashing
-            self.assertEqual(rx[VXLAN].gpid, 220)
+            self.assertEqual(rx[VXLAN].gpid, 441)
             self.assertEqual(rx[VXLAN].vni, 114)
             self.assertTrue(rx[VXLAN].flags.G)
             self.assertTrue(rx[VXLAN].flags.Instance)
@@ -2248,14 +2390,14 @@ class TestGBP(VppTestCase):
         # learn some remote IPv4 EPs
         #
         for ii, l in enumerate(learnt):
-            # a packet with an sclass from a knwon EPG
+            # a packet with an sclass from a known EPG
             # arriving on an unknown TEP
             p = (Ether(src=self.pg2.remote_mac,
                        dst=self.pg2.local_mac) /
-                 IP(src=self.pg2.remote_hosts[1].ip4,
+                 IP(src=self.pg2.remote_hosts[2].ip4,
                     dst=self.pg2.local_ip4) /
                  UDP(sport=1234, dport=48879) /
-                 VXLAN(vni=101, gpid=220, flags=0x88) /
+                 VXLAN(vni=101, gpid=441, flags=0x88) /
                  Ether(src=l['mac'], dst="00:00:00:11:11:11") /
                  IP(src=l['ip'], dst=ep.ip4.address) /
                  UDP(sport=1234, dport=1234) /
@@ -2267,7 +2409,7 @@ class TestGBP(VppTestCase):
             tep1_sw_if_index = find_vxlan_gbp_tunnel(
                 self,
                 self.pg2.local_ip4,
-                self.pg2.remote_hosts[1].ip4,
+                self.pg2.remote_hosts[2].ip4,
                 vx_tun_l3.vni)
             self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
 
@@ -2283,7 +2425,7 @@ class TestGBP(VppTestCase):
                                 epg_220, None,
                                 "10.0.0.88", "11.0.0.88",
                                 "2001:10::88", "3001::88",
-                                VppEnum.vl_api_gbp_endpoint_flags_t.REMOTE,
+                                ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
                                 self.pg2.local_ip4,
                                 self.pg2.remote_hosts[1].ip4,
                                 mac=None)
@@ -2296,14 +2438,14 @@ class TestGBP(VppTestCase):
                                epg_220, None,
                                learnt[0]['ip'], "11.0.0.101",
                                learnt[0]['ip6'], "3001::101",
-                               VppEnum.vl_api_gbp_endpoint_flags_t.REMOTE,
+                               ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
                                self.pg2.local_ip4,
                                self.pg2.remote_hosts[1].ip4,
                                mac=None)
         rep_2.add_vpp_config()
 
         #
-        # Add a route to the leanred EP's v4 subnet
+        # Add a route to the learned EP's v4 subnet
         #  packets should be send on the v4/v6 uu=fwd interface resp.
         #
         se_10_1_24 = VppGbpSubnet(
@@ -2327,7 +2469,7 @@ class TestGBP(VppTestCase):
                 self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
                 self.assertEqual(rx[UDP].dport, 48879)
                 # the UDP source port is a random value for hashing
-                self.assertEqual(rx[VXLAN].gpid, 220)
+                self.assertEqual(rx[VXLAN].gpid, 441)
                 self.assertEqual(rx[VXLAN].vni, 101)
                 self.assertTrue(rx[VXLAN].flags.G)
                 self.assertTrue(rx[VXLAN].flags.Instance)
@@ -2348,30 +2490,29 @@ class TestGBP(VppTestCase):
         rep_88.remove_vpp_config()
         rep_2.remove_vpp_config()
 
-        self.logger.info(self.vapi.cli("show gbp endpoint"))
-
-        self.assertFalse(find_gbp_endpoint(self, ip=rep_88.ip4.address))
+        self.assertTrue(find_gbp_endpoint(self, ip=rep_2.ip4.address))
 
         p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
-             IP(src=ep.ip4.address, dst=rep_88.ip4.address) /
+             IP(src=ep.ip4.address, dst=rep_2.ip4.address) /
              UDP(sport=1234, dport=1234) /
              Raw('\xa5' * 100))
-        rxs = self.send_and_expect(self.pg0, [p], self.pg4)
+        rxs = self.send_and_expect(self.pg0, [p], self.pg2)
 
-        self.assertTrue(find_gbp_endpoint(self, ip=rep_2.ip4.address))
+        self.assertFalse(find_gbp_endpoint(self, ip=rep_88.ip4.address))
 
         p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
-             IP(src=ep.ip4.address, dst=rep_2.ip4.address) /
+             IP(src=ep.ip4.address, dst=rep_88.ip4.address) /
              UDP(sport=1234, dport=1234) /
              Raw('\xa5' * 100))
-        rxs = self.send_and_expect(self.pg0, [p], self.pg2)
+        rxs = self.send_and_expect(self.pg0, [p], self.pg4)
 
         #
-        # to appease the testcase we cannot have the registered EP stll
+        # to appease the testcase we cannot have the registered EP still
         # present (because it's DP learnt) when the TC ends so wait until
         # it is removed
         #
-        self.sleep(2)
+        self.wait_for_ep_timeout(ip=rep_88.ip4.address)
+        self.wait_for_ep_timeout(ip=rep_2.ip4.address)
 
         #
         # shutdown with learnt endpoint present
@@ -2381,7 +2522,7 @@ class TestGBP(VppTestCase):
              IP(src=self.pg2.remote_hosts[1].ip4,
                 dst=self.pg2.local_ip4) /
              UDP(sport=1234, dport=48879) /
-             VXLAN(vni=101, gpid=220, flags=0x88) /
+             VXLAN(vni=101, gpid=441, flags=0x88) /
              Ether(src=l['mac'], dst="00:00:00:11:11:11") /
              IP(src=learnt[1]['ip'], dst=ep.ip4.address) /
              UDP(sport=1234, dport=1234) /
@@ -2407,6 +2548,7 @@ class TestGBP(VppTestCase):
 
         self.vapi.cli("set logging class gbp debug")
 
+        ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
         routed_dst_mac = "00:0c:0c:0c:0c:0c"
         routed_src_mac = "00:22:bd:f8:19:ff"
 
@@ -2417,12 +2559,6 @@ class TestGBP(VppTestCase):
                    'ip': '10.0.1.3',
                    'ip6': '2001:10::3'}]
 
-        #
-        # lower the inactive threshold so these tests pass in a
-        # reasonable amount of time
-        #
-        self.vapi.gbp_endpoint_learn_set_inactive_threshold(1)
-
         #
         # IP tables
         #
@@ -2434,7 +2570,7 @@ class TestGBP(VppTestCase):
         rd1 = VppGbpRouteDomain(self, 2, t4, t6)
         rd1.add_vpp_config()
 
-        self.loop0.set_mac(self.router_mac.address)
+        self.loop0.set_mac(self.router_mac)
 
         #
         # Bind the BVI to the RD
@@ -2474,20 +2610,23 @@ class TestGBP(VppTestCase):
         #
         # The Endpoint-groups in which we are learning endpoints
         #
-        epg_220 = VppGbpEndpointGroup(self, 220, rd1, gbd1,
+        epg_220 = VppGbpEndpointGroup(self, 220, 440, rd1, gbd1,
                                       None, gbd1.bvi,
                                       "10.0.0.128",
-                                      "2001:10::128")
+                                      "2001:10::128",
+                                      VppGbpEndpointRetention(2))
         epg_220.add_vpp_config()
-        epg_221 = VppGbpEndpointGroup(self, 221, rd1, gbd2,
+        epg_221 = VppGbpEndpointGroup(self, 221, 441, rd1, gbd2,
                                       None, gbd2.bvi,
                                       "10.0.1.128",
-                                      "2001:11::128")
+                                      "2001:11::128",
+                                      VppGbpEndpointRetention(2))
         epg_221.add_vpp_config()
-        epg_222 = VppGbpEndpointGroup(self, 222, rd1, gbd1,
+        epg_222 = VppGbpEndpointGroup(self, 222, 442, rd1, gbd1,
                                       None, gbd1.bvi,
                                       "10.0.2.128",
-                                      "2001:12::128")
+                                      "2001:12::128",
+                                      VppGbpEndpointRetention(2))
         epg_222.add_vpp_config()
 
         #
@@ -2512,15 +2651,17 @@ class TestGBP(VppTestCase):
         #
         # EPGs in which the service endpoints exist
         #
-        epg_320 = VppGbpEndpointGroup(self, 320, rd1, gbd3,
+        epg_320 = VppGbpEndpointGroup(self, 320, 550, rd1, gbd3,
                                       None, gbd1.bvi,
                                       "12.0.0.128",
-                                      "4001:10::128")
+                                      "4001:10::128",
+                                      VppGbpEndpointRetention(2))
         epg_320.add_vpp_config()
-        epg_321 = VppGbpEndpointGroup(self, 321, rd1, gbd4,
+        epg_321 = VppGbpEndpointGroup(self, 321, 551, rd1, gbd4,
                                       None, gbd2.bvi,
                                       "12.0.1.128",
-                                      "4001:11::128")
+                                      "4001:11::128",
+                                      VppGbpEndpointRetention(2))
         epg_321.add_vpp_config()
 
         #
@@ -2604,7 +2745,7 @@ class TestGBP(VppTestCase):
         # test the src-ip hash mode
         #
         c1 = VppGbpContract(
-            self, 220, 222, acl_index,
+            self, epg_220.sclass, epg_222.sclass, acl_index,
             [VppGbpContractRule(
                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
@@ -2618,11 +2759,12 @@ class TestGBP(VppTestCase):
                  [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
                                         sep3.ip6, sep3.epg.rd),
                   VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
-                                        sep4.ip6, sep4.epg.rd)])])
+                                        sep4.ip6, sep4.epg.rd)])],
+            [ETH_P_IP, ETH_P_IPV6])
         c1.add_vpp_config()
 
         c2 = VppGbpContract(
-            self, 222, 220, acl_index,
+            self, epg_222.sclass, epg_220.sclass, acl_index,
             [VppGbpContractRule(
                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
@@ -2636,7 +2778,8 @@ class TestGBP(VppTestCase):
                  [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
                                         sep3.ip6, sep3.epg.rd),
                   VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
-                                        sep4.ip6, sep4.epg.rd)])])
+                                        sep4.ip6, sep4.epg.rd)])],
+            [ETH_P_IP, ETH_P_IPV6])
         c2.add_vpp_config()
 
         #
@@ -2734,7 +2877,7 @@ class TestGBP(VppTestCase):
         # test the symmetric hash mode
         #
         c1 = VppGbpContract(
-            self, 220, 222, acl_index,
+            self, epg_220.sclass, epg_222.sclass, acl_index,
             [VppGbpContractRule(
                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
@@ -2748,11 +2891,12 @@ class TestGBP(VppTestCase):
                  [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
                                         sep3.ip6, sep3.epg.rd),
                   VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
-                                        sep4.ip6, sep4.epg.rd)])])
+                                        sep4.ip6, sep4.epg.rd)])],
+            [ETH_P_IP, ETH_P_IPV6])
         c1.add_vpp_config()
 
         c2 = VppGbpContract(
-            self, 222, 220, acl_index,
+            self, epg_222.sclass, epg_220.sclass, acl_index,
             [VppGbpContractRule(
                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
@@ -2766,7 +2910,8 @@ class TestGBP(VppTestCase):
                  [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
                                         sep3.ip6, sep3.epg.rd),
                   VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
-                                        sep4.ip6, sep4.epg.rd)])])
+                                        sep4.ip6, sep4.epg.rd)])],
+            [ETH_P_IP, ETH_P_IPV6])
         c2.add_vpp_config()
 
         #
@@ -2798,39 +2943,40 @@ class TestGBP(VppTestCase):
         # an L3 switch packet between local EPs in different EPGs
         #  different dest ports on each so the are LB hashed differently
         #
-        p4 = [(Ether(src=ep1.mac, dst=self.router_mac.address) /
+        p4 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) /
                IP(src=ep1.ip4.address, dst=ep2.ip4.address) /
                UDP(sport=1234, dport=1234) /
                Raw('\xa5' * 100)),
-              (Ether(src=ep2.mac, dst=self.router_mac.address) /
+              (Ether(src=ep2.mac, dst=str(self.router_mac)) /
                IP(src=ep2.ip4.address, dst=ep1.ip4.address) /
                UDP(sport=1234, dport=1234) /
                Raw('\xa5' * 100))]
-        p6 = [(Ether(src=ep1.mac, dst=self.router_mac.address) /
+        p6 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) /
                IPv6(src=ep1.ip6.address, dst=ep2.ip6.address) /
                UDP(sport=1234, dport=1234) /
                Raw('\xa5' * 100)),
-              (Ether(src=ep2.mac, dst=self.router_mac.address) /
+              (Ether(src=ep2.mac, dst=str(self.router_mac)) /
                IPv6(src=ep2.ip6.address, dst=ep1.ip6.address) /
                UDP(sport=1234, dport=1234) /
                Raw('\xa5' * 100))]
 
         c3 = VppGbpContract(
-             self, 220, 221, acl_index,
-             [VppGbpContractRule(
+            self, epg_220.sclass, epg_221.sclass, acl_index,
+            [VppGbpContractRule(
+                VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+                VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
+                [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
+                                       sep1.ip4, sep1.epg.rd),
+                 VppGbpContractNextHop(sep2.vmac, sep2.epg.bd,
+                                       sep2.ip4, sep2.epg.rd)]),
+             VppGbpContractRule(
                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
                  VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
-                 [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
-                                        sep1.ip4, sep1.epg.rd),
-                  VppGbpContractNextHop(sep2.vmac, sep2.epg.bd,
-                                        sep2.ip4, sep2.epg.rd)]),
-              VppGbpContractRule(
-                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
-                  VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
-                  [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
-                                         sep3.ip6, sep3.epg.rd),
-                   VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
-                                         sep4.ip6, sep4.epg.rd)])])
+                 [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
+                                        sep3.ip6, sep3.epg.rd),
+                  VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
+                                        sep4.ip6, sep4.epg.rd)])],
+            [ETH_P_IP, ETH_P_IPV6])
         c3.add_vpp_config()
 
         rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
@@ -2846,17 +2992,19 @@ class TestGBP(VppTestCase):
         #
         vx_tun_l3 = VppGbpVxlanTunnel(
             self, 444, rd1.rd_id,
-            VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3)
+            VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
+            self.pg2.local_ip4)
         vx_tun_l3.add_vpp_config()
 
         c4 = VppGbpContract(
-            self, 221, 220, acl_index,
+            self, epg_221.sclass, epg_220.sclass, acl_index,
             [VppGbpContractRule(
                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
                 []),
              VppGbpContractRule(
                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
-                 [])])
+                 [])],
+            [ETH_P_IP, ETH_P_IPV6])
         c4.add_vpp_config()
 
         p = (Ether(src=self.pg7.remote_mac,
@@ -2864,8 +3012,8 @@ class TestGBP(VppTestCase):
              IP(src=self.pg7.remote_ip4,
                 dst=self.pg7.local_ip4) /
              UDP(sport=1234, dport=48879) /
-             VXLAN(vni=444, gpid=221, flags=0x88) /
-             Ether(src="00:22:22:22:22:33", dst=self.router_mac.address) /
+             VXLAN(vni=444, gpid=441, flags=0x88) /
+             Ether(src="00:22:22:22:22:33", dst=str(self.router_mac)) /
              IP(src="10.0.0.88", dst=ep1.ip4.address) /
              UDP(sport=1234, dport=1234) /
              Raw('\xa5' * 100))
@@ -2882,8 +3030,8 @@ class TestGBP(VppTestCase):
              IP(src=self.pg7.remote_ip4,
                 dst=self.pg7.local_ip4) /
              UDP(sport=1234, dport=48879) /
-             VXLAN(vni=444, gpid=221, flags=0x88) /
-             Ether(src="00:22:22:22:22:33", dst=self.router_mac.address) /
+             VXLAN(vni=444, gpid=441, flags=0x88) /
+             Ether(src="00:22:22:22:22:33", dst=str(self.router_mac)) /
              IPv6(src="2001:10::88", dst=ep1.ip6.address) /
              UDP(sport=1234, dport=1234) /
              Raw('\xa5' * 100))
@@ -2898,11 +3046,11 @@ class TestGBP(VppTestCase):
         #
         # L3 switch from local to remote EP
         #
-        p4 = [(Ether(src=ep1.mac, dst=self.router_mac.address) /
+        p4 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) /
                IP(src=ep1.ip4.address, dst="10.0.0.88") /
                UDP(sport=1234, dport=1234) /
                Raw('\xa5' * 100))]
-        p6 = [(Ether(src=ep1.mac, dst=self.router_mac.address) /
+        p6 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) /
                IPv6(src=ep1.ip6.address, dst="2001:10::88") /
                UDP(sport=1234, dport=1234) /
                Raw('\xa5' * 100))]
@@ -2927,7 +3075,7 @@ class TestGBP(VppTestCase):
         # test the dst-ip hash mode
         #
         c5 = VppGbpContract(
-            self, 220, 221, acl_index,
+            self, epg_220.sclass, epg_221.sclass, acl_index,
             [VppGbpContractRule(
                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
@@ -2941,7 +3089,8 @@ class TestGBP(VppTestCase):
                  [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
                                         sep3.ip6, sep3.epg.rd),
                   VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
-                                        sep4.ip6, sep4.epg.rd)])])
+                                        sep4.ip6, sep4.epg.rd)])],
+            [ETH_P_IP, ETH_P_IPV6])
         c5.add_vpp_config()
 
         rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
@@ -2960,6 +3109,495 @@ class TestGBP(VppTestCase):
             self.assertEqual(rx[IPv6].src, ep1.ip6.address)
             self.assertEqual(rx[IPv6].dst, "2001:10::88")
 
+        #
+        # cleanup
+        #
+        self.pg7.unconfig_ip4()
+
+    def test_gbp_l3_out(self):
+        """ GBP L3 Out """
+
+        ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
+        self.vapi.cli("set logging class gbp debug")
+
+        routed_dst_mac = "00:0c:0c:0c:0c:0c"
+        routed_src_mac = "00:22:bd:f8:19:ff"
+
+        #
+        # IP tables
+        #
+        t4 = VppIpTable(self, 1)
+        t4.add_vpp_config()
+        t6 = VppIpTable(self, 1, True)
+        t6.add_vpp_config()
+
+        rd1 = VppGbpRouteDomain(self, 2, t4, t6)
+        rd1.add_vpp_config()
+
+        self.loop0.set_mac(self.router_mac)
+
+        #
+        # Bind the BVI to the RD
+        #
+        VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
+        VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
+
+        #
+        # Pg7 hosts a BD's BUM
+        # Pg1 some other l3 interface
+        #
+        self.pg7.config_ip4()
+        self.pg7.resolve_arp()
+
+        #
+        # a multicast vxlan-gbp tunnel for broadcast in the BD
+        #
+        tun_bm = VppVxlanGbpTunnel(self, self.pg7.local_ip4,
+                                   "239.1.1.1", 88,
+                                   mcast_itf=self.pg7)
+        tun_bm.add_vpp_config()
+
+        #
+        # a GBP external bridge domains for the EPs
+        #
+        bd1 = VppBridgeDomain(self, 1)
+        bd1.add_vpp_config()
+        gbd1 = VppGbpBridgeDomain(self, bd1, self.loop0, None, tun_bm)
+        gbd1.add_vpp_config()
+
+        #
+        # The Endpoint-groups in which the external endpoints exist
+        #
+        epg_220 = VppGbpEndpointGroup(self, 220, 113, rd1, gbd1,
+                                      None, gbd1.bvi,
+                                      "10.0.0.128",
+                                      "2001:10::128",
+                                      VppGbpEndpointRetention(2))
+        epg_220.add_vpp_config()
+
+        # the BVIs have the subnets applied ...
+        ip4_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 24)
+        ip4_addr.add_vpp_config()
+        ip6_addr = VppIpInterfaceAddress(self, gbd1.bvi, "2001:10::128", 64)
+        ip6_addr.add_vpp_config()
+
+        # ... which are L3-out subnets
+        l3o_1 = VppGbpSubnet(
+            self, rd1, "10.0.0.0", 24,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+            sclass=113)
+        l3o_1.add_vpp_config()
+
+        #
+        # an external interface attached to the outside world and the
+        # external BD
+        #
+        vlan_100 = VppDot1QSubint(self, self.pg0, 100)
+        vlan_100.admin_up()
+        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()
+
+        #
+        # an unicast vxlan-gbp for inter-RD traffic
+        #
+        vx_tun_l3 = VppGbpVxlanTunnel(
+            self, 444, rd1.rd_id,
+            VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
+            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 external 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()
+
+        #
+        # ARP packet from External EPs are accepted and replied to
+        #
+        p_arp = (Ether(src=eep1.mac, dst="ff:ff:ff:ff:ff:ff") /
+                 Dot1Q(vlan=100) /
+                 ARP(op="who-has",
+                     psrc=eep1.ip4.address, pdst="10.0.0.128",
+                     hwsrc=eep1.mac, hwdst="ff:ff:ff:ff:ff:ff"))
+        rxs = self.send_and_expect(self.pg0, p_arp * 1, self.pg0)
+
+        #
+        # packets destined to unknown addresses in the BVI's subnet
+        # are ARP'd for
+        #
+        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=eep1.mac, dst=str(self.router_mac)) /
+              Dot1Q(vlan=100) /
+              IPv6(src="2001:10::1", dst="2001:10::88") /
+              UDP(sport=1234, dport=1234) /
+              Raw('\xa5' * 100))
+
+        rxs = self.send_and_expect(self.pg0, p4 * 1, self.pg7)
+
+        for rx in rxs:
+            self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+            # self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+            self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+            self.assertEqual(rx[IP].dst, "239.1.1.1")
+            self.assertEqual(rx[VXLAN].vni, 88)
+            self.assertTrue(rx[VXLAN].flags.G)
+            self.assertTrue(rx[VXLAN].flags.Instance)
+            # policy was applied to the original IP packet
+            self.assertEqual(rx[VXLAN].gpid, 113)
+            self.assertTrue(rx[VXLAN].gpflags.A)
+            self.assertFalse(rx[VXLAN].gpflags.D)
+
+            inner = rx[VXLAN].payload
+
+            self.assertTrue(inner.haslayer(ARP))
+
+        #
+        # remote to external
+        #
+        p = (Ether(src=self.pg7.remote_mac,
+                   dst=self.pg7.local_mac) /
+             IP(src=self.pg7.remote_ip4,
+                dst=self.pg7.local_ip4) /
+             UDP(sport=1234, dport=48879) /
+             VXLAN(vni=444, gpid=113, flags=0x88) /
+             Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
+             IP(src="10.0.0.101", dst="10.0.0.1") /
+             UDP(sport=1234, dport=1234) /
+             Raw('\xa5' * 100))
+
+        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 EP1
+        #
+        ip_220 = VppIpRoute(self, "10.220.0.0", 24,
+                            [VppRoutePath(eep1.ip4.address,
+                                          eep1.epg.bvi.sw_if_index)],
+                            table_id=t4.table_id)
+        ip_220.add_vpp_config()
+
+        l3o_220 = VppGbpSubnet(
+            self, rd1, "10.220.0.0", 24,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+            sclass=4220)
+        l3o_220.add_vpp_config()
+
+        #
+        # A subnet reachable through the external EP2
+        #
+        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,
+            sclass=4221)
+        l3o_221.add_vpp_config()
+
+        #
+        # ping between hosts in remote subnets
+        #  dropped without a contract
+        #
+        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_assert_no_replies(self.pg0, p * 1)
+
+        #
+        # contract for the external nets to communicate
+        #
+        acl = VppGbpAcl(self)
+        rule4 = acl.create_rule(permit_deny=1, proto=17)
+        rule6 = acl.create_rule(is_ipv6=1, permit_deny=1, proto=17)
+        acl_index = acl.add_vpp_config([rule4, rule6])
+
+        c1 = VppGbpContract(
+            self, 4220, 4221, acl_index,
+            [VppGbpContractRule(
+                VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+                []),
+             VppGbpContractRule(
+                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+                 [])],
+            [ETH_P_IP, ETH_P_IPV6])
+        c1.add_vpp_config()
+
+        #
+        # Contracts allowing ext-net 200 to talk with external EPs
+        #
+        c2 = VppGbpContract(
+            self, 4220, 113, acl_index,
+            [VppGbpContractRule(
+                VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+                []),
+             VppGbpContractRule(
+                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+                 [])],
+            [ETH_P_IP, ETH_P_IPV6])
+        c2.add_vpp_config()
+        c3 = VppGbpContract(
+            self, 113, 4220, acl_index,
+            [VppGbpContractRule(
+                VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+                []),
+             VppGbpContractRule(
+                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+                 [])],
+            [ETH_P_IP, ETH_P_IPV6])
+        c3.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") /
+             UDP(sport=1234, dport=1234) /
+             Raw('\xa5' * 100))
+
+        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)
+
+        # we did not learn these external hosts
+        self.assertFalse(find_gbp_endpoint(self, ip="10.220.0.1"))
+        self.assertFalse(find_gbp_endpoint(self, ip="10.221.0.1"))
+
+        #
+        # from remote external EP to local external EP
+        #
+        p = (Ether(src=self.pg7.remote_mac,
+                   dst=self.pg7.local_mac) /
+             IP(src=self.pg7.remote_ip4,
+                dst=self.pg7.local_ip4) /
+             UDP(sport=1234, dport=48879) /
+             VXLAN(vni=444, gpid=113, flags=0x88) /
+             Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
+             IP(src="10.0.0.101", dst="10.220.0.1") /
+             UDP(sport=1234, dport=1234) /
+             Raw('\xa5' * 100))
+
+        rxs = self.send_and_expect(self.pg7, p * 1, self.pg0)
+
+        #
+        # ping from an external host to the remote external EP
+        #
+        p = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
+             Dot1Q(vlan=100) /
+             IP(src="10.220.0.1", dst=rep.ip4.address) /
+             UDP(sport=1234, dport=1234) /
+             Raw('\xa5' * 100))
+
+        rxs = self.send_and_expect(self.pg0, p * 1, self.pg7)
+
+        for rx in rxs:
+            self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+            # self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+            self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+            self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+            self.assertEqual(rx[VXLAN].vni, 444)
+            self.assertTrue(rx[VXLAN].flags.G)
+            self.assertTrue(rx[VXLAN].flags.Instance)
+            # the sclass of the ext-net the packet came from
+            self.assertEqual(rx[VXLAN].gpid, 4220)
+            # policy was applied to the original IP packet
+            self.assertTrue(rx[VXLAN].gpflags.A)
+            # since it's an external host the reciever should not learn it
+            self.assertTrue(rx[VXLAN].gpflags.D)
+            inner = rx[VXLAN].payload
+            self.assertEqual(inner[IP].src, "10.220.0.1")
+            self.assertEqual(inner[IP].dst, rep.ip4.address)
+
+        #
+        # An external subnet reachable via the remote external EP
+        #
+
+        #
+        # first the VXLAN-GBP tunnel over which it is reached
+        #
+        vx_tun_r = VppVxlanGbpTunnel(
+            self, self.pg7.local_ip4,
+            self.pg7.remote_ip4, 445,
+            mode=(VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t.
+                  VXLAN_GBP_API_TUNNEL_MODE_L3))
+        vx_tun_r.add_vpp_config()
+        VppIpInterfaceBind(self, vx_tun_r, t4).add_vpp_config()
+
+        self.logger.info(self.vapi.cli("sh vxlan-gbp tunnel"))
+
+        #
+        # then the special adj to resolve through on that tunnel
+        #
+        n1 = VppNeighbor(self,
+                         vx_tun_r.sw_if_index,
+                         "00:0c:0c:0c:0c:0c",
+                         self.pg7.remote_ip4)
+        n1.add_vpp_config()
+
+        #
+        # the route via the adj above
+        #
+        ip_222 = VppIpRoute(self, "10.222.0.0", 24,
+                            [VppRoutePath(self.pg7.remote_ip4,
+                                          vx_tun_r.sw_if_index)],
+                            table_id=t4.table_id)
+        ip_222.add_vpp_config()
+
+        l3o_222 = VppGbpSubnet(
+            self, rd1, "10.222.0.0", 24,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+            sclass=4222)
+        l3o_222.add_vpp_config()
+
+        #
+        # ping between hosts in local and remote external subnets
+        #  dropped without a contract
+        #
+        p = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
+             Dot1Q(vlan=100) /
+             IP(src="10.220.0.1", dst="10.222.0.1") /
+             UDP(sport=1234, dport=1234) /
+             Raw('\xa5' * 100))
+
+        rxs = self.send_and_assert_no_replies(self.pg0, p * 1)
+
+        #
+        # Add contracts ext-nets for 220 -> 222
+        #
+        c4 = VppGbpContract(
+            self, 4220, 4222, acl_index,
+            [VppGbpContractRule(
+                VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+                []),
+             VppGbpContractRule(
+                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+                 [])],
+            [ETH_P_IP, ETH_P_IPV6])
+        c4.add_vpp_config()
+
+        #
+        # ping from host in local to remote external subnets
+        #
+        p = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
+             Dot1Q(vlan=100) /
+             IP(src="10.220.0.1", dst="10.222.0.1") /
+             UDP(sport=1234, dport=1234) /
+             Raw('\xa5' * 100))
+
+        rxs = self.send_and_expect(self.pg0, p * 3, self.pg7)
+
+        for rx in rxs:
+            self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+            self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+            self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+            self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+            self.assertEqual(rx[VXLAN].vni, 445)
+            self.assertTrue(rx[VXLAN].flags.G)
+            self.assertTrue(rx[VXLAN].flags.Instance)
+            # the sclass of the ext-net the packet came from
+            self.assertEqual(rx[VXLAN].gpid, 4220)
+            # policy was applied to the original IP packet
+            self.assertTrue(rx[VXLAN].gpflags.A)
+            # since it's an external host the reciever should not learn it
+            self.assertTrue(rx[VXLAN].gpflags.D)
+            inner = rx[VXLAN].payload
+            self.assertEqual(inner[Ether].dst, "00:0c:0c:0c:0c:0c")
+            self.assertEqual(inner[IP].src, "10.220.0.1")
+            self.assertEqual(inner[IP].dst, "10.222.0.1")
+
+        #
+        # ping from host in remote to local external subnets
+        # there's no contract for this, but the A bit is set.
+        #
+        p = (Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac) /
+             IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4) /
+             UDP(sport=1234, dport=48879) /
+             VXLAN(vni=445, gpid=4222, flags=0x88, gpflags='A') /
+             Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
+             IP(src="10.222.0.1", dst="10.220.0.1") /
+             UDP(sport=1234, dport=1234) /
+             Raw('\xa5' * 100))
+
+        rxs = self.send_and_expect(self.pg7, p * 3, self.pg0)
+        self.assertFalse(find_gbp_endpoint(self, ip="10.222.0.1"))
+
+        #
+        # cleanup
+        #
+        self.pg7.unconfig_ip4()
+        vlan_100.set_vtr(L2_VTR_OP.L2_DISABLED)
+
 
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)