X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=test%2Ftest_ip6.py;h=c9efcbbc51c606f8a901bd38e9c6ff37522f284f;hb=c037423;hp=1432858f38cdd1dbed272116d1cc228b0fa74c89;hpb=dcd6d6254a2b204a4283c889d1feac8f59a62639;p=vpp.git diff --git a/test/test_ip6.py b/test/test_ip6.py index 1432858f38c..c9efcbbc51c 100644 --- a/test/test_ip6.py +++ b/test/test_ip6.py @@ -1,30 +1,33 @@ #!/usr/bin/env python import unittest -from socket import AF_INET6 +import socket from framework import VppTestCase, VppTestRunner +from util import ppp, ip6_normalize from vpp_sub_interface import VppSubInterface, VppDot1QSubint from vpp_pg_interface import is_ipv6_misc from vpp_ip_route import VppIpRoute, VppRoutePath, find_route, VppIpMRoute, \ VppMRoutePath, MRouteItfFlags, MRouteEntryFlags, VppMplsIpBind, \ - VppMplsRoute + VppMplsRoute, DpoProto, VppMplsTable from vpp_neighbor import find_nbr, VppNeighbor from scapy.packet import Raw from scapy.layers.l2 import Ether, Dot1Q -from scapy.layers.inet6 import IPv6, UDP, ICMPv6ND_NS, ICMPv6ND_RS, \ +from scapy.layers.inet6 import IPv6, UDP, TCP, ICMPv6ND_NS, ICMPv6ND_RS, \ ICMPv6ND_RA, ICMPv6NDOptSrcLLAddr, getmacbyip6, ICMPv6MRD_Solicitation, \ ICMPv6NDOptMTU, ICMPv6NDOptSrcLLAddr, ICMPv6NDOptPrefixInfo, \ - ICMPv6ND_NA, ICMPv6NDOptDstLLAddr, ICMPv6DestUnreach, icmp6types - -from util import ppp + ICMPv6ND_NA, ICMPv6NDOptDstLLAddr, ICMPv6DestUnreach, icmp6types, \ + ICMPv6TimeExceeded from scapy.utils6 import in6_getnsma, in6_getnsmac, in6_ptop, in6_islladdr, \ in6_mactoifaceid, in6_ismaddr from scapy.utils import inet_pton, inet_ntop from scapy.contrib.mpls import MPLS +AF_INET6 = socket.AF_INET6 + + def mk_ll_addr(mac): euid = in6_mactoifaceid(mac) addr = "fe80::" + euid @@ -135,14 +138,6 @@ class TestIPv6ND(VppTestCase): rx = rx[0] self.validate_ns(rx_intf, rx, tgt_ip) - def send_and_assert_no_replies(self, intf, pkts, remark): - intf.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - for i in self.pg_interfaces: - i.get_capture(0) - i.assert_nothing_captured(remark=remark) - def verify_ip(self, rx, smac, dmac, sip, dip): ether = rx[Ether] self.assertEqual(ether.dst, dmac) @@ -490,7 +485,7 @@ class TestIPv6(TestIPv6ND): inet=AF_INET6)) def test_ns_duplicates(self): - """ ARP Duplicates""" + """ ND Duplicates""" # # Generate some hosts on the LAN @@ -537,7 +532,7 @@ class TestIPv6(TestIPv6ND): # # remove the duplicate on pg1 - # packet stream shoud generate ARPs out of pg1 + # packet stream shoud generate NSs out of pg1 # ns_pg1.remove_vpp_config() @@ -902,6 +897,289 @@ class TestIPv6(TestIPv6ND): self.pg0.ip6_ra_config(no=1, suppress=1, send_unicast=0) +class TestIPv6RD(TestIPv6ND): + """ IPv6 Router Discovery Test Case """ + + @classmethod + def setUpClass(cls): + super(TestIPv6RD, cls).setUpClass() + + def setUp(self): + super(TestIPv6RD, self).setUp() + + # create 2 pg interfaces + self.create_pg_interfaces(range(2)) + + self.interfaces = list(self.pg_interfaces) + + # setup all interfaces + for i in self.interfaces: + i.admin_up() + i.config_ip6() + + def tearDown(self): + super(TestIPv6RD, self).tearDown() + + def test_rd_send_router_solicitation(self): + """ Verify router solicitation packets """ + + count = 2 + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.vapi.ip6nd_send_router_solicitation(self.pg1.sw_if_index, + mrc=count) + rx_list = self.pg1.get_capture(count, timeout=3) + self.assertEqual(len(rx_list), count) + for packet in rx_list: + self.assertTrue(packet.haslayer(IPv6)) + self.assertTrue(packet[IPv6].haslayer(ICMPv6ND_RS)) + dst = ip6_normalize(packet[IPv6].dst) + dst2 = ip6_normalize("ff02::2") + self.assert_equal(dst, dst2) + src = ip6_normalize(packet[IPv6].src) + src2 = ip6_normalize(self.pg1.local_ip6_ll) + self.assert_equal(src, src2) + self.assertTrue(packet[ICMPv6ND_RS].haslayer(ICMPv6NDOptSrcLLAddr)) + self.assert_equal(packet[ICMPv6NDOptSrcLLAddr].lladdr, + self.pg1.local_mac) + + def verify_prefix_info(self, reported_prefix, prefix_option): + prefix = socket.inet_pton(socket.AF_INET6, + prefix_option.getfieldval("prefix")) + self.assert_equal(reported_prefix.dst_address, prefix) + self.assert_equal(reported_prefix.dst_address_length, + prefix_option.getfieldval("prefixlen")) + L = prefix_option.getfieldval("L") + A = prefix_option.getfieldval("A") + option_flags = (L << 7) | (A << 6) + self.assert_equal(reported_prefix.flags, option_flags) + self.assert_equal(reported_prefix.valid_time, + prefix_option.getfieldval("validlifetime")) + self.assert_equal(reported_prefix.preferred_time, + prefix_option.getfieldval("preferredlifetime")) + + def test_rd_receive_router_advertisement(self): + """ Verify events triggered by received RA packets """ + + self.vapi.want_ip6_ra_events() + + prefix_info_1 = ICMPv6NDOptPrefixInfo( + prefix="1::2", + prefixlen=50, + validlifetime=200, + preferredlifetime=500, + L=1, + A=1, + ) + + prefix_info_2 = ICMPv6NDOptPrefixInfo( + prefix="7::4", + prefixlen=20, + validlifetime=70, + preferredlifetime=1000, + L=1, + A=0, + ) + + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IPv6(dst=self.pg1.local_ip6_ll, + src=mk_ll_addr(self.pg1.remote_mac)) / + ICMPv6ND_RA() / + prefix_info_1 / + prefix_info_2) + self.pg1.add_stream([p]) + self.pg_start() + + ev = self.vapi.wait_for_event(10, "ip6_ra_event") + + self.assert_equal(ev.current_hop_limit, 0) + self.assert_equal(ev.flags, 8) + self.assert_equal(ev.router_lifetime_in_sec, 1800) + self.assert_equal(ev.neighbor_reachable_time_in_msec, 0) + self.assert_equal( + ev.time_in_msec_between_retransmitted_neighbor_solicitations, 0) + + self.assert_equal(ev.n_prefixes, 2) + + self.verify_prefix_info(ev.prefixes[0], prefix_info_1) + self.verify_prefix_info(ev.prefixes[1], prefix_info_2) + + +class TestIPv6RDControlPlane(TestIPv6ND): + """ IPv6 Router Discovery Control Plane Test Case """ + + @classmethod + def setUpClass(cls): + super(TestIPv6RDControlPlane, cls).setUpClass() + + def setUp(self): + super(TestIPv6RDControlPlane, self).setUp() + + # create 1 pg interface + self.create_pg_interfaces(range(1)) + + self.interfaces = list(self.pg_interfaces) + + # setup all interfaces + for i in self.interfaces: + i.admin_up() + i.config_ip6() + + def tearDown(self): + super(TestIPv6RDControlPlane, self).tearDown() + + @staticmethod + def create_ra_packet(pg, routerlifetime=None): + src_ip = pg.remote_ip6_ll + dst_ip = pg.local_ip6 + if routerlifetime is not None: + ra = ICMPv6ND_RA(routerlifetime=routerlifetime) + else: + ra = ICMPv6ND_RA() + p = (Ether(dst=pg.local_mac, src=pg.remote_mac) / + IPv6(dst=dst_ip, src=src_ip) / ra) + return p + + @staticmethod + def get_default_routes(fib): + list = [] + for entry in fib: + if entry.address_length == 0: + for path in entry.path: + if path.sw_if_index != 0xFFFFFFFF: + defaut_route = {} + defaut_route['sw_if_index'] = path.sw_if_index + defaut_route['next_hop'] = path.next_hop + list.append(defaut_route) + return list + + @staticmethod + def get_interface_addresses(fib, pg): + list = [] + for entry in fib: + if entry.address_length == 128: + path = entry.path[0] + if path.sw_if_index == pg.sw_if_index: + list.append(entry.address) + return list + + def test_all(self): + """ Test handling of SLAAC addresses and default routes """ + + fib = self.vapi.ip6_fib_dump() + default_routes = self.get_default_routes(fib) + initial_addresses = set(self.get_interface_addresses(fib, self.pg0)) + self.assertEqual(default_routes, []) + router_address = self.pg0.remote_ip6n_ll + + self.vapi.ip6_nd_address_autoconfig(self.pg0.sw_if_index, 1, 1) + + self.sleep(0.1) + + # send RA + packet = (self.create_ra_packet(self.pg0) / ICMPv6NDOptPrefixInfo( + prefix="1::", + prefixlen=64, + validlifetime=2, + preferredlifetime=2, + L=1, + A=1, + ) / ICMPv6NDOptPrefixInfo( + prefix="7::", + prefixlen=20, + validlifetime=1500, + preferredlifetime=1000, + L=1, + A=0, + )) + self.pg0.add_stream([packet]) + self.pg_start() + + self.sleep(0.1) + + fib = self.vapi.ip6_fib_dump() + + # check FIB for new address + addresses = set(self.get_interface_addresses(fib, self.pg0)) + new_addresses = addresses.difference(initial_addresses) + self.assertEqual(len(new_addresses), 1) + prefix = list(new_addresses)[0][:8] + '\0\0\0\0\0\0\0\0' + self.assertEqual(inet_ntop(AF_INET6, prefix), '1::') + + # check FIB for new default route + default_routes = self.get_default_routes(fib) + self.assertEqual(len(default_routes), 1) + dr = default_routes[0] + self.assertEqual(dr['sw_if_index'], self.pg0.sw_if_index) + self.assertEqual(dr['next_hop'], router_address) + + # send RA to delete default route + packet = self.create_ra_packet(self.pg0, routerlifetime=0) + self.pg0.add_stream([packet]) + self.pg_start() + + self.sleep(0.1) + + # check that default route is deleted + fib = self.vapi.ip6_fib_dump() + default_routes = self.get_default_routes(fib) + self.assertEqual(len(default_routes), 0) + + self.sleep(0.1) + + # send RA + packet = self.create_ra_packet(self.pg0) + self.pg0.add_stream([packet]) + self.pg_start() + + self.sleep(0.1) + + # check FIB for new default route + fib = self.vapi.ip6_fib_dump() + default_routes = self.get_default_routes(fib) + self.assertEqual(len(default_routes), 1) + dr = default_routes[0] + self.assertEqual(dr['sw_if_index'], self.pg0.sw_if_index) + self.assertEqual(dr['next_hop'], router_address) + + # send RA, updating router lifetime to 1s + packet = self.create_ra_packet(self.pg0, 1) + self.pg0.add_stream([packet]) + self.pg_start() + + self.sleep(0.1) + + # check that default route still exists + fib = self.vapi.ip6_fib_dump() + default_routes = self.get_default_routes(fib) + self.assertEqual(len(default_routes), 1) + dr = default_routes[0] + self.assertEqual(dr['sw_if_index'], self.pg0.sw_if_index) + self.assertEqual(dr['next_hop'], router_address) + + self.sleep(1) + + # check that default route is deleted + fib = self.vapi.ip6_fib_dump() + default_routes = self.get_default_routes(fib) + self.assertEqual(len(default_routes), 0) + + # check FIB still contains the SLAAC address + addresses = set(self.get_interface_addresses(fib, self.pg0)) + new_addresses = addresses.difference(initial_addresses) + self.assertEqual(len(new_addresses), 1) + prefix = list(new_addresses)[0][:8] + '\0\0\0\0\0\0\0\0' + self.assertEqual(inet_ntop(AF_INET6, prefix), '1::') + + self.sleep(1) + + # check that SLAAC address is deleted + fib = self.vapi.ip6_fib_dump() + addresses = set(self.get_interface_addresses(fib, self.pg0)) + new_addresses = addresses.difference(initial_addresses) + self.assertEqual(len(new_addresses), 0) + + class IPv6NDProxyTest(TestIPv6ND): """ IPv6 ND ProxyTest Case """ @@ -1178,14 +1456,6 @@ class TestIPDisabled(VppTestCase): i.unconfig_ip4() i.admin_down() - def send_and_assert_no_replies(self, intf, pkts, remark): - intf.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - for i in self.pg_interfaces: - i.get_capture(0) - i.assert_nothing_captured(remark=remark) - def test_ip_disabled(self): """ IP Disabled """ @@ -1260,6 +1530,9 @@ class TestIP6LoadBalance(VppTestCase): self.create_pg_interfaces(range(5)) + mpls_tbl = VppMplsTable(self, 0) + mpls_tbl.add_vpp_config() + for i in self.pg_interfaces: i.admin_up() i.config_ip6() @@ -1267,13 +1540,14 @@ class TestIP6LoadBalance(VppTestCase): i.enable_mpls() def tearDown(self): - super(TestIP6LoadBalance, self).tearDown() for i in self.pg_interfaces: i.unconfig_ip6() i.admin_down() i.disable_mpls() + super(TestIP6LoadBalance, self).tearDown() def send_and_expect_load_balancing(self, input, pkts, outputs): + self.vapi.cli("clear trace") input.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() @@ -1282,6 +1556,7 @@ class TestIP6LoadBalance(VppTestCase): self.assertNotEqual(0, len(rx)) def send_and_expect_one_itf(self, input, pkts, itf): + self.vapi.cli("clear trace") input.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() @@ -1347,10 +1622,10 @@ class TestIP6LoadBalance(VppTestCase): route_3000_1 = VppIpRoute(self, "3000::1", 128, [VppRoutePath(self.pg1.remote_ip6, self.pg1.sw_if_index, - is_ip6=1), + proto=DpoProto.DPO_PROTO_IP6), VppRoutePath(self.pg2.remote_ip6, self.pg2.sw_if_index, - is_ip6=1)], + proto=DpoProto.DPO_PROTO_IP6)], is_ip6=1) route_3000_1.add_vpp_config() @@ -1367,11 +1642,11 @@ class TestIP6LoadBalance(VppTestCase): [VppRoutePath(self.pg1.remote_ip6, self.pg1.sw_if_index, labels=[67], - is_ip6=1), + proto=DpoProto.DPO_PROTO_IP6), VppRoutePath(self.pg2.remote_ip6, self.pg2.sw_if_index, labels=[67], - is_ip6=1)]) + proto=DpoProto.DPO_PROTO_IP6)]) route_67.add_vpp_config() # @@ -1441,20 +1716,20 @@ class TestIP6LoadBalance(VppTestCase): route_3000_2 = VppIpRoute(self, "3000::2", 128, [VppRoutePath(self.pg3.remote_ip6, self.pg3.sw_if_index, - is_ip6=1), + proto=DpoProto.DPO_PROTO_IP6), VppRoutePath(self.pg4.remote_ip6, self.pg4.sw_if_index, - is_ip6=1)], + proto=DpoProto.DPO_PROTO_IP6)], is_ip6=1) route_3000_2.add_vpp_config() route_4000_1 = VppIpRoute(self, "4000::1", 128, [VppRoutePath("3000::1", 0xffffffff, - is_ip6=1), + proto=DpoProto.DPO_PROTO_IP6), VppRoutePath("3000::2", 0xffffffff, - is_ip6=1)], + proto=DpoProto.DPO_PROTO_IP6)], is_ip6=1) route_4000_1.add_vpp_config() @@ -1469,6 +1744,191 @@ class TestIP6LoadBalance(VppTestCase): [self.pg1, self.pg2, self.pg3, self.pg4]) + # + # Recursive prefixes + # - testing that 2 stages of load-balancing no choices + # + port_pkts = [] + + for ii in range(257): + port_pkts.append((Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IPv6(dst="6000::1", src="6000:1::1") / + UDP(sport=1234, dport=1234 + ii) / + Raw('\xa5' * 100))) + + route_5000_2 = VppIpRoute(self, "5000::2", 128, + [VppRoutePath(self.pg3.remote_ip6, + self.pg3.sw_if_index, + proto=DpoProto.DPO_PROTO_IP6)], + is_ip6=1) + route_5000_2.add_vpp_config() + + route_6000_1 = VppIpRoute(self, "6000::1", 128, + [VppRoutePath("5000::2", + 0xffffffff, + proto=DpoProto.DPO_PROTO_IP6)], + is_ip6=1) + route_6000_1.add_vpp_config() + + # + # inject the packet on pg0 - expect load-balancing across all 4 paths + # + self.vapi.cli("clear trace") + self.send_and_expect_one_itf(self.pg0, port_pkts, self.pg3) + + +class TestIP6Punt(VppTestCase): + """ IPv6 Punt Police/Redirect """ + + def setUp(self): + super(TestIP6Punt, self).setUp() + + self.create_pg_interfaces(range(2)) + + for i in self.pg_interfaces: + i.admin_up() + i.config_ip6() + i.resolve_ndp() + + def tearDown(self): + super(TestIP6Punt, self).tearDown() + for i in self.pg_interfaces: + i.unconfig_ip6() + i.admin_down() + + def test_ip_punt(self): + """ IP6 punt police and redirect """ + + p = (Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) / + TCP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + pkts = p * 1025 + + # + # Configure a punt redirect via pg1. + # + nh_addr = inet_pton(AF_INET6, + self.pg1.remote_ip6) + self.vapi.ip_punt_redirect(self.pg0.sw_if_index, + self.pg1.sw_if_index, + nh_addr, + is_ip6=1) + + self.send_and_expect(self.pg0, pkts, self.pg1) + + # + # add a policer + # + policer = self.vapi.policer_add_del("ip6-punt", 400, 0, 10, 0, + rate_type=1) + self.vapi.ip_punt_police(policer.policer_index, is_ip6=1) + + self.vapi.cli("clear trace") + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # + # the number of packet recieved should be greater than 0, + # but not equal to the number sent, since some were policed + # + rx = self.pg1._get_capture(1) + self.assertTrue(len(rx) > 0) + self.assertTrue(len(rx) < len(pkts)) + + # + # remove the poilcer. back to full rx + # + self.vapi.ip_punt_police(policer.policer_index, is_add=0, is_ip6=1) + self.vapi.policer_add_del("ip6-punt", 400, 0, 10, 0, + rate_type=1, is_add=0) + self.send_and_expect(self.pg0, pkts, self.pg1) + + # + # remove the redirect. expect full drop. + # + self.vapi.ip_punt_redirect(self.pg0.sw_if_index, + self.pg1.sw_if_index, + nh_addr, + is_add=0, + is_ip6=1) + self.send_and_assert_no_replies(self.pg0, pkts, + "IP no punt config") + + # + # Add a redirect that is not input port selective + # + self.vapi.ip_punt_redirect(0xffffffff, + self.pg1.sw_if_index, + nh_addr, + is_ip6=1) + self.send_and_expect(self.pg0, pkts, self.pg1) + + self.vapi.ip_punt_redirect(0xffffffff, + self.pg1.sw_if_index, + nh_addr, + is_add=0, + is_ip6=1) + + +class TestIP6Input(VppTestCase): + """ IPv6 Input Exceptions """ + + def setUp(self): + super(TestIP6Input, self).setUp() + + self.create_pg_interfaces(range(2)) + + for i in self.pg_interfaces: + i.admin_up() + i.config_ip6() + i.resolve_ndp() + + def tearDown(self): + super(TestIP6Input, self).tearDown() + for i in self.pg_interfaces: + i.unconfig_ip6() + i.admin_down() + + def test_ip_input(self): + """ IP6 Input Exceptions """ + + # + # bad version - this is dropped + # + p_version = (Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IPv6(src=self.pg0.remote_ip6, + dst=self.pg1.remote_ip6, + version=3) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.send_and_assert_no_replies(self.pg0, p_version * 65, + "funky version") + + # + # hop limit - IMCP replies + # + p_version = (Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IPv6(src=self.pg0.remote_ip6, + dst=self.pg1.remote_ip6, + hlim=1) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rx = self.send_and_expect(self.pg0, p_version * 65, self.pg0) + rx = rx[0] + icmp = rx[ICMPv6TimeExceeded] + self.assertEqual(icmp.type, 3) + # 0: "hop limit exceeded in transit", + self.assertEqual(icmp.code, 0) + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner)