X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=test%2Ftest_gbp.py;h=132bd247dd15482453f383a518939fb5dae7f3c4;hb=b474380f;hp=427b14de5068440d000af397562b3a9b7e8fbf10;hpb=bc27d1be24815e1371dcce3bff2d3075a532acba;p=vpp.git diff --git a/test/test_gbp.py b/test/test_gbp.py index 427b14de506..132bd247dd1 100644 --- a/test/test_gbp.py +++ b/test/test_gbp.py @@ -1,74 +1,259 @@ #!/usr/bin/env python import unittest -import socket -import struct from framework import VppTestCase, VppTestRunner from vpp_object import VppObject +from vpp_neighbor import VppNeighbor +from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable + +from vpp_ip import * +from vpp_mac import * +from vpp_papi_provider import L2_PORT_TYPE from scapy.packet import Raw -from scapy.layers.l2 import Ether +from scapy.layers.l2 import Ether, ARP from scapy.layers.inet import IP, UDP -from scapy.layers.inet6 import IPv6 +from scapy.layers.inet6 import IPv6, ICMPv6ND_NS, ICMPv6NDOptSrcLLAddr, \ + ICMPv6ND_NA +from scapy.utils6 import in6_getnsma, in6_getnsmac from socket import AF_INET, AF_INET6 -from scapy.utils import inet_pton +from scapy.utils import inet_pton, inet_ntop +from util import mactobinary + + +def find_gbp_endpoint(test, sw_if_index, ip=None, mac=None): + vip = VppIpAddress(ip) + + eps = test.vapi.gbp_endpoint_dump() + for ep in eps: + if ep.endpoint.sw_if_index != sw_if_index: + continue + for eip in ep.endpoint.ips: + if vip == eip: + return True + return False class VppGbpEndpoint(VppObject): """ - GDB Endpoint + GBP Endpoint + """ + + @property + def bin_mac(self): + return mactobinary(self.itf.remote_mac) + + @property + def mac(self): + return self.itf.remote_mac + + @property + def ip4(self): + return self._ip4 + + @property + def fip4(self): + return self._fip4 + + @property + def ip6(self): + return self._ip6 + + @property + def fip6(self): + return self._fip6 + + @property + def ips(self): + return [self.ip4, self.ip6] + + @property + def fips(self): + return [self.fip4, self.fip6] + + def __init__(self, test, itf, epg, recirc, ip4, fip4, ip6, fip6): + self._test = test + self.itf = itf + self.epg = epg + self.recirc = recirc + + self._ip4 = VppIpAddress(ip4) + self._fip4 = VppIpAddress(fip4) + self._ip6 = VppIpAddress(ip6) + self._fip6 = VppIpAddress(fip6) + + self.vmac = VppMacAddress(self.itf.remote_mac) + + def add_vpp_config(self): + res = self._test.vapi.gbp_endpoint_add( + self.itf.sw_if_index, + [self.ip4.encode(), self.ip6.encode()], + self.vmac.encode(), + self.epg.epg) + self.handle = res.handle + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_endpoint_del(self.handle) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "gbp-endpoint;[%d:%s:%d]" % (self.itf.sw_if_index, + self.ip4.address, + self.epg.epg) + + def query_vpp_config(self): + return find_gbp_endpoint(self._test, + self.itf.sw_if_index, + self.ip4.address) + + +class VppGbpRecirc(VppObject): + """ + GBP Recirculation Interface """ - def __init__(self, test, sw_if_index, addr, epg, is_ip6=0): + def __init__(self, test, epg, recirc, is_ext=False): self._test = test + self.recirc = recirc + self.epg = epg + self.is_ext = is_ext + + def add_vpp_config(self): + self._test.vapi.gbp_recirc_add_del( + 1, + self.recirc.sw_if_index, + self.epg.epg, + self.is_ext) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_recirc_add_del( + 0, + self.recirc.sw_if_index, + self.epg.epg, + self.is_ext) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "gbp-recirc;[%d]" % (self.recirc.sw_if_index) + + def query_vpp_config(self): + rs = self._test.vapi.gbp_recirc_dump() + for r in rs: + if r.recirc.sw_if_index == self.recirc.sw_if_index: + return True + return False + + +class VppGbpSubnet(VppObject): + """ + GBP Subnet + """ + + def __init__(self, test, table_id, address, address_len, + is_internal=True, + sw_if_index=None, epg=None): + self._test = test + self.table_id = table_id + self.prefix = VppIpPrefix(address, address_len) + self.is_internal = is_internal self.sw_if_index = sw_if_index self.epg = epg - self.addr_p = addr - self.is_ip6 = is_ip6 - if is_ip6: - self.addr = inet_pton(AF_INET6, addr) - else: - self.addr = inet_pton(AF_INET, addr) def add_vpp_config(self): - self._test.vapi.gbp_endpoint_add_del( + self._test.vapi.gbp_subnet_add_del( 1, - self.sw_if_index, - self.addr, - self.is_ip6, - self.epg) + self.table_id, + self.is_internal, + self.prefix.encode(), + sw_if_index=self.sw_if_index if self.sw_if_index else 0xffffffff, + epg_id=self.epg if self.epg else 0xffff) self._test.registry.register(self, self._test.logger) def remove_vpp_config(self): - self._test.vapi.gbp_endpoint_add_del( + self._test.vapi.gbp_subnet_add_del( 0, - self.sw_if_index, - self.addr, - self.is_ip6, - self.epg) + self.table_id, + self.is_internal, + self.prefix.encode()) def __str__(self): return self.object_id() def object_id(self): - return "gbp-endpoint;[%d:%s:%d]" % (self.sw_if_index, - self.addr_p, - self.epg) + return "gbp-subnet;[%d-%s]" % (self.table_id, + self.prefix) def query_vpp_config(self): - eps = self._test.vapi.gbp_endpoint_dump() - for ep in eps: - if ep.endpoint.address == self.addr \ - and ep.endpoint.sw_if_index == self.sw_if_index: + ss = self._test.vapi.gbp_subnet_dump() + for s in ss: + if s.subnet.table_id == self.table_id and \ + s.subnet.prefix == self.prefix: + return True + return False + + +class VppGbpEndpointGroup(VppObject): + """ + GBP Endpoint Group + """ + + def __init__(self, test, epg, rd, bd, uplink, + bvi, bvi_ip4, bvi_ip6=None): + self._test = test + self.uplink = uplink + self.bvi = bvi + self.bvi_ip4 = bvi_ip4 + self.bvi_ip4_n = inet_pton(AF_INET, bvi_ip4) + self.bvi_ip6 = bvi_ip6 + self.bvi_ip6_n = inet_pton(AF_INET6, bvi_ip6) + self.epg = epg + self.bd = bd + self.rd = rd + + def add_vpp_config(self): + self._test.vapi.gbp_endpoint_group_add_del( + 1, + self.epg, + self.bd, + self.rd, + self.rd, + self.uplink.sw_if_index) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_endpoint_group_add_del( + 0, + self.epg, + self.bd, + self.rd, + self.rd, + self.uplink.sw_if_index) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "gbp-endpoint-group;[%d]" % (self.epg) + + def query_vpp_config(self): + epgs = self._test.vapi.gbp_endpoint_group_dump() + for epg in epgs: + if epg.epg.epg_id == self.epg: return True return False class VppGbpContract(VppObject): """ - GDB Contract + GBP Contract """ def __init__(self, test, src_epg, dst_epg, acl_index): @@ -101,10 +286,65 @@ class VppGbpContract(VppObject): self.acl_index) def query_vpp_config(self): - eps = self._test.vapi.gbp_contract_dump() - for ep in eps: - if ep.contract.src_epg == self.src_epg \ - and ep.contract.dst_epg == self.dst_epg: + cs = self._test.vapi.gbp_contract_dump() + for c in cs: + if c.contract.src_epg == self.src_epg \ + and c.contract.dst_epg == self.dst_epg: + return True + return False + + +class VppGbpAcl(VppObject): + """ + GBP Acl + """ + + def __init__(self, test): + self._test = test + self.acl_index = 4294967295 + + def create_rule(self, is_ipv6=0, permit_deny=0, proto=-1, + s_prefix=0, s_ip='\x00\x00\x00\x00', sport_from=0, + sport_to=65535, d_prefix=0, d_ip='\x00\x00\x00\x00', + dport_from=0, dport_to=65535): + if proto == -1 or proto == 0: + sport_to = 0 + dport_to = sport_to + elif proto == 1 or proto == 58: + sport_to = 255 + dport_to = sport_to + rule = ({'is_permit': permit_deny, 'is_ipv6': is_ipv6, 'proto': proto, + 'srcport_or_icmptype_first': sport_from, + 'srcport_or_icmptype_last': sport_to, + 'src_ip_prefix_len': s_prefix, + 'src_ip_addr': s_ip, + 'dstport_or_icmpcode_first': dport_from, + 'dstport_or_icmpcode_last': dport_to, + 'dst_ip_prefix_len': d_prefix, + 'dst_ip_addr': d_ip}) + return rule + + def add_vpp_config(self, rules): + + reply = self._test.vapi.acl_add_replace(self.acl_index, + r=rules, + tag='GBPTest') + self.acl_index = reply.acl_index + return self.acl_index + + def remove_vpp_config(self): + self._test.vapi.acl_del(self.acl_index) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "gbp-acl;[%d]" % (self.acl_index) + + def query_vpp_config(self): + cs = self._test.vapi.acl_dump() + for c in cs: + if c.acl_index == self.acl_index: return True return False @@ -115,233 +355,603 @@ class TestGBP(VppTestCase): def setUp(self): super(TestGBP, self).setUp() - # create 6 pg interfaces for pg0 to pg5 - self.create_pg_interfaces(range(6)) + self.create_pg_interfaces(range(9)) + self.create_loopback_interfaces(9) + + self.router_mac = "00:11:22:33:44:55" for i in self.pg_interfaces: i.admin_up() - i.config_ip4() - i.resolve_arp() - i.config_ip6() - i.resolve_ndp() + for i in self.lo_interfaces: + i.admin_up() + self.vapi.sw_interface_set_mac_address( + i.sw_if_index, + mactobinary(self.router_mac)) def tearDown(self): for i in self.pg_interfaces: - i.unconfig_ip4() - i.unconfig_ip6() + i.admin_down() super(TestGBP, self).tearDown() - def test_gbp4(self): - """ Group Based Policy v4 """ - - ep1 = VppGbpEndpoint(self, - self.pg0.sw_if_index, - self.pg0.remote_ip4, - 220) - ep1.add_vpp_config() - ep2 = VppGbpEndpoint(self, - self.pg1.sw_if_index, - self.pg1.remote_ip4, - 220) - ep2.add_vpp_config() - - ep3 = VppGbpEndpoint(self, - self.pg2.sw_if_index, - self.pg2.remote_ip4, - 221) - ep3.add_vpp_config() - ep4 = VppGbpEndpoint(self, - self.pg3.sw_if_index, - self.pg3.remote_ip4, - 222) - ep4.add_vpp_config() + def send_and_expect_bridged(self, src, tx, dst): + rx = self.send_and_expect(src, tx, dst) - self.logger.info(self.vapi.cli("sh gbp endpoint")) + for r in rx: + self.assertEqual(r[Ether].src, tx[0][Ether].src) + self.assertEqual(r[Ether].dst, tx[0][Ether].dst) + self.assertEqual(r[IP].src, tx[0][IP].src) + self.assertEqual(r[IP].dst, tx[0][IP].dst) + return rx + + def send_and_expect_bridged6(self, src, tx, dst): + rx = self.send_and_expect(src, tx, dst) + + for r in rx: + self.assertEqual(r[Ether].src, tx[0][Ether].src) + self.assertEqual(r[Ether].dst, tx[0][Ether].dst) + self.assertEqual(r[IPv6].src, tx[0][IPv6].src) + self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst) + return rx + + def send_and_expect_routed(self, src, tx, dst, src_mac): + rx = self.send_and_expect(src, tx, dst) + + for r in rx: + self.assertEqual(r[Ether].src, src_mac) + self.assertEqual(r[Ether].dst, dst.remote_mac) + self.assertEqual(r[IP].src, tx[0][IP].src) + self.assertEqual(r[IP].dst, tx[0][IP].dst) + return rx + + def send_and_expect_natted(self, src, tx, dst, src_ip): + rx = self.send_and_expect(src, tx, dst) + + for r in rx: + self.assertEqual(r[Ether].src, tx[0][Ether].src) + self.assertEqual(r[Ether].dst, tx[0][Ether].dst) + self.assertEqual(r[IP].src, src_ip) + self.assertEqual(r[IP].dst, tx[0][IP].dst) + return rx + + def send_and_expect_natted6(self, src, tx, dst, src_ip): + rx = self.send_and_expect(src, tx, dst) + + for r in rx: + self.assertEqual(r[Ether].src, tx[0][Ether].src) + self.assertEqual(r[Ether].dst, tx[0][Ether].dst) + self.assertEqual(r[IPv6].src, src_ip) + self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst) + return rx + + def send_and_expect_unnatted(self, src, tx, dst, dst_ip): + rx = self.send_and_expect(src, tx, dst) + + for r in rx: + self.assertEqual(r[Ether].src, tx[0][Ether].src) + self.assertEqual(r[Ether].dst, tx[0][Ether].dst) + self.assertEqual(r[IP].dst, dst_ip) + self.assertEqual(r[IP].src, tx[0][IP].src) + return rx + + def send_and_expect_unnatted6(self, src, tx, dst, dst_ip): + rx = self.send_and_expect(src, tx, dst) + + for r in rx: + self.assertEqual(r[Ether].src, tx[0][Ether].src) + self.assertEqual(r[Ether].dst, tx[0][Ether].dst) + self.assertEqual(r[IPv6].dst, dst_ip) + self.assertEqual(r[IPv6].src, tx[0][IPv6].src) + return rx + + def send_and_expect_double_natted(self, src, tx, dst, src_ip, dst_ip): + rx = self.send_and_expect(src, tx, dst) + + for r in rx: + self.assertEqual(r[Ether].src, self.router_mac) + self.assertEqual(r[Ether].dst, dst.remote_mac) + self.assertEqual(r[IP].dst, dst_ip) + self.assertEqual(r[IP].src, src_ip) + return rx + + def send_and_expect_double_natted6(self, src, tx, dst, src_ip, dst_ip): + rx = self.send_and_expect(src, tx, dst) + + for r in rx: + self.assertEqual(r[Ether].src, self.router_mac) + self.assertEqual(r[Ether].dst, dst.remote_mac) + self.assertEqual(r[IPv6].dst, dst_ip) + self.assertEqual(r[IPv6].src, src_ip) + return rx + + def test_gbp(self): + """ Group Based Policy """ + + nat_table = VppIpTable(self, 20) + nat_table.add_vpp_config() + nat_table = VppIpTable(self, 20, is_ip6=True) + nat_table.add_vpp_config() # - # in the abscense of policy, endpoints in the same EPG - # can communicate + # Bridge Domains # - pkt_intra_epg = (Ether(src=self.pg0.remote_mac, - dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, - dst=self.pg1.remote_ip4) / - UDP(sport=1234, dport=1234) / - Raw('\xa5' * 100)) + self.vapi.bridge_domain_add_del(1, flood=1, uu_flood=1, forward=1, + learn=0, arp_term=1, is_add=1) + self.vapi.bridge_domain_add_del(2, flood=1, uu_flood=1, forward=1, + learn=0, arp_term=1, is_add=1) + self.vapi.bridge_domain_add_del(20, flood=1, uu_flood=1, forward=1, + learn=0, arp_term=1, is_add=1) + + # + # 3 EPGs, 2 of which share a BD. + # 2 NAT EPGs, one for floating-IP subnets, the other for internet + # + epgs = [VppGbpEndpointGroup(self, 220, 0, 1, self.pg4, + self.loop0, + "10.0.0.128", + "2001:10::128"), + VppGbpEndpointGroup(self, 221, 0, 1, self.pg5, + self.loop0, + "10.0.1.128", + "2001:10:1::128"), + VppGbpEndpointGroup(self, 222, 0, 2, self.pg6, + self.loop1, + "10.0.2.128", + "2001:10:2::128"), + VppGbpEndpointGroup(self, 333, 20, 20, self.pg7, + self.loop2, + "11.0.0.128", + "3001::128"), + VppGbpEndpointGroup(self, 444, 20, 20, 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.loop8, is_ext=True)] - self.send_and_expect(self.pg0, pkt_intra_epg * 65, self.pg1) + epg_nat = epgs[3] + recirc_nat = recircs[3] # - # in the abscense of policy, endpoints in the different EPG - # cannot communicate + # 4 end-points, 2 in the same subnet, 3 in the same BD # - pkt_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac, - dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, - dst=self.pg2.remote_ip4) / - UDP(sport=1234, dport=1234) / - Raw('\xa5' * 100)) - pkt_inter_epg_220_to_222 = (Ether(src=self.pg0.remote_mac, - dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, - dst=self.pg3.remote_ip4) / - UDP(sport=1234, dport=1234) / - Raw('\xa5' * 100)) - pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac, - dst=self.pg2.local_mac) / - IP(src=self.pg2.remote_ip4, - dst=self.pg0.remote_ip4) / - UDP(sport=1234, dport=1234) / - Raw('\xa5' * 100)) + 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.send_and_assert_no_replies(self.pg0, - pkt_inter_epg_220_to_221 * 65) - self.send_and_assert_no_replies(self.pg0, - pkt_inter_epg_221_to_220 * 65) + # + # 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]: + epg.bvi.set_table_ip4(epg.rd) + epg.bvi.set_table_ip6(epg.rd) + + # The BVIs are NAT inside interfaces + self.vapi.nat44_interface_add_del_feature(epg.bvi.sw_if_index, + is_inside=1, + is_add=1) + self.vapi.nat66_add_del_interface(epg.bvi.sw_if_index, + is_inside=1, + is_add=1) + + self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index, + epg.bvi_ip4_n, + 32) + self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index, + epg.bvi_ip6_n, + 128, + is_ipv6=True) + + # EPG uplink interfaces in the BD + epg.uplink.set_table_ip4(epg.rd) + epg.uplink.set_table_ip6(epg.rd) + self.vapi.sw_interface_set_l2_bridge(epg.uplink.sw_if_index, + epg.bd) + + # add the BD ARP termination entry for BVI IP + self.vapi.bd_ip_mac_add_del(bd_id=epg.bd, + mac=mactobinary(self.router_mac), + ip=epg.bvi_ip4_n, + is_ipv6=0, + is_add=1) + self.vapi.bd_ip_mac_add_del(bd_id=epg.bd, + mac=mactobinary(self.router_mac), + ip=epg.bvi_ip6_n, + is_ipv6=1, + is_add=1) + + # epg[1] shares the same BVI to epg[0] + if epg != epgs[1] and epg != epgs[4]: + # BVI in BD + self.vapi.sw_interface_set_l2_bridge( + epg.bvi.sw_if_index, + epg.bd, + port_type=L2_PORT_TYPE.BVI) + + # BVI L2 FIB entry + self.vapi.l2fib_add_del(self.router_mac, + epg.bd, + epg.bvi.sw_if_index, + is_add=1, bvi_mac=1) + + # EPG in VPP + epg.add_vpp_config() + + for recirc in recircs: + # EPG's ingress recirculation interface maps to its RD + recirc.recirc.set_table_ip4(recirc.epg.rd) + recirc.recirc.set_table_ip6(recirc.epg.rd) + + # in the bridge to allow DVR. L2 emulation to punt to L3 + self.vapi.sw_interface_set_l2_bridge(recirc.recirc.sw_if_index, + recirc.epg.bd) + self.vapi.sw_interface_set_l2_emulation( + recirc.recirc.sw_if_index) + + self.vapi.nat44_interface_add_del_feature( + recirc.recirc.sw_if_index, + is_inside=0, + is_add=1) + self.vapi.nat66_add_del_interface( + recirc.recirc.sw_if_index, + is_inside=0, + is_add=1) + + recirc.add_vpp_config() + + ep_routes = [] + ep_arps = [] + 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): + r = VppIpRoute(self, ip.address, ip.length, + [VppRoutePath(ip.address, + ep.epg.bvi.sw_if_index, + proto=ip.dpo_proto)], + is_ip6=ip.is_ip6) + r.add_vpp_config() + ep_routes.append(r) + + # + # ARP entries for the endpoints + # + a = VppNeighbor(self, + ep.epg.bvi.sw_if_index, + ep.itf.remote_mac, + ip.address, + af=ip.af) + a.add_vpp_config() + ep_arps.append(a) + + # add the BD ARP termination entry + self.vapi.bd_ip_mac_add_del(bd_id=ep.epg.bd, + mac=ep.bin_mac, + ip=ip.bytes, + is_ipv6=ip.is_ip6, + is_add=1) + + # Add static mappings for each EP from the 10/8 to 11/8 network + if ip.af == AF_INET: + self.vapi.nat44_add_del_static_mapping(ip.bytes, + fip.bytes, + vrf_id=0, + addr_only=1) + else: + self.vapi.nat66_add_del_static_mapping(ip.bytes, + fip.bytes, + vrf_id=0) + + # add each EP itf to the its BD + self.vapi.sw_interface_set_l2_bridge(ep.itf.sw_if_index, + ep.epg.bd) + + # L2 FIB entry + self.vapi.l2fib_add_del(ep.mac, + ep.epg.bd, + ep.itf.sw_if_index, + 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), timeout=0.2) + + for ii, ip in enumerate(ep.ips): + p = rx[ii] + + if ip.is_ip6: + self.assertTrue(p.haslayer(ICMPv6ND_NA)) + self.assertEqual(p[ICMPv6ND_NA].tgt, ip.address) + else: + self.assertTrue(p.haslayer(ARP)) + self.assertEqual(p[ARP].psrc, ip.address) + self.assertEqual(p[ARP].pdst, ip.address) + + # add the BD ARP termination entry for floating IP + for fip in ep.fips: + self.vapi.bd_ip_mac_add_del(bd_id=epg_nat.bd, + mac=ep.bin_mac, + ip=fip.bytes, + is_ipv6=fip.is_ip6, + is_add=1) + + # floating IPs route via EPG recirc + r = VppIpRoute(self, fip.address, fip.length, + [VppRoutePath(fip.address, + ep.recirc.recirc.sw_if_index, + is_dvr=1, + proto=fip.dpo_proto)], + table_id=20, + is_ip6=fip.is_ip6) + r.add_vpp_config() + ep_routes.append(r) + + # L2 FIB entries in the NAT EPG BD to bridge the packets from + # the outside direct to the internal EPG + self.vapi.l2fib_add_del(ep.mac, + epg_nat.bd, + ep.recirc.recirc.sw_if_index, + is_add=1) # - # A uni-directional contract from EPG 220 -> 221 + # ARP packets for unknown IP are flooded # - c1 = VppGbpContract(self, 220, 221, 0xffffffff) - c1.add_vpp_config() + 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="10.0.0.88")) - self.send_and_expect(self.pg0, - pkt_inter_epg_220_to_221 * 65, - self.pg2) - self.send_and_assert_no_replies(self.pg2, - pkt_inter_epg_221_to_220 * 65) + self.send_and_expect(self.pg0, [pkt_arp], self.pg0) # - # contract for the return direction + # ARP/ND packets get a response # - c2 = VppGbpContract(self, 221, 220, 0xffffffff) - c2.add_vpp_config() + 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.address)) + + self.send_and_expect(self.pg0, [pkt_arp], self.pg0) - self.send_and_expect(self.pg0, - pkt_inter_epg_220_to_221 * 65, - self.pg2) - self.send_and_expect(self.pg2, - pkt_inter_epg_221_to_220 * 65, - self.pg0) + nsma = in6_getnsma(inet_pton(AF_INET6, eps[0].ip6.address)) + d = inet_ntop(AF_INET6, nsma) + pkt_nd = (Ether(dst=in6_getnsmac(nsma)) / + IPv6(dst=d, src=eps[0].ip6.address) / + ICMPv6ND_NS(tgt=epgs[0].bvi_ip6) / + ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac)) + self.send_and_expect(self.pg0, [pkt_nd], self.pg0) # - # check that inter group is still disabled for the groups - # not in the contract. + # broadcast packets are flooded # - self.send_and_assert_no_replies(self.pg0, - pkt_inter_epg_220_to_222 * 65) + pkt_bcast = (Ether(dst="ff:ff:ff:ff:ff:ff", + src=self.pg0.remote_mac) / + IP(src=eps[0].ip4.address, dst="232.1.1.1") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) - self.logger.info(self.vapi.cli("sh gbp contract")) + 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) # - # remove both contracts, traffic stops in both directions + # packets to non-local L3 destinations dropped # - c2.remove_vpp_config() - c1.remove_vpp_config() + pkt_intra_epg_220_ip4 = (Ether(src=self.pg0.remote_mac, + dst=self.router_mac) / + IP(src=eps[0].ip4.address, + dst="10.0.0.99") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + pkt_inter_epg_222_ip4 = (Ether(src=self.pg0.remote_mac, + dst=self.router_mac) / + IP(src=eps[0].ip4.address, + dst="10.0.1.99") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) - self.send_and_assert_no_replies(self.pg2, - pkt_inter_epg_221_to_220 * 65) - self.send_and_assert_no_replies(self.pg0, - pkt_inter_epg_220_to_221 * 65) - self.send_and_expect(self.pg0, pkt_intra_epg * 65, self.pg1) - - def test_gbp6(self): - """ Group Based Policy v6 """ - - ep1 = VppGbpEndpoint(self, - self.pg0.sw_if_index, - self.pg0.remote_ip6, - 220, - is_ip6=1) - ep1.add_vpp_config() - ep2 = VppGbpEndpoint(self, - self.pg1.sw_if_index, - self.pg1.remote_ip6, - 220, - is_ip6=1) - ep2.add_vpp_config() - - ep3 = VppGbpEndpoint(self, - self.pg2.sw_if_index, - self.pg2.remote_ip6, - 221, - is_ip6=1) - ep3.add_vpp_config() - ep4 = VppGbpEndpoint(self, - self.pg3.sw_if_index, - self.pg3.remote_ip6, - 222, - is_ip6=1) - ep4.add_vpp_config() + self.send_and_assert_no_replies(self.pg0, pkt_intra_epg_220_ip4 * 65) + + pkt_inter_epg_222_ip6 = (Ether(src=self.pg0.remote_mac, + dst=self.router_mac) / + IPv6(src=eps[0].ip6.address, + dst="2001:10::99") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + self.send_and_assert_no_replies(self.pg0, pkt_inter_epg_222_ip6 * 65) + + # + # Add the subnet routes + # + s41 = VppGbpSubnet(self, 0, "10.0.0.0", 24) + s42 = VppGbpSubnet(self, 0, "10.0.1.0", 24) + s43 = VppGbpSubnet(self, 0, "10.0.2.0", 24) + s41.add_vpp_config() + s42.add_vpp_config() + s43.add_vpp_config() + s61 = VppGbpSubnet(self, 0, "2001:10::1", 64) + s62 = VppGbpSubnet(self, 0, "2001:10:1::1", 64) + s63 = VppGbpSubnet(self, 0, "2001:10:2::1", 64) + s61.add_vpp_config() + s62.add_vpp_config() + s63.add_vpp_config() + self.send_and_expect_bridged(self.pg0, + pkt_intra_epg_220_ip4 * 65, + self.pg4) + self.send_and_expect_bridged(self.pg3, + pkt_inter_epg_222_ip4 * 65, + self.pg6) + self.send_and_expect_bridged6(self.pg3, + pkt_inter_epg_222_ip6 * 65, + self.pg6) + + 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")) # - # in the abscense of policy, endpoints in the same EPG + # Packet destined to unknown unicast is sent on the epg uplink ... + # + pkt_intra_epg_220_to_uplink = (Ether(src=self.pg0.remote_mac, + dst="00:00:00:33:44:55") / + IP(src=eps[0].ip4.address, + dst="10.0.0.99") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_expect_bridged(self.pg0, + pkt_intra_epg_220_to_uplink * 65, + self.pg4) + # ... and nowhere else + self.pg1.get_capture(0, timeout=0.1) + self.pg1.assert_nothing_captured(remark="Flood onto other VMS") + + pkt_intra_epg_221_to_uplink = (Ether(src=self.pg2.remote_mac, + dst="00:00:00:33:44:66") / + IP(src=eps[0].ip4.address, + dst="10.0.0.99") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_expect_bridged(self.pg2, + pkt_intra_epg_221_to_uplink * 65, + self.pg5) + + # + # Packets from the uplink are forwarded in the absence of a contract + # + pkt_intra_epg_220_from_uplink = (Ether(src="00:00:00:33:44:55", + dst=self.pg0.remote_mac) / + IP(src=eps[0].ip4.address, + dst="10.0.0.99") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_expect_bridged(self.pg4, + pkt_intra_epg_220_from_uplink * 65, + self.pg0) + + # + # in the absence of policy, endpoints in the same EPG # can communicate # pkt_intra_epg = (Ether(src=self.pg0.remote_mac, - dst=self.pg0.local_mac) / - IPv6(src=self.pg0.remote_ip6, - dst=self.pg1.remote_ip6) / + dst=self.pg1.remote_mac) / + IP(src=eps[0].ip4.address, + dst=eps[1].ip4.address) / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) - self.send_and_expect(self.pg0, pkt_intra_epg * 65, self.pg1) + self.send_and_expect_bridged(self.pg0, pkt_intra_epg * 65, self.pg1) # # in the abscense of policy, endpoints in the different EPG # cannot communicate # pkt_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac, - dst=self.pg0.local_mac) / - IPv6(src=self.pg0.remote_ip6, - dst=self.pg2.remote_ip6) / + dst=self.pg2.remote_mac) / + IP(src=eps[0].ip4.address, + dst=eps[2].ip4.address) / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) - pkt_inter_epg_220_to_222 = (Ether(src=self.pg0.remote_mac, - dst=self.pg0.local_mac) / - IPv6(src=self.pg0.remote_ip6, - dst=self.pg3.remote_ip6) / + pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac, + dst=self.pg0.remote_mac) / + IP(src=eps[2].ip4.address, + dst=eps[0].ip4.address) / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) - pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac, - dst=self.pg2.local_mac) / - IPv6(src=self.pg2.remote_ip6, - dst=self.pg0.remote_ip6) / + pkt_inter_epg_220_to_222 = (Ether(src=self.pg0.remote_mac, + dst=self.router_mac) / + IP(src=eps[0].ip4.address, + dst=eps[3].ip4.address) / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) self.send_and_assert_no_replies(self.pg0, pkt_inter_epg_220_to_221 * 65) self.send_and_assert_no_replies(self.pg0, - pkt_inter_epg_221_to_220 * 65) + pkt_inter_epg_220_to_222 * 65) # # A uni-directional contract from EPG 220 -> 221 # - c1 = VppGbpContract(self, 220, 221, 0xffffffff) + acl = VppGbpAcl(self) + rule = acl.create_rule(permit_deny=1, proto=17) + rule2 = acl.create_rule(is_ipv6=1, permit_deny=1, proto=17) + acl_index = acl.add_vpp_config([rule, rule2]) + c1 = VppGbpContract(self, 220, 221, acl_index) c1.add_vpp_config() - self.send_and_expect(self.pg0, - pkt_inter_epg_220_to_221 * 65, - self.pg2) - self.send_and_assert_no_replies(self.pg2, - pkt_inter_epg_221_to_220 * 65) + self.send_and_expect_bridged(self.pg0, + pkt_inter_epg_220_to_221 * 65, + self.pg2) + self.send_and_assert_no_replies(self.pg0, + pkt_inter_epg_220_to_222 * 65) # # contract for the return direction # - c2 = VppGbpContract(self, 221, 220, 0xffffffff) + c2 = VppGbpContract(self, 221, 220, acl_index) c2.add_vpp_config() - self.send_and_expect(self.pg0, - pkt_inter_epg_220_to_221 * 65, - self.pg2) - self.send_and_expect(self.pg2, - pkt_inter_epg_221_to_220 * 65, - self.pg0) + self.send_and_expect_bridged(self.pg0, + pkt_inter_epg_220_to_221 * 65, + self.pg2) + self.send_and_expect_bridged(self.pg2, + pkt_inter_epg_221_to_220 * 65, + self.pg0) # # check that inter group is still disabled for the groups @@ -350,19 +960,235 @@ class TestGBP(VppTestCase): self.send_and_assert_no_replies(self.pg0, pkt_inter_epg_220_to_222 * 65) + # + # A uni-directional contract from EPG 220 -> 222 'L3 routed' + # + c3 = VppGbpContract(self, 220, 222, acl_index) + c3.add_vpp_config() + self.logger.info(self.vapi.cli("sh gbp contract")) + self.send_and_expect_routed(self.pg0, + pkt_inter_epg_220_to_222 * 65, + self.pg3, + self.router_mac) + # # remove both contracts, traffic stops in both directions # c2.remove_vpp_config() c1.remove_vpp_config() + c3.remove_vpp_config() + acl.remove_vpp_config() self.send_and_assert_no_replies(self.pg2, pkt_inter_epg_221_to_220 * 65) self.send_and_assert_no_replies(self.pg0, pkt_inter_epg_220_to_221 * 65) - self.send_and_expect(self.pg0, pkt_intra_epg * 65, self.pg1) + self.send_and_expect_bridged(self.pg0, pkt_intra_epg * 65, self.pg1) + + # + # EPs to the outside world + # + + # in the EP's RD an external subnet via the NAT EPG's recirc + se1 = VppGbpSubnet(self, 0, "0.0.0.0", 0, + is_internal=False, + sw_if_index=recirc_nat.recirc.sw_if_index, + epg=epg_nat.epg) + se1.add_vpp_config() + se2 = VppGbpSubnet(self, 0, "11.0.0.0", 8, + is_internal=False, + sw_if_index=recirc_nat.recirc.sw_if_index, + epg=epg_nat.epg) + se2.add_vpp_config() + se16 = VppGbpSubnet(self, 0, "::", 0, + is_internal=False, + sw_if_index=recirc_nat.recirc.sw_if_index, + epg=epg_nat.epg) + se16.add_vpp_config() + # in the NAT RD an external subnet via the NAT EPG's uplink + se3 = VppGbpSubnet(self, 20, "0.0.0.0", 0, + is_internal=False, + sw_if_index=epg_nat.uplink.sw_if_index, + epg=epg_nat.epg) + se36 = VppGbpSubnet(self, 20, "::", 0, + is_internal=False, + sw_if_index=epg_nat.uplink.sw_if_index, + epg=epg_nat.epg) + se4 = VppGbpSubnet(self, 20, "11.0.0.0", 8, + is_internal=False, + sw_if_index=epg_nat.uplink.sw_if_index, + epg=epg_nat.epg) + se3.add_vpp_config() + se36.add_vpp_config() + se4.add_vpp_config() + + self.logger.info(self.vapi.cli("sh ip fib 0.0.0.0/0")) + self.logger.info(self.vapi.cli("sh ip fib 11.0.0.1")) + self.logger.info(self.vapi.cli("sh ip6 fib ::/0")) + self.logger.info(self.vapi.cli("sh ip6 fib %s" % + eps[0].fip6)) + + # + # From an EP to an outside addess: IN2OUT + # + pkt_inter_epg_220_to_global = (Ether(src=self.pg0.remote_mac, + dst=self.router_mac) / + IP(src=eps[0].ip4.address, + dst="1.1.1.1") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + # no policy yet + self.send_and_assert_no_replies(self.pg0, + pkt_inter_epg_220_to_global * 65) + + acl2 = VppGbpAcl(self) + rule = acl2.create_rule(permit_deny=1, proto=17, sport_from=1234, + sport_to=1234, dport_from=1234, dport_to=1234) + rule2 = acl2.create_rule(is_ipv6=1, permit_deny=1, proto=17, + sport_from=1234, sport_to=1234, + dport_from=1234, dport_to=1234) + + acl_index2 = acl2.add_vpp_config([rule, rule2]) + c4 = VppGbpContract(self, 220, 333, acl_index2) + c4.add_vpp_config() + + self.send_and_expect_natted(self.pg0, + pkt_inter_epg_220_to_global * 65, + self.pg7, + eps[0].fip4.address) + + pkt_inter_epg_220_to_global = (Ether(src=self.pg0.remote_mac, + dst=self.router_mac) / + IPv6(src=eps[0].ip6.address, + dst="6001::1") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_expect_natted6(self.pg0, + pkt_inter_epg_220_to_global * 65, + self.pg7, + eps[0].fip6.address) + + # + # From a global address to an EP: OUT2IN + # + pkt_inter_epg_220_from_global = (Ether(src=self.router_mac, + dst=self.pg0.remote_mac) / + IP(dst=eps[0].fip4.address, + src="1.1.1.1") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_assert_no_replies(self.pg7, + pkt_inter_epg_220_from_global * 65) + + c5 = VppGbpContract(self, 333, 220, acl_index2) + c5.add_vpp_config() + + self.send_and_expect_unnatted(self.pg7, + pkt_inter_epg_220_from_global * 65, + eps[0].itf, + eps[0].ip4.address) + + pkt_inter_epg_220_from_global = (Ether(src=self.router_mac, + dst=self.pg0.remote_mac) / + IPv6(dst=eps[0].fip6.address, + src="6001::1") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_expect_unnatted6(self.pg7, + pkt_inter_epg_220_from_global * 65, + eps[0].itf, + eps[0].ip6.address) + + # + # From a local VM to another local VM using resp. public addresses: + # IN2OUT2IN + # + pkt_intra_epg_220_global = (Ether(src=self.pg0.remote_mac, + dst=self.router_mac) / + IP(src=eps[0].ip4.address, + dst=eps[1].fip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_expect_double_natted(eps[0].itf, + pkt_intra_epg_220_global * 65, + eps[1].itf, + eps[0].fip4.address, + eps[1].ip4.address) + + pkt_intra_epg_220_global = (Ether(src=self.pg0.remote_mac, + dst=self.router_mac) / + IPv6(src=eps[0].ip6.address, + dst=eps[1].fip6.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_expect_double_natted6(eps[0].itf, + pkt_intra_epg_220_global * 65, + eps[1].itf, + eps[0].fip6.address, + eps[1].ip6.address) + + # + # cleanup + # + for ep in eps: + # del static mappings for each EP from the 10/8 to 11/8 network + self.vapi.nat44_add_del_static_mapping(ep.ip4.bytes, + ep.fip4.bytes, + vrf_id=0, + addr_only=1, + is_add=0) + self.vapi.nat66_add_del_static_mapping(ep.ip6.bytes, + ep.fip6.bytes, + vrf_id=0, + is_add=0) + + for epg in epgs: + # IP config on the BVI interfaces + self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index, + epg.bvi_ip4_n, + 32, + is_add=0) + self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index, + epg.bvi_ip6_n, + 128, + is_add=0, + is_ipv6=True) + self.logger.info(self.vapi.cli("sh int addr")) + + epg.uplink.set_table_ip4(0) + epg.uplink.set_table_ip6(0) + + if epg != epgs[0] and epg != epgs[3]: + epg.bvi.set_table_ip4(0) + epg.bvi.set_table_ip6(0) + + self.vapi.nat44_interface_add_del_feature(epg.bvi.sw_if_index, + is_inside=1, + is_add=0) + self.vapi.nat66_add_del_interface(epg.bvi.sw_if_index, + is_inside=1, + is_add=0) + + for recirc in recircs: + recirc.recirc.set_table_ip4(0) + recirc.recirc.set_table_ip6(0) + + self.vapi.nat44_interface_add_del_feature( + recirc.recirc.sw_if_index, + is_inside=0, + is_add=0) + self.vapi.nat66_add_del_interface( + recirc.recirc.sw_if_index, + is_inside=0, + is_add=0) if __name__ == '__main__':