Port glean neighbor entry support to IPv6 59/859/3
authorPierre Pfister <ppfister@cisco.com>
Mon, 25 Apr 2016 13:15:15 +0000 (14:15 +0100)
committerDamjan Marion <damarion@cisco.com>
Tue, 26 Apr 2016 14:09:00 +0000 (14:09 +0000)
This patch is more or less a port of I71f3ba0c8192 to IPv6.
In practice it allows creating a route via a neighbor which is not resolved yet.
It also adds static flag to IPv6 neighbor entries.
And as Damjan suggested, it formalizes ip46_address_t by using
the IPv4 embedded IPv6 address format.

Change-Id: Ifa7328a03380ea4ff118b7ca4897b4ab23a3e57c
Signed-off-by: Pierre Pfister <ppfister@cisco.com>
12 files changed:
vnet/vnet/ethernet/arp.c
vnet/vnet/ip/format.c
vnet/vnet/ip/format.h
vnet/vnet/ip/ip.h
vnet/vnet/ip/ip6.h
vnet/vnet/ip/ip6_format.c
vnet/vnet/ip/ip6_forward.c
vnet/vnet/ip/ip6_neighbor.c
vnet/vnet/ip/ip6_packet.h
vnet/vnet/ip/lookup.c
vnet/vnet/ip/lookup.h
vpp/api/api.c

index 220d0d2..aa37f25 100644 (file)
@@ -390,7 +390,8 @@ vnet_arp_set_ip4_over_ethernet_internal (vnet_main_t * vnm,
       e = pool_elt_at_index (am->ip4_entry_pool, p[0]);
 
       /* Refuse to over-write static arp. */
-      if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC)
+      if (!is_static &&
+          (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC))
        return -2;
       make_new_arp_cache_entry = 0;
     }
@@ -1313,7 +1314,7 @@ arp_add_del_adj_cb (struct ip_lookup_main_t * lm,
        {
          if (!e)
            clib_warning("Adjacency contains unknown ARP next hop %U (del)",
-                        format_ip4_address, &adj->arp.next_hop);
+                        format_ip46_address, &adj->arp.next_hop);
          else
            arp_ip4_entry_del_adj(e, adj->heap_handle);
        }
@@ -1321,7 +1322,7 @@ arp_add_del_adj_cb (struct ip_lookup_main_t * lm,
        {
          if (!e)
            clib_warning("Adjacency contains unknown ARP next hop %U (add)",
-                        format_ip4_address, &adj->arp.next_hop);
+                        format_ip46_address, &adj->arp.next_hop);
          else
            arp_ip4_entry_add_adj(e, adj->heap_handle);
        }
@@ -1601,7 +1602,7 @@ vnet_arp_glean_add(u32 fib_index, void * next_hop_arg)
 
   memset(&args, 0, sizeof(args));
   clib_memcpy(&add_adj, adj, sizeof(add_adj));
-  add_adj.arp.next_hop.ip4.as_u32 = next_hop->as_u32; /* install neighbor /32 route */
+  ip46_address_set_ip4(&add_adj.arp.next_hop, next_hop); /* install neighbor /32 route */
   args.table_index_or_table_id = fib_index;
   args.flags = IP4_ROUTE_FLAG_FIB_INDEX | IP4_ROUTE_FLAG_ADD| IP4_ROUTE_FLAG_NEIGHBOR;
   args.dst_address.as_u32 = next_hop->as_u32;
@@ -1714,7 +1715,7 @@ ip_arp_add_del_command_fn (vlib_main_t * vm,
 
 VLIB_CLI_COMMAND (ip_arp_add_del_command, static) = {
     .path = "set ip arp",
-    .short_help = "set ip arp [del] <intfc> <ip-address> <mac-address>",
+    .short_help = "set ip arp [del] <intfc> <ip-address> <mac-address> [static] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
     .function = ip_arp_add_del_command_fn,
 };
 
index 9dda4c5..0061d7e 100644 (file)
@@ -107,13 +107,3 @@ uword unformat_tcp_udp_port (unformat_input_t * input, va_list * args)
   *result = port;
   return 1;
 }
