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