Learn IP6 link-local ND entries from NSs sourced from link-local address 64/6264/2
authorNeale Ranns <nranns@cisco.com>
Wed, 19 Apr 2017 12:24:40 +0000 (05:24 -0700)
committerFlorin Coras <florin.coras@gmail.com>
Wed, 19 Apr 2017 20:23:34 +0000 (20:23 +0000)
Change-Id: I4c3ce4d58df7977490fc94991291422ea1e31ee3
Signed-off-by: Neale Ranns <nranns@cisco.com>
src/vnet/ethernet/arp.c
src/vnet/ip/ip6_neighbor.c
test/test_dhcp.py
test/test_ip6.py
test/util.py
test/vpp_interface.py
test/vpp_neighbor.py

index 3e292e4..31af4fb 100644 (file)
@@ -584,6 +584,9 @@ vnet_arp_set_ip4_over_ethernet_internal (vnet_main_t * vnm,
                                             FIB_PROTOCOL_IP4, &pfx.fp_addr,
                                             e->sw_if_index, ~0, 1, NULL,
                                             FIB_ROUTE_PATH_FLAG_NONE);
+       }
+      else
+       {
          e->flags |= ETHERNET_ARP_IP4_ENTRY_FLAG_NO_FIB_ENTRY;
        }
     }
index 42edb79..3118277 100644 (file)
@@ -634,6 +634,9 @@ vnet_set_ip6_ethernet_neighbor (vlib_main_t * vm,
                                             FIB_PROTOCOL_IP6, &pfx.fp_addr,
                                             n->key.sw_if_index, ~0, 1, NULL,
                                             FIB_ROUTE_PATH_FLAG_NONE);
+       }
+      else
+       {
          n->flags |= IP6_NEIGHBOR_FLAG_NO_FIB_ENTRY;
        }
     }
@@ -1027,7 +1030,7 @@ icmp6_neighbor_solicitation_or_advertisement (vlib_main_t * vm,
 
          /* If src address unspecified or link local, donot learn neighbor MAC */
          if (PREDICT_TRUE (error0 == ICMP6_ERROR_NONE && o0 != 0 &&
-                           !ip6_sadd_unspecified && !ip6_sadd_link_local))
+                           !ip6_sadd_unspecified))
            {
              ip6_neighbor_main_t *nm = &ip6_neighbor_main;
              if (nm->limit_neighbor_cache_size &&
@@ -1040,7 +1043,7 @@ icmp6_neighbor_solicitation_or_advertisement (vlib_main_t * vm,
                                              &h0->target_address,
                                              o0->ethernet_address,
                                              sizeof (o0->ethernet_address),
-                                             0, 0);
+                                             0, ip6_sadd_link_local);
            }
 
          if (is_solicitation && error0 == ICMP6_ERROR_NONE)
index 89667d3..03c749d 100644 (file)
@@ -6,6 +6,7 @@ import struct
 
 from framework import VppTestCase, VppTestRunner
 from vpp_neighbor import VppNeighbor
+from util import mk_ll_addr
 
 from scapy.layers.l2 import Ether, getmacbyip
 from scapy.layers.inet import IP, UDP, ICMP
@@ -24,13 +25,6 @@ DHCP6_CLIENT_PORT = 547
 DHCP6_SERVER_PORT = 546
 
 
-def mk_ll_addr(mac):
-
-    euid = in6_mactoifaceid(mac)
-    addr = "fe80::" + euid
-    return addr
-
-
 class TestDHCP(VppTestCase):
     """ DHCP Test Case """
 
index a8e8d4d..3ba0923 100644 (file)
@@ -77,7 +77,6 @@ class TestIPv6ND(VppTestCase):
     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)
@@ -86,11 +85,25 @@ class TestIPv6ND(VppTestCase):
         rx = rx[0]
         self.validate_ra(intf, rx, dst_ip)
 