-
-uword unformat_ip46_address (unformat_input_t * input, va_list * args)
-{
-  ip46_address_t * a = va_arg (*args, ip46_address_t *);
-  u32 is_ip6 = va_arg (*args, u32);
-  if (is_ip6)
-    return unformat_user (input, unformat_ip6_address, &a->ip6);
-  else
-    return unformat_user (input, unformat_ip4_address, &a->ip4);
-}
index 511a934..4d73d6b 100644 (file)
@@ -51,6 +51,15 @@ unformat_function_t unformat_tcp_udp_port;
 format_function_t format_ip_adjacency;
 format_function_t format_ip_adjacency_packet_data;
 
+format_function_t format_ip46_address;
+
+typedef enum {
+  IP46_TYPE_ANY,
+  IP46_TYPE_IP4,
+  IP46_TYPE_IP6
+} ip46_type_t;
+/* unformat_ip46_address expects arguments (ip46_address_t *, ip46_type_t)
+ * The type argument is used to enforce a particular IP version. */
 unformat_function_t unformat_ip46_address;
 
 /* IP4 */
index de46ad3..45062cf 100644 (file)
 
 #include <vnet/classify/vnet_classify.h>
 
-typedef union {
-  ip4_address_t ip4;
-  ip6_address_t ip6;
-} ip46_address_t;
-
 /* Per protocol info. */
 typedef struct {
   /* Protocol name (also used as hash key). */
index ff65d3a..b104359 100644 (file)
@@ -373,6 +373,9 @@ void ip6_adjacency_set_interface_route (vnet_main_t * vnm,
                                        u32 sw_if_index,
                                        u32 if_address_index);
 
+u32
+vnet_ip6_neighbor_glean_add(u32 fib_index, void * next_hop_arg);
+
 clib_error_t *
 ip6_probe_neighbor (vlib_main_t * vm, ip6_address_t * dst, u32 sw_if_index);
 
@@ -395,7 +398,8 @@ vnet_set_ip6_ethernet_neighbor (vlib_main_t * vm,
                                 u32 sw_if_index,
                                 ip6_address_t * a,
                                 u8 * link_layer_address,
-                                uword n_bytes_link_layer_address);
+                                uword n_bytes_link_layer_address,
+                                int is_static);
 int
 vnet_unset_ip6_ethernet_neighbor (vlib_main_t * vm,
                                   u32 sw_if_index,
index 1a2810e..ad834f4 100644 (file)
@@ -320,3 +320,28 @@ uword unformat_ip6_header (unformat_input_t * input, va_list * args)
 
   return 1;
 }
+
+/* Parse an IP46 address. */
+uword unformat_ip46_address (unformat_input_t * input, va_list * args)
+{
+  ip46_address_t *ip46 = va_arg (*args, ip46_address_t *);
+  ip46_type_t type = va_arg (*args, ip46_type_t);
+  if ((type != IP46_TYPE_IP6) &&
+      unformat(input, "%U", unformat_ip4_address, &ip46->ip4)) {
+    ip46_address_mask_ip4(ip46);
+    return 1;
+  } else if ((type != IP46_TYPE_IP4) &&
+      unformat(input, "%U", unformat_ip6_address, &ip46->ip6)) {
+    return 1;
+  }
+  return 0;
+}
+
+/* Format an IP46 address. */
+u8 * format_ip46_address (u8 * s, va_list * args)
+{
+  ip46_address_t *ip46 = va_arg (*args, ip46_address_t *);
+  return ip46_address_is_ip4(ip46)?
+      format(s, "%U", format_ip4_address, &ip46->ip4):
+      format(s, "%U", format_ip6_address, &ip46->ip6);
+}
index c8b9df0..d001ebb 100644 (file)
@@ -353,14 +353,29 @@ ip6_add_del_route_next_hop (ip6_main_t * im,
           kv.key[2] = ((u64)((fib - im->fibs))<<32) | 128;
 
           if (BV(clib_bihash_search)(&im->ip6_lookup_table, &kv, &value) < 0)
+          {
+            ip_adjacency_t * adj;
+            nh_adj_index = ip6_fib_lookup_with_table (im, fib_index, next_hop);
+            adj = ip_get_adjacency (lm, nh_adj_index);
+            /* if ND interface adjacencty is present, we need to
+                             install ND adjaceny for specific next hop */
+            if (adj->lookup_next_index == IP_LOOKUP_NEXT_ARP &&
+                adj->arp.next_hop.ip6.as_u64[0] == 0 &&
+                adj->arp.next_hop.ip6.as_u64[1] == 0)
+            {
+              nh_adj_index = vnet_ip6_neighbor_glean_add(fib_index, next_hop);
+            }
+            else
             {
               vnm->api_errno = VNET_API_ERROR_UNKNOWN_DESTINATION;
               error = clib_error_return (0, "next-hop %U/128 not in FIB",
                                          format_ip6_address, next_hop);
               goto done;
             }
-          
-          nh_adj_index = value.value;
+          }
+          else
+            nh_adj_index = value.value;
+
         }
     }
   else
