#!/usr/bin/env python import unittest from framework import VppTestCase, VppTestRunner from vpp_object import VppObject from vpp_neighbor import VppNeighbor from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable, \ VppIpInterfaceAddress, VppIpInterfaceBind, find_route from vpp_l2 import VppBridgeDomain, VppBridgeDomainPort, \ VppBridgeDomainArpEntry, VppL2FibEntry, find_bridge_domain_port from vpp_vxlan_gbp_tunnel import * from vpp_sub_interface import VppDot1QSubint from vpp_ip import * from vpp_mac import * from vpp_papi_provider import L2_PORT_TYPE from vpp_papi import VppEnum from scapy.packet import Raw from scapy.layers.l2 import Ether, ARP, Dot1Q from scapy.layers.inet import IP, UDP from scapy.layers.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, inet_ntop from util import mactobinary 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 = VppMacAddress(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 == 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): """ GBP Endpoint """ @property def bin_mac(self): return self.vmac.bytes @property def mac(self): return self.vmac.address @property def mac(self): return self.itf.remote_mac @property def ip4(self): return self._ip4 @property def fip4(self): return self._fip4 @property def ip6(self): return self._ip6 @property def fip6(self): return self._fip6 @property def ips(self): return [self.ip4, self.ip6] @property def fips(self): return [self.fip4, self.fip6] def __init__(self, test, itf, epg, recirc, ip4, fip4, ip6, fip6, flags=0, tun_src="0.0.0.0", tun_dst="0.0.0.0", mac=True): self._test = test self.itf = itf self.epg = epg self.recirc = recirc self._ip4 = VppIpAddress(ip4) self._fip4 = VppIpAddress(fip4) self._ip6 = VppIpAddress(ip6) self._fip6 = VppIpAddress(fip6) if mac: self.vmac = VppMacAddress(self.itf.remote_mac) else: self.vmac = VppMacAddress("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.encode(), 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_recirc_add_del( 1, 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 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_subnet_add_del( 0, 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-group:[%d]" % (self.epg) def query_vpp_config(self): 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.encode(), 'bd_id': self.bd.bd.bd_id, 'rd_id': self.rd.rd_id} class VppGbpContractRule(): def __init__(self, action, nhs=[]): self.action = action self.nhs = nhs e = VppEnum.vl_api_gbp_hash_mode_t self.hash_mode = e.GBP_API_HASH_MODE_SRC_IP 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): """ GBP Contract """ 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, rules) self._test.registry.register(self, self._test.logger) def remove_vpp_config(self): self._test.vapi.gbp_contract_add_del( 0, self.src_epg, self.dst_epg, self.acl_index, []) def __str__(self): return self.object_id() def object_id(self): return "gbp-contract:[%d:%s:%d]" % (self.src_epg, self.dst_epg, self.acl_index) def query_vpp_config(self): cs = self._test.vapi.gbp_contract_dump() for c in cs: if c.contract.src_epg == self.src_epg \ and c.contract.dst_epg == self.dst_epg: 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 class TestGBP(VppTestCase): """ GBP Test Case """ def setUp(self): super(TestGBP, self).setUp() self.create_pg_interfaces(range(9)) self.create_loopback_interfaces(8) self.router_mac = VppMacAddress("00:11:22:33:44:55") for i in self.pg_interfaces: i.admin_up() for i in self.lo_interfaces: i.admin_up() def tearDown(self): for i in self.pg_interfaces: i.admin_down() super(TestGBP, self).tearDown() def send_and_expect_bridged(self, src, tx, dst): rx = self.send_and_expect(src, tx, dst) for r in rx: self.assertEqual(r[Ether].src, tx[0][Ether].src) self.assertEqual(r[Ether].dst, tx[0][Ether].dst) self.assertEqual(r[IP].src, tx[0][IP].src) self.assertEqual(r[IP].dst, tx[0][IP].dst) return rx def send_and_expect_bridged6(self, src, tx, dst): rx = self.send_and_expect(src, tx, dst) for r in rx: self.assertEqual(r[Ether].src, tx[0][Ether].src) self.assertEqual(r[Ether].dst, tx[0][Ether].dst) self.assertEqual(r[IPv6].src, tx[0][IPv6].src) self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst) return rx def send_and_expect_routed(self, src, tx, dst, src_mac): rx = self.send_and_expect(src, tx, dst) for r in rx: self.assertEqual(r[Ether].src, src_mac) self.assertEqual(r[Ether].dst, dst.remote_mac) self.assertEqual(r[IP].src, tx[0][IP].src) self.assertEqual(r[IP].dst, tx[0][IP].dst) return rx def send_and_expect_natted(self, src, tx, dst, src_ip): rx = self.send_and_expect(src, tx, dst) for r in rx: self.assertEqual(r[Ether].src, tx[0][Ether].src) self.assertEqual(r[Ether].dst, tx[0][Ether].dst) self.assertEqual(r[IP].src, src_ip) self.assertEqual(r[IP].dst, tx[0][IP].dst) return rx def send_and_expect_natted6(self, src, tx, dst, src_ip): rx = self.send_and_expect(src, tx, dst) for r in rx: self.assertEqual(r[Ether].src, tx[0][Ether].src) self.assertEqual(r[Ether].dst, tx[0][Ether].dst) self.assertEqual(r[IPv6].src, src_ip) self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst) return rx def send_and_expect_unnatted(self, src, tx, dst, dst_ip): rx = self.send_and_expect(src, tx, dst) for r in rx: self.assertEqual(r[Ether].src, tx[0][Ether].src) self.assertEqual(r[Ether].dst, tx[0][Ether].dst) self.assertEqual(r[IP].dst, dst_ip) self.assertEqual(r[IP].src, tx[0][IP].src) return rx def send_and_expect_unnatted6(self, src, tx, dst, dst_ip): rx = self.send_and_expect(src, tx, dst) for r in rx: self.assertEqual(r[Ether].src, tx[0][Ether].src) self.assertEqual(r[Ether].dst, tx[0][Ether].dst) self.assertEqual(r[IPv6].dst, dst_ip) self.assertEqual(r[IPv6].src, tx[0][IPv6].src) return rx def send_and_expect_double_natted(self, src, tx, dst, src_ip, dst_ip): rx = self.send_and_expect(src, tx, dst) for r in rx: self.assertEqual(r[Ether].src, self.router_mac.address) 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, self.router_mac.address) 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 """ # # Bridge Domains # bd1 = VppBridgeDomain(self, 1) bd2 = VppBridgeDomain(self, 2) bd20 = VppBridgeDomain(self, 20) bd1.add_vpp_config() bd2.add_vpp_config() bd20.add_vpp_config() gbd1 = VppGbpBridgeDomain(self, bd1, 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() # # Route Domains # gt4 = VppIpTable(self, 0) gt4.add_vpp_config() gt6 = VppIpTable(self, 0, is_ip6=True) gt6.add_vpp_config() nt4 = VppIpTable(self, 20) nt4.add_vpp_config() nt6 = VppIpTable(self, 20, is_ip6=True) nt6.add_vpp_config() rd0 = VppGbpRouteDomain(self, 0, gt4, gt6, None, None) rd20 = VppGbpRouteDomain(self, 20, nt4, nt6, None, None) rd0.add_vpp_config() rd20.add_vpp_config() # # 3 EPGs, 2 of which share a BD. # 2 NAT EPGs, one for floating-IP subnets, the other for internet # epgs = [VppGbpEndpointGroup(self, 220, 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)] epg_nat = epgs[3] recirc_nat = recircs[3] # # 4 end-points, 2 in the same subnet, 3 in the same BD # eps = [VppGbpEndpoint(self, self.pg0, epgs[0], recircs[0], "10.0.0.1", "11.0.0.1", "2001:10::1", "3001::1"), VppGbpEndpoint(self, self.pg1, epgs[0], recircs[0], "10.0.0.2", "11.0.0.2", "2001:10::2", "3001::2"), VppGbpEndpoint(self, self.pg2, epgs[1], recircs[1], "10.0.1.1", "11.0.0.3", "2001:10:1::1", "3001::3"), VppGbpEndpoint(self, self.pg3, epgs[2], recircs[2], "10.0.2.1", "11.0.0.4", "2001:10:2::1", "3001::4")] # # Config related to each of the EPGs # for epg in epgs: # IP config on the BVI interfaces if epg != epgs[1] and epg != epgs[4]: 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.bytes) # 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) 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() # EPG uplink interfaces in the RD VppIpInterfaceBind(self, epg.uplink, epg.rd.t4).add_vpp_config() VppIpInterfaceBind(self, epg.uplink, epg.rd.t6).add_vpp_config() # add the BD ARP termination entry for BVI IP epg.bd_arp_ip4 = VppBridgeDomainArpEntry(self, epg.bd.bd, self.router_mac.address, epg.bvi_ip4) epg.bd_arp_ip6 = VppBridgeDomainArpEntry(self, epg.bd.bd, self.router_mac.address, epg.bvi_ip6) epg.bd_arp_ip4.add_vpp_config() epg.bd_arp_ip6.add_vpp_config() # EPG in VPP epg.add_vpp_config() for recirc in recircs: # EPG's ingress recirculation interface maps to its RD VppIpInterfaceBind(self, recirc.recirc, recirc.epg.rd.t4).add_vpp_config() VppIpInterfaceBind(self, recirc.recirc, recirc.epg.rd.t6).add_vpp_config() self.vapi.sw_interface_set_l2_emulation( recirc.recirc.sw_if_index) self.vapi.nat44_interface_add_del_feature( recirc.recirc.sw_if_index, is_inside=0, 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=self.router_mac.address) / IP(src=eps[0].ip4.address, dst="10.0.0.99") / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) pkt_inter_epg_222_ip4 = (Ether(src=self.pg0.remote_mac, dst=self.router_mac.address) / 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=self.router_mac.address) / 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.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_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.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_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_220_to_222 = (Ether(src=self.pg0.remote_mac, dst=self.router_mac.address) / 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(eps[0].itf, pkt_inter_epg_220_to_221 * 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 # 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_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, 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_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(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, self.router_mac.address) # # remove both contracts, traffic stops in both directions # c2.remove_vpp_config() c1.remove_vpp_config() c3.remove_vpp_config() acl.remove_vpp_config() self.send_and_assert_no_replies(eps[2].itf, pkt_inter_epg_221_to_220 * 65) self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_221 * 65) 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=self.router_mac.address) / 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=self.router_mac.address) / 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=self.router_mac.address, 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=self.router_mac.address, 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=self.router_mac.address) / 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=self.router_mac.address) / 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.sw_interface_set_l2_emulation( recirc.recirc.sw_if_index, enable=0) self.vapi.nat44_interface_add_del_feature( recirc.recirc.sw_if_index, is_inside=0, is_add=0) self.vapi.nat66_add_del_interface( recirc.recirc.sw_if_index, is_inside=0, is_add=0) def test_gbp_learn_l2(self): """ GBP L2 Endpoint Learning """ 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 # self.sleep(2) for l in learnt: self.assertFalse(find_gbp_endpoint(self, tep1_sw_if_index, mac=l['mac'])) self.logger.info(self.vapi.cli("show gbp endpoint")) self.logger.info(self.vapi.cli("show gbp vxlan")) self.logger.info(self.vapi.cli("show vxlan-gbp tunnel")) # # 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) self.sleep(2) for l in learnt: self.assertFalse(find_gbp_endpoint(self, 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 # self.sleep(2) for l in learnt: self.assertFalse(find_gbp_endpoint(self, 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")) def test_gbp_learn_vlan_l2(self): """ GBP L2 Endpoint w/ VLANs""" 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() def test_gbp_learn_l3(self): """ GBP L3 Endpoint Learning """ 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" 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.address) # # 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']) self.sleep(2) 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")) self.sleep(2) for l in learnt: self.assertFalse(find_gbp_endpoint(self, tep1_sw_if_index, 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 send 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", VppEnum.vl_api_gbp_endpoint_flags_t.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", VppEnum.vl_api_gbp_endpoint_flags_t.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") 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.address) # # 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=ep1.mac, dst=ep3.mac) / IP(src=ep1.ip4.address, dst=ep3.ip4.address) / UDP(sport=1234, dport=1235) / 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=ep1.mac, dst=ep3.mac) / IPv6(src=ep1.ip6.address, dst=ep3.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]) c1 = VppGbpContract( self, 220, 222, acl_index, [VppGbpContractRule( VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, [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, [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() # # 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, 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, ep1.ip4.address) self.assertEqual(rx[IP].dst, ep3.ip4.address) rxs = self.send_and_expect(self.pg0, 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, ep1.ip4.address) self.assertEqual(rx[IP].dst, ep3.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.pg0, 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, ep1.ip6.address) self.assertEqual(rx[IPv6].dst, ep3.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) # # 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=self.router_mac.address) / IP(src=ep1.ip4.address, dst=ep2.ip4.address) / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)), (Ether(src=ep1.mac, dst=self.router_mac.address) / IP(src=ep1.ip4.address, dst=ep2.ip4.address) / UDP(sport=1234, dport=1235) / Raw('\xa5' * 100))] p6 = [(Ether(src=ep1.mac, dst=self.router_mac.address) / IPv6(src=ep1.ip6.address, dst=ep2.ip6.address) / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)), (Ether(src=ep1.mac, dst=self.router_mac.address) / IPv6(src=ep1.ip6.address, dst=ep2.ip6.address) / UDP(sport=1234, dport=1230) / Raw('\xa5' * 100))] c2 = VppGbpContract( self, 220, 221, acl_index, [VppGbpContractRule( VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, [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, [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() rxs = self.send_and_expect(self.pg0, p4[0] * 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, 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() c3 = 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, [])]) c3.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=self.router_mac.address) / 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=self.router_mac.address) / 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=self.router_mac.address) / IP(src=ep1.ip4.address, dst="10.0.0.88") / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)), (Ether(src=ep1.mac, dst=self.router_mac.address) / IP(src=ep1.ip4.address, dst="10.0.0.88") / UDP(sport=1234, dport=1235) / Raw('\xa5' * 100))] p6 = [(Ether(src=ep1.mac, dst=self.router_mac.address) / IPv6(src=ep1.ip6.address, dst="2001:10::88") / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)), (Ether(src=ep1.mac, dst=self.router_mac.address) / IPv6(src=ep1.ip6.address, dst="2001:10::88") / UDP(sport=1234, dport=123) / Raw('\xa5' * 100))] rxs = self.send_and_expect(self.pg0, p4[0] * 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, ep1.ip4.address) self.assertEqual(rx[IP].dst, "10.0.0.88") rxs = self.send_and_expect(self.pg0, 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, 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") rxs = self.send_and_expect(self.pg0, 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, ep1.ip6.address) self.assertEqual(rx[IPv6].dst, "2001:10::88") if __name__ == '__main__': unittest.main(testRunner=VppTestRunner)