aboutsummaryrefslogtreecommitdiffstats
path: root/extras/deprecated/plugins/gbp/test_gbp.py
diff options
context:
space:
mode:
Diffstat (limited to 'extras/deprecated/plugins/gbp/test_gbp.py')
-rw-r--r--extras/deprecated/plugins/gbp/test_gbp.py7209
1 files changed, 7209 insertions, 0 deletions
diff --git a/extras/deprecated/plugins/gbp/test_gbp.py b/extras/deprecated/plugins/gbp/test_gbp.py
new file mode 100644
index 00000000000..6a4fe0dae42
--- /dev/null
+++ b/extras/deprecated/plugins/gbp/test_gbp.py
@@ -0,0 +1,7209 @@
+#!/usr/bin/env python3
+import typing
+from socket import AF_INET6, inet_pton, inet_ntop
+import unittest
+from ipaddress import ip_address, IPv4Network, IPv6Network
+
+from scapy.packet import Raw
+from scapy.layers.l2 import Ether, ARP, Dot1Q
+from scapy.layers.inet import IP, UDP, ICMP
+from scapy.layers.inet6 import (
+ IPv6,
+ ICMPv6ND_NS,
+ ICMPv6NDOptSrcLLAddr,
+ ICMPv6ND_NA,
+ ICMPv6EchoRequest,
+)
+from scapy.utils6 import in6_getnsma, in6_getnsmac
+from scapy.layers.vxlan import VXLAN
+from scapy.data import ETH_P_IP, ETH_P_IPV6
+
+from framework import tag_fixme_vpp_workers
+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,
+ FibPathProto,
+ FibPathType,
+)
+from vpp_l2 import (
+ VppBridgeDomain,
+ VppBridgeDomainPort,
+ VppBridgeDomainArpEntry,
+ VppL2FibEntry,
+ find_bridge_domain_port,
+ VppL2Vtr,
+)
+from vpp_sub_interface import L2_VTR_OP, VppDot1QSubint
+from vpp_ip import DpoProto, get_dpo_proto
+from vpp_papi import VppEnum, MACAddress
+from vpp_vxlan_gbp_tunnel import find_vxlan_gbp_tunnel, INDEX_INVALID, VppVxlanGbpTunnel
+from vpp_neighbor import VppNeighbor
+from vpp_acl import AclRule, VppAcl
+
+NUM_PKTS = 67
+
+
+def find_gbp_endpoint(
+ test, sw_if_index=None, ip=None, mac=None, tep=None, sclass=None, flags=None
+):
+ if ip:
+ vip = ip
+ if mac:
+ vmac = MACAddress(mac)
+
+ eps = test.vapi.gbp_endpoint_dump()
+
+ for ep in eps:
+ if tep:
+ src = tep[0]
+ dst = tep[1]
+ if src != str(ep.endpoint.tun.src) or dst != str(ep.endpoint.tun.dst):
+ continue
+ if sw_if_index:
+ if ep.endpoint.sw_if_index != sw_if_index:
+ continue
+ if sclass:
+ if ep.endpoint.sclass != sclass:
+ continue
+ if flags:
+ if flags != (flags & ep.endpoint.flags):
+ continue
+ if ip:
+ for eip in ep.endpoint.ips:
+ if vip == str(eip):
+ return True
+ if mac:
+ if vmac == ep.endpoint.mac:
+ return True
+
+ return False
+
+
+def find_gbp_vxlan(test: VppTestCase, vni):
+ ts = test.vapi.gbp_vxlan_tunnel_dump()
+ for t in ts:
+ if t.tunnel.vni == vni:
+ return True
+ return False
+
+
+class VppGbpEndpoint(VppObject):
+ """
+ GBP Endpoint
+ """
+
+ @property
+ def mac(self):
+ return str(self.vmac)
+
+ @property
+ def ip4(self):
+ return self._ip4
+
+ @property
+ def fip4(self):
+ return self._fip4
+
+ @property
+ def ip6(self):
+ return self._ip6
+
+ @property
+ def fip6(self):
+ return self._fip6
+
+ @property
+ def ips(self):
+ return [self.ip4, self.ip6]
+
+ @property
+ def fips(self):
+ return [self.fip4, self.fip6]
+
+ def __init__(
+ self,
+ test,
+ itf,
+ epg,
+ recirc,
+ ip4,
+ fip4,
+ ip6,
+ fip6,
+ flags=0,
+ tun_src="0.0.0.0",
+ tun_dst="0.0.0.0",
+ mac=True,
+ ):
+ self._test = test
+ self.itf = itf
+ self.handle = None
+ self.epg = epg
+ self.recirc = recirc
+
+ self._ip4 = ip4
+ self._fip4 = fip4
+ self._ip6 = ip6
+ self._fip6 = fip6
+
+ if mac:
+ self.vmac = MACAddress(self.itf.remote_mac)
+ else:
+ self.vmac = MACAddress("00:00:00:00:00:00")
+
+ self.flags = flags
+ self.tun_src = tun_src
+ self.tun_dst = tun_dst
+
+ def encode(self):
+ ips = [self.ip4, self.ip6]
+ return {
+ "sw_if_index": self.itf.sw_if_index,
+ "ips": ips,
+ "n_ips": len(ips),
+ "mac": self.vmac.packed,
+ "sclass": self.epg.sclass,
+ "flags": self.flags,
+ "tun": {
+ "src": self.tun_src,
+ "dst": self.tun_dst,
+ },
+ }
+
+ def add_vpp_config(self):
+ res = self._test.vapi.gbp_endpoint_add(
+ endpoint=self.encode(),
+ )
+ self.handle = res.handle
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_endpoint_del(handle=self.handle)
+
+ def object_id(self):
+ return "gbp-endpoint:[%d==%d:%s:%d]" % (
+ self.handle,
+ self.itf.sw_if_index,
+ self.ip4,
+ self.epg.sclass,
+ )
+
+ def query_vpp_config(self):
+ return find_gbp_endpoint(self._test, self.itf.sw_if_index, self.ip4)
+
+
+class VppGbpRecirc(VppObject):
+ """
+ GBP Recirculation Interface
+ """
+
+ def __init__(self, test, epg, recirc, is_ext=False):
+ self._test = test
+ self.recirc = recirc
+ self.epg = epg
+ self.is_ext = is_ext
+
+ def encode(self):
+ return {
+ "is_ext": self.is_ext,
+ "sw_if_index": self.recirc.sw_if_index,
+ "sclass": self.epg.sclass,
+ }
+
+ def add_vpp_config(self):
+ self._test.vapi.gbp_recirc_add_del(
+ 1,
+ recirc=self.encode(),
+ )
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_recirc_add_del(
+ 0,
+ recirc=self.encode(),
+ )
+
+ def object_id(self):
+ return "gbp-recirc:[%d]" % (self.recirc.sw_if_index)
+
+ def query_vpp_config(self):
+ rs = self._test.vapi.gbp_recirc_dump()
+ for r in rs:
+ if r.recirc.sw_if_index == self.recirc.sw_if_index:
+ return True
+ return False
+
+
+class VppGbpExtItf(VppObject):
+ """
+ GBP ExtItfulation Interface
+ """
+
+ def __init__(self, test, itf, bd, rd, anon=False):
+ self._test = test
+ self.itf = itf
+ self.bd = bd
+ self.rd = rd
+ self.flags = 1 if anon else 0
+
+ def encode(self):
+ return {
+ "sw_if_index": self.itf.sw_if_index,
+ "bd_id": self.bd.bd_id,
+ "rd_id": self.rd.rd_id,
+ "flags": self.flags,
+ }
+
+ def add_vpp_config(self):
+ self._test.vapi.gbp_ext_itf_add_del(
+ 1,
+ ext_itf=self.encode(),
+ )
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_ext_itf_add_del(
+ 0,
+ ext_itf=self.encode(),
+ )
+
+ def object_id(self):
+ return "gbp-ext-itf:[%d]%s" % (
+ self.itf.sw_if_index,
+ " [anon]" if self.flags else "",
+ )
+
+ 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=0xFFFFFFFF,
+ sclass=0xFFFF,
+ ):
+ # TODO: replace hardcoded defaults when vpp_papi supports
+ # defaults in typedefs
+ self._test = test
+ self.rd_id = rd.rd_id
+ a = ip_address(address)
+ if 4 == a.version:
+ self.prefix = IPv4Network("%s/%d" % (address, address_len), strict=False)
+ else:
+ self.prefix = IPv6Network("%s/%d" % (address, address_len), strict=False)
+ self.type = type
+ self.sw_if_index = sw_if_index
+ self.sclass = sclass
+
+ def encode(self):
+ return {
+ "type": self.type,
+ "sw_if_index": self.sw_if_index,
+ "sclass": self.sclass,
+ "prefix": self.prefix,
+ "rd_id": self.rd_id,
+ }
+
+ def add_vpp_config(self):
+ self._test.vapi.gbp_subnet_add_del(
+ is_add=1,
+ subnet=self.encode(),
+ )
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_subnet_add_del(is_add=0, subnet=self.encode())
+
+ def object_id(self):
+ return "gbp-subnet:[%d-%s]" % (self.rd_id, self.prefix)
+
+ def query_vpp_config(self):
+ ss = self._test.vapi.gbp_subnet_dump()
+ for s in ss:
+ if (
+ s.subnet.rd_id == self.rd_id
+ and s.subnet.type == self.type
+ and s.subnet.prefix == self.prefix
+ ):
+ return True
+ 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,
+ 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 = bvi_ip4
+ self.bvi_ip6 = bvi_ip6
+ self.vnid = vnid
+ self.bd = bd # VppGbpBridgeDomain
+ self.rd = rd
+ self.sclass = sclass
+ if 0 == self.sclass:
+ self.sclass = 0xFFFF
+ self.retention = retention
+
+ def encode(self) -> dict:
+ return {
+ "uplink_sw_if_index": (
+ self.uplink.sw_if_index if self.uplink else INDEX_INVALID
+ ),
+ "bd_id": self.bd.bd.bd_id,
+ "rd_id": self.rd.rd_id,
+ "vnid": self.vnid,
+ "sclass": self.sclass,
+ "retention": self.retention.encode(),
+ }
+
+ def add_vpp_config(self):
+ self._test.vapi.gbp_endpoint_group_add(epg=self.encode())
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_endpoint_group_del(sclass=self.sclass)
+
+ def object_id(self) -> str:
+ return "gbp-endpoint-group:[%d]" % (self.vnid)
+
+ def query_vpp_config(self) -> bool:
+ epgs = self._test.vapi.gbp_endpoint_group_dump()
+ for epg in epgs:
+ if epg.epg.vnid == self.vnid:
+ return True
+ return False
+
+
+class VppGbpBridgeDomain(VppObject):
+ """
+ GBP Bridge Domain
+ """
+
+ def __init__(
+ self,
+ test,
+ bd,
+ rd,
+ bvi,
+ uu_fwd: typing.Optional[VppVxlanGbpTunnel] = None,
+ bm_flood=None,
+ learn=True,
+ uu_drop=False,
+ bm_drop=False,
+ ucast_arp=False,
+ ):
+ self._test = test
+ self.bvi = bvi
+ self.uu_fwd = uu_fwd
+ self.bm_flood = bm_flood
+ self.bd = bd
+ self.rd = rd
+
+ e = VppEnum.vl_api_gbp_bridge_domain_flags_t
+
+ self.flags = e.GBP_BD_API_FLAG_NONE
+ if not learn:
+ self.flags |= e.GBP_BD_API_FLAG_DO_NOT_LEARN
+ if uu_drop:
+ self.flags |= e.GBP_BD_API_FLAG_UU_FWD_DROP
+ if bm_drop:
+ self.flags |= e.GBP_BD_API_FLAG_MCAST_DROP
+ if ucast_arp:
+ self.flags |= e.GBP_BD_API_FLAG_UCAST_ARP
+
+ def encode(self) -> dict:
+ return {
+ "flags": self.flags,
+ "bvi_sw_if_index": self.bvi.sw_if_index,
+ "uu_fwd_sw_if_index": (
+ self.uu_fwd.sw_if_index if self.uu_fwd else INDEX_INVALID
+ ),
+ "bm_flood_sw_if_index": (
+ self.bm_flood.sw_if_index if self.bm_flood else INDEX_INVALID
+ ),
+ "bd_id": self.bd.bd_id,
+ "rd_id": self.rd.rd_id,
+ }
+
+ def add_vpp_config(self):
+ self._test.vapi.gbp_bridge_domain_add(
+ bd=self.encode(),
+ )
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_bridge_domain_del(bd_id=self.bd.bd_id)
+
+ def object_id(self) -> str:
+ return "gbp-bridge-domain:[%d]" % (self.bd.bd_id)
+
+ def query_vpp_config(self) -> bool:
+ bds = self._test.vapi.gbp_bridge_domain_dump()
+ for bd in bds:
+ if bd.bd.bd_id == self.bd.bd_id:
+ return True
+ return False
+
+
+class VppGbpRouteDomain(VppObject):
+ """
+ GBP Route Domain
+ """
+
+ def __init__(self, test, rd_id, scope, t4, t6, ip4_uu=None, ip6_uu=None):
+ self._test = test
+ self.rd_id = rd_id
+ self.scope = scope
+ self.t4 = t4
+ self.t6 = t6
+ self.ip4_uu = ip4_uu
+ self.ip6_uu = ip6_uu
+
+ def encode(self) -> dict:
+ return {
+ "rd_id": self.rd_id,
+ "scope": self.scope,
+ "ip4_table_id": self.t4.table_id,
+ "ip6_table_id": self.t6.table_id,
+ "ip4_uu_sw_if_index": (
+ self.ip4_uu.sw_if_index if self.ip4_uu else INDEX_INVALID
+ ),
+ "ip6_uu_sw_if_index": (
+ self.ip6_uu.sw_if_index if self.ip6_uu else INDEX_INVALID
+ ),
+ }
+
+ def add_vpp_config(self):
+ self._test.vapi.gbp_route_domain_add(
+ rd=self.encode(),
+ )
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_route_domain_del(rd_id=self.rd_id)
+
+ def object_id(self):
+ return "gbp-route-domain:[%d]" % (self.rd_id)
+
+ def query_vpp_config(self):
+ rds = self._test.vapi.gbp_route_domain_dump()
+ for rd in rds:
+ if rd.rd.rd_id == self.rd_id:
+ return True
+ return False
+
+
+class VppGbpContractNextHop:
+ def __init__(self, mac, bd, ip, rd):
+ self.mac = mac
+ self.ip = ip
+ self.bd = bd
+ self.rd = rd
+
+ def encode(self) -> dict:
+ return {
+ "ip": self.ip,
+ "mac": self.mac.packed,
+ "bd_id": self.bd.bd.bd_id,
+ "rd_id": self.rd.rd_id,
+ }
+
+
+class VppGbpContractRule:
+ def __init__(self, action, hash_mode, nhs=None):
+ self.action = action
+ self.hash_mode = hash_mode
+ self.nhs = [] if nhs is None else nhs
+
+ def encode(self) -> dict:
+ nhs = []
+ for nh in self.nhs:
+ nhs.append(nh.encode())
+ while len(nhs) < 8:
+ nhs.append({})
+ return {
+ "action": self.action,
+ "nh_set": {"hash_mode": self.hash_mode, "n_nhs": len(self.nhs), "nhs": nhs},
+ }
+
+ def __repr__(self):
+ return "<VppGbpContractRule action=%s, hash_mode=%s>" % (
+ self.action,
+ self.hash_mode,
+ )
+
+
+class VppGbpContract(VppObject):
+ """
+ GBP Contract
+ """
+
+ def __init__(
+ self,
+ test,
+ scope,
+ sclass,
+ dclass,
+ acl_index,
+ rules: list,
+ allowed_ethertypes: list,
+ ):
+ self._test = test
+ self.scope = scope
+ self.acl_index = acl_index
+ 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 encode(self) -> dict:
+ rules = []
+ for r in self.rules:
+ rules.append(r.encode())
+ return {
+ "acl_index": self.acl_index,
+ "scope": self.scope,
+ "sclass": self.sclass,
+ "dclass": self.dclass,
+ "n_rules": len(rules),
+ "rules": rules,
+ "n_ether_types": len(self.allowed_ethertypes),
+ "allowed_ethertypes": self.allowed_ethertypes,
+ }
+
+ def add_vpp_config(self):
+ r = self._test.vapi.gbp_contract_add_del(is_add=1, contract=self.encode())
+
+ self.stats_index = r.stats_index
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_contract_add_del(
+ is_add=0,
+ contract=self.encode(),
+ )
+
+ def object_id(self):
+ return "gbp-contract:[%d:%d:%d:%d]" % (
+ self.scope,
+ 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.scope == self.scope
+ and c.contract.sclass == self.sclass
+ and c.contract.dclass == self.dclass
+ ):
+ return True
+ return False
+
+ def get_drop_stats(self):
+ c = self._test.statistics.get_counter("/net/gbp/contract/drop")
+ return c[0][self.stats_index]
+
+ def get_permit_stats(self):
+ c = self._test.statistics.get_counter("/net/gbp/contract/permit")
+ return c[0][self.stats_index]
+
+
+class VppGbpVxlanTunnel(VppInterface):
+ """
+ GBP VXLAN tunnel
+ """
+
+ 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 encode(self) -> dict:
+ return {
+ "vni": self.vni,
+ "mode": self.mode,
+ "bd_rd_id": self.bd_rd_id,
+ "src": self.src,
+ }
+
+ def add_vpp_config(self):
+ r = self._test.vapi.gbp_vxlan_tunnel_add(
+ tunnel=self.encode(),
+ )
+ self.set_sw_if_index(r.sw_if_index)
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_vxlan_tunnel_del(vni=self.vni)
+
+ def object_id(self):
+ return "gbp-vxlan:%d" % (self.sw_if_index)
+
+ def query_vpp_config(self):
+ return find_gbp_vxlan(self._test, self.vni)
+
+
+@tag_fixme_vpp_workers
+class TestGBP(VppTestCase):
+ """GBP Test Case"""
+
+ @property
+ def nat_config_flags(self):
+ return VppEnum.vl_api_nat_config_flags_t
+
+ @property
+ def nat44_config_flags(self):
+ return VppEnum.vl_api_nat44_config_flags_t
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestGBP, cls).setUpClass()
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TestGBP, cls).tearDownClass()
+
+ def setUp(self):
+ super(TestGBP, self).setUp()
+
+ self.create_pg_interfaces(range(9))
+ self.create_loopback_interfaces(8)
+
+ self.router_mac = MACAddress("00:11:22:33:44:55")
+
+ for i in self.pg_interfaces:
+ i.admin_up()
+ for i in self.lo_interfaces:
+ i.admin_up()
+
+ self.vlan_100 = VppDot1QSubint(self, self.pg0, 100)
+ self.vlan_100.admin_up()
+ self.vlan_101 = VppDot1QSubint(self, self.pg0, 101)
+ self.vlan_101.admin_up()
+ self.vlan_102 = VppDot1QSubint(self, self.pg0, 102)
+ self.vlan_102.admin_up()
+
+ def tearDown(self):
+ for i in self.pg_interfaces:
+ i.admin_down()
+ super(TestGBP, self).tearDown()
+ for i in self.lo_interfaces:
+ i.remove_vpp_config()
+ self.lo_interfaces = []
+ self.vlan_102.remove_vpp_config()
+ self.vlan_101.remove_vpp_config()
+ self.vlan_100.remove_vpp_config()
+
+ def send_and_expect_bridged(self, src, tx, dst):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, tx[0][Ether].src)
+ self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+ self.assertEqual(r[IP].src, tx[0][IP].src)
+ self.assertEqual(r[IP].dst, tx[0][IP].dst)
+ return rx
+
+ def send_and_expect_bridged6(self, src, tx, dst):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, tx[0][Ether].src)
+ self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+ self.assertEqual(r[IPv6].src, tx[0][IPv6].src)
+ self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst)
+ return rx
+
+ def send_and_expect_routed(self, src, tx, dst, src_mac):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, src_mac)
+ self.assertEqual(r[Ether].dst, dst.remote_mac)
+ self.assertEqual(r[IP].src, tx[0][IP].src)
+ self.assertEqual(r[IP].dst, tx[0][IP].dst)
+ return rx
+
+ def send_and_expect_routed6(self, src, tx, dst, src_mac):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, src_mac)
+ self.assertEqual(r[Ether].dst, dst.remote_mac)
+ self.assertEqual(r[IPv6].src, tx[0][IPv6].src)
+ self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst)
+ return rx
+
+ def send_and_expect_natted(self, src, tx, dst, src_ip):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, tx[0][Ether].src)
+ self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+ self.assertEqual(r[IP].src, src_ip)
+ self.assertEqual(r[IP].dst, tx[0][IP].dst)
+ return rx
+
+ def send_and_expect_natted6(self, src, tx, dst, src_ip):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, tx[0][Ether].src)
+ self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+ self.assertEqual(r[IPv6].src, src_ip)
+ self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst)
+ return rx
+
+ def send_and_expect_unnatted(self, src, tx, dst, dst_ip):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, tx[0][Ether].src)
+ self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+ self.assertEqual(r[IP].dst, dst_ip)
+ self.assertEqual(r[IP].src, tx[0][IP].src)
+ return rx
+
+ def send_and_expect_unnatted6(self, src, tx, dst, dst_ip):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, tx[0][Ether].src)
+ self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+ self.assertEqual(r[IPv6].dst, dst_ip)
+ self.assertEqual(r[IPv6].src, tx[0][IPv6].src)
+ return rx
+
+ def send_and_expect_double_natted(self, src, tx, dst, src_ip, dst_ip):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ 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)
+ return rx
+
+ def send_and_expect_double_natted6(self, src, tx, dst, src_ip, dst_ip):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ 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)
+ return rx
+
+ def send_and_expect_no_arp(self, src, tx, dst):
+ self.pg_send(src, tx)
+ dst.get_capture(0, timeout=1)
+ dst.assert_nothing_captured(remark="")
+
+ def send_and_expect_arp(self, src, tx, dst):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, tx[0][Ether].src)
+ self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+ self.assertEqual(r[ARP].psrc, tx[0][ARP].psrc)
+ self.assertEqual(r[ARP].pdst, tx[0][ARP].pdst)
+ self.assertEqual(r[ARP].hwsrc, tx[0][ARP].hwsrc)
+ self.assertEqual(r[ARP].hwdst, tx[0][ARP].hwdst)
+ return rx
+
+ def test_gbp(self):
+ """Group Based Policy"""
+
+ ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
+
+ #
+ # Route Domains
+ #
+ gt4 = VppIpTable(self, 0)
+ gt4.add_vpp_config()
+ gt6 = VppIpTable(self, 0, is_ip6=True)
+ gt6.add_vpp_config()
+ nt4 = VppIpTable(self, 20)
+ nt4.add_vpp_config()
+ nt6 = VppIpTable(self, 20, is_ip6=True)
+ nt6.add_vpp_config()
+
+ rd0 = VppGbpRouteDomain(self, 0, 400, gt4, gt6, None, None)
+ rd20 = VppGbpRouteDomain(self, 20, 420, nt4, nt6, None, None)
+
+ rd0.add_vpp_config()
+ rd20.add_vpp_config()
+
+ #
+ # Bridge Domains
+ #
+ bd1 = VppBridgeDomain(self, 1)
+ bd2 = VppBridgeDomain(self, 2)
+ bd20 = VppBridgeDomain(self, 20)
+
+ bd1.add_vpp_config()
+ bd2.add_vpp_config()
+ bd20.add_vpp_config()
+
+ gbd1 = VppGbpBridgeDomain(self, bd1, rd0, self.loop0)
+ gbd2 = VppGbpBridgeDomain(self, bd2, rd0, self.loop1)
+ gbd20 = VppGbpBridgeDomain(self, bd20, rd20, self.loop2)
+
+ gbd1.add_vpp_config()
+ gbd2.add_vpp_config()
+ gbd20.add_vpp_config()
+
+ #
+ # 3 EPGs, 2 of which share a BD.
+ # 2 NAT EPGs, one for floating-IP subnets, the other for internet
+ #
+ 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]
+
+ #
+ # 4 end-points, 2 in the same subnet, 3 in the same BD
+ #
+ eps = [
+ VppGbpEndpoint(
+ self,
+ self.pg0,
+ epgs[0],
+ recircs[0],
+ "10.0.0.1",
+ "11.0.0.1",
+ "2001:10::1",
+ "3001::1",
+ ),
+ VppGbpEndpoint(
+ self,
+ self.pg1,
+ epgs[0],
+ recircs[0],
+ "10.0.0.2",
+ "11.0.0.2",
+ "2001:10::2",
+ "3001::2",
+ ),
+ VppGbpEndpoint(
+ self,
+ self.pg2,
+ epgs[1],
+ recircs[1],
+ "10.0.1.1",
+ "11.0.0.3",
+ "2001:10:1::1",
+ "3001::3",
+ ),
+ VppGbpEndpoint(
+ self,
+ self.pg3,
+ epgs[2],
+ recircs[2],
+ "10.0.2.1",
+ "11.0.0.4",
+ "2001:10:2::1",
+ "3001::4",
+ ),
+ ]
+
+ self.vapi.nat44_ed_plugin_enable_disable(enable=1)
+ self.vapi.nat66_plugin_enable_disable(enable=1)
+
+ #
+ # Config related to each of the EPGs
+ #
+ for epg in epgs:
+ # IP config on the BVI interfaces
+ if epg != epgs[1] and epg != epgs[4]:
+ b4 = VppIpInterfaceBind(self, epg.bvi, epg.rd.t4).add_vpp_config()
+ b6 = VppIpInterfaceBind(self, epg.bvi, epg.rd.t6).add_vpp_config()
+ epg.bvi.set_mac(self.router_mac)
+
+ # The BVIs are NAT inside interfaces
+ flags = self.nat_config_flags.NAT_IS_INSIDE
+ self.vapi.nat44_interface_add_del_feature(
+ sw_if_index=epg.bvi.sw_if_index, flags=flags, is_add=1
+ )
+ self.vapi.nat66_add_del_interface(
+ sw_if_index=epg.bvi.sw_if_index, flags=flags, is_add=1
+ )
+
+ if_ip4 = VppIpInterfaceAddress(
+ self, epg.bvi, epg.bvi_ip4, 32, bind=b4
+ ).add_vpp_config()
+ if_ip6 = VppIpInterfaceAddress(
+ self, epg.bvi, epg.bvi_ip6, 128, bind=b6
+ ).add_vpp_config()
+
+ # EPG uplink interfaces in the RD
+ VppIpInterfaceBind(self, epg.uplink, epg.rd.t4).add_vpp_config()
+ VppIpInterfaceBind(self, epg.uplink, epg.rd.t6).add_vpp_config()
+
+ # add the BD ARP termination entry for BVI IP
+ epg.bd_arp_ip4 = VppBridgeDomainArpEntry(
+ self, epg.bd.bd, str(self.router_mac), epg.bvi_ip4
+ )
+ epg.bd_arp_ip6 = VppBridgeDomainArpEntry(
+ self, epg.bd.bd, str(self.router_mac), epg.bvi_ip6
+ )
+ epg.bd_arp_ip4.add_vpp_config()
+ epg.bd_arp_ip6.add_vpp_config()
+
+ # EPG in VPP
+ epg.add_vpp_config()
+
+ for recirc in recircs:
+ # EPG's ingress recirculation interface maps to its RD
+ VppIpInterfaceBind(self, recirc.recirc, recirc.epg.rd.t4).add_vpp_config()
+ VppIpInterfaceBind(self, recirc.recirc, recirc.epg.rd.t6).add_vpp_config()
+
+ self.vapi.nat44_interface_add_del_feature(
+ sw_if_index=recirc.recirc.sw_if_index, is_add=1
+ )
+ self.vapi.nat66_add_del_interface(
+ sw_if_index=recirc.recirc.sw_if_index, is_add=1
+ )
+
+ recirc.add_vpp_config()
+
+ for recirc in recircs:
+ self.assertTrue(
+ find_bridge_domain_port(
+ self, recirc.epg.bd.bd.bd_id, recirc.recirc.sw_if_index
+ )
+ )
+
+ for ep in eps:
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ #
+ # routes to the endpoints. We need these since there are no
+ # adj-fibs due to the fact the the BVI address has /32 and
+ # the subnet is not attached.
+ #
+ for ip, fip in zip(ep.ips, ep.fips):
+ # Add static mappings for each EP from the 10/8 to 11/8 network
+ if ip_address(ip).version == 4:
+ flags = self.nat_config_flags.NAT_IS_ADDR_ONLY
+ self.vapi.nat44_add_del_static_mapping(
+ is_add=1,
+ local_ip_address=ip,
+ external_ip_address=fip,
+ external_sw_if_index=0xFFFFFFFF,
+ vrf_id=0,
+ flags=flags,
+ )
+ else:
+ self.vapi.nat66_add_del_static_mapping(
+ local_ip_address=ip, external_ip_address=fip, vrf_id=0, is_add=1
+ )
+
+ # VPP EP create ...
+ ep.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh gbp endpoint"))
+
+ # ... results in a Gratuitous ARP/ND on the EPG's uplink
+ rx = ep.epg.uplink.get_capture(len(ep.ips) + 1, timeout=0.2)
+
+ for ii, ip in enumerate(ep.ips):
+ p = rx[ii]
+
+ if ip_address(ip).version == 6:
+ self.assertTrue(p.haslayer(ICMPv6ND_NA))
+ self.assertEqual(p[ICMPv6ND_NA].tgt, ip)
+ else:
+ self.assertTrue(p.haslayer(ARP))
+ self.assertEqual(p[ARP].psrc, ip)
+ self.assertEqual(p[ARP].pdst, ip)
+
+ # add the BD ARP termination entry for floating IP
+ for fip in ep.fips:
+ ba = VppBridgeDomainArpEntry(self, epg_nat.bd.bd, ep.mac, fip)
+ ba.add_vpp_config()
+
+ # floating IPs route via EPG recirc
+ r = VppIpRoute(
+ self,
+ fip,
+ ip_address(fip).max_prefixlen,
+ [
+ VppRoutePath(
+ fip,
+ ep.recirc.recirc.sw_if_index,
+ type=FibPathType.FIB_PATH_TYPE_DVR,
+ proto=get_dpo_proto(fip),
+ )
+ ],
+ table_id=20,
+ )
+ r.add_vpp_config()
+
+ # L2 FIB entries in the NAT EPG BD to bridge the packets from
+ # the outside direct to the internal EPG
+ lf = VppL2FibEntry(self, epg_nat.bd.bd, ep.mac, ep.recirc.recirc, bvi_mac=0)
+ lf.add_vpp_config()
+
+ self.assert_equal(
+ self.statistics["/net/arp/tx/gratuitous"][
+ :, epgs[0].uplink.sw_if_index
+ ].sum(),
+ 2,
+ )
+ self.assert_equal(
+ self.statistics["/net/arp/tx/gratuitous"][
+ :, epgs[1].uplink.sw_if_index
+ ].sum(),
+ 1,
+ )
+ self.assert_equal(
+ self.statistics["/net/ip6-nd/tx/gratuitous"][
+ :, epgs[0].uplink.sw_if_index
+ ].sum(),
+ 2,
+ )
+ self.assert_equal(
+ self.statistics["/net/ip6-nd/tx/gratuitous"][
+ :, epgs[1].uplink.sw_if_index
+ ].sum(),
+ 1,
+ )
+
+ #
+ # ARP packets for unknown IP are sent to the EPG uplink
+ #
+ pkt_arp = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
+ op="who-has",
+ hwdst="ff:ff:ff:ff:ff:ff",
+ hwsrc=self.pg0.remote_mac,
+ pdst="10.0.0.88",
+ psrc="10.0.0.99",
+ )
+
+ self.vapi.cli("clear trace")
+ self.pg0.add_stream(pkt_arp)
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rxd = epgs[0].uplink.get_capture(1)
+
+ #
+ # ARP/ND packets get a response
+ #
+ pkt_arp = Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(
+ op="who-has",
+ hwdst="ff:ff:ff:ff:ff:ff",
+ hwsrc=self.pg0.remote_mac,
+ pdst=epgs[0].bvi_ip4,
+ psrc=eps[0].ip4,
+ )
+
+ self.send_and_expect(self.pg0, [pkt_arp], self.pg0)
+
+ nsma = in6_getnsma(inet_pton(AF_INET6, eps[0].ip6))
+ d = inet_ntop(AF_INET6, nsma)
+ pkt_nd = (
+ Ether(dst=in6_getnsmac(nsma), src=self.pg0.remote_mac)
+ / IPv6(dst=d, src=eps[0].ip6)
+ / ICMPv6ND_NS(tgt=epgs[0].bvi_ip6)
+ / ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac)
+ )
+ self.send_and_expect(self.pg0, [pkt_nd], self.pg0)
+
+ #
+ # broadcast packets are flooded
+ #
+ pkt_bcast = (
+ Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac)
+ / IP(src=eps[0].ip4, dst="232.1.1.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.vapi.cli("clear trace")
+ self.pg0.add_stream(pkt_bcast)
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rxd = eps[1].itf.get_capture(1)
+ self.assertEqual(rxd[0][Ether].dst, pkt_bcast[Ether].dst)
+ rxd = epgs[0].uplink.get_capture(1)
+ self.assertEqual(rxd[0][Ether].dst, pkt_bcast[Ether].dst)
+
+ #
+ # packets to non-local L3 destinations dropped
+ #
+ pkt_intra_epg_220_ip4 = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src=eps[0].ip4, dst="10.0.0.99")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ pkt_inter_epg_222_ip4 = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src=eps[0].ip4, dst="10.0.1.99")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_assert_no_replies(self.pg0, pkt_intra_epg_220_ip4 * NUM_PKTS)
+
+ pkt_inter_epg_222_ip6 = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IPv6(src=eps[0].ip6, dst="2001:10::99")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ self.send_and_assert_no_replies(self.pg0, pkt_inter_epg_222_ip6 * NUM_PKTS)
+
+ #
+ # Add the subnet routes
+ #
+ s41 = VppGbpSubnet(
+ self,
+ rd0,
+ "10.0.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL,
+ )
+ s42 = VppGbpSubnet(
+ self,
+ rd0,
+ "10.0.1.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL,
+ )
+ s43 = VppGbpSubnet(
+ self,
+ rd0,
+ "10.0.2.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL,
+ )
+ s61 = VppGbpSubnet(
+ self,
+ rd0,
+ "2001:10::1",
+ 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL,
+ )
+ s62 = VppGbpSubnet(
+ self,
+ rd0,
+ "2001:10:1::1",
+ 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL,
+ )
+ s63 = VppGbpSubnet(
+ self,
+ rd0,
+ "2001:10:2::1",
+ 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL,
+ )
+ s41.add_vpp_config()
+ s42.add_vpp_config()
+ s43.add_vpp_config()
+ s61.add_vpp_config()
+ s62.add_vpp_config()
+ s63.add_vpp_config()
+
+ self.send_and_expect_bridged(
+ eps[0].itf, pkt_intra_epg_220_ip4 * NUM_PKTS, eps[0].epg.uplink
+ )
+ self.send_and_expect_bridged(
+ eps[0].itf, pkt_inter_epg_222_ip4 * NUM_PKTS, eps[0].epg.uplink
+ )
+ self.send_and_expect_bridged6(
+ eps[0].itf, pkt_inter_epg_222_ip6 * NUM_PKTS, eps[0].epg.uplink
+ )
+
+ self.logger.info(self.vapi.cli("sh ip fib 11.0.0.2"))
+ self.logger.info(self.vapi.cli("sh gbp endpoint-group"))
+ self.logger.info(self.vapi.cli("sh gbp endpoint"))
+ self.logger.info(self.vapi.cli("sh gbp recirc"))
+ self.logger.info(self.vapi.cli("sh int"))
+ self.logger.info(self.vapi.cli("sh int addr"))
+ self.logger.info(self.vapi.cli("sh int feat loop6"))
+ self.logger.info(self.vapi.cli("sh vlib graph ip4-gbp-src-classify"))
+ self.logger.info(self.vapi.cli("sh int feat loop3"))
+ self.logger.info(self.vapi.cli("sh int feat pg0"))
+
+ #
+ # Packet destined to unknown unicast is sent on the epg uplink ...
+ #
+ pkt_intra_epg_220_to_uplink = (
+ Ether(src=self.pg0.remote_mac, dst="00:00:00:33:44:55")
+ / IP(src=eps[0].ip4, dst="10.0.0.99")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_bridged(
+ eps[0].itf, pkt_intra_epg_220_to_uplink * NUM_PKTS, eps[0].epg.uplink
+ )
+ # ... and nowhere else
+ self.pg1.get_capture(0, timeout=0.1)
+ self.pg1.assert_nothing_captured(remark="Flood onto other VMS")
+
+ pkt_intra_epg_221_to_uplink = (
+ Ether(src=self.pg2.remote_mac, dst="00:00:00:33:44:66")
+ / IP(src=eps[0].ip4, dst="10.0.0.99")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_bridged(
+ eps[2].itf, pkt_intra_epg_221_to_uplink * NUM_PKTS, eps[2].epg.uplink
+ )
+
+ #
+ # Packets from the uplink are forwarded in the absence of a contract
+ #
+ pkt_intra_epg_220_from_uplink = (
+ Ether(src="00:00:00:33:44:55", dst=self.pg0.remote_mac)
+ / IP(src=eps[0].ip4, dst="10.0.0.99")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_bridged(
+ self.pg4, pkt_intra_epg_220_from_uplink * NUM_PKTS, self.pg0
+ )
+
+ #
+ # in the absence of policy, endpoints in the same EPG
+ # can communicate
+ #
+ pkt_intra_epg = (
+ Ether(src=self.pg0.remote_mac, dst=self.pg1.remote_mac)
+ / IP(src=eps[0].ip4, dst=eps[1].ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_bridged(self.pg0, pkt_intra_epg * NUM_PKTS, self.pg1)
+
+ #
+ # in the absence of policy, endpoints in the different EPG
+ # cannot communicate
+ #
+ pkt_inter_epg_220_to_221 = (
+ Ether(src=self.pg0.remote_mac, dst=self.pg2.remote_mac)
+ / IP(src=eps[0].ip4, dst=eps[2].ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ pkt_inter_epg_221_to_220 = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg0.remote_mac)
+ / IP(src=eps[2].ip4, dst=eps[0].ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ pkt_inter_epg_220_to_222 = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src=eps[0].ip4, dst=eps[3].ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_221 * NUM_PKTS)
+ self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_222 * NUM_PKTS)
+
+ #
+ # A uni-directional contract from EPG 220 -> 221
+ #
+ rule = AclRule(is_permit=1, proto=17)
+ rule2 = AclRule(
+ src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)),
+ is_permit=1,
+ proto=17,
+ )
+ acl = VppAcl(self, rules=[rule, rule2])
+ acl.add_vpp_config()
+
+ c1 = VppGbpContract(
+ self,
+ 400,
+ epgs[0].sclass,
+ epgs[1].sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c1.add_vpp_config()
+
+ self.send_and_expect_bridged(
+ eps[0].itf, pkt_inter_epg_220_to_221 * NUM_PKTS, eps[2].itf
+ )
+ self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_222 * NUM_PKTS)
+
+ #
+ # contract for the return direction
+ #
+ c2 = VppGbpContract(
+ self,
+ 400,
+ epgs[1].sclass,
+ epgs[0].sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c2.add_vpp_config()
+
+ self.send_and_expect_bridged(
+ eps[0].itf, pkt_inter_epg_220_to_221 * NUM_PKTS, eps[2].itf
+ )
+ self.send_and_expect_bridged(
+ eps[2].itf, pkt_inter_epg_221_to_220 * NUM_PKTS, eps[0].itf
+ )
+
+ ds = c2.get_drop_stats()
+ self.assertEqual(ds["packets"], 0)
+ ps = c2.get_permit_stats()
+ self.assertEqual(ps["packets"], NUM_PKTS)
+
+ #
+ # 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.
+ #
+ self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_222 * NUM_PKTS)
+
+ #
+ # A uni-directional contract from EPG 220 -> 222 'L3 routed'
+ #
+ c3 = VppGbpContract(
+ self,
+ 400,
+ epgs[0].sclass,
+ epgs[2].sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c3.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh gbp contract"))
+
+ self.send_and_expect_routed(
+ eps[0].itf,
+ pkt_inter_epg_220_to_222 * NUM_PKTS,
+ eps[3].itf,
+ str(self.router_mac),
+ )
+ #
+ # remove both contracts, traffic stops in both directions
+ #
+ c2.remove_vpp_config()
+ c1.remove_vpp_config()
+ c3.remove_vpp_config()
+ acl.remove_vpp_config()
+
+ self.send_and_assert_no_replies(eps[2].itf, pkt_inter_epg_221_to_220 * NUM_PKTS)
+ self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_221 * NUM_PKTS)
+ self.send_and_expect_bridged(eps[0].itf, pkt_intra_epg * NUM_PKTS, eps[1].itf)
+
+ #
+ # EPs to the outside world
+ #
+
+ # in the EP's RD an external subnet via the NAT EPG's recirc
+ se1 = VppGbpSubnet(
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ sclass=epg_nat.sclass,
+ )
+ se1.add_vpp_config()
+ se2.add_vpp_config()
+ se16.add_vpp_config()
+ se3.add_vpp_config()
+ se36.add_vpp_config()
+ se4.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh ip fib 0.0.0.0/0"))
+ self.logger.info(self.vapi.cli("sh ip fib 11.0.0.1"))
+ self.logger.info(self.vapi.cli("sh ip6 fib ::/0"))
+ self.logger.info(self.vapi.cli("sh ip6 fib %s" % eps[0].fip6))
+
+ #
+ # From an EP to an outside address: IN2OUT
+ #
+ pkt_inter_epg_220_to_global = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src=eps[0].ip4, dst="1.1.1.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ # no policy yet
+ self.send_and_assert_no_replies(
+ eps[0].itf, pkt_inter_epg_220_to_global * NUM_PKTS
+ )
+ rule = AclRule(is_permit=1, proto=17, ports=1234)
+ rule2 = AclRule(
+ is_permit=1,
+ proto=17,
+ ports=1234,
+ src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)),
+ )
+ acl2 = VppAcl(self, rules=[rule, rule2])
+ acl2.add_vpp_config()
+
+ c4 = VppGbpContract(
+ self,
+ 400,
+ epgs[0].sclass,
+ epgs[3].sclass,
+ acl2.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c4.add_vpp_config()
+
+ self.send_and_expect_natted(
+ eps[0].itf, pkt_inter_epg_220_to_global * NUM_PKTS, self.pg7, eps[0].fip4
+ )
+
+ pkt_inter_epg_220_to_global = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IPv6(src=eps[0].ip6, dst="6001::1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_natted6(
+ self.pg0, pkt_inter_epg_220_to_global * NUM_PKTS, self.pg7, eps[0].fip6
+ )
+ #
+ # From a global address to an EP: OUT2IN
+ #
+ pkt_inter_epg_220_from_global = (
+ Ether(src=str(self.router_mac), dst=self.pg0.remote_mac)
+ / IP(dst=eps[0].fip4, src="1.1.1.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_assert_no_replies(
+ self.pg7, pkt_inter_epg_220_from_global * NUM_PKTS
+ )
+
+ c5 = VppGbpContract(
+ self,
+ 400,
+ epgs[3].sclass,
+ epgs[0].sclass,
+ acl2.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c5.add_vpp_config()
+
+ self.send_and_expect_unnatted(
+ self.pg7, pkt_inter_epg_220_from_global * NUM_PKTS, eps[0].itf, eps[0].ip4
+ )
+
+ pkt_inter_epg_220_from_global = (
+ Ether(src=str(self.router_mac), dst=self.pg0.remote_mac)
+ / IPv6(dst=eps[0].fip6, src="6001::1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_unnatted6(
+ self.pg7, pkt_inter_epg_220_from_global * NUM_PKTS, eps[0].itf, eps[0].ip6
+ )
+
+ #
+ # From a local VM to another local VM using resp. public addresses:
+ # IN2OUT2IN
+ #
+ pkt_intra_epg_220_global = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src=eps[0].ip4, dst=eps[1].fip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_double_natted(
+ eps[0].itf,
+ pkt_intra_epg_220_global * NUM_PKTS,
+ eps[1].itf,
+ eps[0].fip4,
+ eps[1].ip4,
+ )
+
+ pkt_intra_epg_220_global = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IPv6(src=eps[0].ip6, dst=eps[1].fip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_double_natted6(
+ eps[0].itf,
+ pkt_intra_epg_220_global * NUM_PKTS,
+ eps[1].itf,
+ eps[0].fip6,
+ eps[1].ip6,
+ )
+
+ #
+ # cleanup
+ #
+ self.vapi.nat44_ed_plugin_enable_disable(enable=0)
+ self.vapi.nat66_plugin_enable_disable(enable=0)
+
+ def wait_for_ep_timeout(
+ self, sw_if_index=None, ip=None, mac=None, tep=None, n_tries=100, s_time=1
+ ):
+ # only learnt EP can timeout
+ ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
+ flags = ep_flags.GBP_API_ENDPOINT_FLAG_LEARNT
+ while n_tries:
+ if not find_gbp_endpoint(self, sw_if_index, ip, mac, tep=tep, flags=flags):
+ return True
+ n_tries = n_tries - 1
+ self.sleep(s_time)
+ self.assertFalse(
+ find_gbp_endpoint(self, sw_if_index, ip, mac, tep=tep, flags=flags)
+ )
+ return False
+
+ def test_gbp_learn_l2(self):
+ """GBP L2 Endpoint Learning"""
+
+ drop_no_contract = self.statistics.get_err_counter(
+ "/err/gbp-policy-port/drop-no-contract"
+ )
+ allow_intra_class = self.statistics.get_err_counter(
+ "/err/gbp-policy-port/allow-intra-sclass"
+ )
+
+ 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"},
+ {"mac": "00:00:11:11:11:02", "ip": "10.0.0.2", "ip6": "2001:10::3"},
+ ]
+
+ #
+ # IP tables
+ #
+ gt4 = VppIpTable(self, 1)
+ gt4.add_vpp_config()
+ gt6 = VppIpTable(self, 1, is_ip6=True)
+ gt6.add_vpp_config()
+
+ rd1 = VppGbpRouteDomain(self, 1, 401, gt4, gt6)
+ rd1.add_vpp_config()
+
+ #
+ # Pg2 hosts the vxlan tunnel, hosts on pg2 to act as TEPs
+ # Pg3 hosts the IP4 UU-flood VXLAN tunnel
+ # Pg4 hosts the IP6 UU-flood VXLAN tunnel
+ #
+ self.pg2.config_ip4()
+ self.pg2.resolve_arp()
+ self.pg2.generate_remote_hosts(4)
+ self.pg2.configure_ipv4_neighbors()
+ self.pg3.config_ip4()
+ self.pg3.resolve_arp()
+ 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, rd1, self.loop0, self.pg3, tun_bm)
+ gbd1.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh bridge 1 detail"))
+ self.logger.info(self.vapi.cli("sh gbp bridge"))
+
+ # ... and has a /32 applied
+ ip_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 32)
+ ip_addr.add_vpp_config()
+
+ #
+ # The Endpoint-group in which we are learning endpoints
+ #
+ epg_220 = VppGbpEndpointGroup(
+ self,
+ 220,
+ 112,
+ rd1,
+ gbd1,
+ None,
+ self.loop0,
+ "10.0.0.128",
+ "2001:10::128",
+ VppGbpEndpointRetention(4),
+ )
+ epg_220.add_vpp_config()
+ epg_330 = VppGbpEndpointGroup(
+ self,
+ 330,
+ 113,
+ rd1,
+ gbd1,
+ None,
+ self.loop1,
+ "10.0.1.128",
+ "2001:11::128",
+ VppGbpEndpointRetention(4),
+ )
+ epg_330.add_vpp_config()
+
+ #
+ # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint
+ # 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,
+ self.pg2.local_ip4,
+ )
+ vx_tun_l2_1.add_vpp_config()
+
+ #
+ # A static endpoint that the learnt endpoints are trying to
+ # talk to
+ #
+ ep = VppGbpEndpoint(
+ self,
+ self.pg0,
+ epg_220,
+ None,
+ "10.0.0.127",
+ "11.0.0.127",
+ "2001:10::1",
+ "3001::1",
+ )
+ ep.add_vpp_config()
+
+ self.assertTrue(find_route(self, ep.ip4, 32, table_id=1))
+
+ # 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, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=99, gpid=88, flags=0x88)
+ / Ether(src=learnt[0]["mac"], dst=ep.mac)
+ / IP(src=learnt[0]["ip"], dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_assert_no_replies(self.pg2, p)
+
+ self.logger.info(self.vapi.cli("sh error"))
+ self.assert_error_counter_equal(
+ "/err/gbp-policy-port/drop-no-contract", drop_no_contract + 1
+ )
+
+ #
+ # we should not have learnt a new tunnel endpoint, since
+ # the EPG was not learnt.
+ #
+ self.assertEqual(
+ INDEX_INVALID,
+ find_vxlan_gbp_tunnel(
+ self, self.pg2.local_ip4, self.pg2.remote_hosts[0].ip4, 99
+ ),
+ )
+
+ # ep is not learnt, because the EPG is unknown
+ 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 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)
+ / IP(src=l["ip"], dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\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.assertTrue(
+ find_gbp_endpoint(self, vx_tun_l2_1.sw_if_index, ip=l["ip"])
+ )
+
+ self.assert_error_counter_equal(
+ "/err/gbp-policy-port/allow-intra-sclass", allow_intra_class + 2
+ )
+
+ 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 ip mfib"))
+
+ #
+ # If we sleep for the threshold time, the learnt endpoints should
+ # 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 GARP packets received on the BD's mcast tunnel
+ #
+ for ii, l in enumerate(learnt):
+ # add some junk in the reserved field of the vxlan-header
+ # next to the VNI. we should accept since reserved bits are
+ # ignored on rx.
+ 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, reserved2=0x80, 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(b"\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 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=112, flags=0x88, gpflags="D")
+ / Ether(src=l["mac"], dst=ep.mac)
+ / IP(src=l["ip"], dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+
+ for l in learnt:
+ self.assertFalse(
+ find_gbp_endpoint(self, vx_tun_l2_1.sw_if_index, mac=l["mac"])
+ )
+
+ #
+ # repeat
+ #
+ for l in learnt:
+ # a packet with an sclass from a known EPG
+ # set a reserved bit in addition to the G and I
+ # reserved bits should not be checked on rx.
+ 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=0xC8)
+ / Ether(src=l["mac"], dst=ep.mac)
+ / IP(src=l["ip"], dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+
+ self.assertTrue(
+ find_gbp_endpoint(self, vx_tun_l2_1.sw_if_index, mac=l["mac"])
+ )
+
+ #
+ # Static EP replies to dynamics
+ #
+ self.logger.info(self.vapi.cli("sh l2fib bd_id 1"))
+ for l in learnt:
+ p = (
+ Ether(src=ep.mac, dst=l["mac"])
+ / IP(dst=l["ip"], src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * 17, self.pg2)
+
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+ 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, 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)
+
+ for l in learnt:
+ self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index, mac=l["mac"])
+
+ #
+ # repeat in the other EPG
+ # there's no contract between 220 and 330, but the A-bit is set
+ # so the packet is cleared for delivery
+ #
+ for l in learnt:
+ # 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=113, flags=0x88, gpflags="A")
+ / Ether(src=l["mac"], dst=ep.mac)
+ / IP(src=l["ip"], dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+
+ self.assertTrue(
+ find_gbp_endpoint(self, vx_tun_l2_1.sw_if_index, mac=l["mac"])
+ )
+
+ #
+ # static EP cannot reach the learnt EPs since there is no contract
+ # only test 1 EP as the others could timeout
+ #
+ p = (
+ Ether(src=ep.mac, dst=l["mac"])
+ / IP(dst=learnt[0]["ip"], src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_assert_no_replies(self.pg0, [p])
+
+ #
+ # refresh the entries after the check for no replies above
+ #
+ for l in learnt:
+ # 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=113, flags=0x88, gpflags="A")
+ / Ether(src=l["mac"], dst=ep.mac)
+ / IP(src=l["ip"], dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+
+ self.assertTrue(
+ find_gbp_endpoint(self, vx_tun_l2_1.sw_if_index, mac=l["mac"])
+ )
+
+ #
+ # Add the contract so they can talk
+ #
+ rule = AclRule(is_permit=1, proto=17)
+ rule2 = AclRule(
+ src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)),
+ is_permit=1,
+ proto=17,
+ )
+ acl = VppAcl(self, rules=[rule, rule2])
+ acl.add_vpp_config()
+
+ c1 = VppGbpContract(
+ self,
+ 401,
+ epg_220.sclass,
+ epg_330.sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c1.add_vpp_config()
+
+ for l in learnt:
+ p = (
+ Ether(src=ep.mac, dst=l["mac"])
+ / IP(dst=l["ip"], src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect(self.pg0, [p], self.pg2)
+
+ #
+ # send UU packets from the local EP
+ #
+ self.logger.info(self.vapi.cli("sh gbp bridge"))
+ self.logger.info(self.vapi.cli("sh bridge-domain 1 detail"))
+ p_uu = (
+ Ether(src=ep.mac, dst="00:11:11:11:11:11")
+ / IP(dst="10.0.0.133", src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ rxs = self.send_and_expect(ep.itf, [p_uu], gbd1.uu_fwd)
+
+ self.logger.info(self.vapi.cli("sh bridge 1 detail"))
+
+ p_bm = (
+ Ether(src=ep.mac, dst="ff:ff:ff:ff:ff:ff")
+ / IP(dst="10.0.0.133", src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\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)
+
+ rule = AclRule(is_permit=1, proto=17)
+ rule2 = AclRule(
+ src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)),
+ is_permit=1,
+ proto=17,
+ )
+ acl = VppAcl(self, rules=[rule, rule2])
+ acl.add_vpp_config()
+
+ c2 = VppGbpContract(
+ self,
+ 401,
+ epg_330.sclass,
+ epg_220.sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c2.add_vpp_config()
+
+ for l in learnt:
+ self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index, mac=l["mac"])
+ #
+ # Check v6 Endpoints learning
+ #
+ for l in learnt:
+ # 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=113, flags=0x88)
+ / Ether(src=l["mac"], dst=ep.mac)
+ / IPv6(src=l["ip6"], dst=ep.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+ rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+
+ self.assertTrue(
+ find_gbp_endpoint(
+ self,
+ vx_tun_l2_1.sw_if_index,
+ ip=l["ip6"],
+ tep=[self.pg2.local_ip4, self.pg2.remote_hosts[1].ip4],
+ )
+ )
+
+ self.logger.info(self.vapi.cli("sh int"))
+ self.logger.info(self.vapi.cli("sh vxlan-gbp tunnel"))
+ self.logger.info(self.vapi.cli("sh gbp vxlan"))
+ self.logger.info(self.vapi.cli("sh gbp endpoint"))
+ self.logger.info(self.vapi.cli("sh gbp interface"))
+
+ #
+ # EP moves to a different TEP
+ #
+ for l in learnt:
+ # 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[2].ip4, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=99, gpid=113, flags=0x88)
+ / Ether(src=l["mac"], dst=ep.mac)
+ / IPv6(src=l["ip6"], dst=ep.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, p * 1, self.pg0)
+ rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+
+ self.assertTrue(
+ find_gbp_endpoint(
+ self,
+ vx_tun_l2_1.sw_if_index,
+ sclass=113,
+ mac=l["mac"],
+ tep=[self.pg2.local_ip4, self.pg2.remote_hosts[2].ip4],
+ )
+ )
+
+ #
+ # v6 remote EP reachability
+ #
+ for l in learnt:
+ p = (
+ Ether(src=ep.mac, dst=l["mac"])
+ / IPv6(dst=l["ip6"], src=ep.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2)
+
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[2].ip4)
+ 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, 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.assertEqual(rx[IPv6].dst, l["ip6"])
+
+ #
+ # EP changes sclass
+ #
+ for l in learnt:
+ # 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[2].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)
+ / IPv6(src=l["ip6"], dst=ep.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, p * 1, self.pg0)
+ rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+
+ self.assertTrue(
+ find_gbp_endpoint(
+ self,
+ vx_tun_l2_1.sw_if_index,
+ mac=l["mac"],
+ sclass=112,
+ tep=[self.pg2.local_ip4, self.pg2.remote_hosts[2].ip4],
+ )
+ )
+
+ #
+ # check reachability and contract intra-epg
+ #
+ allow_intra_class = self.statistics.get_err_counter(
+ "/err/gbp-policy-mac/allow-intra-sclass"
+ )
+
+ for l in learnt:
+ p = (
+ Ether(src=ep.mac, dst=l["mac"])
+ / IPv6(dst=l["ip6"], src=ep.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2)
+
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[2].ip4)
+ self.assertEqual(rx[UDP].dport, 48879)
+ 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.assertEqual(rx[IPv6].dst, l["ip6"])
+
+ allow_intra_class += NUM_PKTS
+
+ self.assert_error_counter_equal(
+ "/err/gbp-policy-mac/allow-intra-sclass", allow_intra_class
+ )
+
+ #
+ # clean up
+ #
+ for l in learnt:
+ self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index, mac=l["mac"])
+ self.pg2.unconfig_ip4()
+ self.pg3.unconfig_ip4()
+ self.pg4.unconfig_ip4()
+
+ def test_gbp_contract(self):
+ """GBP Contracts"""
+
+ #
+ # Route Domains
+ #
+ gt4 = VppIpTable(self, 0)
+ gt4.add_vpp_config()
+ gt6 = VppIpTable(self, 0, is_ip6=True)
+ gt6.add_vpp_config()
+
+ rd0 = VppGbpRouteDomain(self, 0, 400, gt4, gt6, None, None)
+
+ rd0.add_vpp_config()
+
+ #
+ # Bridge Domains
+ #
+ bd1 = VppBridgeDomain(self, 1, arp_term=0)
+ bd2 = VppBridgeDomain(self, 2, arp_term=0)
+
+ bd1.add_vpp_config()
+ bd2.add_vpp_config()
+
+ gbd1 = VppGbpBridgeDomain(self, bd1, rd0, self.loop0)
+ gbd2 = VppGbpBridgeDomain(self, bd2, rd0, self.loop1)
+
+ gbd1.add_vpp_config()
+ gbd2.add_vpp_config()
+
+ #
+ # 3 EPGs, 2 of which share a BD.
+ #
+ epgs = [
+ VppGbpEndpointGroup(
+ self,
+ 220,
+ 1220,
+ rd0,
+ gbd1,
+ None,
+ self.loop0,
+ "10.0.0.128",
+ "2001:10::128",
+ ),
+ VppGbpEndpointGroup(
+ self,
+ 221,
+ 1221,
+ rd0,
+ gbd1,
+ None,
+ self.loop0,
+ "10.0.1.128",
+ "2001:10:1::128",
+ ),
+ VppGbpEndpointGroup(
+ self,
+ 222,
+ 1222,
+ rd0,
+ gbd2,
+ None,
+ self.loop1,
+ "10.0.2.128",
+ "2001:10:2::128",
+ ),
+ ]
+ #
+ # 4 end-points, 2 in the same subnet, 3 in the same BD
+ #
+ eps = [
+ VppGbpEndpoint(
+ self,
+ self.pg0,
+ epgs[0],
+ None,
+ "10.0.0.1",
+ "11.0.0.1",
+ "2001:10::1",
+ "3001::1",
+ ),
+ VppGbpEndpoint(
+ self,
+ self.pg1,
+ epgs[0],
+ None,
+ "10.0.0.2",
+ "11.0.0.2",
+ "2001:10::2",
+ "3001::2",
+ ),
+ VppGbpEndpoint(
+ self,
+ self.pg2,
+ epgs[1],
+ None,
+ "10.0.1.1",
+ "11.0.0.3",
+ "2001:10:1::1",
+ "3001::3",
+ ),
+ VppGbpEndpoint(
+ self,
+ self.pg3,
+ epgs[2],
+ None,
+ "10.0.2.1",
+ "11.0.0.4",
+ "2001:10:2::1",
+ "3001::4",
+ ),
+ ]
+
+ #
+ # Config related to each of the EPGs
+ #
+ for epg in epgs:
+ # IP config on the BVI interfaces
+ if epg != epgs[1]:
+ b4 = VppIpInterfaceBind(self, epg.bvi, epg.rd.t4).add_vpp_config()
+ b6 = VppIpInterfaceBind(self, epg.bvi, epg.rd.t6).add_vpp_config()
+ epg.bvi.set_mac(self.router_mac)
+
+ if_ip4 = VppIpInterfaceAddress(
+ self, epg.bvi, epg.bvi_ip4, 32, bind=b4
+ ).add_vpp_config()
+ if_ip6 = VppIpInterfaceAddress(
+ self, epg.bvi, epg.bvi_ip6, 128, bind=b6
+ ).add_vpp_config()
+
+ # add the BD ARP termination entry for BVI IP
+ epg.bd_arp_ip4 = VppBridgeDomainArpEntry(
+ self, epg.bd.bd, str(self.router_mac), epg.bvi_ip4
+ )
+ epg.bd_arp_ip4.add_vpp_config()
+
+ # EPG in VPP
+ epg.add_vpp_config()
+
+ #
+ # config ep
+ #
+ for ep in eps:
+ ep.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("show gbp endpoint"))
+ self.logger.info(self.vapi.cli("show interface"))
+ self.logger.info(self.vapi.cli("show br"))
+
+ #
+ # Intra epg allowed without contract
+ #
+ pkt_intra_epg_220_to_220 = (
+ Ether(src=self.pg0.remote_mac, dst=self.pg1.remote_mac)
+ / IP(src=eps[0].ip4, dst=eps[1].ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_bridged(self.pg0, pkt_intra_epg_220_to_220 * 65, self.pg1)
+
+ pkt_intra_epg_220_to_220 = (
+ Ether(src=self.pg0.remote_mac, dst=self.pg1.remote_mac)
+ / IPv6(src=eps[0].ip6, dst=eps[1].ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect_bridged6(self.pg0, pkt_intra_epg_220_to_220 * 65, self.pg1)
+
+ #
+ # Inter epg denied without contract
+ #
+ pkt_inter_epg_220_to_221 = (
+ Ether(src=self.pg0.remote_mac, dst=self.pg2.remote_mac)
+ / IP(src=eps[0].ip4, dst=eps[2].ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_assert_no_replies(self.pg0, pkt_inter_epg_220_to_221)
+
+ #
+ # A uni-directional contract from EPG 220 -> 221
+ #
+ rule = AclRule(is_permit=1, proto=17)
+ rule2 = AclRule(
+ src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)),
+ is_permit=1,
+ proto=17,
+ )
+ rule3 = AclRule(is_permit=1, proto=1)
+ acl = VppAcl(self, rules=[rule, rule2, rule3])
+ acl.add_vpp_config()
+
+ c1 = VppGbpContract(
+ self,
+ 400,
+ epgs[0].sclass,
+ epgs[1].sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c1.add_vpp_config()
+
+ self.send_and_expect_bridged(
+ eps[0].itf, pkt_inter_epg_220_to_221 * 65, eps[2].itf
+ )
+
+ pkt_inter_epg_220_to_222 = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src=eps[0].ip4, dst=eps[3].ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_222 * 65)
+
+ #
+ # ping router IP in different BD
+ #
+ pkt_router_ping_220_to_221 = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src=eps[0].ip4, dst=epgs[1].bvi_ip4)
+ / ICMP(type="echo-request")
+ )
+
+ self.send_and_expect(self.pg0, [pkt_router_ping_220_to_221], self.pg0)
+
+ pkt_router_ping_220_to_221 = (
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IPv6(src=eps[0].ip6, dst=epgs[1].bvi_ip6)
+ / ICMPv6EchoRequest()
+ )
+
+ self.send_and_expect(self.pg0, [pkt_router_ping_220_to_221], self.pg0)
+
+ #
+ # contract for the return direction
+ #
+ c2 = VppGbpContract(
+ self,
+ 400,
+ epgs[1].sclass,
+ epgs[0].sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c2.add_vpp_config()
+
+ self.send_and_expect_bridged(
+ eps[0].itf, pkt_inter_epg_220_to_221 * 65, eps[2].itf
+ )
+ pkt_inter_epg_221_to_220 = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg0.remote_mac)
+ / IP(src=eps[2].ip4, dst=eps[0].ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ self.send_and_expect_bridged(
+ eps[2].itf, pkt_inter_epg_221_to_220 * 65, eps[0].itf
+ )
+ pkt_inter_epg_221_to_220 = (
+ Ether(src=self.pg2.remote_mac, dst=str(self.router_mac))
+ / IP(src=eps[2].ip4, dst=eps[0].ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ self.send_and_expect_routed(
+ eps[2].itf, pkt_inter_epg_221_to_220 * 65, eps[0].itf, str(self.router_mac)
+ )
+ pkt_inter_epg_221_to_220 = (
+ Ether(src=self.pg2.remote_mac, dst=str(self.router_mac))
+ / IPv6(src=eps[2].ip6, dst=eps[0].ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ self.send_and_expect_routed6(
+ eps[2].itf, pkt_inter_epg_221_to_220 * 65, eps[0].itf, str(self.router_mac)
+ )
+
+ #
+ # contract between 220 and 222 uni-direction
+ #
+ c3 = VppGbpContract(
+ self,
+ 400,
+ epgs[0].sclass,
+ epgs[2].sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c3.add_vpp_config()
+
+ self.send_and_expect(eps[0].itf, pkt_inter_epg_220_to_222 * 65, eps[3].itf)
+
+ c3.remove_vpp_config()
+ c1.remove_vpp_config()
+ c2.remove_vpp_config()
+ acl.remove_vpp_config()
+
+ def test_gbp_bd_drop_flags(self):
+ """GBP BD drop flags"""
+
+ #
+ # IP tables
+ #
+ gt4 = VppIpTable(self, 1)
+ gt4.add_vpp_config()
+ gt6 = VppIpTable(self, 1, is_ip6=True)
+ gt6.add_vpp_config()
+
+ rd1 = VppGbpRouteDomain(self, 1, 401, gt4, gt6)
+ rd1.add_vpp_config()
+
+ #
+ # a GBP bridge domain with a BVI only
+ #
+ bd1 = VppBridgeDomain(self, 1)
+ bd1.add_vpp_config()
+
+ gbd1 = VppGbpBridgeDomain(
+ self, bd1, rd1, self.loop0, None, None, uu_drop=True, bm_drop=True
+ )
+ gbd1.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh bridge 1 detail"))
+ self.logger.info(self.vapi.cli("sh gbp bridge"))
+
+ # ... and has a /32 applied
+ ip_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "10.0.0.128", 32
+ ).add_vpp_config()
+
+ #
+ # The Endpoint-group
+ #
+ epg_220 = VppGbpEndpointGroup(
+ self,
+ 220,
+ 112,
+ rd1,
+ gbd1,
+ None,
+ self.loop0,
+ "10.0.0.128",
+ "2001:10::128",
+ VppGbpEndpointRetention(3),
+ )
+ epg_220.add_vpp_config()
+
+ ep = VppGbpEndpoint(
+ self,
+ self.pg0,
+ epg_220,
+ None,
+ "10.0.0.127",
+ "11.0.0.127",
+ "2001:10::1",
+ "3001::1",
+ )
+ ep.add_vpp_config()
+
+ #
+ # send UU/BM packet from the local EP with UU drop and BM drop enabled
+ # in bd
+ #
+ self.logger.info(self.vapi.cli("sh bridge 1 detail"))
+ self.logger.info(self.vapi.cli("sh gbp bridge"))
+ p_uu = (
+ Ether(src=ep.mac, dst="00:11:11:11:11:11")
+ / IP(dst="10.0.0.133", src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ self.send_and_assert_no_replies(ep.itf, [p_uu])
+
+ p_bm = (
+ Ether(src=ep.mac, dst="ff:ff:ff:ff:ff:ff")
+ / IP(dst="10.0.0.133", src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ self.send_and_assert_no_replies(ep.itf, [p_bm])
+
+ self.pg3.unconfig_ip4()
+
+ self.logger.info(self.vapi.cli("sh int"))
+
+ def test_gbp_bd_arp_flags(self):
+ """GBP BD arp flags"""
+
+ #
+ # IP tables
+ #
+ gt4 = VppIpTable(self, 1)
+ gt4.add_vpp_config()
+ gt6 = VppIpTable(self, 1, is_ip6=True)
+ gt6.add_vpp_config()
+
+ rd1 = VppGbpRouteDomain(self, 1, 401, gt4, gt6)
+ rd1.add_vpp_config()
+
+ #
+ # Pg4 hosts the IP6 UU-flood VXLAN tunnel
+ #
+ self.pg4.config_ip4()
+ self.pg4.resolve_arp()
+
+ #
+ # Add a mcast destination VXLAN-GBP tunnel for B&M traffic
+ #
+ tun_uu = VppVxlanGbpTunnel(
+ self, self.pg4.local_ip4, "239.1.1.1", 88, mcast_itf=self.pg4
+ )
+ tun_uu.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, rd1, self.loop0, tun_uu, None, ucast_arp=True
+ )
+ gbd1.add_vpp_config()
+
+ # ... and has a /32 applied
+ ip_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "10.0.0.128", 32
+ ).add_vpp_config()
+
+ #
+ # The Endpoint-group
+ #
+ epg_220 = VppGbpEndpointGroup(
+ self,
+ 220,
+ 112,
+ rd1,
+ gbd1,
+ None,
+ self.loop0,
+ "10.0.0.128",
+ "2001:10::128",
+ VppGbpEndpointRetention(2),
+ )
+ epg_220.add_vpp_config()
+
+ ep = VppGbpEndpoint(
+ self,
+ self.pg0,
+ epg_220,
+ None,
+ "10.0.0.127",
+ "11.0.0.127",
+ "2001:10::1",
+ "3001::1",
+ )
+ ep.add_vpp_config()
+
+ #
+ # send ARP packet from the local EP expect it on the uu interface
+ #
+ self.logger.info(self.vapi.cli("sh bridge 1 detail"))
+ self.logger.info(self.vapi.cli("sh gbp bridge"))
+ p_arp = Ether(src=ep.mac, dst="ff:ff:ff:ff:ff:ff") / ARP(
+ op="who-has",
+ psrc=ep.ip4,
+ pdst="10.0.0.99",
+ hwsrc=ep.mac,
+ hwdst="ff:ff:ff:ff:ff:ff",
+ )
+ self.send_and_expect(ep.itf, [p_arp], self.pg4)
+
+ self.pg4.unconfig_ip4()
+
+ 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"},
+ {"mac": "00:00:11:11:11:02", "ip": "10.0.0.2", "ip6": "2001:10::3"},
+ ]
+
+ #
+ # IP tables
+ #
+ gt4 = VppIpTable(self, 1)
+ gt4.add_vpp_config()
+ gt6 = VppIpTable(self, 1, is_ip6=True)
+ gt6.add_vpp_config()
+
+ rd1 = VppGbpRouteDomain(self, 1, 401, gt4, gt6)
+ rd1.add_vpp_config()
+
+ #
+ # Pg2 hosts the vxlan tunnel, hosts on pg2 to act as TEPs
+ #
+ self.pg2.config_ip4()
+ self.pg2.resolve_arp()
+ self.pg2.generate_remote_hosts(4)
+ self.pg2.configure_ipv4_neighbors()
+ self.pg3.config_ip4()
+ self.pg3.resolve_arp()
+
+ #
+ # The EP will be on a vlan sub-interface
+ #
+ vlan_11 = VppDot1QSubint(self, self.pg0, 11)
+ vlan_11.admin_up()
+ 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
+ )
+ bd_uu_fwd.add_vpp_config()
+
+ #
+ # a GBP bridge domain with a BVI and a UU-flood interface
+ # The BD is marked as do not learn, so no endpoints are ever
+ # learnt in this BD.
+ #
+ bd1 = VppBridgeDomain(self, 1)
+ bd1.add_vpp_config()
+ gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, bd_uu_fwd, learn=False)
+ gbd1.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh bridge 1 detail"))
+ self.logger.info(self.vapi.cli("sh gbp bridge"))
+
+ # ... and has a /32 applied
+ ip_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "10.0.0.128", 32
+ ).add_vpp_config()
+
+ #
+ # The Endpoint-group in which we are learning endpoints
+ #
+ epg_220 = VppGbpEndpointGroup(
+ self,
+ 220,
+ 441,
+ rd1,
+ gbd1,
+ None,
+ self.loop0,
+ "10.0.0.128",
+ "2001:10::128",
+ VppGbpEndpointRetention(4),
+ )
+ epg_220.add_vpp_config()
+
+ #
+ # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint
+ # 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,
+ self.pg2.local_ip4,
+ )
+ vx_tun_l2_1.add_vpp_config()
+
+ #
+ # A static endpoint that the learnt endpoints are trying to
+ # talk to
+ #
+ ep = VppGbpEndpoint(
+ self,
+ vlan_11,
+ epg_220,
+ None,
+ "10.0.0.127",
+ "11.0.0.127",
+ "2001:10::1",
+ "3001::1",
+ )
+ ep.add_vpp_config()
+
+ self.assertTrue(find_route(self, ep.ip4, 32, table_id=1))
+
+ #
+ # Send to the static EP
+ #
+ 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=441, flags=0x88)
+ / Ether(src=l["mac"], dst=ep.mac)
+ / IP(src=l["ip"], dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg2, [p], self.pg0)
+
+ #
+ # packet to EP has the EP's vlan tag
+ #
+ for rx in rxs:
+ self.assertEqual(rx[Dot1Q].vlan, 11)
+
+ #
+ # the EP is not learnt since the BD setting prevents it
+ # also no TEP too
+ #
+ self.assertFalse(
+ find_gbp_endpoint(self, vx_tun_l2_1.sw_if_index, mac=l["mac"])
+ )
+ self.assertEqual(
+ INDEX_INVALID,
+ find_vxlan_gbp_tunnel(
+ self, self.pg2.local_ip4, self.pg2.remote_hosts[1].ip4, 99
+ ),
+ )
+
+ self.assertEqual(len(self.vapi.gbp_endpoint_dump()), 1)
+
+ #
+ # static to remotes
+ # we didn't learn the remotes so they are sent to the UU-fwd
+ #
+ for l in learnt:
+ p = (
+ Ether(src=ep.mac, dst=l["mac"])
+ / Dot1Q(vlan=11)
+ / IP(dst=l["ip"], src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * 17, self.pg3)
+
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg3.local_ip4)
+ 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, 441)
+ self.assertEqual(rx[VXLAN].vni, 116)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ self.assertFalse(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ self.pg2.unconfig_ip4()
+ self.pg3.unconfig_ip4()
+
+ def test_gbp_learn_l3(self):
+ """GBP L3 Endpoint Learning"""
+
+ self.vapi.cli("set logging class gbp level 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"
+
+ learnt = [
+ {"mac": "00:00:11:11:11:02", "ip": "10.0.1.2", "ip6": "2001:10::2"},
+ {"mac": "00:00:11:11:11:03", "ip": "10.0.1.3", "ip6": "2001:10::3"},
+ ]
+
+ #
+ # IP tables
+ #
+ t4 = VppIpTable(self, 1)
+ t4.add_vpp_config()
+ t6 = VppIpTable(self, 1, True)
+ t6.add_vpp_config()
+
+ tun_ip4_uu = VppVxlanGbpTunnel(
+ self, self.pg4.local_ip4, self.pg4.remote_ip4, 114
+ )
+ tun_ip6_uu = VppVxlanGbpTunnel(
+ self, self.pg4.local_ip4, self.pg4.remote_ip4, 116
+ )
+ tun_ip4_uu.add_vpp_config()
+ tun_ip6_uu.add_vpp_config()
+
+ rd1 = VppGbpRouteDomain(self, 2, 401, t4, t6, tun_ip4_uu, tun_ip6_uu)
+ rd1.add_vpp_config()
+
+ self.loop0.set_mac(self.router_mac)
+
+ #
+ # Bind the BVI to the RD
+ #
+ b4 = VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
+ b6 = VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
+
+ #
+ # Pg2 hosts the vxlan tunnel
+ # hosts on pg2 to act as TEPs
+ # pg3 is BD uu-fwd
+ # pg4 is RD uu-fwd
+ #
+ self.pg2.config_ip4()
+ self.pg2.resolve_arp()
+ self.pg2.generate_remote_hosts(4)
+ self.pg2.configure_ipv4_neighbors()
+ self.pg3.config_ip4()
+ self.pg3.resolve_arp()
+ self.pg4.config_ip4()
+ self.pg4.resolve_arp()
+
+ #
+ # a GBP bridge domain with a BVI and a UU-flood interface
+ #
+ bd1 = VppBridgeDomain(self, 1)
+ bd1.add_vpp_config()
+ gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, self.pg3)
+ gbd1.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh bridge 1 detail"))
+ self.logger.info(self.vapi.cli("sh gbp bridge"))
+ self.logger.info(self.vapi.cli("sh gbp route"))
+
+ # ... and has a /32 and /128 applied
+ ip4_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "10.0.0.128", 32, bind=b4
+ ).add_vpp_config()
+ ip6_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "2001:10::128", 128, bind=b6
+ ).add_vpp_config()
+
+ #
+ # The Endpoint-group in which we are learning endpoints
+ #
+ epg_220 = VppGbpEndpointGroup(
+ self,
+ 220,
+ 441,
+ rd1,
+ gbd1,
+ None,
+ self.loop0,
+ "10.0.0.128",
+ "2001:10::128",
+ VppGbpEndpointRetention(4),
+ )
+ epg_220.add_vpp_config()
+
+ #
+ # The VXLAN GBP tunnel is in L3 mode with learning enabled
+ #
+ vx_tun_l3 = VppGbpVxlanTunnel(
+ self,
+ 101,
+ 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()
+
+ #
+ # A static endpoint that the learnt endpoints are trying to
+ # talk to
+ #
+ ep = VppGbpEndpoint(
+ self,
+ self.pg0,
+ epg_220,
+ None,
+ "10.0.0.127",
+ "11.0.0.127",
+ "2001:10::1",
+ "3001::1",
+ )
+ ep.add_vpp_config()
+
+ #
+ # learn some remote IPv4 EPs
+ #
+ 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=101, gpid=441, flags=0x88)
+ / Ether(src=l["mac"], dst="00:00:00:11:11:11")
+ / IP(src=l["ip"], dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\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, vx_tun_l3.vni
+ )
+ self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
+
+ # endpoint learnt via the parent GBP-vxlan interface
+ self.assertTrue(find_gbp_endpoint(self, vx_tun_l3._sw_if_index, ip=l["ip"]))
+
+ #
+ # Static IPv4 EP replies to learnt
+ #
+ for l in learnt:
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IP(dst=l["ip"], src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * 1, self.pg2)
+
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+ 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, 441)
+ self.assertEqual(rx[VXLAN].vni, 101)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, routed_dst_mac)
+ self.assertEqual(inner[IP].src, ep.ip4)
+ self.assertEqual(inner[IP].dst, l["ip"])
+
+ for l in learnt:
+ self.assertFalse(find_gbp_endpoint(self, tep1_sw_if_index, ip=l["ip"]))
+
+ #
+ # learn some remote IPv6 EPs
+ #
+ 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=101, gpid=441, flags=0x88)
+ / Ether(src=l["mac"], dst="00:00:00:11:11:11")
+ / IPv6(src=l["ip6"], dst=ep.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\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, vx_tun_l3.vni
+ )
+ self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
+
+ self.logger.info(self.vapi.cli("show gbp bridge"))
+ self.logger.info(self.vapi.cli("show vxlan-gbp tunnel"))
+ self.logger.info(self.vapi.cli("show gbp vxlan"))
+ self.logger.info(self.vapi.cli("show int addr"))
+
+ # endpoint learnt via the TEP
+ self.assertTrue(find_gbp_endpoint(self, ip=l["ip6"]))
+
+ self.logger.info(self.vapi.cli("show gbp endpoint"))
+ self.logger.info(self.vapi.cli("show ip fib index 1 %s" % l["ip"]))
+
+ #
+ # Static EP replies to learnt
+ #
+ for l in learnt:
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IPv6(dst=l["ip6"], src=ep.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2)
+
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+ 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, 441)
+ self.assertEqual(rx[VXLAN].vni, 101)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, routed_dst_mac)
+ self.assertEqual(inner[IPv6].src, ep.ip6)
+ self.assertEqual(inner[IPv6].dst, l["ip6"])
+
+ self.logger.info(self.vapi.cli("sh gbp endpoint"))
+ for l in learnt:
+ self.wait_for_ep_timeout(ip=l["ip"])
+
+ #
+ # Static sends to unknown EP with no route
+ #
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IP(dst="10.0.0.99", src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_assert_no_replies(self.pg0, [p])
+
+ #
+ # Add a route to static EP's v4 and v6 subnet
+ #
+ se_10_24 = VppGbpSubnet(
+ self,
+ rd1,
+ "10.0.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT,
+ )
+ se_10_24.add_vpp_config()
+
+ #
+ # static pings router
+ #
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IP(dst=epg_220.bvi_ip4, src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg0)
+
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IPv6(dst=epg_220.bvi_ip6, src=ep.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg0)
+
+ #
+ # packets to address in the subnet are sent on the uu-fwd
+ #
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IP(dst="10.0.0.99", src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, [p], self.pg4)
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg4.local_ip4)
+ 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, 441)
+ self.assertEqual(rx[VXLAN].vni, 114)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # policy is not applied to packets sent to the uu-fwd interfaces
+ self.assertFalse(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ #
+ # learn some remote IPv4 EPs
+ #
+ 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[2].ip4, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / 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)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\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[2].ip4, vx_tun_l3.vni
+ )
+ self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
+
+ # endpoint learnt via the parent GBP-vxlan interface
+ self.assertTrue(find_gbp_endpoint(self, vx_tun_l3._sw_if_index, ip=l["ip"]))
+
+ #
+ # Add a remote endpoint from the API
+ #
+ rep_88 = VppGbpEndpoint(
+ self,
+ vx_tun_l3,
+ epg_220,
+ None,
+ "10.0.0.88",
+ "11.0.0.88",
+ "2001:10::88",
+ "3001::88",
+ ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
+ self.pg2.local_ip4,
+ self.pg2.remote_hosts[2].ip4,
+ mac=None,
+ )
+ rep_88.add_vpp_config()
+
+ #
+ # Add a remote endpoint from the API that matches an existing one
+ # this is a lower priority, hence the packet is sent to the DP leanrt
+ # TEP
+ #
+ rep_2 = VppGbpEndpoint(
+ self,
+ vx_tun_l3,
+ epg_220,
+ None,
+ learnt[0]["ip"],
+ "11.0.0.101",
+ learnt[0]["ip6"],
+ "3001::101",
+ 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 learned EP's v4 subnet
+ # packets should be send on the v4/v6 uu=fwd interface resp.
+ #
+ se_10_1_24 = VppGbpSubnet(
+ self,
+ rd1,
+ "10.0.1.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT,
+ )
+ se_10_1_24.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("show gbp endpoint"))
+
+ ips = ["10.0.0.88", learnt[0]["ip"]]
+ for ip in ips:
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IP(dst=ip, src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2)
+
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[2].ip4)
+ self.assertEqual(rx[UDP].dport, 48879)
+ # the UDP source port is a random value for hashing
+ self.assertEqual(rx[VXLAN].gpid, 441)
+ self.assertEqual(rx[VXLAN].vni, 101)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, routed_dst_mac)
+ self.assertEqual(inner[IP].src, ep.ip4)
+ self.assertEqual(inner[IP].dst, ip)
+
+ #
+ # remove the API remote EPs, only API sourced is gone, the DP
+ # learnt one remains
+ #
+ rep_88.remove_vpp_config()
+ rep_2.remove_vpp_config()
+
+ self.assertTrue(find_gbp_endpoint(self, ip=rep_2.ip4))
+
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IP(src=ep.ip4, dst=rep_2.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ rxs = self.send_and_expect(self.pg0, [p], self.pg2)
+
+ self.assertFalse(find_gbp_endpoint(self, ip=rep_88.ip4))
+
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IP(src=ep.ip4, dst=rep_88.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ rxs = self.send_and_expect(self.pg0, [p], self.pg4)
+
+ #
+ # 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.wait_for_ep_timeout(ip=rep_88.ip4)
+ self.wait_for_ep_timeout(ip=rep_2.ip4)
+
+ #
+ # Same as above, learn a remote EP via CP and DP
+ # this time remove the DP one first. expect the CP data to remain
+ #
+ rep_3 = VppGbpEndpoint(
+ self,
+ vx_tun_l3,
+ epg_220,
+ None,
+ "10.0.1.4",
+ "11.0.0.103",
+ "2001::10:3",
+ "3001::103",
+ ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
+ self.pg2.local_ip4,
+ self.pg2.remote_hosts[1].ip4,
+ mac=None,
+ )
+ rep_3.add_vpp_config()
+
+ p = (
+ Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
+ / IP(src=self.pg2.remote_hosts[2].ip4, dst=self.pg2.local_ip4)
+ / UDP(sport=1234, dport=48879)
+ / VXLAN(vni=101, gpid=441, flags=0x88)
+ / Ether(src=l["mac"], dst="00:00:00:11:11:11")
+ / IP(src="10.0.1.4", dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ rxs = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+
+ self.assertTrue(
+ find_gbp_endpoint(
+ self,
+ vx_tun_l3._sw_if_index,
+ ip=rep_3.ip4,
+ tep=[self.pg2.local_ip4, self.pg2.remote_hosts[2].ip4],
+ )
+ )
+
+ p = (
+ Ether(src=ep.mac, dst=self.loop0.local_mac)
+ / IP(dst="10.0.1.4", src=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2)
+
+ # host 2 is the DP learned TEP
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[2].ip4)
+
+ self.wait_for_ep_timeout(
+ ip=rep_3.ip4, tep=[self.pg2.local_ip4, self.pg2.remote_hosts[2].ip4]
+ )
+
+ rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2)
+
+ # host 1 is the CP learned TEP
+ for rx in rxs:
+ self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
+
+ #
+ # shutdown with learnt endpoint present
+ #
+ 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=441, flags=0x88)
+ / Ether(src=l["mac"], dst="00:00:00:11:11:11")
+ / IP(src=learnt[1]["ip"], dst=ep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rx = self.send_and_expect(self.pg2, [p], self.pg0)
+
+ # endpoint learnt via the parent GBP-vxlan interface
+ self.assertTrue(find_gbp_endpoint(self, vx_tun_l3._sw_if_index, ip=l["ip"]))
+
+ #
+ # TODO
+ # remote endpoint becomes local
+ #
+ self.pg2.unconfig_ip4()
+ self.pg3.unconfig_ip4()
+ self.pg4.unconfig_ip4()
+
+ def test_gbp_redirect(self):
+ """GBP Endpoint Redirect"""
+
+ self.vapi.cli("set logging class gbp level 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"
+
+ learnt = [
+ {"mac": "00:00:11:11:11:02", "ip": "10.0.1.2", "ip6": "2001:10::2"},
+ {"mac": "00:00:11:11:11:03", "ip": "10.0.1.3", "ip6": "2001:10::3"},
+ ]
+
+ #
+ # IP tables
+ #
+ t4 = VppIpTable(self, 1)
+ t4.add_vpp_config()
+ t6 = VppIpTable(self, 1, True)
+ t6.add_vpp_config()
+
+ rd1 = VppGbpRouteDomain(self, 2, 402, t4, t6)
+ rd1.add_vpp_config()
+
+ self.loop0.set_mac(self.router_mac)
+
+ #
+ # Bind the BVI to the RD
+ #
+ b_ip4 = VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
+ b_ip6 = VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
+
+ #
+ # Pg7 hosts a BD's UU-fwd
+ #
+ self.pg7.config_ip4()
+ self.pg7.resolve_arp()
+
+ #
+ # a GBP bridge domains for the EPs
+ #
+ bd1 = VppBridgeDomain(self, 1)
+ bd1.add_vpp_config()
+ gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0)
+ gbd1.add_vpp_config()
+
+ bd2 = VppBridgeDomain(self, 2)
+ bd2.add_vpp_config()
+ gbd2 = VppGbpBridgeDomain(self, bd2, rd1, self.loop1)
+ gbd2.add_vpp_config()
+
+ # ... and has a /32 and /128 applied
+ ip4_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "10.0.0.128", 32, bind=b_ip4
+ ).add_vpp_config()
+ ip6_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "2001:10::128", 128, bind=b_ip6
+ ).add_vpp_config()
+ ip4_addr = VppIpInterfaceAddress(
+ self, gbd2.bvi, "10.0.1.128", 32
+ ).add_vpp_config()
+ ip6_addr = VppIpInterfaceAddress(
+ self, gbd2.bvi, "2001:11::128", 128
+ ).add_vpp_config()
+
+ #
+ # The Endpoint-groups in which we are learning endpoints
+ #
+ epg_220 = VppGbpEndpointGroup(
+ self,
+ 220,
+ 440,
+ rd1,
+ gbd1,
+ None,
+ gbd1.bvi,
+ "10.0.0.128",
+ "2001:10::128",
+ VppGbpEndpointRetention(60),
+ )
+ epg_220.add_vpp_config()
+ epg_221 = VppGbpEndpointGroup(
+ self,
+ 221,
+ 441,
+ rd1,
+ gbd2,
+ None,
+ gbd2.bvi,
+ "10.0.1.128",
+ "2001:11::128",
+ VppGbpEndpointRetention(60),
+ )
+ epg_221.add_vpp_config()
+ epg_222 = VppGbpEndpointGroup(
+ self,
+ 222,
+ 442,
+ rd1,
+ gbd1,
+ None,
+ gbd1.bvi,
+ "10.0.2.128",
+ "2001:12::128",
+ VppGbpEndpointRetention(60),
+ )
+ epg_222.add_vpp_config()
+
+ #
+ # a GBP bridge domains for the SEPs
+ #
+ bd_uu1 = VppVxlanGbpTunnel(self, self.pg7.local_ip4, self.pg7.remote_ip4, 116)
+ bd_uu1.add_vpp_config()
+ bd_uu2 = VppVxlanGbpTunnel(self, self.pg7.local_ip4, self.pg7.remote_ip4, 117)
+ bd_uu2.add_vpp_config()
+
+ bd3 = VppBridgeDomain(self, 3)
+ bd3.add_vpp_config()
+ gbd3 = VppGbpBridgeDomain(self, bd3, rd1, self.loop2, bd_uu1, learn=False)
+ gbd3.add_vpp_config()
+ bd4 = VppBridgeDomain(self, 4)
+ bd4.add_vpp_config()
+ gbd4 = VppGbpBridgeDomain(self, bd4, rd1, self.loop3, bd_uu2, learn=False)
+ gbd4.add_vpp_config()
+
+ #
+ # EPGs in which the service endpoints exist
+ #
+ epg_320 = VppGbpEndpointGroup(
+ self,
+ 320,
+ 550,
+ rd1,
+ gbd3,
+ None,
+ gbd1.bvi,
+ "12.0.0.128",
+ "4001:10::128",
+ VppGbpEndpointRetention(60),
+ )
+ epg_320.add_vpp_config()
+ epg_321 = VppGbpEndpointGroup(
+ self,
+ 321,
+ 551,
+ rd1,
+ gbd4,
+ None,
+ gbd2.bvi,
+ "12.0.1.128",
+ "4001:11::128",
+ VppGbpEndpointRetention(60),
+ )
+ epg_321.add_vpp_config()
+
+ #
+ # three local endpoints
+ #
+ ep1 = VppGbpEndpoint(
+ self,
+ self.pg0,
+ epg_220,
+ None,
+ "10.0.0.1",
+ "11.0.0.1",
+ "2001:10::1",
+ "3001:10::1",
+ )
+ ep1.add_vpp_config()
+ ep2 = VppGbpEndpoint(
+ self,
+ self.pg1,
+ epg_221,
+ None,
+ "10.0.1.1",
+ "11.0.1.1",
+ "2001:11::1",
+ "3001:11::1",
+ )
+ ep2.add_vpp_config()
+ ep3 = VppGbpEndpoint(
+ self,
+ self.pg2,
+ epg_222,
+ None,
+ "10.0.2.2",
+ "11.0.2.2",
+ "2001:12::1",
+ "3001:12::1",
+ )
+ ep3.add_vpp_config()
+
+ #
+ # service endpoints
+ #
+ sep1 = VppGbpEndpoint(
+ self,
+ self.pg3,
+ epg_320,
+ None,
+ "12.0.0.1",
+ "13.0.0.1",
+ "4001:10::1",
+ "5001:10::1",
+ )
+ sep1.add_vpp_config()
+ sep2 = VppGbpEndpoint(
+ self,
+ self.pg4,
+ epg_320,
+ None,
+ "12.0.0.2",
+ "13.0.0.2",
+ "4001:10::2",
+ "5001:10::2",
+ )
+ sep2.add_vpp_config()
+ sep3 = VppGbpEndpoint(
+ self,
+ self.pg5,
+ epg_321,
+ None,
+ "12.0.1.1",
+ "13.0.1.1",
+ "4001:11::1",
+ "5001:11::1",
+ )
+ sep3.add_vpp_config()
+ # this EP is not installed immediately
+ sep4 = VppGbpEndpoint(
+ self,
+ self.pg6,
+ epg_321,
+ None,
+ "12.0.1.2",
+ "13.0.1.2",
+ "4001:11::2",
+ "5001:11::2",
+ )
+
+ #
+ # an L2 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=ep3.mac)
+ / IP(src=ep1.ip4, dst=ep3.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=ep3.mac, dst=ep1.mac)
+ / IP(src=ep3.ip4, dst=ep1.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+ p6 = [
+ (
+ Ether(src=ep1.mac, dst=ep3.mac)
+ / IPv6(src=ep1.ip6, dst=ep3.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=ep3.mac, dst=ep1.mac)
+ / IPv6(src=ep3.ip6, dst=ep1.ip6)
+ / UDP(sport=1234, dport=1230)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+
+ # should be dropped since no contract yet
+ self.send_and_assert_no_replies(self.pg0, [p4[0]])
+ self.send_and_assert_no_replies(self.pg0, [p6[0]])
+
+ #
+ # Add a contract with a rule to load-balance redirect via SEP1 and SEP2
+ # one of the next-hops is via an EP that is not known
+ #
+ rule4 = AclRule(is_permit=1, proto=17)
+ rule6 = AclRule(
+ src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)),
+ is_permit=1,
+ proto=17,
+ )
+ acl = VppAcl(self, rules=[rule4, rule6])
+ acl.add_vpp_config()
+
+ #
+ # test the src-ip hash mode
+ #
+ c1 = VppGbpContract(
+ self,
+ 402,
+ epg_220.sclass,
+ epg_222.sclass,
+ acl.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,
+ [
+ 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_SRC_IP,
+ [
+ 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],
+ )
+ c1.add_vpp_config()
+
+ c2 = VppGbpContract(
+ self,
+ 402,
+ epg_222.sclass,
+ epg_220.sclass,
+ acl.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,
+ [
+ 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_SRC_IP,
+ [
+ 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],
+ )
+ c2.add_vpp_config()
+
+ #
+ # send again with the contract preset, now packets arrive
+ # at SEP1 or SEP2 depending on the hashing
+ #
+ rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, ep3.ip4)
+
+ rxs = self.send_and_expect(self.pg2, p4[1] * 17, sep2.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep2.mac)
+ self.assertEqual(rx[IP].src, ep3.ip4)
+ self.assertEqual(rx[IP].dst, ep1.ip4)
+
+ rxs = self.send_and_expect(self.pg0, p6[0] * 17, 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, 117)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, sep4.mac)
+ self.assertEqual(inner[IPv6].src, ep1.ip6)
+ self.assertEqual(inner[IPv6].dst, ep3.ip6)
+
+ rxs = self.send_and_expect(self.pg2, p6[1] * 17, sep3.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep3.mac)
+ self.assertEqual(rx[IPv6].src, ep3.ip6)
+ self.assertEqual(rx[IPv6].dst, ep1.ip6)
+
+ #
+ # programme the unknown EP
+ #
+ sep4.add_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p6[0] * 17, sep4.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep4.mac)
+ self.assertEqual(rx[IPv6].src, ep1.ip6)
+ self.assertEqual(rx[IPv6].dst, ep3.ip6)
+
+ #
+ # and revert back to unprogrammed
+ #
+ sep4.remove_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p6[0] * 17, 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, 117)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, sep4.mac)
+ self.assertEqual(inner[IPv6].src, ep1.ip6)
+ self.assertEqual(inner[IPv6].dst, ep3.ip6)
+
+ c1.remove_vpp_config()
+ c2.remove_vpp_config()
+
+ #
+ # test the symmetric hash mode
+ #
+ c1 = VppGbpContract(
+ self,
+ 402,
+ epg_220.sclass,
+ epg_222.sclass,
+ acl.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(
+ 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],
+ )
+ c1.add_vpp_config()
+
+ c2 = VppGbpContract(
+ self,
+ 402,
+ epg_222.sclass,
+ epg_220.sclass,
+ acl.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(
+ 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],
+ )
+ c2.add_vpp_config()
+
+ #
+ # send again with the contract preset, now packets arrive
+ # at SEP1 for both directions
+ #
+ rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, ep3.ip4)
+
+ rxs = self.send_and_expect(self.pg2, p4[1] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep3.ip4)
+ self.assertEqual(rx[IP].dst, ep1.ip4)
+
+ #
+ # programme the unknown EP for the L3 tests
+ #
+ sep4.add_vpp_config()
+
+ #
+ # 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=str(self.router_mac))
+ / IP(src=ep1.ip4, dst=ep2.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=ep2.mac, dst=str(self.router_mac))
+ / IP(src=ep2.ip4, dst=ep1.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+ p6 = [
+ (
+ Ether(src=ep1.mac, dst=str(self.router_mac))
+ / IPv6(src=ep1.ip6, dst=ep2.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=ep2.mac, dst=str(self.router_mac))
+ / IPv6(src=ep2.ip6, dst=ep1.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+
+ c3 = VppGbpContract(
+ self,
+ 402,
+ epg_220.sclass,
+ epg_221.sclass,
+ acl.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(
+ 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)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, ep2.ip4)
+
+ #
+ # learn a remote EP in EPG 221
+ # packets coming from unknown remote EPs will be leant & redirected
+ #
+ 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()
+
+ c4 = VppGbpContract(
+ self,
+ 402,
+ epg_221.sclass,
+ epg_220.sclass,
+ acl.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,
+ [
+ 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_SRC_IP,
+ [
+ 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],
+ )
+ c4.add_vpp_config()
+
+ 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=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)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ # unknown remote EP to local EP redirected
+ rxs = self.send_and_expect(self.pg7, [p], sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, "10.0.0.88")
+ self.assertEqual(rx[IP].dst, ep1.ip4)
+
+ # endpoint learnt via the parent GBP-vxlan interface
+ self.assertTrue(find_gbp_endpoint(self, vx_tun_l3._sw_if_index, ip="10.0.0.88"))
+
+ 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=441, flags=0x88)
+ / Ether(src="00:22:22:22:22:33", dst=str(self.router_mac))
+ / IPv6(src="2001:10::88", dst=ep1.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ # unknown remote EP to local EP redirected (ipv6)
+ rxs = self.send_and_expect(self.pg7, [p], sep3.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep3.mac)
+ self.assertEqual(rx[IPv6].src, "2001:10::88")
+ self.assertEqual(rx[IPv6].dst, ep1.ip6)
+
+ # endpoint learnt via the parent GBP-vxlan interface
+ self.assertTrue(
+ find_gbp_endpoint(self, vx_tun_l3._sw_if_index, ip="2001:10::88")
+ )
+
+ #
+ # L3 switch from local to remote EP
+ #
+ p4 = [
+ (
+ Ether(src=ep1.mac, dst=str(self.router_mac))
+ / IP(src=ep1.ip4, dst="10.0.0.88")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ ]
+ p6 = [
+ (
+ Ether(src=ep1.mac, dst=str(self.router_mac))
+ / IPv6(src=ep1.ip6, dst="2001:10::88")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ ]
+
+ rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, "10.0.0.88")
+
+ rxs = self.send_and_expect(self.pg0, p6[0] * 17, sep4.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep4.mac)
+ self.assertEqual(rx[IPv6].src, ep1.ip6)
+ self.assertEqual(rx[IPv6].dst, "2001:10::88")
+
+ #
+ # test the dst-ip hash mode
+ #
+ c5 = VppGbpContract(
+ self,
+ 402,
+ epg_220.sclass,
+ epg_221.sclass,
+ acl.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,
+ [
+ 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_DST_IP,
+ [
+ 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],
+ )
+ c5.add_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, "10.0.0.88")
+
+ rxs = self.send_and_expect(self.pg0, p6[0] * 17, sep3.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep3.mac)
+ self.assertEqual(rx[IPv6].src, ep1.ip6)
+ self.assertEqual(rx[IPv6].dst, "2001:10::88")
+
+ #
+ # a programmed remote SEP in EPG 320
+ #
+
+ # gbp vxlan tunnel for the remote SEP
+ vx_tun_l3_sep = VppGbpVxlanTunnel(
+ self,
+ 555,
+ rd1.rd_id,
+ VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
+ self.pg2.local_ip4,
+ )
+ vx_tun_l3_sep.add_vpp_config()
+
+ # remote SEP
+ sep5 = VppGbpEndpoint(
+ self,
+ vx_tun_l3_sep,
+ epg_320,
+ None,
+ "12.0.0.10",
+ "13.0.0.10",
+ "4001:10::10",
+ "5001:10::10",
+ ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
+ self.pg7.local_ip4,
+ self.pg7.remote_ip4,
+ mac=None,
+ )
+ sep5.add_vpp_config()
+
+ #
+ # local l3out redirect tests
+ #
+
+ # add local l3out
+ # the external bd
+ self.loop4.set_mac(self.router_mac)
+ b_lo4_ip4 = VppIpInterfaceBind(self, self.loop4, t4).add_vpp_config()
+ b_lo4_ip6 = VppIpInterfaceBind(self, self.loop4, t6).add_vpp_config()
+ ebd = VppBridgeDomain(self, 100)
+ ebd.add_vpp_config()
+ gebd = VppGbpBridgeDomain(self, ebd, rd1, self.loop4, None, None)
+ gebd.add_vpp_config()
+ # the external epg
+ eepg = VppGbpEndpointGroup(
+ self,
+ 888,
+ 765,
+ rd1,
+ gebd,
+ None,
+ gebd.bvi,
+ "10.1.0.128",
+ "2001:10:1::128",
+ VppGbpEndpointRetention(60),
+ )
+ eepg.add_vpp_config()
+ # add subnets to BVI
+ VppIpInterfaceAddress(
+ self, gebd.bvi, "10.1.0.128", 24, bind=b_lo4_ip4
+ ).add_vpp_config()
+ VppIpInterfaceAddress(
+ self, gebd.bvi, "2001:10:1::128", 64, bind=b_lo4_ip6
+ ).add_vpp_config()
+ # ... which are L3-out subnets
+ VppGbpSubnet(
+ self,
+ rd1,
+ "10.1.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=765,
+ ).add_vpp_config()
+ VppGbpSubnet(
+ self,
+ rd1,
+ "2001:10:1::128",
+ 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=765,
+ ).add_vpp_config()
+ # external endpoints
+ VppL2Vtr(self, self.vlan_100, L2_VTR_OP.L2_POP_1).add_vpp_config()
+ eep1 = VppGbpEndpoint(
+ self,
+ self.vlan_100,
+ eepg,
+ None,
+ "10.1.0.1",
+ "11.1.0.1",
+ "2001:10:1::1",
+ "3001:10:1::1",
+ ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL,
+ )
+ eep1.add_vpp_config()
+ VppL2Vtr(self, self.vlan_101, L2_VTR_OP.L2_POP_1).add_vpp_config()
+ eep2 = VppGbpEndpoint(
+ self,
+ self.vlan_101,
+ eepg,
+ None,
+ "10.1.0.2",
+ "11.1.0.2",
+ "2001:10:1::2",
+ "3001:10:1::2",
+ ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL,
+ )
+ eep2.add_vpp_config()
+
+ # external subnets reachable though eep1 and eep2 respectively
+ VppIpRoute(
+ self,
+ "10.220.0.0",
+ 24,
+ [VppRoutePath(eep1.ip4, eep1.epg.bvi.sw_if_index)],
+ table_id=t4.table_id,
+ ).add_vpp_config()
+ VppGbpSubnet(
+ self,
+ rd1,
+ "10.220.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4220,
+ ).add_vpp_config()
+ VppIpRoute(
+ self,
+ "10:220::",
+ 64,
+ [VppRoutePath(eep1.ip6, eep1.epg.bvi.sw_if_index)],
+ table_id=t6.table_id,
+ ).add_vpp_config()
+ VppGbpSubnet(
+ self,
+ rd1,
+ "10:220::",
+ 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4220,
+ ).add_vpp_config()
+ VppIpRoute(
+ self,
+ "10.221.0.0",
+ 24,
+ [VppRoutePath(eep2.ip4, eep2.epg.bvi.sw_if_index)],
+ table_id=t4.table_id,
+ ).add_vpp_config()
+ VppGbpSubnet(
+ self,
+ rd1,
+ "10.221.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4221,
+ ).add_vpp_config()
+ VppIpRoute(
+ self,
+ "10:221::",
+ 64,
+ [VppRoutePath(eep2.ip6, eep2.epg.bvi.sw_if_index)],
+ table_id=t6.table_id,
+ ).add_vpp_config()
+ VppGbpSubnet(
+ self,
+ rd1,
+ "10:221::",
+ 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4221,
+ ).add_vpp_config()
+
+ #
+ # l3out redirect to remote (known, then unknown) SEP
+ #
+
+ # packets from 1 external subnet to the other
+ p = [
+ (
+ Ether(src=eep1.mac, dst=self.router_mac)
+ / Dot1Q(vlan=100)
+ / IP(src="10.220.0.17", dst="10.221.0.65")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=eep1.mac, dst=self.router_mac)
+ / Dot1Q(vlan=100)
+ / IPv6(src="10:220::17", dst="10:221::65")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+
+ # packets should be dropped in absence of contract
+ self.send_and_assert_no_replies(self.pg0, p)
+
+ # contract redirecting to sep5
+ VppGbpContract(
+ self,
+ 402,
+ 4220,
+ 4221,
+ acl.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,
+ [
+ VppGbpContractNextHop(
+ sep5.vmac, sep5.epg.bd, sep5.ip4, sep5.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_DST_IP,
+ [
+ VppGbpContractNextHop(
+ sep5.vmac, sep5.epg.bd, sep5.ip6, sep5.epg.rd
+ )
+ ],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ ).add_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p, self.pg7)
+
+ for rx, tx in zip(rxs, p):
+ 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)
+ # this should use the programmed remote leaf TEP
+ self.assertEqual(rx[VXLAN].vni, 555)
+ self.assertEqual(rx[VXLAN].gpid, 4220)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertTrue(rx[VXLAN].gpflags.D)
+ rxip = rx[VXLAN][Ether].payload
+ txip = tx[Dot1Q].payload
+ self.assertEqual(rxip.src, txip.src)
+ self.assertEqual(rxip.dst, txip.dst)
+
+ # remote SEP: it is now an unknown remote SEP and should go
+ # to spine proxy
+ sep5.remove_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p, self.pg7)
+
+ for rx, tx in zip(rxs, p):
+ 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)
+ # this should use the spine proxy TEP
+ self.assertEqual(rx[VXLAN].vni, epg_320.bd.uu_fwd.vni)
+ self.assertEqual(rx[VXLAN].gpid, 4220)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertTrue(rx[VXLAN].gpflags.D)
+ rxip = rx[VXLAN][Ether].payload
+ txip = tx[Dot1Q].payload
+ self.assertEqual(rxip.src, txip.src)
+ self.assertEqual(rxip.dst, txip.dst)
+
+ #
+ # l3out redirect to local SEP
+ #
+
+ # change the contract between l3out to redirect to local SEPs
+ # instead of remote SEP
+ VppGbpContract(
+ self,
+ 402,
+ 4220,
+ 4221,
+ acl.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,
+ [
+ VppGbpContractNextHop(
+ sep1.vmac, sep1.epg.bd, sep1.ip4, sep1.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_DST_IP,
+ [
+ VppGbpContractNextHop(
+ sep1.vmac, sep1.epg.bd, sep1.ip6, sep1.epg.rd
+ )
+ ],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ ).add_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p, sep1.itf)
+ for rx, tx in zip(rxs, p):
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ rxip = rx[Ether].payload
+ txip = tx[Ether].payload
+ self.assertEqual(rxip.src, txip.src)
+ self.assertEqual(rxip.dst, txip.dst)
+
+ #
+ # redirect remote EP to remote (known then unknown) SEP
+ #
+
+ # remote SEP known again
+ sep5.add_vpp_config()
+
+ # contract to redirect to learnt SEP
+ VppGbpContract(
+ self,
+ 402,
+ epg_221.sclass,
+ epg_222.sclass,
+ acl.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,
+ [
+ VppGbpContractNextHop(
+ sep5.vmac, sep5.epg.bd, sep5.ip4, sep5.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_DST_IP,
+ [
+ VppGbpContractNextHop(
+ sep5.vmac, sep5.epg.bd, sep5.ip6, sep5.epg.rd
+ )
+ ],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ ).add_vpp_config()
+
+ # packets from unknown EP 221 to known EP in EPG 222
+ # should be redirected to known remote SEP
+ base = (
+ 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=441, flags=0x88)
+ / Ether(src="00:22:22:22:22:44", dst=str(self.router_mac))
+ )
+ p = [
+ (
+ base
+ / IP(src="10.0.1.100", dst=ep3.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ base
+ / IPv6(src="2001:10::100", dst=ep3.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+
+ # unknown remote EP to local EP redirected to known remote SEP
+ rxs = self.send_and_expect(self.pg7, p, self.pg7)
+
+ for rx, tx in zip(rxs, p):
+ 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)
+ # this should use the programmed remote leaf TEP
+ self.assertEqual(rx[VXLAN].vni, 555)
+ self.assertEqual(rx[VXLAN].gpid, epg_221.sclass)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+ rxip = rx[VXLAN][Ether].payload
+ txip = tx[VXLAN][Ether].payload
+ self.assertEqual(rxip.src, txip.src)
+ self.assertEqual(rxip.dst, txip.dst)
+
+ # endpoint learnt via the parent GBP-vxlan interface
+ self.assertTrue(
+ find_gbp_endpoint(self, vx_tun_l3._sw_if_index, ip="10.0.1.100")
+ )
+ self.assertTrue(
+ find_gbp_endpoint(self, vx_tun_l3._sw_if_index, ip="2001:10::100")
+ )
+
+ # remote SEP: it is now an unknown remote SEP and should go
+ # to spine proxy
+ sep5.remove_vpp_config()
+
+ # remote EP (coming from spine proxy) to local EP redirected to
+ # known remote SEP
+ rxs = self.send_and_expect(self.pg7, p, self.pg7)
+
+ for rx, tx in zip(rxs, p):
+ 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)
+ # this should use the spine proxy TEP
+ self.assertEqual(rx[VXLAN].vni, epg_320.bd.uu_fwd.vni)
+ self.assertEqual(rx[VXLAN].gpid, epg_221.sclass)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+ rxip = rx[VXLAN][Ether].payload
+ txip = tx[VXLAN][Ether].payload
+ self.assertEqual(rxip.src, txip.src)
+ self.assertEqual(rxip.dst, txip.dst)
+
+ #
+ # cleanup
+ #
+ self.pg7.unconfig_ip4()
+
+ def test_gbp_redirect_extended(self):
+ """GBP Endpoint Redirect Extended"""
+
+ self.vapi.cli("set logging class gbp level 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"
+
+ learnt = [
+ {"mac": "00:00:11:11:11:02", "ip": "10.0.1.2", "ip6": "2001:10::2"},
+ {"mac": "00:00:11:11:11:03", "ip": "10.0.1.3", "ip6": "2001:10::3"},
+ ]
+
+ #
+ # IP tables
+ #
+ t4 = VppIpTable(self, 1)
+ t4.add_vpp_config()
+ t6 = VppIpTable(self, 1, True)
+ t6.add_vpp_config()
+
+ # create IPv4 and IPv6 RD UU VxLAN-GBP TEP and bind them to the right
+ # VRF
+ rd_uu4 = VppVxlanGbpTunnel(
+ self,
+ self.pg7.local_ip4,
+ self.pg7.remote_ip4,
+ 114,
+ mode=(
+ VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t.VXLAN_GBP_API_TUNNEL_MODE_L3
+ ),
+ )
+ rd_uu4.add_vpp_config()
+ VppIpInterfaceBind(self, rd_uu4, t4).add_vpp_config()
+
+ rd_uu6 = VppVxlanGbpTunnel(
+ self,
+ self.pg7.local_ip4,
+ self.pg7.remote_ip4,
+ 115,
+ mode=(
+ VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t.VXLAN_GBP_API_TUNNEL_MODE_L3
+ ),
+ )
+ rd_uu6.add_vpp_config()
+ VppIpInterfaceBind(self, rd_uu6, t4).add_vpp_config()
+
+ rd1 = VppGbpRouteDomain(self, 2, 402, t4, t6, rd_uu4, rd_uu6)
+ rd1.add_vpp_config()
+
+ self.loop0.set_mac(self.router_mac)
+ self.loop1.set_mac(self.router_mac)
+ self.loop2.set_mac(self.router_mac)
+
+ #
+ # Bind the BVI to the RD
+ #
+ b_lo0_ip4 = VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
+ b_lo0_ip6 = VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
+ b_lo1_ip4 = VppIpInterfaceBind(self, self.loop1, t4).add_vpp_config()
+ b_lo1_ip6 = VppIpInterfaceBind(self, self.loop1, t6).add_vpp_config()
+ b_lo2_ip4 = VppIpInterfaceBind(self, self.loop2, t4).add_vpp_config()
+ b_lo2_ip6 = VppIpInterfaceBind(self, self.loop2, t6).add_vpp_config()
+
+ #
+ # Pg7 hosts a BD's UU-fwd
+ #
+ self.pg7.config_ip4()
+ self.pg7.resolve_arp()
+
+ #
+ # a GBP bridge domains for the EPs
+ #
+ bd1 = VppBridgeDomain(self, 1)
+ bd1.add_vpp_config()
+ gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0)
+ gbd1.add_vpp_config()
+
+ bd2 = VppBridgeDomain(self, 2)
+ bd2.add_vpp_config()
+ gbd2 = VppGbpBridgeDomain(self, bd2, rd1, self.loop1)
+ gbd2.add_vpp_config()
+
+ # ... and has a /32 and /128 applied
+ ip4_addr1 = VppIpInterfaceAddress(
+ self, gbd1.bvi, "10.0.0.128", 32, bind=b_lo0_ip4
+ ).add_vpp_config()
+ ip6_addr1 = VppIpInterfaceAddress(
+ self, gbd1.bvi, "2001:10::128", 128, bind=b_lo0_ip6
+ ).add_vpp_config()
+ ip4_addr2 = VppIpInterfaceAddress(
+ self, gbd2.bvi, "10.0.1.128", 32, bind=b_lo1_ip4
+ ).add_vpp_config()
+ ip6_addr2 = VppIpInterfaceAddress(
+ self, gbd2.bvi, "2001:11::128", 128, bind=b_lo1_ip6
+ ).add_vpp_config()
+
+ #
+ # The Endpoint-groups
+ #
+ epg_220 = VppGbpEndpointGroup(
+ self,
+ 220,
+ 440,
+ rd1,
+ gbd1,
+ None,
+ gbd1.bvi,
+ "10.0.0.128",
+ "2001:10::128",
+ VppGbpEndpointRetention(60),
+ )
+ epg_220.add_vpp_config()
+ epg_221 = VppGbpEndpointGroup(
+ self,
+ 221,
+ 441,
+ rd1,
+ gbd2,
+ None,
+ gbd2.bvi,
+ "10.0.1.128",
+ "2001:11::128",
+ VppGbpEndpointRetention(60),
+ )
+ epg_221.add_vpp_config()
+
+ #
+ # a GBP bridge domains for the SEPs
+ #
+ bd_uu3 = VppVxlanGbpTunnel(self, self.pg7.local_ip4, self.pg7.remote_ip4, 116)
+ bd_uu3.add_vpp_config()
+
+ bd3 = VppBridgeDomain(self, 3)
+ bd3.add_vpp_config()
+ gbd3 = VppGbpBridgeDomain(self, bd3, rd1, self.loop2, bd_uu3, learn=False)
+ gbd3.add_vpp_config()
+
+ ip4_addr3 = VppIpInterfaceAddress(
+ self, gbd3.bvi, "12.0.0.128", 32, bind=b_lo2_ip4
+ ).add_vpp_config()
+ ip6_addr3 = VppIpInterfaceAddress(
+ self, gbd3.bvi, "4001:10::128", 128, bind=b_lo2_ip6
+ ).add_vpp_config()
+
+ #
+ # self.logger.info(self.vapi.cli("show gbp bridge"))
+ # self.logger.info(self.vapi.cli("show vxlan-gbp tunnel"))
+ # self.logger.info(self.vapi.cli("show gbp vxlan"))
+ # self.logger.info(self.vapi.cli("show int addr"))
+ #
+
+ #
+ # EPGs in which the service endpoints exist
+ #
+ epg_320 = VppGbpEndpointGroup(
+ self,
+ 320,
+ 550,
+ rd1,
+ gbd3,
+ None,
+ gbd3.bvi,
+ "12.0.0.128",
+ "4001:10::128",
+ VppGbpEndpointRetention(60),
+ )
+ epg_320.add_vpp_config()
+
+ #
+ # endpoints
+ #
+ ep1 = VppGbpEndpoint(
+ self,
+ self.pg0,
+ epg_220,
+ None,
+ "10.0.0.1",
+ "11.0.0.1",
+ "2001:10::1",
+ "3001:10::1",
+ )
+ ep1.add_vpp_config()
+ ep2 = VppGbpEndpoint(
+ self,
+ self.pg1,
+ epg_221,
+ None,
+ "10.0.1.1",
+ "11.0.1.1",
+ "2001:11::1",
+ "3001:11::1",
+ )
+ ep2.add_vpp_config()
+
+ #
+ # service endpoints
+ #
+ sep1 = VppGbpEndpoint(
+ self,
+ self.pg3,
+ epg_320,
+ None,
+ "12.0.0.1",
+ "13.0.0.1",
+ "4001:10::1",
+ "5001:10::1",
+ )
+ sep2 = VppGbpEndpoint(
+ self,
+ self.pg4,
+ epg_320,
+ None,
+ "12.0.0.2",
+ "13.0.0.2",
+ "4001:10::2",
+ "5001:10::2",
+ )
+
+ # sep1 and sep2 are not added to config yet
+ # they are unknown for now
+
+ #
+ # add routes to EPG subnets
+ #
+ VppGbpSubnet(
+ self,
+ rd1,
+ "10.0.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT,
+ ).add_vpp_config()
+ VppGbpSubnet(
+ self,
+ rd1,
+ "10.0.1.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT,
+ ).add_vpp_config()
+
+ #
+ # Local host to known local host in different BD
+ # with SFC contract (source and destination are in
+ # one node and service endpoint in another node)
+ #
+ p4 = [
+ (
+ Ether(src=ep1.mac, dst=str(self.router_mac))
+ / IP(src=ep1.ip4, dst=ep2.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=ep2.mac, dst=str(self.router_mac))
+ / IP(src=ep2.ip4, dst=ep1.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+ p6 = [
+ (
+ Ether(src=ep1.mac, dst=str(self.router_mac))
+ / IPv6(src=ep1.ip6, dst=ep2.ip6)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=ep2.mac, dst=str(self.router_mac))
+ / IPv6(src=ep2.ip6, dst=ep1.ip6)
+ / UDP(sport=1234, dport=1230)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+
+ # should be dropped since no contract yet
+ self.send_and_assert_no_replies(self.pg0, [p4[0]])
+ self.send_and_assert_no_replies(self.pg0, [p6[0]])
+
+ #
+ # Add a contract with a rule to load-balance redirect via SEP1 and SEP2
+ # one of the next-hops is via an EP that is not known
+ #
+ rule4 = AclRule(is_permit=1, proto=17)
+ rule6 = AclRule(
+ src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)),
+ is_permit=1,
+ proto=17,
+ )
+ acl = VppAcl(self, rules=[rule4, rule6])
+ acl.add_vpp_config()
+
+ #
+ # test the src-ip hash mode
+ #
+ c1 = VppGbpContract(
+ self,
+ 402,
+ epg_220.sclass,
+ epg_221.sclass,
+ acl.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
+ )
+ ],
+ ),
+ 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.ip6, sep1.epg.rd
+ )
+ ],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c1.add_vpp_config()
+
+ c2 = VppGbpContract(
+ self,
+ 402,
+ epg_221.sclass,
+ epg_220.sclass,
+ acl.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
+ )
+ ],
+ ),
+ 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.ip6, sep1.epg.rd
+ )
+ ],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c2.add_vpp_config()
+
+ # ep1 <--> ep2 redirected through sep1
+ # sep1 is unknown
+ # packet is redirected to sep bd and then go through sep bd UU
+
+ rxs = self.send_and_expect(self.pg0, p4[0] * 17, 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, 116)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, sep1.mac)
+ self.assertEqual(inner[IP].src, ep1.ip4)
+ self.assertEqual(inner[IP].dst, ep2.ip4)
+
+ rxs = self.send_and_expect(self.pg1, p4[1] * 17, 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, 116)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, sep1.mac)
+ self.assertEqual(inner[IP].src, ep2.ip4)
+ self.assertEqual(inner[IP].dst, ep1.ip4)
+
+ rxs = self.send_and_expect(self.pg0, p6[0] * 17, 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, 116)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, sep1.mac)
+ self.assertEqual(inner[IPv6].src, ep1.ip6)
+ self.assertEqual(inner[IPv6].dst, ep2.ip6)
+
+ rxs = self.send_and_expect(self.pg1, p6[1] * 17, 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, 116)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, sep1.mac)
+ self.assertEqual(inner[IPv6].src, ep2.ip6)
+ self.assertEqual(inner[IPv6].dst, ep1.ip6)
+
+ # configure sep1: it is now local
+ # packets between ep1 and ep2 are redirected locally
+ sep1.add_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, ep2.ip4)
+
+ rxs = self.send_and_expect(self.pg1, p6[1] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IPv6].src, ep2.ip6)
+ self.assertEqual(rx[IPv6].dst, ep1.ip6)
+
+ # packet coming from the l2 spine-proxy to sep1
+ 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=116, gpid=440, gpflags=0x08, flags=0x88)
+ / Ether(src=str(self.router_mac), dst=sep1.mac)
+ / IP(src=ep1.ip4, dst=ep2.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg7, [p] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, str(self.router_mac))
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, ep2.ip4)
+
+ # contract for SEP to communicate with dst EP
+ c3 = VppGbpContract(
+ self,
+ 402,
+ epg_320.sclass,
+ epg_221.sclass,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c3.add_vpp_config()
+
+ # temporarily remove ep2, so that ep2 is remote & unknown
+ ep2.remove_vpp_config()
+
+ # packet going back from sep1 to its original dest (ep2)
+ # as ep2 is now unknown (see above), it must go through
+ # the rd UU (packet is routed)
+
+ p1 = (
+ Ether(src=sep1.mac, dst=self.router_mac)
+ / IP(src=ep1.ip4, dst=ep2.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg3, [p1] * 17, 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, 114)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ inner = rx[VXLAN].payload
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, routed_dst_mac)
+ self.assertEqual(inner[IP].src, ep1.ip4)
+ self.assertEqual(inner[IP].dst, ep2.ip4)
+
+ self.logger.info(self.vapi.cli("show bridge 3 detail"))
+ sep1.remove_vpp_config()
+
+ self.logger.info(self.vapi.cli("show bridge 1 detail"))
+ self.logger.info(self.vapi.cli("show bridge 2 detail"))
+
+ # re-add ep2: it is local again :)
+ ep2.add_vpp_config()
+
+ # packet coming back from the remote sep through rd UU
+ p2 = (
+ 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=114, gpid=441, gpflags=0x09, flags=0x88)
+ / Ether(src=str(self.router_mac), dst=self.router_mac)
+ / IP(src=ep1.ip4, dst=ep2.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg7, [p2], self.pg1)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, str(self.router_mac))
+ self.assertEqual(rx[Ether].dst, self.pg1.remote_mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, ep2.ip4)
+
+ #
+ # bd_uu2.add_vpp_config()
+ #
+
+ #
+ # cleanup
+ #
+ c1.remove_vpp_config()
+ c2.remove_vpp_config()
+ c3.remove_vpp_config()
+ 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 level 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, 55, t4, t6)
+ rd1.add_vpp_config()
+
+ self.loop0.set_mac(self.router_mac)
+
+ #
+ # Bind the BVI to the RD
+ #
+ b_ip4 = VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
+ b_ip6 = 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, rd1, 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(4),
+ )
+ epg_220.add_vpp_config()
+
+ # the BVIs have the subnets applied ...
+ ip4_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "10.0.0.128", 24, bind=b_ip4
+ ).add_vpp_config()
+ ip6_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "2001:10::128", 64, bind=b_ip6
+ ).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
+ #
+ VppL2Vtr(self, self.vlan_100, L2_VTR_OP.L2_POP_1).add_vpp_config()
+ VppL2Vtr(self, self.vlan_101, L2_VTR_OP.L2_POP_1).add_vpp_config()
+ vlan_144 = VppDot1QSubint(self, self.pg0, 144)
+ vlan_144.admin_up()
+ # vlan_102 is not poped
+
+ #
+ # 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,
+ 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,
+ 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()
+ eep3 = VppGbpEndpoint(
+ self,
+ self.vlan_102,
+ epg_220,
+ None,
+ "10.0.0.3",
+ "11.0.0.3",
+ "2001:10::3",
+ "3001::3",
+ ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL,
+ )
+ eep3.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()
+
+ #
+ # EP1 impersonating EP3 is dropped
+ #
+ p = (
+ Ether(src=eep1.mac, dst="ff:ff:ff:ff:ff:ff")
+ / Dot1Q(vlan=100)
+ / ARP(
+ op="who-has",
+ psrc="10.0.0.3",
+ pdst="10.0.0.128",
+ hwsrc=eep1.mac,
+ hwdst="ff:ff:ff:ff:ff:ff",
+ )
+ )
+ self.send_and_assert_no_replies(self.pg0, p)
+
+ #
+ # 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,
+ 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)
+
+ #
+ # ARP packet from host in remote subnet are accepted and replied to
+ #
+ p_arp = (
+ Ether(src=eep3.mac, dst="ff:ff:ff:ff:ff:ff")
+ / Dot1Q(vlan=102)
+ / ARP(
+ op="who-has",
+ psrc=eep3.ip4,
+ pdst="10.0.0.128",
+ hwsrc=eep3.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(b"\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(b"\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(b"\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, 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, dst=eep2.ip4)
+ / 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)
+
+ #
+ # local EP pings router w/o vlan tag poped
+ #
+ p = (
+ Ether(src=eep3.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=102)
+ / IP(src=eep3.ip4, 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, self.vlan_102.remote_mac)
+
+ #
+ # A ip4 subnet reachable through the external EP1
+ #
+ ip_220 = VppIpRoute(
+ self,
+ "10.220.0.0",
+ 24,
+ [VppRoutePath(eep1.ip4, 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()
+
+ #
+ # An ip6 subnet reachable through the external EP1
+ #
+ ip6_220 = VppIpRoute(
+ self,
+ "10:220::",
+ 64,
+ [VppRoutePath(eep1.ip6, eep1.epg.bvi.sw_if_index)],
+ table_id=t6.table_id,
+ )
+ ip6_220.add_vpp_config()
+
+ l3o6_220 = VppGbpSubnet(
+ self,
+ rd1,
+ "10:220::",
+ 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4220,
+ )
+ l3o6_220.add_vpp_config()
+
+ #
+ # A subnet reachable through the external EP2
+ #
+ ip_221 = VppIpRoute(
+ self,
+ "10.221.0.0",
+ 24,
+ [VppRoutePath(eep2.ip4, 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")
+ )
+
+ self.send_and_assert_no_replies(self.pg0, p * 1)
+
+ #
+ # contract for the external nets to communicate
+ #
+ rule4 = AclRule(is_permit=1, proto=17)
+ rule6 = AclRule(
+ src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)),
+ is_permit=1,
+ proto=17,
+ )
+ acl = VppAcl(self, rules=[rule4, rule6])
+ acl.add_vpp_config()
+
+ #
+ # A contract with the wrong scope is not matched
+ #
+ c_44 = VppGbpContract(
+ self,
+ 44,
+ 4220,
+ 4221,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c_44.add_vpp_config()
+ self.send_and_assert_no_replies(self.pg0, p * 1)
+
+ c1 = VppGbpContract(
+ self,
+ 55,
+ 4220,
+ 4221,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c1.add_vpp_config()
+
+ #
+ # Contracts allowing ext-net 200 to talk with external EPs
+ #
+ c2 = VppGbpContract(
+ self,
+ 55,
+ 4220,
+ 113,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c2.add_vpp_config()
+ c3 = VppGbpContract(
+ self,
+ 55,
+ 113,
+ 4220,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [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(b"\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(b"\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)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\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)
+
+ #
+ # An external subnet reachable via the remote external EP
+ #
+
+ #
+ # first the VXLAN-GBP tunnel over which it is reached
+ #
+ vx_tun_r1 = 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_r1.add_vpp_config()
+ VppIpInterfaceBind(self, vx_tun_r1, 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_r1.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_r1.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(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_assert_no_replies(self.pg0, p * 1)
+
+ #
+ # Add contracts ext-nets for 220 -> 222
+ #
+ c4 = VppGbpContract(
+ self,
+ 55,
+ 4220,
+ 4222,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [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(b"\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")
+
+ #
+ # make the external subnet ECMP
+ #
+ vx_tun_r2 = VppVxlanGbpTunnel(
+ self,
+ self.pg7.local_ip4,
+ self.pg7.remote_ip4,
+ 446,
+ mode=(
+ VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t.VXLAN_GBP_API_TUNNEL_MODE_L3
+ ),
+ )
+ vx_tun_r2.add_vpp_config()
+ VppIpInterfaceBind(self, vx_tun_r2, t4).add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh vxlan-gbp tunnel"))
+
+ n2 = VppNeighbor(
+ self, vx_tun_r2.sw_if_index, "00:0c:0c:0c:0c:0c", self.pg7.remote_ip4
+ )
+ n2.add_vpp_config()
+
+ ip_222.modify(
+ [
+ VppRoutePath(self.pg7.remote_ip4, vx_tun_r1.sw_if_index),
+ VppRoutePath(self.pg7.remote_ip4, vx_tun_r2.sw_if_index),
+ ]
+ )
+
+ #
+ # now expect load-balance
+ #
+ 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(b"\xa5" * 100)
+ ),
+ (
+ 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=1222, dport=1235)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+
+ rxs = self.send_and_expect(self.pg0, p, self.pg7)
+
+ self.assertEqual(rxs[0][VXLAN].vni, 445)
+ self.assertEqual(rxs[1][VXLAN].vni, 446)
+
+ #
+ # Same LB test for v6
+ #
+ n3 = VppNeighbor(
+ self, vx_tun_r1.sw_if_index, "00:0c:0c:0c:0c:0c", self.pg7.remote_ip6
+ )
+ n3.add_vpp_config()
+ n4 = VppNeighbor(
+ self, vx_tun_r2.sw_if_index, "00:0c:0c:0c:0c:0c", self.pg7.remote_ip6
+ )
+ n4.add_vpp_config()
+
+ ip_222_6 = VppIpRoute(
+ self,
+ "10:222::",
+ 64,
+ [
+ VppRoutePath(self.pg7.remote_ip6, vx_tun_r1.sw_if_index),
+ VppRoutePath(self.pg7.remote_ip6, vx_tun_r2.sw_if_index),
+ ],
+ table_id=t6.table_id,
+ )
+ ip_222_6.add_vpp_config()
+
+ l3o_222_6 = VppGbpSubnet(
+ self,
+ rd1,
+ "10:222::",
+ 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4222,
+ )
+ l3o_222_6.add_vpp_config()
+
+ p = [
+ (
+ Ether(src=eep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IPv6(src="10:220::1", dst="10:222::1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=eep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IPv6(src="10:220::1", dst="10:222::1")
+ / UDP(sport=7777, dport=8881)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+
+ self.logger.info(self.vapi.cli("sh ip6 fib 10:222::1"))
+ rxs = self.send_and_expect(self.pg0, p, self.pg7)
+
+ self.assertEqual(rxs[0][VXLAN].vni, 445)
+ self.assertEqual(rxs[1][VXLAN].vni, 446)
+
+ #
+ # 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(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg7, p * 3, self.pg0)
+ self.assertFalse(find_gbp_endpoint(self, ip="10.222.0.1"))
+
+ #
+ # ping from host in remote to remote external subnets
+ # this is dropped by reflection check.
+ #
+ 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.222.0.2")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_assert_no_replies(self.pg7, p * 3)
+
+ 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))
+ / IPv6(src="10:222::1", dst="10:222::2")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_assert_no_replies(self.pg7, p * 3)
+
+ #
+ # local EP
+ #
+ lep1 = VppGbpEndpoint(
+ self,
+ vlan_144,
+ epg_220,
+ None,
+ "10.0.0.44",
+ "11.0.0.44",
+ "2001:10::44",
+ "3001::44",
+ )
+ lep1.add_vpp_config()
+
+ #
+ # local EP to local ip4 external subnet
+ #
+ p = (
+ Ether(src=lep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=144)
+ / IP(src=lep1.ip4, dst="10.220.0.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\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, eep1.mac)
+ self.assertEqual(rx[Dot1Q].vlan, 100)
+
+ #
+ # local EP to local ip6 external subnet
+ #
+ p = (
+ Ether(src=lep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=144)
+ / IPv6(src=lep1.ip6, dst="10:220::1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\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, eep1.mac)
+ self.assertEqual(rx[Dot1Q].vlan, 100)
+
+ #
+ # ip4 and ip6 subnets that load-balance
+ #
+ ip_20 = VppIpRoute(
+ self,
+ "10.20.0.0",
+ 24,
+ [
+ VppRoutePath(eep1.ip4, eep1.epg.bvi.sw_if_index),
+ VppRoutePath(eep2.ip4, eep2.epg.bvi.sw_if_index),
+ ],
+ table_id=t4.table_id,
+ )
+ ip_20.add_vpp_config()
+
+ l3o_20 = VppGbpSubnet(
+ self,
+ rd1,
+ "10.20.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4220,
+ )
+ l3o_20.add_vpp_config()
+
+ ip6_20 = VppIpRoute(
+ self,
+ "10:20::",
+ 64,
+ [
+ VppRoutePath(eep1.ip6, eep1.epg.bvi.sw_if_index),
+ VppRoutePath(eep2.ip6, eep2.epg.bvi.sw_if_index),
+ ],
+ table_id=t6.table_id,
+ )
+ ip6_20.add_vpp_config()
+
+ l3o6_20 = VppGbpSubnet(
+ self,
+ rd1,
+ "10:20::",
+ 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4220,
+ )
+ l3o6_20.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh ip fib 10.20.0.1"))
+ self.logger.info(self.vapi.cli("sh ip6 fib 10:20::1"))
+
+ # two ip6 packets whose port are chosen so they load-balance
+ p = [
+ (
+ Ether(src=lep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=144)
+ / IPv6(src=lep1.ip6, dst="10:20::1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=lep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=144)
+ / IPv6(src=lep1.ip6, dst="10:20::1")
+ / UDP(sport=124, dport=1230)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+
+ rxs = self.send_and_expect(self.pg0, p, self.pg0, 2)
+
+ self.assertEqual(rxs[0][Dot1Q].vlan, 101)
+ self.assertEqual(rxs[1][Dot1Q].vlan, 100)
+
+ # two ip4 packets whose port are chosen so they load-balance
+ p = [
+ (
+ Ether(src=lep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=144)
+ / IP(src=lep1.ip4, dst="10.20.0.1")
+ / UDP(sport=1235, dport=1235)
+ / Raw(b"\xa5" * 100)
+ ),
+ (
+ Ether(src=lep1.mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=144)
+ / IP(src=lep1.ip4, dst="10.20.0.1")
+ / UDP(sport=124, dport=1230)
+ / Raw(b"\xa5" * 100)
+ ),
+ ]
+
+ rxs = self.send_and_expect(self.pg0, p, self.pg0, 2)
+
+ self.assertEqual(rxs[0][Dot1Q].vlan, 101)
+ self.assertEqual(rxs[1][Dot1Q].vlan, 100)
+
+ #
+ # cleanup
+ #
+ ip_222.remove_vpp_config()
+ self.pg7.unconfig_ip4()
+ self.vlan_101.set_vtr(L2_VTR_OP.L2_DISABLED)
+ self.vlan_100.set_vtr(L2_VTR_OP.L2_DISABLED)
+
+ def test_gbp_anon_l3_out(self):
+ """GBP Anonymous L3 Out"""
+
+ ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
+ self.vapi.cli("set logging class gbp level 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, 55, t4, t6)
+ rd1.add_vpp_config()
+
+ self.loop0.set_mac(self.router_mac)
+
+ #
+ # Bind the BVI to the RD
+ #
+ bind_l0_ip4 = VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
+ bind_l0_ip6 = 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 GBP external bridge domains for the EPs
+ #
+ bd1 = VppBridgeDomain(self, 1)
+ bd1.add_vpp_config()
+ gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, None, None)
+ 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(4),
+ )
+ epg_220.add_vpp_config()
+
+ # the BVIs have the subnet applied ...
+ ip4_addr = VppIpInterfaceAddress(
+ self, gbd1.bvi, "10.0.0.128", 24, bind=bind_l0_ip4
+ ).add_vpp_config()
+
+ # ... which is an Anonymous L3-out subnets
+ l3o_1 = VppGbpSubnet(
+ self,
+ rd1,
+ "10.0.0.0",
+ 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_ANON_L3_OUT,
+ sclass=113,
+ )
+ l3o_1.add_vpp_config()
+
+ #
+ # an external interface attached to the outside world and the
+ # external BD
+ #
+ VppL2Vtr(self, self.vlan_100, L2_VTR_OP.L2_POP_1).add_vpp_config()
+ VppL2Vtr(self, self.vlan_101, L2_VTR_OP.L2_POP_1).add_vpp_config()
+
+ #
+ # vlan_100 and vlan_101 are anonymous l3-out interfaces
+ #
+ ext_itf = VppGbpExtItf(self, self.vlan_100, bd1, rd1, anon=True)
+ ext_itf.add_vpp_config()
+ ext_itf = VppGbpExtItf(self, self.vlan_101, bd1, rd1, anon=True)
+ 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()
+
+ #
+ # A remote external endpoint
+ #
+ rep = VppGbpEndpoint(
+ self,
+ vx_tun_l3,
+ epg_220,
+ None,
+ "10.0.0.201",
+ "11.0.0.201",
+ "2001:10::201",
+ "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 host in external subnet are accepted, flooded and
+ # replied to. We expect 2 packets:
+ # - APR request flooded over the other vlan subif
+ # - ARP reply from BVI
+ #
+ p_arp = (
+ Ether(src=self.vlan_100.remote_mac, dst="ff:ff:ff:ff:ff:ff")
+ / Dot1Q(vlan=100)
+ / ARP(
+ op="who-has",
+ psrc="10.0.0.100",
+ pdst="10.0.0.128",
+ hwsrc=self.vlan_100.remote_mac,
+ hwdst="ff:ff:ff:ff:ff:ff",
+ )
+ )
+ rxs = self.send_and_expect(self.pg0, p_arp * 1, self.pg0, n_rx=2)
+
+ p_arp = (
+ Ether(src=self.vlan_101.remote_mac, dst="ff:ff:ff:ff:ff:ff")
+ / Dot1Q(vlan=101)
+ / ARP(
+ op="who-has",
+ psrc="10.0.0.101",
+ pdst="10.0.0.128",
+ hwsrc=self.vlan_101.remote_mac,
+ hwdst="ff:ff:ff:ff:ff:ff",
+ )
+ )
+ rxs = self.send_and_expect(self.pg0, p_arp * 1, self.pg0, n_rx=2)
+
+ #
+ # 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=vx_tun_l3.vni, gpid=epg_220.sclass, flags=0x88)
+ / Ether(src=self.pg0.remote_mac, dst=str(self.router_mac))
+ / IP(src=str(rep.ip4), dst="10.0.0.100")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+ rxs = self.send_and_expect(self.pg7, p * 1, self.pg0)
+
+ #
+ # local EP pings router
+ #
+ p = (
+ Ether(src=self.vlan_100.remote_mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IP(src="10.0.0.100", 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, self.vlan_100.remote_mac)
+ self.assertEqual(rx[Dot1Q].vlan, 100)
+
+ #
+ # local EP pings other local EP
+ #
+ p = (
+ Ether(src=self.vlan_100.remote_mac, dst=self.vlan_101.remote_mac)
+ / Dot1Q(vlan=100)
+ / IP(src="10.0.0.100", dst="10.0.0.101")
+ / ICMP(type="echo-request")
+ )
+ rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.vlan_100.remote_mac)
+ self.assertEqual(rx[Ether].dst, self.vlan_101.remote_mac)
+ self.assertEqual(rx[Dot1Q].vlan, 101)
+
+ #
+ # A subnet reachable through an external router on vlan 100
+ #
+ ip_220 = VppIpRoute(
+ self,
+ "10.220.0.0",
+ 24,
+ [VppRoutePath("10.0.0.100", epg_220.bvi.sw_if_index)],
+ table_id=t4.table_id,
+ )
+ ip_220.add_vpp_config()
+
+ l3o_220 = VppGbpSubnet(
+ self,
+ rd1,
+ "10.220.0.0",
+ 24,
+ # note: this a "regular" L3 out subnet (not connected)
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4220,
+ )
+ l3o_220.add_vpp_config()
+
+ #
+ # A subnet reachable through an external router on vlan 101
+ #
+ ip_221 = VppIpRoute(
+ self,
+ "10.221.0.0",
+ 24,
+ [VppRoutePath("10.0.0.101", epg_220.bvi.sw_if_index)],
+ table_id=t4.table_id,
+ )
+ ip_221.add_vpp_config()
+
+ l3o_221 = VppGbpSubnet(
+ self,
+ rd1,
+ "10.221.0.0",
+ 24,
+ # note: this a "regular" L3 out subnet (not connected)
+ 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=self.vlan_100.remote_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
+ #
+ rule4 = AclRule(is_permit=1, proto=17)
+ rule6 = AclRule(
+ src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)),
+ is_permit=1,
+ proto=17,
+ )
+ acl = VppAcl(self, rules=[rule4, rule6])
+ acl.add_vpp_config()
+
+ c1 = VppGbpContract(
+ self,
+ 55,
+ 4220,
+ 4221,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c1.add_vpp_config()
+
+ #
+ # Contracts allowing ext-net 200 to talk with external EPs
+ #
+ c2 = VppGbpContract(
+ self,
+ 55,
+ 4220,
+ 113,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c2.add_vpp_config()
+ c3 = VppGbpContract(
+ self,
+ 55,
+ 113,
+ 4220,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c3.add_vpp_config()
+
+ #
+ # ping between hosts in remote subnets
+ #
+ p = (
+ Ether(src=self.vlan_100.remote_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(b"\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, self.vlan_101.remote_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=rep.ip4, dst="10.220.0.1")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\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=self.vlan_100.remote_mac, dst=str(self.router_mac))
+ / Dot1Q(vlan=100)
+ / IP(src="10.220.0.1", dst=rep.ip4)
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\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)
+
+ #
+ # 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,
+ # note: this a "regular" l3out subnet (not connected)
+ 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=self.vlan_100.remote_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(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_assert_no_replies(self.pg0, p * 1)
+
+ #
+ # Add contracts ext-nets for 220 -> 222
+ #
+ c4 = VppGbpContract(
+ self,
+ 55,
+ 4220,
+ 4222,
+ acl.acl_index,
+ [
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [],
+ ),
+ ],
+ [ETH_P_IP, ETH_P_IPV6],
+ )
+ c4.add_vpp_config()
+
+ #
+ # ping from host in local to remote external subnets
+ #
+ p = (
+ Ether(src=self.vlan_100.remote_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(b"\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(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_expect(self.pg7, p * 3, self.pg0)
+ self.assertFalse(find_gbp_endpoint(self, ip="10.222.0.1"))
+
+ #
+ # ping from host in remote to remote external subnets
+ # this is dropped by reflection check.
+ #
+ 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.222.0.2")
+ / UDP(sport=1234, dport=1234)
+ / Raw(b"\xa5" * 100)
+ )
+
+ rxs = self.send_and_assert_no_replies(self.pg7, p * 3)
+
+ #
+ # cleanup
+ #
+ self.vlan_101.set_vtr(L2_VTR_OP.L2_DISABLED)
+ self.vlan_100.set_vtr(L2_VTR_OP.L2_DISABLED)
+ self.pg7.unconfig_ip4()
+ # make sure the programmed EP is no longer learnt from DP
+ self.wait_for_ep_timeout(sw_if_index=rep.itf.sw_if_index, ip=rep.ip4)
+
+
+if __name__ == "__main__":
+ unittest.main(testRunner=VppTestRunner)