X-Git-Url: https://gerrit.fd.io/r/gitweb?p=vpp.git;a=blobdiff_plain;f=test%2Ftest_ip6.py;h=990b53ec0357fcd113bba7d0ef36bac948fa032d;hp=21d8025d583f13c9f5e52c1e8daedd850dbc4eb2;hb=59f71132e;hpb=744902e306c5a1f00bf13bbe344c6616cc816f0e diff --git a/test/test_ip6.py b/test/test_ip6.py index 21d8025d583..990b53ec035 100644 --- a/test/test_ip6.py +++ b/test/test_ip6.py @@ -1,37 +1,45 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -import unittest import socket +from socket import inet_pton, inet_ntop +import unittest + +from parameterized import parameterized +import scapy.compat +import scapy.layers.inet6 as inet6 +from scapy.contrib.mpls import MPLS +from scapy.layers.inet6 import IPv6, ICMPv6ND_NS, ICMPv6ND_RS, \ + ICMPv6ND_RA, ICMPv6NDOptMTU, ICMPv6NDOptSrcLLAddr, ICMPv6NDOptPrefixInfo, \ + ICMPv6ND_NA, ICMPv6NDOptDstLLAddr, ICMPv6DestUnreach, icmp6types, \ + ICMPv6TimeExceeded, ICMPv6EchoRequest, ICMPv6EchoReply, \ + IPv6ExtHdrHopByHop, ICMPv6MLReport2, ICMPv6MLDMultAddrRec +from scapy.layers.l2 import Ether, Dot1Q +from scapy.packet import Raw +from scapy.utils6 import in6_getnsma, in6_getnsmac, in6_ptop, in6_islladdr, \ + in6_mactoifaceid +from six import moves 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 util import ppp, ip6_normalize, mk_ll_addr +from vpp_ip import DpoProto from vpp_ip_route import VppIpRoute, VppRoutePath, find_route, VppIpMRoute, \ VppMRoutePath, MRouteItfFlags, MRouteEntryFlags, VppMplsIpBind, \ - VppMplsRoute, DpoProto, VppMplsTable + VppMplsRoute, VppMplsTable, VppIpTable, FibPathType, FibPathProto, \ + VppIpInterfaceAddress, find_route_in_dump, find_mroute_in_dump 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, TCP, ICMPv6ND_NS, ICMPv6ND_RS, \ - ICMPv6ND_RA, ICMPv6NDOptSrcLLAddr, getmacbyip6, ICMPv6MRD_Solicitation, \ - ICMPv6NDOptMTU, ICMPv6NDOptSrcLLAddr, ICMPv6NDOptPrefixInfo, \ - 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 - +from vpp_pg_interface import is_ipv6_misc +from vpp_sub_interface import VppSubInterface, VppDot1QSubint +from vpp_policer import VppPolicer +from ipaddress import IPv6Network, IPv6Address AF_INET6 = socket.AF_INET6 +try: + text_type = unicode +except NameError: + text_type = str -def mk_ll_addr(mac): - euid = in6_mactoifaceid(mac) - addr = "fe80::" + euid - return addr +NUM_PKTS = 67 class TestIPv6ND(VppTestCase): @@ -73,7 +81,8 @@ class TestIPv6ND(VppTestCase): in6_ptop(dst_ip)) # and come from the target address - self.assertEqual(in6_ptop(rx[IPv6].src), in6_ptop(tgt_ip)) + self.assertEqual( + in6_ptop(rx[IPv6].src), in6_ptop(tgt_ip)) # Dest link-layer options should have the router's MAC dll = rx[ICMPv6NDOptDstLLAddr] @@ -91,14 +100,16 @@ class TestIPv6ND(VppTestCase): # the rx'd NS should be addressed to an mcast address # derived from the target address - self.assertEqual(in6_ptop(rx[IPv6].dst), in6_ptop(dst_ip)) + self.assertEqual( + in6_ptop(rx[IPv6].dst), in6_ptop(dst_ip)) # expect the tgt IP in the NS header ns = rx[ICMPv6ND_NS] self.assertEqual(in6_ptop(ns.tgt), in6_ptop(tgt_ip)) # packet is from the router's local address - self.assertEqual(in6_ptop(rx[IPv6].src), intf.local_ip6) + self.assertEqual( + in6_ptop(rx[IPv6].src), intf.local_ip6) # Src link-layer options should have the router's MAC sll = rx[ICMPv6NDOptSrcLLAddr] @@ -129,6 +140,7 @@ class TestIPv6ND(VppTestCase): def send_and_expect_ns(self, tx_intf, rx_intf, pkts, tgt_ip, filter_out_fn=is_ipv6_misc): + self.vapi.cli("clear trace") tx_intf.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() @@ -155,6 +167,10 @@ class TestIPv6(TestIPv6ND): def setUpClass(cls): super(TestIPv6, cls).setUpClass() + @classmethod + def tearDownClass(cls): + super(TestIPv6, cls).tearDownClass() + def setUp(self): """ Perform test setup before test case. @@ -172,7 +188,6 @@ class TestIPv6(TestIPv6ND): :ivar list interfaces: pg interfaces and subinterfaces. :ivar dict flows: IPv4 packet flows in test. - :ivar list pg_if_packet_sizes: packet sizes in test. *TODO:* Create AD sub interface """ @@ -195,8 +210,7 @@ class TestIPv6(TestIPv6ND): self.flows[self.pg2.sub_if] = [self.pg0, self.pg1.sub_if] # packet sizes - self.pg_if_packet_sizes = [64, 512, 1518, 9018] - self.sub_if_packet_sizes = [64, 512, 1518 + 4, 9018 + 4] + self.pg_if_packet_sizes = [64, 1500, 9020] self.interfaces = list(self.pg_interfaces) self.interfaces.extend(self.sub_interfaces) @@ -207,14 +221,10 @@ class TestIPv6(TestIPv6ND): i.config_ip6() i.resolve_ndp() - # config 2M FIB entries - self.config_fib_entries(200) - def tearDown(self): """Run standard test teardown and log ``show ip6 neighbors``.""" for i in self.interfaces: i.unconfig_ip6() - i.ip6_disable() i.admin_down() for i in self.sub_interfaces: i.remove_vpp_config() @@ -224,53 +234,46 @@ class TestIPv6(TestIPv6ND): self.logger.info(self.vapi.cli("show ip6 neighbors")) # info(self.vapi.cli("show ip6 fib")) # many entries - def config_fib_entries(self, count): - """For each interface add to the FIB table *count* routes to - "fd02::1/128" destination with interface's local address as next-hop - address. - - :param int count: Number of FIB entries. + def modify_packet(self, src_if, packet_size, pkt): + """Add load, set destination IP and extend packet to required packet + size for defined interface. - - *TODO:* check if the next-hop address shouldn't be remote address - instead of local address. + :param VppInterface src_if: Interface to create packet for. + :param int packet_size: Required packet size. + :param Scapy pkt: Packet to be modified. """ - n_int = len(self.interfaces) - percent = 0 - counter = 0.0 - dest_addr = inet_pton(AF_INET6, "fd02::1") - dest_addr_len = 128 - for i in self.interfaces: - next_hop_address = i.local_ip6n - for j in range(count / n_int): - self.vapi.ip_add_del_route( - dest_addr, dest_addr_len, next_hop_address, is_ipv6=1) - counter += 1 - if counter / count * 100 > percent: - self.logger.info("Configure %d FIB entries .. %d%% done" % - (count, percent)) - percent += 1 - - def create_stream(self, src_if, packet_sizes): + dst_if_idx = int(packet_size / 10 % 2) + dst_if = self.flows[src_if][dst_if_idx] + info = self.create_packet_info(src_if, dst_if) + payload = self.info_to_payload(info) + p = pkt / Raw(payload) + p[IPv6].dst = dst_if.remote_ip6 + info.data = p.copy() + if isinstance(src_if, VppSubInterface): + p = src_if.add_dot1_layer(p) + self.extend_packet(p, packet_size) + + return p + + def create_stream(self, src_if): """Create input packet stream for defined interface. :param VppInterface src_if: Interface to create packet stream for. - :param list packet_sizes: Required packet sizes. """ - pkts = [] - for i in range(0, 257): - dst_if = self.flows[src_if][i % 2] - info = self.create_packet_info(src_if, dst_if) - payload = self.info_to_payload(info) - p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / - IPv6(src=src_if.remote_ip6, dst=dst_if.remote_ip6) / - UDP(sport=1234, dport=1234) / - Raw(payload)) - info.data = p.copy() - if isinstance(src_if, VppSubInterface): - p = src_if.add_dot1_layer(p) - size = packet_sizes[(i // 2) % len(packet_sizes)] - self.extend_packet(p, size) - pkts.append(p) + hdr_ext = 4 if isinstance(src_if, VppSubInterface) else 0 + pkt_tmpl = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / + IPv6(src=src_if.remote_ip6) / + inet6.UDP(sport=1234, dport=1234)) + + pkts = [self.modify_packet(src_if, i, pkt_tmpl) + for i in moves.range(self.pg_if_packet_sizes[0], + self.pg_if_packet_sizes[1], 10)] + pkts_b = [self.modify_packet(src_if, i, pkt_tmpl) + for i in moves.range(self.pg_if_packet_sizes[1] + hdr_ext, + self.pg_if_packet_sizes[2] + hdr_ext, + 50)] + pkts.extend(pkts_b) + return pkts def verify_capture(self, dst_if, capture): @@ -295,8 +298,8 @@ class TestIPv6(TestIPv6ND): self.assertTrue(Dot1Q not in packet) try: ip = packet[IPv6] - udp = packet[UDP] - payload_info = self.payload_to_info(str(packet[Raw])) + udp = packet[inet6.UDP] + payload_info = self.payload_to_info(packet[Raw]) packet_index = payload_info.index self.assertEqual(payload_info.dst, dst_sw_if_index) self.logger.debug( @@ -310,10 +313,14 @@ class TestIPv6(TestIPv6ND): self.assertEqual(packet_index, next_info.index) saved_packet = next_info.data # Check standard fields - self.assertEqual(ip.src, saved_packet[IPv6].src) - self.assertEqual(ip.dst, saved_packet[IPv6].dst) - self.assertEqual(udp.sport, saved_packet[UDP].sport) - self.assertEqual(udp.dport, saved_packet[UDP].dport) + self.assertEqual( + ip.src, saved_packet[IPv6].src) + self.assertEqual( + ip.dst, saved_packet[IPv6].dst) + self.assertEqual( + udp.sport, saved_packet[inet6.UDP].sport) + self.assertEqual( + udp.dport, saved_packet[inet6.UDP].dport) except: self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise @@ -324,6 +331,24 @@ class TestIPv6(TestIPv6ND): "Interface %s: Packet expected from interface %s " "didn't arrive" % (dst_if.name, i.name)) + def test_next_header_anomaly(self): + """ IPv6 next header anomaly test + + Test scenario: + - ipv6 next header field = Fragment Header (44) + - next header is ICMPv6 Echo Request + - wait for reassembly + """ + pkt = (Ether(src=self.pg0.local_mac, dst=self.pg0.remote_mac) / + IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6, nh=44) / + ICMPv6EchoRequest()) + + self.pg0.add_stream(pkt) + self.pg_start() + + # wait for reassembly + self.sleep(10) + def test_fib(self): """ IPv6 FIB test @@ -333,11 +358,11 @@ class TestIPv6(TestIPv6ND): - Send and verify received packets on each interface. """ - pkts = self.create_stream(self.pg0, self.pg_if_packet_sizes) + pkts = self.create_stream(self.pg0) self.pg0.add_stream(pkts) for i in self.sub_interfaces: - pkts = self.create_stream(i, self.sub_if_packet_sizes) + pkts = self.create_stream(i) i.parent.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) @@ -368,7 +393,8 @@ class TestIPv6(TestIPv6ND): p = (Ether(dst=in6_getnsmac(nsma)) / IPv6(dst=d, src="2002::2") / ICMPv6ND_NS(tgt=self.pg0.local_ip6) / - ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac)) + ICMPv6NDOptSrcLLAddr( + lladdr=self.pg0.remote_mac)) pkts = [p] self.send_and_assert_no_replies( @@ -386,7 +412,8 @@ class TestIPv6(TestIPv6ND): p = (Ether(dst=in6_getnsmac(nsma)) / IPv6(dst=d, src=self.pg0.remote_ip6) / ICMPv6ND_NS(tgt=self.pg0.local_ip6) / - ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac)) + ICMPv6NDOptSrcLLAddr( + lladdr=self.pg0.remote_mac)) pkts = [p] self.send_and_assert_no_replies( @@ -402,7 +429,8 @@ class TestIPv6(TestIPv6ND): p = (Ether(dst=in6_getnsmac(nsma)) / IPv6(dst=d, src=self.pg0.remote_ip6) / ICMPv6ND_NS(tgt="fd::ffff") / - ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac)) + ICMPv6NDOptSrcLLAddr( + lladdr=self.pg0.remote_mac)) pkts = [p] self.send_and_assert_no_replies(self.pg0, pkts, @@ -416,7 +444,6 @@ class TestIPv6(TestIPv6ND): self.pg0.sw_if_index, self.pg0.remote_hosts[2].mac, self.pg0.remote_hosts[2].ip6, - af=AF_INET6, is_no_fib_entry=1) nd_entry.add_vpp_config() @@ -425,21 +452,21 @@ class TestIPv6(TestIPv6ND): # self.assertTrue(find_nbr(self, self.pg0.sw_if_index, - self.pg0._remote_hosts[2].ip6, - inet=AF_INET6)) + self.pg0._remote_hosts[2].ip6)) self.assertFalse(find_route(self, self.pg0._remote_hosts[2].ip6, - 128, - inet=AF_INET6)) + 128)) # # send an NS from a link local address to the interface's global # address # p = (Ether(dst=in6_getnsmac(nsma), src=self.pg0.remote_mac) / - IPv6(dst=d, src=self.pg0._remote_hosts[2].ip6_ll) / + IPv6( + dst=d, src=self.pg0._remote_hosts[2].ip6_ll) / ICMPv6ND_NS(tgt=self.pg0.local_ip6) / - ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac)) + ICMPv6NDOptSrcLLAddr( + lladdr=self.pg0.remote_mac)) self.send_and_expect_na(self.pg0, p, "NS from link-local", @@ -452,20 +479,20 @@ class TestIPv6(TestIPv6ND): # self.assertTrue(find_nbr(self, self.pg0.sw_if_index, - self.pg0._remote_hosts[2].ip6_ll, - inet=AF_INET6)) + self.pg0._remote_hosts[2].ip6_ll)) self.assertFalse(find_route(self, self.pg0._remote_hosts[2].ip6_ll, - 128, - inet=AF_INET6)) + 128)) # # An NS to the router's own Link-local # p = (Ether(dst=in6_getnsmac(nsma), src=self.pg0.remote_mac) / - IPv6(dst=d, src=self.pg0._remote_hosts[3].ip6_ll) / + IPv6( + dst=d, src=self.pg0._remote_hosts[3].ip6_ll) / ICMPv6ND_NS(tgt=self.pg0.local_ip6_ll) / - ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac)) + ICMPv6NDOptSrcLLAddr( + lladdr=self.pg0.remote_mac)) self.send_and_expect_na(self.pg0, p, "NS to/from link-local", @@ -478,12 +505,10 @@ class TestIPv6(TestIPv6ND): # self.assertTrue(find_nbr(self, self.pg0.sw_if_index, - self.pg0._remote_hosts[3].ip6_ll, - inet=AF_INET6)) + self.pg0._remote_hosts[3].ip6_ll)) self.assertFalse(find_route(self, self.pg0._remote_hosts[3].ip6_ll, - 128, - inet=AF_INET6)) + 128)) def test_ns_duplicates(self): """ ND Duplicates""" @@ -499,14 +524,12 @@ class TestIPv6(TestIPv6ND): ns_pg1 = VppNeighbor(self, self.pg1.sw_if_index, self.pg1.remote_hosts[1].mac, - self.pg1.remote_hosts[1].ip6, - af=AF_INET6) + self.pg1.remote_hosts[1].ip6) ns_pg1.add_vpp_config() ns_pg2 = VppNeighbor(self, self.pg2.sw_if_index, self.pg2.remote_mac, - self.pg1.remote_hosts[1].ip6, - af=AF_INET6) + self.pg1.remote_hosts[1].ip6) ns_pg2.add_vpp_config() # @@ -516,7 +539,7 @@ class TestIPv6(TestIPv6ND): src=self.pg0.remote_mac) / IPv6(src=self.pg0.remote_ip6, dst=self.pg1.remote_hosts[1].ip6) / - UDP(sport=1234, dport=1234) / + inet6.UDP(sport=1234, dport=1234) / Raw()) self.pg0.add_stream(p) @@ -533,7 +556,7 @@ class TestIPv6(TestIPv6ND): # # remove the duplicate on pg1 - # packet stream shoud generate NSs out of pg1 + # packet stream should generate NSs out of pg1 # ns_pg1.remove_vpp_config() @@ -557,9 +580,12 @@ class TestIPv6(TestIPv6ND): self.pg0.remote_ip6, self.pg1.remote_hosts[1].ip6) - def validate_ra(self, intf, rx, dst_ip=None, mtu=9000, pi_opt=None): + def validate_ra(self, intf, rx, dst_ip=None, src_ip=None, + mtu=9000, pi_opt=None): if not dst_ip: dst_ip = intf.remote_ip6 + if not src_ip: + src_ip = mk_ll_addr(intf.local_mac) # unicasted packets must come to the unicast mac self.assertEqual(rx[Ether].dst, intf.remote_mac) @@ -574,8 +600,7 @@ class TestIPv6(TestIPv6ND): # and come from the router's link local self.assertTrue(in6_islladdr(rx[IPv6].src)) - self.assertEqual(in6_ptop(rx[IPv6].src), - in6_ptop(mk_ll_addr(intf.local_mac))) + self.assertEqual(in6_ptop(rx[IPv6].src), in6_ptop(src_ip)) # it should contain the links MTU ra = rx[ICMPv6ND_RA] @@ -587,29 +612,36 @@ class TestIPv6(TestIPv6ND): if not pi_opt: # the RA should not contain prefix information - self.assertFalse(ra.haslayer(ICMPv6NDOptPrefixInfo)) + self.assertFalse(ra.haslayer( + ICMPv6NDOptPrefixInfo)) else: raos = rx.getlayer(ICMPv6NDOptPrefixInfo, 1) # the options are nested in the scapy packet in way that i cannot # decipher how to decode. this 1st layer of option always returns # nested classes, so a direct obj1=obj2 comparison always fails. - # however, the getlayer(.., 2) does give one instnace. - # so we cheat here and construct a new opt instnace for comparison - rd = ICMPv6NDOptPrefixInfo(prefixlen=raos.prefixlen, - prefix=raos.prefix, - L=raos.L, - A=raos.A) + # however, the getlayer(.., 2) does give one instance. + # so we cheat here and construct a new opt instance for comparison + rd = ICMPv6NDOptPrefixInfo( + prefixlen=raos.prefixlen, + prefix=raos.prefix, + L=raos.L, + A=raos.A) if type(pi_opt) is list: for ii in range(len(pi_opt)): self.assertEqual(pi_opt[ii], rd) - rd = rx.getlayer(ICMPv6NDOptPrefixInfo, ii+2) + rd = rx.getlayer( + ICMPv6NDOptPrefixInfo, ii + 2) else: - self.assertEqual(pi_opt, raos) + self.assertEqual(pi_opt, raos, 'Expected: %s, received: %s' + % (pi_opt.show(dump=True), + raos.show(dump=True))) def send_and_expect_ra(self, intf, pkts, remark, dst_ip=None, filter_out_fn=is_ipv6_misc, - opt=None): + opt=None, + src_ip=None): + self.vapi.cli("clear trace") intf.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() @@ -617,7 +649,7 @@ class TestIPv6(TestIPv6ND): self.assertEqual(len(rx), 1) rx = rx[0] - self.validate_ra(intf, rx, dst_ip, pi_opt=opt) + self.validate_ra(intf, rx, dst_ip, src_ip=src_ip, pi_opt=opt) def test_rs(self): """ IPv6 Router Solicitation Exceptions @@ -651,7 +683,8 @@ class TestIPv6(TestIPv6ND): self.send_and_assert_no_replies(self.pg0, pkts, "RA rate limited") # - # When we reconfiure the IPv6 RA config, we reset the RA rate limiting, + # When we reconfigure the IPv6 RA config, + # we reset the RA rate limiting, # so we need to do this before each test below so as not to drop # packets for rate limiting reasons. Test this works here. # @@ -663,7 +696,8 @@ class TestIPv6(TestIPv6ND): # self.pg0.ip6_ra_config(send_unicast=1) p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IPv6(dst=self.pg0.local_ip6, src="2002::ffff") / + IPv6(dst=self.pg0.local_ip6, + src="2002::ffff") / ICMPv6ND_RS()) pkts = [p] self.send_and_assert_no_replies(self.pg0, pkts, @@ -717,16 +751,17 @@ class TestIPv6(TestIPv6ND): # # Configure The RA to announce the links prefix # - self.pg0.ip6_ra_prefix(self.pg0.local_ip6n, - self.pg0.local_ip6_prefix_len) + self.pg0.ip6_ra_prefix('%s/%s' % (self.pg0.local_ip6, + self.pg0.local_ip6_prefix_len)) # # RAs should now contain the prefix information option # - opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len, - prefix=self.pg0.local_ip6, - L=1, - A=1) + opt = ICMPv6NDOptPrefixInfo( + prefixlen=self.pg0.local_ip6_prefix_len, + prefix=self.pg0.local_ip6, + L=1, + A=1) self.pg0.ip6_ra_config(send_unicast=1) ll = mk_ll_addr(self.pg0.remote_mac) @@ -742,14 +777,15 @@ class TestIPv6(TestIPv6ND): # Change the prefix info to not off-link # L-flag is clear # - self.pg0.ip6_ra_prefix(self.pg0.local_ip6n, - self.pg0.local_ip6_prefix_len, + self.pg0.ip6_ra_prefix('%s/%s' % (self.pg0.local_ip6, + self.pg0.local_ip6_prefix_len), off_link=1) - opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len, - prefix=self.pg0.local_ip6, - L=0, - A=1) + opt = ICMPv6NDOptPrefixInfo( + prefixlen=self.pg0.local_ip6_prefix_len, + prefix=self.pg0.local_ip6, + L=0, + A=1) self.pg0.ip6_ra_config(send_unicast=1) self.send_and_expect_ra(self.pg0, p, @@ -761,15 +797,16 @@ class TestIPv6(TestIPv6ND): # Change the prefix info to not off-link, no-autoconfig # L and A flag are clear in the advert # - self.pg0.ip6_ra_prefix(self.pg0.local_ip6n, - self.pg0.local_ip6_prefix_len, + self.pg0.ip6_ra_prefix('%s/%s' % (self.pg0.local_ip6, + self.pg0.local_ip6_prefix_len), off_link=1, no_autoconfig=1) - opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len, - prefix=self.pg0.local_ip6, - L=0, - A=0) + opt = ICMPv6NDOptPrefixInfo( + prefixlen=self.pg0.local_ip6_prefix_len, + prefix=self.pg0.local_ip6, + L=0, + A=0) self.pg0.ip6_ra_config(send_unicast=1) self.send_and_expect_ra(self.pg0, p, @@ -781,13 +818,14 @@ class TestIPv6(TestIPv6ND): # Change the flag settings back to the defaults # L and A flag are set in the advert # - self.pg0.ip6_ra_prefix(self.pg0.local_ip6n, - self.pg0.local_ip6_prefix_len) + self.pg0.ip6_ra_prefix('%s/%s' % (self.pg0.local_ip6, + self.pg0.local_ip6_prefix_len)) - opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len, - prefix=self.pg0.local_ip6, - L=1, - A=1) + opt = ICMPv6NDOptPrefixInfo( + prefixlen=self.pg0.local_ip6_prefix_len, + prefix=self.pg0.local_ip6, + L=1, + A=1) self.pg0.ip6_ra_config(send_unicast=1) self.send_and_expect_ra(self.pg0, p, @@ -799,15 +837,16 @@ class TestIPv6(TestIPv6ND): # Change the prefix info to not off-link, no-autoconfig # L and A flag are clear in the advert # - self.pg0.ip6_ra_prefix(self.pg0.local_ip6n, - self.pg0.local_ip6_prefix_len, + self.pg0.ip6_ra_prefix('%s/%s' % (self.pg0.local_ip6, + self.pg0.local_ip6_prefix_len), off_link=1, no_autoconfig=1) - opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len, - prefix=self.pg0.local_ip6, - L=0, - A=0) + opt = ICMPv6NDOptPrefixInfo( + prefixlen=self.pg0.local_ip6_prefix_len, + prefix=self.pg0.local_ip6, + L=0, + A=0) self.pg0.ip6_ra_config(send_unicast=1) self.send_and_expect_ra(self.pg0, p, @@ -816,17 +855,18 @@ class TestIPv6(TestIPv6ND): opt=opt) # - # Use the reset to defults option to revert to defaults + # Use the reset to defaults option to revert to defaults # L and A flag are clear in the advert # - self.pg0.ip6_ra_prefix(self.pg0.local_ip6n, - self.pg0.local_ip6_prefix_len, + self.pg0.ip6_ra_prefix('%s/%s' % (self.pg0.local_ip6, + self.pg0.local_ip6_prefix_len), use_default=1) - opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len, - prefix=self.pg0.local_ip6, - L=1, - A=1) + opt = ICMPv6NDOptPrefixInfo( + prefixlen=self.pg0.local_ip6_prefix_len, + prefix=self.pg0.local_ip6, + L=1, + A=1) self.pg0.ip6_ra_config(send_unicast=1) self.send_and_expect_ra(self.pg0, p, @@ -837,19 +877,21 @@ class TestIPv6(TestIPv6ND): # # Advertise Another prefix. With no L-flag/A-flag # - self.pg0.ip6_ra_prefix(self.pg1.local_ip6n, - self.pg1.local_ip6_prefix_len, + self.pg0.ip6_ra_prefix('%s/%s' % (self.pg1.local_ip6, + self.pg1.local_ip6_prefix_len), off_link=1, no_autoconfig=1) - opt = [ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len, - prefix=self.pg0.local_ip6, - L=1, - A=1), - ICMPv6NDOptPrefixInfo(prefixlen=self.pg1.local_ip6_prefix_len, - prefix=self.pg1.local_ip6, - L=0, - A=0)] + opt = [ICMPv6NDOptPrefixInfo( + prefixlen=self.pg0.local_ip6_prefix_len, + prefix=self.pg0.local_ip6, + L=1, + A=1), + ICMPv6NDOptPrefixInfo( + prefixlen=self.pg1.local_ip6_prefix_len, + prefix=self.pg1.local_ip6, + L=0, + A=0)] self.pg0.ip6_ra_config(send_unicast=1) ll = mk_ll_addr(self.pg0.remote_mac) @@ -862,17 +904,18 @@ class TestIPv6(TestIPv6ND): opt=opt) # - # Remove the first refix-info - expect the second is still in the + # Remove the first prefix-info - expect the second is still in the # advert # - self.pg0.ip6_ra_prefix(self.pg0.local_ip6n, - self.pg0.local_ip6_prefix_len, + self.pg0.ip6_ra_prefix('%s/%s' % (self.pg0.local_ip6, + self.pg0.local_ip6_prefix_len), is_no=1) - opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg1.local_ip6_prefix_len, - prefix=self.pg1.local_ip6, - L=0, - A=0) + opt = ICMPv6NDOptPrefixInfo( + prefixlen=self.pg1.local_ip6_prefix_len, + prefix=self.pg1.local_ip6, + L=0, + A=0) self.pg0.ip6_ra_config(send_unicast=1) self.send_and_expect_ra(self.pg0, p, @@ -881,22 +924,204 @@ class TestIPv6(TestIPv6ND): opt=opt) # - # Remove the second prefix-info - expect no prefix-info i nthe adverts + # Remove the second prefix-info - expect no prefix-info in the adverts # - self.pg0.ip6_ra_prefix(self.pg1.local_ip6n, - self.pg1.local_ip6_prefix_len, + self.pg0.ip6_ra_prefix('%s/%s' % (self.pg1.local_ip6, + self.pg1.local_ip6_prefix_len), is_no=1) + # + # change the link's link local, so we know that works too. + # + self.vapi.sw_interface_ip6_set_link_local_address( + sw_if_index=self.pg0.sw_if_index, + ip="fe80::88") + self.pg0.ip6_ra_config(send_unicast=1) self.send_and_expect_ra(self.pg0, p, "RA with Prefix reverted to defaults", - dst_ip=ll) + dst_ip=ll, + src_ip="fe80::88") # # Reset the periodic advertisements back to default values # self.pg0.ip6_ra_config(no=1, suppress=1, send_unicast=0) + def test_mld(self): + """ MLD Report """ + # + # test one MLD is sent after applying an IPv6 Address on an interface + # + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + subitf = VppDot1QSubint(self, self.pg1, 99) + + subitf.admin_up() + subitf.config_ip6() + + rxs = self.pg1._get_capture(timeout=4, filter_out_fn=None) + + # + # hunt for the MLD on vlan 99 + # + for rx in rxs: + # make sure ipv6 packets with hop by hop options have + # correct checksums + self.assert_packet_checksums_valid(rx) + if rx.haslayer(IPv6ExtHdrHopByHop) and \ + rx.haslayer(Dot1Q) and \ + rx[Dot1Q].vlan == 99: + mld = rx[ICMPv6MLReport2] + + self.assertEqual(mld.records_number, 4) + + +class TestIPv6IfAddrRoute(VppTestCase): + """ IPv6 Interface Addr Route Test Case """ + + @classmethod + def setUpClass(cls): + super(TestIPv6IfAddrRoute, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TestIPv6IfAddrRoute, cls).tearDownClass() + + def setUp(self): + super(TestIPv6IfAddrRoute, self).setUp() + + # create 1 pg interface + self.create_pg_interfaces(range(1)) + + for i in self.pg_interfaces: + i.admin_up() + i.config_ip6() + i.resolve_ndp() + + def tearDown(self): + super(TestIPv6IfAddrRoute, self).tearDown() + for i in self.pg_interfaces: + i.unconfig_ip6() + i.admin_down() + + def test_ipv6_ifaddrs_same_prefix(self): + """ IPv6 Interface Addresses Same Prefix test + + Test scenario: + + - Verify no route in FIB for prefix 2001:10::/64 + - Configure IPv4 address 2001:10::10/64 on an interface + - Verify route in FIB for prefix 2001:10::/64 + - Configure IPv4 address 2001:10::20/64 on an interface + - Delete 2001:10::10/64 from interface + - Verify route in FIB for prefix 2001:10::/64 + - Delete 2001:10::20/64 from interface + - Verify no route in FIB for prefix 2001:10::/64 + """ + + addr1 = "2001:10::10" + addr2 = "2001:10::20" + + if_addr1 = VppIpInterfaceAddress(self, self.pg0, addr1, 64) + if_addr2 = VppIpInterfaceAddress(self, self.pg0, addr2, 64) + self.assertFalse(if_addr1.query_vpp_config()) + self.assertFalse(find_route(self, addr1, 128)) + self.assertFalse(find_route(self, addr2, 128)) + + # configure first address, verify route present + if_addr1.add_vpp_config() + self.assertTrue(if_addr1.query_vpp_config()) + self.assertTrue(find_route(self, addr1, 128)) + self.assertFalse(find_route(self, addr2, 128)) + + # configure second address, delete first, verify route not removed + if_addr2.add_vpp_config() + if_addr1.remove_vpp_config() + self.assertFalse(if_addr1.query_vpp_config()) + self.assertTrue(if_addr2.query_vpp_config()) + self.assertFalse(find_route(self, addr1, 128)) + self.assertTrue(find_route(self, addr2, 128)) + + # delete second address, verify route removed + if_addr2.remove_vpp_config() + self.assertFalse(if_addr1.query_vpp_config()) + self.assertFalse(find_route(self, addr1, 128)) + self.assertFalse(find_route(self, addr2, 128)) + + +class TestICMPv6Echo(VppTestCase): + """ ICMPv6 Echo Test Case """ + + @classmethod + def setUpClass(cls): + super(TestICMPv6Echo, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TestICMPv6Echo, cls).tearDownClass() + + def setUp(self): + super(TestICMPv6Echo, self).setUp() + + # create 1 pg interface + self.create_pg_interfaces(range(1)) + + for i in self.pg_interfaces: + i.admin_up() + i.config_ip6() + i.resolve_ndp() + + def tearDown(self): + super(TestICMPv6Echo, self).tearDown() + for i in self.pg_interfaces: + i.unconfig_ip6() + i.admin_down() + + def test_icmpv6_echo(self): + """ VPP replies to ICMPv6 Echo Request + + Test scenario: + + - Receive ICMPv6 Echo Request message on pg0 interface. + - Check outgoing ICMPv6 Echo Reply message on pg0 interface. + """ + + icmpv6_id = 0xb + icmpv6_seq = 5 + icmpv6_data = b'\x0a' * 18 + p_echo_request = (Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IPv6(src=self.pg0.remote_ip6, + dst=self.pg0.local_ip6) / + ICMPv6EchoRequest( + id=icmpv6_id, + seq=icmpv6_seq, + data=icmpv6_data)) + + self.pg0.add_stream(p_echo_request) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture(1) + rx = rx[0] + ether = rx[Ether] + ipv6 = rx[IPv6] + icmpv6 = rx[ICMPv6EchoReply] + + self.assertEqual(ether.src, self.pg0.local_mac) + self.assertEqual(ether.dst, self.pg0.remote_mac) + + self.assertEqual(ipv6.src, self.pg0.local_ip6) + self.assertEqual(ipv6.dst, self.pg0.remote_ip6) + + self.assertEqual( + icmp6types[icmpv6.type], "Echo Reply") + self.assertEqual(icmpv6.id, icmpv6_id) + self.assertEqual(icmpv6.seq, icmpv6_seq) + self.assertEqual(icmpv6.data, icmpv6_data) + class TestIPv6RD(TestIPv6ND): """ IPv6 Router Discovery Test Case """ @@ -905,6 +1130,10 @@ class TestIPv6RD(TestIPv6ND): def setUpClass(cls): super(TestIPv6RD, cls).setUpClass() + @classmethod + def tearDownClass(cls): + super(TestIPv6RD, cls).tearDownClass() + def setUp(self): super(TestIPv6RD, self).setUp() @@ -935,24 +1164,30 @@ class TestIPv6RD(TestIPv6ND): 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)) + self.assertEqual(packet.haslayer(IPv6), 1) + self.assertEqual(packet[IPv6].haslayer( + ICMPv6ND_RS), 1) 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) + self.assertTrue( + bool(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")) + prefix = IPv6Network( + text_type(prefix_option.getfieldval("prefix") + + "/" + + text_type(prefix_option.getfieldval("prefixlen"))), + strict=False) + self.assert_equal(reported_prefix.prefix.network_address, + prefix.network_address) L = prefix_option.getfieldval("L") A = prefix_option.getfieldval("A") option_flags = (L << 7) | (A << 6) @@ -965,7 +1200,7 @@ class TestIPv6RD(TestIPv6ND): def test_rd_receive_router_advertisement(self): """ Verify events triggered by received RA packets """ - self.vapi.want_ip6_ra_events() + self.vapi.want_ip6_ra_events(enable=1) prefix_info_1 = ICMPv6NDOptPrefixInfo( prefix="1::2", @@ -1016,6 +1251,10 @@ class TestIPv6RDControlPlane(TestIPv6ND): def setUpClass(cls): super(TestIPv6RDControlPlane, cls).setUpClass() + @classmethod + def tearDownClass(cls): + super(TestIPv6RDControlPlane, cls).tearDownClass() + def setUp(self): super(TestIPv6RDControlPlane, self).setUp() @@ -1048,12 +1287,12 @@ class TestIPv6RDControlPlane(TestIPv6ND): def get_default_routes(fib): list = [] for entry in fib: - if entry.address_length == 0: - for path in entry.path: + if entry.route.prefix.prefixlen == 0: + for path in entry.route.paths: if path.sw_if_index != 0xFFFFFFFF: defaut_route = {} defaut_route['sw_if_index'] = path.sw_if_index - defaut_route['next_hop'] = path.next_hop + defaut_route['next_hop'] = path.nh.address.ip6 list.append(defaut_route) return list @@ -1061,27 +1300,39 @@ class TestIPv6RDControlPlane(TestIPv6ND): def get_interface_addresses(fib, pg): list = [] for entry in fib: - if entry.address_length == 128: - path = entry.path[0] + if entry.route.prefix.prefixlen == 128: + path = entry.route.paths[0] if path.sw_if_index == pg.sw_if_index: - list.append(entry.address) + list.append(str(entry.route.prefix.network_address)) return list + def wait_for_no_default_route(self, n_tries=50, s_time=1): + while (n_tries): + fib = self.vapi.ip_route_dump(0, True) + default_routes = self.get_default_routes(fib) + if 0 == len(default_routes): + return True + n_tries = n_tries - 1 + self.sleep(s_time) + + return False + def test_all(self): """ Test handling of SLAAC addresses and default routes """ - fib = self.vapi.ip6_fib_dump() + fib = self.vapi.ip_route_dump(0, True) 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 + router_address = IPv6Address(text_type(self.pg0.remote_ip6_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( + packet = (self.create_ra_packet( + self.pg0) / ICMPv6NDOptPrefixInfo( prefix="1::", prefixlen=64, validlifetime=2, @@ -1099,16 +1350,17 @@ class TestIPv6RDControlPlane(TestIPv6ND): self.pg0.add_stream([packet]) self.pg_start() - self.sleep(0.1) + self.sleep_on_vpp_time(0.1) - fib = self.vapi.ip6_fib_dump() + fib = self.vapi.ip_route_dump(0, True) # 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::') + prefix = IPv6Network(text_type("%s/%d" % (list(new_addresses)[0], 20)), + strict=False) + self.assertEqual(prefix, IPv6Network(text_type('1::/20'))) # check FIB for new default route default_routes = self.get_default_routes(fib) @@ -1122,24 +1374,24 @@ class TestIPv6RDControlPlane(TestIPv6ND): self.pg0.add_stream([packet]) self.pg_start() - self.sleep(0.1) + self.sleep_on_vpp_time(0.1) # check that default route is deleted - fib = self.vapi.ip6_fib_dump() + fib = self.vapi.ip_route_dump(0, True) default_routes = self.get_default_routes(fib) self.assertEqual(len(default_routes), 0) - self.sleep(0.1) + self.sleep_on_vpp_time(0.1) # send RA packet = self.create_ra_packet(self.pg0) self.pg0.add_stream([packet]) self.pg_start() - self.sleep(0.1) + self.sleep_on_vpp_time(0.1) # check FIB for new default route - fib = self.vapi.ip6_fib_dump() + fib = self.vapi.ip_route_dump(0, True) default_routes = self.get_default_routes(fib) self.assertEqual(len(default_routes), 1) dr = default_routes[0] @@ -1151,34 +1403,34 @@ class TestIPv6RDControlPlane(TestIPv6ND): self.pg0.add_stream([packet]) self.pg_start() - self.sleep(0.1) + self.sleep_on_vpp_time(0.1) # check that default route still exists - fib = self.vapi.ip6_fib_dump() + fib = self.vapi.ip_route_dump(0, True) 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) + self.sleep_on_vpp_time(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.assertTrue(self.wait_for_no_default_route()) # 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::') + prefix = IPv6Network(text_type("%s/%d" % (list(new_addresses)[0], 20)), + strict=False) + self.assertEqual(prefix, IPv6Network(text_type('1::/20'))) - self.sleep(1) + self.sleep_on_vpp_time(1) # check that SLAAC address is deleted - fib = self.vapi.ip6_fib_dump() + fib = self.vapi.ip_route_dump(0, True) addresses = set(self.get_interface_addresses(fib, self.pg0)) new_addresses = addresses.difference(initial_addresses) self.assertEqual(len(new_addresses), 0) @@ -1187,6 +1439,14 @@ class TestIPv6RDControlPlane(TestIPv6ND): class IPv6NDProxyTest(TestIPv6ND): """ IPv6 ND ProxyTest Case """ + @classmethod + def setUpClass(cls): + super(IPv6NDProxyTest, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(IPv6NDProxyTest, cls).tearDownClass() + def setUp(self): super(IPv6NDProxyTest, self).setUp() @@ -1221,18 +1481,20 @@ class IPv6NDProxyTest(TestIPv6ND): # on the link that has the prefix configured # ns_pg1 = (Ether(dst=in6_getnsmac(nsma), src=self.pg1.remote_mac) / - IPv6(dst=d, src=self.pg0._remote_hosts[2].ip6) / + IPv6(dst=d, + src=self.pg0._remote_hosts[2].ip6) / ICMPv6ND_NS(tgt=self.pg0.local_ip6) / - ICMPv6NDOptSrcLLAddr(lladdr=self.pg0._remote_hosts[2].mac)) + ICMPv6NDOptSrcLLAddr( + lladdr=self.pg0._remote_hosts[2].mac)) self.send_and_assert_no_replies(self.pg1, ns_pg1, "Off link NS") # # Add proxy support for the host # - self.vapi.ip6_nd_proxy( - inet_pton(AF_INET6, self.pg0._remote_hosts[2].ip6), - self.pg1.sw_if_index) + self.vapi.ip6nd_proxy_add_del( + is_add=1, ip=inet_pton(AF_INET6, self.pg0._remote_hosts[2].ip6), + sw_if_index=self.pg1.sw_if_index) # # try that NS again. this time we expect an NA back @@ -1247,8 +1509,7 @@ class IPv6NDProxyTest(TestIPv6ND): # self.assertTrue(find_nbr(self, self.pg1.sw_if_index, - self.pg0._remote_hosts[2].ip6, - inet=AF_INET6)) + self.pg0._remote_hosts[2].ip6)) # # ... and we can route traffic to it @@ -1256,8 +1517,8 @@ class IPv6NDProxyTest(TestIPv6ND): t = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IPv6(dst=self.pg0._remote_hosts[2].ip6, src=self.pg0.remote_ip6) / - UDP(sport=10000, dport=20000) / - Raw('\xa5' * 100)) + inet6.UDP(sport=10000, dport=20000) / + Raw(b'\xa5' * 100)) self.pg0.add_stream(t) self.pg_enable_capture(self.pg_interfaces) @@ -1268,16 +1529,20 @@ class IPv6NDProxyTest(TestIPv6ND): self.assertEqual(rx[Ether].dst, self.pg0._remote_hosts[2].mac) self.assertEqual(rx[Ether].src, self.pg1.local_mac) - self.assertEqual(rx[IPv6].src, t[IPv6].src) - self.assertEqual(rx[IPv6].dst, t[IPv6].dst) + self.assertEqual(rx[IPv6].src, + t[IPv6].src) + self.assertEqual(rx[IPv6].dst, + t[IPv6].dst) # # Test we proxy for the host on the main interface # ns_pg0 = (Ether(dst=in6_getnsmac(nsma), src=self.pg0.remote_mac) / IPv6(dst=d, src=self.pg0.remote_ip6) / - ICMPv6ND_NS(tgt=self.pg0._remote_hosts[2].ip6) / - ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac)) + ICMPv6ND_NS( + tgt=self.pg0._remote_hosts[2].ip6) / + ICMPv6NDOptSrcLLAddr( + lladdr=self.pg0.remote_mac)) self.send_and_expect_na(self.pg0, ns_pg0, "NS to proxy entry on main", @@ -1288,13 +1553,15 @@ class IPv6NDProxyTest(TestIPv6ND): # Setup and resolve proxy for another host on another interface # ns_pg2 = (Ether(dst=in6_getnsmac(nsma), src=self.pg2.remote_mac) / - IPv6(dst=d, src=self.pg0._remote_hosts[3].ip6) / + IPv6(dst=d, + src=self.pg0._remote_hosts[3].ip6) / ICMPv6ND_NS(tgt=self.pg0.local_ip6) / - ICMPv6NDOptSrcLLAddr(lladdr=self.pg0._remote_hosts[2].mac)) + ICMPv6NDOptSrcLLAddr( + lladdr=self.pg0._remote_hosts[2].mac)) - self.vapi.ip6_nd_proxy( - inet_pton(AF_INET6, self.pg0._remote_hosts[3].ip6), - self.pg2.sw_if_index) + self.vapi.ip6nd_proxy_add_del( + is_add=1, ip=inet_pton(AF_INET6, self.pg0._remote_hosts[3].ip6), + sw_if_index=self.pg2.sw_if_index) self.send_and_expect_na(self.pg2, ns_pg2, "NS to proxy entry other interface", @@ -1303,8 +1570,7 @@ class IPv6NDProxyTest(TestIPv6ND): self.assertTrue(find_nbr(self, self.pg2.sw_if_index, - self.pg0._remote_hosts[3].ip6, - inet=AF_INET6)) + self.pg0._remote_hosts[3].ip6)) # # hosts can communicate. pg2->pg1 @@ -1313,8 +1579,8 @@ class IPv6NDProxyTest(TestIPv6ND): src=self.pg0.remote_hosts[3].mac) / IPv6(dst=self.pg0._remote_hosts[2].ip6, src=self.pg0._remote_hosts[3].ip6) / - UDP(sport=10000, dport=20000) / - Raw('\xa5' * 100)) + inet6.UDP(sport=10000, dport=20000) / + Raw(b'\xa5' * 100)) self.pg2.add_stream(t2) self.pg_enable_capture(self.pg_interfaces) @@ -1325,29 +1591,27 @@ class IPv6NDProxyTest(TestIPv6ND): self.assertEqual(rx[Ether].dst, self.pg0._remote_hosts[2].mac) self.assertEqual(rx[Ether].src, self.pg1.local_mac) - self.assertEqual(rx[IPv6].src, t2[IPv6].src) - self.assertEqual(rx[IPv6].dst, t2[IPv6].dst) + self.assertEqual(rx[IPv6].src, + t2[IPv6].src) + self.assertEqual(rx[IPv6].dst, + t2[IPv6].dst) # # remove the proxy configs # - self.vapi.ip6_nd_proxy( - inet_pton(AF_INET6, self.pg0._remote_hosts[2].ip6), - self.pg1.sw_if_index, - is_del=1) - self.vapi.ip6_nd_proxy( - inet_pton(AF_INET6, self.pg0._remote_hosts[3].ip6), - self.pg2.sw_if_index, - is_del=1) + self.vapi.ip6nd_proxy_add_del( + ip=inet_pton(AF_INET6, self.pg0._remote_hosts[2].ip6), + sw_if_index=self.pg1.sw_if_index, is_add=0) + self.vapi.ip6nd_proxy_add_del( + ip=inet_pton(AF_INET6, self.pg0._remote_hosts[3].ip6), + sw_if_index=self.pg2.sw_if_index, is_add=0) self.assertFalse(find_nbr(self, self.pg2.sw_if_index, - self.pg0._remote_hosts[3].ip6, - inet=AF_INET6)) + self.pg0._remote_hosts[3].ip6)) self.assertFalse(find_nbr(self, self.pg1.sw_if_index, - self.pg0._remote_hosts[2].ip6, - inet=AF_INET6)) + self.pg0._remote_hosts[2].ip6)) # # no longer proxy-ing... @@ -1372,6 +1636,14 @@ class IPv6NDProxyTest(TestIPv6ND): class TestIPNull(VppTestCase): """ IPv6 routes via NULL """ + @classmethod + def setUpClass(cls): + super(TestIPNull, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TestIPNull, cls).tearDownClass() + def setUp(self): super(TestIPNull, self).setUp() @@ -1395,13 +1667,16 @@ class TestIPNull(VppTestCase): p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IPv6(src=self.pg0.remote_ip6, dst="2001::1") / - UDP(sport=1234, dport=1234) / - Raw('\xa5' * 100)) + inet6.UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) # # A route via IP NULL that will reply with ICMP unreachables # - ip_unreach = VppIpRoute(self, "2001::", 64, [], is_unreach=1, is_ip6=1) + ip_unreach = VppIpRoute( + self, "2001::", 64, + [VppRoutePath("::", 0xffffffff, + type=FibPathType.FIB_PATH_TYPE_ICMP_UNREACH)]) ip_unreach.add_vpp_config() self.pg0.add_stream(p) @@ -1421,8 +1696,10 @@ class TestIPNull(VppTestCase): # # A route via IP NULL that will reply with ICMP prohibited # - ip_prohibit = VppIpRoute(self, "2001::1", 128, [], - is_prohibit=1, is_ip6=1) + ip_prohibit = VppIpRoute( + self, "2001::1", 128, + [VppRoutePath("::", 0xffffffff, + type=FibPathType.FIB_PATH_TYPE_ICMP_PROHIBIT)]) ip_prohibit.add_vpp_config() self.pg0.add_stream(p) @@ -1440,13 +1717,21 @@ class TestIPNull(VppTestCase): class TestIPDisabled(VppTestCase): """ IPv6 disabled """ + @classmethod + def setUpClass(cls): + super(TestIPDisabled, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TestIPDisabled, cls).tearDownClass() + def setUp(self): super(TestIPDisabled, self).setUp() # create 2 pg interfaces self.create_pg_interfaces(range(2)) - # PG0 is IP enalbed + # PG0 is IP enabled self.pg0.admin_up() self.pg0.config_ip6() self.pg0.resolve_ndp() @@ -1475,20 +1760,19 @@ class TestIPDisabled(VppTestCase): [VppMRoutePath(self.pg1.sw_if_index, MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT), VppMRoutePath(self.pg0.sw_if_index, - MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)], - is_ip6=1) + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)]) route_ff_01.add_vpp_config() pu = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / IPv6(src="2001::1", dst=self.pg0.remote_ip6) / - UDP(sport=1234, dport=1234) / - Raw('\xa5' * 100)) + inet6.UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) pm = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / IPv6(src="2001::1", dst="ffef::1") / - UDP(sport=1234, dport=1234) / - Raw('\xa5' * 100)) + inet6.UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) # # PG1 does not forward IP traffic @@ -1529,6 +1813,14 @@ class TestIPDisabled(VppTestCase): class TestIP6LoadBalance(VppTestCase): """ IPv6 Load-Balancing """ + @classmethod + def setUpClass(cls): + super(TestIP6LoadBalance, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TestIP6LoadBalance, cls).tearDownClass() + def setUp(self): super(TestIP6LoadBalance, self).setUp() @@ -1550,20 +1842,20 @@ class TestIP6LoadBalance(VppTestCase): i.disable_mpls() super(TestIP6LoadBalance, self).tearDown() - def send_and_expect_load_balancing(self, input, pkts, outputs): + def pg_send(self, input, pkts): self.vapi.cli("clear trace") input.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() + + def send_and_expect_load_balancing(self, input, pkts, outputs): + self.pg_send(input, pkts) for oo in outputs: rx = oo._get_capture(1) 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() + self.pg_send(input, pkts) rx = itf.get_capture(len(pkts)) def test_ip6_load_balance(self): @@ -1587,10 +1879,11 @@ class TestIP6LoadBalance(VppTestCase): src_ip_pkts = [] src_mpls_pkts = [] - for ii in range(65): - port_ip_hdr = (IPv6(dst="3000::1", src="3000:1::1") / - UDP(sport=1234, dport=1234 + ii) / - Raw('\xa5' * 100)) + for ii in range(NUM_PKTS): + port_ip_hdr = ( + IPv6(dst="3000::1", src="3000:1::1") / + inet6.UDP(sport=1234, dport=1234 + ii) / + Raw(b'\xa5' * 100)) port_ip_pkts.append((Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / port_ip_hdr)) @@ -1609,9 +1902,10 @@ class TestIP6LoadBalance(VppTestCase): MPLS(label=14, ttl=2) / MPLS(label=999, ttl=2) / port_ip_hdr)) - src_ip_hdr = (IPv6(dst="3000::1", src="3000:1::%d" % ii) / - UDP(sport=1234, dport=1234) / - Raw('\xa5' * 100)) + src_ip_hdr = ( + IPv6(dst="3000::1", src="3000:1::%d" % ii) / + inet6.UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) src_ip_pkts.append((Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / src_ip_hdr)) @@ -1621,16 +1915,13 @@ class TestIP6LoadBalance(VppTestCase): src_ip_hdr)) # - # A route for the IP pacekts + # A route for the IP packets # route_3000_1 = VppIpRoute(self, "3000::1", 128, [VppRoutePath(self.pg1.remote_ip6, - self.pg1.sw_if_index, - proto=DpoProto.DPO_PROTO_IP6), + self.pg1.sw_if_index), VppRoutePath(self.pg2.remote_ip6, - self.pg2.sw_if_index, - proto=DpoProto.DPO_PROTO_IP6)], - is_ip6=1) + self.pg2.sw_if_index)]) route_3000_1.add_vpp_config() # @@ -1645,12 +1936,10 @@ class TestIP6LoadBalance(VppTestCase): route_67 = VppMplsRoute(self, 67, 0, [VppRoutePath(self.pg1.remote_ip6, self.pg1.sw_if_index, - labels=[67], - proto=DpoProto.DPO_PROTO_IP6), + labels=[67]), VppRoutePath(self.pg2.remote_ip6, self.pg2.sw_if_index, - labels=[67], - proto=DpoProto.DPO_PROTO_IP6)]) + labels=[67])]) route_67.add_vpp_config() # @@ -1659,7 +1948,7 @@ class TestIP6LoadBalance(VppTestCase): # src,dst # We are not going to ensure equal amounts of packets across each link, # since the hash algorithm is statistical and therefore this can never - # be guaranteed. But wuth 64 different packets we do expect some + # be guaranteed. But with 64 different packets we do expect some # balancing. So instead just ensure there is traffic on each link. # self.send_and_expect_load_balancing(self.pg0, port_ip_pkts, @@ -1675,7 +1964,7 @@ class TestIP6LoadBalance(VppTestCase): # # The packets with Entropy label in should not load-balance, - # since the Entorpy value is fixed. + # since the Entropy value is fixed. # self.send_and_expect_one_itf(self.pg0, port_ent_pkts, self.pg1) @@ -1684,7 +1973,8 @@ class TestIP6LoadBalance(VppTestCase): # - now only the stream with differing source address will # load-balance # - self.vapi.set_ip_flow_hash(0, is_ip6=1, src=1, dst=1, sport=0, dport=0) + self.vapi.set_ip_flow_hash(vrf_id=0, src=1, dst=1, sport=0, dport=0, + is_ipv6=1) self.send_and_expect_load_balancing(self.pg0, src_ip_pkts, [self.pg1, self.pg2]) @@ -1695,7 +1985,8 @@ class TestIP6LoadBalance(VppTestCase): # # change the flow hash config back to defaults # - self.vapi.set_ip_flow_hash(0, is_ip6=1, src=1, dst=1, sport=1, dport=1) + self.vapi.set_ip_flow_hash(vrf_id=0, src=1, dst=1, sport=1, dport=1, + is_ipv6=1) # # Recursive prefixes @@ -1708,33 +1999,30 @@ class TestIP6LoadBalance(VppTestCase): for ii in range(257): port_pkts.append((Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IPv6(dst="4000::1", src="4000:1::1") / - UDP(sport=1234, dport=1234 + ii) / - Raw('\xa5' * 100))) + IPv6(dst="4000::1", + src="4000:1::1") / + inet6.UDP(sport=1234, + dport=1234 + ii) / + Raw(b'\xa5' * 100))) src_pkts.append((Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IPv6(dst="4000::1", src="4000:1::%d" % ii) / - UDP(sport=1234, dport=1234) / - Raw('\xa5' * 100))) + IPv6(dst="4000::1", + src="4000:1::%d" % ii) / + inet6.UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100))) route_3000_2 = VppIpRoute(self, "3000::2", 128, [VppRoutePath(self.pg3.remote_ip6, - self.pg3.sw_if_index, - proto=DpoProto.DPO_PROTO_IP6), + self.pg3.sw_if_index), VppRoutePath(self.pg4.remote_ip6, - self.pg4.sw_if_index, - proto=DpoProto.DPO_PROTO_IP6)], - is_ip6=1) + self.pg4.sw_if_index)]) route_3000_2.add_vpp_config() route_4000_1 = VppIpRoute(self, "4000::1", 128, [VppRoutePath("3000::1", - 0xffffffff, - proto=DpoProto.DPO_PROTO_IP6), + 0xffffffff), VppRoutePath("3000::2", - 0xffffffff, - proto=DpoProto.DPO_PROTO_IP6)], - is_ip6=1) + 0xffffffff)]) route_4000_1.add_vpp_config() # @@ -1757,22 +2045,20 @@ class TestIP6LoadBalance(VppTestCase): 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))) + IPv6(dst="6000::1", + src="6000:1::1") / + inet6.UDP(sport=1234, + dport=1234 + ii) / + Raw(b'\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) + self.pg3.sw_if_index)]) 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) + 0xffffffff)]) route_6000_1.add_vpp_config() # @@ -1785,10 +2071,18 @@ class TestIP6LoadBalance(VppTestCase): class TestIP6Punt(VppTestCase): """ IPv6 Punt Police/Redirect """ + @classmethod + def setUpClass(cls): + super(TestIP6Punt, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TestIP6Punt, cls).tearDownClass() + def setUp(self): super(TestIP6Punt, self).setUp() - self.create_pg_interfaces(range(2)) + self.create_pg_interfaces(range(4)) for i in self.pg_interfaces: i.admin_up() @@ -1806,29 +2100,28 @@ class TestIP6Punt(VppTestCase): 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)) + IPv6(src=self.pg0.remote_ip6, + dst=self.pg0.local_ip6) / + inet6.TCP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) pkts = p * 1025 # # Configure a punt redirect via pg1. # - nh_addr = inet_pton(AF_INET6, - self.pg1.remote_ip6) + nh_addr = self.pg1.remote_ip6 self.vapi.ip_punt_redirect(self.pg0.sw_if_index, self.pg1.sw_if_index, - nh_addr, - is_ip6=1) + nh_addr) 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) + policer = VppPolicer(self, "ip6-punt", 400, 0, 10, 0, rate_type=1) + policer.add_vpp_config() self.vapi.ip_punt_police(policer.policer_index, is_ip6=1) self.vapi.cli("clear trace") @@ -1837,19 +2130,18 @@ class TestIP6Punt(VppTestCase): self.pg_start() # - # the number of packet recieved should be greater than 0, + # the number of packet received 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)) + self.assertGreater(len(rx), 0) + self.assertLess(len(rx), len(pkts)) # - # remove the poilcer. back to full rx + # remove the policer. 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) + policer.remove_vpp_config() self.send_and_expect(self.pg0, pkts, self.pg1) # @@ -1858,8 +2150,7 @@ class TestIP6Punt(VppTestCase): self.vapi.ip_punt_redirect(self.pg0.sw_if_index, self.pg1.sw_if_index, nh_addr, - is_add=0, - is_ip6=1) + is_add=0) self.send_and_assert_no_replies(self.pg0, pkts, "IP no punt config") @@ -1868,24 +2159,65 @@ class TestIP6Punt(VppTestCase): # self.vapi.ip_punt_redirect(0xffffffff, self.pg1.sw_if_index, - nh_addr, - is_ip6=1) + nh_addr) 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) + is_add=0) + def test_ip_punt_dump(self): + """ IP6 punt redirect dump""" -class TestIP6Input(VppTestCase): - """ IPv6 Input Exceptions """ + # + # Configure a punt redirects + # + nh_addr = self.pg3.remote_ip6 + self.vapi.ip_punt_redirect(self.pg0.sw_if_index, + self.pg3.sw_if_index, + nh_addr) + self.vapi.ip_punt_redirect(self.pg1.sw_if_index, + self.pg3.sw_if_index, + nh_addr) + self.vapi.ip_punt_redirect(self.pg2.sw_if_index, + self.pg3.sw_if_index, + '0::0') + + # + # Dump pg0 punt redirects + # + punts = self.vapi.ip_punt_redirect_dump(self.pg0.sw_if_index, + is_ipv6=1) + for p in punts: + self.assertEqual(p.punt.rx_sw_if_index, self.pg0.sw_if_index) + + # + # Dump punt redirects for all interfaces + # + punts = self.vapi.ip_punt_redirect_dump(0xffffffff, is_ipv6=1) + self.assertEqual(len(punts), 3) + for p in punts: + self.assertEqual(p.punt.tx_sw_if_index, self.pg3.sw_if_index) + self.assertNotEqual(punts[1].punt.nh, self.pg3.remote_ip6) + self.assertEqual(str(punts[2].punt.nh), '::') + + +class TestIPDeag(VppTestCase): + """ IPv6 Deaggregate Routes """ + + @classmethod + def setUpClass(cls): + super(TestIPDeag, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TestIPDeag, cls).tearDownClass() def setUp(self): - super(TestIP6Input, self).setUp() + super(TestIPDeag, self).setUp() - self.create_pg_interfaces(range(2)) + self.create_pg_interfaces(range(3)) for i in self.pg_interfaces: i.admin_up() @@ -1893,45 +2225,500 @@ class TestIP6Input(VppTestCase): i.resolve_ndp() def tearDown(self): - super(TestIP6Input, self).tearDown() + super(TestIPDeag, self).tearDown() for i in self.pg_interfaces: i.unconfig_ip6() i.admin_down() - def test_ip_input(self): - """ IP6 Input Exceptions """ + def test_ip_deag(self): + """ IP Deag Routes """ # - # bad version - this is dropped + # Create a table to be used for: + # 1 - another destination address lookup + # 2 - a source address lookup # - 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)) + table_dst = VppIpTable(self, 1, is_ip6=1) + table_src = VppIpTable(self, 2, is_ip6=1) + table_dst.add_vpp_config() + table_src.add_vpp_config() - self.send_and_assert_no_replies(self.pg0, p_version * 65, - "funky version") + # + # 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", 128, + [VppRoutePath("::", + 0xffffffff, + nh_table_id=1)]) + route_to_src = VppIpRoute( + self, "1::2", 128, + [VppRoutePath("::", + 0xffffffff, + nh_table_id=2, + type=FibPathType.FIB_PATH_TYPE_SOURCE_LOOKUP)]) + + 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) / + IPv6(src="5::5", dst="1::1") / + inet6.TCP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + p_src = (Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IPv6(src="2::2", dst="1::2") / + inet6.TCP(sport=1234, dport=1234) / + Raw(b'\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", 128, + [VppRoutePath(self.pg1.remote_ip6, + 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", 128, + [VppRoutePath(self.pg2.remote_ip6, + self.pg2.sw_if_index)], + table_id=2) + route_in_src.add_vpp_config() + self.send_and_expect(self.pg0, pkts_src, self.pg2) + + # + # loop in the lookup DP + # + route_loop = VppIpRoute(self, "3::3", 128, + [VppRoutePath("::", + 0xffffffff)]) + route_loop.add_vpp_config() + + p_l = (Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IPv6(src="3::4", dst="3::3") / + inet6.TCP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_assert_no_replies(self.pg0, p_l * 257, + "IP lookup loop") + + +class TestIP6Input(VppTestCase): + """ IPv6 Input Exception Test Cases """ + + @classmethod + def setUpClass(cls): + super(TestIP6Input, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TestIP6Input, cls).tearDownClass() + + 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_icmp_reply(self): + """ IP6 Input Exception - Return ICMP (3,0) """ # - # hop limit - IMCP replies + # hop limit - ICMP 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)) + inet6.UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) - rx = self.send_and_expect(self.pg0, p_version * 65, self.pg0) + rx = self.send_and_expect(self.pg0, p_version * NUM_PKTS, self.pg0) rx = rx[0] icmp = rx[ICMPv6TimeExceeded] - self.assertEqual(icmp.type, 3) + # 0: "hop limit exceeded in transit", - self.assertEqual(icmp.code, 0) + self.assertEqual((icmp.type, icmp.code), (3, 0)) + + icmpv6_data = '\x0a' * 18 + all_0s = "::" + all_1s = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF" + + @parameterized.expand([ + # Name, src, dst, l4proto, msg, timeout + ("src='iface', dst='iface'", None, None, + inet6.UDP(sport=1234, dport=1234), "funky version", None), + ("src='All 0's', dst='iface'", all_0s, None, + ICMPv6EchoRequest(id=0xb, seq=5, data=icmpv6_data), None, 0.1), + ("src='iface', dst='All 0's'", None, all_0s, + ICMPv6EchoRequest(id=0xb, seq=5, data=icmpv6_data), None, 0.1), + ("src='All 1's', dst='iface'", all_1s, None, + ICMPv6EchoRequest(id=0xb, seq=5, data=icmpv6_data), None, 0.1), + ("src='iface', dst='All 1's'", None, all_1s, + ICMPv6EchoRequest(id=0xb, seq=5, data=icmpv6_data), None, 0.1), + ("src='All 1's', dst='All 1's'", all_1s, all_1s, + ICMPv6EchoRequest(id=0xb, seq=5, data=icmpv6_data), None, 0.1), + + ]) + def test_ip_input_no_replies(self, name, src, dst, l4, msg, timeout): + + self._testMethodDoc = 'IPv6 Input Exception - %s' % name + + p_version = (Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IPv6(src=src or self.pg0.remote_ip6, + dst=dst or self.pg1.remote_ip6, + version=3) / + l4 / + Raw(b'\xa5' * 100)) + + self.send_and_assert_no_replies(self.pg0, p_version * NUM_PKTS, + remark=msg or "", + timeout=timeout) + + def test_hop_by_hop(self): + """ Hop-by-hop header test """ + + p = (Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) / + IPv6ExtHdrHopByHop() / + inet6.UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + +class TestIPReplace(VppTestCase): + """ IPv6 Table Replace """ + + @classmethod + def setUpClass(cls): + super(TestIPReplace, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TestIPReplace, cls).tearDownClass() + + def setUp(self): + super(TestIPReplace, self).setUp() + + self.create_pg_interfaces(range(4)) + + table_id = 1 + self.tables = [] + + for i in self.pg_interfaces: + i.admin_up() + i.config_ip6() + i.resolve_arp() + i.generate_remote_hosts(2) + self.tables.append(VppIpTable(self, table_id, + True).add_vpp_config()) + table_id += 1 + + def tearDown(self): + super(TestIPReplace, self).tearDown() + for i in self.pg_interfaces: + i.admin_down() + i.unconfig_ip4() + + def test_replace(self): + """ IP Table Replace """ + + N_ROUTES = 20 + links = [self.pg0, self.pg1, self.pg2, self.pg3] + routes = [[], [], [], []] + + # the sizes of 'empty' tables + for t in self.tables: + self.assertEqual(len(t.dump()), 2) + self.assertEqual(len(t.mdump()), 5) + + # load up the tables with some routes + for ii, t in enumerate(self.tables): + for jj in range(1, N_ROUTES): + uni = VppIpRoute( + self, "2001::%d" % jj if jj != 0 else "2001::", 128, + [VppRoutePath(links[ii].remote_hosts[0].ip6, + links[ii].sw_if_index), + VppRoutePath(links[ii].remote_hosts[1].ip6, + links[ii].sw_if_index)], + table_id=t.table_id).add_vpp_config() + multi = VppIpMRoute( + self, "::", + "ff:2001::%d" % jj, 128, + MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE, + [VppMRoutePath(self.pg0.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT, + proto=FibPathProto.FIB_PATH_NH_PROTO_IP6), + VppMRoutePath(self.pg1.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD, + proto=FibPathProto.FIB_PATH_NH_PROTO_IP6), + VppMRoutePath(self.pg2.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD, + proto=FibPathProto.FIB_PATH_NH_PROTO_IP6), + VppMRoutePath(self.pg3.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD, + proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)], + table_id=t.table_id).add_vpp_config() + routes[ii].append({'uni': uni, + 'multi': multi}) + + # + # replace the tables a few times + # + for kk in range(3): + # replace each table + for t in self.tables: + t.replace_begin() + + # all the routes are still there + for ii, t in enumerate(self.tables): + dump = t.dump() + mdump = t.mdump() + for r in routes[ii]: + self.assertTrue(find_route_in_dump(dump, r['uni'], t)) + self.assertTrue(find_mroute_in_dump(mdump, r['multi'], t)) + + # redownload the even numbered routes + for ii, t in enumerate(self.tables): + for jj in range(0, N_ROUTES, 2): + routes[ii][jj]['uni'].add_vpp_config() + routes[ii][jj]['multi'].add_vpp_config() + + # signal each table converged + for t in self.tables: + t.replace_end() + + # we should find the even routes, but not the odd + for ii, t in enumerate(self.tables): + dump = t.dump() + mdump = t.mdump() + for jj in range(0, N_ROUTES, 2): + self.assertTrue(find_route_in_dump( + dump, routes[ii][jj]['uni'], t)) + self.assertTrue(find_mroute_in_dump( + mdump, routes[ii][jj]['multi'], t)) + for jj in range(1, N_ROUTES - 1, 2): + self.assertFalse(find_route_in_dump( + dump, routes[ii][jj]['uni'], t)) + self.assertFalse(find_mroute_in_dump( + mdump, routes[ii][jj]['multi'], t)) + + # reload all the routes + for ii, t in enumerate(self.tables): + for r in routes[ii]: + r['uni'].add_vpp_config() + r['multi'].add_vpp_config() + + # all the routes are still there + for ii, t in enumerate(self.tables): + dump = t.dump() + mdump = t.mdump() + for r in routes[ii]: + self.assertTrue(find_route_in_dump(dump, r['uni'], t)) + self.assertTrue(find_mroute_in_dump(mdump, r['multi'], t)) + + # + # finally flush the tables for good measure + # + for t in self.tables: + t.flush() + self.assertEqual(len(t.dump()), 2) + self.assertEqual(len(t.mdump()), 5) + + +class TestIP6Replace(VppTestCase): + """ IPv4 Interface Address Replace """ + + @classmethod + def setUpClass(cls): + super(TestIP6Replace, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TestIP6Replace, cls).tearDownClass() + + def setUp(self): + super(TestIP6Replace, self).setUp() + + self.create_pg_interfaces(range(4)) + + for i in self.pg_interfaces: + i.admin_up() + + def tearDown(self): + super(TestIP6Replace, self).tearDown() + for i in self.pg_interfaces: + i.admin_down() + + def get_n_pfxs(self, intf): + return len(self.vapi.ip_address_dump(intf.sw_if_index, True)) + + def test_replace(self): + """ IP interface address replace """ + + intf_pfxs = [[], [], [], []] + + # add prefixes to each of the interfaces + for i in range(len(self.pg_interfaces)): + intf = self.pg_interfaces[i] + + # 2001:16:x::1/64 + addr = "2001:16:%d::1" % intf.sw_if_index + a = VppIpInterfaceAddress(self, intf, addr, 64).add_vpp_config() + intf_pfxs[i].append(a) + + # 2001:16:x::2/64 - a different address in the same subnet as above + addr = "2001:16:%d::2" % intf.sw_if_index + a = VppIpInterfaceAddress(self, intf, addr, 64).add_vpp_config() + intf_pfxs[i].append(a) + + # 2001:15:x::2/64 - a different address and subnet + addr = "2001:15:%d::2" % intf.sw_if_index + a = VppIpInterfaceAddress(self, intf, addr, 64).add_vpp_config() + intf_pfxs[i].append(a) + + # a dump should n_address in it + for intf in self.pg_interfaces: + self.assertEqual(self.get_n_pfxs(intf), 3) + + # + # remove all the address thru a replace + # + self.vapi.sw_interface_address_replace_begin() + self.vapi.sw_interface_address_replace_end() + for intf in self.pg_interfaces: + self.assertEqual(self.get_n_pfxs(intf), 0) + + # + # add all the interface addresses back + # + for p in intf_pfxs: + for v in p: + v.add_vpp_config() + for intf in self.pg_interfaces: + self.assertEqual(self.get_n_pfxs(intf), 3) + + # + # replace again, but this time update/re-add the address on the first + # two interfaces + # + self.vapi.sw_interface_address_replace_begin() + + for p in intf_pfxs[:2]: + for v in p: + v.add_vpp_config() + + self.vapi.sw_interface_address_replace_end() + + # on the first two the address still exist, + # on the other two they do not + for intf in self.pg_interfaces[:2]: + self.assertEqual(self.get_n_pfxs(intf), 3) + for p in intf_pfxs[:2]: + for v in p: + self.assertTrue(v.query_vpp_config()) + for intf in self.pg_interfaces[2:]: + self.assertEqual(self.get_n_pfxs(intf), 0) + + # + # add all the interface addresses back on the last two + # + for p in intf_pfxs[2:]: + for v in p: + v.add_vpp_config() + for intf in self.pg_interfaces: + self.assertEqual(self.get_n_pfxs(intf), 3) + + # + # replace again, this time add different prefixes on all the interfaces + # + self.vapi.sw_interface_address_replace_begin() + + pfxs = [] + for intf in self.pg_interfaces: + # 2001:18:x::1/64 + addr = "2001:18:%d::1" % intf.sw_if_index + pfxs.append(VppIpInterfaceAddress(self, intf, addr, + 64).add_vpp_config()) + + self.vapi.sw_interface_address_replace_end() + + # only .18 should exist on each interface + for intf in self.pg_interfaces: + self.assertEqual(self.get_n_pfxs(intf), 1) + for pfx in pfxs: + self.assertTrue(pfx.query_vpp_config()) + + # + # remove everything + # + self.vapi.sw_interface_address_replace_begin() + self.vapi.sw_interface_address_replace_end() + for intf in self.pg_interfaces: + self.assertEqual(self.get_n_pfxs(intf), 0) + + # + # add prefixes to each interface. post-begin add the prefix from + # interface X onto interface Y. this would normally be an error + # since it would generate a 'duplicate address' warning. but in + # this case, since what is newly downloaded is sane, it's ok + # + for intf in self.pg_interfaces: + # 2001:18:x::1/64 + addr = "2001:18:%d::1" % intf.sw_if_index + VppIpInterfaceAddress(self, intf, addr, 64).add_vpp_config() + + self.vapi.sw_interface_address_replace_begin() + + pfxs = [] + for intf in self.pg_interfaces: + # 2001:18:x::1/64 + addr = "2001:18:%d::1" % (intf.sw_if_index + 1) + pfxs.append(VppIpInterfaceAddress(self, intf, + addr, 64).add_vpp_config()) + + self.vapi.sw_interface_address_replace_end() + + self.logger.info(self.vapi.cli("sh int addr")) + + for intf in self.pg_interfaces: + self.assertEqual(self.get_n_pfxs(intf), 1) + for pfx in pfxs: + self.assertTrue(pfx.query_vpp_config()) if __name__ == '__main__':