X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=test%2Ftest_gbp.py;h=d587759fffa78af8ffb761bf73e388a9ff005a52;hb=8006c6a;hp=427b14de5068440d000af397562b3a9b7e8fbf10;hpb=bc27d1be24815e1371dcce3bff2d3075a532acba;p=vpp.git diff --git a/test/test_gbp.py b/test/test_gbp.py index 427b14de506..d587759fffa 100644 --- a/test/test_gbp.py +++ b/test/test_gbp.py @@ -1,88 +1,460 @@ #!/usr/bin/env python import unittest -import socket -import struct -from framework import VppTestCase, VppTestRunner +from framework import VppTestCase, VppTestRunner, is_skip_aarch64_set, \ + is_platform_aarch64 from vpp_object import VppObject +from vpp_neighbor import VppNeighbor +from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable, \ + VppIpInterfaceAddress, VppIpInterfaceBind, find_route +from vpp_l2 import VppBridgeDomain, VppBridgeDomainPort, \ + VppBridgeDomainArpEntry, VppL2FibEntry, find_bridge_domain_port +from vpp_vxlan_gbp_tunnel import * +from vpp_sub_interface import VppDot1QSubint + +from vpp_ip import * +from vpp_papi_provider import L2_PORT_TYPE +from vpp_papi import VppEnum, MACAddress from scapy.packet import Raw -from scapy.layers.l2 import Ether +from scapy.layers.l2 import Ether, ARP, Dot1Q from scapy.layers.inet import IP, UDP -from scapy.layers.inet6 import IPv6 +from scapy.layers.inet6 import IPv6, ICMPv6ND_NS, ICMPv6NDOptSrcLLAddr, \ + ICMPv6ND_NA +from scapy.utils6 import in6_getnsma, in6_getnsmac +from scapy.layers.vxlan import VXLAN from socket import AF_INET, AF_INET6 -from scapy.utils import inet_pton +from scapy.utils import inet_pton, inet_ntop +from vpp_papi_provider import L2_VTR_OP + + +def find_gbp_endpoint(test, sw_if_index=None, ip=None, mac=None): + if ip: + vip = VppIpAddress(ip) + if mac: + vmac = MACAddress(mac) + + eps = test.vapi.gbp_endpoint_dump() + + for ep in eps: + if sw_if_index: + if ep.endpoint.sw_if_index != sw_if_index: + continue + if ip: + for eip in ep.endpoint.ips: + if vip == eip: + return True + if mac: + if vmac.packed == ep.endpoint.mac: + return True + return False + + +def find_gbp_vxlan(test, vni): + ts = test.vapi.gbp_vxlan_tunnel_dump() + for t in ts: + if t.tunnel.vni == vni: + return True + return False class VppGbpEndpoint(VppObject): """ - GDB Endpoint + GBP Endpoint """ - def __init__(self, test, sw_if_index, addr, epg, is_ip6=0): + @property + def mac(self): + return str(self.vmac) + + @property + def mac(self): + return self.itf.remote_mac + + @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.sw_if_index = sw_if_index + self.itf = itf self.epg = epg - self.addr_p = addr - self.is_ip6 = is_ip6 - if is_ip6: - self.addr = inet_pton(AF_INET6, addr) + self.recirc = recirc + + self._ip4 = VppIpAddress(ip4) + self._fip4 = VppIpAddress(fip4) + self._ip6 = VppIpAddress(ip6) + self._fip6 = VppIpAddress(fip6) + + if mac: + self.vmac = MACAddress(self.itf.remote_mac) else: - self.addr = inet_pton(AF_INET, addr) + self.vmac = MACAddress("00:00:00:00:00:00") + + self.flags = flags + self.tun_src = VppIpAddress(tun_src) + self.tun_dst = VppIpAddress(tun_dst) + + def add_vpp_config(self): + res = self._test.vapi.gbp_endpoint_add( + self.itf.sw_if_index, + [self.ip4.encode(), self.ip6.encode()], + self.vmac.packed, + self.epg.epg, + self.flags, + self.tun_src.encode(), + self.tun_dst.encode()) + self.handle = res.handle + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_endpoint_del(self.handle) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "gbp-endpoint:[%d==%d:%s:%d]" % (self.handle, + self.itf.sw_if_index, + self.ip4.address, + self.epg.epg) + + def query_vpp_config(self): + return find_gbp_endpoint(self._test, + self.itf.sw_if_index, + self.ip4.address) + + +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 add_vpp_config(self): - self._test.vapi.gbp_endpoint_add_del( + self._test.vapi.gbp_recirc_add_del( 1, - self.sw_if_index, - self.addr, - self.is_ip6, - self.epg) + self.recirc.sw_if_index, + self.epg.epg, + self.is_ext) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_recirc_add_del( + 0, + self.recirc.sw_if_index, + self.epg.epg, + self.is_ext) + + def __str__(self): + return self.object_id() + + 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): + self._test = test + self.itf = itf + self.bd = bd + self.rd = rd + + def add_vpp_config(self): + self._test.vapi.gbp_ext_itf_add_del( + 1, + self.itf.sw_if_index, + self.bd.bd_id, + self.rd.rd_id) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_ext_itf_add_del( + 0, + self.itf.sw_if_index, + self.bd.bd_id, + self.rd.rd_id) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "gbp-ext-itf:[%d]" % (self.itf.sw_if_index) + + def query_vpp_config(self): + rs = self._test.vapi.gbp_ext_itf_dump() + for r in rs: + if r.ext_itf.sw_if_index == self.itf.sw_if_index: + return True + return False + + +class VppGbpSubnet(VppObject): + """ + GBP Subnet + """ + def __init__(self, test, rd, address, address_len, + type, sw_if_index=None, epg=None): + self._test = test + self.rd_id = rd.rd_id + self.prefix = VppIpPrefix(address, address_len) + self.type = type + self.sw_if_index = sw_if_index + self.epg = epg + + def add_vpp_config(self): + self._test.vapi.gbp_subnet_add_del( + 1, + self.rd_id, + self.prefix.encode(), + self.type, + sw_if_index=self.sw_if_index if self.sw_if_index else 0xffffffff, + epg_id=self.epg if self.epg else 0xffff) self._test.registry.register(self, self._test.logger) def remove_vpp_config(self): - self._test.vapi.gbp_endpoint_add_del( + self._test.vapi.gbp_subnet_add_del( 0, - self.sw_if_index, - self.addr, - self.is_ip6, + self.rd_id, + self.prefix.encode(), + self.type) + + def __str__(self): + return self.object_id() + + 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 VppGbpEndpointGroup(VppObject): + """ + GBP Endpoint Group + """ + + def __init__(self, test, epg, rd, bd, uplink, + bvi, bvi_ip4, bvi_ip6=None): + self._test = test + self.uplink = uplink + self.bvi = bvi + self.bvi_ip4 = VppIpAddress(bvi_ip4) + self.bvi_ip6 = VppIpAddress(bvi_ip6) + self.epg = epg + self.bd = bd + self.rd = rd + + def add_vpp_config(self): + self._test.vapi.gbp_endpoint_group_add( + self.epg, + self.bd.bd.bd_id, + self.rd.rd_id, + self.uplink.sw_if_index if self.uplink else INDEX_INVALID) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_endpoint_group_del( self.epg) def __str__(self): return self.object_id() def object_id(self): - return "gbp-endpoint;[%d:%s:%d]" % (self.sw_if_index, - self.addr_p, - self.epg) + return "gbp-endpoint-group:[%d]" % (self.epg) def query_vpp_config(self): - eps = self._test.vapi.gbp_endpoint_dump() - for ep in eps: - if ep.endpoint.address == self.addr \ - and ep.endpoint.sw_if_index == self.sw_if_index: + epgs = self._test.vapi.gbp_endpoint_group_dump() + for epg in epgs: + if epg.epg.epg_id == self.epg: + return True + return False + + +class VppGbpBridgeDomain(VppObject): + """ + GBP Bridge Domain + """ + + def __init__(self, test, bd, bvi, uu_flood=None, learn=True): + self._test = test + self.bvi = bvi + self.uu_flood = uu_flood + self.bd = bd + + e = VppEnum.vl_api_gbp_bridge_domain_flags_t + if (learn): + self.learn = e.GBP_BD_API_FLAG_NONE + else: + self.learn = e.GBP_BD_API_FLAG_DO_NOT_LEARN + + def add_vpp_config(self): + self._test.vapi.gbp_bridge_domain_add( + self.bd.bd_id, + self.learn, + self.bvi.sw_if_index, + self.uu_flood.sw_if_index if self.uu_flood else INDEX_INVALID) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_bridge_domain_del(self.bd.bd_id) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "gbp-bridge-domain:[%d]" % (self.bd.bd_id) + + def query_vpp_config(self): + 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, t4, t6, ip4_uu=None, ip6_uu=None): + self._test = test + self.rd_id = rd_id + self.t4 = t4 + self.t6 = t6 + self.ip4_uu = ip4_uu + self.ip6_uu = ip6_uu + + def add_vpp_config(self): + self._test.vapi.gbp_route_domain_add( + self.rd_id, + self.t4.table_id, + self.t6.table_id, + self.ip4_uu.sw_if_index if self.ip4_uu else INDEX_INVALID, + self.ip6_uu.sw_if_index if self.ip6_uu else INDEX_INVALID) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_route_domain_del(self.rd_id) + + def __str__(self): + return self.object_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): + return {'ip': self.ip.encode(), + '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=[]): + self.action = action + self.hash_mode = hash_mode + self.nhs = nhs + + def encode(self): + 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}} + + class VppGbpContract(VppObject): """ - GDB Contract + GBP Contract """ - def __init__(self, test, src_epg, dst_epg, acl_index): + def __init__(self, test, src_epg, dst_epg, acl_index, rules=[]): self._test = test self.acl_index = acl_index self.src_epg = src_epg self.dst_epg = dst_epg + self.rules = rules def add_vpp_config(self): + rules = [] + for r in self.rules: + rules.append(r.encode()) self._test.vapi.gbp_contract_add_del( 1, self.src_epg, self.dst_epg, - self.acl_index) + self.acl_index, + rules) self._test.registry.register(self, self._test.logger) def remove_vpp_config(self): @@ -90,21 +462,110 @@ class VppGbpContract(VppObject): 0, self.src_epg, self.dst_epg, - self.acl_index) + self.acl_index, + []) def __str__(self): return self.object_id() def object_id(self): - return "gbp-contract;[%d:%s:%d]" % (self.src_epg, + return "gbp-contract:[%d:%s:%d]" % (self.src_epg, self.dst_epg, self.acl_index) def query_vpp_config(self): - eps = self._test.vapi.gbp_contract_dump() - for ep in eps: - if ep.contract.src_epg == self.src_epg \ - and ep.contract.dst_epg == self.dst_epg: + cs = self._test.vapi.gbp_contract_dump() + for c in cs: + if c.contract.src_epg == self.src_epg \ + and c.contract.dst_epg == self.dst_epg: + return True + return False + + +class VppGbpVxlanTunnel(VppInterface): + """ + GBP VXLAN tunnel + """ + + def __init__(self, test, vni, bd_rd_id, mode): + super(VppGbpVxlanTunnel, self).__init__(test) + self._test = test + self.vni = vni + self.bd_rd_id = bd_rd_id + self.mode = mode + + def add_vpp_config(self): + r = self._test.vapi.gbp_vxlan_tunnel_add( + self.vni, + self.bd_rd_id, + self.mode) + 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(self.vni) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "gbp-vxlan:%d" % (self.vni) + + def query_vpp_config(self): + return find_gbp_vxlan(self._test, self.vni) + + +class VppGbpAcl(VppObject): + """ + GBP Acl + """ + + def __init__(self, test): + self._test = test + self.acl_index = 4294967295 + + def create_rule(self, is_ipv6=0, permit_deny=0, proto=-1, + s_prefix=0, s_ip='\x00\x00\x00\x00', sport_from=0, + sport_to=65535, d_prefix=0, d_ip='\x00\x00\x00\x00', + dport_from=0, dport_to=65535): + if proto == -1 or proto == 0: + sport_to = 0 + dport_to = sport_to + elif proto == 1 or proto == 58: + sport_to = 255 + dport_to = sport_to + rule = ({'is_permit': permit_deny, 'is_ipv6': is_ipv6, 'proto': proto, + 'srcport_or_icmptype_first': sport_from, + 'srcport_or_icmptype_last': sport_to, + 'src_ip_prefix_len': s_prefix, + 'src_ip_addr': s_ip, + 'dstport_or_icmpcode_first': dport_from, + 'dstport_or_icmpcode_last': dport_to, + 'dst_ip_prefix_len': d_prefix, + 'dst_ip_addr': d_ip}) + return rule + + def add_vpp_config(self, rules): + + reply = self._test.vapi.acl_add_replace(self.acl_index, + r=rules, + tag='GBPTest') + self.acl_index = reply.acl_index + return self.acl_index + + def remove_vpp_config(self): + self._test.vapi.acl_del(self.acl_index) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "gbp-acl:[%d]" % (self.acl_index) + + def query_vpp_config(self): + cs = self._test.vapi.acl_dump() + for c in cs: + if c.acl_index == self.acl_index: return True return False @@ -115,254 +576,2724 @@ class TestGBP(VppTestCase): def setUp(self): super(TestGBP, self).setUp() - # create 6 pg interfaces for pg0 to pg5 - self.create_pg_interfaces(range(6)) + 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() - i.config_ip4() - i.resolve_arp() - i.config_ip6() - i.resolve_ndp() + for i in self.lo_interfaces: + i.admin_up() def tearDown(self): for i in self.pg_interfaces: - i.unconfig_ip4() - i.unconfig_ip6() + i.admin_down() super(TestGBP, self).tearDown() - def test_gbp4(self): - """ Group Based Policy v4 """ + def send_and_expect_bridged(self, src, tx, dst): + rx = self.send_and_expect(src, tx, dst) - ep1 = VppGbpEndpoint(self, - self.pg0.sw_if_index, - self.pg0.remote_ip4, - 220) - ep1.add_vpp_config() - ep2 = VppGbpEndpoint(self, - self.pg1.sw_if_index, - self.pg1.remote_ip4, - 220) - ep2.add_vpp_config() + 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 - ep3 = VppGbpEndpoint(self, - self.pg2.sw_if_index, - self.pg2.remote_ip4, - 221) - ep3.add_vpp_config() - ep4 = VppGbpEndpoint(self, - self.pg3.sw_if_index, - self.pg3.remote_ip4, - 222) - ep4.add_vpp_config() + def send_and_expect_bridged6(self, src, tx, dst): + rx = self.send_and_expect(src, tx, dst) - self.logger.info(self.vapi.cli("sh gbp endpoint")) + 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 - # - # in the abscense of policy, endpoints in the same EPG - # can communicate - # - pkt_intra_epg = (Ether(src=self.pg0.remote_mac, - dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, - dst=self.pg1.remote_ip4) / - UDP(sport=1234, dport=1234) / - Raw('\xa5' * 100)) + 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_natted(self, src, tx, dst, src_ip): + rx = self.send_and_expect(src, tx, dst) - self.send_and_expect(self.pg0, pkt_intra_epg * 65, self.pg1) + 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 test_gbp(self): + """ Group Based Policy """ + + ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t # - # in the abscense of policy, endpoints in the different EPG - # cannot communicate + # Bridge Domains # - pkt_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac, - dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, - dst=self.pg2.remote_ip4) / - UDP(sport=1234, dport=1234) / - Raw('\xa5' * 100)) - pkt_inter_epg_220_to_222 = (Ether(src=self.pg0.remote_mac, - dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, - dst=self.pg3.remote_ip4) / - UDP(sport=1234, dport=1234) / - Raw('\xa5' * 100)) - pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac, - dst=self.pg2.local_mac) / - IP(src=self.pg2.remote_ip4, - dst=self.pg0.remote_ip4) / - UDP(sport=1234, dport=1234) / - Raw('\xa5' * 100)) + bd1 = VppBridgeDomain(self, 1) + bd2 = VppBridgeDomain(self, 2) + bd20 = VppBridgeDomain(self, 20) - self.send_and_assert_no_replies(self.pg0, - pkt_inter_epg_220_to_221 * 65) - self.send_and_assert_no_replies(self.pg0, - pkt_inter_epg_221_to_220 * 65) + bd1.add_vpp_config() + bd2.add_vpp_config() + bd20.add_vpp_config() + + gbd1 = VppGbpBridgeDomain(self, bd1, self.loop0) + gbd2 = VppGbpBridgeDomain(self, bd2, self.loop1) + gbd20 = VppGbpBridgeDomain(self, bd20, self.loop2) + + gbd1.add_vpp_config() + gbd2.add_vpp_config() + gbd20.add_vpp_config() # - # A uni-directional contract from EPG 220 -> 221 + # Route Domains # - c1 = VppGbpContract(self, 220, 221, 0xffffffff) - c1.add_vpp_config() + 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() - self.send_and_expect(self.pg0, - pkt_inter_epg_220_to_221 * 65, - self.pg2) - self.send_and_assert_no_replies(self.pg2, - pkt_inter_epg_221_to_220 * 65) + rd0 = VppGbpRouteDomain(self, 0, gt4, gt6, None, None) + rd20 = VppGbpRouteDomain(self, 20, nt4, nt6, None, None) + + rd0.add_vpp_config() + rd20.add_vpp_config() # - # contract for the return direction + # 3 EPGs, 2 of which share a BD. + # 2 NAT EPGs, one for floating-IP subnets, the other for internet # - c2 = VppGbpContract(self, 221, 220, 0xffffffff) - c2.add_vpp_config() + epgs = [VppGbpEndpointGroup(self, 220, rd0, gbd1, self.pg4, + self.loop0, + "10.0.0.128", + "2001:10::128"), + VppGbpEndpointGroup(self, 221, rd0, gbd1, self.pg5, + self.loop0, + "10.0.1.128", + "2001:10:1::128"), + VppGbpEndpointGroup(self, 222, rd0, gbd2, self.pg6, + self.loop1, + "10.0.2.128", + "2001:10:2::128"), + VppGbpEndpointGroup(self, 333, rd20, gbd20, self.pg7, + self.loop2, + "11.0.0.128", + "3001::128"), + VppGbpEndpointGroup(self, 444, rd20, gbd20, self.pg8, + self.loop2, + "11.0.0.129", + "3001::129")] + recircs = [VppGbpRecirc(self, epgs[0], + self.loop3), + VppGbpRecirc(self, epgs[1], + self.loop4), + VppGbpRecirc(self, epgs[2], + self.loop5), + VppGbpRecirc(self, epgs[3], + self.loop6, is_ext=True), + VppGbpRecirc(self, epgs[4], + self.loop7, is_ext=True)] - self.send_and_expect(self.pg0, - pkt_inter_epg_220_to_221 * 65, - self.pg2) - self.send_and_expect(self.pg2, - pkt_inter_epg_221_to_220 * 65, - self.pg0) + epg_nat = epgs[3] + recirc_nat = recircs[3] # - # check that inter group is still disabled for the groups - # not in the contract. + # 4 end-points, 2 in the same subnet, 3 in the same BD # - self.send_and_assert_no_replies(self.pg0, - pkt_inter_epg_220_to_222 * 65) - - self.logger.info(self.vapi.cli("sh gbp contract")) + 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")] # - # remove both contracts, traffic stops in both directions + # Config related to each of the EPGs # - c2.remove_vpp_config() - c1.remove_vpp_config() + for epg in epgs: + # IP config on the BVI interfaces + if epg != epgs[1] and epg != epgs[4]: + VppIpInterfaceBind(self, epg.bvi, epg.rd.t4).add_vpp_config() + VppIpInterfaceBind(self, epg.bvi, epg.rd.t6).add_vpp_config() + self.vapi.sw_interface_set_mac_address( + epg.bvi.sw_if_index, + self.router_mac.packed) - self.send_and_assert_no_replies(self.pg2, - pkt_inter_epg_221_to_220 * 65) - self.send_and_assert_no_replies(self.pg0, - pkt_inter_epg_220_to_221 * 65) - self.send_and_expect(self.pg0, pkt_intra_epg * 65, self.pg1) + # The BVIs are NAT inside interfaces + self.vapi.nat44_interface_add_del_feature(epg.bvi.sw_if_index, + is_inside=1, + is_add=1) + self.vapi.nat66_add_del_interface(epg.bvi.sw_if_index, + is_inside=1, + is_add=1) - def test_gbp6(self): - """ Group Based Policy v6 """ + if_ip4 = VppIpInterfaceAddress(self, epg.bvi, epg.bvi_ip4, 32) + if_ip6 = VppIpInterfaceAddress(self, epg.bvi, epg.bvi_ip6, 128) + if_ip4.add_vpp_config() + if_ip6.add_vpp_config() - ep1 = VppGbpEndpoint(self, - self.pg0.sw_if_index, - self.pg0.remote_ip6, - 220, - is_ip6=1) - ep1.add_vpp_config() - ep2 = VppGbpEndpoint(self, - self.pg1.sw_if_index, - self.pg1.remote_ip6, - 220, - is_ip6=1) - ep2.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() - ep3 = VppGbpEndpoint(self, - self.pg2.sw_if_index, - self.pg2.remote_ip6, - 221, - is_ip6=1) - ep3.add_vpp_config() - ep4 = VppGbpEndpoint(self, - self.pg3.sw_if_index, - self.pg3.remote_ip6, - 222, - is_ip6=1) - ep4.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() - self.logger.info(self.vapi.cli("sh gbp endpoint")) + # EPG in VPP + epg.add_vpp_config() - # - # in the abscense of policy, endpoints in the same EPG + 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( + recirc.recirc.sw_if_index, + is_inside=0, + is_add=1) + self.vapi.nat66_add_del_interface( + recirc.recirc.sw_if_index, + is_inside=0, + 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.af == AF_INET: + self.vapi.nat44_add_del_static_mapping(ip.bytes, + fip.bytes, + vrf_id=0, + addr_only=1) + else: + self.vapi.nat66_add_del_static_mapping(ip.bytes, + fip.bytes, + vrf_id=0) + + # 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), timeout=0.2) + + for ii, ip in enumerate(ep.ips): + p = rx[ii] + + if ip.is_ip6: + self.assertTrue(p.haslayer(ICMPv6ND_NA)) + self.assertEqual(p[ICMPv6ND_NA].tgt, ip.address) + else: + self.assertTrue(p.haslayer(ARP)) + self.assertEqual(p[ARP].psrc, ip.address) + self.assertEqual(p[ARP].pdst, ip.address) + + # 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.address, fip.length, + [VppRoutePath(fip.address, + ep.recirc.recirc.sw_if_index, + is_dvr=1, + proto=fip.dpo_proto)], + table_id=20, + is_ip6=fip.is_ip6) + 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() + + # + # 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.address, + psrc=eps[0].ip4.address)) + + self.send_and_expect(self.pg0, [pkt_arp], self.pg0) + + nsma = in6_getnsma(inet_pton(AF_INET6, eps[0].ip6.address)) + 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.address) / + ICMPv6ND_NS(tgt=epgs[0].bvi_ip6.address) / + 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.address, dst="232.1.1.1") / + UDP(sport=1234, dport=1234) / + Raw('\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.address, + dst="10.0.0.99") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + pkt_inter_epg_222_ip4 = (Ether(src=self.pg0.remote_mac, + dst=str(self.router_mac)) / + IP(src=eps[0].ip4.address, + dst="10.0.1.99") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_assert_no_replies(self.pg0, pkt_intra_epg_220_ip4 * 65) + + pkt_inter_epg_222_ip6 = (Ether(src=self.pg0.remote_mac, + dst=str(self.router_mac)) / + IPv6(src=eps[0].ip6.address, + dst="2001:10::99") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + self.send_and_assert_no_replies(self.pg0, pkt_inter_epg_222_ip6 * 65) + + # + # 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 * 65, + eps[0].epg.uplink) + self.send_and_expect_bridged(eps[0].itf, + pkt_inter_epg_222_ip4 * 65, + eps[0].epg.uplink) + self.send_and_expect_bridged6(eps[0].itf, + pkt_inter_epg_222_ip6 * 65, + 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.address, + dst="10.0.0.99") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_expect_bridged(eps[0].itf, + pkt_intra_epg_220_to_uplink * 65, + 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.address, + dst="10.0.0.99") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_expect_bridged(eps[2].itf, + pkt_intra_epg_221_to_uplink * 65, + 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.address, + dst="10.0.0.99") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_expect_bridged(self.pg4, + pkt_intra_epg_220_from_uplink * 65, + 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.pg0.local_mac) / - IPv6(src=self.pg0.remote_ip6, - dst=self.pg1.remote_ip6) / + dst=self.pg1.remote_mac) / + IP(src=eps[0].ip4.address, + dst=eps[1].ip4.address) / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) - self.send_and_expect(self.pg0, pkt_intra_epg * 65, self.pg1) + self.send_and_expect_bridged(self.pg0, pkt_intra_epg * 65, self.pg1) # # in the abscense of policy, endpoints in the different EPG # cannot communicate # pkt_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac, - dst=self.pg0.local_mac) / - IPv6(src=self.pg0.remote_ip6, - dst=self.pg2.remote_ip6) / + dst=self.pg2.remote_mac) / + IP(src=eps[0].ip4.address, + dst=eps[2].ip4.address) / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) - pkt_inter_epg_220_to_222 = (Ether(src=self.pg0.remote_mac, - dst=self.pg0.local_mac) / - IPv6(src=self.pg0.remote_ip6, - dst=self.pg3.remote_ip6) / + pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac, + dst=self.pg0.remote_mac) / + IP(src=eps[2].ip4.address, + dst=eps[0].ip4.address) / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) - pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac, - dst=self.pg2.local_mac) / - IPv6(src=self.pg2.remote_ip6, - dst=self.pg0.remote_ip6) / + pkt_inter_epg_220_to_222 = (Ether(src=self.pg0.remote_mac, + dst=str(self.router_mac)) / + IP(src=eps[0].ip4.address, + dst=eps[3].ip4.address) / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) - self.send_and_assert_no_replies(self.pg0, + self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_221 * 65) - self.send_and_assert_no_replies(self.pg0, - pkt_inter_epg_221_to_220 * 65) + self.send_and_assert_no_replies(eps[0].itf, + pkt_inter_epg_220_to_222 * 65) # # A uni-directional contract from EPG 220 -> 221 # - c1 = VppGbpContract(self, 220, 221, 0xffffffff) + acl = VppGbpAcl(self) + rule = acl.create_rule(permit_deny=1, proto=17) + rule2 = acl.create_rule(is_ipv6=1, permit_deny=1, proto=17) + acl_index = acl.add_vpp_config([rule, rule2]) + c1 = VppGbpContract( + self, 220, 221, acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + [])]) c1.add_vpp_config() - self.send_and_expect(self.pg0, - pkt_inter_epg_220_to_221 * 65, - self.pg2) - self.send_and_assert_no_replies(self.pg2, - pkt_inter_epg_221_to_220 * 65) + self.send_and_expect_bridged(eps[0].itf, + pkt_inter_epg_220_to_221 * 65, + eps[2].itf) + self.send_and_assert_no_replies(eps[0].itf, + pkt_inter_epg_220_to_222 * 65) # # contract for the return direction # - c2 = VppGbpContract(self, 221, 220, 0xffffffff) + c2 = VppGbpContract( + self, 221, 220, acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + [])]) + c2.add_vpp_config() - self.send_and_expect(self.pg0, - pkt_inter_epg_220_to_221 * 65, - self.pg2) - self.send_and_expect(self.pg2, - pkt_inter_epg_221_to_220 * 65, - self.pg0) + self.send_and_expect_bridged(eps[0].itf, + pkt_inter_epg_220_to_221 * 65, + eps[2].itf) + self.send_and_expect_bridged(eps[2].itf, + pkt_inter_epg_221_to_220 * 65, + eps[0].itf) # # check that inter group is still disabled for the groups # not in the contract. # - self.send_and_assert_no_replies(self.pg0, + self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_222 * 65) + # + # A uni-directional contract from EPG 220 -> 222 'L3 routed' + # + c3 = VppGbpContract( + self, 220, 222, acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + [])]) + + 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 * 65, + 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(self.pg2, + self.send_and_assert_no_replies(eps[2].itf, pkt_inter_epg_221_to_220 * 65) - self.send_and_assert_no_replies(self.pg0, + self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_221 * 65) - self.send_and_expect(self.pg0, pkt_intra_epg * 65, self.pg1) + self.send_and_expect_bridged(eps[0].itf, + pkt_intra_epg * 65, + 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, + epg=epg_nat.epg) + se2 = VppGbpSubnet( + self, rd0, "11.0.0.0", 8, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL, + sw_if_index=recirc_nat.recirc.sw_if_index, + epg=epg_nat.epg) + se16 = VppGbpSubnet( + self, rd0, "::", 0, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL, + sw_if_index=recirc_nat.recirc.sw_if_index, + epg=epg_nat.epg) + # in the NAT RD an external subnet via the NAT EPG's uplink + se3 = VppGbpSubnet( + self, rd20, "0.0.0.0", 0, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL, + sw_if_index=epg_nat.uplink.sw_if_index, + epg=epg_nat.epg) + se36 = VppGbpSubnet( + self, rd20, "::", 0, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL, + sw_if_index=epg_nat.uplink.sw_if_index, + epg=epg_nat.epg) + se4 = VppGbpSubnet( + self, rd20, "11.0.0.0", 8, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL, + sw_if_index=epg_nat.uplink.sw_if_index, + epg=epg_nat.epg) + 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 addess: IN2OUT + # + pkt_inter_epg_220_to_global = (Ether(src=self.pg0.remote_mac, + dst=str(self.router_mac)) / + IP(src=eps[0].ip4.address, + dst="1.1.1.1") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + # no policy yet + self.send_and_assert_no_replies(eps[0].itf, + pkt_inter_epg_220_to_global * 65) + + acl2 = VppGbpAcl(self) + rule = acl2.create_rule(permit_deny=1, proto=17, sport_from=1234, + sport_to=1234, dport_from=1234, dport_to=1234) + rule2 = acl2.create_rule(is_ipv6=1, permit_deny=1, proto=17, + sport_from=1234, sport_to=1234, + dport_from=1234, dport_to=1234) + + acl_index2 = acl2.add_vpp_config([rule, rule2]) + c4 = VppGbpContract( + self, 220, 333, acl_index2, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + [])]) + + c4.add_vpp_config() + + self.send_and_expect_natted(eps[0].itf, + pkt_inter_epg_220_to_global * 65, + self.pg7, + eps[0].fip4.address) + + pkt_inter_epg_220_to_global = (Ether(src=self.pg0.remote_mac, + dst=str(self.router_mac)) / + IPv6(src=eps[0].ip6.address, + dst="6001::1") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_expect_natted6(self.pg0, + pkt_inter_epg_220_to_global * 65, + self.pg7, + eps[0].fip6.address) + + # + # 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.address, + src="1.1.1.1") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_assert_no_replies(self.pg7, + pkt_inter_epg_220_from_global * 65) + + c5 = VppGbpContract( + self, 333, 220, acl_index2, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + [])]) + + c5.add_vpp_config() + + self.send_and_expect_unnatted(self.pg7, + pkt_inter_epg_220_from_global * 65, + eps[0].itf, + eps[0].ip4.address) + + pkt_inter_epg_220_from_global = (Ether(src=str(self.router_mac), + dst=self.pg0.remote_mac) / + IPv6(dst=eps[0].fip6.address, + src="6001::1") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_expect_unnatted6(self.pg7, + pkt_inter_epg_220_from_global * 65, + eps[0].itf, + eps[0].ip6.address) + + # + # 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.address, + dst=eps[1].fip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_expect_double_natted(eps[0].itf, + pkt_intra_epg_220_global * 65, + eps[1].itf, + eps[0].fip4.address, + eps[1].ip4.address) + + pkt_intra_epg_220_global = (Ether(src=self.pg0.remote_mac, + dst=str(self.router_mac)) / + IPv6(src=eps[0].ip6.address, + dst=eps[1].fip6.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_expect_double_natted6(eps[0].itf, + pkt_intra_epg_220_global * 65, + eps[1].itf, + eps[0].fip6.address, + eps[1].ip6.address) + + # + # cleanup + # + for ep in eps: + # del static mappings for each EP from the 10/8 to 11/8 network + self.vapi.nat44_add_del_static_mapping(ep.ip4.bytes, + ep.fip4.bytes, + vrf_id=0, + addr_only=1, + is_add=0) + self.vapi.nat66_add_del_static_mapping(ep.ip6.bytes, + ep.fip6.bytes, + vrf_id=0, + is_add=0) + + for epg in epgs: + # IP config on the BVI interfaces + if epg != epgs[0] and epg != epgs[3]: + self.vapi.nat44_interface_add_del_feature(epg.bvi.sw_if_index, + is_inside=1, + is_add=0) + self.vapi.nat66_add_del_interface(epg.bvi.sw_if_index, + is_inside=1, + is_add=0) + + for recirc in recircs: + self.vapi.nat44_interface_add_del_feature( + recirc.recirc.sw_if_index, + is_inside=0, + is_add=0) + self.vapi.nat66_add_del_interface( + recirc.recirc.sw_if_index, + is_inside=0, + is_add=0) + + def wait_for_ep_timeout(self, sw_if_index=None, ip=None, mac=None, + n_tries=100, s_time=1): + while (n_tries): + if not find_gbp_endpoint(self, sw_if_index, ip, mac): + return True + n_tries = n_tries - 1 + self.sleep(s_time) + self.assertFalse(find_gbp_endpoint(self, sw_if_index, ip, mac)) + return False + + def test_gbp_learn_l2(self): + """ GBP L2 Endpoint Learning """ + + ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t + learnt = [{'mac': '00:00:11:11:11:01', + 'ip': '10.0.0.1', + 'ip6': '2001:10::2'}, + {'mac': '00:00:11:11:11:02', + 'ip': '10.0.0.2', + 'ip6': '2001:10::3'}] + + # + # lower the inactive threshold so these tests pass in a + # reasonable amount of time + # + self.vapi.gbp_endpoint_learn_set_inactive_threshold(1) + + # + # IP tables + # + gt4 = VppIpTable(self, 1) + gt4.add_vpp_config() + gt6 = VppIpTable(self, 1, is_ip6=True) + gt6.add_vpp_config() + + rd1 = VppGbpRouteDomain(self, 1, 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() + + # + # a GBP bridge domain with a BVI and a UU-flood interface + # + bd1 = VppBridgeDomain(self, 1) + bd1.add_vpp_config() + gbd1 = VppGbpBridgeDomain(self, bd1, self.loop0, self.pg3) + gbd1.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, rd1, gbd1, + None, self.loop0, + "10.0.0.128", + "2001:10::128") + epg_220.add_vpp_config() + epg_330 = VppGbpEndpointGroup(self, 330, rd1, gbd1, + None, self.loop1, + "10.0.1.128", + "2001:11::128") + epg_330.add_vpp_config() + + # + # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint + # leanring enabled + # + vx_tun_l2_1 = VppGbpVxlanTunnel( + self, 99, bd1.bd_id, + VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L2) + 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.address, 32, table_id=1)) + + # a packet with an sclass from an unknwon 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.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_assert_no_replies(self.pg2, p) + + # + # 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)) + + # epg is not learnt, becasue the EPG is unknwon + self.assertEqual(len(self.vapi.gbp_endpoint_dump()), 1) + + for ii, l in enumerate(learnt): + # a packet with an sclass from a knwon EPG + # arriving on an unknown TEP + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=220, flags=0x88) / + Ether(src=l['mac'], dst=ep.mac) / + IP(src=l['ip'], dst=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, [p], self.pg0) + + # the new TEP + tep1_sw_if_index = find_vxlan_gbp_tunnel( + self, + self.pg2.local_ip4, + self.pg2.remote_hosts[1].ip4, + 99) + self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index) + + # + # the EP is learnt via the learnt TEP + # both from its MAC and its IP + # + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + mac=l['mac'])) + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + ip=l['ip'])) + + 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")) + + # + # 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']) + + # + # repeat. the do not learn bit is set so the EPs are not learnt + # + for l in learnt: + # a packet with an sclass from a knwon EPG + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=220, flags=0x88, gpflags="D") / + Ether(src=l['mac'], dst=ep.mac) / + IP(src=l['ip'], dst=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, p*65, 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 knwon EPG + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=220, flags=0x88) / + Ether(src=l['mac'], dst=ep.mac) / + IP(src=l['ip'], dst=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, p*65, 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.address) / + UDP(sport=1234, dport=1234) / + Raw('\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, 220) + 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 knwon EPG + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=330, flags=0x88, gpflags='A') / + Ether(src=l['mac'], dst=ep.mac) / + IP(src=l['ip'], dst=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, p*65, 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.address) / + UDP(sport=1234, dport=1234) / + Raw('\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 knwon EPG + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=330, flags=0x88, gpflags='A') / + Ether(src=l['mac'], dst=ep.mac) / + IP(src=l['ip'], dst=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, p*65, 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 + # + acl = VppGbpAcl(self) + rule = acl.create_rule(permit_deny=1, proto=17) + rule2 = acl.create_rule(is_ipv6=1, permit_deny=1, proto=17) + acl_index = acl.add_vpp_config([rule, rule2]) + c1 = VppGbpContract( + self, 220, 330, acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + [])]) + + c1.add_vpp_config() + + for l in learnt: + p = (Ether(src=ep.mac, dst=l['mac']) / + IP(dst=l['ip'], src=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\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 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.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + rxs = self.send_and_expect(ep.itf, [p_uu], gbd1.uu_flood) + + # + # Add a mcast destination VXLAN-GBP tunnel for B&M traffic + # + tun_bm = VppVxlanGbpTunnel(self, self.pg4.local_ip4, + "239.1.1.1", 88, + mcast_itf=self.pg4) + tun_bm.add_vpp_config() + bp_bm = VppBridgeDomainPort(self, bd1, tun_bm, + port_type=L2_PORT_TYPE.NORMAL) + bp_bm.add_vpp_config() + + 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.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + rxs = self.send_and_expect_only(ep.itf, [p_bm], tun_bm.mcast_itf) + + # + # Check v6 Endpoints + # + for l in learnt: + # a packet with an sclass from a knwon EPG + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=330, flags=0x88, gpflags='A') / + Ether(src=l['mac'], dst=ep.mac) / + IPv6(src=l['ip6'], dst=ep.ip6.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, p*65, self.pg0) + + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + mac=l['mac'])) + + # + # L3 Endpoint Learning + # - configured on the bridge's BVI + # + + # + # 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() + + self.logger.info(self.vapi.cli("sh int")) + self.logger.info(self.vapi.cli("sh gbp vxlan")) + + @unittest.skipIf(is_skip_aarch64_set() and is_platform_aarch64(), + "test doesn't work on aarch64") + 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'}] + + # + # lower the inactive threshold so these tests pass in a + # reasonable amount of time + # + self.vapi.gbp_endpoint_learn_set_inactive_threshold(1) + + # + # IP tables + # + gt4 = VppIpTable(self, 1) + gt4.add_vpp_config() + gt6 = VppIpTable(self, 1, is_ip6=True) + gt6.add_vpp_config() + + rd1 = VppGbpRouteDomain(self, 1, 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.sw_interface_set_l2_tag_rewrite(vlan_11.sw_if_index, + L2_VTR_OP.L2_POP_1, + 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, 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) + ip_addr.add_vpp_config() + + # + # The Endpoint-group in which we are learning endpoints + # + epg_220 = VppGbpEndpointGroup(self, 220, rd1, gbd1, + None, self.loop0, + "10.0.0.128", + "2001:10::128") + epg_220.add_vpp_config() + + # + # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint + # leanring enabled + # + vx_tun_l2_1 = VppGbpVxlanTunnel( + self, 99, bd1.bd_id, + VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L2) + 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.address, 32, table_id=1)) + + # + # Send to the static EP + # + for ii, l in enumerate(learnt): + # a packet with an sclass from a knwon EPG + # arriving on an unknown TEP + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=220, flags=0x88) / + Ether(src=l['mac'], dst=ep.mac) / + IP(src=l['ip'], dst=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\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.address) / + UDP(sport=1234, dport=1234) / + Raw('\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, 220) + 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() + + @unittest.skipIf(is_skip_aarch64_set() and is_platform_aarch64(), + "test doesn't work on aarch64") + def test_gbp_learn_l3(self): + """ GBP L3 Endpoint Learning """ + + self.vapi.cli("set logging class gbp debug") + + ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t + routed_dst_mac = "00:0c:0c:0c:0c:0c" + routed_src_mac = "00:22:bd:f8:19:ff" + + 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'}] + + # + # lower the inactive threshold so these tests pass in a + # reasonable amount of time + # + self.vapi.gbp_endpoint_learn_set_inactive_threshold(1) + + # + # IP tables + # + 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, 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 + # + VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config() + 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, 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) + ip4_addr.add_vpp_config() + ip6_addr = VppIpInterfaceAddress(self, gbd1.bvi, "2001:10::128", 128) + ip6_addr.add_vpp_config() + + # + # The Endpoint-group in which we are learning endpoints + # + epg_220 = VppGbpEndpointGroup(self, 220, rd1, gbd1, + None, self.loop0, + "10.0.0.128", + "2001:10::128") + epg_220.add_vpp_config() + + # + # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint + # leanring enabled + # + vx_tun_l3 = VppGbpVxlanTunnel( + self, 101, rd1.rd_id, + VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3) + 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 knwon EPG + # arriving on an unknown TEP + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=101, gpid=220, flags=0x88) / + Ether(src=l['mac'], dst="00:00:00:11:11:11") / + IP(src=l['ip'], dst=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, [p], self.pg0) + + # the new TEP + tep1_sw_if_index = find_vxlan_gbp_tunnel( + self, + self.pg2.local_ip4, + self.pg2.remote_hosts[1].ip4, + 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.address) / + UDP(sport=1234, dport=1234) / + Raw('\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, 220) + 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.address) + 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 knwon EPG + # arriving on an unknown TEP + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=101, gpid=220, flags=0x88) / + Ether(src=l['mac'], dst="00:00:00:11:11:11") / + IPv6(src=l['ip6'], dst=ep.ip6.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, [p], self.pg0) + + # the new TEP + tep1_sw_if_index = find_vxlan_gbp_tunnel( + self, + self.pg2.local_ip4, + self.pg2.remote_hosts[1].ip4, + 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.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p*65, 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, 220) + 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.address) + 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.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_assert_no_replies(self.pg0, [p]) + + # + # Add a route to static EP's v4 and v6 subnet + # packets should be sent on the v4/v6 uu=fwd interface resp. + # + 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() + + p = (Ether(src=ep.mac, dst=self.loop0.local_mac) / + IP(dst="10.0.0.99", src=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\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, 220) + 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 knwon EPG + # arriving on an unknown TEP + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=101, gpid=220, flags=0x88) / + Ether(src=l['mac'], dst="00:00:00:11:11:11") / + IP(src=l['ip'], dst=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, [p], self.pg0) + + # the new TEP + tep1_sw_if_index = find_vxlan_gbp_tunnel( + self, + self.pg2.local_ip4, + self.pg2.remote_hosts[1].ip4, + 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[1].ip4, + mac=None) + rep_88.add_vpp_config() + + # + # Add a remote endpoint from the API that matches an existing one + # + 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 leanred 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.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p*65, 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, 220) + 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.address) + 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.logger.info(self.vapi.cli("show gbp endpoint")) + + self.assertFalse(find_gbp_endpoint(self, ip=rep_88.ip4.address)) + + p = (Ether(src=ep.mac, dst=self.loop0.local_mac) / + IP(src=ep.ip4.address, dst=rep_88.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + rxs = self.send_and_expect(self.pg0, [p], self.pg4) + + self.assertTrue(find_gbp_endpoint(self, ip=rep_2.ip4.address)) + + p = (Ether(src=ep.mac, dst=self.loop0.local_mac) / + IP(src=ep.ip4.address, dst=rep_2.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + rxs = self.send_and_expect(self.pg0, [p], self.pg2) + + # + # to appease the testcase we cannot have the registered EP stll + # present (because it's DP learnt) when the TC ends so wait until + # it is removed + # + self.sleep(2) + + # + # 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=220, flags=0x88) / + Ether(src=l['mac'], dst="00:00:00:11:11:11") / + IP(src=learnt[1]['ip'], dst=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\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 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'}] + + # + # lower the inactive threshold so these tests pass in a + # reasonable amount of time + # + self.vapi.gbp_endpoint_learn_set_inactive_threshold(1) + + # + # IP tables + # + t4 = VppIpTable(self, 1) + t4.add_vpp_config() + t6 = VppIpTable(self, 1, True) + t6.add_vpp_config() + + rd1 = VppGbpRouteDomain(self, 2, t4, t6) + rd1.add_vpp_config() + + self.loop0.set_mac(self.router_mac) + + # + # Bind the BVI to the RD + # + VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config() + VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config() + + # + # Pg7 hosts a BD's 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, self.loop0) + gbd1.add_vpp_config() + + bd2 = VppBridgeDomain(self, 2) + bd2.add_vpp_config() + gbd2 = VppGbpBridgeDomain(self, bd2, self.loop1) + gbd2.add_vpp_config() + + # ... and has a /32 and /128 applied + ip4_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 32) + ip4_addr.add_vpp_config() + ip6_addr = VppIpInterfaceAddress(self, gbd1.bvi, "2001:10::128", 128) + ip6_addr.add_vpp_config() + ip4_addr = VppIpInterfaceAddress(self, gbd2.bvi, "10.0.1.128", 32) + ip4_addr.add_vpp_config() + ip6_addr = VppIpInterfaceAddress(self, gbd2.bvi, "2001:11::128", 128) + ip6_addr.add_vpp_config() + + # + # The Endpoint-groups in which we are learning endpoints + # + epg_220 = VppGbpEndpointGroup(self, 220, rd1, gbd1, + None, gbd1.bvi, + "10.0.0.128", + "2001:10::128") + epg_220.add_vpp_config() + epg_221 = VppGbpEndpointGroup(self, 221, rd1, gbd2, + None, gbd2.bvi, + "10.0.1.128", + "2001:11::128") + epg_221.add_vpp_config() + epg_222 = VppGbpEndpointGroup(self, 222, rd1, gbd1, + None, gbd1.bvi, + "10.0.2.128", + "2001:12::128") + 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, self.loop2, bd_uu1, learn=False) + gbd3.add_vpp_config() + bd4 = VppBridgeDomain(self, 4) + bd4.add_vpp_config() + gbd4 = VppGbpBridgeDomain(self, bd4, self.loop3, bd_uu2, learn=False) + gbd4.add_vpp_config() + + # + # EPGs in which the service endpoints exist + # + epg_320 = VppGbpEndpointGroup(self, 320, rd1, gbd3, + None, gbd1.bvi, + "12.0.0.128", + "4001:10::128") + epg_320.add_vpp_config() + epg_321 = VppGbpEndpointGroup(self, 321, rd1, gbd4, + None, gbd2.bvi, + "12.0.1.128", + "4001:11::128") + 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.address, dst=ep3.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)), + (Ether(src=ep3.mac, dst=ep1.mac) / + IP(src=ep3.ip4.address, dst=ep1.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100))] + p6 = [(Ether(src=ep1.mac, dst=ep3.mac) / + IPv6(src=ep1.ip6.address, dst=ep3.ip6.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)), + (Ether(src=ep3.mac, dst=ep1.mac) / + IPv6(src=ep3.ip6.address, dst=ep1.ip6.address) / + UDP(sport=1234, dport=1230) / + Raw('\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 + # + acl = VppGbpAcl(self) + rule4 = acl.create_rule(permit_deny=1, proto=17) + rule6 = acl.create_rule(is_ipv6=1, permit_deny=1, proto=17) + acl_index = acl.add_vpp_config([rule4, rule6]) + + # + # test the src-ip hash mode + # + c1 = VppGbpContract( + self, 220, 222, 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)])]) + c1.add_vpp_config() + + c2 = VppGbpContract( + self, 222, 220, 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)])]) + 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.address) + self.assertEqual(rx[IP].dst, ep3.ip4.address) + + 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.address) + self.assertEqual(rx[IP].dst, ep1.ip4.address) + + 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.address) + self.assertEqual(inner[IPv6].dst, ep3.ip6.address) + + 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.address) + self.assertEqual(rx[IPv6].dst, ep1.ip6.address) + + # + # 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.address) + self.assertEqual(rx[IPv6].dst, ep3.ip6.address) + + # + # 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.address) + self.assertEqual(inner[IPv6].dst, ep3.ip6.address) + + c1.remove_vpp_config() + c2.remove_vpp_config() + + # + # test the symmetric hash mode + # + c1 = VppGbpContract( + self, 220, 222, 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)])]) + c1.add_vpp_config() + + c2 = VppGbpContract( + self, 222, 220, 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)])]) + 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.address) + self.assertEqual(rx[IP].dst, ep3.ip4.address) + + 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.address) + self.assertEqual(rx[IP].dst, ep1.ip4.address) + + # + # 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.address, dst=ep2.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)), + (Ether(src=ep2.mac, dst=str(self.router_mac)) / + IP(src=ep2.ip4.address, dst=ep1.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100))] + p6 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) / + IPv6(src=ep1.ip6.address, dst=ep2.ip6.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)), + (Ether(src=ep2.mac, dst=str(self.router_mac)) / + IPv6(src=ep2.ip6.address, dst=ep1.ip6.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100))] + + c3 = VppGbpContract( + self, 220, 221, acl_index, + [VppGbpContractRule( + 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)])]) + 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.address) + self.assertEqual(rx[IP].dst, ep2.ip4.address) + + # + # learn a remote EP in EPG 221 + # + vx_tun_l3 = VppGbpVxlanTunnel( + self, 444, rd1.rd_id, + VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3) + vx_tun_l3.add_vpp_config() + + c4 = VppGbpContract( + self, 221, 220, acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + [])]) + 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=221, flags=0x88) / + Ether(src="00:22:22:22:22:33", dst=str(self.router_mac)) / + IP(src="10.0.0.88", dst=ep1.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg7, [p], self.pg0) + + # 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=221, flags=0x88) / + Ether(src="00:22:22:22:22:33", dst=str(self.router_mac)) / + IPv6(src="2001:10::88", dst=ep1.ip6.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg7, [p], self.pg0) + + # 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.address, dst="10.0.0.88") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100))] + p6 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) / + IPv6(src=ep1.ip6.address, dst="2001:10::88") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100))] + + 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.address) + 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.address) + self.assertEqual(rx[IPv6].dst, "2001:10::88") + + # + # test the dst-ip hash mode + # + c5 = VppGbpContract( + self, 220, 221, 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)])]) + 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.address) + 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.address) + self.assertEqual(rx[IPv6].dst, "2001:10::88") + + # + # cleanup + # + self.pg7.unconfig_ip4() + + def test_gbp_l3_out(self): + """ GBP L3 Out """ + + ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t + self.vapi.cli("set logging class gbp debug") + + routed_dst_mac = "00:0c:0c:0c:0c:0c" + routed_src_mac = "00:22:bd:f8:19:ff" + + # + # IP tables + # + t4 = VppIpTable(self, 1) + t4.add_vpp_config() + t6 = VppIpTable(self, 1, True) + t6.add_vpp_config() + + rd1 = VppGbpRouteDomain(self, 2, t4, t6) + rd1.add_vpp_config() + + self.loop0.set_mac(self.router_mac) + + # + # Bind the BVI to the RD + # + VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config() + VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config() + + # + # Pg7 hosts a BD's BUM + # Pg1 some other l3 interface + # + self.pg7.config_ip4() + self.pg7.resolve_arp() + + # + # a GBP external bridge domains for the EPs + # + bd1 = VppBridgeDomain(self, 1) + bd1.add_vpp_config() + gbd1 = VppGbpBridgeDomain(self, bd1, self.loop0) + gbd1.add_vpp_config() + + # + # The Endpoint-groups in which the external endpoints exist + # + epg_220 = VppGbpEndpointGroup(self, 220, rd1, gbd1, + None, gbd1.bvi, + "10.0.0.128", + "2001:10::128") + epg_220.add_vpp_config() + + # the BVIs have the subnets applied ... + ip4_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 24) + ip4_addr.add_vpp_config() + ip6_addr = VppIpInterfaceAddress(self, gbd1.bvi, "2001:10::128", 64) + ip6_addr.add_vpp_config() + + # ... which are L3-out subnets + l3o_1 = VppGbpSubnet( + self, rd1, "10.0.0.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + epg=200) + l3o_1.add_vpp_config() + + # + # an external interface attached to the outside world and the + # external BD + # + vlan_100 = VppDot1QSubint(self, self.pg0, 100) + vlan_100.admin_up() + ext_itf = VppGbpExtItf(self, vlan_100, bd1, rd1) + ext_itf.add_vpp_config() + + # + # 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() + bp_bm = VppBridgeDomainPort(self, bd1, tun_bm, + port_type=L2_PORT_TYPE.NORMAL) + bp_bm.add_vpp_config() + + # + # an unicast vxlan-gbp for inter-BD traffic + # + vx_tun_l3 = VppGbpVxlanTunnel( + self, 444, rd1.rd_id, + VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3) + vx_tun_l3.add_vpp_config() + + # + # packets destined to unkown addresses in the BVI's subnet + # are ARP'd for + # + p4 = (Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src="10.0.0.1", dst="10.0.0.88") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + p6 = (Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IPv6(src="2001:10::1", dst="2001:10::88") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p4 * 1, self.pg7) + + for rx in rxs: + self.assertEqual(rx[Ether].src, self.pg7.local_mac) + # self.assertEqual(rx[Ether].dst, self.pg7.remote_mac) + self.assertEqual(rx[IP].src, self.pg7.local_ip4) + self.assertEqual(rx[IP].dst, "239.1.1.1") + self.assertEqual(rx[VXLAN].vni, 88) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + # policy is not applied since we don't know where it's going + self.assertFalse(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + inner = rx[VXLAN].payload + + self.assertTrue(inner.haslayer(ARP)) + + # + # An external Endpoint + # + eep = VppGbpEndpoint(self, vlan_100, + epg_220, None, + "10.0.0.1", "11.0.0.1", + "2001:10::1", "3001::1", + ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL) + eep.add_vpp_config() + + # + # A remote endpoint + # + rep = VppGbpEndpoint(self, vx_tun_l3, + epg_220, None, + "10.0.0.101", "11.0.0.101", + "2001:10::101", "3001::101", + ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE, + self.pg7.local_ip4, + self.pg7.remote_ip4, + mac=None) + rep.add_vpp_config() + + # + # remote to external + # + p = (Ether(src=self.pg7.remote_mac, + 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=220, flags=0x88) / + Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) / + IP(src="10.0.0.101", dst="10.0.0.1") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rxs = self.send_and_expect(self.pg7, p * 1, self.pg0) + + # + # A subnet reachable through the external EP + # + ip_220 = VppIpRoute(self, "10.220.0.0", 24, + [VppRoutePath(eep.ip4.address, + eep.epg.bvi.sw_if_index)], + 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, + epg=220) + l3o_220.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=220, flags=0x88) / + Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) / + IP(src="10.0.0.101", dst="10.220.0.1") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rxs = self.send_and_expect(self.pg7, p * 1, self.pg0) + + # + # another external subnet, this time in a different EPG + # + ip_200 = VppIpRoute(self, "10.200.0.0", 24, + [VppRoutePath(eep.ip4.address, + eep.epg.bvi.sw_if_index)], + table_id=t4.table_id) + ip_200.add_vpp_config() + + l3o_200 = VppGbpSubnet( + self, rd1, "10.200.0.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + epg=200) + l3o_200.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=220, flags=0x88) / + Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) / + IP(src="10.0.0.101", dst="10.200.0.1") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + # + # packets dropped due to lack of contract. + # + rxs = self.send_and_assert_no_replies(self.pg7, p * 1) + + # + # from the the subnet in EPG 220 beyond the external to remote + # + p4 = (Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src="10.220.0.1", dst=rep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, 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, self.pg7.remote_ip4) + self.assertEqual(rx[VXLAN].vni, 444) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + # + # from the the subnet in EPG 200 beyond the external to remote + # dropped due to no contract + # + p4 = (Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src="10.200.0.1", dst=rep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rxs = self.send_and_assert_no_replies(self.pg0, p4 * 1) + + # + # add a contract + # + acl = VppGbpAcl(self) + rule = acl.create_rule(permit_deny=1, proto=17) + rule2 = acl.create_rule(is_ipv6=1, permit_deny=1, proto=17) + acl_index = acl.add_vpp_config([rule, rule2]) + c1 = VppGbpContract( + self, 200, 220, acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + [])]) + c1.add_vpp_config() + + 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, self.pg7.remote_ip4) + self.assertEqual(rx[VXLAN].vni, 444) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + # + # cleanup + # + self.pg7.unconfig_ip4() if __name__ == '__main__':