Attached hosts 20/5720/6
authorNeale Ranns <nranns@cisco.com>
Sat, 11 Mar 2017 13:55:21 +0000 (05:55 -0800)
committerFlorin Coras <florin.coras@gmail.com>
Fri, 17 Mar 2017 15:49:39 +0000 (15:49 +0000)
allow this config to function:
  set int ip address loop0 169.254.1.1/32  (the default GW address for attached hosts)
  set int unnumbered af_packet0 use loop0  ('enable' IP on the host interface)
  ip route add 192.168.1.1/32 via af_packet0 (where to find the host)
repeat for each host and host interface.
Inter-host communication is throught the /32 routes.
To allow this:
 1 - attached host routes have the ATTACHED flag set, so the ARP code accepts then as legitimate sources
 2 - unnumbered interfaces inherit the source address from the IP interface

Change-Id: Ib66c5f0e848c528f79372813adc3a0c11b50717f
Signed-off-by: Neale Ranns <nranns@cisco.com>
src/vnet/ethernet/arp.c
src/vnet/fib/fib_entry_src.c
src/vnet/fib/fib_path.c
src/vnet/fib/fib_path.h
src/vnet/fib/fib_table.c
src/vnet/fib/fib_types.h
src/vnet/interface_api.c
src/vnet/interface_cli.c
test/test_neighbor.py
test/vpp_interface.py

index d8ae844..75c7e20 100644 (file)
@@ -1016,7 +1016,6 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
          vnet_hw_interface_t *hw_if0;
          ethernet_arp_header_t *arp0;
          ethernet_header_t *eth0;
-         ip_adjacency_t *adj0;
          ip4_address_t *if_addr0, proxy_src;
          u32 pi0, error0, next0, sw_if_index0, conn_sw_if_index0, fib_index0;
          u8 is_request0, dst_is_local0, is_unnum0;
@@ -1073,6 +1072,11 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
                                          32);
          dst_flags = fib_entry_get_flags (dst_fei);
 
+         src_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
+                                         &arp0->ip4_over_ethernet[0].ip4,
+                                         32);
+         src_flags = fib_entry_get_flags (src_fei);
+
          conn_sw_if_index0 = fib_entry_get_resolving_interface (dst_fei);
 
          if (!(FIB_ENTRY_FLAG_CONNECTED & dst_flags))
@@ -1085,11 +1089,6 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
          is_unnum0 = sw_if_index0 != conn_sw_if_index0;
 
          /* Source must also be local to subnet of matching interface address. */
-         src_fei = ip4_fib_table_lookup (ip4_fib_get (fib_index0),
-                                         &arp0->ip4_over_ethernet[0].ip4,
-                                         32);
-         src_flags = fib_entry_get_flags (src_fei);
-
          if (!((FIB_ENTRY_FLAG_ATTACHED & src_flags) ||
                (FIB_ENTRY_FLAG_CONNECTED & src_flags)))
            {
@@ -1187,25 +1186,62 @@ arp_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
          /* get the adj from the destination's covering connected */
          if (NULL == pa)
            {
-             adj0 =
-               adj_get (fib_entry_get_adj_for_source
-                        (ip4_fib_table_lookup
-                         (ip4_fib_get (fib_index0),
-                          &arp0->ip4_over_ethernet[1].ip4, 31),
-                         FIB_SOURCE_INTERFACE));
-             if (adj0->lookup_next_index != IP_LOOKUP_NEXT_GLEAN)
-               {
-                 error0 = ETHERNET_ARP_ERROR_missing_interface_address;
-                 goto drop2;
-               }
              if (is_unnum0)
                {
                  if (!arp_unnumbered (p0, pi0, eth0, conn_sw_if_index0))
                    goto drop2;
                }
              else
-               vlib_buffer_advance (p0, -adj0->rewrite_header.data_bytes);
+               {
+                 ip_adjacency_t *adj0 = NULL;
+                 adj_index_t ai;
+
+                 if (FIB_ENTRY_FLAG_ATTACHED & src_flags)
+                   {
+                     /*
+                      * If the source is attached use the adj from that source.
+                      */
+                     ai = fib_entry_get_adj (src_fei);
+                     if (ADJ_INDEX_INVALID != ai)
+                       {
+                         adj0 = adj_get (ai);
+                       }
+                   }
+                 else
+                   {
+                     /*
+                      * Get the glean adj from the cover. This is presumably interface
+                      * sourced, and therefre needs to be a glean adj.
+                      */
+                     ai = fib_entry_get_adj_for_source
+                       (ip4_fib_table_lookup
+                        (ip4_fib_get (fib_index0),
+                         &arp0->ip4_over_ethernet[1].ip4, 31),
+                        FIB_SOURCE_INTERFACE);
+
+                     if (ADJ_INDEX_INVALID != ai)
+                       {
+                         adj0 = adj_get (ai);
+
+                         if (adj0->lookup_next_index == IP_LOOKUP_NEXT_GLEAN)
+                           {
+                             adj0 = NULL;
+                           }
+                       }
+                   }
+                 if (NULL != adj0)
+                   {
+                     vlib_buffer_advance (p0,
+                                          -adj0->rewrite_header.data_bytes);
+                   }
+                 else
+                   {
+                     error0 = ETHERNET_ARP_ERROR_missing_interface_address;
+                     goto drop2;
+                   }
+               }
            }
+
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
                                           n_left_to_next, pi0, next0);
 
