X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=test%2Ftest_neighbor.py;h=241479a1d30a5017bc35054cd6158dab1b5bc58a;hb=e2fe09742;hp=7157839127b0ff544e60312e0fe893bd23668f4d;hpb=1284f8c71da8ec35cba04351cf62cba7bdd7f847;p=vpp.git diff --git a/test/test_neighbor.py b/test/test_neighbor.py index 7157839127b..241479a1d30 100644 --- a/test/test_neighbor.py +++ b/test/test_neighbor.py @@ -7,13 +7,14 @@ from socket import AF_INET, AF_INET6, inet_pton from framework import VppTestCase, VppTestRunner from vpp_neighbor import VppNeighbor, find_nbr from vpp_ip_route import VppIpRoute, VppRoutePath, find_route, \ - VppIpTable, DpoProto, FibPathType + VppIpTable, DpoProto, FibPathType, VppIpInterfaceAddress from vpp_papi import VppEnum +from vpp_ip import VppIpPuntRedirect import scapy.compat from scapy.packet import Raw from scapy.layers.l2 import Ether, ARP, Dot1Q -from scapy.layers.inet import IP, UDP +from scapy.layers.inet import IP, UDP, TCP from scapy.layers.inet6 import IPv6 from scapy.contrib.mpls import MPLS from scapy.layers.inet6 import IPv6 @@ -204,6 +205,8 @@ class ARPTestCase(VppTestCase): dyn_arp.add_vpp_config() self.assertTrue(dyn_arp.query_vpp_config()) + self.logger.info(self.vapi.cli("show ip neighbor-watcher")) + # this matches all of the listnerers es = [self.vapi.wait_for_event(1, "ip_neighbor_event") for i in range(3)] @@ -761,8 +764,10 @@ class ARPTestCase(VppTestCase): # # Configure Proxy ARP for the subnet on PG0addresses on pg0 # - self.vapi.proxy_arp_add_del(self.pg0._local_ip4_subnet, - self.pg0._local_ip4_bcast) + self.vapi.proxy_arp_add_del(proxy={'table_id': 0, + 'low': self.pg0._local_ip4_subnet, + 'hi': self.pg0._local_ip4_bcast}, + is_add=1) # Make pg2 un-numbered to pg0 # @@ -777,11 +782,7 @@ class ARPTestCase(VppTestCase): # 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) + rx = self.send_and_expect(self.pg2, [arp_req_from_me], self.pg2) self.verify_arp_resp(rx[0], self.pg2.local_mac, self.pg2.remote_mac, @@ -795,13 +796,56 @@ class ARPTestCase(VppTestCase): self.pg2.sw_if_index, self.pg0.local_ip4)) + # + # setup a punt redirect so packets from the uplink go to the tap + # + redirect = VppIpPuntRedirect(self, self.pg0.sw_if_index, + self.pg2.sw_if_index, self.pg0.local_ip4) + redirect.add_vpp_config() + + p_tcp = (Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac,) / + IP(src=self.pg0.remote_ip4, + dst=self.pg0.local_ip4) / + TCP(sport=80, dport=80) / + Raw()) + rx = self.send_and_expect(self.pg0, [p_tcp], self.pg2) + + # there's no ARP entry so this is an ARP req + self.assertTrue(rx[0].haslayer(ARP)) + + # and ARP entry for VPP's pg0 address on the host interface + n1 = VppNeighbor(self, + self.pg2.sw_if_index, + self.pg2.remote_mac, + self.pg0.local_ip4, + is_no_fib_entry=True).add_vpp_config() + # now the packets shold forward + rx = self.send_and_expect(self.pg0, [p_tcp], self.pg2) + self.assertFalse(rx[0].haslayer(ARP)) + self.assertEqual(rx[0][Ether].dst, self.pg2.remote_mac) + + # + # flush the neighbor cache on the uplink + # + af = VppEnum.vl_api_address_family_t + self.vapi.ip_neighbor_flush(af.ADDRESS_IP4, self.pg0.sw_if_index) + + # ensure we can still resolve the ARPs on the uplink + self.pg0.resolve_arp() + + self.assertTrue(find_nbr(self, + self.pg0.sw_if_index, + self.pg0.remote_ip4)) + # # cleanup # - self.pg2.set_proxy_arp(0) - self.vapi.proxy_arp_add_del(self.pg0._local_ip4_subnet, - self.pg0._local_ip4_bcast, + self.vapi.proxy_arp_add_del(proxy={'table_id': 0, + 'low': self.pg0._local_ip4_subnet, + 'hi': self.pg0._local_ip4_bcast}, is_add=0) + redirect.remove_vpp_config() def test_proxy_arp(self): """ Proxy ARP """ @@ -846,8 +890,10 @@ class ARPTestCase(VppTestCase): # # 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")) + self.vapi.proxy_arp_add_del(proxy={'table_id': 0, + 'low': "10.10.10.2", + 'hi': "10.10.10.124"}, + is_add=1) # # No responses are sent when the interfaces are not enabled for proxy @@ -1209,6 +1255,116 @@ class ARPTestCase(VppTestCase): static_arp.remove_vpp_config() self.pg2.set_table_ip4(0) + def test_arp_static_replace_dynamic_same_mac(self): + """ ARP Static can replace Dynamic (same mac) """ + self.pg2.generate_remote_hosts(1) + + dyn_arp = VppNeighbor(self, + self.pg2.sw_if_index, + self.pg2.remote_hosts[0].mac, + self.pg2.remote_hosts[0].ip4) + static_arp = VppNeighbor(self, + self.pg2.sw_if_index, + self.pg2.remote_hosts[0].mac, + self.pg2.remote_hosts[0].ip4, + is_static=1) + + # + # Add a dynamic ARP entry + # + dyn_arp.add_vpp_config() + + # + # We should find the dynamic nbr + # + self.assertFalse(find_nbr(self, + self.pg2.sw_if_index, + self.pg2.remote_hosts[0].ip4, + is_static=1)) + self.assertTrue(find_nbr(self, + self.pg2.sw_if_index, + self.pg2.remote_hosts[0].ip4, + is_static=0, + mac=self.pg2.remote_hosts[0].mac)) + + # + # Add a static ARP entry with the same mac + # + static_arp.add_vpp_config() + + # + # We should now find the static nbr with the same mac + # + self.assertFalse(find_nbr(self, + self.pg2.sw_if_index, + self.pg2.remote_hosts[0].ip4, + is_static=0)) + self.assertTrue(find_nbr(self, + self.pg2.sw_if_index, + self.pg2.remote_hosts[0].ip4, + is_static=1, + mac=self.pg2.remote_hosts[0].mac)) + + # + # clean-up + # + static_arp.remove_vpp_config() + + def test_arp_static_replace_dynamic_diff_mac(self): + """ ARP Static can replace Dynamic (diff mac) """ + self.pg2.generate_remote_hosts(2) + + dyn_arp = VppNeighbor(self, + self.pg2.sw_if_index, + self.pg2.remote_hosts[0].mac, + self.pg2.remote_hosts[0].ip4) + static_arp = VppNeighbor(self, + self.pg2.sw_if_index, + self.pg2.remote_hosts[1].mac, + self.pg2.remote_hosts[0].ip4, + is_static=1) + + # + # Add a dynamic ARP entry + # + dyn_arp.add_vpp_config() + + # + # We should find the dynamic nbr + # + self.assertFalse(find_nbr(self, + self.pg2.sw_if_index, + self.pg2.remote_hosts[0].ip4, + is_static=1)) + self.assertTrue(find_nbr(self, + self.pg2.sw_if_index, + self.pg2.remote_hosts[0].ip4, + is_static=0, + mac=self.pg2.remote_hosts[0].mac)) + + # + # Add a static ARP entry with a changed mac + # + static_arp.add_vpp_config() + + # + # We should now find the static nbr with a changed mac + # + self.assertFalse(find_nbr(self, + self.pg2.sw_if_index, + self.pg2.remote_hosts[0].ip4, + is_static=0)) + self.assertTrue(find_nbr(self, + self.pg2.sw_if_index, + self.pg2.remote_hosts[0].ip4, + is_static=1, + mac=self.pg2.remote_hosts[1].mac)) + + # + # clean-up + # + static_arp.remove_vpp_config() + def test_arp_incomplete(self): """ ARP Incomplete""" self.pg1.generate_remote_hosts(3) @@ -1246,13 +1402,8 @@ class ARPTestCase(VppTestCase): # # change the interface's MAC # - mac = [scapy.compat.chb(0x00), scapy.compat.chb(0x00), - scapy.compat.chb(0x00), scapy.compat.chb(0x33), - scapy.compat.chb(0x33), scapy.compat.chb(0x33)] - mac_string = ''.join(mac) - self.vapi.sw_interface_set_mac_address(self.pg1.sw_if_index, - mac_string) + "00:00:00:33:33:33") # # now ARP requests come from the new source mac @@ -1278,7 +1429,7 @@ class ARPTestCase(VppTestCase): # configured subnet and thus no glean # self.vapi.sw_interface_set_mac_address(self.pg2.sw_if_index, - mac_string) + "00:00:00:33:33:33") def test_garp(self): """ GARP """ @@ -1287,6 +1438,7 @@ class ARPTestCase(VppTestCase): # Generate some hosts on the LAN # self.pg1.generate_remote_hosts(4) + self.pg2.generate_remote_hosts(4) # # And an ARP entry @@ -1378,7 +1530,37 @@ class ARPTestCase(VppTestCase): self.pg1.sw_if_index, self.pg1.remote_hosts[2].ip4)) - def test_arp_incomplete(self): + # + # IP address in different subnets are not learnt + # + self.pg2.configure_ipv4_neighbors() + + for op in ["is-at", "who-has"]: + p1 = [(Ether(dst="ff:ff:ff:ff:ff:ff", + src=self.pg2.remote_hosts[1].mac) / + ARP(op=op, + hwdst=self.pg2.local_mac, + hwsrc=self.pg2.remote_hosts[1].mac, + pdst=self.pg2.remote_hosts[1].ip4, + psrc=self.pg2.remote_hosts[1].ip4)), + (Ether(dst="ff:ff:ff:ff:ff:ff", + src=self.pg2.remote_hosts[1].mac) / + ARP(op=op, + hwdst="ff:ff:ff:ff:ff:ff", + hwsrc=self.pg2.remote_hosts[1].mac, + pdst=self.pg2.remote_hosts[1].ip4, + psrc=self.pg2.remote_hosts[1].ip4))] + + self.send_and_assert_no_replies(self.pg1, p1) + self.assertFalse(find_nbr(self, + self.pg1.sw_if_index, + self.pg2.remote_hosts[1].ip4)) + + # they are all dropped because the subnet's don't match + self.assertEqual(4, self.statistics.get_err_counter( + "/err/arp-reply/IP4 destination address not local to subnet")) + + def test_arp_incomplete2(self): """ Incomplete Entries """ # @@ -1539,6 +1721,95 @@ class ARPTestCase(VppTestCase): self.pg1.unconfig_ip4() self.pg1.set_table_ip4(0) + def test_glean_src_select(self): + """ Multi Connecteds """ + + # + # configure multiple connected subnets on an interface + # and ensure that ARP requests for hosts on those subnets + # pick up the correct source address + # + conn1 = VppIpInterfaceAddress(self, self.pg1, + "10.0.0.1", 24).add_vpp_config() + conn2 = VppIpInterfaceAddress(self, self.pg1, + "10.0.1.1", 24).add_vpp_config() + + p1 = (Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IP(src=self.pg1.remote_ip4, + dst="10.0.0.128") / + Raw(b'0x5' * 100)) + + rxs = self.send_and_expect(self.pg0, [p1], self.pg1) + for rx in rxs: + self.verify_arp_req(rx, + self.pg1.local_mac, + "10.0.0.1", + "10.0.0.128") + + p2 = (Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IP(src=self.pg1.remote_ip4, + dst="10.0.1.128") / + Raw(b'0x5' * 100)) + + rxs = self.send_and_expect(self.pg0, [p2], self.pg1) + for rx in rxs: + self.verify_arp_req(rx, + self.pg1.local_mac, + "10.0.1.1", + "10.0.1.128") + + # + # add a local address in the same subnet + # the source addresses are equivalent. VPP happens to + # choose the last one that was added + conn3 = VppIpInterfaceAddress(self, self.pg1, + "10.0.1.2", 24).add_vpp_config() + + rxs = self.send_and_expect(self.pg0, [p2], self.pg1) + for rx in rxs: + self.verify_arp_req(rx, + self.pg1.local_mac, + "10.0.1.2", + "10.0.1.128") + + # + # remove + # + conn3.remove_vpp_config() + rxs = self.send_and_expect(self.pg0, [p2], self.pg1) + for rx in rxs: + self.verify_arp_req(rx, + self.pg1.local_mac, + "10.0.1.1", + "10.0.1.128") + + # + # add back, this time remove the first one + # + conn3 = VppIpInterfaceAddress(self, self.pg1, + "10.0.1.2", 24).add_vpp_config() + + rxs = self.send_and_expect(self.pg0, [p2], self.pg1) + for rx in rxs: + self.verify_arp_req(rx, + self.pg1.local_mac, + "10.0.1.2", + "10.0.1.128") + + conn1.remove_vpp_config() + rxs = self.send_and_expect(self.pg0, [p2], self.pg1) + for rx in rxs: + self.verify_arp_req(rx, + self.pg1.local_mac, + "10.0.1.2", + "10.0.1.128") + + # cleanup + conn3.remove_vpp_config() + conn2.remove_vpp_config() + class NeighborStatsTestCase(VppTestCase): """ ARP/ND Counters """ @@ -1811,16 +2082,51 @@ class NeighborAgeTestCase(VppTestCase): # # load up some neighbours again with 2s aging enabled # they should be removed after 10s (2s age + 4s for probes + gap) + # check for the add and remove events # + enum = VppEnum.vl_api_ip_neighbor_event_flags_t + + self.vapi.want_ip_neighbor_events_v2(enable=1) for ii in range(10): VppNeighbor(self, self.pg0.sw_if_index, self.pg0.remote_hosts[ii].mac, self.pg0.remote_hosts[ii].ip4).add_vpp_config() + + e = self.vapi.wait_for_event(1, "ip_neighbor_event_v2") + self.assertEqual(e.flags, + enum.IP_NEIGHBOR_API_EVENT_FLAG_ADDED) + self.assertEqual(str(e.neighbor.ip_address), + self.pg0.remote_hosts[ii].ip4) + self.assertEqual(e.neighbor.mac_address, + self.pg0.remote_hosts[ii].mac) + self.sleep(10) self.assertFalse(self.vapi.ip_neighbor_dump(sw_if_index=0xffffffff, af=vaf.ADDRESS_IP4)) + evs = [] + for ii in range(10): + e = self.vapi.wait_for_event(1, "ip_neighbor_event_v2") + self.assertEqual(e.flags, + enum.IP_NEIGHBOR_API_EVENT_FLAG_REMOVED) + evs.append(e) + + # check we got the correct mac/ip pairs - done separately + # because we don't care about the order the remove notifications + # arrive + for ii in range(10): + found = False + mac = self.pg0.remote_hosts[ii].mac + ip = self.pg0.remote_hosts[ii].ip4 + + for e in evs: + if (e.neighbor.mac_address == mac and + str(e.neighbor.ip_address) == ip): + found = True + break + self.assertTrue(found) + # # check if we can set age and recycle with empty neighbor list # @@ -1849,5 +2155,234 @@ class NeighborAgeTestCase(VppTestCase): self.pg0.remote_hosts[0].ip4)) +class NeighborReplaceTestCase(VppTestCase): + """ ARP/ND Replacement """ + + @classmethod + def setUpClass(cls): + super(NeighborReplaceTestCase, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(NeighborReplaceTestCase, cls).tearDownClass() + + def setUp(self): + super(NeighborReplaceTestCase, self).setUp() + + 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() + i.config_ip4() + i.config_ip6() + i.resolve_arp() + i.resolve_ndp() + + def tearDown(self): + super(NeighborReplaceTestCase, self).tearDown() + + for i in self.pg_interfaces: + i.unconfig_ip4() + i.unconfig_ip6() + i.admin_down() + + def test_replace(self): + """ replace """ + + N_HOSTS = 16 + + for i in self.pg_interfaces: + i.generate_remote_hosts(N_HOSTS) + i.configure_ipv4_neighbors() + i.configure_ipv6_neighbors() + + # replace them all + self.vapi.ip_neighbor_replace_begin() + self.vapi.ip_neighbor_replace_end() + + for i in self.pg_interfaces: + for h in range(N_HOSTS): + self.assertFalse(find_nbr(self, + self.pg0.sw_if_index, + self.pg0.remote_hosts[h].ip4)) + self.assertFalse(find_nbr(self, + self.pg0.sw_if_index, + self.pg0.remote_hosts[h].ip6)) + + # + # and them all back via the API + # + for i in self.pg_interfaces: + for h in range(N_HOSTS): + VppNeighbor(self, + i.sw_if_index, + i.remote_hosts[h].mac, + i.remote_hosts[h].ip4).add_vpp_config() + VppNeighbor(self, + i.sw_if_index, + i.remote_hosts[h].mac, + i.remote_hosts[h].ip6).add_vpp_config() + + # + # begin the replacement again, this time touch some + # the neighbours on pg1 so they are not deleted + # + self.vapi.ip_neighbor_replace_begin() + + # update from the API all neighbours on pg1 + for h in range(N_HOSTS): + VppNeighbor(self, + self.pg1.sw_if_index, + self.pg1.remote_hosts[h].mac, + self.pg1.remote_hosts[h].ip4).add_vpp_config() + VppNeighbor(self, + self.pg1.sw_if_index, + self.pg1.remote_hosts[h].mac, + self.pg1.remote_hosts[h].ip6).add_vpp_config() + + # update from the data-plane all neighbours on pg3 + self.pg3.configure_ipv4_neighbors() + self.pg3.configure_ipv6_neighbors() + + # complete the replacement + self.logger.info(self.vapi.cli("sh ip neighbors")) + self.vapi.ip_neighbor_replace_end() + + for i in self.pg_interfaces: + if i == self.pg1 or i == self.pg3: + # neighbours on pg1 and pg3 are still present + for h in range(N_HOSTS): + self.assertTrue(find_nbr(self, + i.sw_if_index, + i.remote_hosts[h].ip4)) + self.assertTrue(find_nbr(self, + i.sw_if_index, + i.remote_hosts[h].ip6)) + else: + # all other neighbours are toast + for h in range(N_HOSTS): + self.assertFalse(find_nbr(self, + i.sw_if_index, + i.remote_hosts[h].ip4)) + self.assertFalse(find_nbr(self, + i.sw_if_index, + i.remote_hosts[h].ip6)) + + +class NeighborFlush(VppTestCase): + """ Neighbor Flush """ + + @classmethod + def setUpClass(cls): + super(NeighborFlush, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(NeighborFlush, cls).tearDownClass() + + def setUp(self): + super(NeighborFlush, self).setUp() + + self.create_pg_interfaces(range(2)) + + for i in self.pg_interfaces: + i.admin_up() + i.config_ip4() + i.config_ip6() + i.resolve_arp() + i.resolve_ndp() + + def tearDown(self): + super(NeighborFlush, self).tearDown() + + for i in self.pg_interfaces: + i.unconfig_ip4() + i.unconfig_ip6() + i.admin_down() + + def test_flush(self): + """ Neighbour Flush """ + + e = VppEnum + nf = e.vl_api_ip_neighbor_flags_t + af = e.vl_api_address_family_t + N_HOSTS = 16 + static = [False, True] + self.pg0.generate_remote_hosts(N_HOSTS) + self.pg1.generate_remote_hosts(N_HOSTS) + + for s in static: + # a few v4 and v6 dynamic neoghbors + for n in range(N_HOSTS): + VppNeighbor(self, + self.pg0.sw_if_index, + self.pg0.remote_hosts[n].mac, + self.pg0.remote_hosts[n].ip4, + is_static=s).add_vpp_config() + VppNeighbor(self, + self.pg1.sw_if_index, + self.pg1.remote_hosts[n].mac, + self.pg1.remote_hosts[n].ip6, + is_static=s).add_vpp_config() + + # flush the interfaces individually + self.vapi.ip_neighbor_flush(af.ADDRESS_IP4, self.pg0.sw_if_index) + + # check we haven't flushed that which we shouldn't + for n in range(N_HOSTS): + self.assertTrue(find_nbr(self, + self.pg1.sw_if_index, + self.pg1.remote_hosts[n].ip6, + is_static=s)) + + self.vapi.ip_neighbor_flush(af.ADDRESS_IP6, self.pg1.sw_if_index) + + for n in range(N_HOSTS): + self.assertFalse(find_nbr(self, + self.pg0.sw_if_index, + self.pg0.remote_hosts[n].ip4)) + self.assertFalse(find_nbr(self, + self.pg1.sw_if_index, + self.pg1.remote_hosts[n].ip6)) + + # add the nieghbours back + for n in range(N_HOSTS): + VppNeighbor(self, + self.pg0.sw_if_index, + self.pg0.remote_hosts[n].mac, + self.pg0.remote_hosts[n].ip4, + is_static=s).add_vpp_config() + VppNeighbor(self, + self.pg1.sw_if_index, + self.pg1.remote_hosts[n].mac, + self.pg1.remote_hosts[n].ip6, + is_static=s).add_vpp_config() + + self.logger.info(self.vapi.cli("sh ip neighbor")) + + # flush both interfaces at the same time + self.vapi.ip_neighbor_flush(af.ADDRESS_IP6, 0xffffffff) + + # check we haven't flushed that which we shouldn't + for n in range(N_HOSTS): + self.assertTrue(find_nbr(self, + self.pg0.sw_if_index, + self.pg0.remote_hosts[n].ip4, + is_static=s)) + + self.vapi.ip_neighbor_flush(af.ADDRESS_IP4, 0xffffffff) + + for n in range(N_HOSTS): + self.assertFalse(find_nbr(self, + self.pg0.sw_if_index, + self.pg0.remote_hosts[n].ip4)) + self.assertFalse(find_nbr(self, + self.pg1.sw_if_index, + self.pg1.remote_hosts[n].ip6)) + + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner)