diff options
author | Neale Ranns <neale.ranns@cisco.com> | 2018-10-10 07:22:51 -0700 |
---|---|---|
committer | Damjan Marion <dmarion@me.com> | 2018-11-07 12:00:10 +0000 |
commit | 93cc3ee3b3a9c9224a1446625882205f3282a949 (patch) | |
tree | 077421ee51238c22181a3b3f4871b648bb1299d3 /test | |
parent | c3df1e9a0ab79c1fe254394748ef441ffe224c43 (diff) |
GBP Endpoint Learning
Learning GBP endpoints over vxlan-gbp tunnels
Change-Id: I1db9fda5a16802d9ad8b4efd4e475614f3b21502
Signed-off-by: Neale Ranns <neale.ranns@cisco.com>
Diffstat (limited to 'test')
-rw-r--r-- | test/framework.py | 30 | ||||
-rw-r--r-- | test/test_gbp.py | 1405 | ||||
-rw-r--r-- | test/test_l2_flood.py | 2 | ||||
-rw-r--r-- | test/vpp_interface.py | 3 | ||||
-rw-r--r-- | test/vpp_ip.py | 12 | ||||
-rw-r--r-- | test/vpp_ip_route.py | 100 | ||||
-rw-r--r-- | test/vpp_l2.py | 221 | ||||
-rw-r--r-- | test/vpp_mac.py | 15 | ||||
-rw-r--r-- | test/vpp_papi_provider.py | 166 | ||||
-rw-r--r-- | test/vpp_vxlan_gbp_tunnel.py | 69 |
10 files changed, 1741 insertions, 282 deletions
diff --git a/test/framework.py b/test/framework.py index fbe6c370a3f..5bbd56a6ef7 100644 --- a/test/framework.py +++ b/test/framework.py @@ -929,7 +929,35 @@ class VppTestCase(unittest.TestCase): input.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - rx = output.get_capture(len(pkts)) + if isinstance(object, (list,)): + rx = [] + for o in output: + rx.append(output.get_capture(len(pkts))) + else: + rx = output.get_capture(len(pkts)) + return rx + + def send_and_expect_only(self, input, pkts, output, timeout=None): + self.vapi.cli("clear trace") + input.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + if isinstance(object, (list,)): + outputs = output + rx = [] + for o in outputs: + rx.append(output.get_capture(len(pkts))) + else: + rx = output.get_capture(len(pkts)) + outputs = [output] + if not timeout: + timeout = 1 + for i in self.pg_interfaces: + if i not in outputs: + i.get_capture(0, timeout=timeout) + i.assert_nothing_captured() + timeout = 0.1 + return rx diff --git a/test/test_gbp.py b/test/test_gbp.py index ef4bf7071eb..a45b2f845b8 100644 --- a/test/test_gbp.py +++ b/test/test_gbp.py @@ -5,11 +5,16 @@ import unittest from framework import VppTestCase, VppTestRunner from vpp_object import VppObject from vpp_neighbor import VppNeighbor -from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable +from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable, \ + VppIpInterfaceAddress, VppIpInterfaceBind, find_route +from vpp_l2 import VppBridgeDomain, VppBridgeDomainPort, \ + VppBridgeDomainArpEntry, VppL2FibEntry, find_bridge_domain_port +from vpp_vxlan_gbp_tunnel import * from vpp_ip import * from vpp_mac import * from vpp_papi_provider import L2_PORT_TYPE +from vpp_papi import VppEnum from scapy.packet import Raw from scapy.layers.l2 import Ether, ARP @@ -17,25 +22,43 @@ from scapy.layers.inet import IP, UDP from scapy.layers.inet6 import IPv6, ICMPv6ND_NS, ICMPv6NDOptSrcLLAddr, \ ICMPv6ND_NA from scapy.utils6 import in6_getnsma, in6_getnsmac +from scapy.layers.vxlan import VXLAN from socket import AF_INET, AF_INET6 from scapy.utils import inet_pton, inet_ntop from util import mactobinary -def find_gbp_endpoint(test, sw_if_index, ip=None, mac=None): - vip = VppIpAddress(ip) +def find_gbp_endpoint(test, sw_if_index=None, ip=None, mac=None): + if ip: + vip = VppIpAddress(ip) + if mac: + vmac = VppMacAddress(mac) eps = test.vapi.gbp_endpoint_dump() + for ep in eps: - if ep.endpoint.sw_if_index != sw_if_index: - continue - for eip in ep.endpoint.ips: - if vip == eip: + if sw_if_index: + if ep.endpoint.sw_if_index != sw_if_index: + continue + if ip: + for eip in ep.endpoint.ips: + if vip == eip: + return True + if mac: + if vmac == ep.endpoint.mac: return True return False +def find_gbp_vxlan(test, vni): + ts = test.vapi.gbp_vxlan_tunnel_dump() + for t in ts: + if t.tunnel.vni == vni: + return True + return False + + class VppGbpEndpoint(VppObject): """ GBP Endpoint @@ -43,7 +66,11 @@ class VppGbpEndpoint(VppObject): @property def bin_mac(self): - return mactobinary(self.itf.remote_mac) + return self.vmac.bytes + + @property + def mac(self): + return self.vmac.address @property def mac(self): @@ -73,7 +100,11 @@ class VppGbpEndpoint(VppObject): def fips(self): return [self.fip4, self.fip6] - def __init__(self, test, itf, epg, recirc, ip4, fip4, ip6, fip6): + def __init__(self, test, itf, epg, recirc, ip4, fip4, ip6, fip6, + flags=0, + tun_src="0.0.0.0", + tun_dst="0.0.0.0", + mac=True): self._test = test self.itf = itf self.epg = epg @@ -84,14 +115,24 @@ class VppGbpEndpoint(VppObject): self._ip6 = VppIpAddress(ip6) self._fip6 = VppIpAddress(fip6) - self.vmac = VppMacAddress(self.itf.remote_mac) + if mac: + self.vmac = VppMacAddress(self.itf.remote_mac) + else: + self.vmac = VppMacAddress("00:00:00:00:00:00") + + self.flags = flags + self.tun_src = VppIpAddress(tun_src) + self.tun_dst = VppIpAddress(tun_dst) def add_vpp_config(self): res = self._test.vapi.gbp_endpoint_add( self.itf.sw_if_index, [self.ip4.encode(), self.ip6.encode()], self.vmac.encode(), - self.epg.epg) + self.epg.epg, + self.flags, + self.tun_src.encode(), + self.tun_dst.encode()) self.handle = res.handle self._test.registry.register(self, self._test.logger) @@ -102,9 +143,10 @@ class VppGbpEndpoint(VppObject): 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) + return "gbp-endpoint:[%d==%d:%s:%d]" % (self.handle, + self.itf.sw_if_index, + self.ip4.address, + self.epg.epg) def query_vpp_config(self): return find_gbp_endpoint(self._test, @@ -142,7 +184,7 @@ class VppGbpRecirc(VppObject): return self.object_id() def object_id(self): - return "gbp-recirc;[%d]" % (self.recirc.sw_if_index) + return "gbp-recirc:[%d]" % (self.recirc.sw_if_index) def query_vpp_config(self): rs = self._test.vapi.gbp_recirc_dump() @@ -156,23 +198,21 @@ class VppGbpSubnet(VppObject): """ GBP Subnet """ - - def __init__(self, test, table_id, address, address_len, - is_internal=True, - sw_if_index=None, epg=None): + def __init__(self, test, rd, address, address_len, + type, sw_if_index=None, epg=None): self._test = test - self.table_id = table_id + self.rd_id = rd.rd_id self.prefix = VppIpPrefix(address, address_len) - self.is_internal = is_internal + self.type = type self.sw_if_index = sw_if_index self.epg = epg def add_vpp_config(self): self._test.vapi.gbp_subnet_add_del( 1, - self.table_id, - self.is_internal, + self.rd_id, self.prefix.encode(), + self.type, sw_if_index=self.sw_if_index if self.sw_if_index else 0xffffffff, epg_id=self.epg if self.epg else 0xffff) self._test.registry.register(self, self._test.logger) @@ -180,21 +220,21 @@ class VppGbpSubnet(VppObject): def remove_vpp_config(self): self._test.vapi.gbp_subnet_add_del( 0, - self.table_id, - self.is_internal, - self.prefix.encode()) + self.rd_id, + self.prefix.encode(), + self.type) def __str__(self): return self.object_id() def object_id(self): - return "gbp-subnet;[%d-%s]" % (self.table_id, - self.prefix) + 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.table_id == self.table_id and \ + if s.subnet.rd_id == self.rd_id and \ + s.subnet.type == self.type and \ s.subnet.prefix == self.prefix: return True return False @@ -217,29 +257,22 @@ class VppGbpEndpointGroup(VppObject): self.rd = rd def add_vpp_config(self): - self._test.vapi.gbp_endpoint_group_add_del( - 1, + self._test.vapi.gbp_endpoint_group_add( self.epg, - self.bd, - self.rd, - self.rd, - self.uplink.sw_if_index) + self.bd.bd.bd_id, + self.rd.rd_id, + self.uplink.sw_if_index if self.uplink else INDEX_INVALID) self._test.registry.register(self, self._test.logger) def remove_vpp_config(self): - self._test.vapi.gbp_endpoint_group_add_del( - 0, - self.epg, - self.bd, - self.rd, - self.rd, - self.uplink.sw_if_index) + self._test.vapi.gbp_endpoint_group_del( + self.epg) def __str__(self): return self.object_id() def object_id(self): - return "gbp-endpoint-group;[%d]" % (self.epg) + return "gbp-endpoint-group:[%d]" % (self.epg) def query_vpp_config(self): epgs = self._test.vapi.gbp_endpoint_group_dump() @@ -249,6 +282,80 @@ class VppGbpEndpointGroup(VppObject): return False +class VppGbpBridgeDomain(VppObject): + """ + GBP Bridge Domain + """ + + def __init__(self, test, bd, bvi, uu_flood=None): + self._test = test + self.bvi = bvi + self.uu_flood = uu_flood + self.bd = bd + + def add_vpp_config(self): + self._test.vapi.gbp_bridge_domain_add( + self.bd.bd_id, + self.bvi.sw_if_index, + self.uu_flood.sw_if_index if self.uu_flood else INDEX_INVALID) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_bridge_domain_del(self.bd.bd_id) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "gbp-bridge-domain:[%d]" % (self.bd.bd_id) + + def query_vpp_config(self): + bds = self._test.vapi.gbp_bridge_domain_dump() + for bd in bds: + if bd.bd.bd_id == self.bd.bd_id: + return True + return False + + +class VppGbpRouteDomain(VppObject): + """ + GBP Route Domain + """ + + def __init__(self, test, rd_id, t4, t6, ip4_uu=None, ip6_uu=None): + self._test = test + self.rd_id = rd_id + self.t4 = t4 + self.t6 = t6 + self.ip4_uu = ip4_uu + self.ip6_uu = ip6_uu + + def add_vpp_config(self): + self._test.vapi.gbp_route_domain_add( + self.rd_id, + self.t4.table_id, + self.t6.table_id, + self.ip4_uu.sw_if_index if self.ip4_uu else INDEX_INVALID, + self.ip6_uu.sw_if_index if self.ip6_uu else INDEX_INVALID) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_route_domain_del(self.rd_id) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "gbp-route-domain:[%d]" % (self.rd_id) + + def query_vpp_config(self): + rds = self._test.vapi.gbp_route_domain_dump() + for rd in rds: + if rd.rd.rd_id == self.rd_id: + return True + return False + + class VppGbpContract(VppObject): """ GBP Contract @@ -279,7 +386,7 @@ class VppGbpContract(VppObject): return self.object_id() def object_id(self): - return "gbp-contract;[%d:%s:%d]" % (self.src_epg, + return "gbp-contract:[%d:%s:%d]" % (self.src_epg, self.dst_epg, self.acl_index) @@ -292,6 +399,39 @@ class VppGbpContract(VppObject): return False +class VppGbpVxlanTunnel(VppInterface): + """ + GBP VXLAN tunnel + """ + + def __init__(self, test, vni, bd_rd_id, mode): + super(VppGbpVxlanTunnel, self).__init__(test) + self._test = test + self.vni = vni + self.bd_rd_id = bd_rd_id + self.mode = mode + + def add_vpp_config(self): + r = self._test.vapi.gbp_vxlan_tunnel_add( + self.vni, + self.bd_rd_id, + self.mode) + self.set_sw_if_index(r.sw_if_index) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_vxlan_tunnel_del(self.vni) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "gbp-vxlan:%d" % (self.vni) + + def query_vpp_config(self): + return find_gbp_vxlan(self._test, self.vni) + + class VppGbpAcl(VppObject): """ GBP Acl @@ -337,7 +477,7 @@ class VppGbpAcl(VppObject): return self.object_id() def object_id(self): - return "gbp-acl;[%d]" % (self.acl_index) + return "gbp-acl:[%d]" % (self.acl_index) def query_vpp_config(self): cs = self._test.vapi.acl_dump() @@ -354,7 +494,7 @@ class TestGBP(VppTestCase): super(TestGBP, self).setUp() self.create_pg_interfaces(range(9)) - self.create_loopback_interfaces(9) + self.create_loopback_interfaces(8) self.router_mac = VppMacAddress("00:11:22:33:44:55") @@ -362,9 +502,6 @@ class TestGBP(VppTestCase): i.admin_up() for i in self.lo_interfaces: i.admin_up() - self.vapi.sw_interface_set_mac_address( - i.sw_if_index, - self.router_mac.bytes) def tearDown(self): for i in self.pg_interfaces: @@ -465,42 +602,64 @@ class TestGBP(VppTestCase): 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() - # # Bridge Domains # - 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) + bd1 = VppBridgeDomain(self, 1) + bd2 = VppBridgeDomain(self, 2) + bd20 = VppBridgeDomain(self, 20) + + bd1.add_vpp_config() + bd2.add_vpp_config() + bd20.add_vpp_config() + + gbd1 = VppGbpBridgeDomain(self, bd1, self.loop0) + gbd2 = VppGbpBridgeDomain(self, bd2, self.loop1) + gbd20 = VppGbpBridgeDomain(self, bd20, self.loop2) + + gbd1.add_vpp_config() + gbd2.add_vpp_config() + gbd20.add_vpp_config() + + # + # Route Domains + # + gt4 = VppIpTable(self, 0) + gt4.add_vpp_config() + gt6 = VppIpTable(self, 0, is_ip6=True) + gt6.add_vpp_config() + nt4 = VppIpTable(self, 20) + nt4.add_vpp_config() + nt6 = VppIpTable(self, 20, is_ip6=True) + nt6.add_vpp_config() + + rd0 = VppGbpRouteDomain(self, 0, gt4, gt6, None, None) + rd20 = VppGbpRouteDomain(self, 20, nt4, nt6, None, None) + + rd0.add_vpp_config() + rd20.add_vpp_config() # # 3 EPGs, 2 of which share a BD. # 2 NAT EPGs, one for floating-IP subnets, the other for internet # - epgs = [VppGbpEndpointGroup(self, 220, 0, 1, self.pg4, + epgs = [VppGbpEndpointGroup(self, 220, rd0, gbd1, self.pg4, self.loop0, "10.0.0.128", "2001:10::128"), - VppGbpEndpointGroup(self, 221, 0, 1, self.pg5, + VppGbpEndpointGroup(self, 221, rd0, gbd1, self.pg5, self.loop0, "10.0.1.128", "2001:10:1::128"), - VppGbpEndpointGroup(self, 222, 0, 2, self.pg6, + VppGbpEndpointGroup(self, 222, rd0, gbd2, self.pg6, self.loop1, "10.0.2.128", "2001:10:2::128"), - VppGbpEndpointGroup(self, 333, 20, 20, self.pg7, + VppGbpEndpointGroup(self, 333, rd20, gbd20, self.pg7, self.loop2, "11.0.0.128", "3001::128"), - VppGbpEndpointGroup(self, 444, 20, 20, self.pg8, + VppGbpEndpointGroup(self, 444, rd20, gbd20, self.pg8, self.loop2, "11.0.0.129", "3001::129")] @@ -513,7 +672,7 @@ class TestGBP(VppTestCase): VppGbpRecirc(self, epgs[3], self.loop6, is_ext=True), VppGbpRecirc(self, epgs[4], - self.loop8, is_ext=True)] + self.loop7, is_ext=True)] epg_nat = epgs[3] recirc_nat = recircs[3] @@ -544,8 +703,11 @@ class TestGBP(VppTestCase): 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) + VppIpInterfaceBind(self, epg.bvi, epg.rd.t4).add_vpp_config() + VppIpInterfaceBind(self, epg.bvi, epg.rd.t6).add_vpp_config() + self.vapi.sw_interface_set_mac_address( + epg.bvi.sw_if_index, + self.router_mac.bytes) # The BVIs are NAT inside interfaces self.vapi.nat44_interface_add_del_feature(epg.bvi.sw_if_index, @@ -555,60 +717,37 @@ class TestGBP(VppTestCase): is_inside=1, is_add=1) - self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index, - epg.bvi_ip4.bytes, - 32) - self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index, - epg.bvi_ip6.bytes, - 128, - is_ipv6=True) + if_ip4 = VppIpInterfaceAddress(self, epg.bvi, epg.bvi_ip4, 32) + if_ip6 = VppIpInterfaceAddress(self, epg.bvi, epg.bvi_ip6, 128) + if_ip4.add_vpp_config() + if_ip6.add_vpp_config() - # EPG uplink interfaces in the 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) + # 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 - self.vapi.bd_ip_mac_add_del(bd_id=epg.bd, - mac=self.router_mac.encode(), - ip=epg.bvi_ip4.encode(), - is_ipv6=0, - is_add=1) - self.vapi.bd_ip_mac_add_del(bd_id=epg.bd, - mac=self.router_mac.encode(), - ip=epg.bvi_ip6.encode(), - 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.address, - epg.bd, - epg.bvi.sw_if_index, - is_add=1, bvi_mac=1) + epg.bd_arp_ip4 = VppBridgeDomainArpEntry(self, epg.bd.bd, + self.router_mac.address, + epg.bvi_ip4) + epg.bd_arp_ip6 = VppBridgeDomainArpEntry(self, epg.bd.bd, + self.router_mac.address, + epg.bvi_ip6) + epg.bd_arp_ip4.add_vpp_config() + epg.bd_arp_ip6.add_vpp_config() # EPG in VPP epg.add_vpp_config() for recirc in recircs: # EPG's ingress recirculation interface maps to its RD - recirc.recirc.set_table_ip4(recirc.epg.rd) - recirc.recirc.set_table_ip6(recirc.epg.rd) + VppIpInterfaceBind(self, recirc.recirc, + recirc.epg.rd.t4).add_vpp_config() + VppIpInterfaceBind(self, recirc.recirc, + recirc.epg.rd.t6).add_vpp_config() - # 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, @@ -620,8 +759,11 @@ class TestGBP(VppTestCase): recirc.add_vpp_config() - ep_routes = [] - ep_arps = [] + 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() @@ -631,32 +773,6 @@ class TestGBP(VppTestCase): # 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.vmac.encode(), - ip=ip.encode(), - 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, @@ -668,16 +784,6 @@ class TestGBP(VppTestCase): 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() @@ -699,11 +805,8 @@ class TestGBP(VppTestCase): # 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.vmac.encode(), - ip=fip.encode(), - is_ipv6=fip.is_ip6, - is_add=1) + ba = VppBridgeDomainArpEntry(self, epg_nat.bd.bd, ep.mac, fip) + ba.add_vpp_config() # floating IPs route via EPG recirc r = VppIpRoute(self, fip.address, fip.length, @@ -714,27 +817,31 @@ class TestGBP(VppTestCase): 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) + 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 flooded + # 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=epgs[0].bvi_ip4.address, - psrc="10.0.0.88")) + pdst="10.0.0.88", + psrc="10.0.0.99")) - self.send_and_expect(self.pg0, [pkt_arp], self.pg0) + 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 @@ -751,7 +858,8 @@ class TestGBP(VppTestCase): nsma = in6_getnsma(inet_pton(AF_INET6, eps[0].ip6.address)) d = inet_ntop(AF_INET6, nsma) - pkt_nd = (Ether(dst=in6_getnsmac(nsma)) / + pkt_nd = (Ether(dst=in6_getnsmac(nsma), + src=self.pg0.remote_mac) / IPv6(dst=d, src=eps[0].ip6.address) / ICMPv6ND_NS(tgt=epgs[0].bvi_ip6.address) / ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac)) @@ -806,28 +914,40 @@ class TestGBP(VppTestCase): # # 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 = 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 = 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, + self.send_and_expect_bridged(eps[0].itf, pkt_intra_epg_220_ip4 * 65, - self.pg4) - self.send_and_expect_bridged(self.pg3, + eps[0].epg.uplink) + self.send_and_expect_bridged(eps[0].itf, pkt_inter_epg_222_ip4 * 65, - self.pg6) - self.send_and_expect_bridged6(self.pg3, + eps[0].epg.uplink) + self.send_and_expect_bridged6(eps[0].itf, pkt_inter_epg_222_ip6 * 65, - self.pg6) + 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")) @@ -838,6 +958,7 @@ class TestGBP(VppTestCase): 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 ... @@ -849,9 +970,9 @@ class TestGBP(VppTestCase): UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) - self.send_and_expect_bridged(self.pg0, + self.send_and_expect_bridged(eps[0].itf, pkt_intra_epg_220_to_uplink * 65, - self.pg4) + 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") @@ -863,9 +984,9 @@ class TestGBP(VppTestCase): UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) - self.send_and_expect_bridged(self.pg2, + self.send_and_expect_bridged(eps[2].itf, pkt_intra_epg_221_to_uplink * 65, - self.pg5) + eps[2].epg.uplink) # # Packets from the uplink are forwarded in the absence of a contract @@ -917,9 +1038,9 @@ class TestGBP(VppTestCase): UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) - self.send_and_assert_no_replies(self.pg0, + self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_221 * 65) - self.send_and_assert_no_replies(self.pg0, + self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_222 * 65) # @@ -932,10 +1053,10 @@ class TestGBP(VppTestCase): c1 = VppGbpContract(self, 220, 221, acl_index) c1.add_vpp_config() - self.send_and_expect_bridged(self.pg0, + self.send_and_expect_bridged(eps[0].itf, pkt_inter_epg_220_to_221 * 65, - self.pg2) - self.send_and_assert_no_replies(self.pg0, + eps[2].itf) + self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_222 * 65) # @@ -944,18 +1065,18 @@ class TestGBP(VppTestCase): c2 = VppGbpContract(self, 221, 220, acl_index) c2.add_vpp_config() - self.send_and_expect_bridged(self.pg0, + self.send_and_expect_bridged(eps[0].itf, pkt_inter_epg_220_to_221 * 65, - self.pg2) - self.send_and_expect_bridged(self.pg2, + eps[2].itf) + self.send_and_expect_bridged(eps[2].itf, pkt_inter_epg_221_to_220 * 65, - self.pg0) + eps[0].itf) # # check that inter group is still disabled for the groups # not in the contract. # - self.send_and_assert_no_replies(self.pg0, + self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_222 * 65) # @@ -966,9 +1087,9 @@ class TestGBP(VppTestCase): self.logger.info(self.vapi.cli("sh gbp contract")) - self.send_and_expect_routed(self.pg0, + self.send_and_expect_routed(eps[0].itf, pkt_inter_epg_220_to_222 * 65, - self.pg3, + eps[3].itf, self.router_mac.address) # @@ -979,45 +1100,53 @@ class TestGBP(VppTestCase): c3.remove_vpp_config() acl.remove_vpp_config() - self.send_and_assert_no_replies(self.pg2, + self.send_and_assert_no_replies(eps[2].itf, pkt_inter_epg_221_to_220 * 65) - self.send_and_assert_no_replies(self.pg0, + self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_221 * 65) - self.send_and_expect_bridged(self.pg0, pkt_intra_epg * 65, self.pg1) + self.send_and_expect_bridged(eps[0].itf, + pkt_intra_epg * 65, + eps[1].itf) # # EPs to the outside world # # in the EP's RD an external subnet via the NAT EPG's recirc - se1 = VppGbpSubnet(self, 0, "0.0.0.0", 0, - is_internal=False, - sw_if_index=recirc_nat.recirc.sw_if_index, - epg=epg_nat.epg) + se1 = VppGbpSubnet( + self, rd0, "0.0.0.0", 0, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL, + sw_if_index=recirc_nat.recirc.sw_if_index, + epg=epg_nat.epg) + se2 = VppGbpSubnet( + self, rd0, "11.0.0.0", 8, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL, + sw_if_index=recirc_nat.recirc.sw_if_index, + epg=epg_nat.epg) + se16 = VppGbpSubnet( + self, rd0, "::", 0, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL, + sw_if_index=recirc_nat.recirc.sw_if_index, + epg=epg_nat.epg) + # in the NAT RD an external subnet via the NAT EPG's uplink + se3 = VppGbpSubnet( + self, rd20, "0.0.0.0", 0, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL, + sw_if_index=epg_nat.uplink.sw_if_index, + epg=epg_nat.epg) + se36 = VppGbpSubnet( + self, rd20, "::", 0, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL, + sw_if_index=epg_nat.uplink.sw_if_index, + epg=epg_nat.epg) + se4 = VppGbpSubnet( + self, rd20, "11.0.0.0", 8, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL, + sw_if_index=epg_nat.uplink.sw_if_index, + epg=epg_nat.epg) se1.add_vpp_config() - se2 = 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() @@ -1039,7 +1168,7 @@ class TestGBP(VppTestCase): Raw('\xa5' * 100)) # no policy yet - self.send_and_assert_no_replies(self.pg0, + self.send_and_assert_no_replies(eps[0].itf, pkt_inter_epg_220_to_global * 65) acl2 = VppGbpAcl(self) @@ -1053,7 +1182,7 @@ class TestGBP(VppTestCase): c4 = VppGbpContract(self, 220, 333, acl_index2) c4.add_vpp_config() - self.send_and_expect_natted(self.pg0, + self.send_and_expect_natted(eps[0].itf, pkt_inter_epg_220_to_global * 65, self.pg7, eps[0].fip4.address) @@ -1150,24 +1279,7 @@ class TestGBP(VppTestCase): 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.bytes, - 32, - is_add=0) - self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index, - epg.bvi_ip6.bytes, - 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) @@ -1176,9 +1288,8 @@ class TestGBP(VppTestCase): is_add=0) for recirc in recircs: - recirc.recirc.set_table_ip4(0) - recirc.recirc.set_table_ip6(0) - + self.vapi.sw_interface_set_l2_emulation( + recirc.recirc.sw_if_index, enable=0) self.vapi.nat44_interface_add_del_feature( recirc.recirc.sw_if_index, is_inside=0, @@ -1188,6 +1299,818 @@ class TestGBP(VppTestCase): is_inside=0, is_add=0) + def test_gbp_learn_l2(self): + """ GBP L2 Endpoint Learning """ + + learnt = [{'mac': '00:00:11:11:11:01', + 'ip': '10.0.0.1', + 'ip6': '2001:10::2'}, + {'mac': '00:00:11:11:11:02', + 'ip': '10.0.0.2', + 'ip6': '2001:10::3'}] + + # + # lower the inactive threshold so these tests pass in a + # reasonable amount of time + # + self.vapi.gbp_endpoint_learn_set_inactive_threshold(1) + + # + # IP tables + # + gt4 = VppIpTable(self, 1) + gt4.add_vpp_config() + gt6 = VppIpTable(self, 1, is_ip6=True) + gt6.add_vpp_config() + + rd1 = VppGbpRouteDomain(self, 1, gt4, gt6) + rd1.add_vpp_config() + + # + # Pg2 hosts the vxlan tunnel, hosts on pg2 to act as TEPs + # Pg3 hosts the IP4 UU-flood VXLAN tunnel + # Pg4 hosts the IP6 UU-flood VXLAN tunnel + # + self.pg2.config_ip4() + self.pg2.resolve_arp() + self.pg2.generate_remote_hosts(4) + self.pg2.configure_ipv4_neighbors() + self.pg3.config_ip4() + self.pg3.resolve_arp() + self.pg4.config_ip4() + self.pg4.resolve_arp() + + # + # a GBP bridge domain with a BVI and a UU-flood interface + # + bd1 = VppBridgeDomain(self, 1) + bd1.add_vpp_config() + gbd1 = VppGbpBridgeDomain(self, bd1, self.loop0, self.pg3) + gbd1.add_vpp_config() + + self.logger.info(self.vapi.cli("sh bridge 1 detail")) + self.logger.info(self.vapi.cli("sh gbp bridge")) + + # ... and has a /32 applied + ip_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 32) + ip_addr.add_vpp_config() + + # + # The Endpoint-group in which we are learning endpoints + # + epg_220 = VppGbpEndpointGroup(self, 220, rd1, gbd1, + None, self.loop0, + "10.0.0.128", + "2001:10::128") + epg_220.add_vpp_config() + epg_330 = VppGbpEndpointGroup(self, 330, rd1, gbd1, + None, self.loop1, + "10.0.1.128", + "2001:11::128") + epg_330.add_vpp_config() + + # + # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint + # leanring enabled + # + vx_tun_l2_1 = VppGbpVxlanTunnel( + self, 99, bd1.bd_id, + VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L2) + vx_tun_l2_1.add_vpp_config() + + # + # A static endpoint that the learnt endpoints are trying to + # talk to + # + ep = VppGbpEndpoint(self, self.pg0, + epg_220, None, + "10.0.0.127", "11.0.0.127", + "2001:10::1", "3001::1") + ep.add_vpp_config() + + self.assertTrue(find_route(self, ep.ip4.address, 32, table_id=1)) + + # a packet with an sclass from an unknwon EPG + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[0].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=88, flags=0x88) / + Ether(src=learnt[0]["mac"], dst=ep.mac) / + IP(src=learnt[0]["ip"], dst=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_assert_no_replies(self.pg2, p) + + # + # we should not have learnt a new tunnel endpoint, since + # the EPG was not learnt. + # + self.assertEqual(INDEX_INVALID, + find_vxlan_gbp_tunnel(self, + self.pg2.local_ip4, + self.pg2.remote_hosts[0].ip4, + 99)) + + # epg is not learnt, becasue the EPG is unknwon + self.assertEqual(len(self.vapi.gbp_endpoint_dump()), 1) + + for ii, l in enumerate(learnt): + # a packet with an sclass from a knwon EPG + # arriving on an unknown TEP + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=220, flags=0x88) / + Ether(src=l['mac'], dst=ep.mac) / + IP(src=l['ip'], dst=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, [p], self.pg0) + + # the new TEP + tep1_sw_if_index = find_vxlan_gbp_tunnel( + self, + self.pg2.local_ip4, + self.pg2.remote_hosts[1].ip4, + 99) + self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index) + + # + # the EP is learnt via the learnt TEP + # both from its MAC and its IP + # + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + mac=l['mac'])) + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + ip=l['ip'])) + + self.logger.info(self.vapi.cli("show gbp endpoint")) + self.logger.info(self.vapi.cli("show gbp vxlan")) + self.logger.info(self.vapi.cli("show vxlan-gbp tunnel")) + + # + # If we sleep for the threshold time, the learnt endpoints should + # age out + # + self.sleep(2) + for l in learnt: + self.assertFalse(find_gbp_endpoint(self, + tep1_sw_if_index, + mac=l['mac'])) + + self.logger.info(self.vapi.cli("show gbp endpoint")) + self.logger.info(self.vapi.cli("show gbp vxlan")) + self.logger.info(self.vapi.cli("show vxlan-gbp tunnel")) + + # + # repeat. the do not learn bit is set so the EPs are not learnt + # + for l in learnt: + # a packet with an sclass from a knwon EPG + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=220, flags=0x88, gpflags="D") / + Ether(src=l['mac'], dst=ep.mac) / + IP(src=l['ip'], dst=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, p*65, self.pg0) + + for l in learnt: + self.assertFalse(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + mac=l['mac'])) + + # + # repeat + # + for l in learnt: + # a packet with an sclass from a knwon EPG + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=220, flags=0x88) / + Ether(src=l['mac'], dst=ep.mac) / + IP(src=l['ip'], dst=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, p*65, self.pg0) + + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + mac=l['mac'])) + + # + # Static EP replies to dynamics + # + self.logger.info(self.vapi.cli("sh l2fib bd_id 1")) + for l in learnt: + p = (Ether(src=ep.mac, dst=l['mac']) / + IP(dst=l['ip'], src=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p * 17, self.pg2) + + for rx in rxs: + self.assertEqual(rx[IP].src, self.pg2.local_ip4) + self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4) + self.assertEqual(rx[UDP].dport, 48879) + # the UDP source port is a random value for hashing + self.assertEqual(rx[VXLAN].gpid, 220) + self.assertEqual(rx[VXLAN].vni, 99) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + self.sleep(2) + for l in learnt: + self.assertFalse(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + mac=l['mac'])) + + # + # repeat in the other EPG + # there's no contract between 220 and 330, but the A-bit is set + # so the packet is cleared for delivery + # + for l in learnt: + # a packet with an sclass from a knwon EPG + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=330, flags=0x88, gpflags='A') / + Ether(src=l['mac'], dst=ep.mac) / + IP(src=l['ip'], dst=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, p*65, self.pg0) + + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + mac=l['mac'])) + + # + # static EP cannot reach the learnt EPs since there is no contract + # + self.logger.info(self.vapi.cli("show gbp endpoint")) + self.logger.info(self.vapi.cli("show l2fib all")) + for l in learnt: + p = (Ether(src=ep.mac, dst=l['mac']) / + IP(dst=l['ip'], src=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_assert_no_replies(self.pg0, [p], timeout=0.2) + + # + # refresh the entries after the check for no replies above + # + for l in learnt: + # a packet with an sclass from a knwon EPG + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=330, flags=0x88, gpflags='A') / + Ether(src=l['mac'], dst=ep.mac) / + IP(src=l['ip'], dst=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, p*65, self.pg0) + + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + mac=l['mac'])) + + # + # Add the contract so they can talk + # + acl = VppGbpAcl(self) + rule = acl.create_rule(permit_deny=1, proto=17) + rule2 = acl.create_rule(is_ipv6=1, permit_deny=1, proto=17) + acl_index = acl.add_vpp_config([rule, rule2]) + c1 = VppGbpContract(self, 220, 330, acl_index) + c1.add_vpp_config() + + for l in learnt: + p = (Ether(src=ep.mac, dst=l['mac']) / + IP(dst=l['ip'], src=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_expect(self.pg0, [p], self.pg2) + + # + # send UU packets from the local EP + # + self.logger.info(self.vapi.cli("sh bridge 1 detail")) + self.logger.info(self.vapi.cli("sh gbp bridge")) + p_uu = (Ether(src=ep.mac, dst="00:11:11:11:11:11") / + IP(dst="10.0.0.133", src=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + rxs = self.send_and_expect(ep.itf, [p_uu], gbd1.uu_flood) + + # + # Add a mcast destination VXLAN-GBP tunnel for B&M traffic + # + tun_bm = VppVxlanGbpTunnel(self, self.pg4.local_ip4, + "239.1.1.1", 88, + mcast_itf=self.pg4) + tun_bm.add_vpp_config() + bp_bm = VppBridgeDomainPort(self, bd1, tun_bm, + port_type=L2_PORT_TYPE.NORMAL) + bp_bm.add_vpp_config() + + self.logger.info(self.vapi.cli("sh bridge 1 detail")) + + p_bm = (Ether(src=ep.mac, dst="ff:ff:ff:ff:ff:ff") / + IP(dst="10.0.0.133", src=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + rxs = self.send_and_expect_only(ep.itf, [p_bm], tun_bm.mcast_itf) + + # + # Check v6 Endpoints + # + for l in learnt: + # a packet with an sclass from a knwon EPG + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=330, flags=0x88, gpflags='A') / + Ether(src=l['mac'], dst=ep.mac) / + IPv6(src=l['ip6'], dst=ep.ip6.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, p*65, self.pg0) + + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + mac=l['mac'])) + + # + # L3 Endpoint Learning + # - configured on the bridge's BVI + # + + # + # clean up + # + self.sleep(2) + for l in learnt: + self.assertFalse(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + mac=l['mac'])) + + self.pg2.unconfig_ip4() + self.pg3.unconfig_ip4() + self.pg4.unconfig_ip4() + + self.logger.info(self.vapi.cli("sh int")) + self.logger.info(self.vapi.cli("sh gbp vxlan")) + + def test_gbp_learn_l3(self): + """ GBP L3 Endpoint Learning """ + + routed_dst_mac = "00:0c:0c:0c:0c:0c" + routed_src_mac = "00:22:bd:f8:19:ff" + + learnt = [{'mac': '00:00:11:11:11:02', + 'ip': '10.0.1.2', + 'ip6': '2001:10::2'}, + {'mac': '00:00:11:11:11:03', + 'ip': '10.0.1.3', + 'ip6': '2001:10::3'}] + + # + # lower the inactive threshold so these tests pass in a + # reasonable amount of time + # + self.vapi.gbp_endpoint_learn_set_inactive_threshold(1) + + # + # IP tables + # + t4 = VppIpTable(self, 1) + t4.add_vpp_config() + t6 = VppIpTable(self, 1, True) + t6.add_vpp_config() + + tun_ip4_uu = VppVxlanGbpTunnel(self, self.pg4.local_ip4, + self.pg4.remote_ip4, 114) + tun_ip6_uu = VppVxlanGbpTunnel(self, self.pg4.local_ip4, + self.pg4.remote_ip4, 116) + tun_ip4_uu.add_vpp_config() + tun_ip6_uu.add_vpp_config() + + rd1 = VppGbpRouteDomain(self, 2, t4, t6, tun_ip4_uu, tun_ip6_uu) + rd1.add_vpp_config() + + self.loop0.set_mac(self.router_mac.address) + + # + # Bind the BVI to the RD + # + VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config() + VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config() + + # + # Pg2 hosts the vxlan tunnel + # hosts on pg2 to act as TEPs + # pg3 is BD uu-fwd + # pg4 is RD uu-fwd + # + self.pg2.config_ip4() + self.pg2.resolve_arp() + self.pg2.generate_remote_hosts(4) + self.pg2.configure_ipv4_neighbors() + self.pg3.config_ip4() + self.pg3.resolve_arp() + self.pg4.config_ip4() + self.pg4.resolve_arp() + + # + # a GBP bridge domain with a BVI and a UU-flood interface + # + bd1 = VppBridgeDomain(self, 1) + bd1.add_vpp_config() + gbd1 = VppGbpBridgeDomain(self, bd1, self.loop0, self.pg3) + gbd1.add_vpp_config() + + self.logger.info(self.vapi.cli("sh bridge 1 detail")) + self.logger.info(self.vapi.cli("sh gbp bridge")) + self.logger.info(self.vapi.cli("sh gbp route")) + self.logger.info(self.vapi.cli("show l2fib all")) + + # ... and has a /32 and /128 applied + ip4_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 32) + ip4_addr.add_vpp_config() + ip6_addr = VppIpInterfaceAddress(self, gbd1.bvi, "2001:10::128", 128) + ip6_addr.add_vpp_config() + + # + # The Endpoint-group in which we are learning endpoints + # + epg_220 = VppGbpEndpointGroup(self, 220, rd1, gbd1, + None, self.loop0, + "10.0.0.128", + "2001:10::128") + epg_220.add_vpp_config() + + # + # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint + # leanring enabled + # + vx_tun_l3 = VppGbpVxlanTunnel( + self, 101, rd1.rd_id, + VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3) + vx_tun_l3.add_vpp_config() + + # + # A static endpoint that the learnt endpoints are trying to + # talk to + # + ep = VppGbpEndpoint(self, self.pg0, + epg_220, None, + "10.0.0.127", "11.0.0.127", + "2001:10::1", "3001::1") + ep.add_vpp_config() + + # + # learn some remote IPv4 EPs + # + for ii, l in enumerate(learnt): + # a packet with an sclass from a knwon EPG + # arriving on an unknown TEP + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=101, gpid=220, flags=0x88) / + Ether(src=l['mac'], dst="00:00:00:11:11:11") / + IP(src=l['ip'], dst=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, [p], self.pg0) + + # the new TEP + tep1_sw_if_index = find_vxlan_gbp_tunnel( + self, + self.pg2.local_ip4, + self.pg2.remote_hosts[1].ip4, + vx_tun_l3.vni) + self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index) + + # endpoint learnt via the parent GBP-vxlan interface + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l3._sw_if_index, + ip=l['ip'])) + + # + # Static IPv4 EP replies to learnt + # + for l in learnt: + p = (Ether(src=ep.mac, dst=self.loop0.local_mac) / + IP(dst=l['ip'], src=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p*1, self.pg2) + + for rx in rxs: + self.assertEqual(rx[IP].src, self.pg2.local_ip4) + self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4) + self.assertEqual(rx[UDP].dport, 48879) + # the UDP source port is a random value for hashing + self.assertEqual(rx[VXLAN].gpid, 220) + self.assertEqual(rx[VXLAN].vni, 101) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + inner = rx[VXLAN].payload + + self.assertEqual(inner[Ether].src, routed_src_mac) + self.assertEqual(inner[Ether].dst, routed_dst_mac) + self.assertEqual(inner[IP].src, ep.ip4.address) + self.assertEqual(inner[IP].dst, l['ip']) + + self.sleep(2) + for l in learnt: + self.assertFalse(find_gbp_endpoint(self, + tep1_sw_if_index, + ip=l['ip'])) + + # + # learn some remote IPv6 EPs + # + for ii, l in enumerate(learnt): + # a packet with an sclass from a knwon EPG + # arriving on an unknown TEP + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=101, gpid=220, flags=0x88) / + Ether(src=l['mac'], dst="00:00:00:11:11:11") / + IPv6(src=l['ip6'], dst=ep.ip6.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, [p], self.pg0) + + # the new TEP + tep1_sw_if_index = find_vxlan_gbp_tunnel( + self, + self.pg2.local_ip4, + self.pg2.remote_hosts[1].ip4, + vx_tun_l3.vni) + self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index) + + self.logger.info(self.vapi.cli("show gbp bridge")) + self.logger.info(self.vapi.cli("show vxlan-gbp tunnel")) + self.logger.info(self.vapi.cli("show gbp vxlan")) + self.logger.info(self.vapi.cli("show int addr")) + + # endpoint learnt via the TEP + self.assertTrue(find_gbp_endpoint(self, ip=l['ip6'])) + + self.logger.info(self.vapi.cli("show gbp endpoint")) + self.logger.info(self.vapi.cli("show ip fib index 1 %s" % l['ip'])) + + # + # Static EP replies to learnt + # + for l in learnt: + p = (Ether(src=ep.mac, dst=self.loop0.local_mac) / + IPv6(dst=l['ip6'], src=ep.ip6.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p*65, self.pg2) + + for rx in rxs: + self.assertEqual(rx[IP].src, self.pg2.local_ip4) + self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4) + self.assertEqual(rx[UDP].dport, 48879) + # the UDP source port is a random value for hashing + self.assertEqual(rx[VXLAN].gpid, 220) + self.assertEqual(rx[VXLAN].vni, 101) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + inner = rx[VXLAN].payload + + self.assertEqual(inner[Ether].src, routed_src_mac) + self.assertEqual(inner[Ether].dst, routed_dst_mac) + self.assertEqual(inner[IPv6].src, ep.ip6.address) + self.assertEqual(inner[IPv6].dst, l['ip6']) + + self.logger.info(self.vapi.cli("sh gbp endpoint")) + self.sleep(2) + for l in learnt: + self.assertFalse(find_gbp_endpoint(self, + tep1_sw_if_index, + ip=l['ip'])) + + # + # Static sends to unknown EP with no route + # + p = (Ether(src=ep.mac, dst=self.loop0.local_mac) / + IP(dst="10.0.0.99", src=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_assert_no_replies(self.pg0, [p]) + + # + # Add a route to static EP's v4 and v6 subnet + # packets should be send on the v4/v6 uu=fwd interface resp. + # + se_10_24 = VppGbpSubnet( + self, rd1, "10.0.0.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT) + se_10_24.add_vpp_config() + + p = (Ether(src=ep.mac, dst=self.loop0.local_mac) / + IP(dst="10.0.0.99", src=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, [p], self.pg4) + for rx in rxs: + self.assertEqual(rx[IP].src, self.pg4.local_ip4) + self.assertEqual(rx[IP].dst, self.pg4.remote_ip4) + self.assertEqual(rx[UDP].dport, 48879) + # the UDP source port is a random value for hashing + self.assertEqual(rx[VXLAN].gpid, 220) + self.assertEqual(rx[VXLAN].vni, 114) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + # policy is not applied to packets sent to the uu-fwd interfaces + self.assertFalse(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + # + # learn some remote IPv4 EPs + # + for ii, l in enumerate(learnt): + # a packet with an sclass from a knwon EPG + # arriving on an unknown TEP + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=101, gpid=220, flags=0x88) / + Ether(src=l['mac'], dst="00:00:00:11:11:11") / + IP(src=l['ip'], dst=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, [p], self.pg0) + + # the new TEP + tep1_sw_if_index = find_vxlan_gbp_tunnel( + self, + self.pg2.local_ip4, + self.pg2.remote_hosts[1].ip4, + vx_tun_l3.vni) + self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index) + + # endpoint learnt via the parent GBP-vxlan interface + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l3._sw_if_index, + ip=l['ip'])) + + # + # Add a remote endpoint from the API + # + rep_88 = VppGbpEndpoint(self, vx_tun_l3, + epg_220, None, + "10.0.0.88", "11.0.0.88", + "2001:10::88", "3001::88", + VppEnum.vl_api_gbp_endpoint_flags_t.REMOTE, + self.pg2.local_ip4, + self.pg2.remote_hosts[1].ip4, + mac=None) + rep_88.add_vpp_config() + + # + # Add a remote endpoint from the API that matches an existing one + # + rep_2 = VppGbpEndpoint(self, vx_tun_l3, + epg_220, None, + learnt[0]['ip'], "11.0.0.101", + learnt[0]['ip6'], "3001::101", + VppEnum.vl_api_gbp_endpoint_flags_t.REMOTE, + self.pg2.local_ip4, + self.pg2.remote_hosts[1].ip4, + mac=None) + rep_2.add_vpp_config() + + # + # Add a route to the leanred EP's v4 subnet + # packets should be send on the v4/v6 uu=fwd interface resp. + # + se_10_1_24 = VppGbpSubnet( + self, rd1, "10.0.1.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT) + se_10_1_24.add_vpp_config() + + self.logger.info(self.vapi.cli("show gbp endpoint")) + + ips = ["10.0.0.88", learnt[0]['ip']] + for ip in ips: + p = (Ether(src=ep.mac, dst=self.loop0.local_mac) / + IP(dst=ip, src=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p*65, self.pg2) + + for rx in rxs: + self.assertEqual(rx[IP].src, self.pg2.local_ip4) + self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4) + self.assertEqual(rx[UDP].dport, 48879) + # the UDP source port is a random value for hashing + self.assertEqual(rx[VXLAN].gpid, 220) + self.assertEqual(rx[VXLAN].vni, 101) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + inner = rx[VXLAN].payload + + self.assertEqual(inner[Ether].src, routed_src_mac) + self.assertEqual(inner[Ether].dst, routed_dst_mac) + self.assertEqual(inner[IP].src, ep.ip4.address) + self.assertEqual(inner[IP].dst, ip) + + # + # remove the API remote EPs, they are now UU-fwd + # + rep_88.remove_vpp_config() + rep_2.remove_vpp_config() + + self.logger.info(self.vapi.cli("show gbp endpoint")) + + for ip in ips: + self.assertFalse(find_gbp_endpoint(self, ip=ip)) + + p = (Ether(src=ep.mac, dst=self.loop0.local_mac) / + IP(dst=ip, src=ep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, [p], self.pg4) + + # + # shutdown with learnt endpoint present + # + self.logger.info(self.vapi.cli("show gbp endpoint-group")) + + # + # TODO + # remote endpoint becomes local + # + self.pg2.unconfig_ip4() + self.pg3.unconfig_ip4() + self.pg4.unconfig_ip4() + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/test_l2_flood.py b/test/test_l2_flood.py index 5a2694cbb63..9f3ef533091 100644 --- a/test/test_l2_flood.py +++ b/test/test_l2_flood.py @@ -5,7 +5,7 @@ import socket from framework import VppTestCase, VppTestRunner from vpp_ip_route import VppIpRoute, VppRoutePath -from vpp_papi_provider import L2_PORT_TYPE, BRIDGE_FLAGS +from vpp_l2 import L2_PORT_TYPE, BRIDGE_FLAGS from scapy.packet import Raw from scapy.layers.l2 import Ether diff --git a/test/vpp_interface.py b/test/vpp_interface.py index c7918fcfbd2..1fe6652cfc7 100644 --- a/test/vpp_interface.py +++ b/test/vpp_interface.py @@ -454,3 +454,6 @@ class VppInterface(object): "admin state") self.test.assert_equal(if_state.link_up_down, link_up_down, "link state") + + def __str__(self): + return self.name diff --git a/test/vpp_ip.py b/test/vpp_ip.py index e92c91943db..6d22c16ff60 100644 --- a/test/vpp_ip.py +++ b/test/vpp_ip.py @@ -120,6 +120,10 @@ class VppIpAddress(): return self.addr.bytes @property + def bytes(self): + return self.addr.bytes + + @property def address(self): return self.addr.address @@ -169,9 +173,17 @@ class VppIpPrefix(): return self.addr.address @property + def bytes(self): + return self.addr.bytes + + @property def length(self): return self.len + @property + def is_ip6(self): + return self.addr.is_ip6 + def __str__(self): return "%s/%d" % (self.address, self.length) diff --git a/test/vpp_ip_route.py b/test/vpp_ip_route.py index 00a79f44232..45609e80786 100644 --- a/test/vpp_ip_route.py +++ b/test/vpp_ip_route.py @@ -35,6 +35,13 @@ class MplsLspMode: UNIFORM = 1 +def ip_to_dpo_proto(addr): + if addr.version is 6: + return DpoProto.DPO_PROTO_IP6 + else: + return DpoProto.DPO_PROTO_IP4 + + def find_route(test, ip_addr, len, table_id=0, inet=AF_INET): if inet == AF_INET: s = 4 @@ -71,6 +78,23 @@ def find_mroute(test, grp_addr, src_addr, grp_addr_len, return False +def fib_interface_ip_prefix(test, address, length, sw_if_index): + vp = VppIpPrefix(address, length) + addrs = test.vapi.ip_address_dump(sw_if_index, is_ipv6=vp.is_ip6) + + if vp.is_ip6: + n = 16 + else: + n = 4 + + for a in addrs: + if a.prefix_length == length and \ + a.sw_if_index == sw_if_index and \ + a.ip[:n] == vp.bytes: + return True + return False + + class VppIpTable(VppObject): def __init__(self, @@ -95,6 +119,9 @@ class VppIpTable(VppObject): is_add=0) def query_vpp_config(self): + if self.table_id == 0: + # the default table always exists + return False # find the default route return find_route(self._test, "::" if self.is_ip6 else "0.0.0.0", @@ -111,6 +138,79 @@ class VppIpTable(VppObject): self.table_id)) +class VppIpInterfaceAddress(VppObject): + + def __init__(self, test, intf, addr, len): + self._test = test + self.intf = intf + self.prefix = VppIpPrefix(addr, len) + + def add_vpp_config(self): + self._test.vapi.sw_interface_add_del_address( + self.intf.sw_if_index, + self.prefix.bytes, + self.prefix.length, + is_add=1, + is_ipv6=self.prefix.is_ip6) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.sw_interface_add_del_address( + self.intf.sw_if_index, + self.prefix.bytes, + self.prefix.length, + is_add=0, + is_ipv6=self.prefix.is_ip6) + + def query_vpp_config(self): + return fib_interface_ip_prefix(self._test, + self.prefix.address, + self.prefix.length, + self.intf.sw_if_index) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "interface-ip-%s-%s" % (self.intf, self.prefix) + + +class VppIpInterfaceBind(VppObject): + + def __init__(self, test, intf, table): + self._test = test + self.intf = intf + self.table = table + + def add_vpp_config(self): + if self.table.is_ip6: + self.intf.set_table_ip6(self.table.table_id) + else: + self.intf.set_table_ip4(self.table.table_id) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + if 0 == self.table.table_id: + return + if self.table.is_ip6: + self.intf.set_table_ip6(0) + else: + self.intf.set_table_ip4(0) + + def query_vpp_config(self): + if 0 == self.table.table_id: + return False + return self._test.vapi.sw_interface_get_table( + self.intf.sw_if_index, + self.table.is_ip6).vrf_id == self.table.table_id + + def __str__(self): + return self.object_id() + + def object_id(self): + return "interface-bind-%s-%s" % (self.intf, self.table) + + class VppMplsLabel(object): def __init__(self, value, mode=MplsLspMode.PIPE, ttl=64, exp=0): self.value = value diff --git a/test/vpp_l2.py b/test/vpp_l2.py new file mode 100644 index 00000000000..a6b43efe14c --- /dev/null +++ b/test/vpp_l2.py @@ -0,0 +1,221 @@ +""" + L2/BD Types + +""" + +from vpp_object import * +from util import mactobinary +from vpp_ip import VppIpAddress +from vpp_mac import VppMacAddress +from vpp_lo_interface import VppLoInterface + + +class L2_PORT_TYPE: + NORMAL = 0 + BVI = 1 + UU_FWD = 2 + + +class BRIDGE_FLAGS: + NONE = 0 + LEARN = 1 + FWD = 2 + FLOOD = 4 + UU_FLOOD = 8 + ARP_TERM = 16 + + +def find_bridge_domain(test, bd_id): + bds = test.vapi.bridge_domain_dump(bd_id) + return len(bds) == 1 + + +def find_bridge_domain_port(test, bd_id, sw_if_index): + bds = test.vapi.bridge_domain_dump(bd_id) + for bd in bds: + for p in bd.sw_if_details: + if p.sw_if_index == sw_if_index: + return True + return False + + +def find_bridge_domain_arp_entry(test, bd_id, mac, ip): + vmac = VppMacAddress(mac) + vip = VppIpAddress(ip) + + if vip.version == 4: + n = 4 + else: + n = 16 + + arps = test.vapi.bd_ip_mac_dump(bd_id) + for arp in arps: + # do IP addr comparison too once .api is fixed... + if vmac.bytes == arp.mac_address and \ + vip.bytes == arp.ip_address[:n]: + return True + return False + + +def find_l2_fib_entry(test, bd_id, mac, sw_if_index): + vmac = VppMacAddress(mac) + lfs = test.vapi.l2_fib_table_dump(bd_id) + for lf in lfs: + if vmac.bytes == lf.mac and sw_if_index == lf.sw_if_index: + return True + return False + + +class VppBridgeDomain(VppObject): + + def __init__(self, test, bd_id, + flood=1, uu_flood=1, forward=1, + learn=1, arp_term=1): + self._test = test + self.bd_id = bd_id + self.flood = flood + self.uu_flood = uu_flood + self.forward = forward + self.learn = learn + self.arp_term = arp_term + + def add_vpp_config(self): + self._test.vapi.bridge_domain_add_del( + self.bd_id, + is_add=1, + flood=self.flood, + uu_flood=self.uu_flood, + forward=self.forward, + learn=self.learn, + arp_term=self.arp_term) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.bridge_domain_add_del(self.bd_id, is_add=0) + + def query_vpp_config(self): + return find_bridge_domain(self._test, self.bd_id) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "bridge-domain-%d" % (self.bd_id) + + +class VppBridgeDomainPort(VppObject): + + def __init__(self, test, bd, itf, + port_type=L2_PORT_TYPE.NORMAL): + self._test = test + self.bd = bd + self.itf = itf + self.port_type = port_type + + def add_vpp_config(self): + self._test.vapi.sw_interface_set_l2_bridge( + self.itf.sw_if_index, + self.bd.bd_id, + port_type=self.port_type, + enable=1) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.sw_interface_set_l2_bridge( + self.itf.sw_if_index, + self.bd.bd_id, + port_type=self.port_type, + enable=0) + + def query_vpp_config(self): + return find_bridge_domain_port(self._test, + self.bd.bd_id, + self.itf.sw_if_index) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "BD-Port-%s-%s" % (self.bd, self.itf) + + +class VppBridgeDomainArpEntry(VppObject): + + def __init__(self, test, bd, mac, ip): + self._test = test + self.bd = bd + self.mac = VppMacAddress(mac) + self.ip = VppIpAddress(ip) + + def add_vpp_config(self): + self._test.vapi.bd_ip_mac_add_del( + self.bd.bd_id, + self.mac.encode(), + self.ip.encode(), + is_add=1) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.bd_ip_mac_add_del( + self.bd.bd_id, + self.mac.encode(), + self.ip.encode(), + is_add=0) + + def query_vpp_config(self): + return find_bridge_domain_arp_entry(self._test, + self.bd.bd_id, + self.mac.address, + self.ip.address) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "BD-Arp-Entry-%s-%s-%s" % (self.bd, self.mac, self.ip.address) + + +class VppL2FibEntry(VppObject): + + def __init__(self, test, bd, mac, itf, + static_mac=0, filter_mac=0, bvi_mac=-1): + self._test = test + self.bd = bd + self.mac = VppMacAddress(mac) + self.itf = itf + self.static_mac = static_mac + self.filter_mac = filter_mac + if bvi_mac == -1: + self.bvi_mac = isinstance(self.itf, VppLoInterface) + else: + self.bvi_mac = bvi_mac + + def add_vpp_config(self): + self._test.vapi.l2fib_add_del( + self.mac.address, + self.bd.bd_id, + self.itf.sw_if_index, + is_add=1, + static_mac=self.static_mac, + filter_mac=self.filter_mac, + bvi_mac=self.bvi_mac) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.l2fib_add_del( + self.mac.address, + self.bd.bd_id, + self.itf.sw_if_index, + is_add=0) + + def query_vpp_config(self): + return find_l2_fib_entry(self._test, + self.bd.bd_id, + self.mac.address, + self.itf.sw_if_index) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "L2-Fib-Entry-%s-%s-%s" % (self.bd, self.mac, self.itf) diff --git a/test/vpp_mac.py b/test/vpp_mac.py index c9ee11e6658..c0e69746175 100644 --- a/test/vpp_mac.py +++ b/test/vpp_mac.py @@ -22,3 +22,18 @@ class VppMacAddress(): @property def address(self): return self.addr.address + + def __str__(self): + return self.address + + def __eq__(self, other): + if isinstance(other, self.__class__): + return self.address == other.addres + elif hasattr(other, "bytes"): + # vl_api_mac_addres_t + return self.bytes == other.bytes + else: + raise Exception("Comparing VppMacAddress:%s" + "with unknown type: %s" % + (self, other)) + return False diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index c2192471437..1adcc1ba24b 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -18,6 +18,7 @@ except: if do_import: from vpp_papi import VPP + from vpp_l2 import L2_PORT_TYPE # from vnet/vnet/mpls/mpls_types.h MPLS_IETF_MAX_LABEL = 0xfffff @@ -43,21 +44,6 @@ class QOS_SOURCE: IP = 3 -class L2_PORT_TYPE: - NORMAL = 0 - BVI = 1 - UU_FWD = 2 - - -class BRIDGE_FLAGS: - NONE = 0 - LEARN = 1 - FWD = 2 - FLOOD = 4 - UU_FLOOD = 8 - ARP_TERM = 16 - - class UnexpectedApiReturnValueError(Exception): """ exception raised when the API return value is unexpected """ pass @@ -257,6 +243,17 @@ class VppPapiProvider(object): {'sw_if_index': sw_if_index, 'is_ipv6': is_ipv6, 'vrf_id': table_id}) + def sw_interface_get_table(self, sw_if_index, is_ipv6): + """ Get the IPvX Table-id for the Interface + + :param sw_if_index: + :param is_ipv6: + :return table_id + + """ + return self.api(self.papi.sw_interface_get_table, + {'sw_if_index': sw_if_index, 'is_ipv6': is_ipv6}) + def sw_interface_add_del_address(self, sw_if_index, addr, addr_len, is_ipv6=0, is_add=1, del_all=0): """ @@ -277,6 +274,11 @@ class VppPapiProvider(object): 'address_length': addr_len, 'address': addr}) + def ip_address_dump(self, sw_if_index, is_ipv6=0): + return self.api(self.papi.ip_address_dump, + {'sw_if_index': sw_if_index, + 'is_ipv6': is_ipv6}) + def sw_interface_set_unnumbered(self, sw_if_index, ip_sw_if_index, is_add=1): """ Set the Interface to be unnumbered @@ -477,10 +479,13 @@ class VppPapiProvider(object): return self.api(self.papi.bd_ip_mac_add_del, {'bd_id': bd_id, 'is_add': is_add, - 'is_ipv6': is_ipv6, 'ip': ip, 'mac': mac}) + def bd_ip_mac_dump(self, bd_id): + return self.api(self.papi.bd_ip_mac_dump, + {'bd_id': bd_id}) + def want_ip4_arp_events(self, enable_disable=1, address=0): return self.api(self.papi.want_ip4_arp_events, {'enable_disable': enable_disable, @@ -647,6 +652,11 @@ class VppPapiProvider(object): """ return self.api(self.papi.l2fib_flush_all, {}) + def l2_fib_table_dump(self, bd_id): + """ Dump the L2 FIB """ + return self.api(self.papi.l2_fib_table_dump, + {'bd_id': bd_id}) + def sw_interface_set_l2_bridge(self, sw_if_index, bd_id, shg=0, port_type=L2_PORT_TYPE.NORMAL, enable=1): @@ -2771,7 +2781,6 @@ class VppPapiProvider(object): is_add=1, is_ipv6=0, encap_table_id=0, - decap_next_index=0xFFFFFFFF, vni=0, instance=0xFFFFFFFF): """ @@ -2794,7 +2803,6 @@ class VppPapiProvider(object): 'dst': dst, 'mcast_sw_if_index': mcast_sw_if_index, 'encap_table_id': encap_table_id, - 'decap_next_index': decap_next_index, 'vni': vni, 'instance': instance}}) @@ -3482,15 +3490,22 @@ class VppPapiProvider(object): 'enable_ip6': 1 if enable_ip6 else 0, }) - def gbp_endpoint_add(self, sw_if_index, ips, mac, epg): + def gbp_endpoint_add(self, sw_if_index, ips, mac, epg, flags, + tun_src, tun_dst): """ GBP endpoint Add """ return self.api(self.papi.gbp_endpoint_add, {'endpoint': { 'sw_if_index': sw_if_index, + 'flags': 0, 'ips': ips, 'n_ips': len(ips), 'mac': mac, - 'epg_id': epg}}) + 'epg_id': epg, + 'flags': flags, + 'tun': { + 'src': tun_src, + 'dst': tun_dst, + }}}) def gbp_endpoint_del(self, handle): """ GBP endpoint Del """ @@ -3501,24 +3516,73 @@ class VppPapiProvider(object): """ GBP endpoint Dump """ return self.api(self.papi.gbp_endpoint_dump, {}) - def gbp_endpoint_group_add_del(self, is_add, epg, bd, - ip4_rd, - ip6_rd, - uplink_sw_if_index): - """ GBP endpoint group Add/Del """ - return self.api(self.papi.gbp_endpoint_group_add_del, - {'is_add': is_add, - 'epg': { + def gbp_endpoint_group_add(self, epg, bd, + rd, uplink_sw_if_index): + """ GBP endpoint group Add """ + return self.api(self.papi.gbp_endpoint_group_add, + {'epg': + { 'uplink_sw_if_index': uplink_sw_if_index, 'bd_id': bd, - 'ip4_table_id': ip4_rd, - 'ip6_table_id': ip6_rd, - 'epg_id': epg}}) + 'rd_id': rd, + 'epg_id': epg + }}) + + def gbp_endpoint_group_del(self, epg): + """ GBP endpoint group Del """ + return self.api(self.papi.gbp_endpoint_group_del, + {'epg_id': epg}) def gbp_endpoint_group_dump(self): """ GBP endpoint group Dump """ return self.api(self.papi.gbp_endpoint_group_dump, {}) + def gbp_bridge_domain_add(self, bd_id, + bvi_sw_if_index, + uu_fwd_sw_if_index): + """ GBP bridge-domain Add """ + return self.api(self.papi.gbp_bridge_domain_add, + {'bd': + { + 'bvi_sw_if_index': bvi_sw_if_index, + 'uu_fwd_sw_if_index': uu_fwd_sw_if_index, + 'bd_id': bd_id + }}) + + def gbp_bridge_domain_del(self, bd_id): + """ GBP bridge-domain Del """ + return self.api(self.papi.gbp_bridge_domain_del, + {'bd_id': bd_id}) + + def gbp_bridge_domain_dump(self): + """ GBP Bridge Domain Dump """ + return self.api(self.papi.gbp_bridge_domain_dump, {}) + + def gbp_route_domain_add(self, rd_id, + ip4_table_id, + ip6_table_id, + ip4_uu_sw_if_index, + ip6_uu_sw_if_index): + """ GBP route-domain Add """ + return self.api(self.papi.gbp_route_domain_add, + {'rd': + { + 'ip4_table_id': ip4_table_id, + 'ip6_table_id': ip6_table_id, + 'ip4_uu_sw_if_index': ip4_uu_sw_if_index, + 'ip6_uu_sw_if_index': ip6_uu_sw_if_index, + 'rd_id': rd_id + }}) + + def gbp_route_domain_del(self, rd_id): + """ GBP route-domain Del """ + return self.api(self.papi.gbp_route_domain_del, + {'rd_id': rd_id}) + + def gbp_route_domain_dump(self): + """ GBP Route Domain Dump """ + return self.api(self.papi.gbp_route_domain_dump, {}) + def gbp_recirc_add_del(self, is_add, sw_if_index, epg, is_ext): """ GBP recirc Add/Del """ return self.api(self.papi.gbp_recirc_add_del, @@ -3532,22 +3596,19 @@ class VppPapiProvider(object): """ GBP recirc Dump """ return self.api(self.papi.gbp_recirc_dump, {}) - def gbp_subnet_add_del(self, is_add, table_id, - is_internal, - prefix, + def gbp_subnet_add_del(self, is_add, rd_id, + prefix, type, sw_if_index=0xffffffff, - epg_id=0xffff, - is_ip6=False): + epg_id=0xffff): """ GBP Subnet Add/Del """ return self.api(self.papi.gbp_subnet_add_del, {'is_add': is_add, 'subnet': { - 'is_internal': is_internal, - 'is_ip6': is_ip6, + 'type': type, 'sw_if_index': sw_if_index, 'epg_id': epg_id, 'prefix': prefix, - 'table_id': table_id}}) + 'rd_id': rd_id}}) def gbp_subnet_dump(self): """ GBP Subnet Dump """ @@ -3566,6 +3627,33 @@ class VppPapiProvider(object): """ GBP contract Dump """ return self.api(self.papi.gbp_contract_dump, {}) + def gbp_endpoint_learn_set_inactive_threshold(self, threshold): + """ GBP set inactive threshold """ + return self.api(self.papi.gbp_endpoint_learn_set_inactive_threshold, + {'threshold': threshold}) + + def gbp_vxlan_tunnel_add(self, vni, bd_rd_id, mode): + """ GBP VXLAN tunnel add """ + return self.api(self.papi.gbp_vxlan_tunnel_add, + { + 'tunnel': { + 'vni': vni, + 'mode': mode, + 'bd_rd_id': bd_rd_id + } + }) + + def gbp_vxlan_tunnel_del(self, vni): + """ GBP VXLAN tunnel del """ + return self.api(self.papi.gbp_vxlan_tunnel_del, + { + 'vni': vni, + }) + + def gbp_vxlan_tunnel_dump(self): + """ GBP VXLAN tunnel add/del """ + return self.api(self.papi.gbp_vxlan_tunnel_dump, {}) + def ipip_6rd_add_tunnel(self, ip6_table_id, ip6_prefix, ip6_prefix_len, ip4_table_id, ip4_prefix, ip4_prefix_len, ip4_src, security_check): diff --git a/test/vpp_vxlan_gbp_tunnel.py b/test/vpp_vxlan_gbp_tunnel.py new file mode 100644 index 00000000000..805d4c5f3e2 --- /dev/null +++ b/test/vpp_vxlan_gbp_tunnel.py @@ -0,0 +1,69 @@ + +from vpp_interface import VppInterface +from vpp_ip import VppIpAddress + + +INDEX_INVALID = 0xffffffff + + +def find_vxlan_gbp_tunnel(test, src, dst, vni): + vsrc = VppIpAddress(src) + vdst = VppIpAddress(dst) + + ts = test.vapi.vxlan_gbp_tunnel_dump(INDEX_INVALID) + for t in ts: + if vsrc == t.tunnel.src and \ + vdst == t.tunnel.dst and \ + t.tunnel.vni == vni: + return t.tunnel.sw_if_index + return INDEX_INVALID + + +class VppVxlanGbpTunnel(VppInterface): + """ + VPP VXLAN GBP interface + """ + + def __init__(self, test, src, dst, vni, mcast_itf=None): + """ Create VXLAN-GBP Tunnel interface """ + super(VppVxlanGbpTunnel, self).__init__(test) + self.src = VppIpAddress(src) + self.dst = VppIpAddress(dst) + self.vni = vni + self.mcast_itf = mcast_itf + + def add_vpp_config(self): + mcast_sw_if_index = INDEX_INVALID + if (self.mcast_itf): + mcast_sw_if_index = self.mcast_itf.sw_if_index + reply = self.test.vapi.vxlan_gbp_tunnel_add_del( + self.src.encode(), + self.dst.encode(), + vni=self.vni, + mcast_sw_if_index=mcast_sw_if_index) + self.set_sw_if_index(reply.sw_if_index) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + mcast_sw_if_index = INDEX_INVALID + if (self.mcast_itf): + mcast_sw_if_index = self.mcast_itf.sw_if_index + self.test.vapi.vxlan_gbp_tunnel_add_del( + self.src.encode(), + self.dst.encode(), + vni=self.vni, + is_add=0, + mcast_sw_if_index=mcast_sw_if_index) + + def query_vpp_config(self): + return (INDEX_INVALID != find_vxlan_gbp_tunnel(self._test, + self.src.address, + self.dst.address, + self.vni)) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "vxlan-gbp-%d-%d-%s-%s" % (self.sw_if_index, self.vni, + self.src, self.dst) |