index feb232d..aa1d5a2 100644 (file)
@@ -946,6 +946,10 @@ fib_path_is_attached (const fib_route_path_t *rpath)
     {
        return (!0);
     }
+    else if (rpath->frp_flags & FIB_ROUTE_PATH_ATTACHED)
+    {
+        return (!0);
+    }
     return (0);
 }
 
index aa545b5..3ed309f 100644 (file)
@@ -109,6 +109,10 @@ typedef enum fib_path_oper_attribute_t_ {
      * The path is resolved
      */
     FIB_PATH_OPER_ATTRIBUTE_RESOLVED,
+    /**
+     * The path is attached, despite what the next-hop may say.
+     */
+    FIB_PATH_OPER_ATTRIBUTE_ATTACHED,
     /**
      * The path has become a permanent drop.
      */
@@ -143,6 +147,7 @@ typedef enum fib_path_oper_flags_t_ {
     FIB_PATH_OPER_FLAG_RECURSIVE_LOOP = (1 << FIB_PATH_OPER_ATTRIBUTE_RECURSIVE_LOOP),
     FIB_PATH_OPER_FLAG_DROP = (1 << FIB_PATH_OPER_ATTRIBUTE_DROP),
     FIB_PATH_OPER_FLAG_RESOLVED = (1 << FIB_PATH_OPER_ATTRIBUTE_RESOLVED),
+    FIB_PATH_OPER_FLAG_ATTACHED = (1 << FIB_PATH_OPER_ATTRIBUTE_ATTACHED),
 } __attribute__ ((packed)) fib_path_oper_flags_t;
 
 /**
@@ -963,6 +968,8 @@ fib_path_route_flags_to_cfg_flags (const fib_route_path_t *rpath)
        cfg_flags |= FIB_PATH_CFG_FLAG_RESOLVE_ATTACHED;
     if (rpath->frp_flags & FIB_ROUTE_PATH_LOCAL)
        cfg_flags |= FIB_PATH_CFG_FLAG_LOCAL;
+    if (rpath->frp_flags & FIB_ROUTE_PATH_ATTACHED)
+       cfg_flags |= FIB_PATH_CFG_FLAG_ATTACHED;
 
     return (cfg_flags);
 }
index 91f49d0..14efc1a 100644 (file)
@@ -62,6 +62,10 @@ typedef enum fib_path_cfg_attribute_t_ {
      * Recursion constraint via attached
      */
     FIB_PATH_CFG_ATTRIBUTE_RESOLVE_ATTACHED,
+    /**
+     * The path is attached
+     */
+    FIB_PATH_CFG_ATTRIBUTE_ATTACHED,
     /**
      * The path is a for-us path
      */
@@ -83,6 +87,7 @@ typedef enum fib_path_cfg_attribute_t_ {
     [FIB_PATH_CFG_ATTRIBUTE_RESOLVE_HOST] = "resolve-host", \
     [FIB_PATH_CFG_ATTRIBUTE_RESOLVE_ATTACHED] = "resolve-attached", \
     [FIB_PATH_CFG_ATTRIBUTE_LOCAL] = "local",          \
+    [FIB_PATH_CFG_ATTRIBUTE_ATTACHED] = "attached",    \
 }
 
 #define FOR_EACH_FIB_PATH_CFG_ATTRIBUTE(_item) \
@@ -100,6 +105,7 @@ typedef enum fib_path_cfg_flags_t_ {
     FIB_PATH_CFG_FLAG_RESOLVE_HOST = (1 << FIB_PATH_CFG_ATTRIBUTE_RESOLVE_HOST),
     FIB_PATH_CFG_FLAG_RESOLVE_ATTACHED = (1 << FIB_PATH_CFG_ATTRIBUTE_RESOLVE_ATTACHED),
     FIB_PATH_CFG_FLAG_LOCAL = (1 << FIB_PATH_CFG_ATTRIBUTE_LOCAL),
+    FIB_PATH_CFG_FLAG_ATTACHED = (1 << FIB_PATH_CFG_ATTRIBUTE_ATTACHED),
 } __attribute__ ((packed)) fib_path_cfg_flags_t;
 
 
index a0ce0bb..7818d02 100644 (file)
@@ -480,6 +480,7 @@ fib_table_route_path_fixup (const fib_prefix_t *prefix,
        path->frp_sw_if_index != ~0)
     {
        path->frp_addr = prefix->fp_addr;
+        path->frp_flags |= FIB_ROUTE_PATH_ATTACHED;
     }
 }                
 
