+
+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")
+
+