X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=test%2Ftest_ip6.py;h=b95809b1bee939a6757a23e47fe94733c1268577;hb=37be73693a46b05360483778efee3313a71654f5;hp=ea669b708a6294b86ce97ad4fc779612819e5898;hpb=da505f608e0919c45089dc80f9e3e16330a6551a;p=vpp.git diff --git a/test/test_ip6.py b/test/test_ip6.py index ea669b708a6..b95809b1bee 100644 --- a/test/test_ip6.py +++ b/test/test_ip6.py @@ -1,18 +1,24 @@ #!/usr/bin/env python import unittest -import socket +from socket import AF_INET6 from framework import VppTestCase, VppTestRunner from vpp_sub_interface import VppSubInterface, VppDot1QSubint +from vpp_pg_interface import is_ipv6_misc +from vpp_neighbor import find_nbr +from vpp_ip_route import VppIpRoute, VppRoutePath from scapy.packet import Raw from scapy.layers.l2 import Ether, Dot1Q from scapy.layers.inet6 import IPv6, UDP, ICMPv6ND_NS, ICMPv6ND_RS, \ - ICMPv6ND_RA, ICMPv6NDOptSrcLLAddr, getmacbyip6, ICMPv6MRD_Solicitation + ICMPv6ND_RA, ICMPv6NDOptSrcLLAddr, getmacbyip6, ICMPv6MRD_Solicitation, \ + ICMPv6NDOptMTU, ICMPv6NDOptSrcLLAddr, ICMPv6NDOptPrefixInfo, \ + ICMPv6ND_NA, ICMPv6NDOptDstLLAddr, ICMPv6DestUnreach, icmp6types + from util import ppp from scapy.utils6 import in6_getnsma, in6_getnsmac, in6_ptop, in6_islladdr, \ - in6_mactoifaceid + in6_mactoifaceid, in6_ismaddr from scapy.utils import inet_pton, inet_ntop @@ -22,7 +28,71 @@ def mk_ll_addr(mac): return addr -class TestIPv6(VppTestCase): +class TestIPv6ND(VppTestCase): + def validate_ra(self, intf, rx, dst_ip=None): + if not dst_ip: + dst_ip = intf.remote_ip6 + + # unicasted packets must come to the unicast mac + self.assertEqual(rx[Ether].dst, intf.remote_mac) + + # and from the router's MAC + self.assertEqual(rx[Ether].src, intf.local_mac) + + # the rx'd RA should be addressed to the sender's source + self.assertTrue(rx.haslayer(ICMPv6ND_RA)) + self.assertEqual(in6_ptop(rx[IPv6].dst), + in6_ptop(dst_ip)) + + # 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))) + + def validate_na(self, intf, rx, dst_ip=None, tgt_ip=None): + if not dst_ip: + dst_ip = intf.remote_ip6 + if not tgt_ip: + dst_ip = intf.local_ip6 + + # unicasted packets must come to the unicast mac + self.assertEqual(rx[Ether].dst, intf.remote_mac) + + # and from the router's MAC + self.assertEqual(rx[Ether].src, intf.local_mac) + + # the rx'd NA should be addressed to the sender's source + self.assertTrue(rx.haslayer(ICMPv6ND_NA)) + self.assertEqual(in6_ptop(rx[IPv6].dst), + in6_ptop(dst_ip)) + + # and come from the target address + self.assertEqual(in6_ptop(rx[IPv6].src), in6_ptop(tgt_ip)) + + # Dest link-layer options should have the router's MAC + dll = rx[ICMPv6NDOptDstLLAddr] + self.assertEqual(dll.lladdr, intf.local_mac) + + def send_and_expect_ra(self, intf, pkts, remark, dst_ip=None, + filter_out_fn=is_ipv6_misc): + intf.add_stream(pkts) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + rx = intf.get_capture(1, filter_out_fn=filter_out_fn) + + self.assertEqual(len(rx), 1) + rx = rx[0] + self.validate_ra(intf, rx, dst_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() + intf.assert_nothing_captured(remark=remark) + + +class TestIPv6(TestIPv6ND): """ IPv6 Test Case """ @classmethod @@ -110,7 +180,7 @@ class TestIPv6(VppTestCase): n_int = len(self.interfaces) percent = 0 counter = 0.0 - dest_addr = socket.inet_pton(socket.AF_INET6, "fd02::1") + dest_addr = inet_pton(AF_INET6, "fd02::1") dest_addr_len = 128 for i in self.interfaces: next_hop_address = i.local_ip6n @@ -223,12 +293,6 @@ class TestIPv6(VppTestCase): pkts = i.parent.get_capture() self.verify_capture(i, pkts) - def send_and_assert_no_replies(self, intf, pkts, remark): - intf.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - intf.assert_nothing_captured(remark=remark) - def test_ns(self): """ IPv6 Neighbour Solicitation Exceptions @@ -241,8 +305,8 @@ class TestIPv6(VppTestCase): # # An NS from a non link source address # - nsma = in6_getnsma(inet_pton(socket.AF_INET6, self.pg0.local_ip6)) - d = inet_ntop(socket.AF_INET6, nsma) + nsma = in6_getnsma(inet_pton(AF_INET6, self.pg0.local_ip6)) + d = inet_ntop(AF_INET6, nsma) p = (Ether(dst=in6_getnsmac(nsma)) / IPv6(dst=d, src="2002::2") / @@ -259,8 +323,8 @@ class TestIPv6(VppTestCase): # not a member of FAILS # if 0: - nsma = in6_getnsma(inet_pton(socket.AF_INET6, "fd::ffff")) - d = inet_ntop(socket.AF_INET6, nsma) + nsma = in6_getnsma(inet_pton(AF_INET6, "fd::ffff")) + d = inet_ntop(AF_INET6, nsma) p = (Ether(dst=in6_getnsmac(nsma)) / IPv6(dst=d, src=self.pg0.remote_ip6) / @@ -275,8 +339,8 @@ class TestIPv6(VppTestCase): # # An NS whose target address is one the router does not own # - nsma = in6_getnsma(inet_pton(socket.AF_INET6, self.pg0.local_ip6)) - d = inet_ntop(socket.AF_INET6, nsma) + nsma = in6_getnsma(inet_pton(AF_INET6, self.pg0.local_ip6)) + d = inet_ntop(AF_INET6, nsma) p = (Ether(dst=in6_getnsmac(nsma)) / IPv6(dst=d, src=self.pg0.remote_ip6) / @@ -287,28 +351,68 @@ class TestIPv6(VppTestCase): self.send_and_assert_no_replies(self.pg0, pkts, "No response to NS for unknown target") - def send_and_expect_ra(self, intf, pkts, remark, src_ip=None): - if not src_ip: - src_ip = intf.remote_ip6 - intf.add_stream(pkts) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - rx = intf.get_capture(1) + def validate_ra(self, intf, rx, dst_ip=None, mtu=9000, pi_opt=None): + if not dst_ip: + dst_ip = intf.remote_ip6 - self.assertEqual(len(rx), 1) - rx = rx[0] + # unicasted packets must come to the unicast mac + self.assertEqual(rx[Ether].dst, intf.remote_mac) + + # and from the router's MAC + self.assertEqual(rx[Ether].src, intf.local_mac) # the rx'd RA should be addressed to the sender's source self.assertTrue(rx.haslayer(ICMPv6ND_RA)) self.assertEqual(in6_ptop(rx[IPv6].dst), - in6_ptop(src_ip)) + in6_ptop(dst_ip)) # 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))) + # it should contain the links MTU + ra = rx[ICMPv6ND_RA] + self.assertEqual(ra[ICMPv6NDOptMTU].mtu, mtu) + + # it should contain the source's link layer address option + sll = ra[ICMPv6NDOptSrcLLAddr] + self.assertEqual(sll.lladdr, intf.local_mac) + + if not pi_opt: + # the RA should not contain prefix information + 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) + 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) + else: + self.assertEqual(pi_opt, raos) + + def send_and_expect_ra(self, intf, pkts, remark, dst_ip=None, + filter_out_fn=is_ipv6_misc, + opt=None): + intf.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + rx = intf.get_capture(1, filter_out_fn=filter_out_fn) + + self.assertEqual(len(rx), 1) + rx = rx[0] + self.validate_ra(intf, rx, dst_ip, pi_opt=opt) + def test_rs(self): """ IPv6 Router Solicitation Exceptions @@ -319,6 +423,9 @@ class TestIPv6(VppTestCase): # Before we begin change the IPv6 RA responses to use the unicast # address - that way we will not confuse them with the periodic # RAs which go to the mcast address + # Sit and wait for the first periodic RA. + # + # TODO # self.pg0.ip6_ra_config(send_unicast=1) @@ -365,8 +472,23 @@ class TestIPv6(VppTestCase): IPv6(dst=self.pg0.local_ip6, src=ll) / ICMPv6ND_RS()) pkts = [p] - self.send_and_expect_ra( - self.pg0, pkts, "RS sourced from link-local", src_ip=ll) + self.send_and_expect_ra(self.pg0, pkts, + "RS sourced from link-local", + dst_ip=ll) + + # + # Send the RS multicast + # + self.pg0.ip6_ra_config(send_unicast=1) + dmac = in6_getnsmac(inet_pton(AF_INET6, "ff02::2")) + ll = mk_ll_addr(self.pg0.remote_mac) + p = (Ether(dst=dmac, src=self.pg0.remote_mac) / + IPv6(dst="ff02::2", src=ll) / + ICMPv6ND_RS()) + pkts = [p] + self.send_and_expect_ra(self.pg0, pkts, + "RS sourced from link-local", + dst_ip=ll) # # Source from the unspecified address ::. This happens when the RS @@ -376,73 +498,463 @@ class TestIPv6(VppTestCase): # If we happen to pick up the periodic RA at this point then so be it, # it's not an error. # + self.pg0.ip6_ra_config(send_unicast=1, suppress=1) + p = (Ether(dst=dmac, src=self.pg0.remote_mac) / + IPv6(dst="ff02::2", src="::") / + ICMPv6ND_RS()) + pkts = [p] + self.send_and_expect_ra(self.pg0, pkts, + "RS sourced from unspecified", + dst_ip="ff02::1", + filter_out_fn=None) + + # + # Configure The RA to announce the links prefix + # + self.pg0.ip6_ra_prefix(self.pg0.local_ip6n, + 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) + self.pg0.ip6_ra_config(send_unicast=1) + ll = mk_ll_addr(self.pg0.remote_mac) p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IPv6(dst=self.pg0.local_ip6, src="::") / + IPv6(dst=self.pg0.local_ip6, src=ll) / ICMPv6ND_RS()) - pkts = [p] + self.send_and_expect_ra(self.pg0, p, + "RA with prefix-info", + dst_ip=ll, + opt=opt) - self.pg0.add_stream(pkts) - self.pg0.add_stream(pkts) + # + # 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, + off_link=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, + "RA with Prefix info with L-flag=0", + dst_ip=ll, + opt=opt) + + # + # 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, + off_link=1, + no_autoconfig=1) + + 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, + "RA with Prefix info with A & L-flag=0", + dst_ip=ll, + opt=opt) + + # + # 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) + + 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, + "RA with Prefix info", + dst_ip=ll, + opt=opt) + + # + # 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, + off_link=1, + no_autoconfig=1) + + 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, + "RA with Prefix info with A & L-flag=0", + dst_ip=ll, + opt=opt) + + # + # Use the reset to defults 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, + use_default=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, + "RA with Prefix reverted to defaults", + dst_ip=ll, + opt=opt) + + # + # Advertise Another prefix. With no L-flag/A-flag + # + self.pg0.ip6_ra_prefix(self.pg1.local_ip6n, + 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)] + + self.pg0.ip6_ra_config(send_unicast=1) + ll = mk_ll_addr(self.pg0.remote_mac) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(dst=self.pg0.local_ip6, src=ll) / + ICMPv6ND_RS()) + self.send_and_expect_ra(self.pg0, p, + "RA with multiple Prefix infos", + dst_ip=ll, + opt=opt) + + # + # Remove the first refix-info - expect the second is still in the + # advert + # + self.pg0.ip6_ra_prefix(self.pg0.local_ip6n, + 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) + + 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, + opt=opt) + + # + # Remove the second prefix-info - expect no prefix-info i nthe adverts + # + self.pg0.ip6_ra_prefix(self.pg1.local_ip6n, + self.pg1.local_ip6_prefix_len, + is_no=1) + + 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) + + # + # Reset the periodic advertisements back to default values + # + self.pg0.ip6_ra_config(no=1, suppress=1, send_unicast=0) + + +class IPv6NDProxyTest(TestIPv6ND): + """ IPv6 ND ProxyTest Case """ + + def setUp(self): + super(IPv6NDProxyTest, self).setUp() + + # create 3 pg interfaces + self.create_pg_interfaces(range(3)) + + # pg0 is the master interface, with the configured subnet + self.pg0.admin_up() + self.pg0.config_ip6() + self.pg0.resolve_ndp() + + self.pg1.ip6_enable() + self.pg2.ip6_enable() + + def tearDown(self): + super(IPv6NDProxyTest, self).tearDown() + + def test_nd_proxy(self): + """ IPv6 Proxy ND """ + + # + # Generate some hosts in the subnet that we are proxying + # + self.pg0.generate_remote_hosts(8) + + nsma = in6_getnsma(inet_pton(AF_INET6, self.pg0.local_ip6)) + d = inet_ntop(AF_INET6, nsma) + + # + # Send an NS for one of those remote hosts on one of the proxy links + # expect no response since it's from an address that is not + # 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) / + ICMPv6ND_NS(tgt=self.pg0.local_ip6) / + 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) + + # + # try that NS again. this time we expect an NA back + # + self.pg1.add_stream(ns_pg1) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg0.get_capture(1, filter_out_fn=None) - found = 0 - for rx in capture: - if (rx.haslayer(ICMPv6ND_RA)): - # 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(self.pg0.local_mac))) - # sent to the all hosts mcast - self.assertEqual(in6_ptop(rx[IPv6].dst), "ff02::1") - - found = 1 - self.assertTrue(found) - - @unittest.skip("Unsupported") - def test_mrs(self): - """ IPv6 Multicast Router Solicitation Exceptions + rx = self.pg1.get_capture(1) - Test scenario: - """ + self.validate_na(self.pg1, rx[0], + dst_ip=self.pg0._remote_hosts[2].ip6, + tgt_ip=self.pg0.local_ip6) # - # An RS from a link source address - # - expect an RA in return + # ... and that we have an entry in the ND cache # - nsma = in6_getnsma(inet_pton(socket.AF_INET6, self.pg0.local_ip6)) - d = inet_ntop(socket.AF_INET6, nsma) + self.assertTrue(find_nbr(self, + self.pg1.sw_if_index, + self.pg0._remote_hosts[2].ip6, + inet=AF_INET6)) - p = (Ether(dst=getmacbyip6("ff02::2")) / - IPv6(dst=d, src=self.pg0.remote_ip6) / - ICMPv6MRD_Solicitation()) - pkts = [p] + # + # ... and we can route traffic to it + # + 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)) - self.pg0.add_stream(pkts) + self.pg0.add_stream(t) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - self.pg0.assert_nothing_captured( - remark="No response to NS source by address not on sub-net") + rx = self.pg1.get_capture(1) + rx = rx[0] + + 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) # - # An RS from a non link source address + # Test we proxy for the host on the main interface # - nsma = in6_getnsma(inet_pton(socket.AF_INET6, self.pg0.local_ip6)) - d = inet_ntop(socket.AF_INET6, nsma) + 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)) - p = (Ether(dst=getmacbyip6("ff02::2")) / - IPv6(dst=d, src="2002::2") / - ICMPv6MRD_Solicitation()) - pkts = [p] + self.pg0.add_stream(ns_pg0) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + rx = self.pg0.get_capture(1) - self.send_and_assert_no_replies(self.pg0, pkts, - "RA rate limited") - self.pg0.add_stream(pkts) + self.validate_na(self.pg0, rx[0], + tgt_ip=self.pg0._remote_hosts[2].ip6, + dst_ip=self.pg0.remote_ip6) + + # + # 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) / + ICMPv6ND_NS(tgt=self.pg0.local_ip6) / + 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.pg2.add_stream(ns_pg2) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + rx = self.pg2.get_capture(1) + + self.validate_na(self.pg2, rx[0], + dst_ip=self.pg0._remote_hosts[3].ip6, + tgt_ip=self.pg0.local_ip6) + + self.assertTrue(find_nbr(self, + self.pg2.sw_if_index, + self.pg0._remote_hosts[3].ip6, + inet=AF_INET6)) + + # + # hosts can communicate. pg2->pg1 + # + t2 = (Ether(dst=self.pg2.local_mac, + 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)) + + self.pg2.add_stream(t2) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + rx = self.pg1.get_capture(1) + rx = rx[0] + + 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) + + # + # 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.assertFalse(find_nbr(self, + self.pg2.sw_if_index, + self.pg0._remote_hosts[3].ip6, + inet=AF_INET6)) + self.assertFalse(find_nbr(self, + self.pg1.sw_if_index, + self.pg0._remote_hosts[2].ip6, + inet=AF_INET6)) + + # + # no longer proxy-ing... + # + self.send_and_assert_no_replies(self.pg0, ns_pg0, "Proxy unconfigured") + self.send_and_assert_no_replies(self.pg1, ns_pg1, "Proxy unconfigured") + self.send_and_assert_no_replies(self.pg2, ns_pg2, "Proxy unconfigured") + + # + # no longer forwarding. traffic generates NS out of the glean/main + # interface + # + self.pg2.add_stream(t2) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture(1) + + self.assertTrue(rx[0].haslayer(ICMPv6ND_NS)) + + +class TestIPNull(VppTestCase): + """ IPv6 routes via NULL """ + + def setUp(self): + super(TestIPNull, self).setUp() + + # create 2 pg interfaces + 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(TestIPNull, self).tearDown() + for i in self.pg_interfaces: + i.unconfig_ip6() + i.admin_down() + + def test_ip_null(self): + """ IP NULL route """ + + 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)) + + # + # 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.add_vpp_config() + + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture(1) + rx = rx[0] + icmp = rx[ICMPv6DestUnreach] + + # 0 = "No route to destination" + self.assertEqual(icmp.code, 0) + + # ICMP is rate limited. pause a bit + self.sleep(1) + + # + # 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.add_vpp_config() + + self.pg0.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - self.pg0.assert_nothing_captured( - remark="No response to NS source by address not on sub-net") + + rx = self.pg0.get_capture(1) + rx = rx[0] + icmp = rx[ICMPv6DestUnreach] + + # 1 = "Communication with destination administratively prohibited" + self.assertEqual(icmp.code, 1) if __name__ == '__main__':