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)
{
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;
}
}
* 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;
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));
}
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))
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
}
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
{
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)
{
* 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;
/* *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* */
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
/* *INDENT-OFF* */
hash_foreach (key, ipni, *hash,
({
- cb (ipni, ctx);
+ if (WALK_STOP == cb (ipni, ctx))
+ break;
}));
/* *INDENT-ON* */
}
/* *INDENT-OFF* */
hash_foreach (key, ipni, hash,
({
- cb (ipni, ctx);
+ if (WALK_STOP == cb (ipni, ctx))
+ break;
}));
/* *INDENT-ON* */
}
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
*/
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
}
else
{
- *wait = ttl;
+ /* here we are sure that ttl <= ipndb_age */
+ *wait = ipndb_age - ttl + 1;
return (IP_NEIGHBOR_AGE_ALIVE);
}
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]);
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) {
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;
}
}