X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=test%2Ftest_ip6.py;h=a8e8d4de9eaae5e31ae113fa034550e33972454d;hb=180279b912827c30494ec1b90ee4325a15cb337c;hp=e188970ac108709e79ccd11b56da71399c7b2182;hpb=32e1c010b0c34fd0984f7fc45fae648a182025c5;p=vpp.git diff --git a/test/test_ip6.py b/test/test_ip6.py index e188970ac10..a8e8d4de9ea 100644 --- a/test/test_ip6.py +++ b/test/test_ip6.py @@ -1,28 +1,99 @@ #!/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_ip_route import VppIpRoute, VppRoutePath, find_route, VppIpMRoute, \ + VppMRoutePath, MRouteItfFlags, MRouteEntryFlags +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, \ - 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_ismaddr from scapy.utils import inet_pton, inet_ntop + def mk_ll_addr(mac): euid = in6_mactoifaceid(mac) addr = "fe80::" + euid 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 +181,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 +294,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 +306,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 +324,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 +340,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,11 +352,35 @@ class TestIPv6(VppTestCase): self.send_and_assert_no_replies(self.pg0, pkts, "No response to NS for unknown target") - def validate_ra(self, intf, rx, dst_ip=None): + # + # A neighbor entry that has no associated FIB-entry + # + self.pg0.generate_remote_hosts(4) + nd_entry = VppNeighbor(self, + 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() + + # + # check we have the neighbor, but no route + # + self.assertTrue(find_nbr(self, + self.pg0.sw_if_index, + self.pg0._remote_hosts[2].ip6, + inet=AF_INET6)) + self.assertFalse(find_route(self, + self.pg0._remote_hosts[2].ip6, + 128, + inet=AF_INET6)) + + def validate_ra(self, intf, rx, dst_ip=None, mtu=9000, pi_opt=None): if not dst_ip: dst_ip = intf.remote_ip6 - # unicasted packets must come to the unicast mac + # unicasted packets must come to the unicast mac self.assertEqual(rx[Ether].dst, intf.remote_mac) # and from the router's MAC @@ -307,18 +396,47 @@ class TestIPv6(VppTestCase): 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): + filter_out_fn=is_ipv6_misc, + opt=None): 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) + self.validate_ra(intf, rx, dst_ip, pi_opt=opt) def test_rs(self): """ IPv6 Router Solicitation Exceptions @@ -387,7 +505,7 @@ class TestIPv6(VppTestCase): # Send the RS multicast # self.pg0.ip6_ra_config(send_unicast=1) - dmac = in6_getnsmac(inet_pton(socket.AF_INET6, "ff02::2")) + 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) / @@ -416,9 +534,550 @@ class TestIPv6(VppTestCase): filter_out_fn=None) # - # Reset the periodic advertisements back to default values + # 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=ll) / + ICMPv6ND_RS()) + 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 + # 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() + rx = self.pg1.get_capture(1) + + self.validate_na(self.pg1, rx[0], + dst_ip=self.pg0._remote_hosts[2].ip6, + tgt_ip=self.pg0.local_ip6) + + # + # ... and that we have an entry in the ND cache + # + self.assertTrue(find_nbr(self, + self.pg1.sw_if_index, + self.pg0._remote_hosts[2].ip6, + inet=AF_INET6)) + + # + # ... 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(t) + 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, 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)) + + self.pg0.add_stream(ns_pg0) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + rx = self.pg0.get_capture(1) + + 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() + + rx = self.pg0.get_capture(1) + rx = rx[0] + icmp = rx[ICMPv6DestUnreach] + + # 1 = "Communication with destination administratively prohibited" + self.assertEqual(icmp.code, 1) + + +class TestIPDisabled(VppTestCase): + """ IPv6 disabled """ + + def setUp(self): + super(TestIPDisabled, self).setUp() + + # create 2 pg interfaces + self.create_pg_interfaces(range(2)) + + # PG0 is IP enalbed + self.pg0.admin_up() + self.pg0.config_ip6() + self.pg0.resolve_ndp() + + # PG 1 is not IP enabled + self.pg1.admin_up() + + def tearDown(self): + super(TestIPDisabled, self).tearDown() + for i in self.pg_interfaces: + 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 """ + + # + # An (S,G). + # one accepting interface, pg0, 2 forwarding interfaces + # + route_ff_01 = VppIpMRoute( + self, + "::", + "ffef::1", 128, + MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE, + [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) + 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)) + 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)) + + # + # PG1 does not forward IP traffic + # + self.send_and_assert_no_replies(self.pg1, pu, "IPv6 disabled") + self.send_and_assert_no_replies(self.pg1, pm, "IPv6 disabled") + + # + # IP enable PG1 + # + self.pg1.config_ip6() + + # + # Now we get packets through + # + self.pg1.add_stream(pu) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + rx = self.pg0.get_capture(1) + + self.pg1.add_stream(pm) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + rx = self.pg0.get_capture(1) + + # + # Disable PG1 + # + self.pg1.unconfig_ip6() + + # + # PG1 does not forward IP traffic + # + self.send_and_assert_no_replies(self.pg1, pu, "IPv6 disabled") + self.send_and_assert_no_replies(self.pg1, pm, "IPv6 disabled") + + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner)