ip-neighbor: Allow to replace dynamic entry
[vpp.git] / src / vnet / ip-neighbor / ip_neighbor.c
index d8c386d..4111b02 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)
 {
@@ -135,6 +141,8 @@ ip_neighbor_list_remove (ip_neighbor_t * ipn)
       elt = pool_elt_at_index (ip_neighbor_elt_pool, ipn->ipn_elt);
 
       clib_llist_remove (ip_neighbor_elt_pool, ipne_anchor, elt);
+
+      ipn->ipn_elt = ~0;
     }
 }
 
@@ -145,6 +153,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;
 
@@ -473,6 +482,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))
@@ -483,6 +494,17 @@ ip_neighbor_add (const ip46_address_t * ip,
          return -2;
        }
 
+      /* A dynamic entry can become static, but not vice-versa.
+       * i.e. since if it was programmed by the CP then it must
+       * be removed by the CP */
+      if ((flags & IP_NEIGHBOR_FLAG_STATIC) &&
+         !(ipn->ipn_flags & IP_NEIGHBOR_FLAG_STATIC))
+       {
+         ip_neighbor_list_remove (ipn);
+         ipn->ipn_flags |= IP_NEIGHBOR_FLAG_STATIC;
+         ipn->ipn_flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC;
+       }
+
       /*
        * prevent a DoS attack from the data-plane that
        * spams us with no-op updates to the MAC address
@@ -494,17 +516,6 @@ ip_neighbor_add (const ip46_address_t * ip,
        }
 
       mac_address_copy (&ipn->ipn_mac, mac);
-
-      /* A dynamic entry can become static, but not vice-versa.
-       * i.e. since if it was programmed by the CP then it must
-       * be removed by the CP */
-      if ((flags & IP_NEIGHBOR_FLAG_STATIC) &&
-         !(ipn->ipn_flags & IP_NEIGHBOR_FLAG_STATIC))
-       {
-         ip_neighbor_list_remove (ipn);
-         ipn->ipn_flags |= IP_NEIGHBOR_FLAG_STATIC;
-         ipn->ipn_flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC;
-       }
     }
   else
     {
@@ -567,6 +578,40 @@ ip_neighbor_del (const ip46_address_t * ip, ip46_type_t type, u32 sw_if_index)
   return (0);
 }
 
+typedef struct ip_neighbor_del_all_ctx_t_
+{
+  index_t *ipn_del;
+} ip_neighbor_del_all_ctx_t;
+
+static walk_rc_t
+ip_neighbor_del_all_walk_cb (index_t ipni, void *arg)
+{
+  ip_neighbor_del_all_ctx_t *ctx = arg;
+
+  vec_add1 (ctx->ipn_del, ipni);
+
+  return (WALK_CONTINUE);
+}
+
+void
+ip_neighbor_del_all (ip46_type_t type, u32 sw_if_index)
+{
+  IP_NEIGHBOR_INFO ("delete-all: %U, %U",
+                   format_ip46_type, type,
+                   format_vnet_sw_if_index_name, vnet_get_main (),
+                   sw_if_index);
+
+  ip_neighbor_del_all_ctx_t ctx = {
+    .ipn_del = NULL,
+  };
+  index_t *ipni;
+
+  ip_neighbor_walk (type, sw_if_index, ip_neighbor_del_all_walk_cb, &ctx);
+
+  vec_foreach (ipni, ctx.ipn_del) ip_neighbor_free (ip_neighbor_get (*ipni));
+  vec_free (ctx.ipn_del);
+}
+
 void
 ip_neighbor_update (vnet_main_t * vnm, adj_index_t ai)
 {
@@ -615,6 +660,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;
@@ -771,12 +823,11 @@ ip_neighbor_entries (u32 sw_if_index, ip46_type_t type)
   /* *INDENT-OFF* */
   pool_foreach (ipn, ip_neighbor_pool,
   ({
-    if (sw_if_index != ~0 &&
-        ipn->ipn_key->ipnk_sw_if_index != sw_if_index &&
+    if ((sw_if_index == ~0 ||
+        ipn->ipn_key->ipnk_sw_if_index == sw_if_index) &&
         (IP46_TYPE_ANY == type ||
-         (ipn->ipn_key->ipnk_type == type)))
-      continue;
-    vec_add1 (ipnis, ip_neighbor_get_index(ipn));
+         ipn->ipn_key->ipnk_type == type))
+       vec_add1 (ipnis, ip_neighbor_get_index(ipn));
   }));
 
   /* *INDENT-ON* */
@@ -997,7 +1048,8 @@ ip_neighbor_walk (ip46_type_t type,
           /* *INDENT-OFF* */
           hash_foreach (key, ipni, *hash,
           ({
-            cb (ipni, ctx);
+            if (WALK_STOP == cb (ipni, ctx))
+             break;
           }));
           /* *INDENT-ON* */
       }
@@ -1013,7 +1065,8 @@ ip_neighbor_walk (ip46_type_t type,
       /* *INDENT-OFF* */
       hash_foreach (key, ipni, hash,
       ({
-        cb (ipni, ctx);
+        if (WALK_STOP == cb (ipni, ctx))
+         break;
       }));
       /* *INDENT-ON* */
     }
@@ -1170,6 +1223,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 +1502,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 +1538,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 +1584,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 +1601,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 +1629,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;
          }
        }