ip-neighbor: Replace feature for the ip-neighbor data-base
[vpp.git] / src / vnet / ip-neighbor / ip_neighbor.c
index 11684eb..5b18473 100644 (file)
@@ -99,6 +99,12 @@ ip_neighbor_get_index (const ip_neighbor_t * ipn)
   return (ipn - ip_neighbor_pool);
 }
 
+static void
+ip_neighbor_touch (ip_neighbor_t * ipn)
+{
+  ipn->ipn_flags &= ~IP_NEIGHBOR_FLAG_STALE;
+}
+
 static bool
 ip_neighbor_is_dynamic (const ip_neighbor_t * ipn)
 {
@@ -145,6 +151,7 @@ ip_neighbor_refresh (ip_neighbor_t * ipn)
    * list is time sorted, newest first */
   ip_neighbor_elt_t *elt, *head;
 
+  ip_neighbor_touch (ipn);
   ipn->ipn_time_last_updated = vlib_time_now (vlib_get_main ());
   ipn->ipn_n_probes = 0;
 
@@ -339,7 +346,7 @@ ip_neighbor_mk_incomplete (adj_index_t ai)
                          ethernet_build_rewrite (vnet_get_main (),
                                                  adj->
                                                  rewrite_header.sw_if_index,
-                                                 adj_get_link_type (ai),
+                                                 VNET_LINK_ARP,
                                                  VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
 }
 
@@ -473,6 +480,8 @@ ip_neighbor_add (const ip46_address_t * ip,
                       format_ip_neighbor_flags, flags, format_mac_address_t,
                       mac);
 
+      ip_neighbor_touch (ipn);
+
       /* Refuse to over-write static neighbor entry. */
       if (!(flags & IP_NEIGHBOR_FLAG_STATIC) &&
          (ipn->ipn_flags & IP_NEIGHBOR_FLAG_STATIC))
@@ -615,6 +624,13 @@ ip_neighbor_update (vnet_main_t * vnm, adj_index_t ai)
           * wouldn't be bad either, but that's more code than i'm prepared to
           * write at this time for relatively little reward.
           */
+         /*
+          * adj_nbr_update_rewrite may actually call fib_walk_sync.
+          * fib_walk_sync may allocate a new adjacency and potentially cause
+          * a realloc for adj_pool. When that happens, adj pointer is no
+          * longer valid here.x We refresh adj pointer accordingly.
+          */
+         adj = adj_get (ai);
          ip_neighbor_probe (adj);
        }
       break;
@@ -976,9 +992,9 @@ ip_neighbor_advertise (vlib_main_t * vm,
   vnet_main_t *vnm = vnet_get_main ();
 
   if (type == IP46_TYPE_IP4 || type == IP46_TYPE_BOTH)
-    ip4_neighbor_advertise (vm, vnm, sw_if_index, &addr->ip4);
+    ip4_neighbor_advertise (vm, vnm, sw_if_index, (addr) ? &addr->ip4 : NULL);
   if (type == IP46_TYPE_IP6 || type == IP46_TYPE_BOTH)
-    ip6_neighbor_advertise (vm, vnm, sw_if_index, &addr->ip6);
+    ip6_neighbor_advertise (vm, vnm, sw_if_index, (addr) ? &addr->ip6 : NULL);
 }
 
 void
@@ -1170,6 +1186,60 @@ ip_neighbor_flush (ip46_type_t type, u32 sw_if_index)
   vec_free (ipnis);
 }
 
+static walk_rc_t
+ip_neighbor_mark_one (index_t ipni, void *ctx)
+{
+  ip_neighbor_t *ipn;
+
+  ipn = ip_neighbor_get (ipni);
+
+  ipn->ipn_flags |= IP_NEIGHBOR_FLAG_STALE;
+
+  return (WALK_CONTINUE);
+}
+
+void
+ip_neighbor_mark (ip46_type_t type)
+{
+  ip_neighbor_walk (type, ~0, ip_neighbor_mark_one, NULL);
+}
+
+typedef struct ip_neighbor_sweep_ctx_t_
+{
+  index_t *ipnsc_stale;
+} ip_neighbor_sweep_ctx_t;
+
+static walk_rc_t
+ip_neighbor_sweep_one (index_t ipni, void *arg)
+{
+  ip_neighbor_sweep_ctx_t *ctx = arg;
+  ip_neighbor_t *ipn;
+
+  ipn = ip_neighbor_get (ipni);
+
+  if (ipn->ipn_flags & IP_NEIGHBOR_FLAG_STALE)
+    {
+      vec_add1 (ctx->ipnsc_stale, ipni);
+    }
+
+  return (WALK_CONTINUE);
+}
+
+void
+ip_neighbor_sweep (ip46_type_t type)
+{
+  ip_neighbor_sweep_ctx_t ctx = { };
+  index_t *ipni;
+
+  ip_neighbor_walk (type, ~0, ip_neighbor_sweep_one, &ctx);
+
+  vec_foreach (ipni, ctx.ipnsc_stale)
+  {
+    ip_neighbor_free (ip_neighbor_get (*ipni));
+  }
+  vec_free (ctx.ipnsc_stale);
+}
+
 /*
  * Remove any arp entries associated with the specified interface
  */