index 05e0e0a..1c5299a 100644 (file)
@@ -282,6 +282,10 @@ typedef enum fib_route_path_flags_t_
      * A for-us/local path
      */
     FIB_ROUTE_PATH_LOCAL = (1 << 2),
+    /**
+     * Attached path
+     */
+    FIB_ROUTE_PATH_ATTACHED = (1 << 3),
 } fib_route_path_flags_t;
 
 /**
index 28b09b5..44798c8 100644 (file)
@@ -459,11 +459,26 @@ static void vl_api_sw_interface_set_unnumbered_t_handler
     {
       si->flags |= VNET_SW_INTERFACE_FLAG_UNNUMBERED;
       si->unnumbered_sw_if_index = sw_if_index;
+
+      ip4_main.lookup_main.if_address_pool_index_by_sw_if_index
+       [unnumbered_sw_if_index] =
+       ip4_main.
+       lookup_main.if_address_pool_index_by_sw_if_index[sw_if_index];
+      ip6_main.
+       lookup_main.if_address_pool_index_by_sw_if_index
+       [unnumbered_sw_if_index] =
+       ip6_main.
+       lookup_main.if_address_pool_index_by_sw_if_index[sw_if_index];
     }
   else
     {
       si->flags &= ~(VNET_SW_INTERFACE_FLAG_UNNUMBERED);
       si->unnumbered_sw_if_index = (u32) ~ 0;
+
+      ip4_main.lookup_main.if_address_pool_index_by_sw_if_index
+       [unnumbered_sw_if_index] = ~0;
+      ip6_main.lookup_main.if_address_pool_index_by_sw_if_index
+       [unnumbered_sw_if_index] = ~0;
     }
   ip4_sw_interface_enable_disable (unnumbered_sw_if_index, mp->is_add);
   ip6_sw_interface_enable_disable (unnumbered_sw_if_index, mp->is_add);
index bd715e4..ec8530d 100644 (file)
@@ -864,6 +864,10 @@ set_unnumbered (vlib_main_t * vm,
       si->unnumbered_sw_if_index = (u32) ~ 0;
       ip4_sw_interface_enable_disable (unnumbered_sw_if_index, 0);
       ip6_sw_interface_enable_disable (unnumbered_sw_if_index, 0);
+      ip4_main.lookup_main.if_address_pool_index_by_sw_if_index
+       [unnumbered_sw_if_index] = ~0;
+      ip6_main.lookup_main.if_address_pool_index_by_sw_if_index
+       [unnumbered_sw_if_index] = ~0;
     }
   else if (is_set)
     {
@@ -871,6 +875,14 @@ set_unnumbered (vlib_main_t * vm,
       si->unnumbered_sw_if_index = inherit_from_sw_if_index;
       ip4_sw_interface_enable_disable (unnumbered_sw_if_index, 1);
       ip6_sw_interface_enable_disable (unnumbered_sw_if_index, 1);
+      ip4_main.lookup_main.if_address_pool_index_by_sw_if_index
+       [unnumbered_sw_if_index] =
+       ip4_main.lookup_main.if_address_pool_index_by_sw_if_index
+       [inherit_from_sw_if_index];
+      ip6_main.lookup_main.if_address_pool_index_by_sw_if_index
+       [unnumbered_sw_if_index] =
+       ip6_main.lookup_main.if_address_pool_index_by_sw_if_index
+       [inherit_from_sw_if_index];
     }
 
   return 0;
index a97a63f..f2b1cfa 100644 (file)
@@ -44,9 +44,15 @@ class ARPTestCase(VppTestCase):
 
     def tearDown(self):
         super(ARPTestCase, self).tearDown()
+        self.pg0.unconfig_ip4()
+        self.pg0.unconfig_ip6()
+
+        self.pg1.unconfig_ip4()
+        self.pg1.unconfig_ip6()
+
+        self.pg3.unconfig_ip4()
+
         for i in self.pg_interfaces:
-            i.unconfig_ip4()
-            i.unconfig_ip6()
             i.admin_down()
 
     def verify_arp_req(self, rx, smac, sip, dip):
@@ -115,7 +121,7 @@ class ARPTestCase(VppTestCase):
         #
         # Generate some hosts on the LAN
         #
-        self.pg1.generate_remote_hosts(6)
+        self.pg1.generate_remote_hosts(9)
 
         #
         # Send IP traffic to one of these unresolved hosts.
@@ -249,6 +255,47 @@ class ARPTestCase(VppTestCase):
         self.assertTrue(find_nbr(self,
                                  self.pg1.sw_if_index,
                                  self.pg1._remote_hosts[3].ip4))
+        #
+        # Fire in an ARP request before the interface becomes IP enabled
+        #
+        self.pg2.generate_remote_hosts(4)
+
+        p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
+             ARP(op="who-has",
+                 hwsrc=self.pg2.remote_mac,
+                 pdst=self.pg1.local_ip4,
+                 psrc=self.pg2.remote_hosts[3].ip4))
+        self.send_and_assert_no_replies(self.pg2, p,
+                                        "interface not IP enabled")
+
+        #
+        # Make pg2 un-numbered to pg1
+        #
+        self.pg2.set_unnumbered(self.pg1.sw_if_index)
+
+        #
+        # We should respond to ARP requests for the unnumbered to address
+        # once an attached route to the source is known
+        #
+        self.send_and_assert_no_replies(
+            self.pg2, p,
+            "ARP req for unnumbered address - no source")
+
+        attached_host = VppIpRoute(self, self.pg2.remote_hosts[3].ip4, 32,
+                                   [VppRoutePath("0.0.0.0",
+                                                 self.pg2.sw_if_index)])
+        attached_host.add_vpp_config()
+
+        self.pg2.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+
+        rx = self.pg2.get_capture(1)
+        self.verify_arp_resp(rx[0],
+                             self.pg2.local_mac,
+                             self.pg2.remote_mac,
+                             self.pg1.local_ip4,
+                             self.pg2.remote_hosts[3].ip4)
 
         #
         # A neighbor entry that has no associated FIB-entry
@@ -270,12 +317,8 @@ class ARPTestCase(VppTestCase):
                                     self.pg1._remote_hosts[4].ip4,
                                     32))
         #
-        # Unnumbered pg2 to pg1
-        #
-        self.pg2.set_unnumbered(self.pg1.sw_if_index)
-
-        #
-        # now we can form adjacencies out of pg2 from within pg1's subnet
+        # pg2 is unnumbered to pg1, so we can form adjacencies out of pg2
+        # from within pg1's subnet
         #
         arp_unnum = VppNeighbor(self,
                                 self.pg2.sw_if_index,
@@ -301,11 +344,101 @@ class ARPTestCase(VppTestCase):
                        self.pg0.remote_ip4,
                        self.pg1._remote_hosts[5].ip4)
 
+        #
+        # ARP requests from hosts in pg1's subnet sent on pg2 are replied to
+        # with the unnumbered interface's address as the source
+        #
+        p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
+             ARP(op="who-has",
+                 hwsrc=self.pg2.remote_mac,
+                 pdst=self.pg1.local_ip4,
+                 psrc=self.pg1.remote_hosts[6].ip4))
+
+        self.pg2.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+
+        rx = self.pg2.get_capture(1)
+        self.verify_arp_resp(rx[0],
+                             self.pg2.local_mac,
+                             self.pg2.remote_mac,
+                             self.pg1.local_ip4,
+                             self.pg1.remote_hosts[6].ip4)
+
+        #
+        # An attached host route out of pg2 for an undiscovered hosts generates
+        # an ARP request with the unnumbered address as the source
+        #
+        att_unnum = VppIpRoute(self, self.pg1.remote_hosts[7].ip4, 32,
+                               [VppRoutePath("0.0.0.0",
+                                             self.pg2.sw_if_index)])
+        att_unnum.add_vpp_config()
+
+        p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+             IP(src=self.pg0.remote_ip4,
+                dst=self.pg1._remote_hosts[7].ip4) /
+             UDP(sport=1234, dport=1234) /
+             Raw())
+
+        self.pg0.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+
+        rx = self.pg2.get_capture(1)
+
+        self.verify_arp_req(rx[0],
+                            self.pg2.local_mac,
+                            self.pg1.local_ip4,
+                            self.pg1._remote_hosts[7].ip4)
+
+        p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
+             ARP(op="who-has",
+                 hwsrc=self.pg2.remote_mac,
+                 pdst=self.pg1.local_ip4,
+                 psrc=self.pg1.remote_hosts[7].ip4))
+
+        self.pg2.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+
+        rx = self.pg2.get_capture(1)
+        self.verify_arp_resp(rx[0],
+                             self.pg2.local_mac,
+                             self.pg2.remote_mac,
+                             self.pg1.local_ip4,
+                             self.pg1.remote_hosts[7].ip4)
+
+        #
+        # An attached host route as yet unresolved out of pg2 for an
+        # undiscovered host, an ARP requests begets a response.
+        #
+        att_unnum1 = VppIpRoute(self, self.pg1.remote_hosts[8].ip4, 32,
+                                [VppRoutePath("0.0.0.0",
+                                              self.pg2.sw_if_index)])
+        att_unnum1.add_vpp_config()
+
+        p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
+             ARP(op="who-has",
+                 hwsrc=self.pg2.remote_mac,
+                 pdst=self.pg1.local_ip4,
+                 psrc=self.pg1.remote_hosts[8].ip4))
+
+        self.pg2.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+
+        rx = self.pg2.get_capture(1)
+        self.verify_arp_resp(rx[0],
+                             self.pg2.local_mac,
+                             self.pg2.remote_mac,
+                             self.pg1.local_ip4,
+                             self.pg1.remote_hosts[8].ip4)
+
         #
         # ERROR Cases
         #  1 - don't respond to ARP request for address not within the
         #      interface's sub-net
-        #
+        #  1a - nor within the unnumbered subnet
         p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
              ARP(op="who-has",
                  hwsrc=self.pg0.remote_mac,
@@ -313,6 +446,14 @@ class ARPTestCase(VppTestCase):
                  psrc=self.pg0.remote_ip4))
         self.send_and_assert_no_replies(self.pg0, p,
                                         "ARP req for non-local destination")
+        p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
+             ARP(op="who-has",
+                 hwsrc=self.pg2.remote_mac,
+                 pdst="10.10.10.3",
+                 psrc=self.pg1.remote_hosts[7].ip4))
+        self.send_and_assert_no_replies(
+            self.pg0, p,
+            "ARP req for non-local destination - unnum")
 
         #
         #  2 - don't respond to ARP request from an address not within the
@@ -325,6 +466,14 @@ class ARPTestCase(VppTestCase):
                  pdst=self.pg0.local_ip4))
         self.send_and_assert_no_replies(self.pg0, p,
                                         "ARP req for non-local source")
+        p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg2.remote_mac) /
+             ARP(op="who-has",
+                 hwsrc=self.pg2.remote_mac,
+                 psrc="10.10.10.3",
+                 pdst=self.pg0.local_ip4))
+        self.send_and_assert_no_replies(
+            self.pg0, p,
+            "ARP req for non-local source - unnum")
 
         #
         #  3 - don't respond to ARP request from an address that belongs to
@@ -358,6 +507,10 @@ class ARPTestCase(VppTestCase):
         static_arp.remove_vpp_config()
         self.pg2.unset_unnumbered(self.pg1.sw_if_index)
 
+        # need this to flush the adj-fibs
+        self.pg2.unset_unnumbered(self.pg1.sw_if_index)
+        self.pg2.admin_down()
+
     def test_proxy_arp(self):
         """ Proxy ARP """
 
@@ -500,7 +653,7 @@ class ARPTestCase(VppTestCase):
         #
         # clean up on interface 2
         #
-        self.pg2.set_unnumbered(self.pg1.sw_if_index)
+        self.pg2.unset_unnumbered(self.pg1.sw_if_index)
 
     def test_mpls(self):
         """ MPLS """
@@ -560,6 +713,7 @@ class ARPTestCase(VppTestCase):
                               55,
                               self.pg0.remote_ip4,
                               "10.0.0.1")
+        self.pg2.unconfig_ip4()
 
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)
index 8135bc8..aeaf27a 100644 (file)
@@ -336,7 +336,7 @@ class VppInterface(object):
             ip_sw_if_index)
 
     def unset_unnumbered(self, ip_sw_if_index):
-        """ Unaet the interface to unnumbered via ip_sw_if_index """
+        """ Unset the interface to unnumbered via ip_sw_if_index """
         self.test.vapi.sw_interface_set_unnumbered(
             self.sw_if_index,
             ip_sw_if_index,