@@ -424,6 +439,28 @@ ip6_add_del_route_next_hop (ip6_main_t * im,
       goto done;
     }
 
+  /* Destination is not known and default weight is set so add route
+     to existing non-multipath adjacency */
+  if (dst_adj_index == ~0 && next_hop_weight == 1 && next_hop_sw_if_index == ~0)
+  {
+    /* create new adjacency */
+    ip6_add_del_route_args_t a;
+    a.table_index_or_table_id = fib_index;
+    a.flags = ((is_del ? IP6_ROUTE_FLAG_DEL : IP6_ROUTE_FLAG_ADD)
+        | IP6_ROUTE_FLAG_FIB_INDEX
+        | IP6_ROUTE_FLAG_KEEP_OLD_ADJACENCY
+        | (flags & (IP6_ROUTE_FLAG_NO_REDISTRIBUTE
+            | IP6_ROUTE_FLAG_NOT_LAST_IN_GROUP)));
+    a.dst_address = dst_address[0];
+    a.dst_address_length = dst_address_length;
+    a.adj_index = nh_adj_index;
+    a.add_adj = 0;
+    a.n_add_adj = 0;
+
+    ip6_add_del_route (im, &a);
+    goto done;
+  }
+
   old_mp_adj_index = dst_adj ? dst_adj->heap_handle : ~0;
 
   if (! ip_multipath_adjacency_add_del_next_hop
@@ -872,6 +909,8 @@ void ip6_adjacency_set_interface_route (vnet_main_t * vnm,
       n = IP_LOOKUP_NEXT_ARP;
       node_index = ip6_discover_neighbor_node.index;
       adj->if_address_index = if_address_index;
+      adj->arp.next_hop.ip6.as_u64[0] = 0;
+      adj->arp.next_hop.ip6.as_u64[1] = 0;
   }
   else
     {
@@ -1840,6 +1879,12 @@ ip6_discover_neighbor (vlib_main_t * vm,
 
          adj0 = ip_get_adjacency (lm, adj_index0);
 
+         if (adj0->arp.next_hop.ip6.as_u64[0] ||
+             adj0->arp.next_hop.ip6.as_u64[1]) {
+           ip0->dst_address.as_u64[0] = adj0->arp.next_hop.ip6.as_u64[0];
+           ip0->dst_address.as_u64[1] = adj0->arp.next_hop.ip6.as_u64[1];
+         }
+
          a0 = hash_seeds[0];
          b0 = hash_seeds[1];
          c0 = hash_seeds[2];
index acb1d8d..329cc6d 100644 (file)
@@ -36,7 +36,11 @@ typedef struct {
 typedef struct {
   ip6_neighbor_key_t key;
   u8 link_layer_address[8];
+  u16 flags;
+#define IP6_NEIGHBOR_FLAG_STATIC (1 << 0)
+#define IP6_NEIGHBOR_FLAG_GLEAN  (2 << 0)
   u64 cpu_time_last_updated;
+  u32 *adjacencies;
 } ip6_neighbor_t;
 
 /* advertised prefix option */ 
@@ -200,17 +204,26 @@ static u8 * format_ip6_neighbor_ip6_entry (u8 * s, va_list * va)
   ip6_neighbor_t * n = va_arg (*va, ip6_neighbor_t *);
   vnet_main_t * vnm = vnet_get_main();
   vnet_sw_interface_t * si;
+  u8 * flags = 0;
 
   if (! n)
-    return format (s, "%=12s%=20s%=20s%=40s", "Time", "Address", "Link layer", "Interface");
+    return format (s, "%=12s%=20s%=6s%=20s%=40s", "Time", "Address", "Flags", "Link layer", "Interface");
+
+  if (n->flags & IP6_NEIGHBOR_FLAG_GLEAN)
+    flags = format(flags, "G");
+
+  if (n->flags & IP6_NEIGHBOR_FLAG_STATIC)
+    flags = format(flags, "S");
 
   si = vnet_get_sw_interface (vnm, n->key.sw_if_index);
-  s = format (s, "%=12U%=20U%=20U%=40U",
+  s = format (s, "%=12U%=20U%=6s%=20U%=40U",
              format_vlib_cpu_time, vm, n->cpu_time_last_updated,
              format_ip6_address, &n->key.ip6_address,
+             flags ? (char *)flags : "",
              format_ethernet_address, n->link_layer_address,
              format_vnet_sw_interface_name, vnm, si);
 
+  vec_free(flags);
   return s;
 }
 
@@ -278,7 +291,7 @@ static void unset_random_neighbor_entry (void)
 
 typedef struct {
   u8 is_add;
-  u8 pad;
+  u8 is_static;
   u8 link_layer_address[6];
   u32 sw_if_index;
   ip6_address_t addr;
@@ -293,13 +306,14 @@ static void set_unset_ip6_neighbor_rpc
  u32 sw_if_index,
  ip6_address_t * a,
  u8 *link_layer_addreess,
- int is_add)
+ int is_add, int is_static)
 {
   ip6_neighbor_set_unset_rpc_args_t args;
   void vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length);
   
   args.sw_if_index = sw_if_index;
   args.is_add = is_add;
+  args.is_static = is_static;
   clib_memcpy (&args.addr, a, sizeof (*a));
   clib_memcpy (args.link_layer_address, link_layer_addreess, 6);
   
@@ -313,22 +327,27 @@ vnet_set_ip6_ethernet_neighbor (vlib_main_t * vm,
                                 u32 sw_if_index,
                                 ip6_address_t * a,
                                 u8 * link_layer_address,
-                                uword n_bytes_link_layer_address)
+                                uword n_bytes_link_layer_address,
+                                int is_static)
 {
   vnet_main_t * vnm = vnet_get_main();
   ip6_neighbor_main_t * nm = &ip6_neighbor_main;
   ip6_neighbor_key_t k;
-  ip6_neighbor_t * n;
+  ip6_neighbor_t * n = 0;
   ip6_main_t * im = &ip6_main;
+  ip_lookup_main_t * lm = &im->lookup_main;
+  int make_new_nd_cache_entry=1;
   uword * p;
   u32 next_index;
+  u32 adj_index;
+  ip_adjacency_t *existing_adj;
   pending_resolution_t * pr;
 
 #if DPDK > 0
   if (os_get_cpu_number())
     {
       set_unset_ip6_neighbor_rpc (vm, sw_if_index, a, link_layer_address,
-                                  1 /* set new neighbor */);
+                                  1 /* set new neighbor */, is_static);
       return 0;
     }
 #endif
@@ -340,44 +359,80 @@ vnet_set_ip6_ethernet_neighbor (vlib_main_t * vm,
   vlib_worker_thread_barrier_sync (vm);
 
   p = mhash_get (&nm->neighbor_index_by_key, &k);
-  if (p)
+  if (p) {
     n = pool_elt_at_index (nm->neighbor_pool, p[0]);
-  else
+    /* Refuse to over-write static neighbor entry. */
+    if (!is_static &&
+        (n->flags & IP6_NEIGHBOR_FLAG_STATIC))
+      return -2;
+    make_new_nd_cache_entry = 0;
+  }
+
+  /* Note: always install the route. It might have been deleted */
+  ip6_add_del_route_args_t args;
+  ip_adjacency_t adj;
+
+  memset (&adj, 0, sizeof(adj));
+  adj.lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
+  adj.explicit_fib_index = ~0;
+
+  vnet_rewrite_for_sw_interface
+  (vnm,
+   VNET_L3_PACKET_TYPE_IP6,
+   sw_if_index,
+   ip6_rewrite_node.index,
+   link_layer_address,
+   &adj.rewrite_header,
+   sizeof (adj.rewrite_data));
+
+  /* result of this lookup should be next-hop adjacency */
+  adj_index = ip6_fib_lookup_with_table (im, im->fib_index_by_sw_if_index[sw_if_index], a);
+  existing_adj = ip_get_adjacency(lm, adj_index);
+
+  if (existing_adj->lookup_next_index == IP_LOOKUP_NEXT_ARP &&
+      existing_adj->arp.next_hop.ip6.as_u64[0] == a->as_u64[0] &&
+      existing_adj->arp.next_hop.ip6.as_u64[1] == a->as_u64[1])
+  {
+    u32 * ai;
+    u32 * adjs = vec_dup(n->adjacencies);
+    /* Update all adj assigned to this arp entry */
+    vec_foreach(ai, adjs)
     {
-      ip6_add_del_route_args_t args;
-      ip_adjacency_t adj;
-
-      memset (&adj, 0, sizeof(adj));
-      adj.lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
-      adj.explicit_fib_index = ~0;
-
-      vnet_rewrite_for_sw_interface
-       (vnm,
-        VNET_L3_PACKET_TYPE_IP6,
-        sw_if_index,
-        ip6_rewrite_node.index,
-        link_layer_address,
-        &adj.rewrite_header,
-        sizeof (adj.rewrite_data));
-
-      args.table_index_or_table_id = im->fib_index_by_sw_if_index[sw_if_index];
-      args.flags = IP6_ROUTE_FLAG_FIB_INDEX | IP6_ROUTE_FLAG_ADD | IP6_ROUTE_FLAG_NEIGHBOR;
-      args.dst_address = a[0];
-      args.dst_address_length = 128;
-      args.adj_index = ~0;
-      args.add_adj = &adj;
-      args.n_add_adj = 1;
-
-      ip6_add_del_route (im, &args);
-      pool_get (nm->neighbor_pool, n);
-      mhash_set (&nm->neighbor_index_by_key, &k, n - nm->neighbor_pool,
-                /* old value */ 0);
-      n->key = k;
+      int i;
+      ip_adjacency_t * uadj = ip_get_adjacency(lm, *ai);
+      for (i = 0; i < uadj->n_adj; i++)
+        if (uadj[i].lookup_next_index == IP_LOOKUP_NEXT_ARP &&
+            uadj[i].arp.next_hop.ip6.as_u64[0] == a->as_u64[0] &&
+            uadj[i].arp.next_hop.ip6.as_u64[1] == a->as_u64[1])
+          ip_update_adjacency (lm, *ai + i, &adj);
     }
+    vec_free(adjs);
+  }
+  else
+  {
+    /* create new adj */
+    args.table_index_or_table_id = im->fib_index_by_sw_if_index[sw_if_index];
+    args.flags = IP6_ROUTE_FLAG_FIB_INDEX | IP6_ROUTE_FLAG_ADD | IP6_ROUTE_FLAG_NEIGHBOR;
+    args.dst_address = a[0];
+    args.dst_address_length = 128;
+    args.adj_index = ~0;
+    args.add_adj = &adj;
+    args.n_add_adj = 1;
+    ip6_add_del_route (im, &args);
+  }
+
+  if (make_new_nd_cache_entry) {
+    pool_get (nm->neighbor_pool, n);
+    mhash_set (&nm->neighbor_index_by_key, &k, n - nm->neighbor_pool,
+               /* old value */ 0);
+    n->key = k;
+  }
 
   /* Update time stamp and ethernet address. */
   clib_memcpy (n->link_layer_address, link_layer_address, n_bytes_link_layer_address);
   n->cpu_time_last_updated = clib_cpu_time_now ();
+  if (is_static)
+    n->flags |= IP6_NEIGHBOR_FLAG_STATIC;
 
   /* Customer(s) waiting for this address to be resolved? */
   p = mhash_get (&nm->pending_resolutions_by_address, a);
@@ -422,7 +477,7 @@ vnet_unset_ip6_ethernet_neighbor (vlib_main_t * vm,
   if (os_get_cpu_number())
     {
       set_unset_ip6_neighbor_rpc (vm, sw_if_index, a, link_layer_address,
-                                  0 /* unset */);
+                                  0 /* unset */, 0);
       return 0;
     }
 #endif
@@ -458,6 +513,56 @@ vnet_unset_ip6_ethernet_neighbor (vlib_main_t * vm,
   return rv;
 }
 
+
+u32
+vnet_ip6_neighbor_glean_add(u32 fib_index, void * next_hop_arg)
+{
+  ip6_neighbor_main_t * nm = &ip6_neighbor_main;
+  ip6_main_t * im = &ip6_main;
+  ip_lookup_main_t * lm = &im->lookup_main;
+  ip6_address_t * next_hop = next_hop_arg;
+  ip_adjacency_t add_adj, *adj;
+  ip6_add_del_route_args_t args;
+  ip6_neighbor_t * n;
+  ip6_neighbor_key_t k;
+  u32 adj_index;
+
+  adj_index = ip6_fib_lookup_with_table(im, fib_index, next_hop);
+  adj = ip_get_adjacency(lm, adj_index);
+
+  if (!adj || adj->lookup_next_index != IP_LOOKUP_NEXT_ARP)
+    return ~0;
+
+  if (adj->arp.next_hop.ip6.as_u64[0] ||
+      adj->arp.next_hop.ip6.as_u64[1])
+    return adj_index;
+
+  k.sw_if_index = adj->rewrite_header.sw_if_index;
+  k.ip6_address = *next_hop;
+  k.pad = 0;
+  if (mhash_get (&nm->neighbor_index_by_key, &k))
+    return adj_index;
+
+  pool_get (nm->neighbor_pool, n);
+  mhash_set (&nm->neighbor_index_by_key, &k, n - nm->neighbor_pool, /* old value */ 0);
+  n->key = k;
+  n->cpu_time_last_updated = clib_cpu_time_now ();
+  n->flags = IP6_NEIGHBOR_FLAG_GLEAN;
+
+  memset(&args, 0, sizeof(args));
+  memcpy(&add_adj, adj, sizeof(add_adj));
+  add_adj.arp.next_hop.ip6 = *next_hop; /* install neighbor /128 route */
+  args.table_index_or_table_id = fib_index;
+  args.flags = IP6_ROUTE_FLAG_FIB_INDEX | IP6_ROUTE_FLAG_ADD | IP6_ROUTE_FLAG_NEIGHBOR;
+  args.dst_address = *next_hop;
+  args.dst_address_length = 128;
+  args.adj_index = ~0;
+  args.add_adj = &add_adj;
+  args.n_add_adj = 1;
+  ip6_add_del_route (im, &args);
+  return ip6_fib_lookup_with_table (im, fib_index, next_hop);
+}
+
 #if DPDK > 0
 static void ip6_neighbor_set_unset_rpc_callback 
 ( ip6_neighbor_set_unset_rpc_args_t * a)
@@ -465,7 +570,7 @@ static void ip6_neighbor_set_unset_rpc_callback
   vlib_main_t * vm = vlib_get_main();
   if (a->is_add) 
       vnet_set_ip6_ethernet_neighbor (vm, a->sw_if_index, &a->addr, 
-                                      a->link_layer_address, 6);
+                                      a->link_layer_address, 6, a->is_static);
   else
     vnet_unset_ip6_ethernet_neighbor (vm, a->sw_if_index, &a->addr, 
                                       a->link_layer_address, 6);
@@ -530,6 +635,7 @@ set_ip6_neighbor (vlib_main_t * vm,
   u8 mac_address[6];
   int addr_valid = 0;
   int is_del = 0;
+  int is_static = 0;
   u32 sw_if_index;
 
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) 
@@ -543,6 +649,8 @@ set_ip6_neighbor (vlib_main_t * vm,
 
       else if (unformat (input, "delete") || unformat (input, "del"))
         is_del = 1;
+      else if (unformat (input, "static"))
+        is_static = 1;
       else
         break;
     }
@@ -552,7 +660,7 @@ set_ip6_neighbor (vlib_main_t * vm,
   
   if (!is_del)
     vnet_set_ip6_ethernet_neighbor (vm, sw_if_index, &addr,
-                                    mac_address, sizeof(mac_address));
+                                    mac_address, sizeof(mac_address), is_static);
   else
     vnet_unset_ip6_ethernet_neighbor (vm, sw_if_index, &addr,
                                       mac_address, sizeof(mac_address));
@@ -562,7 +670,7 @@ set_ip6_neighbor (vlib_main_t * vm,
 VLIB_CLI_COMMAND (set_ip6_neighbor_command, static) = {
   .path = "set ip6 neighbor",
   .function = set_ip6_neighbor,
-  .short_help = "set ip6 neighbor [del] <intfc> <ip6-address> <mac-address>",
+  .short_help = "set ip6 neighbor [del] <intfc> <ip6-address> <mac-address> [static]",
 };
 
 typedef enum {
@@ -666,7 +774,7 @@ icmp6_neighbor_solicitation_or_advertisement (vlib_main_t * vm,
               vnet_set_ip6_ethernet_neighbor (
                   vm, sw_if_index0,
                   is_solicitation ? &ip0->src_address : &h0->target_address,
-                  o0->ethernet_address, sizeof (o0->ethernet_address));
+                  o0->ethernet_address, sizeof (o0->ethernet_address), 0);
             }
 
          if (is_solicitation && error0 == ICMP6_ERROR_NONE)
@@ -931,7 +1039,7 @@ icmp6_router_solicitation(vlib_main_t * vm,
               vnet_set_ip6_ethernet_neighbor (vm, sw_if_index0,
                                               &ip0->src_address,
                                               o0->ethernet_address,
-                                              sizeof (o0->ethernet_address));
+                                              sizeof (o0->ethernet_address), 0);
           }
              
          /* default is to drop */
@@ -3072,10 +3180,87 @@ clib_error_t *ip6_set_neighbor_limit (u32 neighbor_limit)
   return 0;
 }
 
+
+static void
+ip6_neighbor_entry_del_adj(ip6_neighbor_t *n, u32 adj_index)
+{
+  int done = 0;
+  int i;
+  while (!done)
+    {
+      vec_foreach_index(i, n->adjacencies)
+        if (vec_elt(n->adjacencies, i) == adj_index)
+          {
+            vec_del1(n->adjacencies, i);
+            continue;
+          }
+      done = 1;
+    }
+}
+
+static void
+ip6_neighbor_entry_add_adj(ip6_neighbor_t *n, u32 adj_index)
+{
+  int i;
+  vec_foreach_index(i, n->adjacencies)
+    if (vec_elt(n->adjacencies, i) == adj_index)
+      return;
+  vec_add1(n->adjacencies, adj_index);
+}
+
+static void
+ip6_neighbor_add_del_adj_cb (struct ip_lookup_main_t * lm,
+                    u32 adj_index,
+                    ip_adjacency_t * adj,
+                    u32 is_del)
+{
+  ip6_neighbor_main_t * nm = &ip6_neighbor_main;
+  ip6_neighbor_key_t k;
+  ip6_neighbor_t *n = 0;
+  uword * p;
+  u32 ai;
+
+  for(ai = adj->heap_handle; ai < adj->heap_handle + adj->n_adj ; ai++)
+    {
+      adj = ip_get_adjacency (lm, ai);
+      if (adj->lookup_next_index == IP_LOOKUP_NEXT_ARP &&
+          (adj->arp.next_hop.ip6.as_u64[0] || adj->arp.next_hop.ip6.as_u64[1]))
+        {
+          k.sw_if_index = adj->rewrite_header.sw_if_index;
+          k.ip6_address.as_u64[0] = adj->arp.next_hop.ip6.as_u64[0];
+          k.ip6_address.as_u64[1] = adj->arp.next_hop.ip6.as_u64[1];
+          k.pad = 0;
+          p = mhash_get (&nm->neighbor_index_by_key, &k);
+          if (p)
+            n = pool_elt_at_index (nm->neighbor_pool, p[0]);
+        }
+      else
+        continue;
+
+      if (is_del)
+        {
+          if (!n)
+            clib_warning("Adjacency contains unknown ND next hop %U (del)",
+                         format_ip46_address, &adj->arp.next_hop);
+          else
+            ip6_neighbor_entry_del_adj(n, adj->heap_handle);
+        }
+      else /* add */
+        {
+          if (!n)
+            clib_warning("Adjacency contains unknown ND next hop %U (add)",
+                         format_ip46_address, &adj->arp.next_hop);
+          else
+            ip6_neighbor_entry_add_adj(n, adj->heap_handle);
+        }
+    }
+}
+
 static clib_error_t * ip6_neighbor_init (vlib_main_t * vm)
 {
   ip6_neighbor_main_t * nm = &ip6_neighbor_main;
   ip6_main_t * im = &ip6_main;
+  ip_lookup_main_t * lm = &im->lookup_main;
  
   mhash_init (&nm->neighbor_index_by_key,
              /* value size */ sizeof (uword),
@@ -3111,6 +3296,8 @@ static clib_error_t * ip6_neighbor_init (vlib_main_t * vm)
       (im->discover_neighbor_next_index_by_hw_if_index, 32, 0 /* drop */);
 #endif
 
+  ip_register_add_del_adjacency_callback(lm, ip6_neighbor_add_del_adj_cb);
+
   return 0;
 }
 
index 9a52cf7..7fbcab1 100644 (file)
@@ -40,6 +40,9 @@
 #ifndef included_ip6_packet_h
 #define included_ip6_packet_h
 
+#include <vnet/ip/tcp_packet.h>
+#include <vnet/ip/ip4_packet.h>
+
 typedef union {
   u8 as_u8[16];
   u16 as_u16[8];
@@ -55,6 +58,17 @@ typedef CLIB_PACKED (struct {
   u32 fib_index;
 }) ip6_address_fib_t;
 
+typedef CLIB_PACKED (union {
+  struct {
+    u32 pad[3];
+    ip4_address_t ip4;
+  };
+  ip6_address_t ip6;
+}) ip46_address_t;
+#define ip46_address_is_ip4(ip46) (((ip46)->pad[0] | (ip46)->pad[1] | (ip46)->pad[2]) == 0)
+#define ip46_address_mask_ip4(ip46) ((ip46)->pad[0] = (ip46)->pad[1] = (ip46)->pad[2] = 0)
+#define ip46_address_set_ip4(ip46, ip) (ip46_address_mask_ip4(ip46), (ip46)->ip4 = (ip)[0])
+
 always_inline void
 ip6_addr_fib_init (ip6_address_fib_t * addr_fib, ip6_address_t * address,
                   u32 fib_index)
index 9e3cdc0..df14a5f 100644 (file)
@@ -998,8 +998,8 @@ u8 * format_ip_adjacency (u8 * s, va_list * args)
        case IP_LOOKUP_NEXT_ARP:
          if (adj->if_address_index != ~0)
            s = format (s, " %U", format_ip_interface_address, lm, adj->if_address_index);
-         if (adj->arp.next_hop.ip4.as_u32)
-           s = format (s, " via %U", format_ip4_address, &adj->arp.next_hop.ip4.as_u32);
+         if (adj->arp.next_hop.ip6.as_u64[0] || adj->arp.next_hop.ip6.as_u64[1])
+           s = format (s, " via %U", format_ip46_address, &adj->arp.next_hop);
          break;
        case IP_LOOKUP_NEXT_LOCAL:
          if (adj->if_address_index != ~0)
@@ -1091,7 +1091,7 @@ static uword unformat_ip_adjacency (unformat_input_t * input, va_list * args)
 
   if (unformat (input, "arp %U %U",
                unformat_vnet_sw_interface, vnm, &sw_if_index,
-               unformat_ip46_address, &a46, is_ip6))
+               unformat_ip46_address, &a46, is_ip6?IP46_TYPE_IP6:IP46_TYPE_IP4))
     {
       ip_lookup_main_t * lm = is_ip6 ? &ip6_main.lookup_main : &ip4_main.lookup_main;
       ip_adjacency_t * a_adj;
index 23cb02d..62de210 100644 (file)
@@ -43,6 +43,7 @@
 #include <vnet/vnet.h>
 #include <vlib/buffer.h>
 #include <vnet/ip/ip4_packet.h>
+#include <vnet/ip/ip6_packet.h>
 
 /* Next index stored in adjacency. */
 typedef enum {
@@ -168,9 +169,7 @@ typedef struct {
   union {
     /* IP_LOOKUP_NEXT_ARP only */
     struct {
-      union {
-        ip4_address_t ip4;
-      } next_hop;
+      ip46_address_t next_hop;
       u32 next_adj_index_with_same_next_hop;
     } arp;
     /* IP_LOOKUP_NEXT_CLASSIFY only */
index ea76693..829a70f 100644 (file)
@@ -2213,7 +2213,7 @@ vl_api_ip_neighbor_add_del_t_handler (vl_api_ip_neighbor_add_del_t *mp, vlib_mai
             rv = vnet_set_ip6_ethernet_neighbor 
                 (vm, ntohl(mp->sw_if_index),
                  (ip6_address_t *)(mp->dst_address), 
-                 mp->mac_address, sizeof (mp->mac_address));
+                 mp->mac_address, sizeof (mp->mac_address), mp->is_static);
         else
             rv = vnet_unset_ip6_ethernet_neighbor 
                 (vm, ntohl(mp->sw_if_index),