@@ -1395,22 +1465,24 @@ static ip_neighbor_age_state_t
 ip_neighbour_age_out (index_t ipni, f64 now, f64 * wait)
 {
   ip_neighbor_t *ipn;
-  f64 ttl;
+  u32 ipndb_age;
+  u32 ttl;
 
   ipn = ip_neighbor_get (ipni);
+  ipndb_age = ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_age;
   ttl = now - ipn->ipn_time_last_updated;
-  *wait = IP_NEIGHBOR_PROCESS_SLEEP_LONG;
+  *wait = ipndb_age;
 
-  if (ttl > ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_age)
+  if (ttl > ipndb_age)
     {
       IP_NEIGHBOR_DBG ("aged: %U @%f - %f > %d",
                       format_ip_neighbor, ipni, now,
-                      ipn->ipn_time_last_updated,
-                      ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_age);
+                      ipn->ipn_time_last_updated, ipndb_age);
       if (ipn->ipn_n_probes > 2)
        {
          /* 3 strikes and yea-re out */
          IP_NEIGHBOR_DBG ("dead: %U", format_ip_neighbor, ipni);
+         *wait = 1;
          return (IP_NEIGHBOR_AGE_DEAD);
        }
       else
@@ -1429,7 +1501,8 @@ ip_neighbour_age_out (index_t ipni, f64 now, f64 * wait)
     }
   else
     {
-      *wait = ttl;
+      /* here we are sure that ttl <= ipndb_age */
+      *wait = ipndb_age - ttl + 1;
       return (IP_NEIGHBOR_AGE_ALIVE);
     }
 
@@ -1474,7 +1547,7 @@ ip_neighbor_age_loop (vlib_main_t * vm,
            ip_neighbor_elt_t *elt, *head;
            f64 wait;
 
-           timeout = 1e5;
+           timeout = ip_neighbor_db[type].ipndb_age;
            head = pool_elt_at_index (ip_neighbor_elt_pool,
                                      ip_neighbor_list_head[type]);
 
@@ -1491,6 +1564,7 @@ ip_neighbor_age_loop (vlib_main_t * vm,
 
             if (IP_NEIGHBOR_AGE_ALIVE == res) {
               /* the oldest neighbor has not yet expired, go back to sleep */
+              timeout = clib_min (wait, timeout);
               break;
             }
             else if (IP_NEIGHBOR_AGE_DEAD == res) {
@@ -1518,13 +1592,16 @@ ip_neighbor_age_loop (vlib_main_t * vm,
 
            head = pool_elt_at_index (ip_neighbor_elt_pool,
                                      ip_neighbor_list_head[type]);
-           elt = clib_llist_prev (ip_neighbor_elt_pool, ipne_anchor, head);
+           /* no neighbors yet */
+           if (clib_llist_is_empty (ip_neighbor_elt_pool, ipne_anchor, head))
+             {
+               timeout = ip_neighbor_db[type].ipndb_age;
+               break;
+             }
 
            /* poke the oldset neighbour for aging, which returns how long we sleep for */
-           if (IP_NEIGHBOR_AGE_PROBE ==
-               ip_neighbour_age_out (elt->ipne_index, now, &timeout))
-             /* we probed for the oldest entry, sleep for a short time to get to the next */
-             timeout = 0.01;
+           elt = clib_llist_prev (ip_neighbor_elt_pool, ipne_anchor, head);
+           ip_neighbour_age_out (elt->ipne_index, now, &timeout);
            break;
          }
        }