def thru_host_stack_setup(self): self.vapi.session_enable_disable(is_enabled=1) self.create_loopback_interfaces(2) table_id = 1 for i in self.lo_interfaces: i.admin_up() if table_id != 0: tbl = VppIpTable(self, table_id) tbl.add_vpp_config() i.set_table_ip4(table_id) i.config_ip4() table_id += 1 # Configure namespaces self.vapi.app_namespace_add_del(namespace_id=b"1", secret=1234, sw_if_index=self.loop0.sw_if_index) self.vapi.app_namespace_add_del(namespace_id=b"2", secret=5678, sw_if_index=self.loop1.sw_if_index) # Add inter-table routes ip_t01 = VppIpRoute(self, self.loop1.local_ip4, 32, [VppRoutePath("0.0.0.0", 0xffffffff, nh_table_id=2)], table_id=1) ip_t10 = VppIpRoute(self, self.loop0.local_ip4, 32, [VppRoutePath("0.0.0.0", 0xffffffff, nh_table_id=1)], table_id=2) ip_t01.add_vpp_config() ip_t10.add_vpp_config() self.logger.debug(self.vapi.cli("show ip fib"))
def thru_host_stack_setup(self): self.vapi.session_enable_disable(is_enabled=1) self.create_loopback_interfaces(2) table_id = 1 for i in self.lo_interfaces: i.admin_up() if table_id != 0: tbl = VppIpTable(self, table_id) tbl.add_vpp_config() i.set_table_ip4(table_id) i.config_ip4() table_id += 1 # Configure namespaces self.vapi.app_namespace_add_del(namespace_id=b"1", secret=1234, sw_if_index=self.loop0.sw_if_index) self.vapi.app_namespace_add_del(namespace_id=b"2", secret=5678, sw_if_index=self.loop1.sw_if_index) # Add inter-table routes ip_t01 = VppIpRoute(self, self.loop1.local_ip4, 32, [VppRoutePath("0.0.0.0", 0xffffffff, nh_table_id=2)], table_id=1) ip_t10 = VppIpRoute(self, self.loop0.local_ip4, 32, [VppRoutePath("0.0.0.0", 0xffffffff, nh_table_id=1)], table_id=2) ip_t01.add_vpp_config() ip_t10.add_vpp_config() self.logger.debug(self.vapi.cli("show ip fib"))
def setUp(self): super(TestSession, self).setUp() self.vapi.session_enable_disable(is_enable=1) self.create_loopback_interfaces(2) table_id = 0 for i in self.lo_interfaces: i.admin_up() if table_id != 0: tbl = VppIpTable(self, table_id) tbl.add_vpp_config() i.set_table_ip4(table_id) i.config_ip4() table_id += 1 # Configure namespaces self.vapi.app_namespace_add_del( namespace_id="0", sw_if_index=self.loop0.sw_if_index ) self.vapi.app_namespace_add_del( namespace_id="1", sw_if_index=self.loop1.sw_if_index )
def setUp(self): super(TestDHCP, self).setUp() # create 3 pg interfaces self.create_pg_interfaces(range(4)) self.tables = [] # pg0 and 1 are IP configured in VRF 0 and 1. # pg2 and 3 are non IP-configured in VRF 0 and 1 table_id = 0 for table_id in range(1, 4): tbl4 = VppIpTable(self, table_id) tbl4.add_vpp_config() self.tables.append(tbl4) tbl6 = VppIpTable(self, table_id, is_ip6=1) tbl6.add_vpp_config() self.tables.append(tbl6) table_id = 0 for i in self.pg_interfaces[:2]: i.admin_up() i.set_table_ip4(table_id) i.set_table_ip6(table_id) i.config_ip4() i.resolve_arp() i.config_ip6() i.resolve_ndp() table_id += 1 table_id = 0 for i in self.pg_interfaces[2:]: i.admin_up() i.set_table_ip4(table_id) i.set_table_ip6(table_id) table_id += 1
def setUp(self): super(TestUdpEncap, self).setUp() # create 2 pg interfaces self.create_pg_interfaces(range(4)) # setup interfaces # assign them different tables. table_id = 0 self.tables = [] for i in self.pg_interfaces: i.admin_up() if table_id != 0: tbl = VppIpTable(self, table_id) tbl.add_vpp_config() self.tables.append(tbl) tbl = VppIpTable(self, table_id, is_ip6=1) tbl.add_vpp_config() self.tables.append(tbl) i.set_table_ip4(table_id) i.set_table_ip6(table_id) i.config_ip4() i.resolve_arp() i.config_ip6() i.resolve_ndp() table_id += 1
def setUp(self): super(TestUdpEncap, self).setUp() # create 2 pg interfaces self.create_pg_interfaces(range(4)) # setup interfaces # assign them different tables. table_id = 0 self.tables = [] for i in self.pg_interfaces: i.admin_up() if table_id != 0: tbl = VppIpTable(self, table_id) tbl.add_vpp_config() self.tables.append(tbl) tbl = VppIpTable(self, table_id, is_ip6=1) tbl.add_vpp_config() self.tables.append(tbl) i.set_table_ip4(table_id) i.set_table_ip6(table_id) i.config_ip4() i.resolve_arp() i.config_ip6() i.resolve_ndp() table_id += 1
def test_ipip_vrf_create(self): """ ipip create / delete interface VRF test """ t = VppIpTable(self, 20) t.add_vpp_config() rv = ipip_add_tunnel(self, '1.2.3.4', '2.3.4.5', table_id=20) sw_if_index = rv.sw_if_index self.vapi.ipip_del_tunnel(sw_if_index)
def test_ipip_vrf_create(self): """ ipip create / delete interface VRF test """ t = VppIpTable(self, 20) t.add_vpp_config() rv = self.vapi.ipip_add_tunnel( src_address=inet_pton(AF_INET, '1.2.3.4'), dst_address=inet_pton(AF_INET, '2.3.4.5'), is_ipv6=0, table_id=20) sw_if_index = rv.sw_if_index self.vapi.ipip_del_tunnel(sw_if_index)
def test_ipip_vrf_create(self): """ ipip create / delete interface VRF test """ t = VppIpTable(self, 20) t.add_vpp_config() rv = self.vapi.ipip_add_tunnel( src_address=inet_pton(AF_INET, '1.2.3.4'), dst_address=inet_pton(AF_INET, '2.3.4.5'), is_ipv6=0, table_id=20) sw_if_index = rv.sw_if_index self.vapi.ipip_del_tunnel(sw_if_index)
def thru_host_stack_ipv6_setup(self): self.vapi.session_enable_disable(is_enabled=1) self.create_loopback_interfaces(2) table_id = 1 for i in self.lo_interfaces: i.admin_up() tbl = VppIpTable(self, table_id, is_ip6=1) tbl.add_vpp_config() i.set_table_ip6(table_id) i.config_ip6() table_id += 1 # Configure namespaces self.vapi.app_namespace_add(namespace_id="1", secret=1234, sw_if_index=self.loop0.sw_if_index) self.vapi.app_namespace_add(namespace_id="2", secret=5678, sw_if_index=self.loop1.sw_if_index) # Add inter-table routes ip_t01 = VppIpRoute(self, self.loop1.local_ip6, 128, [ VppRoutePath("::0", 0xffffffff, nh_table_id=2, proto=DpoProto.DPO_PROTO_IP6) ], table_id=1, is_ip6=1) ip_t10 = VppIpRoute(self, self.loop0.local_ip6, 128, [ VppRoutePath("::0", 0xffffffff, nh_table_id=1, proto=DpoProto.DPO_PROTO_IP6) ], table_id=2, is_ip6=1) ip_t01.add_vpp_config() ip_t10.add_vpp_config() self.logger.debug(self.vapi.cli("show interface addr")) self.logger.debug(self.vapi.cli("show ip6 fib"))
def setUp(self): super(QUICTestCase, self).setUp() var = "VPP_BUILD_DIR" self.build_dir = os.getenv(var, None) if self.build_dir is None: raise Exception("Environment variable `%s' not set" % var) self.vppDebug = 'vpp_debug' in self.build_dir self.timeout = 20 self.vapi.session_enable_disable(is_enabled=1) self.pre_test_sleep = 0.3 self.post_test_sleep = 0.2 self.create_loopback_interfaces(2) self.uri = "quic://%s/1234" % self.loop0.local_ip4 table_id = 1 for i in self.lo_interfaces: i.admin_up() if table_id != 0: tbl = VppIpTable(self, table_id) tbl.add_vpp_config() i.set_table_ip4(table_id) i.config_ip4() table_id += 1 # Configure namespaces self.vapi.app_namespace_add_del(namespace_id=b"server", sw_if_index=self.loop0.sw_if_index) self.vapi.app_namespace_add_del(namespace_id=b"client", sw_if_index=self.loop1.sw_if_index) # Add inter-table routes self.ip_t01 = VppIpRoute( self, self.loop1.local_ip4, 32, [VppRoutePath("0.0.0.0", 0xffffffff, nh_table_id=2)], table_id=1) self.ip_t10 = VppIpRoute( self, self.loop0.local_ip4, 32, [VppRoutePath("0.0.0.0", 0xffffffff, nh_table_id=1)], table_id=2) self.ip_t01.add_vpp_config() self.ip_t10.add_vpp_config() self.logger.debug(self.vapi.cli("show ip fib"))
def setUp(self): super(Test6RD, self).setUp() t4 = VppIpTable(self, 10) t6 = VppIpTable(self, 20, True) t4.add_vpp_config() t6.add_vpp_config() for n in range(4): i = self.pg_interfaces[n] i.admin_up() if (n > 1): i.set_table_ip4(10) i.set_table_ip6(20) i.config_ip4() i.config_ip6() i.disable_ipv6_ra() i.resolve_arp() i.resolve_ndp()
def setUp(self): super(Test6RD, self).setUp() t4 = VppIpTable(self, 10) t6 = VppIpTable(self, 20, True) t4.add_vpp_config() t6.add_vpp_config() for n in range(4): i = self.pg_interfaces[n] i.admin_up() if n > 1: i.set_table_ip4(10) i.set_table_ip6(20) i.config_ip4() i.config_ip6() i.disable_ipv6_ra() i.resolve_arp() i.resolve_ndp()
def thru_host_stack_ipv4_setup(self): super(QUICTestCase, self).setUp() self.create_loopback_interfaces(2) self.uri = "quic://%s/1234" % self.loop0.local_ip4 common_args = ["uri", self.uri, "fifo-size", "64"] self.server_echo_test_args = common_args + ["appns", "server"] self.client_echo_test_args = common_args + [ "appns", "client", "test-bytes" ] table_id = 1 for i in self.lo_interfaces: i.admin_up() if table_id != 0: tbl = VppIpTable(self, table_id) tbl.add_vpp_config() i.set_table_ip4(table_id) i.config_ip4() table_id += 1 # Configure namespaces self.vapi.app_namespace_add_del(namespace_id=b"server", sw_if_index=self.loop0.sw_if_index) self.vapi.app_namespace_add_del(namespace_id=b"client", sw_if_index=self.loop1.sw_if_index) # Add inter-table routes self.ip_t01 = VppIpRoute( self, self.loop1.local_ip4, 32, [VppRoutePath("0.0.0.0", 0xffffffff, nh_table_id=2)], table_id=1) self.ip_t10 = VppIpRoute( self, self.loop0.local_ip4, 32, [VppRoutePath("0.0.0.0", 0xffffffff, nh_table_id=1)], table_id=2) self.ip_t01.add_vpp_config() self.ip_t10.add_vpp_config() self.logger.debug(self.vapi.cli("show ip fib"))
def setUp(self): super(QUICTestCase, self).setUp() self.vppDebug = "vpp_debug" in config.vpp_build_dir self.create_loopback_interfaces(2) self.uri = "quic://%s/1234" % self.loop0.local_ip4 table_id = 1 for i in self.lo_interfaces: i.admin_up() if table_id != 0: tbl = VppIpTable(self, table_id) tbl.add_vpp_config() i.set_table_ip4(table_id) i.config_ip4() table_id += 1 # Configure namespaces self.vapi.app_namespace_add_del(namespace_id="server", sw_if_index=self.loop0.sw_if_index) self.vapi.app_namespace_add_del(namespace_id="client", sw_if_index=self.loop1.sw_if_index) # Add inter-table routes self.ip_t01 = VppIpRoute( self, self.loop1.local_ip4, 32, [VppRoutePath("0.0.0.0", 0xFFFFFFFF, nh_table_id=2)], table_id=1, ) self.ip_t10 = VppIpRoute( self, self.loop0.local_ip4, 32, [VppRoutePath("0.0.0.0", 0xFFFFFFFF, nh_table_id=1)], table_id=2, ) self.ip_t01.add_vpp_config() self.ip_t10.add_vpp_config() self.logger.debug(self.vapi.cli("show ip fib"))
def thru_host_stack_ipv4_setup(self): super(QUICTestCase, self).setUp() self.create_loopback_interfaces(2) self.uri = "quic://%s/1234" % self.loop0.local_ip4 common_args = ["uri", self.uri, "fifo-size", "64"] self.server_echo_test_args = common_args + ["appns", "server"] self.client_echo_test_args = common_args + ["appns", "client", "test-bytes"] table_id = 1 for i in self.lo_interfaces: i.admin_up() if table_id != 0: tbl = VppIpTable(self, table_id) tbl.add_vpp_config() i.set_table_ip4(table_id) i.config_ip4() table_id += 1 # Configure namespaces self.vapi.app_namespace_add_del(namespace_id=b"server", sw_if_index=self.loop0.sw_if_index) self.vapi.app_namespace_add_del(namespace_id=b"client", sw_if_index=self.loop1.sw_if_index) # Add inter-table routes self.ip_t01 = VppIpRoute(self, self.loop1.local_ip4, 32, [VppRoutePath("0.0.0.0", 0xffffffff, nh_table_id=2)], table_id=1) self.ip_t10 = VppIpRoute(self, self.loop0.local_ip4, 32, [VppRoutePath("0.0.0.0", 0xffffffff, nh_table_id=1)], table_id=2) self.ip_t01.add_vpp_config() self.ip_t10.add_vpp_config() self.logger.debug(self.vapi.cli("show ip fib"))
def setUp(self): super(TestQUIC, self).setUp() self.vapi.session_enable_disable(is_enabled=1) self.create_loopback_interfaces(2) table_id = 1 for i in self.lo_interfaces: i.admin_up() if table_id != 0: tbl = VppIpTable(self, table_id) tbl.add_vpp_config() i.set_table_ip4(table_id) i.config_ip4() table_id += 1 # Configure namespaces self.vapi.app_namespace_add_del(namespace_id=b"1", sw_if_index=self.loop0.sw_if_index) self.vapi.app_namespace_add_del(namespace_id=b"2", sw_if_index=self.loop1.sw_if_index)
def thru_host_stack_ipv6_setup(self): self.vapi.session_enable_disable(is_enabled=1) self.create_loopback_interfaces(2) table_id = 1 for i in self.lo_interfaces: i.admin_up() tbl = VppIpTable(self, table_id, is_ip6=1) tbl.add_vpp_config() i.set_table_ip6(table_id) i.config_ip6() table_id += 1 # Configure namespaces self.vapi.app_namespace_add_del(namespace_id=b"1", secret=1234, sw_if_index=self.loop0.sw_if_index) self.vapi.app_namespace_add_del(namespace_id=b"2", secret=5678, sw_if_index=self.loop1.sw_if_index) # Add inter-table routes ip_t01 = VppIpRoute(self, self.loop1.local_ip6, 128, [VppRoutePath("::0", 0xffffffff, nh_table_id=2, proto=DpoProto.DPO_PROTO_IP6)], table_id=1, is_ip6=1) ip_t10 = VppIpRoute(self, self.loop0.local_ip6, 128, [VppRoutePath("::0", 0xffffffff, nh_table_id=1, proto=DpoProto.DPO_PROTO_IP6)], table_id=2, is_ip6=1) ip_t01.add_vpp_config() ip_t10.add_vpp_config() self.logger.debug(self.vapi.cli("show interface addr")) self.logger.debug(self.vapi.cli("show ip6 fib"))
def setUp(self): super(TestIPMcast, self).setUp() # create 8 pg interfaces self.create_pg_interfaces(range(9)) # setup interfaces for i in self.pg_interfaces[:8]: i.admin_up() i.config_ip4() i.config_ip6() i.resolve_arp() i.resolve_ndp() # one more in a vrf tbl4 = VppIpTable(self, 10) tbl4.add_vpp_config() self.pg8.set_table_ip4(10) self.pg8.config_ip4() tbl6 = VppIpTable(self, 10, is_ip6=1) tbl6.add_vpp_config() self.pg8.set_table_ip6(10) self.pg8.config_ip6()
def setUp(self): super(TestSVS, self).setUp() # create 2 pg interfaces self.create_pg_interfaces(range(4)) table_id = 0 for i in self.pg_interfaces: i.admin_up() if table_id != 0: tbl = VppIpTable(self, table_id) tbl.add_vpp_config() tbl = VppIpTable(self, table_id, is_ip6=1) tbl.add_vpp_config() i.set_table_ip4(table_id) i.set_table_ip6(table_id) i.config_ip4() i.resolve_arp() i.config_ip6() i.resolve_ndp() table_id += 1
def setUp(self): super(TestSVS, self).setUp() # create 2 pg interfaces self.create_pg_interfaces(range(4)) table_id = 0 for i in self.pg_interfaces: i.admin_up() if table_id != 0: tbl = VppIpTable(self, table_id) tbl.add_vpp_config() tbl = VppIpTable(self, table_id, is_ip6=1) tbl.add_vpp_config() i.set_table_ip4(table_id) i.set_table_ip6(table_id) i.config_ip4() i.resolve_arp() i.config_ip6() i.resolve_ndp() table_id += 1
def setUp(self): super(TestIPMcast, self).setUp() # create 8 pg interfaces self.create_pg_interfaces(range(9)) # setup interfaces for i in self.pg_interfaces[:8]: i.admin_up() i.config_ip4() i.config_ip6() i.resolve_arp() i.resolve_ndp() # one more in a vrf tbl4 = VppIpTable(self, 10) tbl4.add_vpp_config() self.pg8.set_table_ip4(10) self.pg8.config_ip4() tbl6 = VppIpTable(self, 10, is_ip6=1) tbl6.add_vpp_config() self.pg8.set_table_ip6(10) self.pg8.config_ip6()
class TestIPIP(VppTestCase): """ IPIP Test Case """ @classmethod def setUpClass(cls): super(TestIPIP, cls).setUpClass() cls.create_pg_interfaces(range(3)) cls.interfaces = list(cls.pg_interfaces) @classmethod def tearDownClass(cls): super(TestIPIP, cls).tearDownClass() def setUp(self): super(TestIPIP, self).setUp() self.table = VppIpTable(self, 1, register=False) self.table.add_vpp_config() for i in self.interfaces: i.admin_up() self.pg2.set_table_ip4(self.table.table_id) for i in self.interfaces: i.config_ip4() i.config_ip6() i.disable_ipv6_ra() i.resolve_arp() i.resolve_ndp() def tearDown(self): super(TestIPIP, self).tearDown() if not self.vpp_dead: for i in self.pg_interfaces: i.unconfig_ip4() i.unconfig_ip6() i.set_table_ip4(0) i.admin_down() self.table.remove_vpp_config() def validate(self, rx, expected): self.assertEqual(rx, expected.__class__(expected)) def generate_ip4_frags(self, payload_length, fragment_size): p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) p_payload = UDP(sport=1234, dport=1234) / self.payload(payload_length) p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4) outer_ip4 = (p_ether / IP( src=self.pg1.remote_ip4, id=RandShort(), dst=self.pg0.local_ip4) / p_ip4 / p_payload) frags = fragment(outer_ip4, fragment_size) p4_reply = (p_ip4 / p_payload) p4_reply.ttl -= 1 return frags, p4_reply def verify_ip4ip4_encaps(self, a, p_ip4s, p_ip4_encaps): for i, p_ip4 in enumerate(p_ip4s): p_ip4.dst = a p4 = (self.p_ether / p_ip4 / self.p_payload) p_ip4_inner = p_ip4 p_ip4_inner.ttl -= 1 p4_reply = (p_ip4_encaps[i] / p_ip4_inner / self.p_payload) p4_reply.ttl -= 1 p4_reply.id = 0 rx = self.send_and_expect(self.pg0, p4 * N_PACKETS, self.pg1) for p in rx: self.validate(p[1], p4_reply) self.assert_packet_checksums_valid(p) def verify_ip6ip4_encaps(self, a, p_ip6s, p_ip4_encaps): for i, p_ip6 in enumerate(p_ip6s): p_ip6.dst = a p6 = (self.p_ether / p_ip6 / self.p_payload) p_inner_ip6 = p_ip6 p_inner_ip6.hlim -= 1 p6_reply = (p_ip4_encaps[i] / p_inner_ip6 / self.p_payload) p6_reply.ttl -= 1 rx = self.send_and_expect(self.pg0, p6 * N_PACKETS, self.pg1) for p in rx: self.validate(p[1], p6_reply) self.assert_packet_checksums_valid(p) def test_ipip4(self): """ ip{v4,v6} over ip4 test """ self.pg1.generate_remote_hosts(5) self.pg1.configure_ipv4_neighbors() e = VppEnum.vl_api_tunnel_encap_decap_flags_t d = VppEnum.vl_api_ip_dscp_t self.p_ether = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) self.p_payload = UDP(sport=1234, dport=1234) / Raw(b'X' * 100) # create a TOS byte by shifting a DSCP code point 2 bits. those 2 bits # are for the ECN. dscp = d.IP_API_DSCP_AF31 << 2 ecn = 3 dscp_ecn = d.IP_API_DSCP_AF31 << 2 | ecn # IPv4 transport that copies the DCSP from the payload tun_dscp = VppIpIpTunInterface( self, self.pg0, self.pg0.local_ip4, self.pg1.remote_hosts[0].ip4, flags=e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP) tun_dscp.add_vpp_config() # IPv4 transport that copies the DCSP and ECN from the payload tun_dscp_ecn = VppIpIpTunInterface( self, self.pg0, self.pg0.local_ip4, self.pg1.remote_hosts[1].ip4, flags=(e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP | e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN)) tun_dscp_ecn.add_vpp_config() # IPv4 transport that copies the ECN from the payload and sets the # DF bit on encap. copies the ECN on decap tun_ecn = VppIpIpTunInterface( self, self.pg0, self.pg0.local_ip4, self.pg1.remote_hosts[2].ip4, flags=(e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN | e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_SET_DF | e.TUNNEL_API_ENCAP_DECAP_FLAG_DECAP_COPY_ECN)) tun_ecn.add_vpp_config() # IPv4 transport that sets a fixed DSCP in the encap and copies # the DF bit tun = VppIpIpTunInterface( self, self.pg0, self.pg0.local_ip4, self.pg1.remote_hosts[3].ip4, dscp=d.IP_API_DSCP_AF11, flags=e.TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DF) tun.add_vpp_config() # array of all the tunnels tuns = [tun_dscp, tun_dscp_ecn, tun_ecn, tun] # addresses for prefixes routed via each tunnel a4s = ["" for i in range(len(tuns))] a6s = ["" for i in range(len(tuns))] # IP headers with each combination of DSCp/ECN tested p_ip6s = [ IPv6(src="1::1", dst="DEAD::1", nh='UDP', tc=dscp), IPv6(src="1::1", dst="DEAD::1", nh='UDP', tc=dscp_ecn), IPv6(src="1::1", dst="DEAD::1", nh='UDP', tc=ecn), IPv6(src="1::1", dst="DEAD::1", nh='UDP', tc=0xff) ] p_ip4s = [ IP(src="1.2.3.4", dst="130.67.0.1", tos=dscp, flags='DF'), IP(src="1.2.3.4", dst="130.67.0.1", tos=dscp_ecn), IP(src="1.2.3.4", dst="130.67.0.1", tos=ecn), IP(src="1.2.3.4", dst="130.67.0.1", tos=0xff) ] # Configure each tunnel for i, t in enumerate(tuns): # Set interface up and enable IP on it self.vapi.sw_interface_set_flags(t.sw_if_index, 1) self.vapi.sw_interface_set_unnumbered( sw_if_index=self.pg0.sw_if_index, unnumbered_sw_if_index=t.sw_if_index) # prefix for route / destination address for packets a4s[i] = "130.67.%d.0" % i a6s[i] = "dead:%d::" % i # Add IPv4 and IPv6 routes via tunnel interface ip4_via_tunnel = VppIpRoute(self, a4s[i], 24, [ VppRoutePath("0.0.0.0", t.sw_if_index, proto=FibPathProto.FIB_PATH_NH_PROTO_IP4) ]) ip4_via_tunnel.add_vpp_config() ip6_via_tunnel = VppIpRoute(self, a6s[i], 64, [ VppRoutePath("::", t.sw_if_index, proto=FibPathProto.FIB_PATH_NH_PROTO_IP6) ]) ip6_via_tunnel.add_vpp_config() # # Encapsulation # # tun_dscp copies only the dscp # expected TC values are thus only the DCSP value is present from the # inner exp_tcs = [dscp, dscp, 0, 0xfc] p_ip44_encaps = [ IP(src=self.pg0.local_ip4, dst=tun_dscp.dst, tos=tc) for tc in exp_tcs ] p_ip64_encaps = [ IP(src=self.pg0.local_ip4, dst=tun_dscp.dst, proto='ipv6', id=0, tos=tc) for tc in exp_tcs ] # IPv4 in to IPv4 tunnel self.verify_ip4ip4_encaps(a4s[0], p_ip4s, p_ip44_encaps) # IPv6 in to IPv4 tunnel self.verify_ip6ip4_encaps(a6s[0], p_ip6s, p_ip64_encaps) # tun_dscp_ecn copies the dscp and the ecn exp_tcs = [dscp, dscp_ecn, ecn, 0xff] p_ip44_encaps = [ IP(src=self.pg0.local_ip4, dst=tun_dscp_ecn.dst, tos=tc) for tc in exp_tcs ] p_ip64_encaps = [ IP(src=self.pg0.local_ip4, dst=tun_dscp_ecn.dst, proto='ipv6', id=0, tos=tc) for tc in exp_tcs ] self.verify_ip4ip4_encaps(a4s[1], p_ip4s, p_ip44_encaps) self.verify_ip6ip4_encaps(a6s[1], p_ip6s, p_ip64_encaps) # tun_ecn copies only the ecn and always sets DF exp_tcs = [0, ecn, ecn, ecn] p_ip44_encaps = [ IP(src=self.pg0.local_ip4, dst=tun_ecn.dst, flags='DF', tos=tc) for tc in exp_tcs ] p_ip64_encaps = [ IP(src=self.pg0.local_ip4, dst=tun_ecn.dst, flags='DF', proto='ipv6', id=0, tos=tc) for tc in exp_tcs ] self.verify_ip4ip4_encaps(a4s[2], p_ip4s, p_ip44_encaps) self.verify_ip6ip4_encaps(a6s[2], p_ip6s, p_ip64_encaps) # tun sets a fixed dscp and copies DF fixed_dscp = tun.dscp << 2 flags = ['DF', 0, 0, 0] p_ip44_encaps = [ IP(src=self.pg0.local_ip4, dst=tun.dst, flags=f, tos=fixed_dscp) for f in flags ] p_ip64_encaps = [ IP(src=self.pg0.local_ip4, dst=tun.dst, proto='ipv6', id=0, tos=fixed_dscp) for i in range(len(p_ip4s)) ] self.verify_ip4ip4_encaps(a4s[3], p_ip4s, p_ip44_encaps) self.verify_ip6ip4_encaps(a6s[3], p_ip6s, p_ip64_encaps) # # Decapsulation # n_packets_decapped = 0 self.p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) # IPv4 tunnel to IPv4 tcs = [0, dscp, dscp_ecn, ecn] # one overlay packet and all combinations of its encap p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4) p_ip4_encaps = [ IP(src=tun.dst, dst=self.pg0.local_ip4, tos=tc) for tc in tcs ] # for each encap tun will produce the same inner packet because it does # not copy up fields from the payload for p_ip4_encap in p_ip4_encaps: p4 = (self.p_ether / p_ip4_encap / p_ip4 / self.p_payload) p4_reply = (p_ip4 / self.p_payload) p4_reply.ttl -= 1 rx = self.send_and_expect(self.pg1, p4 * N_PACKETS, self.pg0) n_packets_decapped += N_PACKETS for p in rx: self.validate(p[1], p4_reply) self.assert_packet_checksums_valid(p) err = self.statistics.get_err_counter( '/err/ipip4-input/packets decapsulated') self.assertEqual(err, n_packets_decapped) # tun_ecn copies the ECN bits from the encap to the inner p_ip4_encaps = [ IP(src=tun_ecn.dst, dst=self.pg0.local_ip4, tos=tc) for tc in tcs ] p_ip4_replys = [p_ip4.copy() for i in range(len(p_ip4_encaps))] p_ip4_replys[2].tos = ecn p_ip4_replys[3].tos = ecn for i, p_ip4_encap in enumerate(p_ip4_encaps): p4 = (self.p_ether / p_ip4_encap / p_ip4 / self.p_payload) p4_reply = (p_ip4_replys[i] / self.p_payload) p4_reply.ttl -= 1 rx = self.send_and_expect(self.pg1, p4 * N_PACKETS, self.pg0) n_packets_decapped += N_PACKETS for p in rx: self.validate(p[1], p4_reply) self.assert_packet_checksums_valid(p) err = self.statistics.get_err_counter( '/err/ipip4-input/packets decapsulated') self.assertEqual(err, n_packets_decapped) # IPv4 tunnel to IPv6 # for each encap tun will produce the same inner packet because it does # not copy up fields from the payload p_ip4_encaps = [ IP(src=tun.dst, dst=self.pg0.local_ip4, tos=tc) for tc in tcs ] p_ip6 = IPv6(src="1:2:3::4", dst=self.pg0.remote_ip6) for p_ip4_encap in p_ip4_encaps: p6 = (self.p_ether / p_ip4_encap / p_ip6 / self.p_payload) p6_reply = (p_ip6 / self.p_payload) p6_reply.hlim = 63 rx = self.send_and_expect(self.pg1, p6 * N_PACKETS, self.pg0) n_packets_decapped += N_PACKETS for p in rx: self.validate(p[1], p6_reply) self.assert_packet_checksums_valid(p) err = self.statistics.get_err_counter( '/err/ipip4-input/packets decapsulated') self.assertEqual(err, n_packets_decapped) # IPv4 tunnel to IPv6 # tun_ecn copies the ECN bits from the encap to the inner p_ip4_encaps = [ IP(src=tun_ecn.dst, dst=self.pg0.local_ip4, tos=tc) for tc in tcs ] p_ip6 = IPv6(src="1:2:3::4", dst=self.pg0.remote_ip6) p_ip6_replys = [p_ip6.copy() for i in range(len(p_ip4_encaps))] p_ip6_replys[2].tc = ecn p_ip6_replys[3].tc = ecn for i, p_ip4_encap in enumerate(p_ip4_encaps): p6 = (self.p_ether / p_ip4_encap / p_ip6 / self.p_payload) p6_reply = (p_ip6_replys[i] / self.p_payload) p6_reply.hlim = 63 rx = self.send_and_expect(self.pg1, p6 * N_PACKETS, self.pg0) n_packets_decapped += N_PACKETS for p in rx: self.validate(p[1], p6_reply) self.assert_packet_checksums_valid(p) err = self.statistics.get_err_counter( '/err/ipip4-input/packets decapsulated') self.assertEqual(err, n_packets_decapped) # # Fragmentation / Reassembly and Re-fragmentation # rv = self.vapi.ip_reassembly_enable_disable( sw_if_index=self.pg1.sw_if_index, enable_ip4=1) self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=1000, max_reassembly_length=1000, expire_walk_interval_ms=10000, is_ip6=0) # Send lots of fragments, verify reassembled packet frags, p4_reply = self.generate_ip4_frags(3131, 1400) f = [] for i in range(0, 1000): f.extend(frags) self.pg1.add_stream(f) self.pg_enable_capture() self.pg_start() rx = self.pg0.get_capture(1000) n_packets_decapped += 1000 for p in rx: self.validate(p[1], p4_reply) err = self.statistics.get_err_counter( '/err/ipip4-input/packets decapsulated') self.assertEqual(err, n_packets_decapped) f = [] r = [] for i in range(1, 90): frags, p4_reply = self.generate_ip4_frags(i * 100, 1000) f.extend(frags) r.extend(p4_reply) self.pg_enable_capture() self.pg1.add_stream(f) self.pg_start() rx = self.pg0.get_capture(89) i = 0 for p in rx: self.validate(p[1], r[i]) i += 1 # Now try with re-fragmentation # # Send fragments to tunnel head-end, for the tunnel head end # to reassemble and then refragment # self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [576, 0, 0, 0]) frags, p4_reply = self.generate_ip4_frags(3123, 1200) self.pg_enable_capture() self.pg1.add_stream(frags) self.pg_start() rx = self.pg0.get_capture(6) reass_pkt = reassemble4(rx) p4_reply.id = 256 self.validate(reass_pkt, p4_reply) self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [1600, 0, 0, 0]) frags, p4_reply = self.generate_ip4_frags(3123, 1200) self.pg_enable_capture() self.pg1.add_stream(frags) self.pg_start() rx = self.pg0.get_capture(2) reass_pkt = reassemble4(rx) p4_reply.id = 512 self.validate(reass_pkt, p4_reply) # send large packets through the tunnel, expect them to be fragmented self.vapi.sw_interface_set_mtu(tun_dscp.sw_if_index, [600, 0, 0, 0]) p4 = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IP(src="1.2.3.4", dst="130.67.0.1", tos=42) / UDP(sport=1234, dport=1234) / Raw(b'Q' * 1000)) rx = self.send_and_expect(self.pg0, p4 * 15, self.pg1, 30) inners = [] for p in rx: inners.append(p[IP].payload) reass_pkt = reassemble4(inners) for p in reass_pkt: self.assert_packet_checksums_valid(p) self.assertEqual(p[IP].ttl, 63) def test_ipip_create(self): """ ipip create / delete interface test """ rv = ipip_add_tunnel(self, '1.2.3.4', '2.3.4.5') sw_if_index = rv.sw_if_index self.vapi.ipip_del_tunnel(sw_if_index) def test_ipip_vrf_create(self): """ ipip create / delete interface VRF test """ t = VppIpTable(self, 20) t.add_vpp_config() rv = ipip_add_tunnel(self, '1.2.3.4', '2.3.4.5', table_id=20) sw_if_index = rv.sw_if_index self.vapi.ipip_del_tunnel(sw_if_index) def payload(self, len): return 'x' * len def test_mipip4(self): """ p2mp IPv4 tunnel Tests """ for itf in self.pg_interfaces[:2]: # # one underlay nh for each overlay/tunnel peer # itf.generate_remote_hosts(4) itf.configure_ipv4_neighbors() # # Create an p2mo IPIP tunnel. # - set it admin up # - assign an IP Addres # - Add a route via the tunnel # ipip_if = VppIpIpTunInterface( self, itf, itf.local_ip4, "0.0.0.0", mode=(VppEnum.vl_api_tunnel_mode_t.TUNNEL_API_MODE_MP)) ipip_if.add_vpp_config() ipip_if.admin_up() ipip_if.config_ip4() ipip_if.generate_remote_hosts(4) self.logger.info(self.vapi.cli("sh adj")) self.logger.info(self.vapi.cli("sh ip fib")) # # ensure we don't match to the tunnel if the source address # is all zeros # # tx = self.create_tunnel_stream_4o4(self.pg0, # "0.0.0.0", # itf.local_ip4, # self.pg0.local_ip4, # self.pg0.remote_ip4) # self.send_and_assert_no_replies(self.pg0, tx) # # for-each peer # for ii in range(1, 4): route_addr = "4.4.4.%d" % ii # # route traffic via the peer # route_via_tun = VppIpRoute(self, route_addr, 32, [ VppRoutePath(ipip_if._remote_hosts[ii].ip4, ipip_if.sw_if_index) ]) route_via_tun.add_vpp_config() # # Add a TEIB entry resolves the peer # teib = VppTeib(self, ipip_if, ipip_if._remote_hosts[ii].ip4, itf._remote_hosts[ii].ip4) teib.add_vpp_config() self.logger.info( self.vapi.cli("sh adj nbr ipip0 %s" % ipip_if._remote_hosts[ii].ip4)) # # Send a packet stream that is routed into the tunnel # - packets are IPIP encapped # inner = (IP(dst=route_addr, src="5.5.5.5") / UDP(sport=1234, dport=1234) / Raw(b'0x44' * 100)) tx_e = [ (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / inner) for x in range(63) ] rxs = self.send_and_expect(self.pg0, tx_e, itf) for rx in rxs: self.assertEqual(rx[IP].src, itf.local_ip4) self.assertEqual(rx[IP].dst, itf._remote_hosts[ii].ip4) tx_i = [ (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=itf._remote_hosts[ii].ip4, dst=itf.local_ip4) / IP(src=self.pg0.local_ip4, dst=self.pg0.remote_ip4) / UDP(sport=1234, dport=1234) / Raw(b'0x44' * 100)) for x in range(63) ] self.logger.info(self.vapi.cli("sh ipip tunnel-hash")) rx = self.send_and_expect(self.pg0, tx_i, self.pg0) # # delete and re-add the TEIB # teib.remove_vpp_config() self.send_and_assert_no_replies(self.pg0, tx_e) self.send_and_assert_no_replies(self.pg0, tx_i) teib.add_vpp_config() rx = self.send_and_expect(self.pg0, tx_e, itf) for rx in rxs: self.assertEqual(rx[IP].src, itf.local_ip4) self.assertEqual(rx[IP].dst, itf._remote_hosts[ii].ip4) rx = self.send_and_expect(self.pg0, tx_i, self.pg0) # # we can also send to the peer's address # inner = (IP(dst=teib.peer, src="5.5.5.5") / UDP(sport=1234, dport=1234) / Raw(b'0x44' * 100)) tx_e = [ (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / inner) for x in range(63) ] rxs = self.send_and_expect(self.pg0, tx_e, itf) # # with all of the peers in place, swap the ip-table of # the ipip interface # table = VppIpTable(self, 2) table.add_vpp_config() ipip_if.unconfig_ip4() ipip_if.set_table_ip4(self.table.table_id) ipip_if.config_ip4() # # we should still be able to reach the peers from the new table # inner = (IP(dst=teib.peer, src="5.5.5.5") / UDP(sport=1234, dport=1234) / Raw(b'0x44' * 100)) tx_e = [(Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / inner) for x in range(63)] rxs = self.send_and_expect(self.pg2, tx_e, itf) ipip_if.admin_down() ipip_if.unconfig_ip4() ipip_if.set_table_ip4(0)
class ARPTestCase(VppTestCase): """ ARP Test Case """ def setUp(self): super(ARPTestCase, self).setUp() # create 3 pg interfaces self.create_pg_interfaces(range(4)) # pg0 configured with ip4 and 6 addresses used for input # pg1 configured with ip4 and 6 addresses used for output # pg2 is unnumbered to pg0 for i in self.pg_interfaces: i.admin_up() self.pg0.config_ip4() self.pg0.config_ip6() self.pg0.resolve_arp() self.pg1.config_ip4() self.pg1.config_ip6() # pg3 in a different VRF self.tbl = VppIpTable(self, 1) self.tbl.add_vpp_config() self.pg3.set_table_ip4(1) self.pg3.config_ip4() def tearDown(self): self.pg0.unconfig_ip4() self.pg0.unconfig_ip6() self.pg1.unconfig_ip4() self.pg1.unconfig_ip6() self.pg3.unconfig_ip4() self.pg3.set_table_ip4(0) for i in self.pg_interfaces: i.admin_down() super(ARPTestCase, self).tearDown() def verify_arp_req(self, rx, smac, sip, dip): ether = rx[Ether] self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff") self.assertEqual(ether.src, smac) arp = rx[ARP] self.assertEqual(arp.hwtype, 1) self.assertEqual(arp.ptype, 0x800) self.assertEqual(arp.hwlen, 6) self.assertEqual(arp.plen, 4) self.assertEqual(arp.op, arp_opts["who-has"]) self.assertEqual(arp.hwsrc, smac) self.assertEqual(arp.hwdst, "00:00:00:00:00:00") self.assertEqual(arp.psrc, sip) self.assertEqual(arp.pdst, dip) def verify_arp_resp(self, rx, smac, dmac, sip, dip): ether = rx[Ether] self.assertEqual(ether.dst, dmac) self.assertEqual(ether.src, smac) arp = rx[ARP] self.assertEqual(arp.hwtype, 1) self.assertEqual(arp.ptype, 0x800) self.assertEqual(arp.hwlen, 6) self.assertEqual(arp.plen, 4) self.assertEqual(arp.op, arp_opts["is-at"]) self.assertEqual(arp.hwsrc, smac) self.assertEqual(arp.hwdst, dmac) self.assertEqual(arp.psrc, sip) self.assertEqual(arp.pdst, dip) def verify_arp_vrrp_resp(self, rx, smac, dmac, sip, dip): ether = rx[Ether] self.assertEqual(ether.dst, dmac) self.assertEqual(ether.src, smac) arp = rx[ARP] self.assertEqual(arp.hwtype, 1) self.assertEqual(arp.ptype, 0x800) self.assertEqual(arp.hwlen, 6) self.assertEqual(arp.plen, 4) self.assertEqual(arp.op, arp_opts["is-at"]) self.assertNotEqual(arp.hwsrc, smac) self.assertTrue("00:00:5e:00:01" in arp.hwsrc or "00:00:5E:00:01" in arp.hwsrc) self.assertEqual(arp.hwdst, dmac) self.assertEqual(arp.psrc, sip) self.assertEqual(arp.pdst, dip) def verify_ip(self, rx, smac, dmac, sip, dip): ether = rx[Ether] self.assertEqual(ether.dst, dmac) self.assertEqual(ether.src, smac) ip = rx[IP] self.assertEqual(ip.src, sip) self.assertEqual(ip.dst, dip) def verify_ip_o_mpls(self, rx, smac, dmac, label, sip, dip): ether = rx[Ether] self.assertEqual(ether.dst, dmac) self.assertEqual(ether.src, smac) mpls = rx[MPLS] self.assertTrue(mpls.label, label) ip = rx[IP] self.assertEqual(ip.src, sip) self.assertEqual(ip.dst, dip) def test_arp(self): """ ARP """ # # Generate some hosts on the LAN # self.pg1.generate_remote_hosts(11) # # Send IP traffic to one of these unresolved hosts. # expect the generation of an ARP request # p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst=self.pg1._remote_hosts[1].ip4) / UDP(sport=1234, dport=1234) / Raw()) self.pg0.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg1.get_capture(1) self.verify_arp_req(rx[0], self.pg1.local_mac, self.pg1.local_ip4, self.pg1._remote_hosts[1].ip4) # # And a dynamic ARP entry for host 1 # dyn_arp = VppNeighbor(self, self.pg1.sw_if_index, self.pg1.remote_hosts[1].mac, self.pg1.remote_hosts[1].ip4) dyn_arp.add_vpp_config() # # now we expect IP traffic forwarded # dyn_p = ( Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst=self.pg1._remote_hosts[1].ip4) / UDP(sport=1234, dport=1234) / Raw()) self.pg0.add_stream(dyn_p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg1.get_capture(1) self.verify_ip(rx[0], self.pg1.local_mac, self.pg1.remote_hosts[1].mac, self.pg0.remote_ip4, self.pg1._remote_hosts[1].ip4) # # And a Static ARP entry for host 2 # static_arp = VppNeighbor(self, self.pg1.sw_if_index, self.pg1.remote_hosts[2].mac, self.pg1.remote_hosts[2].ip4, is_static=1) static_arp.add_vpp_config() static_p = ( Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst=self.pg1._remote_hosts[2].ip4) / UDP(sport=1234, dport=1234) / Raw()) self.pg0.add_stream(static_p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg1.get_capture(1) self.verify_ip(rx[0], self.pg1.local_mac, self.pg1.remote_hosts[2].mac, self.pg0.remote_ip4, self.pg1._remote_hosts[2].ip4) # # flap the link. dynamic ARPs get flush, statics don't # self.pg1.admin_down() self.pg1.admin_up() self.pg0.add_stream(static_p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg1.get_capture(1) self.verify_ip(rx[0], self.pg1.local_mac, self.pg1.remote_hosts[2].mac, self.pg0.remote_ip4, self.pg1._remote_hosts[2].ip4) self.pg0.add_stream(dyn_p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg1.get_capture(1) self.verify_arp_req(rx[0], self.pg1.local_mac, self.pg1.local_ip4, self.pg1._remote_hosts[1].ip4) # # Send an ARP request from one of the so-far unlearned remote hosts # p = ( Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg1._remote_hosts[3].mac) / ARP(op="who-has", hwsrc=self.pg1._remote_hosts[3].mac, pdst=self.pg1.local_ip4, psrc=self.pg1._remote_hosts[3].ip4)) self.pg1.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg1.get_capture(1) self.verify_arp_resp(rx[0], self.pg1.local_mac, self.pg1._remote_hosts[3].mac, self.pg1.local_ip4, self.pg1._remote_hosts[3].ip4) # # VPP should have learned the mapping for the remote host # self.assertTrue( find_nbr(self, self.pg1.sw_if_index, self.pg1._remote_hosts[3].ip4)) # # Fire in an ARP request before the interface becomes IP enabled # self.pg2.generate_remote_hosts(4) p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) / ARP(op="who-has", hwsrc=self.pg2.remote_mac, pdst=self.pg1.local_ip4, psrc=self.pg2.remote_hosts[3].ip4)) pt = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) / Dot1Q(vlan=0) / ARP(op="who-has", hwsrc=self.pg2.remote_mac, pdst=self.pg1.local_ip4, psrc=self.pg2.remote_hosts[3].ip4)) self.send_and_assert_no_replies(self.pg2, p, "interface not IP enabled") # # Make pg2 un-numbered to pg1 # self.pg2.set_unnumbered(self.pg1.sw_if_index) # # We should respond to ARP requests for the unnumbered to address # once an attached route to the source is known # self.send_and_assert_no_replies( self.pg2, p, "ARP req for unnumbered address - no source") attached_host = VppIpRoute( self, self.pg2.remote_hosts[3].ip4, 32, [VppRoutePath("0.0.0.0", self.pg2.sw_if_index)]) attached_host.add_vpp_config() self.pg2.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg2.get_capture(1) self.verify_arp_resp(rx[0], self.pg2.local_mac, self.pg2.remote_mac, self.pg1.local_ip4, self.pg2.remote_hosts[3].ip4) self.pg2.add_stream(pt) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg2.get_capture(1) self.verify_arp_resp(rx[0], self.pg2.local_mac, self.pg2.remote_mac, self.pg1.local_ip4, self.pg2.remote_hosts[3].ip4) # # A neighbor entry that has no associated FIB-entry # arp_no_fib = VppNeighbor(self, self.pg1.sw_if_index, self.pg1.remote_hosts[4].mac, self.pg1.remote_hosts[4].ip4, is_no_fib_entry=1) arp_no_fib.add_vpp_config() # # check we have the neighbor, but no route # self.assertTrue( find_nbr(self, self.pg1.sw_if_index, self.pg1._remote_hosts[4].ip4)) self.assertFalse(find_route(self, self.pg1._remote_hosts[4].ip4, 32)) # # pg2 is unnumbered to pg1, so we can form adjacencies out of pg2 # from within pg1's subnet # arp_unnum = VppNeighbor(self, self.pg2.sw_if_index, self.pg1.remote_hosts[5].mac, self.pg1.remote_hosts[5].ip4) arp_unnum.add_vpp_config() p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst=self.pg1._remote_hosts[5].ip4) / UDP(sport=1234, dport=1234) / Raw()) self.pg0.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg2.get_capture(1) self.verify_ip(rx[0], self.pg2.local_mac, self.pg1.remote_hosts[5].mac, self.pg0.remote_ip4, self.pg1._remote_hosts[5].ip4) # # ARP requests from hosts in pg1's subnet sent on pg2 are replied to # with the unnumbered interface's address as the source # p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) / ARP(op="who-has", hwsrc=self.pg2.remote_mac, pdst=self.pg1.local_ip4, psrc=self.pg1.remote_hosts[6].ip4)) self.pg2.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg2.get_capture(1) self.verify_arp_resp(rx[0], self.pg2.local_mac, self.pg2.remote_mac, self.pg1.local_ip4, self.pg1.remote_hosts[6].ip4) # # An attached host route out of pg2 for an undiscovered hosts generates # an ARP request with the unnumbered address as the source # att_unnum = VppIpRoute(self, self.pg1.remote_hosts[7].ip4, 32, [VppRoutePath("0.0.0.0", self.pg2.sw_if_index)]) att_unnum.add_vpp_config() p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst=self.pg1._remote_hosts[7].ip4) / UDP(sport=1234, dport=1234) / Raw()) self.pg0.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg2.get_capture(1) self.verify_arp_req(rx[0], self.pg2.local_mac, self.pg1.local_ip4, self.pg1._remote_hosts[7].ip4) p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) / ARP(op="who-has", hwsrc=self.pg2.remote_mac, pdst=self.pg1.local_ip4, psrc=self.pg1.remote_hosts[7].ip4)) self.pg2.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg2.get_capture(1) self.verify_arp_resp(rx[0], self.pg2.local_mac, self.pg2.remote_mac, self.pg1.local_ip4, self.pg1.remote_hosts[7].ip4) # # An attached host route as yet unresolved out of pg2 for an # undiscovered host, an ARP requests begets a response. # att_unnum1 = VppIpRoute( self, self.pg1.remote_hosts[8].ip4, 32, [VppRoutePath("0.0.0.0", self.pg2.sw_if_index)]) att_unnum1.add_vpp_config() p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) / ARP(op="who-has", hwsrc=self.pg2.remote_mac, pdst=self.pg1.local_ip4, psrc=self.pg1.remote_hosts[8].ip4)) self.pg2.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg2.get_capture(1) self.verify_arp_resp(rx[0], self.pg2.local_mac, self.pg2.remote_mac, self.pg1.local_ip4, self.pg1.remote_hosts[8].ip4) # # Send an ARP request from one of the so-far unlearned remote hosts # with a VLAN0 tag # p = ( Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg1._remote_hosts[9].mac) / Dot1Q(vlan=0) / ARP(op="who-has", hwsrc=self.pg1._remote_hosts[9].mac, pdst=self.pg1.local_ip4, psrc=self.pg1._remote_hosts[9].ip4)) self.pg1.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg1.get_capture(1) self.verify_arp_resp(rx[0], self.pg1.local_mac, self.pg1._remote_hosts[9].mac, self.pg1.local_ip4, self.pg1._remote_hosts[9].ip4) # # Add a hierachy of routes for a host in the sub-net. # Should still get an ARP resp since the cover is attached # p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg1.remote_mac) / ARP(op="who-has", hwsrc=self.pg1.remote_mac, pdst=self.pg1.local_ip4, psrc=self.pg1.remote_hosts[10].ip4)) r1 = VppIpRoute(self, self.pg1.remote_hosts[10].ip4, 30, [ VppRoutePath(self.pg1.remote_hosts[10].ip4, self.pg1.sw_if_index) ]) r1.add_vpp_config() self.pg1.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg1.get_capture(1) self.verify_arp_resp(rx[0], self.pg1.local_mac, self.pg1.remote_mac, self.pg1.local_ip4, self.pg1.remote_hosts[10].ip4) r2 = VppIpRoute(self, self.pg1.remote_hosts[10].ip4, 32, [ VppRoutePath(self.pg1.remote_hosts[10].ip4, self.pg1.sw_if_index) ]) r2.add_vpp_config() self.pg1.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg1.get_capture(1) self.verify_arp_resp(rx[0], self.pg1.local_mac, self.pg1.remote_mac, self.pg1.local_ip4, self.pg1.remote_hosts[10].ip4) # # add an ARP entry that's not on the sub-net and so whose # adj-fib fails the refinement check. then send an ARP request # from that source # a1 = VppNeighbor(self, self.pg0.sw_if_index, self.pg0.remote_mac, "100.100.100.50") a1.add_vpp_config() p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(op="who-has", hwsrc=self.pg0.remote_mac, psrc="100.100.100.50", pdst=self.pg0.remote_ip4)) self.send_and_assert_no_replies(self.pg0, p, "ARP req for from failed adj-fib") # # ERROR Cases # 1 - don't respond to ARP request for address not within the # interface's sub-net # 1b - nor within the unnumbered subnet # 1c - nor within the subnet of a different interface # p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(op="who-has", hwsrc=self.pg0.remote_mac, pdst="10.10.10.3", psrc=self.pg0.remote_ip4)) self.send_and_assert_no_replies(self.pg0, p, "ARP req for non-local destination") self.assertFalse(find_nbr(self, self.pg0.sw_if_index, "10.10.10.3")) p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) / ARP(op="who-has", hwsrc=self.pg2.remote_mac, pdst="10.10.10.3", psrc=self.pg1.remote_hosts[7].ip4)) self.send_and_assert_no_replies( self.pg0, p, "ARP req for non-local destination - unnum") p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(op="who-has", hwsrc=self.pg0.remote_mac, pdst=self.pg1.local_ip4, psrc=self.pg1.remote_ip4)) self.send_and_assert_no_replies(self.pg0, p, "ARP req diff sub-net") self.assertFalse( find_nbr(self, self.pg0.sw_if_index, self.pg1.remote_ip4)) # # 2 - don't respond to ARP request from an address not within the # interface's sub-net # 2b - to a prxied address # 2c - not within a differents interface's sub-net p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(op="who-has", hwsrc=self.pg0.remote_mac, psrc="10.10.10.3", pdst=self.pg0.local_ip4)) self.send_and_assert_no_replies(self.pg0, p, "ARP req for non-local source") p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) / ARP(op="who-has", hwsrc=self.pg2.remote_mac, psrc="10.10.10.3", pdst=self.pg0.local_ip4)) self.send_and_assert_no_replies( self.pg0, p, "ARP req for non-local source - unnum") p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(op="who-has", hwsrc=self.pg0.remote_mac, psrc=self.pg1.remote_ip4, pdst=self.pg0.local_ip4)) self.send_and_assert_no_replies(self.pg0, p, "ARP req for non-local source 2c") # # 3 - don't respond to ARP request from an address that belongs to # the router # p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(op="who-has", hwsrc=self.pg0.remote_mac, psrc=self.pg0.local_ip4, pdst=self.pg0.local_ip4)) self.send_and_assert_no_replies(self.pg0, p, "ARP req for non-local source") # # 4 - don't respond to ARP requests that has mac source different # from ARP request HW source # p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(op="who-has", hwsrc="00:00:00:DE:AD:BE", psrc=self.pg0.remote_ip4, pdst=self.pg0.local_ip4)) self.send_and_assert_no_replies(self.pg0, p, "ARP req for non-local source") # # 5 - don't respond to ARP requests for address within the # interface's sub-net but not the interface's address # self.pg0.generate_remote_hosts(2) p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(op="who-has", hwsrc=self.pg0.remote_mac, psrc=self.pg0.remote_hosts[0].ip4, pdst=self.pg0.remote_hosts[1].ip4)) self.send_and_assert_no_replies(self.pg0, p, "ARP req for non-local destination") # # cleanup # dyn_arp.remove_vpp_config() static_arp.remove_vpp_config() self.pg2.unset_unnumbered(self.pg1.sw_if_index) # need this to flush the adj-fibs self.pg2.unset_unnumbered(self.pg1.sw_if_index) self.pg2.admin_down() self.pg1.admin_down() def test_proxy_mirror_arp(self): """ Interface Mirror Proxy ARP """ # # When VPP has an interface whose address is also applied to a TAP # interface on the host, then VPP's TAP interface will be unnumbered # to the 'real' interface and do proxy ARP from the host. # the curious aspect of this setup is that ARP requests from the host # will come from the VPP's own address. # self.pg0.generate_remote_hosts(2) arp_req_from_me = ( Ether(src=self.pg2.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(op="who-has", hwsrc=self.pg2.remote_mac, pdst=self.pg0.remote_hosts[1].ip4, psrc=self.pg0.local_ip4)) # # Configure Proxy ARP for the subnet on PG0addresses on pg0 # self.vapi.proxy_arp_add_del(self.pg0._local_ip4n_subnet, self.pg0._local_ip4n_bcast) # Make pg2 un-numbered to pg0 # self.pg2.set_unnumbered(self.pg0.sw_if_index) # # Enable pg2 for proxy ARP # self.pg2.set_proxy_arp() # # Send the ARP request with an originating address that # is VPP's own address # self.pg2.add_stream(arp_req_from_me) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg2.get_capture(1) self.verify_arp_resp(rx[0], self.pg2.local_mac, self.pg2.remote_mac, self.pg0.remote_hosts[1].ip4, self.pg0.local_ip4) # # validate we have not learned an ARP entry as a result of this # self.assertFalse( find_nbr(self, self.pg2.sw_if_index, self.pg0.local_ip4)) # # cleanup # self.pg2.set_proxy_arp(0) self.vapi.proxy_arp_add_del(self.pg0._local_ip4n_subnet, self.pg0._local_ip4n_bcast, is_add=0) def test_proxy_arp(self): """ Proxy ARP """ self.pg1.generate_remote_hosts(2) # # Proxy ARP rewquest packets for each interface # arp_req_pg0 = ( Ether(src=self.pg0.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(op="who-has", hwsrc=self.pg0.remote_mac, pdst="10.10.10.3", psrc=self.pg0.remote_ip4)) arp_req_pg0_tagged = ( Ether(src=self.pg0.remote_mac, dst="ff:ff:ff:ff:ff:ff") / Dot1Q(vlan=0) / ARP(op="who-has", hwsrc=self.pg0.remote_mac, pdst="10.10.10.3", psrc=self.pg0.remote_ip4)) arp_req_pg1 = ( Ether(src=self.pg1.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(op="who-has", hwsrc=self.pg1.remote_mac, pdst="10.10.10.3", psrc=self.pg1.remote_ip4)) arp_req_pg2 = ( Ether(src=self.pg2.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(op="who-has", hwsrc=self.pg2.remote_mac, pdst="10.10.10.3", psrc=self.pg1.remote_hosts[1].ip4)) arp_req_pg3 = ( Ether(src=self.pg3.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(op="who-has", hwsrc=self.pg3.remote_mac, pdst="10.10.10.3", psrc=self.pg3.remote_ip4)) # # Configure Proxy ARP for 10.10.10.0 -> 10.10.10.124 # self.vapi.proxy_arp_add_del(inet_pton(AF_INET, "10.10.10.2"), inet_pton(AF_INET, "10.10.10.124")) # # No responses are sent when the interfaces are not enabled for proxy # ARP # self.send_and_assert_no_replies(self.pg0, arp_req_pg0, "ARP req from unconfigured interface") self.send_and_assert_no_replies(self.pg2, arp_req_pg2, "ARP req from unconfigured interface") # # Make pg2 un-numbered to pg1 # still won't reply. # self.pg2.set_unnumbered(self.pg1.sw_if_index) self.send_and_assert_no_replies(self.pg2, arp_req_pg2, "ARP req from unnumbered interface") # # Enable each interface to reply to proxy ARPs # for i in self.pg_interfaces: i.set_proxy_arp() # # Now each of the interfaces should reply to a request to a proxied # address # self.pg0.add_stream(arp_req_pg0) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg0.get_capture(1) self.verify_arp_resp(rx[0], self.pg0.local_mac, self.pg0.remote_mac, "10.10.10.3", self.pg0.remote_ip4) self.pg0.add_stream(arp_req_pg0_tagged) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg0.get_capture(1) self.verify_arp_resp(rx[0], self.pg0.local_mac, self.pg0.remote_mac, "10.10.10.3", self.pg0.remote_ip4) self.pg1.add_stream(arp_req_pg1) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg1.get_capture(1) self.verify_arp_resp(rx[0], self.pg1.local_mac, self.pg1.remote_mac, "10.10.10.3", self.pg1.remote_ip4) self.pg2.add_stream(arp_req_pg2) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg2.get_capture(1) self.verify_arp_resp(rx[0], self.pg2.local_mac, self.pg2.remote_mac, "10.10.10.3", self.pg1.remote_hosts[1].ip4) # # A request for an address out of the configured range # arp_req_pg1_hi = ( Ether(src=self.pg1.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(op="who-has", hwsrc=self.pg1.remote_mac, pdst="10.10.10.125", psrc=self.pg1.remote_ip4)) self.send_and_assert_no_replies(self.pg1, arp_req_pg1_hi, "ARP req out of range HI") arp_req_pg1_low = ( Ether(src=self.pg1.remote_mac, dst="ff:ff:ff:ff:ff:ff") / ARP(op="who-has", hwsrc=self.pg1.remote_mac, pdst="10.10.10.1", psrc=self.pg1.remote_ip4)) self.send_and_assert_no_replies(self.pg1, arp_req_pg1_low, "ARP req out of range Low") # # Request for an address in the proxy range but from an interface # in a different VRF # self.send_and_assert_no_replies(self.pg3, arp_req_pg3, "ARP req from different VRF") # # Disable Each interface for proxy ARP # - expect none to respond # for i in self.pg_interfaces: i.set_proxy_arp(0) self.send_and_assert_no_replies(self.pg0, arp_req_pg0, "ARP req from disable") self.send_and_assert_no_replies(self.pg1, arp_req_pg1, "ARP req from disable") self.send_and_assert_no_replies(self.pg2, arp_req_pg2, "ARP req from disable") # # clean up on interface 2 # self.pg2.unset_unnumbered(self.pg1.sw_if_index) def test_mpls(self): """ MPLS """ # # Interface 2 does not yet have ip4 config # self.pg2.config_ip4() self.pg2.generate_remote_hosts(2) # # Add a reoute with out going label via an ARP unresolved next-hop # ip_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32, [ VppRoutePath(self.pg2.remote_hosts[1].ip4, self.pg2.sw_if_index, labels=[55]) ]) ip_10_0_0_1.add_vpp_config() # # packets should generate an ARP request # p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IP(src=self.pg0.remote_ip4, dst="10.0.0.1") / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) self.pg0.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg2.get_capture(1) self.verify_arp_req(rx[0], self.pg2.local_mac, self.pg2.local_ip4, self.pg2._remote_hosts[1].ip4) # # now resolve the neighbours # self.pg2.configure_ipv4_neighbors() # # Now packet should be properly MPLS encapped. # This verifies that MPLS link-type adjacencies are completed # when the ARP entry resolves # self.pg0.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg2.get_capture(1) self.verify_ip_o_mpls(rx[0], self.pg2.local_mac, self.pg2.remote_hosts[1].mac, 55, self.pg0.remote_ip4, "10.0.0.1") self.pg2.unconfig_ip4() def test_arp_vrrp(self): """ ARP reply with VRRP virtual src hw addr """ # # IP packet destined for pg1 remote host arrives on pg0 resulting # in an ARP request for the address of the remote host on pg1 # p0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / UDP(sport=1234, dport=1234) / Raw()) self.pg0.add_stream(p0) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx1 = self.pg1.get_capture(1) self.verify_arp_req(rx1[0], self.pg1.local_mac, self.pg1.local_ip4, self.pg1.remote_ip4) # # ARP reply for address of pg1 remote host arrives on pg1 with # the hw src addr set to a value in the VRRP IPv4 range of # MAC addresses # p1 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / ARP(op="is-at", hwdst=self.pg1.local_mac, hwsrc="00:00:5e:00:01:09", pdst=self.pg1.local_ip4, psrc=self.pg1.remote_ip4)) self.pg1.add_stream(p1) self.pg_enable_capture(self.pg_interfaces) self.pg_start() # # IP packet destined for pg1 remote host arrives on pg0 again. # VPP should have an ARP entry for that address now and the packet # should be sent out pg1. # self.pg0.add_stream(p0) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx1 = self.pg1.get_capture(1) self.verify_ip(rx1[0], self.pg1.local_mac, "00:00:5e:00:01:09", self.pg0.remote_ip4, self.pg1.remote_ip4) self.pg1.admin_down() self.pg1.admin_up() def test_arp_duplicates(self): """ ARP Duplicates""" # # Generate some hosts on the LAN # self.pg1.generate_remote_hosts(3) # # Add host 1 on pg1 and pg2 # arp_pg1 = VppNeighbor(self, self.pg1.sw_if_index, self.pg1.remote_hosts[1].mac, self.pg1.remote_hosts[1].ip4) arp_pg1.add_vpp_config() arp_pg2 = VppNeighbor(self, self.pg2.sw_if_index, self.pg2.remote_mac, self.pg1.remote_hosts[1].ip4) arp_pg2.add_vpp_config() # # IP packet destined for pg1 remote host arrives on pg1 again. # p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_hosts[1].ip4) / UDP(sport=1234, dport=1234) / Raw()) self.pg0.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx1 = self.pg1.get_capture(1) self.verify_ip(rx1[0], self.pg1.local_mac, self.pg1.remote_hosts[1].mac, self.pg0.remote_ip4, self.pg1.remote_hosts[1].ip4) # # remove the duplicate on pg1 # packet stream shoud generate ARPs out of pg1 # arp_pg1.remove_vpp_config() self.pg0.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx1 = self.pg1.get_capture(1) self.verify_arp_req(rx1[0], self.pg1.local_mac, self.pg1.local_ip4, self.pg1.remote_hosts[1].ip4) # # Add it back # arp_pg1.add_vpp_config() self.pg0.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx1 = self.pg1.get_capture(1) self.verify_ip(rx1[0], self.pg1.local_mac, self.pg1.remote_hosts[1].mac, self.pg0.remote_ip4, self.pg1.remote_hosts[1].ip4) def test_arp_static(self): """ ARP Static""" self.pg2.generate_remote_hosts(3) # # Add a static ARP entry # static_arp = VppNeighbor(self, self.pg2.sw_if_index, self.pg2.remote_hosts[1].mac, self.pg2.remote_hosts[1].ip4, is_static=1) static_arp.add_vpp_config() # # Add the connected prefix to the interface # self.pg2.config_ip4() # # We should now find the adj-fib # self.assertTrue( find_nbr(self, self.pg2.sw_if_index, self.pg2.remote_hosts[1].ip4, is_static=1)) self.assertTrue(find_route(self, self.pg2.remote_hosts[1].ip4, 32)) # # remove the connected # self.pg2.unconfig_ip4() # # put the interface into table 1 # self.pg2.set_table_ip4(1) # # configure the same connected and expect to find the # adj fib in the new table # self.pg2.config_ip4() self.assertTrue( find_route(self, self.pg2.remote_hosts[1].ip4, 32, table_id=1)) # # clean-up # self.pg2.unconfig_ip4() self.pg2.set_table_ip4(0)
class TestGRE(VppTestCase): """ GRE Test Case """ @classmethod def setUpClass(cls): super(TestGRE, cls).setUpClass() @classmethod def tearDownClass(cls): super(TestGRE, cls).tearDownClass() def setUp(self): super(TestGRE, self).setUp() # create 3 pg interfaces - set one in a non-default table. self.create_pg_interfaces(range(3)) self.tbl = VppIpTable(self, 1) self.tbl.add_vpp_config() self.pg1.set_table_ip4(1) for i in self.pg_interfaces: i.admin_up() self.pg0.config_ip4() self.pg0.resolve_arp() self.pg1.config_ip4() self.pg1.resolve_arp() self.pg2.config_ip6() self.pg2.resolve_ndp() def tearDown(self): for i in self.pg_interfaces: i.unconfig_ip4() i.unconfig_ip6() i.admin_down() self.pg1.set_table_ip4(0) super(TestGRE, self).tearDown() def create_stream_ip4(self, src_if, src_ip, dst_ip): pkts = [] for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IP(src=src_ip, dst=dst_ip) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def create_stream_ip6(self, src_if, src_ip, dst_ip): pkts = [] for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IPv6(src=src_ip, dst=dst_ip) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def create_tunnel_stream_4o4(self, src_if, tunnel_src, tunnel_dst, src_ip, dst_ip): pkts = [] for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IP(src=tunnel_src, dst=tunnel_dst) / GRE() / IP(src=src_ip, dst=dst_ip) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def create_tunnel_stream_6o4(self, src_if, tunnel_src, tunnel_dst, src_ip, dst_ip): pkts = [] for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IP(src=tunnel_src, dst=tunnel_dst) / GRE() / IPv6(src=src_ip, dst=dst_ip) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def create_tunnel_stream_6o6(self, src_if, tunnel_src, tunnel_dst, src_ip, dst_ip): pkts = [] for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IPv6(src=tunnel_src, dst=tunnel_dst) / GRE() / IPv6(src=src_ip, dst=dst_ip) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def create_tunnel_stream_l2o4(self, src_if, tunnel_src, tunnel_dst): pkts = [] for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IP(src=tunnel_src, dst=tunnel_dst) / GRE() / Ether(dst=RandMAC('*:*:*:*:*:*'), src=RandMAC('*:*:*:*:*:*')) / IP(src=scapy.compat.raw(RandIP()), dst=scapy.compat.raw(RandIP())) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def create_tunnel_stream_vlano4(self, src_if, tunnel_src, tunnel_dst, vlan): pkts = [] for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IP(src=tunnel_src, dst=tunnel_dst) / GRE() / Ether(dst=RandMAC('*:*:*:*:*:*'), src=RandMAC('*:*:*:*:*:*')) / Dot1Q(vlan=vlan) / IP(src=scapy.compat.raw(RandIP()), dst=scapy.compat.raw(RandIP())) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def verify_tunneled_4o4(self, src_if, capture, sent, tunnel_src, tunnel_dst): self.assertEqual(len(capture), len(sent)) for i in range(len(capture)): try: tx = sent[i] rx = capture[i] tx_ip = tx[IP] rx_ip = rx[IP] self.assertEqual(rx_ip.src, tunnel_src) self.assertEqual(rx_ip.dst, tunnel_dst) rx_gre = rx[GRE] rx_ip = rx_gre[IP] self.assertEqual(rx_ip.src, tx_ip.src) self.assertEqual(rx_ip.dst, tx_ip.dst) # IP processing post pop has decremented the TTL self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl) except: self.logger.error(ppp("Rx:", rx)) self.logger.error(ppp("Tx:", tx)) raise def verify_tunneled_6o6(self, src_if, capture, sent, tunnel_src, tunnel_dst): self.assertEqual(len(capture), len(sent)) for i in range(len(capture)): try: tx = sent[i] rx = capture[i] tx_ip = tx[IPv6] rx_ip = rx[IPv6] self.assertEqual(rx_ip.src, tunnel_src) self.assertEqual(rx_ip.dst, tunnel_dst) rx_gre = GRE(scapy.compat.raw(rx_ip[IPv6].payload)) rx_ip = rx_gre[IPv6] self.assertEqual(rx_ip.src, tx_ip.src) self.assertEqual(rx_ip.dst, tx_ip.dst) except: self.logger.error(ppp("Rx:", rx)) self.logger.error(ppp("Tx:", tx)) raise def verify_tunneled_4o6(self, src_if, capture, sent, tunnel_src, tunnel_dst): self.assertEqual(len(capture), len(sent)) for i in range(len(capture)): try: tx = sent[i] rx = capture[i] rx_ip = rx[IPv6] self.assertEqual(rx_ip.src, tunnel_src) self.assertEqual(rx_ip.dst, tunnel_dst) rx_gre = GRE(scapy.compat.raw(rx_ip[IPv6].payload)) tx_ip = tx[IP] rx_ip = rx_gre[IP] self.assertEqual(rx_ip.src, tx_ip.src) self.assertEqual(rx_ip.dst, tx_ip.dst) except: self.logger.error(ppp("Rx:", rx)) self.logger.error(ppp("Tx:", tx)) raise def verify_tunneled_6o4(self, src_if, capture, sent, tunnel_src, tunnel_dst): self.assertEqual(len(capture), len(sent)) for i in range(len(capture)): try: tx = sent[i] rx = capture[i] rx_ip = rx[IP] self.assertEqual(rx_ip.src, tunnel_src) self.assertEqual(rx_ip.dst, tunnel_dst) rx_gre = GRE(scapy.compat.raw(rx_ip[IP].payload)) rx_ip = rx_gre[IPv6] tx_ip = tx[IPv6] self.assertEqual(rx_ip.src, tx_ip.src) self.assertEqual(rx_ip.dst, tx_ip.dst) except: self.logger.error(ppp("Rx:", rx)) self.logger.error(ppp("Tx:", tx)) raise def verify_tunneled_l2o4(self, src_if, capture, sent, tunnel_src, tunnel_dst): self.assertEqual(len(capture), len(sent)) for i in range(len(capture)): try: tx = sent[i] rx = capture[i] tx_ip = tx[IP] rx_ip = rx[IP] self.assertEqual(rx_ip.src, tunnel_src) self.assertEqual(rx_ip.dst, tunnel_dst) rx_gre = rx[GRE] rx_l2 = rx_gre[Ether] rx_ip = rx_l2[IP] tx_gre = tx[GRE] tx_l2 = tx_gre[Ether] tx_ip = tx_l2[IP] self.assertEqual(rx_ip.src, tx_ip.src) self.assertEqual(rx_ip.dst, tx_ip.dst) # bridged, not L3 forwarded, so no TTL decrement self.assertEqual(rx_ip.ttl, tx_ip.ttl) except: self.logger.error(ppp("Rx:", rx)) self.logger.error(ppp("Tx:", tx)) raise def verify_tunneled_vlano4(self, src_if, capture, sent, tunnel_src, tunnel_dst, vlan): try: self.assertEqual(len(capture), len(sent)) except: ppc("Unexpected packets captured:", capture) raise for i in range(len(capture)): try: tx = sent[i] rx = capture[i] tx_ip = tx[IP] rx_ip = rx[IP] self.assertEqual(rx_ip.src, tunnel_src) self.assertEqual(rx_ip.dst, tunnel_dst) rx_gre = rx[GRE] rx_l2 = rx_gre[Ether] rx_vlan = rx_l2[Dot1Q] rx_ip = rx_l2[IP] self.assertEqual(rx_vlan.vlan, vlan) tx_gre = tx[GRE] tx_l2 = tx_gre[Ether] tx_ip = tx_l2[IP] self.assertEqual(rx_ip.src, tx_ip.src) self.assertEqual(rx_ip.dst, tx_ip.dst) # bridged, not L3 forwarded, so no TTL decrement self.assertEqual(rx_ip.ttl, tx_ip.ttl) except: self.logger.error(ppp("Rx:", rx)) self.logger.error(ppp("Tx:", tx)) raise def verify_decapped_4o4(self, src_if, capture, sent): self.assertEqual(len(capture), len(sent)) for i in range(len(capture)): try: tx = sent[i] rx = capture[i] tx_ip = tx[IP] rx_ip = rx[IP] tx_gre = tx[GRE] tx_ip = tx_gre[IP] self.assertEqual(rx_ip.src, tx_ip.src) self.assertEqual(rx_ip.dst, tx_ip.dst) # IP processing post pop has decremented the TTL self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl) except: self.logger.error(ppp("Rx:", rx)) self.logger.error(ppp("Tx:", tx)) raise def verify_decapped_6o4(self, src_if, capture, sent): self.assertEqual(len(capture), len(sent)) for i in range(len(capture)): try: tx = sent[i] rx = capture[i] tx_ip = tx[IP] rx_ip = rx[IPv6] tx_gre = tx[GRE] tx_ip = tx_gre[IPv6] self.assertEqual(rx_ip.src, tx_ip.src) self.assertEqual(rx_ip.dst, tx_ip.dst) self.assertEqual(rx_ip.hlim + 1, tx_ip.hlim) except: self.logger.error(ppp("Rx:", rx)) self.logger.error(ppp("Tx:", tx)) raise def test_gre(self): """ GRE IPv4 tunnel Tests """ # # Create an L3 GRE tunnel. # - set it admin up # - assign an IP Addres # - Add a route via the tunnel # gre_if = VppGreInterface(self, self.pg0.local_ip4, "1.1.1.2") gre_if.add_vpp_config() # # The double create (create the same tunnel twice) should fail, # and we should still be able to use the original # try: gre_if.add_vpp_config() except Exception: pass else: self.fail("Double GRE tunnel add does not fail") gre_if.admin_up() gre_if.config_ip4() route_via_tun = VppIpRoute(self, "4.4.4.4", 32, [VppRoutePath("0.0.0.0", gre_if.sw_if_index)]) route_via_tun.add_vpp_config() # # Send a packet stream that is routed into the tunnel # - they are all dropped since the tunnel's destintation IP # is unresolved - or resolves via the default route - which # which is a drop. # tx = self.create_stream_ip4(self.pg0, "5.5.5.5", "4.4.4.4") self.send_and_assert_no_replies(self.pg0, tx) # # Add a route that resolves the tunnel's destination # route_tun_dst = VppIpRoute(self, "1.1.1.2", 32, [VppRoutePath(self.pg0.remote_ip4, self.pg0.sw_if_index)]) route_tun_dst.add_vpp_config() # # Send a packet stream that is routed into the tunnel # - packets are GRE encapped # tx = self.create_stream_ip4(self.pg0, "5.5.5.5", "4.4.4.4") rx = self.send_and_expect(self.pg0, tx, self.pg0) self.verify_tunneled_4o4(self.pg0, rx, tx, self.pg0.local_ip4, "1.1.1.2") # # Send tunneled packets that match the created tunnel and # are decapped and forwarded # tx = self.create_tunnel_stream_4o4(self.pg0, "1.1.1.2", self.pg0.local_ip4, self.pg0.local_ip4, self.pg0.remote_ip4) rx = self.send_and_expect(self.pg0, tx, self.pg0) self.verify_decapped_4o4(self.pg0, rx, tx) # # Send tunneled packets that do not match the tunnel's src # self.vapi.cli("clear trace") tx = self.create_tunnel_stream_4o4(self.pg0, "1.1.1.3", self.pg0.local_ip4, self.pg0.local_ip4, self.pg0.remote_ip4) self.send_and_assert_no_replies( self.pg0, tx, remark="GRE packets forwarded despite no SRC address match") # # Configure IPv6 on the PG interface so we can route IPv6 # packets # self.pg0.config_ip6() self.pg0.resolve_ndp() # # Send IPv6 tunnel encapslated packets # - dropped since IPv6 is not enabled on the tunnel # tx = self.create_tunnel_stream_6o4(self.pg0, "1.1.1.2", self.pg0.local_ip4, self.pg0.local_ip6, self.pg0.remote_ip6) self.send_and_assert_no_replies(self.pg0, tx, "IPv6 GRE packets forwarded " "despite IPv6 not enabled on tunnel") # # Enable IPv6 on the tunnel # gre_if.config_ip6() # # Send IPv6 tunnel encapslated packets # - forwarded since IPv6 is enabled on the tunnel # tx = self.create_tunnel_stream_6o4(self.pg0, "1.1.1.2", self.pg0.local_ip4, self.pg0.local_ip6, self.pg0.remote_ip6) rx = self.send_and_expect(self.pg0, tx, self.pg0) self.verify_decapped_6o4(self.pg0, rx, tx) # # Send v6 packets for v4 encap # route6_via_tun = VppIpRoute( self, "2001::1", 128, [VppRoutePath("::", gre_if.sw_if_index, proto=DpoProto.DPO_PROTO_IP6)], is_ip6=1) route6_via_tun.add_vpp_config() tx = self.create_stream_ip6(self.pg0, "2001::2", "2001::1") rx = self.send_and_expect(self.pg0, tx, self.pg0) self.verify_tunneled_6o4(self.pg0, rx, tx, self.pg0.local_ip4, "1.1.1.2") # # test case cleanup # route_tun_dst.remove_vpp_config() route_via_tun.remove_vpp_config() route6_via_tun.remove_vpp_config() gre_if.remove_vpp_config() self.pg0.unconfig_ip6() def test_gre6(self): """ GRE IPv6 tunnel Tests """ self.pg1.config_ip6() self.pg1.resolve_ndp() # # Create an L3 GRE tunnel. # - set it admin up # - assign an IP Address # - Add a route via the tunnel # gre_if = VppGreInterface(self, self.pg2.local_ip6, "1002::1") gre_if.add_vpp_config() gre_if.admin_up() gre_if.config_ip6() route_via_tun = VppIpRoute( self, "4004::1", 128, [VppRoutePath("0::0", gre_if.sw_if_index, proto=DpoProto.DPO_PROTO_IP6)], is_ip6=1) route_via_tun.add_vpp_config() # # Send a packet stream that is routed into the tunnel # - they are all dropped since the tunnel's destintation IP # is unresolved - or resolves via the default route - which # which is a drop. # tx = self.create_stream_ip6(self.pg2, "5005::1", "4004::1") self.send_and_assert_no_replies( self.pg2, tx, "GRE packets forwarded without DIP resolved") # # Add a route that resolves the tunnel's destination # route_tun_dst = VppIpRoute( self, "1002::1", 128, [VppRoutePath(self.pg2.remote_ip6, self.pg2.sw_if_index, proto=DpoProto.DPO_PROTO_IP6)], is_ip6=1) route_tun_dst.add_vpp_config() # # Send a packet stream that is routed into the tunnel # - packets are GRE encapped # tx = self.create_stream_ip6(self.pg2, "5005::1", "4004::1") rx = self.send_and_expect(self.pg2, tx, self.pg2) self.verify_tunneled_6o6(self.pg2, rx, tx, self.pg2.local_ip6, "1002::1") # # Test decap. decapped packets go out pg1 # tx = self.create_tunnel_stream_6o6(self.pg2, "1002::1", self.pg2.local_ip6, "2001::1", self.pg1.remote_ip6) rx = self.send_and_expect(self.pg2, tx, self.pg1) # # RX'd packet is UDP over IPv6, test the GRE header is gone. # self.assertFalse(rx[0].haslayer(GRE)) self.assertEqual(rx[0][IPv6].dst, self.pg1.remote_ip6) # # Send v4 over v6 # route4_via_tun = VppIpRoute(self, "1.1.1.1", 32, [VppRoutePath("0.0.0.0", gre_if.sw_if_index)]) route4_via_tun.add_vpp_config() tx = self.create_stream_ip4(self.pg0, "1.1.1.2", "1.1.1.1") rx = self.send_and_expect(self.pg0, tx, self.pg2) self.verify_tunneled_4o6(self.pg0, rx, tx, self.pg2.local_ip6, "1002::1") # # test case cleanup # route_tun_dst.remove_vpp_config() route_via_tun.remove_vpp_config() route4_via_tun.remove_vpp_config() gre_if.remove_vpp_config() self.pg2.unconfig_ip6() self.pg1.unconfig_ip6() def test_gre_vrf(self): """ GRE tunnel VRF Tests """ # # Create an L3 GRE tunnel whose destination is in the non-default # table. The underlay is thus non-default - the overlay is still # the default. # - set it admin up # - assign an IP Addres # gre_if = VppGreInterface(self, self.pg1.local_ip4, "2.2.2.2", outer_fib_id=1) gre_if.add_vpp_config() gre_if.admin_up() gre_if.config_ip4() # # Add a route via the tunnel - in the overlay # route_via_tun = VppIpRoute(self, "9.9.9.9", 32, [VppRoutePath("0.0.0.0", gre_if.sw_if_index)]) route_via_tun.add_vpp_config() # # Add a route that resolves the tunnel's destination - in the # underlay table # route_tun_dst = VppIpRoute(self, "2.2.2.2", 32, table_id=1, paths=[VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)]) route_tun_dst.add_vpp_config() # # Send a packet stream that is routed into the tunnel # packets are sent in on pg0 which is in the default table # - packets are GRE encapped # self.vapi.cli("clear trace") tx = self.create_stream_ip4(self.pg0, "5.5.5.5", "9.9.9.9") rx = self.send_and_expect(self.pg0, tx, self.pg1) self.verify_tunneled_4o4(self.pg1, rx, tx, self.pg1.local_ip4, "2.2.2.2") # # Send tunneled packets that match the created tunnel and # are decapped and forwarded. This tests the decap lookup # does not happen in the encap table # self.vapi.cli("clear trace") tx = self.create_tunnel_stream_4o4(self.pg1, "2.2.2.2", self.pg1.local_ip4, self.pg0.local_ip4, self.pg0.remote_ip4) rx = self.send_and_expect(self.pg1, tx, self.pg0) self.verify_decapped_4o4(self.pg0, rx, tx) # # Send tunneled packets that match the created tunnel # but arrive on an interface that is not in the tunnel's # encap VRF, these are dropped. # IP enable the interface so they aren't dropped due to # IP not being enabled. # self.pg2.config_ip4() self.vapi.cli("clear trace") tx = self.create_tunnel_stream_4o4(self.pg2, "2.2.2.2", self.pg1.local_ip4, self.pg0.local_ip4, self.pg0.remote_ip4) rx = self.send_and_assert_no_replies( self.pg2, tx, "GRE decap packets in wrong VRF") self.pg2.unconfig_ip4() # # test case cleanup # route_tun_dst.remove_vpp_config() route_via_tun.remove_vpp_config() gre_if.remove_vpp_config() def test_gre_l2(self): """ GRE tunnel L2 Tests """ # # Add routes to resolve the tunnel destinations # route_tun1_dst = VppIpRoute(self, "2.2.2.2", 32, [VppRoutePath(self.pg0.remote_ip4, self.pg0.sw_if_index)]) route_tun2_dst = VppIpRoute(self, "2.2.2.3", 32, [VppRoutePath(self.pg0.remote_ip4, self.pg0.sw_if_index)]) route_tun1_dst.add_vpp_config() route_tun2_dst.add_vpp_config() # # Create 2 L2 GRE tunnels and x-connect them # gre_if1 = VppGreInterface(self, self.pg0.local_ip4, "2.2.2.2", type=(VppEnum.vl_api_gre_tunnel_type_t. GRE_API_TUNNEL_TYPE_TEB)) gre_if2 = VppGreInterface(self, self.pg0.local_ip4, "2.2.2.3", type=(VppEnum.vl_api_gre_tunnel_type_t. GRE_API_TUNNEL_TYPE_TEB)) gre_if1.add_vpp_config() gre_if2.add_vpp_config() gre_if1.admin_up() gre_if2.admin_up() self.vapi.sw_interface_set_l2_xconnect(gre_if1.sw_if_index, gre_if2.sw_if_index, enable=1) self.vapi.sw_interface_set_l2_xconnect(gre_if2.sw_if_index, gre_if1.sw_if_index, enable=1) # # Send in tunnel encapped L2. expect out tunnel encapped L2 # in both directions # tx = self.create_tunnel_stream_l2o4(self.pg0, "2.2.2.2", self.pg0.local_ip4) rx = self.send_and_expect(self.pg0, tx, self.pg0) self.verify_tunneled_l2o4(self.pg0, rx, tx, self.pg0.local_ip4, "2.2.2.3") tx = self.create_tunnel_stream_l2o4(self.pg0, "2.2.2.3", self.pg0.local_ip4) rx = self.send_and_expect(self.pg0, tx, self.pg0) self.verify_tunneled_l2o4(self.pg0, rx, tx, self.pg0.local_ip4, "2.2.2.2") self.vapi.sw_interface_set_l2_xconnect(gre_if1.sw_if_index, gre_if2.sw_if_index, enable=0) self.vapi.sw_interface_set_l2_xconnect(gre_if2.sw_if_index, gre_if1.sw_if_index, enable=0) # # Create a VLAN sub-interfaces on the GRE TEB interfaces # then x-connect them # gre_if_11 = VppDot1QSubint(self, gre_if1, 11) gre_if_12 = VppDot1QSubint(self, gre_if2, 12) # gre_if_11.add_vpp_config() # gre_if_12.add_vpp_config() gre_if_11.admin_up() gre_if_12.admin_up() self.vapi.sw_interface_set_l2_xconnect(gre_if_11.sw_if_index, gre_if_12.sw_if_index, enable=1) self.vapi.sw_interface_set_l2_xconnect(gre_if_12.sw_if_index, gre_if_11.sw_if_index, enable=1) # # Configure both to pop thier respective VLAN tags, # so that during the x-coonect they will subsequently push # self.vapi.l2_interface_vlan_tag_rewrite( sw_if_index=gre_if_12.sw_if_index, vtr_op=L2_VTR_OP.L2_POP_1, push_dot1q=12) self.vapi.l2_interface_vlan_tag_rewrite( sw_if_index=gre_if_11.sw_if_index, vtr_op=L2_VTR_OP.L2_POP_1, push_dot1q=11) # # Send traffic in both directiond - expect the VLAN tags to # be swapped. # tx = self.create_tunnel_stream_vlano4(self.pg0, "2.2.2.2", self.pg0.local_ip4, 11) rx = self.send_and_expect(self.pg0, tx, self.pg0) self.verify_tunneled_vlano4(self.pg0, rx, tx, self.pg0.local_ip4, "2.2.2.3", 12) tx = self.create_tunnel_stream_vlano4(self.pg0, "2.2.2.3", self.pg0.local_ip4, 12) rx = self.send_and_expect(self.pg0, tx, self.pg0) self.verify_tunneled_vlano4(self.pg0, rx, tx, self.pg0.local_ip4, "2.2.2.2", 11) # # Cleanup Test resources # gre_if_11.remove_vpp_config() gre_if_12.remove_vpp_config() gre_if1.remove_vpp_config() gre_if2.remove_vpp_config() route_tun1_dst.add_vpp_config() route_tun2_dst.add_vpp_config() def test_gre_loop(self): """ GRE tunnel loop Tests """ # # Create an L3 GRE tunnel. # - set it admin up # - assign an IP Addres # gre_if = VppGreInterface(self, self.pg0.local_ip4, "1.1.1.2") gre_if.add_vpp_config() gre_if.admin_up() gre_if.config_ip4() # # add a route to the tunnel's destination that points # through the tunnel, hence forming a loop in the forwarding # graph # route_dst = VppIpRoute(self, "1.1.1.2", 32, [VppRoutePath("0.0.0.0", gre_if.sw_if_index)]) route_dst.add_vpp_config() # # packets to the tunnels destination should be dropped # tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "1.1.1.2") self.send_and_assert_no_replies(self.pg2, tx) self.logger.info(self.vapi.ppcli("sh adj 7")) # # break the loop # route_dst.modify([VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)]) route_dst.add_vpp_config() rx = self.send_and_expect(self.pg0, tx, self.pg1) # # a good route throught the tunnel to check it restacked # route_via_tun_2 = VppIpRoute(self, "2.2.2.2", 32, [VppRoutePath("0.0.0.0", gre_if.sw_if_index)]) route_via_tun_2.add_vpp_config() tx = self.create_stream_ip4(self.pg0, "2.2.2.3", "2.2.2.2") rx = self.send_and_expect(self.pg0, tx, self.pg1) self.verify_tunneled_4o4(self.pg1, rx, tx, self.pg0.local_ip4, "1.1.1.2") # # cleanup # route_via_tun_2.remove_vpp_config() gre_if.remove_vpp_config()
def test_abf4(self): """ IPv4 ACL Based Forwarding """ # # We are not testing the various matching capabilities # of ACLs, that's done elsewhere. Here ware are testing # the application of ACLs to a forwarding path to achieve # ABF # So we construct just a few ACLs to ensure the ABF policies # are correclty constructed and used. And a few path types # to test the API path decoding. # # # Rule 1 # rule_1 = ({'is_permit': 1, 'is_ipv6': 0, 'proto': 17, 'srcport_or_icmptype_first': 1234, 'srcport_or_icmptype_last': 1234, 'src_ip_prefix_len': 32, 'src_ip_addr': inet_pton(AF_INET, "1.1.1.1"), 'dstport_or_icmpcode_first': 1234, 'dstport_or_icmpcode_last': 1234, 'dst_ip_prefix_len': 32, 'dst_ip_addr': inet_pton(AF_INET, "1.1.1.2")}) acl_1 = self.vapi.acl_add_replace(acl_index=4294967295, r=[rule_1]) # # ABF policy for ACL 1 - path via interface 1 # abf_1 = VppAbfPolicy(self, 10, acl_1, [VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)]) abf_1.add_vpp_config() # # Attach the policy to input interface Pg0 # attach_1 = VppAbfAttach(self, 10, self.pg0.sw_if_index, 50) attach_1.add_vpp_config() # # fire in packet matching the ACL src,dst. If it's forwarded # then the ABF was successful, since default routing will drop it # p_1 = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IP(src="1.1.1.1", dst="1.1.1.2") / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) self.send_and_expect(self.pg0, p_1*65, self.pg1) # # Attach a 'better' priority policy to the same interface # abf_2 = VppAbfPolicy(self, 11, acl_1, [VppRoutePath(self.pg2.remote_ip4, self.pg2.sw_if_index)]) abf_2.add_vpp_config() attach_2 = VppAbfAttach(self, 11, self.pg0.sw_if_index, 40) attach_2.add_vpp_config() self.send_and_expect(self.pg0, p_1*65, self.pg2) # # Attach a policy with priority in the middle # abf_3 = VppAbfPolicy(self, 12, acl_1, [VppRoutePath(self.pg3.remote_ip4, self.pg3.sw_if_index)]) abf_3.add_vpp_config() attach_3 = VppAbfAttach(self, 12, self.pg0.sw_if_index, 45) attach_3.add_vpp_config() self.send_and_expect(self.pg0, p_1*65, self.pg2) # # remove the best priority # attach_2.remove_vpp_config() self.send_and_expect(self.pg0, p_1*65, self.pg3) # # Attach one of the same policies to Pg1 # attach_4 = VppAbfAttach(self, 12, self.pg1.sw_if_index, 45) attach_4.add_vpp_config() p_2 = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / IP(src="1.1.1.1", dst="1.1.1.2") / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) self.send_and_expect(self.pg1, p_2 * 65, self.pg3) # # detach the policy from PG1, now expect traffic to be dropped # attach_4.remove_vpp_config() self.send_and_assert_no_replies(self.pg1, p_2 * 65, "Detached") # # Swap to route via a next-hop in the non-default table # table_20 = VppIpTable(self, 20) table_20.add_vpp_config() self.pg4.set_table_ip4(table_20.table_id) self.pg4.admin_up() self.pg4.config_ip4() self.pg4.resolve_arp() abf_13 = VppAbfPolicy(self, 13, acl_1, [VppRoutePath(self.pg4.remote_ip4, 0xffffffff, nh_table_id=table_20.table_id)]) abf_13.add_vpp_config() attach_5 = VppAbfAttach(self, 13, self.pg0.sw_if_index, 30) attach_5.add_vpp_config() self.send_and_expect(self.pg0, p_1*65, self.pg4) self.pg4.unconfig_ip4() self.pg4.set_table_ip4(0)
def test_abf4(self): """ IPv4 ACL Based Forwarding """ # # We are not testing the various matching capabilities # of ACLs, that's done elsewhere. Here ware are testing # the application of ACLs to a forwarding path to achieve # ABF # So we construct just a few ACLs to ensure the ABF policies # are correctly constructed and used. And a few path types # to test the API path decoding. # # # Rule 1 # rule_1 = AclRule(is_permit=1, proto=17, ports=1234, src_prefix=IPv4Network("1.1.1.1/32"), dst_prefix=IPv4Network("1.1.1.2/32")) acl_1 = VppAcl(self, rules=[rule_1]) acl_1.add_vpp_config() # # ABF policy for ACL 1 - path via interface 1 # abf_1 = VppAbfPolicy(self, 10, acl_1, [VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)]) abf_1.add_vpp_config() # # Attach the policy to input interface Pg0 # attach_1 = VppAbfAttach(self, 10, self.pg0.sw_if_index, 50) attach_1.add_vpp_config() # # fire in packet matching the ACL src,dst. If it's forwarded # then the ABF was successful, since default routing will drop it # p_1 = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IP(src="1.1.1.1", dst="1.1.1.2") / UDP(sport=1234, dport=1234) / Raw(b'\xa5' * 100)) self.send_and_expect(self.pg0, p_1*NUM_PKTS, self.pg1) # # Attach a 'better' priority policy to the same interface # abf_2 = VppAbfPolicy(self, 11, acl_1, [VppRoutePath(self.pg2.remote_ip4, self.pg2.sw_if_index)]) abf_2.add_vpp_config() attach_2 = VppAbfAttach(self, 11, self.pg0.sw_if_index, 40) attach_2.add_vpp_config() self.send_and_expect(self.pg0, p_1*NUM_PKTS, self.pg2) # # Attach a policy with priority in the middle # abf_3 = VppAbfPolicy(self, 12, acl_1, [VppRoutePath(self.pg3.remote_ip4, self.pg3.sw_if_index)]) abf_3.add_vpp_config() attach_3 = VppAbfAttach(self, 12, self.pg0.sw_if_index, 45) attach_3.add_vpp_config() self.send_and_expect(self.pg0, p_1*NUM_PKTS, self.pg2) # # remove the best priority # attach_2.remove_vpp_config() self.send_and_expect(self.pg0, p_1*NUM_PKTS, self.pg3) # # Attach one of the same policies to Pg1 # attach_4 = VppAbfAttach(self, 12, self.pg1.sw_if_index, 45) attach_4.add_vpp_config() p_2 = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / IP(src="1.1.1.1", dst="1.1.1.2") / UDP(sport=1234, dport=1234) / Raw(b'\xa5' * 100)) self.send_and_expect(self.pg1, p_2 * NUM_PKTS, self.pg3) # # detach the policy from PG1, now expect traffic to be dropped # attach_4.remove_vpp_config() self.send_and_assert_no_replies(self.pg1, p_2 * NUM_PKTS, "Detached") # # Swap to route via a next-hop in the non-default table # table_20 = VppIpTable(self, 20) table_20.add_vpp_config() self.pg4.set_table_ip4(table_20.table_id) self.pg4.admin_up() self.pg4.config_ip4() self.pg4.resolve_arp() abf_13 = VppAbfPolicy(self, 13, acl_1, [VppRoutePath(self.pg4.remote_ip4, 0xffffffff, nh_table_id=table_20.table_id)]) abf_13.add_vpp_config() attach_5 = VppAbfAttach(self, 13, self.pg0.sw_if_index, 30) attach_5.add_vpp_config() self.send_and_expect(self.pg0, p_1*NUM_PKTS, self.pg4) self.pg4.unconfig_ip4() self.pg4.set_table_ip4(0)
class TestIgmp(VppTestCase): """ IGMP Test Case """ @classmethod def setUpClass(cls): super(TestIgmp, cls).setUpClass() @classmethod def tearDownClass(cls): super(TestIgmp, cls).tearDownClass() def setUp(self): super(TestIgmp, self).setUp() self.create_pg_interfaces(range(4)) self.sg_list = [] self.config_list = [] self.ip_addr = [] self.ip_table = VppIpTable(self, 1) self.ip_table.add_vpp_config() for pg in self.pg_interfaces[2:]: pg.set_table_ip4(1) for pg in self.pg_interfaces: pg.admin_up() pg.config_ip4() pg.resolve_arp() def tearDown(self): for pg in self.pg_interfaces: self.vapi.igmp_clear_interface(pg.sw_if_index) pg.unconfig_ip4() pg.set_table_ip4(0) pg.admin_down() super(TestIgmp, self).tearDown() def send(self, ti, pkts): ti.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() def test_igmp_flush(self): """ IGMP Link Up/down and Flush """ # # FIX THIS. Link down. # def test_igmp_enable(self): """ IGMP enable/disable on an interface check for the addition/removal of the IGMP mroutes """ self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.HOST) self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 1, IGMP_MODE.HOST) self.assertTrue(find_mroute(self, "224.0.0.1", "0.0.0.0", 32)) self.assertTrue(find_mroute(self, "224.0.0.22", "0.0.0.0", 32)) self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 1, IGMP_MODE.HOST) self.vapi.igmp_enable_disable(self.pg3.sw_if_index, 1, IGMP_MODE.HOST) self.assertTrue(find_mroute(self, "224.0.0.1", "0.0.0.0", 32, table_id=1)) self.assertTrue(find_mroute(self, "224.0.0.22", "0.0.0.0", 32, table_id=1)) self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.HOST) self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 0, IGMP_MODE.HOST) self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 0, IGMP_MODE.HOST) self.vapi.igmp_enable_disable(self.pg3.sw_if_index, 0, IGMP_MODE.HOST) self.assertFalse(find_mroute(self, "224.0.0.1", "0.0.0.0", 32)) self.assertFalse(find_mroute(self, "224.0.0.22", "0.0.0.0", 32)) self.assertFalse(find_mroute(self, "224.0.0.1", "0.0.0.0", 32, table_id=1)) self.assertFalse(find_mroute(self, "224.0.0.22", "0.0.0.0", 32, table_id=1)) def verify_general_query(self, p): ip = p[IP] self.assertEqual(len(ip.options), 1) self.assertEqual(ip.options[0].option, 20) self.assertEqual(ip.dst, "224.0.0.1") self.assertEqual(ip.proto, 2) igmp = p[IGMPv3] self.assertEqual(igmp.type, 0x11) self.assertEqual(igmp.gaddr, "0.0.0.0") def verify_group_query(self, p, grp, srcs): ip = p[IP] self.assertEqual(ip.dst, grp) self.assertEqual(ip.proto, 2) self.assertEqual(len(ip.options), 1) self.assertEqual(ip.options[0].option, 20) self.assertEqual(ip.proto, 2) igmp = p[IGMPv3] self.assertEqual(igmp.type, 0x11) self.assertEqual(igmp.gaddr, grp) def verify_report(self, rx, records): ip = rx[IP] self.assertEqual(rx[IP].dst, "224.0.0.22") self.assertEqual(len(ip.options), 1) self.assertEqual(ip.options[0].option, 20) self.assertEqual(ip.proto, 2) self.assertEqual(IGMPv3.igmpv3types[rx[IGMPv3].type], "Version 3 Membership Report") self.assertEqual(rx[IGMPv3mr].numgrp, len(records)) received = rx[IGMPv3mr].records for ii in range(len(records)): gr = received[ii] r = records[ii] self.assertEqual(IGMPv3gr.igmpv3grtypes[gr.rtype], r.type) self.assertEqual(gr.numsrc, len(r.sg.saddrs)) self.assertEqual(gr.maddr, r.sg.gaddr) self.assertEqual(len(gr.srcaddrs), len(r.sg.saddrs)) self.assertEqual(sorted(gr.srcaddrs), sorted(r.sg.saddrs)) def add_group(self, itf, sg, n_pkts=2): self.pg_enable_capture(self.pg_interfaces) self.pg_start() hs = VppHostState(self, IGMP_FILTER.INCLUDE, itf.sw_if_index, sg) hs.add_vpp_config() capture = itf.get_capture(n_pkts, timeout=10) # reports are transmitted twice due to default rebostness value=2 self.verify_report(capture[0], [IgmpRecord(sg, "Allow New Sources")]), self.verify_report(capture[1], [IgmpRecord(sg, "Allow New Sources")]), return hs def remove_group(self, hs): self.pg_enable_capture(self.pg_interfaces) self.pg_start() hs.remove_vpp_config() capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(hs.sg, "Block Old Sources")]) def test_igmp_host(self): """ IGMP Host functions """ # # Enable interface for host functions # self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.HOST) # # Add one S,G of state and expect a state-change event report # indicating the addition of the S,G # h1 = self.add_group(self.pg0, IgmpSG("239.1.1.1", ["1.1.1.1"])) # search for the corresponding state created in VPP dump = self.vapi.igmp_dump(self.pg0.sw_if_index) self.assertEqual(len(dump), 1) self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "1.1.1.1")) # # Send a general query (to the all router's address) # expect VPP to respond with a membership report # p_g = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='224.0.0.1', tos=0xc0) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="0.0.0.0")) self.send(self.pg0, p_g) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")]) # # Group specific query # p_gs = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='239.1.1.1', tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="239.1.1.1")) self.send(self.pg0, p_gs) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")]) # # A group and source specific query, with the source matching # the source VPP has # p_gs1 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='239.1.1.1', tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.1"])) self.send(self.pg0, p_gs1) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")]) # # A group and source specific query, with the source NOT matching # the source VPP has. There should be no response. # p_gs2 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='239.1.1.1', tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.2"])) self.send_and_assert_no_replies(self.pg0, p_gs2, timeout=10) # # A group and source specific query, with the multiple sources # one of which matches the source VPP has. # The report should contain only the source VPP has. # p_gs3 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='239.1.1.1', tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.1", "1.1.1.2", "1.1.1.3"])) self.send(self.pg0, p_gs3) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")]) # # Two source and group specific queries in quick succession, the # first does not have VPPs source the second does. then vice-versa # self.send(self.pg0, [p_gs2, p_gs1]) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")]) self.send(self.pg0, [p_gs1, p_gs2]) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")]) # # remove state, expect the report for the removal # self.remove_group(h1) dump = self.vapi.igmp_dump() self.assertFalse(dump) # # A group with multiple sources # h2 = self.add_group(self.pg0, IgmpSG("239.1.1.1", ["1.1.1.1", "1.1.1.2", "1.1.1.3"])) # search for the corresponding state created in VPP dump = self.vapi.igmp_dump(self.pg0.sw_if_index) self.assertEqual(len(dump), 3) for s in h2.sg.saddrs: self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", s)) # # Send a general query (to the all router's address) # expect VPP to respond with a membership report will all sources # self.send(self.pg0, p_g) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h2.sg, "Mode Is Include")]) # # Group and source specific query; some present some not # p_gs = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='239.1.1.1', tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.1", "1.1.1.2", "1.1.1.4"])) self.send(self.pg0, p_gs) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord( IgmpSG('239.1.1.1', ["1.1.1.1", "1.1.1.2"]), "Mode Is Include")]) # # add loads more groups # h3 = self.add_group(self.pg0, IgmpSG("239.1.1.2", ["2.1.1.1", "2.1.1.2", "2.1.1.3"])) h4 = self.add_group(self.pg0, IgmpSG("239.1.1.3", ["3.1.1.1", "3.1.1.2", "3.1.1.3"])) h5 = self.add_group(self.pg0, IgmpSG("239.1.1.4", ["4.1.1.1", "4.1.1.2", "4.1.1.3"])) h6 = self.add_group(self.pg0, IgmpSG("239.1.1.5", ["5.1.1.1", "5.1.1.2", "5.1.1.3"])) h7 = self.add_group(self.pg0, IgmpSG("239.1.1.6", ["6.1.1.1", "6.1.1.2", "6.1.1.3", "6.1.1.4", "6.1.1.5", "6.1.1.6", "6.1.1.7", "6.1.1.8", "6.1.1.9", "6.1.1.10", "6.1.1.11", "6.1.1.12", "6.1.1.13", "6.1.1.14", "6.1.1.15", "6.1.1.16"])) # # general query. # the order the groups come in is not important, so what is # checked for is what VPP is sending today. # self.send(self.pg0, p_g) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h3.sg, "Mode Is Include"), IgmpRecord(h2.sg, "Mode Is Include"), IgmpRecord(h6.sg, "Mode Is Include"), IgmpRecord(h4.sg, "Mode Is Include"), IgmpRecord(h5.sg, "Mode Is Include"), IgmpRecord(h7.sg, "Mode Is Include")]) # # modify a group to add and remove some sources # h7.sg = IgmpSG("239.1.1.6", ["6.1.1.1", "6.1.1.2", "6.1.1.5", "6.1.1.6", "6.1.1.7", "6.1.1.8", "6.1.1.9", "6.1.1.10", "6.1.1.11", "6.1.1.12", "6.1.1.13", "6.1.1.14", "6.1.1.15", "6.1.1.16", "6.1.1.17", "6.1.1.18"]) self.pg_enable_capture(self.pg_interfaces) self.pg_start() h7.add_vpp_config() capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(IgmpSG("239.1.1.6", ["6.1.1.17", "6.1.1.18"]), "Allow New Sources"), IgmpRecord(IgmpSG("239.1.1.6", ["6.1.1.3", "6.1.1.4"]), "Block Old Sources")]) # # add an additional groups with many sources so that each group # consumes the link MTU. We should therefore see multiple state # state reports when queried. # self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [560, 0, 0, 0]) src_list = [] for i in range(128): src_list.append("10.1.1.%d" % i) h8 = self.add_group(self.pg0, IgmpSG("238.1.1.1", src_list)) h9 = self.add_group(self.pg0, IgmpSG("238.1.1.2", src_list)) self.send(self.pg0, p_g) capture = self.pg0.get_capture(4, timeout=10) self.verify_report(capture[0], [IgmpRecord(h3.sg, "Mode Is Include"), IgmpRecord(h2.sg, "Mode Is Include"), IgmpRecord(h6.sg, "Mode Is Include"), IgmpRecord(h4.sg, "Mode Is Include"), IgmpRecord(h5.sg, "Mode Is Include")]) self.verify_report(capture[1], [IgmpRecord(h8.sg, "Mode Is Include")]) self.verify_report(capture[2], [IgmpRecord(h7.sg, "Mode Is Include")]) self.verify_report(capture[3], [IgmpRecord(h9.sg, "Mode Is Include")]) # # drop the MTU further (so a 128 sized group won't fit) # self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [512, 0, 0, 0]) self.pg_enable_capture(self.pg_interfaces) self.pg_start() h10 = VppHostState(self, IGMP_FILTER.INCLUDE, self.pg0.sw_if_index, IgmpSG("238.1.1.3", src_list)) h10.add_vpp_config() capture = self.pg0.get_capture(2, timeout=10) # # remove state, expect the report for the removal # the dump should be empty # self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [600, 0, 0, 0]) self.remove_group(h8) self.remove_group(h9) self.remove_group(h2) self.remove_group(h3) self.remove_group(h4) self.remove_group(h5) self.remove_group(h6) self.remove_group(h7) self.remove_group(h10) self.logger.info(self.vapi.cli("sh igmp config")) self.assertFalse(self.vapi.igmp_dump()) # # TODO # ADD STATE ON MORE INTERFACES # self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.HOST) def test_igmp_router(self): """ IGMP Router Functions """ # # Drop reports when not enabled # p_j = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Version 3 Membership Report") / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype="Allow New Sources", maddr="239.1.1.1", srcaddrs=["10.1.1.1", "10.1.1.2"])) p_l = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Version 3 Membership Report") / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype="Block Old Sources", maddr="239.1.1.1", srcaddrs=["10.1.1.1", "10.1.1.2"])) self.send(self.pg0, p_j) self.assertFalse(self.vapi.igmp_dump()) # # drop the default timer values so these tests execute in a # reasonable time frame # self.vapi.cli("test igmp timers query 1 src 3 leave 1") # # enable router functions on the interface # self.pg_enable_capture(self.pg_interfaces) self.pg_start() self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.ROUTER) self.vapi.want_igmp_events(1) # # wait for router to send general query # for ii in range(3): capture = self.pg0.get_capture(1, timeout=2) self.verify_general_query(capture[0]) self.pg_enable_capture(self.pg_interfaces) self.pg_start() # # re-send the report. VPP should now hold state for the new group # VPP sends a notification that a new group has been joined # self.send(self.pg0, p_j) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.1", 1)) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 1)) dump = self.vapi.igmp_dump(self.pg0.sw_if_index) self.assertEqual(len(dump), 2) self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.1")) self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.2")) # # wait for the per-source timer to expire # the state should be reaped # VPP sends a notification that the group has been left # self.assertTrue(wait_for_igmp_event(self, 4, self.pg0, "239.1.1.1", "10.1.1.1", 0)) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 0)) self.assertFalse(self.vapi.igmp_dump()) # # resend the join. wait for two queries and then send a current-state # record to include all sources. this should reset the expiry time # on the sources and thus they will still be present in 2 seconds time. # If the source timer was not refreshed, then the state would have # expired in 3 seconds. # self.send(self.pg0, p_j) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.1", 1)) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 1)) dump = self.vapi.igmp_dump(self.pg0.sw_if_index) self.assertEqual(len(dump), 2) capture = self.pg0.get_capture(2, timeout=3) self.verify_general_query(capture[0]) self.verify_general_query(capture[1]) p_cs = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Version 3 Membership Report") / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype="Mode Is Include", maddr="239.1.1.1", srcaddrs=["10.1.1.1", "10.1.1.2"])) self.send(self.pg0, p_cs) self.sleep(2) dump = self.vapi.igmp_dump(self.pg0.sw_if_index) self.assertEqual(len(dump), 2) self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.1")) self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.2")) # # wait for the per-source timer to expire # the state should be reaped # self.assertTrue(wait_for_igmp_event(self, 4, self.pg0, "239.1.1.1", "10.1.1.1", 0)) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 0)) self.assertFalse(self.vapi.igmp_dump()) # # resend the join, then a leave. Router sends a group+source # specific query containing both sources # self.send(self.pg0, p_j) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.1", 1)) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 1)) dump = self.vapi.igmp_dump(self.pg0.sw_if_index) self.assertEqual(len(dump), 2) self.send(self.pg0, p_l) capture = self.pg0.get_capture(1, timeout=3) self.verify_group_query(capture[0], "239.1.1.1", ["10.1.1.1", "10.1.1.2"]) # # the group specific query drops the timeout to leave (=1) seconds # self.assertTrue(wait_for_igmp_event(self, 2, self.pg0, "239.1.1.1", "10.1.1.1", 0)) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 0)) self.assertFalse(self.vapi.igmp_dump()) self.assertFalse(self.vapi.igmp_dump()) # # a TO_EX({}) / IN_EX({}) is treated like a (*,G) join # p_j = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Version 3 Membership Report") / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype="Change To Exclude Mode", maddr="239.1.1.2")) self.send(self.pg0, p_j) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.2", "0.0.0.0", 1)) p_j = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Version 3 Membership Report") / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype="Mode Is Exclude", maddr="239.1.1.3")) self.send(self.pg0, p_j) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.3", "0.0.0.0", 1)) # # A 'allow sources' for {} should be ignored as it should # never be sent. # p_j = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Version 3 Membership Report") / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype="Allow New Sources", maddr="239.1.1.4")) self.send(self.pg0, p_j) dump = self.vapi.igmp_dump(self.pg0.sw_if_index) self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.2", "0.0.0.0")) self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.3", "0.0.0.0")) self.assertFalse(find_igmp_state(dump, self.pg0, "239.1.1.4", "0.0.0.0")) # # a TO_IN({}) and IS_IN({}) are treated like a (*,G) leave # self.vapi.cli("set logging class igmp level debug") p_l = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Version 3 Membership Report") / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype="Change To Include Mode", maddr="239.1.1.2")) self.send(self.pg0, p_l) self.assertTrue(wait_for_igmp_event(self, 2, self.pg0, "239.1.1.2", "0.0.0.0", 0)) p_l = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Version 3 Membership Report") / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype="Mode Is Include", maddr="239.1.1.3")) self.send(self.pg0, p_l) self.assertTrue(wait_for_igmp_event(self, 2, self.pg0, "239.1.1.3", "0.0.0.0", 0)) self.assertFalse(self.vapi.igmp_dump(self.pg0.sw_if_index)) # # disable router config # self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.ROUTER) def _create_igmpv3_pck(self, itf, rtype, maddr, srcaddrs): p = (Ether(dst=itf.local_mac, src=itf.remote_mac) / IP(src=itf.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Version 3 Membership Report") / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype=rtype, maddr=maddr, srcaddrs=srcaddrs)) return p def test_igmp_proxy_device(self): """ IGMP proxy device """ self.pg2.admin_down() self.pg2.unconfig_ip4() self.pg2.set_table_ip4(0) self.pg2.config_ip4() self.pg2.admin_up() self.vapi.cli('test igmp timers query 10 src 3 leave 1') # enable IGMP self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.HOST) self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 1, IGMP_MODE.ROUTER) self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 1, IGMP_MODE.ROUTER) # create IGMP proxy device self.vapi.igmp_proxy_device_add_del(0, self.pg0.sw_if_index, 1) self.vapi.igmp_proxy_device_add_del_interface(0, self.pg1.sw_if_index, 1) self.vapi.igmp_proxy_device_add_del_interface(0, self.pg2.sw_if_index, 1) # send join on pg1. join should be proxied by pg0 p_j = self._create_igmpv3_pck(self.pg1, "Allow New Sources", "239.1.1.1", ["10.1.1.1", "10.1.1.2"]) self.send(self.pg1, p_j) capture = self.pg0.get_capture(1, timeout=1) self.verify_report(capture[0], [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.1", "10.1.1.2"]), "Allow New Sources")]) self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32)) # send join on pg2. join should be proxied by pg0. # the group should contain only 10.1.1.3 as # 10.1.1.1 was already reported p_j = self._create_igmpv3_pck(self.pg2, "Allow New Sources", "239.1.1.1", ["10.1.1.1", "10.1.1.3"]) self.send(self.pg2, p_j) capture = self.pg0.get_capture(1, timeout=1) self.verify_report(capture[0], [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.3"]), "Allow New Sources")]) self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32)) # send leave on pg2. leave for 10.1.1.3 should be proxyed # as pg2 was the only interface interested in 10.1.1.3 p_l = self._create_igmpv3_pck(self.pg2, "Block Old Sources", "239.1.1.1", ["10.1.1.3"]) self.send(self.pg2, p_l) capture = self.pg0.get_capture(1, timeout=2) self.verify_report(capture[0], [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.3"]), "Block Old Sources")]) self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32)) # disable igmp on pg1 (also removes interface from proxy device) # proxy leave for 10.1.1.2. pg2 is still interested in 10.1.1.1 self.pg_enable_capture(self.pg_interfaces) self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 0, IGMP_MODE.ROUTER) capture = self.pg0.get_capture(1, timeout=1) self.verify_report(capture[0], [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.2"]), "Block Old Sources")]) self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32)) # disable IGMP on pg0 and pg1. # disabling IGMP on pg0 (proxy device upstream interface) # removes this proxy device self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.HOST) self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 0, IGMP_MODE.ROUTER) self.assertFalse(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
class TestGRE(VppTestCase): """ GRE Test Case """ @classmethod def setUpClass(cls): super(TestGRE, cls).setUpClass() @classmethod def tearDownClass(cls): super(TestGRE, cls).tearDownClass() def setUp(self): super(TestGRE, self).setUp() # create 3 pg interfaces - set one in a non-default table. self.create_pg_interfaces(range(3)) self.tbl = VppIpTable(self, 1) self.tbl.add_vpp_config() self.pg1.set_table_ip4(1) for i in self.pg_interfaces: i.admin_up() self.pg0.config_ip4() self.pg0.resolve_arp() self.pg1.config_ip4() self.pg1.resolve_arp() self.pg2.config_ip6() self.pg2.resolve_ndp() def tearDown(self): for i in self.pg_interfaces: i.unconfig_ip4() i.unconfig_ip6() i.admin_down() self.pg1.set_table_ip4(0) super(TestGRE, self).tearDown() def create_stream_ip4(self, src_if, src_ip, dst_ip): pkts = [] for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IP(src=src_ip, dst=dst_ip) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def create_stream_ip6(self, src_if, src_ip, dst_ip): pkts = [] for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IPv6(src=src_ip, dst=dst_ip) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def create_tunnel_stream_4o4(self, src_if, tunnel_src, tunnel_dst, src_ip, dst_ip): pkts = [] for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IP(src=tunnel_src, dst=tunnel_dst) / GRE() / IP(src=src_ip, dst=dst_ip) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def create_tunnel_stream_6o4(self, src_if, tunnel_src, tunnel_dst, src_ip, dst_ip): pkts = [] for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IP(src=tunnel_src, dst=tunnel_dst) / GRE() / IPv6(src=src_ip, dst=dst_ip) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def create_tunnel_stream_6o6(self, src_if, tunnel_src, tunnel_dst, src_ip, dst_ip): pkts = [] for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IPv6(src=tunnel_src, dst=tunnel_dst) / GRE() / IPv6(src=src_ip, dst=dst_ip) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def create_tunnel_stream_l2o4(self, src_if, tunnel_src, tunnel_dst): pkts = [] for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = ( Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IP(src=tunnel_src, dst=tunnel_dst) / GRE() / Ether(dst=RandMAC('*:*:*:*:*:*'), src=RandMAC('*:*:*:*:*:*')) / IP(src=scapy.compat.raw(RandIP()), dst=scapy.compat.raw(RandIP())) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def create_tunnel_stream_vlano4(self, src_if, tunnel_src, tunnel_dst, vlan): pkts = [] for i in range(0, 257): info = self.create_packet_info(src_if, src_if) payload = self.info_to_payload(info) p = ( Ether(dst=src_if.local_mac, src=src_if.remote_mac) / IP(src=tunnel_src, dst=tunnel_dst) / GRE() / Ether(dst=RandMAC('*:*:*:*:*:*'), src=RandMAC('*:*:*:*:*:*')) / Dot1Q(vlan=vlan) / IP(src=scapy.compat.raw(RandIP()), dst=scapy.compat.raw(RandIP())) / UDP(sport=1234, dport=1234) / Raw(payload)) info.data = p.copy() pkts.append(p) return pkts def verify_tunneled_4o4(self, src_if, capture, sent, tunnel_src, tunnel_dst): self.assertEqual(len(capture), len(sent)) for i in range(len(capture)): try: tx = sent[i] rx = capture[i] tx_ip = tx[IP] rx_ip = rx[IP] self.assertEqual(rx_ip.src, tunnel_src) self.assertEqual(rx_ip.dst, tunnel_dst) rx_gre = rx[GRE] rx_ip = rx_gre[IP] self.assertEqual(rx_ip.src, tx_ip.src) self.assertEqual(rx_ip.dst, tx_ip.dst) # IP processing post pop has decremented the TTL self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl) except: self.logger.error(ppp("Rx:", rx)) self.logger.error(ppp("Tx:", tx)) raise def verify_tunneled_6o6(self, src_if, capture, sent, tunnel_src, tunnel_dst): self.assertEqual(len(capture), len(sent)) for i in range(len(capture)): try: tx = sent[i] rx = capture[i] tx_ip = tx[IPv6] rx_ip = rx[IPv6] self.assertEqual(rx_ip.src, tunnel_src) self.assertEqual(rx_ip.dst, tunnel_dst) rx_gre = GRE(scapy.compat.raw(rx_ip[IPv6].payload)) rx_ip = rx_gre[IPv6] self.assertEqual(rx_ip.src, tx_ip.src) self.assertEqual(rx_ip.dst, tx_ip.dst) except: self.logger.error(ppp("Rx:", rx)) self.logger.error(ppp("Tx:", tx)) raise def verify_tunneled_4o6(self, src_if, capture, sent, tunnel_src, tunnel_dst): self.assertEqual(len(capture), len(sent)) for i in range(len(capture)): try: tx = sent[i] rx = capture[i] rx_ip = rx[IPv6] self.assertEqual(rx_ip.src, tunnel_src) self.assertEqual(rx_ip.dst, tunnel_dst) rx_gre = GRE(scapy.compat.raw(rx_ip[IPv6].payload)) tx_ip = tx[IP] rx_ip = rx_gre[IP] self.assertEqual(rx_ip.src, tx_ip.src) self.assertEqual(rx_ip.dst, tx_ip.dst) except: self.logger.error(ppp("Rx:", rx)) self.logger.error(ppp("Tx:", tx)) raise def verify_tunneled_6o4(self, src_if, capture, sent, tunnel_src, tunnel_dst): self.assertEqual(len(capture), len(sent)) for i in range(len(capture)): try: tx = sent[i] rx = capture[i] rx_ip = rx[IP] self.assertEqual(rx_ip.src, tunnel_src) self.assertEqual(rx_ip.dst, tunnel_dst) rx_gre = GRE(scapy.compat.raw(rx_ip[IP].payload)) rx_ip = rx_gre[IPv6] tx_ip = tx[IPv6] self.assertEqual(rx_ip.src, tx_ip.src) self.assertEqual(rx_ip.dst, tx_ip.dst) except: self.logger.error(ppp("Rx:", rx)) self.logger.error(ppp("Tx:", tx)) raise def verify_tunneled_l2o4(self, src_if, capture, sent, tunnel_src, tunnel_dst): self.assertEqual(len(capture), len(sent)) for i in range(len(capture)): try: tx = sent[i] rx = capture[i] tx_ip = tx[IP] rx_ip = rx[IP] self.assertEqual(rx_ip.src, tunnel_src) self.assertEqual(rx_ip.dst, tunnel_dst) rx_gre = rx[GRE] rx_l2 = rx_gre[Ether] rx_ip = rx_l2[IP] tx_gre = tx[GRE] tx_l2 = tx_gre[Ether] tx_ip = tx_l2[IP] self.assertEqual(rx_ip.src, tx_ip.src) self.assertEqual(rx_ip.dst, tx_ip.dst) # bridged, not L3 forwarded, so no TTL decrement self.assertEqual(rx_ip.ttl, tx_ip.ttl) except: self.logger.error(ppp("Rx:", rx)) self.logger.error(ppp("Tx:", tx)) raise def verify_tunneled_vlano4(self, src_if, capture, sent, tunnel_src, tunnel_dst, vlan): try: self.assertEqual(len(capture), len(sent)) except: ppc("Unexpected packets captured:", capture) raise for i in range(len(capture)): try: tx = sent[i] rx = capture[i] tx_ip = tx[IP] rx_ip = rx[IP] self.assertEqual(rx_ip.src, tunnel_src) self.assertEqual(rx_ip.dst, tunnel_dst) rx_gre = rx[GRE] rx_l2 = rx_gre[Ether] rx_vlan = rx_l2[Dot1Q] rx_ip = rx_l2[IP] self.assertEqual(rx_vlan.vlan, vlan) tx_gre = tx[GRE] tx_l2 = tx_gre[Ether] tx_ip = tx_l2[IP] self.assertEqual(rx_ip.src, tx_ip.src) self.assertEqual(rx_ip.dst, tx_ip.dst) # bridged, not L3 forwarded, so no TTL decrement self.assertEqual(rx_ip.ttl, tx_ip.ttl) except: self.logger.error(ppp("Rx:", rx)) self.logger.error(ppp("Tx:", tx)) raise def verify_decapped_4o4(self, src_if, capture, sent): self.assertEqual(len(capture), len(sent)) for i in range(len(capture)): try: tx = sent[i] rx = capture[i] tx_ip = tx[IP] rx_ip = rx[IP] tx_gre = tx[GRE] tx_ip = tx_gre[IP] self.assertEqual(rx_ip.src, tx_ip.src) self.assertEqual(rx_ip.dst, tx_ip.dst) # IP processing post pop has decremented the TTL self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl) except: self.logger.error(ppp("Rx:", rx)) self.logger.error(ppp("Tx:", tx)) raise def verify_decapped_6o4(self, src_if, capture, sent): self.assertEqual(len(capture), len(sent)) for i in range(len(capture)): try: tx = sent[i] rx = capture[i] tx_ip = tx[IP] rx_ip = rx[IPv6] tx_gre = tx[GRE] tx_ip = tx_gre[IPv6] self.assertEqual(rx_ip.src, tx_ip.src) self.assertEqual(rx_ip.dst, tx_ip.dst) self.assertEqual(rx_ip.hlim + 1, tx_ip.hlim) except: self.logger.error(ppp("Rx:", rx)) self.logger.error(ppp("Tx:", tx)) raise def test_gre(self): """ GRE IPv4 tunnel Tests """ # # Create an L3 GRE tunnel. # - set it admin up # - assign an IP Addres # - Add a route via the tunnel # gre_if = VppGreInterface(self, self.pg0.local_ip4, "1.1.1.2") gre_if.add_vpp_config() # # The double create (create the same tunnel twice) should fail, # and we should still be able to use the original # try: gre_if.add_vpp_config() except Exception: pass else: self.fail("Double GRE tunnel add does not fail") gre_if.admin_up() gre_if.config_ip4() route_via_tun = VppIpRoute( self, "4.4.4.4", 32, [VppRoutePath("0.0.0.0", gre_if.sw_if_index)]) route_via_tun.add_vpp_config() # # Send a packet stream that is routed into the tunnel # - they are all dropped since the tunnel's destintation IP # is unresolved - or resolves via the default route - which # which is a drop. # tx = self.create_stream_ip4(self.pg0, "5.5.5.5", "4.4.4.4") self.send_and_assert_no_replies(self.pg0, tx) # # Add a route that resolves the tunnel's destination # route_tun_dst = VppIpRoute( self, "1.1.1.2", 32, [VppRoutePath(self.pg0.remote_ip4, self.pg0.sw_if_index)]) route_tun_dst.add_vpp_config() # # Send a packet stream that is routed into the tunnel # - packets are GRE encapped # tx = self.create_stream_ip4(self.pg0, "5.5.5.5", "4.4.4.4") rx = self.send_and_expect(self.pg0, tx, self.pg0) self.verify_tunneled_4o4(self.pg0, rx, tx, self.pg0.local_ip4, "1.1.1.2") # # Send tunneled packets that match the created tunnel and # are decapped and forwarded # tx = self.create_tunnel_stream_4o4(self.pg0, "1.1.1.2", self.pg0.local_ip4, self.pg0.local_ip4, self.pg0.remote_ip4) rx = self.send_and_expect(self.pg0, tx, self.pg0) self.verify_decapped_4o4(self.pg0, rx, tx) # # Send tunneled packets that do not match the tunnel's src # self.vapi.cli("clear trace") tx = self.create_tunnel_stream_4o4(self.pg0, "1.1.1.3", self.pg0.local_ip4, self.pg0.local_ip4, self.pg0.remote_ip4) self.send_and_assert_no_replies( self.pg0, tx, remark="GRE packets forwarded despite no SRC address match") # # Configure IPv6 on the PG interface so we can route IPv6 # packets # self.pg0.config_ip6() self.pg0.resolve_ndp() # # Send IPv6 tunnel encapslated packets # - dropped since IPv6 is not enabled on the tunnel # tx = self.create_tunnel_stream_6o4(self.pg0, "1.1.1.2", self.pg0.local_ip4, self.pg0.local_ip6, self.pg0.remote_ip6) self.send_and_assert_no_replies( self.pg0, tx, "IPv6 GRE packets forwarded " "despite IPv6 not enabled on tunnel") # # Enable IPv6 on the tunnel # gre_if.config_ip6() # # Send IPv6 tunnel encapslated packets # - forwarded since IPv6 is enabled on the tunnel # tx = self.create_tunnel_stream_6o4(self.pg0, "1.1.1.2", self.pg0.local_ip4, self.pg0.local_ip6, self.pg0.remote_ip6) rx = self.send_and_expect(self.pg0, tx, self.pg0) self.verify_decapped_6o4(self.pg0, rx, tx) # # Send v6 packets for v4 encap # route6_via_tun = VppIpRoute(self, "2001::1", 128, [ VppRoutePath( "::", gre_if.sw_if_index, proto=DpoProto.DPO_PROTO_IP6) ]) route6_via_tun.add_vpp_config() tx = self.create_stream_ip6(self.pg0, "2001::2", "2001::1") rx = self.send_and_expect(self.pg0, tx, self.pg0) self.verify_tunneled_6o4(self.pg0, rx, tx, self.pg0.local_ip4, "1.1.1.2") # # test case cleanup # route_tun_dst.remove_vpp_config() route_via_tun.remove_vpp_config() route6_via_tun.remove_vpp_config() gre_if.remove_vpp_config() self.pg0.unconfig_ip6() def test_gre6(self): """ GRE IPv6 tunnel Tests """ self.pg1.config_ip6() self.pg1.resolve_ndp() # # Create an L3 GRE tunnel. # - set it admin up # - assign an IP Address # - Add a route via the tunnel # gre_if = VppGreInterface(self, self.pg2.local_ip6, "1002::1") gre_if.add_vpp_config() gre_if.admin_up() gre_if.config_ip6() route_via_tun = VppIpRoute(self, "4004::1", 128, [VppRoutePath("0::0", gre_if.sw_if_index)]) route_via_tun.add_vpp_config() # # Send a packet stream that is routed into the tunnel # - they are all dropped since the tunnel's destintation IP # is unresolved - or resolves via the default route - which # which is a drop. # tx = self.create_stream_ip6(self.pg2, "5005::1", "4004::1") self.send_and_assert_no_replies( self.pg2, tx, "GRE packets forwarded without DIP resolved") # # Add a route that resolves the tunnel's destination # route_tun_dst = VppIpRoute( self, "1002::1", 128, [VppRoutePath(self.pg2.remote_ip6, self.pg2.sw_if_index)]) route_tun_dst.add_vpp_config() # # Send a packet stream that is routed into the tunnel # - packets are GRE encapped # tx = self.create_stream_ip6(self.pg2, "5005::1", "4004::1") rx = self.send_and_expect(self.pg2, tx, self.pg2) self.verify_tunneled_6o6(self.pg2, rx, tx, self.pg2.local_ip6, "1002::1") # # Test decap. decapped packets go out pg1 # tx = self.create_tunnel_stream_6o6(self.pg2, "1002::1", self.pg2.local_ip6, "2001::1", self.pg1.remote_ip6) rx = self.send_and_expect(self.pg2, tx, self.pg1) # # RX'd packet is UDP over IPv6, test the GRE header is gone. # self.assertFalse(rx[0].haslayer(GRE)) self.assertEqual(rx[0][IPv6].dst, self.pg1.remote_ip6) # # Send v4 over v6 # route4_via_tun = VppIpRoute( self, "1.1.1.1", 32, [VppRoutePath("0.0.0.0", gre_if.sw_if_index)]) route4_via_tun.add_vpp_config() tx = self.create_stream_ip4(self.pg0, "1.1.1.2", "1.1.1.1") rx = self.send_and_expect(self.pg0, tx, self.pg2) self.verify_tunneled_4o6(self.pg0, rx, tx, self.pg2.local_ip6, "1002::1") # # test case cleanup # route_tun_dst.remove_vpp_config() route_via_tun.remove_vpp_config() route4_via_tun.remove_vpp_config() gre_if.remove_vpp_config() self.pg2.unconfig_ip6() self.pg1.unconfig_ip6() def test_gre_vrf(self): """ GRE tunnel VRF Tests """ # # Create an L3 GRE tunnel whose destination is in the non-default # table. The underlay is thus non-default - the overlay is still # the default. # - set it admin up # - assign an IP Addres # gre_if = VppGreInterface(self, self.pg1.local_ip4, "2.2.2.2", outer_fib_id=1) gre_if.add_vpp_config() gre_if.admin_up() gre_if.config_ip4() # # Add a route via the tunnel - in the overlay # route_via_tun = VppIpRoute( self, "9.9.9.9", 32, [VppRoutePath("0.0.0.0", gre_if.sw_if_index)]) route_via_tun.add_vpp_config() # # Add a route that resolves the tunnel's destination - in the # underlay table # route_tun_dst = VppIpRoute( self, "2.2.2.2", 32, table_id=1, paths=[VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)]) route_tun_dst.add_vpp_config() # # Send a packet stream that is routed into the tunnel # packets are sent in on pg0 which is in the default table # - packets are GRE encapped # self.vapi.cli("clear trace") tx = self.create_stream_ip4(self.pg0, "5.5.5.5", "9.9.9.9") rx = self.send_and_expect(self.pg0, tx, self.pg1) self.verify_tunneled_4o4(self.pg1, rx, tx, self.pg1.local_ip4, "2.2.2.2") # # Send tunneled packets that match the created tunnel and # are decapped and forwarded. This tests the decap lookup # does not happen in the encap table # self.vapi.cli("clear trace") tx = self.create_tunnel_stream_4o4(self.pg1, "2.2.2.2", self.pg1.local_ip4, self.pg0.local_ip4, self.pg0.remote_ip4) rx = self.send_and_expect(self.pg1, tx, self.pg0) self.verify_decapped_4o4(self.pg0, rx, tx) # # Send tunneled packets that match the created tunnel # but arrive on an interface that is not in the tunnel's # encap VRF, these are dropped. # IP enable the interface so they aren't dropped due to # IP not being enabled. # self.pg2.config_ip4() self.vapi.cli("clear trace") tx = self.create_tunnel_stream_4o4(self.pg2, "2.2.2.2", self.pg1.local_ip4, self.pg0.local_ip4, self.pg0.remote_ip4) rx = self.send_and_assert_no_replies(self.pg2, tx, "GRE decap packets in wrong VRF") self.pg2.unconfig_ip4() # # test case cleanup # route_tun_dst.remove_vpp_config() route_via_tun.remove_vpp_config() gre_if.remove_vpp_config() def test_gre_l2(self): """ GRE tunnel L2 Tests """ # # Add routes to resolve the tunnel destinations # route_tun1_dst = VppIpRoute( self, "2.2.2.2", 32, [VppRoutePath(self.pg0.remote_ip4, self.pg0.sw_if_index)]) route_tun2_dst = VppIpRoute( self, "2.2.2.3", 32, [VppRoutePath(self.pg0.remote_ip4, self.pg0.sw_if_index)]) route_tun1_dst.add_vpp_config() route_tun2_dst.add_vpp_config() # # Create 2 L2 GRE tunnels and x-connect them # gre_if1 = VppGreInterface( self, self.pg0.local_ip4, "2.2.2.2", type=(VppEnum.vl_api_gre_tunnel_type_t.GRE_API_TUNNEL_TYPE_TEB)) gre_if2 = VppGreInterface( self, self.pg0.local_ip4, "2.2.2.3", type=(VppEnum.vl_api_gre_tunnel_type_t.GRE_API_TUNNEL_TYPE_TEB)) gre_if1.add_vpp_config() gre_if2.add_vpp_config() gre_if1.admin_up() gre_if2.admin_up() self.vapi.sw_interface_set_l2_xconnect(gre_if1.sw_if_index, gre_if2.sw_if_index, enable=1) self.vapi.sw_interface_set_l2_xconnect(gre_if2.sw_if_index, gre_if1.sw_if_index, enable=1) # # Send in tunnel encapped L2. expect out tunnel encapped L2 # in both directions # tx = self.create_tunnel_stream_l2o4(self.pg0, "2.2.2.2", self.pg0.local_ip4) rx = self.send_and_expect(self.pg0, tx, self.pg0) self.verify_tunneled_l2o4(self.pg0, rx, tx, self.pg0.local_ip4, "2.2.2.3") tx = self.create_tunnel_stream_l2o4(self.pg0, "2.2.2.3", self.pg0.local_ip4) rx = self.send_and_expect(self.pg0, tx, self.pg0) self.verify_tunneled_l2o4(self.pg0, rx, tx, self.pg0.local_ip4, "2.2.2.2") self.vapi.sw_interface_set_l2_xconnect(gre_if1.sw_if_index, gre_if2.sw_if_index, enable=0) self.vapi.sw_interface_set_l2_xconnect(gre_if2.sw_if_index, gre_if1.sw_if_index, enable=0) # # Create a VLAN sub-interfaces on the GRE TEB interfaces # then x-connect them # gre_if_11 = VppDot1QSubint(self, gre_if1, 11) gre_if_12 = VppDot1QSubint(self, gre_if2, 12) # gre_if_11.add_vpp_config() # gre_if_12.add_vpp_config() gre_if_11.admin_up() gre_if_12.admin_up() self.vapi.sw_interface_set_l2_xconnect(gre_if_11.sw_if_index, gre_if_12.sw_if_index, enable=1) self.vapi.sw_interface_set_l2_xconnect(gre_if_12.sw_if_index, gre_if_11.sw_if_index, enable=1) # # Configure both to pop thier respective VLAN tags, # so that during the x-coonect they will subsequently push # self.vapi.l2_interface_vlan_tag_rewrite( sw_if_index=gre_if_12.sw_if_index, vtr_op=L2_VTR_OP.L2_POP_1, push_dot1q=12) self.vapi.l2_interface_vlan_tag_rewrite( sw_if_index=gre_if_11.sw_if_index, vtr_op=L2_VTR_OP.L2_POP_1, push_dot1q=11) # # Send traffic in both directiond - expect the VLAN tags to # be swapped. # tx = self.create_tunnel_stream_vlano4(self.pg0, "2.2.2.2", self.pg0.local_ip4, 11) rx = self.send_and_expect(self.pg0, tx, self.pg0) self.verify_tunneled_vlano4(self.pg0, rx, tx, self.pg0.local_ip4, "2.2.2.3", 12) tx = self.create_tunnel_stream_vlano4(self.pg0, "2.2.2.3", self.pg0.local_ip4, 12) rx = self.send_and_expect(self.pg0, tx, self.pg0) self.verify_tunneled_vlano4(self.pg0, rx, tx, self.pg0.local_ip4, "2.2.2.2", 11) # # Cleanup Test resources # gre_if_11.remove_vpp_config() gre_if_12.remove_vpp_config() gre_if1.remove_vpp_config() gre_if2.remove_vpp_config() route_tun1_dst.add_vpp_config() route_tun2_dst.add_vpp_config() def test_gre_loop(self): """ GRE tunnel loop Tests """ # # Create an L3 GRE tunnel. # - set it admin up # - assign an IP Addres # gre_if = VppGreInterface(self, self.pg0.local_ip4, "1.1.1.2") gre_if.add_vpp_config() gre_if.admin_up() gre_if.config_ip4() # # add a route to the tunnel's destination that points # through the tunnel, hence forming a loop in the forwarding # graph # route_dst = VppIpRoute(self, "1.1.1.2", 32, [VppRoutePath("0.0.0.0", gre_if.sw_if_index)]) route_dst.add_vpp_config() # # packets to the tunnels destination should be dropped # tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "1.1.1.2") self.send_and_assert_no_replies(self.pg2, tx) self.logger.info(self.vapi.ppcli("sh adj 7")) # # break the loop # route_dst.modify( [VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)]) route_dst.add_vpp_config() rx = self.send_and_expect(self.pg0, tx, self.pg1) # # a good route throught the tunnel to check it restacked # route_via_tun_2 = VppIpRoute( self, "2.2.2.2", 32, [VppRoutePath("0.0.0.0", gre_if.sw_if_index)]) route_via_tun_2.add_vpp_config() tx = self.create_stream_ip4(self.pg0, "2.2.2.3", "2.2.2.2") rx = self.send_and_expect(self.pg0, tx, self.pg1) self.verify_tunneled_4o4(self.pg1, rx, tx, self.pg0.local_ip4, "1.1.1.2") # # cleanup # route_via_tun_2.remove_vpp_config() gre_if.remove_vpp_config()
def test_ip_deag(self): """ IP Deag Routes """ # # Create a table to be used for: # 1 - another destination address lookup # 2 - a source address lookup # table_dst = VppIpTable(self, 1) table_src = VppIpTable(self, 2) table_dst.add_vpp_config() table_src.add_vpp_config() # # Add a route in the default table to point to a deag/ # second lookup in each of these tables # route_to_dst = VppIpRoute( self, "1.1.1.1", 32, [VppRoutePath("0.0.0.0", 0xffffffff, nh_table_id=1)]) route_to_src = VppIpRoute(self, "1.1.1.2", 32, [ VppRoutePath( "0.0.0.0", 0xffffffff, nh_table_id=2, is_source_lookup=1) ]) route_to_dst.add_vpp_config() route_to_src.add_vpp_config() # # packets to these destination are dropped, since they'll # hit the respective default routes in the second table # p_dst = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IP(src="5.5.5.5", dst="1.1.1.1") / TCP(sport=1234, dport=1234) / Raw('\xa5' * 100)) p_src = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IP(src="2.2.2.2", dst="1.1.1.2") / TCP(sport=1234, dport=1234) / Raw('\xa5' * 100)) pkts_dst = p_dst * 257 pkts_src = p_src * 257 self.send_and_assert_no_replies(self.pg0, pkts_dst, "IP in dst table") self.send_and_assert_no_replies(self.pg0, pkts_src, "IP in src table") # # add a route in the dst table to forward via pg1 # route_in_dst = VppIpRoute( self, "1.1.1.1", 32, [VppRoutePath(self.pg1.remote_ip4, self.pg1.sw_if_index)], table_id=1) route_in_dst.add_vpp_config() self.send_and_expect(self.pg0, pkts_dst, self.pg1) # # add a route in the src table to forward via pg2 # route_in_src = VppIpRoute( self, "2.2.2.2", 32, [VppRoutePath(self.pg2.remote_ip4, self.pg2.sw_if_index)], table_id=2) route_in_src.add_vpp_config() self.send_and_expect(self.pg0, pkts_src, self.pg2)
def test_mipip4(self): """ p2mp IPv4 tunnel Tests """ for itf in self.pg_interfaces[:2]: # # one underlay nh for each overlay/tunnel peer # itf.generate_remote_hosts(4) itf.configure_ipv4_neighbors() # # Create an p2mo IPIP tunnel. # - set it admin up # - assign an IP Addres # - Add a route via the tunnel # ipip_if = VppIpIpTunInterface( self, itf, itf.local_ip4, "0.0.0.0", mode=(VppEnum.vl_api_tunnel_mode_t.TUNNEL_API_MODE_MP)) ipip_if.add_vpp_config() ipip_if.admin_up() ipip_if.config_ip4() ipip_if.generate_remote_hosts(4) self.logger.info(self.vapi.cli("sh adj")) self.logger.info(self.vapi.cli("sh ip fib")) # # ensure we don't match to the tunnel if the source address # is all zeros # # tx = self.create_tunnel_stream_4o4(self.pg0, # "0.0.0.0", # itf.local_ip4, # self.pg0.local_ip4, # self.pg0.remote_ip4) # self.send_and_assert_no_replies(self.pg0, tx) # # for-each peer # for ii in range(1, 4): route_addr = "4.4.4.%d" % ii # # route traffic via the peer # route_via_tun = VppIpRoute(self, route_addr, 32, [ VppRoutePath(ipip_if._remote_hosts[ii].ip4, ipip_if.sw_if_index) ]) route_via_tun.add_vpp_config() # # Add a TEIB entry resolves the peer # teib = VppTeib(self, ipip_if, ipip_if._remote_hosts[ii].ip4, itf._remote_hosts[ii].ip4) teib.add_vpp_config() self.logger.info( self.vapi.cli("sh adj nbr ipip0 %s" % ipip_if._remote_hosts[ii].ip4)) # # Send a packet stream that is routed into the tunnel # - packets are IPIP encapped # inner = (IP(dst=route_addr, src="5.5.5.5") / UDP(sport=1234, dport=1234) / Raw(b'0x44' * 100)) tx_e = [ (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / inner) for x in range(63) ] rxs = self.send_and_expect(self.pg0, tx_e, itf) for rx in rxs: self.assertEqual(rx[IP].src, itf.local_ip4) self.assertEqual(rx[IP].dst, itf._remote_hosts[ii].ip4) tx_i = [ (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=itf._remote_hosts[ii].ip4, dst=itf.local_ip4) / IP(src=self.pg0.local_ip4, dst=self.pg0.remote_ip4) / UDP(sport=1234, dport=1234) / Raw(b'0x44' * 100)) for x in range(63) ] self.logger.info(self.vapi.cli("sh ipip tunnel-hash")) rx = self.send_and_expect(self.pg0, tx_i, self.pg0) # # delete and re-add the TEIB # teib.remove_vpp_config() self.send_and_assert_no_replies(self.pg0, tx_e) self.send_and_assert_no_replies(self.pg0, tx_i) teib.add_vpp_config() rx = self.send_and_expect(self.pg0, tx_e, itf) for rx in rxs: self.assertEqual(rx[IP].src, itf.local_ip4) self.assertEqual(rx[IP].dst, itf._remote_hosts[ii].ip4) rx = self.send_and_expect(self.pg0, tx_i, self.pg0) # # we can also send to the peer's address # inner = (IP(dst=teib.peer, src="5.5.5.5") / UDP(sport=1234, dport=1234) / Raw(b'0x44' * 100)) tx_e = [ (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / inner) for x in range(63) ] rxs = self.send_and_expect(self.pg0, tx_e, itf) # # with all of the peers in place, swap the ip-table of # the ipip interface # table = VppIpTable(self, 2) table.add_vpp_config() ipip_if.unconfig_ip4() ipip_if.set_table_ip4(self.table.table_id) ipip_if.config_ip4() # # we should still be able to reach the peers from the new table # inner = (IP(dst=teib.peer, src="5.5.5.5") / UDP(sport=1234, dport=1234) / Raw(b'0x44' * 100)) tx_e = [(Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / inner) for x in range(63)] rxs = self.send_and_expect(self.pg2, tx_e, itf) ipip_if.admin_down() ipip_if.unconfig_ip4() ipip_if.set_table_ip4(0)
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) # # 3 EPGs, 2 of which share a BD. # epgs = [] recircs = [] epgs.append( VppGbpEndpointGroup(self, 220, 0, 1, self.pg4, self.loop0, "10.0.0.128", "2001:10::128")) recircs.append(VppGbpRecirc(self, epgs[0], self.loop3)) epgs.append( VppGbpEndpointGroup(self, 221, 0, 1, self.pg5, self.loop0, "10.0.1.128", "2001:10:1::128")) recircs.append(VppGbpRecirc(self, epgs[1], self.loop4)) epgs.append( VppGbpEndpointGroup(self, 222, 0, 2, self.pg6, self.loop1, "10.0.2.128", "2001:10:2::128")) recircs.append(VppGbpRecirc(self, epgs[2], self.loop5)) # # 2 NAT EPGs, one for floating-IP subnets, the other for internet # epgs.append( VppGbpEndpointGroup(self, 333, 20, 20, self.pg7, self.loop2, "11.0.0.128", "3001::128")) recircs.append(VppGbpRecirc(self, epgs[3], self.loop6, is_ext=True)) epgs.append( VppGbpEndpointGroup(self, 444, 20, 20, self.pg8, self.loop2, "11.0.0.129", "3001::129")) recircs.append(VppGbpRecirc(self, epgs[4], self.loop8, is_ext=True)) epg_nat = epgs[3] recirc_nat = recircs[3] # # 4 end-points, 2 in the same subnet, 3 in the same BD # eps = [] eps.append( VppGbpEndpoint(self, self.pg0, epgs[0], recircs[0], "10.0.0.1", "11.0.0.1")) eps.append( VppGbpEndpoint(self, self.pg1, epgs[0], recircs[0], "10.0.0.2", "11.0.0.2")) eps.append( VppGbpEndpoint(self, self.pg2, epgs[1], recircs[1], "10.0.1.1", "11.0.0.3")) eps.append( VppGbpEndpoint(self, self.pg3, epgs[2], recircs[2], "10.0.2.1", "11.0.0.4")) eps.append( VppGbpEndpoint(self, self.pg0, epgs[0], recircs[0], "2001:10::1", "3001::1", is_ip6=True)) eps.append( VppGbpEndpoint(self, self.pg1, epgs[0], recircs[0], "2001:10::2", "3001::2", is_ip6=True)) eps.append( VppGbpEndpoint(self, self.pg2, epgs[1], recircs[1], "2001:10:1::1", "3001::3", is_ip6=True)) eps.append( VppGbpEndpoint(self, self.pg3, epgs[2], recircs[2], "2001:10:2::1", "3001::4", is_ip6=True)) # # 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, bvi=1) # 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. # r = VppIpRoute( self, ep.ip, ep.ip_len, [VppRoutePath(ep.ip, ep.epg.bvi.sw_if_index, proto=ep.proto)], is_ip6=ep.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, ep.ip, af=ep.af) a.add_vpp_config() ep_arps.append(a) # add each EP itf to the its BD self.vapi.sw_interface_set_l2_bridge(ep.itf.sw_if_index, ep.epg.bd) # add the BD ARP termination entry self.vapi.bd_ip_mac_add_del(bd_id=ep.epg.bd, mac=ep.bin_mac, ip=ep.ip_n, is_ipv6=0, is_add=1) # L2 FIB entry self.vapi.l2fib_add_del(ep.mac, ep.epg.bd, ep.itf.sw_if_index, is_add=1) # Add static mappings for each EP from the 10/8 to 11/8 network if ep.af == AF_INET: self.vapi.nat44_add_del_static_mapping(ep.ip_n, ep.floating_ip_n, vrf_id=0, addr_only=1) else: self.vapi.nat66_add_del_static_mapping(ep.ip_n, ep.floating_ip_n, vrf_id=0) # VPP EP create ... ep.add_vpp_config() # ... results in a Gratuitous ARP/ND on the EPG's uplink rx = ep.epg.uplink.get_capture(1, timeout=0.2) if ep.is_ip6: self.assertTrue(rx[0].haslayer(ICMPv6ND_NA)) self.assertEqual(rx[0][ICMPv6ND_NA].tgt, ep.ip) else: self.assertTrue(rx[0].haslayer(ARP)) self.assertEqual(rx[0][ARP].psrc, ep.ip) self.assertEqual(rx[0][ARP].pdst, ep.ip) # add the BD ARP termination entry for floating IP self.vapi.bd_ip_mac_add_del(bd_id=epg_nat.bd, mac=ep.bin_mac, ip=ep.floating_ip_n, is_ipv6=ep.is_ip6, is_add=1) # floating IPs route via EPG recirc r = VppIpRoute(self, ep.floating_ip, ep.ip_len, [ VppRoutePath(ep.floating_ip, ep.recirc.recirc.sw_if_index, is_dvr=1, proto=ep.proto) ], table_id=20, is_ip6=ep.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) # # ARP packets for unknown IP are flooded # 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_arp], self.pg0) # # ARP/ND packets get a response # pkt_arp = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / ARP(op="who-has", hwdst="ff:ff:ff:ff:ff:ff", hwsrc=self.pg0.remote_mac, pdst=epgs[0].bvi_ip4, psrc=eps[0].ip)) self.send_and_expect(self.pg0, [pkt_arp], self.pg0) nsma = in6_getnsma(inet_pton(AF_INET6, eps[4].ip)) d = inet_ntop(AF_INET6, nsma) pkt_nd = (Ether(dst=in6_getnsmac(nsma)) / IPv6(dst=d, src=eps[4].ip) / ICMPv6ND_NS(tgt=epgs[0].bvi_ip6) / ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac)) self.send_and_expect(self.pg0, [pkt_nd], self.pg0) # # broadcast packets are flooded # pkt_bcast = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) / IP(src=eps[0].ip, dst="232.1.1.1") / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) self.vapi.cli("clear trace") self.pg0.add_stream(pkt_bcast) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rxd = eps[1].itf.get_capture(1) self.assertEqual(rxd[0][Ether].dst, pkt_bcast[Ether].dst) rxd = epgs[0].uplink.get_capture(1) self.assertEqual(rxd[0][Ether].dst, pkt_bcast[Ether].dst) # # packets to non-local L3 destinations dropped # pkt_intra_epg_220_ip4 = ( Ether(src=self.pg0.remote_mac, dst=self.router_mac) / IP(src=eps[0].ip, 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].ip, dst="10.0.1.99") / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) self.send_and_assert_no_replies(self.pg0, pkt_intra_epg_220_ip4 * 65) pkt_inter_epg_222_ip6 = ( Ether(src=self.pg0.remote_mac, dst=self.router_mac) / IPv6(src=eps[4].ip, 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, is_ip6=True) s62 = VppGbpSubnet(self, 0, "2001:10:1::1", 64, is_ip6=True) s63 = VppGbpSubnet(self, 0, "2001:10:2::1", 64, is_ip6=True) 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")) # # 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].ip, 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].ip, 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].ip, dst="10.0.0.99") / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) self.send_and_expect_bridged(self.pg4, pkt_intra_epg_220_from_uplink * 65, self.pg0) # # in the absence of policy, endpoints in the same EPG # can communicate # pkt_intra_epg = ( Ether(src=self.pg0.remote_mac, dst=self.pg1.remote_mac) / IP(src=eps[0].ip, dst=eps[1].ip) / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) self.send_and_expect_bridged(self.pg0, pkt_intra_epg * 65, self.pg1) # # in the abscense of policy, endpoints in the different EPG # cannot communicate # pkt_inter_epg_220_to_221 = ( Ether(src=self.pg0.remote_mac, dst=self.pg2.remote_mac) / IP(src=eps[0].ip, dst=eps[2].ip) / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) pkt_inter_epg_221_to_220 = ( Ether(src=self.pg2.remote_mac, dst=self.pg0.remote_mac) / IP(src=eps[2].ip, dst=eps[0].ip) / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) pkt_inter_epg_220_to_222 = ( Ether(src=self.pg0.remote_mac, dst=self.router_mac) / IP(src=eps[0].ip, dst=eps[3].ip) / 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_220_to_222 * 65) # # A uni-directional contract from EPG 220 -> 221 # c1 = VppGbpContract(self, 220, 221, 0) c1.add_vpp_config() 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, 0) c2.add_vpp_config() 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 # not in the contract. # 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, 0) 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() 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_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, is_ip6=True) 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, is_ip6=True) 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[4].floating_ip)) # # 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].ip, 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) c4 = VppGbpContract(self, 220, 333, 0) c4.add_vpp_config() self.send_and_expect_natted(self.pg0, pkt_inter_epg_220_to_global * 65, self.pg7, eps[0].floating_ip) pkt_inter_epg_220_to_global = ( Ether(src=self.pg0.remote_mac, dst=self.router_mac) / IPv6(src=eps[4].ip, 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[4].floating_ip) # # 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].floating_ip, 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, 0) c5.add_vpp_config() self.send_and_expect_unnatted(self.pg7, pkt_inter_epg_220_from_global * 65, eps[0].itf, eps[0].ip) pkt_inter_epg_220_from_global = ( Ether(src=self.router_mac, dst=self.pg0.remote_mac) / IPv6(dst=eps[4].floating_ip, 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[4].itf, eps[4].ip) # # 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].ip, dst=eps[1].floating_ip) / 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].floating_ip, eps[1].ip) pkt_intra_epg_220_global = ( Ether(src=self.pg4.remote_mac, dst=self.router_mac) / IPv6(src=eps[4].ip, dst=eps[5].floating_ip) / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) self.send_and_expect_double_natted6(eps[4].itf, pkt_intra_epg_220_global * 65, eps[5].itf, eps[4].floating_ip, eps[5].ip) # # cleanup # for ep in eps: # del static mappings for each EP from the 10/8 to 11/8 network if ep.af == AF_INET: self.vapi.nat44_add_del_static_mapping(ep.ip_n, ep.floating_ip_n, vrf_id=0, addr_only=1, is_add=0) else: self.vapi.nat66_add_del_static_mapping(ep.ip_n, ep.floating_ip_n, 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)
class TestIgmp(VppTestCase): """ IGMP Test Case """ @classmethod def setUpClass(cls): super(TestIgmp, cls).setUpClass() @classmethod def tearDownClass(cls): super(TestIgmp, cls).tearDownClass() def setUp(self): super(TestIgmp, self).setUp() self.create_pg_interfaces(range(4)) self.sg_list = [] self.config_list = [] self.ip_addr = [] self.ip_table = VppIpTable(self, 1) self.ip_table.add_vpp_config() for pg in self.pg_interfaces[2:]: pg.set_table_ip4(1) for pg in self.pg_interfaces: pg.admin_up() pg.config_ip4() pg.resolve_arp() def tearDown(self): for pg in self.pg_interfaces: self.vapi.igmp_clear_interface(pg.sw_if_index) pg.unconfig_ip4() pg.set_table_ip4(0) pg.admin_down() super(TestIgmp, self).tearDown() def send(self, ti, pkts): ti.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() def test_igmp_flush(self): """ IGMP Link Up/down and Flush """ # # FIX THIS. Link down. # def test_igmp_enable(self): """ IGMP enable/disable on an interface check for the addition/removal of the IGMP mroutes """ self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.HOST) self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 1, IGMP_MODE.HOST) self.assertTrue(find_mroute(self, "224.0.0.1", "0.0.0.0", 32)) self.assertTrue(find_mroute(self, "224.0.0.22", "0.0.0.0", 32)) self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 1, IGMP_MODE.HOST) self.vapi.igmp_enable_disable(self.pg3.sw_if_index, 1, IGMP_MODE.HOST) self.assertTrue(find_mroute(self, "224.0.0.1", "0.0.0.0", 32, table_id=1)) self.assertTrue(find_mroute(self, "224.0.0.22", "0.0.0.0", 32, table_id=1)) self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.HOST) self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 0, IGMP_MODE.HOST) self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 0, IGMP_MODE.HOST) self.vapi.igmp_enable_disable(self.pg3.sw_if_index, 0, IGMP_MODE.HOST) self.assertFalse(find_mroute(self, "224.0.0.1", "0.0.0.0", 32)) self.assertFalse(find_mroute(self, "224.0.0.22", "0.0.0.0", 32)) self.assertFalse(find_mroute(self, "224.0.0.1", "0.0.0.0", 32, table_id=1)) self.assertFalse(find_mroute(self, "224.0.0.22", "0.0.0.0", 32, table_id=1)) def verify_general_query(self, p): ip = p[IP] self.assertEqual(len(ip.options), 1) self.assertEqual(ip.options[0].option, 20) self.assertEqual(ip.dst, "224.0.0.1") self.assertEqual(ip.proto, 2) igmp = p[IGMPv3] self.assertEqual(igmp.type, 0x11) self.assertEqual(igmp.gaddr, "0.0.0.0") def verify_group_query(self, p, grp, srcs): ip = p[IP] self.assertEqual(ip.dst, grp) self.assertEqual(ip.proto, 2) self.assertEqual(len(ip.options), 1) self.assertEqual(ip.options[0].option, 20) self.assertEqual(ip.proto, 2) igmp = p[IGMPv3] self.assertEqual(igmp.type, 0x11) self.assertEqual(igmp.gaddr, grp) def verify_report(self, rx, records): ip = rx[IP] self.assertEqual(rx[IP].dst, "224.0.0.22") self.assertEqual(len(ip.options), 1) self.assertEqual(ip.options[0].option, 20) self.assertEqual(ip.proto, 2) self.assertEqual(IGMPv3.igmpv3types[rx[IGMPv3].type], "Version 3 Membership Report") self.assertEqual(rx[IGMPv3mr].numgrp, len(records)) received = rx[IGMPv3mr].records for ii in range(len(records)): gr = received[ii] r = records[ii] self.assertEqual(IGMPv3gr.igmpv3grtypes[gr.rtype], r.type) self.assertEqual(gr.numsrc, len(r.sg.saddrs)) self.assertEqual(gr.maddr, r.sg.gaddr) self.assertEqual(len(gr.srcaddrs), len(r.sg.saddrs)) self.assertEqual(sorted(gr.srcaddrs), sorted(r.sg.saddrs)) def add_group(self, itf, sg, n_pkts=2): self.pg_enable_capture(self.pg_interfaces) self.pg_start() hs = VppHostState(self, IGMP_FILTER.INCLUDE, itf.sw_if_index, sg) hs.add_vpp_config() capture = itf.get_capture(n_pkts, timeout=10) # reports are transmitted twice due to default rebostness value=2 self.verify_report(capture[0], [IgmpRecord(sg, "Allow New Sources")]), self.verify_report(capture[1], [IgmpRecord(sg, "Allow New Sources")]), return hs def remove_group(self, hs): self.pg_enable_capture(self.pg_interfaces) self.pg_start() hs.remove_vpp_config() capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(hs.sg, "Block Old Sources")]) def test_igmp_host(self): """ IGMP Host functions """ # # Enable interface for host functions # self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.HOST) # # Add one S,G of state and expect a state-change event report # indicating the addition of the S,G # h1 = self.add_group(self.pg0, IgmpSG("239.1.1.1", ["1.1.1.1"])) # search for the corresponding state created in VPP dump = self.vapi.igmp_dump(self.pg0.sw_if_index) self.assertEqual(len(dump), 1) self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "1.1.1.1")) # # Send a general query (to the all router's address) # expect VPP to respond with a membership report. # Pad the query with 0 - some devices in the big wild # internet are prone to this. # p_g = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='224.0.0.1', tos=0xc0) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="0.0.0.0") / Raw(b'\x00' * 10)) self.send(self.pg0, p_g) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")]) # # Group specific query # p_gs = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='239.1.1.1', tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="239.1.1.1")) self.send(self.pg0, p_gs) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")]) # # A group and source specific query, with the source matching # the source VPP has # p_gs1 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='239.1.1.1', tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.1"])) self.send(self.pg0, p_gs1) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")]) # # A group and source specific query that reports more sources # than the packet actually has. # p_gs2 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='239.1.1.1', tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="239.1.1.1", numsrc=4, srcaddrs=["1.1.1.1"])) self.send_and_assert_no_replies(self.pg0, p_gs2, timeout=10) # # A group and source specific query, with the source NOT matching # the source VPP has. There should be no response. # p_gs2 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='239.1.1.1', tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.2"])) self.send_and_assert_no_replies(self.pg0, p_gs2, timeout=10) # # A group and source specific query, with the multiple sources # one of which matches the source VPP has. # The report should contain only the source VPP has. # p_gs3 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='239.1.1.1', tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.1", "1.1.1.2", "1.1.1.3"])) self.send(self.pg0, p_gs3) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")]) # # Two source and group specific queries in quick succession, the # first does not have VPPs source the second does. then vice-versa # self.send(self.pg0, [p_gs2, p_gs1]) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")]) self.send(self.pg0, [p_gs1, p_gs2]) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")]) # # remove state, expect the report for the removal # self.remove_group(h1) dump = self.vapi.igmp_dump() self.assertFalse(dump) # # A group with multiple sources # h2 = self.add_group(self.pg0, IgmpSG("239.1.1.1", ["1.1.1.1", "1.1.1.2", "1.1.1.3"])) # search for the corresponding state created in VPP dump = self.vapi.igmp_dump(self.pg0.sw_if_index) self.assertEqual(len(dump), 3) for s in h2.sg.saddrs: self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", s)) # # Send a general query (to the all router's address) # expect VPP to respond with a membership report will all sources # self.send(self.pg0, p_g) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h2.sg, "Mode Is Include")]) # # Group and source specific query; some present some not # p_gs = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='239.1.1.1', tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.1", "1.1.1.2", "1.1.1.4"])) self.send(self.pg0, p_gs) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord( IgmpSG('239.1.1.1', ["1.1.1.1", "1.1.1.2"]), "Mode Is Include")]) # # add loads more groups # h3 = self.add_group(self.pg0, IgmpSG("239.1.1.2", ["2.1.1.1", "2.1.1.2", "2.1.1.3"])) h4 = self.add_group(self.pg0, IgmpSG("239.1.1.3", ["3.1.1.1", "3.1.1.2", "3.1.1.3"])) h5 = self.add_group(self.pg0, IgmpSG("239.1.1.4", ["4.1.1.1", "4.1.1.2", "4.1.1.3"])) h6 = self.add_group(self.pg0, IgmpSG("239.1.1.5", ["5.1.1.1", "5.1.1.2", "5.1.1.3"])) h7 = self.add_group(self.pg0, IgmpSG("239.1.1.6", ["6.1.1.1", "6.1.1.2", "6.1.1.3", "6.1.1.4", "6.1.1.5", "6.1.1.6", "6.1.1.7", "6.1.1.8", "6.1.1.9", "6.1.1.10", "6.1.1.11", "6.1.1.12", "6.1.1.13", "6.1.1.14", "6.1.1.15", "6.1.1.16"])) # # general query. # the order the groups come in is not important, so what is # checked for is what VPP is sending today. # self.send(self.pg0, p_g) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h3.sg, "Mode Is Include"), IgmpRecord(h2.sg, "Mode Is Include"), IgmpRecord(h6.sg, "Mode Is Include"), IgmpRecord(h4.sg, "Mode Is Include"), IgmpRecord(h5.sg, "Mode Is Include"), IgmpRecord(h7.sg, "Mode Is Include")]) # # modify a group to add and remove some sources # h7.sg = IgmpSG("239.1.1.6", ["6.1.1.1", "6.1.1.2", "6.1.1.5", "6.1.1.6", "6.1.1.7", "6.1.1.8", "6.1.1.9", "6.1.1.10", "6.1.1.11", "6.1.1.12", "6.1.1.13", "6.1.1.14", "6.1.1.15", "6.1.1.16", "6.1.1.17", "6.1.1.18"]) self.pg_enable_capture(self.pg_interfaces) self.pg_start() h7.add_vpp_config() capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(IgmpSG("239.1.1.6", ["6.1.1.17", "6.1.1.18"]), "Allow New Sources"), IgmpRecord(IgmpSG("239.1.1.6", ["6.1.1.3", "6.1.1.4"]), "Block Old Sources")]) # # add an additional groups with many sources so that each group # consumes the link MTU. We should therefore see multiple state # state reports when queried. # self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [560, 0, 0, 0]) src_list = [] for i in range(128): src_list.append("10.1.1.%d" % i) h8 = self.add_group(self.pg0, IgmpSG("238.1.1.1", src_list)) h9 = self.add_group(self.pg0, IgmpSG("238.1.1.2", src_list)) self.send(self.pg0, p_g) capture = self.pg0.get_capture(4, timeout=10) self.verify_report(capture[0], [IgmpRecord(h3.sg, "Mode Is Include"), IgmpRecord(h2.sg, "Mode Is Include"), IgmpRecord(h6.sg, "Mode Is Include"), IgmpRecord(h4.sg, "Mode Is Include"), IgmpRecord(h5.sg, "Mode Is Include")]) self.verify_report(capture[1], [IgmpRecord(h8.sg, "Mode Is Include")]) self.verify_report(capture[2], [IgmpRecord(h7.sg, "Mode Is Include")]) self.verify_report(capture[3], [IgmpRecord(h9.sg, "Mode Is Include")]) # # drop the MTU further (so a 128 sized group won't fit) # self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [512, 0, 0, 0]) self.pg_enable_capture(self.pg_interfaces) self.pg_start() h10 = VppHostState(self, IGMP_FILTER.INCLUDE, self.pg0.sw_if_index, IgmpSG("238.1.1.3", src_list)) h10.add_vpp_config() capture = self.pg0.get_capture(2, timeout=10) # wait for a little bit self.sleep(1) # # remove state, expect the report for the removal # the dump should be empty # self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [600, 0, 0, 0]) self.remove_group(h8) self.remove_group(h9) self.remove_group(h2) self.remove_group(h3) self.remove_group(h4) self.remove_group(h5) self.remove_group(h6) self.remove_group(h7) self.remove_group(h10) self.logger.info(self.vapi.cli("sh igmp config")) self.assertFalse(self.vapi.igmp_dump()) # # TODO # ADD STATE ON MORE INTERFACES # self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.HOST) def test_igmp_router(self): """ IGMP Router Functions """ # # Drop reports when not enabled # p_j = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Version 3 Membership Report") / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype="Allow New Sources", maddr="239.1.1.1", srcaddrs=["10.1.1.1", "10.1.1.2"])) p_l = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Version 3 Membership Report") / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype="Block Old Sources", maddr="239.1.1.1", srcaddrs=["10.1.1.1", "10.1.1.2"])) self.send(self.pg0, p_j) self.assertFalse(self.vapi.igmp_dump()) # # drop the default timer values so these tests execute in a # reasonable time frame # self.vapi.cli("test igmp timers query 1 src 3 leave 1") # # enable router functions on the interface # self.pg_enable_capture(self.pg_interfaces) self.pg_start() self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.ROUTER) self.vapi.want_igmp_events(1) # # wait for router to send general query # for ii in range(3): capture = self.pg0.get_capture(1, timeout=2) self.verify_general_query(capture[0]) self.pg_enable_capture(self.pg_interfaces) self.pg_start() # # re-send the report. VPP should now hold state for the new group # VPP sends a notification that a new group has been joined # self.send(self.pg0, p_j) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.1", 1)) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 1)) dump = self.vapi.igmp_dump(self.pg0.sw_if_index) self.assertEqual(len(dump), 2) self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.1")) self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.2")) # # wait for the per-source timer to expire # the state should be reaped # VPP sends a notification that the group has been left # self.assertTrue(wait_for_igmp_event(self, 4, self.pg0, "239.1.1.1", "10.1.1.1", 0)) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 0)) self.assertFalse(self.vapi.igmp_dump()) # # resend the join. wait for two queries and then send a current-state # record to include all sources. this should reset the expiry time # on the sources and thus they will still be present in 2 seconds time. # If the source timer was not refreshed, then the state would have # expired in 3 seconds. # self.send(self.pg0, p_j) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.1", 1)) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 1)) dump = self.vapi.igmp_dump(self.pg0.sw_if_index) self.assertEqual(len(dump), 2) capture = self.pg0.get_capture(2, timeout=3) self.verify_general_query(capture[0]) self.verify_general_query(capture[1]) p_cs = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Version 3 Membership Report") / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype="Mode Is Include", maddr="239.1.1.1", srcaddrs=["10.1.1.1", "10.1.1.2"])) self.send(self.pg0, p_cs) self.sleep(2) dump = self.vapi.igmp_dump(self.pg0.sw_if_index) self.assertEqual(len(dump), 2) self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.1")) self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.2")) # # wait for the per-source timer to expire # the state should be reaped # self.assertTrue(wait_for_igmp_event(self, 4, self.pg0, "239.1.1.1", "10.1.1.1", 0)) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 0)) self.assertFalse(self.vapi.igmp_dump()) # # resend the join, then a leave. Router sends a group+source # specific query containing both sources # self.send(self.pg0, p_j) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.1", 1)) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 1)) dump = self.vapi.igmp_dump(self.pg0.sw_if_index) self.assertEqual(len(dump), 2) self.send(self.pg0, p_l) capture = self.pg0.get_capture(1, timeout=3) self.verify_group_query(capture[0], "239.1.1.1", ["10.1.1.1", "10.1.1.2"]) # # the group specific query drops the timeout to leave (=1) seconds # self.assertTrue(wait_for_igmp_event(self, 2, self.pg0, "239.1.1.1", "10.1.1.1", 0)) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 0)) self.assertFalse(self.vapi.igmp_dump()) self.assertFalse(self.vapi.igmp_dump()) # # a TO_EX({}) / IN_EX({}) is treated like a (*,G) join # p_j = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Version 3 Membership Report") / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype="Change To Exclude Mode", maddr="239.1.1.2")) self.send(self.pg0, p_j) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.2", "0.0.0.0", 1)) p_j = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Version 3 Membership Report") / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype="Mode Is Exclude", maddr="239.1.1.3")) self.send(self.pg0, p_j) self.assertTrue(wait_for_igmp_event(self, 1, self.pg0, "239.1.1.3", "0.0.0.0", 1)) # # A 'allow sources' for {} should be ignored as it should # never be sent. # p_j = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Version 3 Membership Report") / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype="Allow New Sources", maddr="239.1.1.4")) self.send(self.pg0, p_j) dump = self.vapi.igmp_dump(self.pg0.sw_if_index) self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.2", "0.0.0.0")) self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.3", "0.0.0.0")) self.assertFalse(find_igmp_state(dump, self.pg0, "239.1.1.4", "0.0.0.0")) # # a TO_IN({}) and IS_IN({}) are treated like a (*,G) leave # self.vapi.cli("set logging class igmp level debug") p_l = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Version 3 Membership Report") / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype="Change To Include Mode", maddr="239.1.1.2")) self.send(self.pg0, p_l) self.assertTrue(wait_for_igmp_event(self, 2, self.pg0, "239.1.1.2", "0.0.0.0", 0)) p_l = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Version 3 Membership Report") / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype="Mode Is Include", maddr="239.1.1.3")) self.send(self.pg0, p_l) self.assertTrue(wait_for_igmp_event(self, 2, self.pg0, "239.1.1.3", "0.0.0.0", 0)) self.assertFalse(self.vapi.igmp_dump(self.pg0.sw_if_index)) # # disable router config # self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.ROUTER) def _create_igmpv3_pck(self, itf, rtype, maddr, srcaddrs): p = (Ether(dst=itf.local_mac, src=itf.remote_mac) / IP(src=itf.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Version 3 Membership Report") / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype=rtype, maddr=maddr, srcaddrs=srcaddrs)) return p def test_igmp_proxy_device(self): """ IGMP proxy device """ self.pg2.admin_down() self.pg2.unconfig_ip4() self.pg2.set_table_ip4(0) self.pg2.config_ip4() self.pg2.admin_up() self.vapi.cli('test igmp timers query 10 src 3 leave 1') # enable IGMP self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.HOST) self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 1, IGMP_MODE.ROUTER) self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 1, IGMP_MODE.ROUTER) # create IGMP proxy device self.vapi.igmp_proxy_device_add_del(0, self.pg0.sw_if_index, 1) self.vapi.igmp_proxy_device_add_del_interface(0, self.pg1.sw_if_index, 1) self.vapi.igmp_proxy_device_add_del_interface(0, self.pg2.sw_if_index, 1) # send join on pg1. join should be proxied by pg0 p_j = self._create_igmpv3_pck(self.pg1, "Allow New Sources", "239.1.1.1", ["10.1.1.1", "10.1.1.2"]) self.send(self.pg1, p_j) capture = self.pg0.get_capture(1, timeout=1) self.verify_report(capture[0], [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.1", "10.1.1.2"]), "Allow New Sources")]) self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32)) # send join on pg2. join should be proxied by pg0. # the group should contain only 10.1.1.3 as # 10.1.1.1 was already reported p_j = self._create_igmpv3_pck(self.pg2, "Allow New Sources", "239.1.1.1", ["10.1.1.1", "10.1.1.3"]) self.send(self.pg2, p_j) capture = self.pg0.get_capture(1, timeout=1) self.verify_report(capture[0], [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.3"]), "Allow New Sources")]) self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32)) # send leave on pg2. leave for 10.1.1.3 should be proxyed # as pg2 was the only interface interested in 10.1.1.3 p_l = self._create_igmpv3_pck(self.pg2, "Block Old Sources", "239.1.1.1", ["10.1.1.3"]) self.send(self.pg2, p_l) capture = self.pg0.get_capture(1, timeout=2) self.verify_report(capture[0], [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.3"]), "Block Old Sources")]) self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32)) # disable igmp on pg1 (also removes interface from proxy device) # proxy leave for 10.1.1.2. pg2 is still interested in 10.1.1.1 self.pg_enable_capture(self.pg_interfaces) self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 0, IGMP_MODE.ROUTER) capture = self.pg0.get_capture(1, timeout=1) self.verify_report(capture[0], [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.2"]), "Block Old Sources")]) self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32)) # disable IGMP on pg0 and pg1. # disabling IGMP on pg0 (proxy device upstream interface) # removes this proxy device self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.HOST) self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 0, IGMP_MODE.ROUTER) self.assertFalse(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))