+    def send_and_expect_na(self, intf, pkts, remark, dst_ip=None,
+                           tgt_ip=None,
+                           filter_out_fn=is_ipv6_misc):
+        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_na(intf, rx, dst_ip, tgt_ip)
+
     def send_and_assert_no_replies(self, intf, pkts, remark):
         intf.add_stream(pkts)
         self.pg_enable_capture(self.pg_interfaces)
         self.pg_start()
-        intf.assert_nothing_captured(remark=remark)
+        for i in self.pg_interfaces:
+            i.get_capture(0)
+            i.assert_nothing_captured(remark=remark)
 
 
 class TestIPv6(TestIPv6ND):
@@ -376,6 +389,59 @@ class TestIPv6(TestIPv6ND):
                                     128,
                                     inet=AF_INET6))
 
+        #
+        # 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) /
+             ICMPv6ND_NS(tgt=self.pg0.local_ip6) /
+             ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac))
+
+        self.send_and_expect_na(self.pg0, p,
+                                "NS from link-local",
+                                dst_ip=self.pg0._remote_hosts[2].ip6_ll,
+                                tgt_ip=self.pg0.local_ip6)
+
+        #
+        # we should have learned an ND entry for the peer's link-local
+        # but not inserted a route to it in the FIB
+        #
+        self.assertTrue(find_nbr(self,
+                                 self.pg0.sw_if_index,
+                                 self.pg0._remote_hosts[2].ip6_ll,
+                                 inet=AF_INET6))
+        self.assertFalse(find_route(self,
+                                    self.pg0._remote_hosts[2].ip6_ll,
+                                    128,
+                                    inet=AF_INET6))
+
+        #
+        # 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) /
+             ICMPv6ND_NS(tgt=self.pg0.local_ip6_ll) /
+             ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac))
+
+        self.send_and_expect_na(self.pg0, p,
+                                "NS to/from link-local",
+                                dst_ip=self.pg0._remote_hosts[3].ip6_ll,
+                                tgt_ip=self.pg0.local_ip6_ll)
+
+        #
+        # we should have learned an ND entry for the peer's link-local
+        # but not inserted a route to it in the FIB
+        #
+        self.assertTrue(find_nbr(self,
+                                 self.pg0.sw_if_index,
+                                 self.pg0._remote_hosts[3].ip6_ll,
+                                 inet=AF_INET6))
+        self.assertFalse(find_route(self,
+                                    self.pg0._remote_hosts[3].ip6_ll,
+                                    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
@@ -770,14 +836,10 @@ class IPv6NDProxyTest(TestIPv6ND):
         #
         # 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)
+        self.send_and_expect_na(self.pg1, ns_pg1,
+                                "NS to proxy entry",
+                                dst_ip=self.pg0._remote_hosts[2].ip6,
+                                tgt_ip=self.pg0.local_ip6)
 
         #
         # ... and that we have an entry in the ND cache
@@ -816,14 +878,10 @@ class IPv6NDProxyTest(TestIPv6ND):
                   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)
+        self.send_and_expect_na(self.pg0, ns_pg0,
+                                "NS to proxy entry on main",
+                                tgt_ip=self.pg0._remote_hosts[2].ip6,
+                                dst_ip=self.pg0.remote_ip6)
 
         #
         # Setup and resolve proxy for another host on another interface
@@ -837,14 +895,10 @@ class IPv6NDProxyTest(TestIPv6ND):
             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.send_and_expect_na(self.pg2, ns_pg2,
+                                "NS to proxy entry other interface",
+                                dst_ip=self.pg0._remote_hosts[3].ip6,
+                                tgt_ip=self.pg0.local_ip6)
 
         self.assertTrue(find_nbr(self,
                                  self.pg2.sw_if_index,
index d6b77f9..aeba2ab 100644 (file)
@@ -4,6 +4,7 @@ import socket
 import sys
 from abc import abstractmethod, ABCMeta
 from cStringIO import StringIO
+from scapy.layers.inet6 import in6_mactoifaceid
 
 
 def ppp(headline, packet):
@@ -52,6 +53,12 @@ def mactobinary(mac):
     return mac.replace(':', '').decode('hex')
 
 
+def mk_ll_addr(mac):
+    euid = in6_mactoifaceid(mac)
+    addr = "fe80::" + euid
+    return addr
+
+
 class NumericConstant(object):
     __metaclass__ = ABCMeta
 
@@ -101,10 +108,22 @@ class Host(object):
         """ IPv6 address of remote host - raw, suitable as API parameter."""
         return socket.inet_pton(socket.AF_INET6, self._ip6)
 
-    def __init__(self, mac=None, ip4=None, ip6=None):
+    @property
+    def ip6_ll(self):
+        """ IPv6 link-local address - string """
+        return self._ip6_ll
+
+    @property
+    def ip6n_ll(self):
+        """ IPv6 link-local address of remote host -
+        raw, suitable as API parameter."""
+        return socket.inet_pton(socket.AF_INET6, self._ip6_ll)
+
+    def __init__(self, mac=None, ip4=None, ip6=None, ip6_ll=None):
         self._mac = mac
         self._ip4 = ip4
         self._ip6 = ip6
+        self._ip6_ll = ip6_ll
 
 
 class ForeignAddressFactory(object):
index 5dba097..662015e 100644 (file)
@@ -1,7 +1,7 @@
 from abc import abstractmethod, ABCMeta
 import socket
 
-from util import Host
+from util import Host, mk_ll_addr
 from vpp_neighbor import VppNeighbor
 
 
@@ -54,6 +54,16 @@ class VppInterface(object):
         """Local IPv6 address - raw, suitable as API parameter."""
         return socket.inet_pton(socket.AF_INET6, self.local_ip6)
 
+    @property
+    def local_ip6_ll(self):
+        """Local IPv6 linnk-local address on VPP interface (string)."""
+        return self._local_ip6_ll
+
+    @property
+    def local_ip6n_ll(self):
+        """Local IPv6 link-local address - raw, suitable as API parameter."""
+        return self.local_ip6n_ll
+
     @property
     def remote_ip6(self):
         """IPv6 address of remote peer "connected" to this interface."""
@@ -133,7 +143,8 @@ class VppInterface(object):
             mac = "02:%02x:00:00:ff:%02x" % (self.sw_if_index, i)
             ip4 = "172.16.%u.%u" % (self.sw_if_index, i)
             ip6 = "fd01:%x::%x" % (self.sw_if_index, i)
-            host = Host(mac, ip4, ip6)
+            ip6_ll = mk_ll_addr(mac)
+            host = Host(mac, ip4, ip6, ip6_ll)
             self._remote_hosts.append(host)
             self._hosts_by_mac[mac] = host
             self._hosts_by_ip4[ip4] = host
@@ -176,6 +187,9 @@ class VppInterface(object):
                 "Could not find interface with sw_if_index %d "
                 "in interface dump %s" %
                 (self.sw_if_index, repr(r)))
+        self._local_ip6_ll = mk_ll_addr(self.local_mac)
+        self._local_ip6n_ll = socket.inet_pton(socket.AF_INET6,
+                                               self.local_ip6_ll)
 
     def config_ip4(self):
         """Configure IPv4 address on the VPP interface."""
index 6968b5f..5919cf8 100644 (file)
@@ -31,7 +31,7 @@ class VppNeighbor(VppObject):
     """
 
     def __init__(self, test, sw_if_index, mac_addr, nbr_addr,
-                 af=AF_INET, is_static=False, is_no_fib_entry=False):
+                 af=AF_INET, is_static=False, is_no_fib_entry=0):
         self._test = test
         self.sw_if_index = sw_if_index
         self.mac_addr = mactobinary(mac_addr)