2 * src/vnet/ip/ip_neighboor.c: ip neighbor generic handling
4 * Copyright (c) 2018 Cisco and/or its affiliates.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 #include <vppinfra/llist.h>
20 #include <vnet/ip-neighbor/ip_neighbor.h>
21 #include <vnet/ip-neighbor/ip4_neighbor.h>
22 #include <vnet/ip-neighbor/ip6_neighbor.h>
23 #include <vnet/ip-neighbor/ip_neighbor_watch.h>
25 #include <vnet/ip/ip6_ll_table.h>
26 #include <vnet/fib/fib_table.h>
27 #include <vnet/adj/adj_mcast.h>
29 /** Pool for All IP neighbors */
30 static ip_neighbor_t *ip_neighbor_pool;
32 /** protocol specific lists of time sorted neighbors */
33 index_t ip_neighbor_list_head[IP46_N_TYPES];
35 typedef struct ip_neighbor_elt_t_
37 clib_llist_anchor_t ipne_anchor;
41 /** Pool of linked list elemeents */
42 ip_neighbor_elt_t *ip_neighbor_elt_pool;
44 typedef struct ip_neighbor_db_t_
46 /** per interface hash */
48 /** per-protocol limit - max number of neighbors*/
50 /** max age of a neighbor before it's forcibly evicted */
52 /** when the limit is reached and new neighbors are created, should
53 * we recycle an old one */
55 /** per-protocol number of elements */
57 /** per-protocol number of elements per-fib-index*/
58 u32 *ipndb_n_elts_per_fib;
61 static vlib_log_class_t ipn_logger;
63 /* DBs of neighbours one per AF */
65 static ip_neighbor_db_t ip_neighbor_db[IP46_N_TYPES] = {
68 /* Default to not aging and not recycling */
70 .ipndb_recycle = false,
74 /* Default to not aging and not recycling */
76 .ipndb_recycle = false,
81 #define IP_NEIGHBOR_DBG(...) \
82 vlib_log_debug (ipn_logger, __VA_ARGS__);
84 #define IP_NEIGHBOR_INFO(...) \
85 vlib_log_notice (ipn_logger, __VA_ARGS__);
88 ip_neighbor_get (index_t ipni)
90 if (pool_is_free_index (ip_neighbor_pool, ipni))
93 return (pool_elt_at_index (ip_neighbor_pool, ipni));
97 ip_neighbor_get_index (const ip_neighbor_t * ipn)
99 return (ipn - ip_neighbor_pool);
103 ip_neighbor_touch (ip_neighbor_t * ipn)
105 ipn->ipn_flags &= ~IP_NEIGHBOR_FLAG_STALE;
109 ip_neighbor_is_dynamic (const ip_neighbor_t * ipn)
111 return (ipn->ipn_flags & IP_NEIGHBOR_FLAG_DYNAMIC);
114 const ip46_address_t *
115 ip_neighbor_get_ip (const ip_neighbor_t * ipn)
117 return (&ipn->ipn_key->ipnk_ip);
120 const mac_address_t *
121 ip_neighbor_get_mac (const ip_neighbor_t * ipn)
123 return (&ipn->ipn_mac);
127 ip_neighbor_get_sw_if_index (const ip_neighbor_t * ipn)
129 return (ipn->ipn_key->ipnk_sw_if_index);
133 ip_neighbor_list_remove (ip_neighbor_t * ipn)
135 /* new neighbours, are added to the head of the list, since the
136 * list is time sorted, newest first */
137 ip_neighbor_elt_t *elt;
139 if (~0 != ipn->ipn_elt)
141 elt = pool_elt_at_index (ip_neighbor_elt_pool, ipn->ipn_elt);
143 clib_llist_remove (ip_neighbor_elt_pool, ipne_anchor, elt);
150 ip_neighbor_refresh (ip_neighbor_t * ipn)
152 /* new neighbours, are added to the head of the list, since the
153 * list is time sorted, newest first */
154 ip_neighbor_elt_t *elt, *head;
156 ip_neighbor_touch (ipn);
157 ipn->ipn_time_last_updated = vlib_time_now (vlib_get_main ());
158 ipn->ipn_n_probes = 0;
160 if (ip_neighbor_is_dynamic (ipn))
162 if (~0 == ipn->ipn_elt)
163 /* first time insertion */
164 pool_get_zero (ip_neighbor_elt_pool, elt);
167 /* already inserted - extract first */
168 elt = pool_elt_at_index (ip_neighbor_elt_pool, ipn->ipn_elt);
170 clib_llist_remove (ip_neighbor_elt_pool, ipne_anchor, elt);
172 head = pool_elt_at_index (ip_neighbor_elt_pool,
173 ip_neighbor_list_head[ipn->
174 ipn_key->ipnk_type]);
176 elt->ipne_index = ip_neighbor_get_index (ipn);
177 clib_llist_add (ip_neighbor_elt_pool, ipne_anchor, elt, head);
178 ipn->ipn_elt = elt - ip_neighbor_elt_pool;
183 ip_neighbor_db_add (const ip_neighbor_t * ipn)
185 vec_validate (ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_hash,
186 ipn->ipn_key->ipnk_sw_if_index);
188 if (!ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_hash
189 [ipn->ipn_key->ipnk_sw_if_index])
190 ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_hash[ipn->
191 ipn_key->ipnk_sw_if_index]
192 = hash_create_mem (0, sizeof (ip_neighbor_key_t), sizeof (index_t));
194 hash_set_mem (ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_hash
195 [ipn->ipn_key->ipnk_sw_if_index], ipn->ipn_key,
196 ip_neighbor_get_index (ipn));
198 ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_n_elts++;
202 ip_neighbor_db_remove (const ip_neighbor_key_t * key)
204 vec_validate (ip_neighbor_db[key->ipnk_type].ipndb_hash,
205 key->ipnk_sw_if_index);
207 hash_unset_mem (ip_neighbor_db[key->ipnk_type].ipndb_hash
208 [key->ipnk_sw_if_index], key);
210 ip_neighbor_db[key->ipnk_type].ipndb_n_elts--;
213 static ip_neighbor_t *
214 ip_neighbor_db_find (const ip_neighbor_key_t * key)
218 if (key->ipnk_sw_if_index >=
219 vec_len (ip_neighbor_db[key->ipnk_type].ipndb_hash))
223 hash_get_mem (ip_neighbor_db[key->ipnk_type].ipndb_hash
224 [key->ipnk_sw_if_index], key);
227 return ip_neighbor_get (p[0]);
233 ip46_type_pfx_len (ip46_type_t type)
235 return (type == IP46_TYPE_IP4 ? 32 : 128);
239 ip_neighbor_adj_fib_add (ip_neighbor_t * ipn, u32 fib_index)
241 if (ipn->ipn_key->ipnk_type == IP46_TYPE_IP6 &&
242 ip6_address_is_link_local_unicast (&ipn->ipn_key->ipnk_ip.ip6))
244 ip6_ll_prefix_t pfx = {
245 .ilp_addr = ipn->ipn_key->ipnk_ip.ip6,
246 .ilp_sw_if_index = ipn->ipn_key->ipnk_sw_if_index,
248 ipn->ipn_fib_entry_index =
249 ip6_ll_table_entry_update (&pfx, FIB_ROUTE_PATH_FLAG_NONE);
253 fib_protocol_t fproto;
255 fproto = fib_proto_from_ip46 (ipn->ipn_key->ipnk_type);
258 .fp_len = ip46_type_pfx_len (ipn->ipn_key->ipnk_type),
260 .fp_addr = ipn->ipn_key->ipnk_ip,
263 ipn->ipn_fib_entry_index =
264 fib_table_entry_path_add (fib_index, &pfx, FIB_SOURCE_ADJ,
265 FIB_ENTRY_FLAG_ATTACHED,
266 fib_proto_to_dpo (fproto),
268 ipn->ipn_key->ipnk_sw_if_index,
269 ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
271 vec_validate (ip_neighbor_db
272 [ipn->ipn_key->ipnk_type].ipndb_n_elts_per_fib,
275 ip_neighbor_db[ipn->ipn_key->
276 ipnk_type].ipndb_n_elts_per_fib[fib_index]++;
279 ip_neighbor_db[ipn->ipn_key->
280 ipnk_type].ipndb_n_elts_per_fib[fib_index])
281 fib_table_lock (fib_index, fproto, FIB_SOURCE_ADJ);
286 ip_neighbor_adj_fib_remove (ip_neighbor_t * ipn, u32 fib_index)
288 if (FIB_NODE_INDEX_INVALID != ipn->ipn_fib_entry_index)
290 if (ipn->ipn_key->ipnk_type == IP46_TYPE_IP6 &&
291 ip6_address_is_link_local_unicast (&ipn->ipn_key->ipnk_ip.ip6))
293 ip6_ll_prefix_t pfx = {
294 .ilp_addr = ipn->ipn_key->ipnk_ip.ip6,
295 .ilp_sw_if_index = ipn->ipn_key->ipnk_sw_if_index,
297 ip6_ll_table_entry_delete (&pfx);
301 fib_protocol_t fproto;
303 fproto = fib_proto_from_ip46 (ipn->ipn_key->ipnk_type);
306 .fp_len = ip46_type_pfx_len (ipn->ipn_key->ipnk_type),
308 .fp_addr = ipn->ipn_key->ipnk_ip,
311 fib_table_entry_path_remove (fib_index,
314 fib_proto_to_dpo (fproto),
316 ipn->ipn_key->ipnk_sw_if_index,
317 ~0, 1, FIB_ROUTE_PATH_FLAG_NONE);
319 ip_neighbor_db[ipn->ipn_key->
320 ipnk_type].ipndb_n_elts_per_fib[fib_index]--;
323 ip_neighbor_db[ipn->ipn_key->
324 ipnk_type].ipndb_n_elts_per_fib[fib_index])
325 fib_table_unlock (fib_index, fproto, FIB_SOURCE_ADJ);
331 ip_neighbor_mk_complete (adj_index_t ai, ip_neighbor_t * ipn)
333 adj_nbr_update_rewrite (ai, ADJ_NBR_REWRITE_FLAG_COMPLETE,
334 ethernet_build_rewrite (vnet_get_main (),
336 ipn_key->ipnk_sw_if_index,
337 adj_get_link_type (ai),
338 ipn->ipn_mac.bytes));
342 ip_neighbor_mk_incomplete (adj_index_t ai)
344 ip_adjacency_t *adj = adj_get (ai);
346 adj_nbr_update_rewrite (ai,
347 ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
348 ethernet_build_rewrite (vnet_get_main (),
350 rewrite_header.sw_if_index,
352 VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
356 ip_neighbor_mk_complete_walk (adj_index_t ai, void *ctx)
358 ip_neighbor_t *ipn = ctx;
360 ip_neighbor_mk_complete (ai, ipn);
362 return (ADJ_WALK_RC_CONTINUE);
366 ip_neighbor_mk_incomplete_walk (adj_index_t ai, void *ctx)
368 ip_neighbor_mk_incomplete (ai);
370 return (ADJ_WALK_RC_CONTINUE);
374 ip_neighbor_destroy (ip_neighbor_t * ipn)
376 IP_NEIGHBOR_DBG ("free: %U", format_ip_neighbor,
377 ip_neighbor_get_index (ipn));
379 ip_neighbor_publish (ip_neighbor_get_index (ipn),
380 IP_NEIGHBOR_EVENT_REMOVED);
382 adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
383 fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
384 &ipn->ipn_key->ipnk_ip,
385 ip_neighbor_mk_incomplete_walk, ipn);
386 ip_neighbor_adj_fib_remove
388 fib_table_get_index_for_sw_if_index
389 (fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
390 ipn->ipn_key->ipnk_sw_if_index));
392 ip_neighbor_list_remove (ipn);
393 ip_neighbor_db_remove (ipn->ipn_key);
394 clib_mem_free (ipn->ipn_key);
396 pool_put (ip_neighbor_pool, ipn);
400 ip_neighbor_force_reuse (ip46_type_t type)
402 if (!ip_neighbor_db[type].ipndb_recycle)
405 /* pluck the oldest entry, which is the one from the end of the list */
406 ip_neighbor_elt_t *elt, *head;
409 pool_elt_at_index (ip_neighbor_elt_pool, ip_neighbor_list_head[type]);
411 if (clib_llist_is_empty (ip_neighbor_elt_pool, ipne_anchor, head))
414 elt = clib_llist_prev (ip_neighbor_elt_pool, ipne_anchor, head);
415 ip_neighbor_destroy (ip_neighbor_get (elt->ipne_index));
420 static ip_neighbor_t *
421 ip_neighbor_alloc (const ip_neighbor_key_t * key,
422 const mac_address_t * mac, ip_neighbor_flags_t flags)
426 if (ip_neighbor_db[key->ipnk_type].ipndb_limit &&
427 (ip_neighbor_db[key->ipnk_type].ipndb_n_elts >=
428 ip_neighbor_db[key->ipnk_type].ipndb_limit))
430 if (!ip_neighbor_force_reuse (key->ipnk_type))
434 pool_get_zero (ip_neighbor_pool, ipn);
436 ipn->ipn_key = clib_mem_alloc (sizeof (*ipn->ipn_key));
437 clib_memcpy (ipn->ipn_key, key, sizeof (*ipn->ipn_key));
439 ipn->ipn_fib_entry_index = FIB_NODE_INDEX_INVALID;
440 ipn->ipn_flags = flags;
443 mac_address_copy (&ipn->ipn_mac, mac);
445 ip_neighbor_db_add (ipn);
447 /* create the adj-fib. the entry in the FIB table for the peer's interface */
448 if (!(ipn->ipn_flags & IP_NEIGHBOR_FLAG_NO_FIB_ENTRY))
449 ip_neighbor_adj_fib_add
450 (ipn, fib_table_get_index_for_sw_if_index
451 (fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
452 ipn->ipn_key->ipnk_sw_if_index));
458 ip_neighbor_add (const ip46_address_t * ip,
460 const mac_address_t * mac,
462 ip_neighbor_flags_t flags, u32 * stats_index)
464 fib_protocol_t fproto;
467 /* main thread only */
468 ASSERT (0 == vlib_get_thread_index ());
470 fproto = fib_proto_from_ip46 (type);
472 const ip_neighbor_key_t key = {
474 .ipnk_sw_if_index = sw_if_index,
478 ipn = ip_neighbor_db_find (&key);
482 IP_NEIGHBOR_DBG ("update: %U, %U",
483 format_vnet_sw_if_index_name, vnet_get_main (),
484 sw_if_index, format_ip46_address, ip, type,
485 format_ip_neighbor_flags, flags, format_mac_address_t,
488 ip_neighbor_touch (ipn);
490 /* Refuse to over-write static neighbor entry. */
491 if (!(flags & IP_NEIGHBOR_FLAG_STATIC) &&
492 (ipn->ipn_flags & IP_NEIGHBOR_FLAG_STATIC))
494 /* if MAC address match, still check to send event */
495 if (0 == mac_address_cmp (&ipn->ipn_mac, mac))
496 goto check_customers;
500 /* A dynamic entry can become static, but not vice-versa.
501 * i.e. since if it was programmed by the CP then it must
502 * be removed by the CP */
503 if ((flags & IP_NEIGHBOR_FLAG_STATIC) &&
504 !(ipn->ipn_flags & IP_NEIGHBOR_FLAG_STATIC))
506 ip_neighbor_list_remove (ipn);
507 ipn->ipn_flags |= IP_NEIGHBOR_FLAG_STATIC;
508 ipn->ipn_flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC;
512 * prevent a DoS attack from the data-plane that
513 * spams us with no-op updates to the MAC address
515 if (0 == mac_address_cmp (&ipn->ipn_mac, mac))
517 ip_neighbor_refresh (ipn);
518 goto check_customers;
521 mac_address_copy (&ipn->ipn_mac, mac);
525 IP_NEIGHBOR_INFO ("add: %U, %U",
526 format_vnet_sw_if_index_name, vnet_get_main (),
527 sw_if_index, format_ip46_address, ip, type,
528 format_ip_neighbor_flags, flags, format_mac_address_t,
531 ipn = ip_neighbor_alloc (&key, mac, flags);
534 return VNET_API_ERROR_LIMIT_EXCEEDED;
537 /* Update time stamp and flags. */
538 ip_neighbor_refresh (ipn);
540 adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
541 fproto, &ipn->ipn_key->ipnk_ip,
542 ip_neighbor_mk_complete_walk, ipn);
545 /* Customer(s) requesting event for this address? */
546 ip_neighbor_publish (ip_neighbor_get_index (ipn), IP_NEIGHBOR_EVENT_ADDED);
549 *stats_index = adj_nbr_find (fproto,
550 fib_proto_to_link (fproto),
551 &ipn->ipn_key->ipnk_ip,
552 ipn->ipn_key->ipnk_sw_if_index);
557 ip_neighbor_del (const ip46_address_t * ip, ip46_type_t type, u32 sw_if_index)
561 /* main thread only */
562 ASSERT (0 == vlib_get_thread_index ());
564 IP_NEIGHBOR_INFO ("delete: %U, %U",
565 format_vnet_sw_if_index_name, vnet_get_main (),
566 sw_if_index, format_ip46_address, ip, type);
568 const ip_neighbor_key_t key = {
570 .ipnk_sw_if_index = sw_if_index,
574 ipn = ip_neighbor_db_find (&key);
577 return (VNET_API_ERROR_NO_SUCH_ENTRY);
579 ip_neighbor_destroy (ipn);
584 typedef struct ip_neighbor_del_all_ctx_t_
587 } ip_neighbor_del_all_ctx_t;
590 ip_neighbor_del_all_walk_cb (index_t ipni, void *arg)
592 ip_neighbor_del_all_ctx_t *ctx = arg;
594 vec_add1 (ctx->ipn_del, ipni);
596 return (WALK_CONTINUE);
600 ip_neighbor_del_all (ip46_type_t type, u32 sw_if_index)
602 IP_NEIGHBOR_INFO ("delete-all: %U, %U",
603 format_ip46_type, type,
604 format_vnet_sw_if_index_name, vnet_get_main (),
607 ip_neighbor_del_all_ctx_t ctx = {
612 ip_neighbor_walk (type, sw_if_index, ip_neighbor_del_all_walk_cb, &ctx);
615 ctx.ipn_del) ip_neighbor_destroy (ip_neighbor_get (*ipni));
616 vec_free (ctx.ipn_del);
620 ip_neighbor_update (vnet_main_t * vnm, adj_index_t ai)
627 ip_neighbor_key_t key = {
628 .ipnk_ip = adj->sub_type.nbr.next_hop,
629 .ipnk_type = fib_proto_to_ip46 (adj->ia_nh_proto),
630 .ipnk_sw_if_index = adj->rewrite_header.sw_if_index,
632 ipn = ip_neighbor_db_find (&key);
634 switch (adj->lookup_next_index)
636 case IP_LOOKUP_NEXT_ARP:
639 adj_nbr_walk_nh (adj->rewrite_header.sw_if_index,
641 &ipn->ipn_key->ipnk_ip,
642 ip_neighbor_mk_complete_walk, ipn);
647 * no matching ARP entry.
648 * construct the rewrite required to for an ARP packet, and stick
649 * that in the adj's pipe to smoke.
651 adj_nbr_update_rewrite
653 ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
654 ethernet_build_rewrite
656 adj->rewrite_header.sw_if_index,
658 VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
661 * since the FIB has added this adj for a route, it makes sense it
662 * may want to forward traffic sometime soon. Let's send a
663 * speculative ARP. just one. If we were to do periodically that
664 * wouldn't be bad either, but that's more code than i'm prepared to
665 * write at this time for relatively little reward.
668 * adj_nbr_update_rewrite may actually call fib_walk_sync.
669 * fib_walk_sync may allocate a new adjacency and potentially cause
670 * a realloc for adj_pool. When that happens, adj pointer is no
671 * longer valid here.x We refresh adj pointer accordingly.
674 ip_neighbor_probe (adj);
677 case IP_LOOKUP_NEXT_GLEAN:
678 case IP_LOOKUP_NEXT_BCAST:
679 case IP_LOOKUP_NEXT_MCAST:
680 case IP_LOOKUP_NEXT_DROP:
681 case IP_LOOKUP_NEXT_PUNT:
682 case IP_LOOKUP_NEXT_LOCAL:
683 case IP_LOOKUP_NEXT_REWRITE:
684 case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
685 case IP_LOOKUP_NEXT_MIDCHAIN:
686 case IP_LOOKUP_NEXT_ICMP_ERROR:
687 case IP_LOOKUP_N_NEXT:
694 ip_neighbor_learn (const ip_neighbor_learn_t * l)
696 ip_neighbor_add (&l->ip, l->type, &l->mac, l->sw_if_index,
697 IP_NEIGHBOR_FLAG_DYNAMIC, NULL);
700 static clib_error_t *
701 ip_neighbor_cmd (vlib_main_t * vm,
702 unformat_input_t * input, vlib_cli_command_t * cmd)
704 ip46_address_t ip = ip46_address_initializer;
705 mac_address_t mac = ZERO_MAC_ADDRESS;
706 vnet_main_t *vnm = vnet_get_main ();
707 ip_neighbor_flags_t flags;
708 u32 sw_if_index = ~0;
712 flags = IP_NEIGHBOR_FLAG_DYNAMIC;
714 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
716 /* set ip arp TenGigE1/1/0/1 1.2.3.4 aa:bb:... or aabb.ccdd... */
717 if (unformat (input, "%U %U %U",
718 unformat_vnet_sw_interface, vnm, &sw_if_index,
719 unformat_ip46_address, &ip, IP46_TYPE_ANY,
720 unformat_mac_address_t, &mac))
722 else if (unformat (input, "delete") || unformat (input, "del"))
724 else if (unformat (input, "static"))
726 flags |= IP_NEIGHBOR_FLAG_STATIC;
727 flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC;
729 else if (unformat (input, "no-fib-entry"))
730 flags |= IP_NEIGHBOR_FLAG_NO_FIB_ENTRY;
731 else if (unformat (input, "count %d", &count))
737 if (sw_if_index == ~0 ||
738 ip46_address_is_zero (&ip) || mac_address_is_zero (&mac))
739 return clib_error_return (0,
740 "specify interface, IP address and MAC: `%U'",
741 format_unformat_error, input);
746 ip_neighbor_add (&ip, ip46_address_get_type (&ip), &mac, sw_if_index,
749 ip_neighbor_del (&ip, ip46_address_get_type (&ip), sw_if_index);
751 ip46_address_increment (ip46_address_get_type (&ip), &ip);
752 mac_address_increment (&mac);
762 * Add or delete IPv4 ARP cache entries.
764 * @note 'set ip neighbor' options (e.g. delete, static, 'fib-id <id>',
765 * 'count <number>', 'interface ip4_addr mac_addr') can be added in
766 * any order and combination.
770 * Add or delete IPv4 ARP cache entries as follows. MAC Address can be in
771 * either aa:bb:cc:dd:ee:ff format or aabb.ccdd.eeff format.
772 * @cliexcmd{set ip neighbor GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
773 * @cliexcmd{set ip neighbor delete GigabitEthernet2/0/0 6.0.0.3 de:ad:be:ef:ba:be}
775 * To add or delete an IPv4 ARP cache entry to or from a specific fib
777 * @cliexcmd{set ip neighbor fib-id 1 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
778 * @cliexcmd{set ip neighbor fib-id 1 delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
780 * Add or delete IPv4 static ARP cache entries as follows:
781 * @cliexcmd{set ip neighbor static GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
782 * @cliexcmd{set ip neighbor static delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
784 * For testing / debugging purposes, the 'set ip neighbor' command can add or
785 * delete multiple entries. Supply the 'count N' parameter:
786 * @cliexcmd{set ip neighbor count 10 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
789 VLIB_CLI_COMMAND (ip_neighbor_command, static) = {
790 .path = "set ip neighbor",
792 "set ip neighbor [del] <intfc> <ip-address> <mac-address> [static] [no-fib-entry] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
793 .function = ip_neighbor_cmd,
795 VLIB_CLI_COMMAND (ip_neighbor_command2, static) = {
796 .path = "ip neighbor",
798 "ip neighbor [del] <intfc> <ip-address> <mac-address> [static] [no-fib-entry] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
799 .function = ip_neighbor_cmd,
804 ip_neighbor_sort (void *a1, void *a2)
806 index_t *ipni1 = a1, *ipni2 = a2;
807 ip_neighbor_t *ipn1, *ipn2;
810 ipn1 = ip_neighbor_get (*ipni1);
811 ipn2 = ip_neighbor_get (*ipni2);
813 cmp = vnet_sw_interface_compare (vnet_get_main (),
814 ipn1->ipn_key->ipnk_sw_if_index,
815 ipn2->ipn_key->ipnk_sw_if_index);
817 cmp = ip46_address_cmp (&ipn1->ipn_key->ipnk_ip, &ipn2->ipn_key->ipnk_ip);
822 ip_neighbor_entries (u32 sw_if_index, ip46_type_t type)
824 index_t *ipnis = NULL;
828 pool_foreach (ipn, ip_neighbor_pool,
830 if ((sw_if_index == ~0 ||
831 ipn->ipn_key->ipnk_sw_if_index == sw_if_index) &&
832 (IP46_TYPE_ANY == type ||
833 ipn->ipn_key->ipnk_type == type))
834 vec_add1 (ipnis, ip_neighbor_get_index(ipn));
840 vec_sort_with_function (ipnis, ip_neighbor_sort);
844 static clib_error_t *
845 ip_neighbor_show_sorted_i (vlib_main_t * vm,
846 unformat_input_t * input,
847 vlib_cli_command_t * cmd, ip46_type_t type)
849 ip_neighbor_elt_t *elt, *head;
851 head = pool_elt_at_index (ip_neighbor_elt_pool,
852 ip_neighbor_list_head[type]);
855 vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP",
856 "Flags", "Ethernet", "Interface");
859 /* the list is time sorted, newest first, so start from the back
860 * and work forwards. Stop when we get to one that is alive */
861 clib_llist_foreach_reverse(ip_neighbor_elt_pool,
862 ipne_anchor, head, elt,
864 vlib_cli_output (vm, "%U", format_ip_neighbor, elt->ipne_index);
871 static clib_error_t *
872 ip_neighbor_show_i (vlib_main_t * vm,
873 unformat_input_t * input,
874 vlib_cli_command_t * cmd, ip46_type_t type)
876 index_t *ipni, *ipnis = NULL;
879 /* Filter entries by interface if given. */
881 (void) unformat_user (input, unformat_vnet_sw_interface, vnet_get_main (),
884 ipnis = ip_neighbor_entries (sw_if_index, type);
887 vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP",
888 "Flags", "Ethernet", "Interface");
890 vec_foreach (ipni, ipnis)
892 vlib_cli_output (vm, "%U", format_ip_neighbor, *ipni);
899 static clib_error_t *
900 ip_neighbor_show (vlib_main_t * vm,
901 unformat_input_t * input, vlib_cli_command_t * cmd)
903 return (ip_neighbor_show_i (vm, input, cmd, IP46_TYPE_ANY));
906 static clib_error_t *
907 ip6_neighbor_show (vlib_main_t * vm,
908 unformat_input_t * input, vlib_cli_command_t * cmd)
910 return (ip_neighbor_show_i (vm, input, cmd, IP46_TYPE_IP6));
913 static clib_error_t *
914 ip4_neighbor_show (vlib_main_t * vm,
915 unformat_input_t * input, vlib_cli_command_t * cmd)
917 return (ip_neighbor_show_i (vm, input, cmd, IP46_TYPE_IP4));
920 static clib_error_t *
921 ip6_neighbor_show_sorted (vlib_main_t * vm,
922 unformat_input_t * input, vlib_cli_command_t * cmd)
924 return (ip_neighbor_show_sorted_i (vm, input, cmd, IP46_TYPE_IP6));
927 static clib_error_t *
928 ip4_neighbor_show_sorted (vlib_main_t * vm,
929 unformat_input_t * input, vlib_cli_command_t * cmd)
931 return (ip_neighbor_show_sorted_i (vm, input, cmd, IP46_TYPE_IP4));
935 * Display all the IP neighbor entries.
938 * Example of how to display the IPv4 ARP table:
939 * @cliexstart{show ip neighbor}
940 * Time FIB IP4 Flags Ethernet Interface
941 * 346.3028 0 6.1.1.3 de:ad:be:ef:ba:be GigabitEthernet2/0/0
942 * 3077.4271 0 6.1.1.4 S de:ad:be:ef:ff:ff GigabitEthernet2/0/0
943 * 2998.6409 1 6.2.2.3 de:ad:be:ef:00:01 GigabitEthernet2/0/0
944 * Proxy arps enabled for:
945 * Fib_index 0 6.0.0.1 - 6.0.0.11
949 VLIB_CLI_COMMAND (show_ip_neighbors_cmd_node, static) = {
950 .path = "show ip neighbors",
951 .function = ip_neighbor_show,
952 .short_help = "show ip neighbors [interface]",
954 VLIB_CLI_COMMAND (show_ip4_neighbors_cmd_node, static) = {
955 .path = "show ip4 neighbors",
956 .function = ip4_neighbor_show,
957 .short_help = "show ip4 neighbors [interface]",
959 VLIB_CLI_COMMAND (show_ip6_neighbors_cmd_node, static) = {
960 .path = "show ip6 neighbors",
961 .function = ip6_neighbor_show,
962 .short_help = "show ip6 neighbors [interface]",
964 VLIB_CLI_COMMAND (show_ip_neighbor_cmd_node, static) = {
965 .path = "show ip neighbor",
966 .function = ip_neighbor_show,
967 .short_help = "show ip neighbor [interface]",
969 VLIB_CLI_COMMAND (show_ip4_neighbor_cmd_node, static) = {
970 .path = "show ip4 neighbor",
971 .function = ip4_neighbor_show,
972 .short_help = "show ip4 neighbor [interface]",
974 VLIB_CLI_COMMAND (show_ip6_neighbor_cmd_node, static) = {
975 .path = "show ip6 neighbor",
976 .function = ip6_neighbor_show,
977 .short_help = "show ip6 neighbor [interface]",
979 VLIB_CLI_COMMAND (show_ip4_neighbor_sorted_cmd_node, static) = {
980 .path = "show ip4 neighbor-sorted",
981 .function = ip4_neighbor_show_sorted,
982 .short_help = "show ip4 neighbor-sorted",
984 VLIB_CLI_COMMAND (show_ip6_neighbor_sorted_cmd_node, static) = {
985 .path = "show ip6 neighbor-sorted",
986 .function = ip6_neighbor_show_sorted,
987 .short_help = "show ip6 neighbor-sorted",
991 static ip_neighbor_vft_t ip_nbr_vfts[IP46_N_TYPES];
994 ip_neighbor_register (ip46_type_t type, const ip_neighbor_vft_t * vft)
996 ip_nbr_vfts[type] = *vft;
1000 ip_neighbor_probe_dst (const ip_adjacency_t * adj, const ip46_address_t * dst)
1002 if (!vnet_sw_interface_is_admin_up (vnet_get_main (),
1003 adj->rewrite_header.sw_if_index))
1006 switch (adj->ia_nh_proto)
1008 case FIB_PROTOCOL_IP6:
1009 ip6_neighbor_probe_dst (adj, &dst->ip6);
1011 case FIB_PROTOCOL_IP4:
1012 ip4_neighbor_probe_dst (adj, &dst->ip4);
1014 case FIB_PROTOCOL_MPLS:
1021 ip_neighbor_probe (const ip_adjacency_t * adj)
1023 ip_neighbor_probe_dst (adj, &adj->sub_type.nbr.next_hop);
1027 ip_neighbor_advertise (vlib_main_t * vm,
1029 const ip46_address_t * addr, u32 sw_if_index)
1031 vnet_main_t *vnm = vnet_get_main ();
1033 if (type == IP46_TYPE_IP4 || type == IP46_TYPE_BOTH)
1034 ip4_neighbor_advertise (vm, vnm, sw_if_index, (addr) ? &addr->ip4 : NULL);
1035 if (type == IP46_TYPE_IP6 || type == IP46_TYPE_BOTH)
1036 ip6_neighbor_advertise (vm, vnm, sw_if_index, (addr) ? &addr->ip6 : NULL);
1040 ip_neighbor_walk (ip46_type_t type,
1041 u32 sw_if_index, ip_neighbor_walk_cb_t cb, void *ctx)
1043 ip_neighbor_key_t *key;
1046 if (~0 == sw_if_index)
1050 vec_foreach (hash, ip_neighbor_db[type].ipndb_hash)
1053 hash_foreach (key, ipni, *hash,
1055 if (WALK_STOP == cb (ipni, ctx))
1065 if (vec_len (ip_neighbor_db[type].ipndb_hash) <= sw_if_index)
1067 hash = ip_neighbor_db[type].ipndb_hash[sw_if_index];
1070 hash_foreach (key, ipni, hash,
1072 if (WALK_STOP == cb (ipni, ctx))
1080 ip4_neighbor_proxy_add (u32 fib_index,
1081 const ip4_address_t * start,
1082 const ip4_address_t * end)
1084 if (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_add)
1086 return (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_add
1087 (fib_index, start, end));
1094 ip4_neighbor_proxy_delete (u32 fib_index,
1095 const ip4_address_t * start,
1096 const ip4_address_t * end)
1098 if (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_del)
1100 return (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_del
1101 (fib_index, start, end));
1107 ip4_neighbor_proxy_enable (u32 sw_if_index)
1109 if (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_enable)
1111 return (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_enable (sw_if_index));
1117 ip4_neighbor_proxy_disable (u32 sw_if_index)
1119 if (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_disable)
1121 return (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_disable (sw_if_index));
1127 ip6_neighbor_proxy_add (u32 sw_if_index, const ip6_address_t * addr)
1129 if (ip_nbr_vfts[IP46_TYPE_IP6].inv_proxy6_add)
1131 return (ip_nbr_vfts[IP46_TYPE_IP6].inv_proxy6_add (sw_if_index, addr));
1137 ip6_neighbor_proxy_del (u32 sw_if_index, const ip6_address_t * addr)
1139 if (ip_nbr_vfts[IP46_TYPE_IP6].inv_proxy6_del)
1141 return (ip_nbr_vfts[IP46_TYPE_IP6].inv_proxy6_del (sw_if_index, addr));
1147 ip_neighbor_ethernet_change_mac (ethernet_main_t * em,
1148 u32 sw_if_index, uword opaque)
1153 IP_NEIGHBOR_DBG ("mac-change: %U",
1154 format_vnet_sw_if_index_name, vnet_get_main (),
1158 pool_foreach (ipn, ip_neighbor_pool,
1160 if (ipn->ipn_key->ipnk_sw_if_index == sw_if_index)
1161 adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
1162 fib_proto_from_ip46(ipn->ipn_key->ipnk_type),
1163 &ipn->ipn_key->ipnk_ip,
1164 ip_neighbor_mk_complete_walk,
1169 ai = adj_glean_get (FIB_PROTOCOL_IP4, sw_if_index);
1171 if (ADJ_INDEX_INVALID != ai)
1172 adj_glean_update_rewrite (ai);
1176 ip_neighbor_populate (ip46_type_t type, u32 sw_if_index)
1178 index_t *ipnis = NULL, *ipni;
1181 IP_NEIGHBOR_DBG ("populate: %U %U",
1182 format_vnet_sw_if_index_name, vnet_get_main (),
1183 sw_if_index, format_ip46_type, type);
1186 pool_foreach (ipn, ip_neighbor_pool,
1188 if (ipn->ipn_key->ipnk_type == type &&
1189 ipn->ipn_key->ipnk_sw_if_index == sw_if_index)
1190 vec_add1 (ipnis, ipn - ip_neighbor_pool);
1194 vec_foreach (ipni, ipnis)
1196 ipn = ip_neighbor_get (*ipni);
1198 adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
1199 fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
1200 &ipn->ipn_key->ipnk_ip,
1201 ip_neighbor_mk_complete_walk, ipn);
1207 ip_neighbor_flush (ip46_type_t type, u32 sw_if_index)
1209 index_t *ipnis = NULL, *ipni;
1212 IP_NEIGHBOR_DBG ("flush: %U %U",
1213 format_vnet_sw_if_index_name, vnet_get_main (),
1214 sw_if_index, format_ip46_type, type);
1217 pool_foreach (ipn, ip_neighbor_pool,
1219 if (ipn->ipn_key->ipnk_type == type &&
1220 ipn->ipn_key->ipnk_sw_if_index == sw_if_index &&
1221 ip_neighbor_is_dynamic (ipn))
1222 vec_add1 (ipnis, ipn - ip_neighbor_pool);
1226 vec_foreach (ipni, ipnis) ip_neighbor_destroy (ip_neighbor_get (*ipni));
1231 ip_neighbor_mark_one (index_t ipni, void *ctx)
1235 ipn = ip_neighbor_get (ipni);
1237 ipn->ipn_flags |= IP_NEIGHBOR_FLAG_STALE;
1239 return (WALK_CONTINUE);
1243 ip_neighbor_mark (ip46_type_t type)
1245 ip_neighbor_walk (type, ~0, ip_neighbor_mark_one, NULL);
1248 typedef struct ip_neighbor_sweep_ctx_t_
1250 index_t *ipnsc_stale;
1251 } ip_neighbor_sweep_ctx_t;
1254 ip_neighbor_sweep_one (index_t ipni, void *arg)
1256 ip_neighbor_sweep_ctx_t *ctx = arg;
1259 ipn = ip_neighbor_get (ipni);
1261 if (ipn->ipn_flags & IP_NEIGHBOR_FLAG_STALE)
1263 vec_add1 (ctx->ipnsc_stale, ipni);
1266 return (WALK_CONTINUE);
1270 ip_neighbor_sweep (ip46_type_t type)
1272 ip_neighbor_sweep_ctx_t ctx = { };
1275 ip_neighbor_walk (type, ~0, ip_neighbor_sweep_one, &ctx);
1277 vec_foreach (ipni, ctx.ipnsc_stale)
1279 ip_neighbor_destroy (ip_neighbor_get (*ipni));
1281 vec_free (ctx.ipnsc_stale);
1285 * Remove any arp entries associated with the specified interface
1287 static clib_error_t *
1288 ip_neighbor_interface_admin_change (vnet_main_t * vnm,
1289 u32 sw_if_index, u32 flags)
1293 IP_NEIGHBOR_DBG ("interface-admin: %U %s",
1294 format_vnet_sw_if_index_name, vnet_get_main (),
1296 (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ? "up" : "down"));
1298 if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
1300 FOREACH_IP46_TYPE (type) ip_neighbor_populate (type, sw_if_index);
1304 /* admin down, flush all neighbours */
1305 FOREACH_IP46_TYPE (type) ip_neighbor_flush (type, sw_if_index);
1311 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ip_neighbor_interface_admin_change);
1314 * Remove any arp entries associated with the specified interface
1316 static clib_error_t *
1317 ip_neighbor_delete_sw_interface (vnet_main_t * vnm,
1318 u32 sw_if_index, u32 is_add)
1320 IP_NEIGHBOR_DBG ("interface-change: %U %s",
1321 format_vnet_sw_if_index_name, vnet_get_main (),
1322 sw_if_index, (is_add ? "add" : "del"));
1324 if (!is_add && sw_if_index != ~0)
1328 FOREACH_IP46_TYPE (type) ip_neighbor_flush (type, sw_if_index);
1334 VNET_SW_INTERFACE_ADD_DEL_FUNCTION (ip_neighbor_delete_sw_interface);
1336 typedef struct ip_neighbor_walk_covered_ctx_t_
1339 ip46_address_t addr;
1342 } ip_neighbor_walk_covered_ctx_t;
1345 ip_neighbor_walk_covered (index_t ipni, void *arg)
1347 ip_neighbor_walk_covered_ctx_t *ctx = arg;
1350 ipn = ip_neighbor_get (ipni);
1352 ASSERT (ipn->ipn_key->ipnk_type == ctx->type);
1354 if (IP46_TYPE_IP4 == ctx->type)
1356 if (ip4_destination_matches_route (&ip4_main,
1357 &ipn->ipn_key->ipnk_ip.ip4,
1360 ip_neighbor_is_dynamic (ipn))
1362 vec_add1 (ctx->ipnis, ip_neighbor_get_index (ipn));
1365 return (WALK_CONTINUE);
1370 * callback when an interface address is added or deleted
1373 ip_neighbor_add_del_interface_address_v4 (ip4_main_t * im,
1376 ip4_address_t * address,
1378 u32 if_address_index, u32 is_del)
1381 * Flush the ARP cache of all entries covered by the address
1382 * that is being removed.
1384 IP_NEIGHBOR_DBG ("addr-%d: %U, %U/%d",
1385 (is_del ? "del" : "add"),
1386 format_vnet_sw_if_index_name, vnet_get_main (),
1387 sw_if_index, format_ip4_address, address, address_length);
1391 ip_neighbor_walk_covered_ctx_t ctx = {
1392 .addr.ip4 = *address,
1393 .type = IP46_TYPE_IP4,
1394 .length = address_length,
1398 ip_neighbor_walk (IP46_TYPE_IP4, sw_if_index,
1399 ip_neighbor_walk_covered, &ctx);
1401 vec_foreach (ipni, ctx.ipnis)
1402 ip_neighbor_destroy (ip_neighbor_get (*ipni));
1404 vec_free (ctx.ipnis);
1409 * callback when an interface address is added or deleted
1412 ip_neighbor_add_del_interface_address_v6 (ip6_main_t * im,
1415 ip6_address_t * address,
1417 u32 if_address_index, u32 is_del)
1420 * Flush the ARP cache of all entries covered by the address
1421 * that is being removed.
1423 IP_NEIGHBOR_DBG ("addr-change: %U, %U/%d %s",
1424 format_vnet_sw_if_index_name, vnet_get_main (),
1425 sw_if_index, format_ip6_address, address, address_length,
1426 (is_del ? "del" : "add"));
1430 ip_neighbor_walk_covered_ctx_t ctx = {
1431 .addr.ip6 = *address,
1432 .type = IP46_TYPE_IP6,
1433 .length = address_length,
1437 ip_neighbor_walk (IP46_TYPE_IP6, sw_if_index,
1438 ip_neighbor_walk_covered, &ctx);
1440 vec_foreach (ipni, ctx.ipnis)
1441 ip_neighbor_destroy (ip_neighbor_get (*ipni));
1443 vec_free (ctx.ipnis);
1447 typedef struct ip_neighbor_table_bind_ctx_t_
1451 } ip_neighbor_table_bind_ctx_t;
1454 ip_neighbor_walk_table_bind (index_t ipni, void *arg)
1456 ip_neighbor_table_bind_ctx_t *ctx = arg;
1459 ipn = ip_neighbor_get (ipni);
1460 ip_neighbor_adj_fib_remove (ipn, ctx->old_fib_index);
1461 ip_neighbor_adj_fib_add (ipn, ctx->new_fib_index);
1463 return (WALK_CONTINUE);
1467 ip_neighbor_table_bind_v4 (ip4_main_t * im,
1470 u32 new_fib_index, u32 old_fib_index)
1472 ip_neighbor_table_bind_ctx_t ctx = {
1473 .old_fib_index = old_fib_index,
1474 .new_fib_index = new_fib_index,
1477 ip_neighbor_walk (IP46_TYPE_IP4, sw_if_index,
1478 ip_neighbor_walk_table_bind, &ctx);
1482 ip_neighbor_table_bind_v6 (ip6_main_t * im,
1485 u32 new_fib_index, u32 old_fib_index)
1487 ip_neighbor_table_bind_ctx_t ctx = {
1488 .old_fib_index = old_fib_index,
1489 .new_fib_index = new_fib_index,
1492 ip_neighbor_walk (IP46_TYPE_IP6, sw_if_index,
1493 ip_neighbor_walk_table_bind, &ctx);
1496 typedef enum ip_neighbor_age_state_t_
1498 IP_NEIGHBOR_AGE_ALIVE,
1499 IP_NEIGHBOR_AGE_PROBE,
1500 IP_NEIGHBOR_AGE_DEAD,
1501 } ip_neighbor_age_state_t;
1503 #define IP_NEIGHBOR_PROCESS_SLEEP_LONG (0)
1505 static ip_neighbor_age_state_t
1506 ip_neighbour_age_out (index_t ipni, f64 now, f64 * wait)
1512 ipn = ip_neighbor_get (ipni);
1513 ipndb_age = ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_age;
1514 ttl = now - ipn->ipn_time_last_updated;
1517 if (ttl > ipndb_age)
1519 IP_NEIGHBOR_DBG ("aged: %U @%f - %f > %d",
1520 format_ip_neighbor, ipni, now,
1521 ipn->ipn_time_last_updated, ipndb_age);
1522 if (ipn->ipn_n_probes > 2)
1524 /* 3 strikes and yea-re out */
1525 IP_NEIGHBOR_DBG ("dead: %U", format_ip_neighbor, ipni);
1527 return (IP_NEIGHBOR_AGE_DEAD);
1533 ai = adj_glean_get (fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
1534 ip_neighbor_get_sw_if_index (ipn));
1536 if (ADJ_INDEX_INVALID != ai)
1537 ip_neighbor_probe_dst (adj_get (ai), ip_neighbor_get_ip (ipn));
1539 ipn->ipn_n_probes++;
1545 /* here we are sure that ttl <= ipndb_age */
1546 *wait = ipndb_age - ttl + 1;
1547 return (IP_NEIGHBOR_AGE_ALIVE);
1550 return (IP_NEIGHBOR_AGE_PROBE);
1553 typedef enum ip_neighbor_process_event_t_
1555 IP_NEIGHBOR_AGE_PROCESS_WAKEUP,
1556 } ip_neighbor_process_event_t;
1559 ip_neighbor_age_loop (vlib_main_t * vm,
1560 vlib_node_runtime_t * rt,
1561 vlib_frame_t * f, ip46_type_t type)
1563 uword event_type, *event_data = NULL;
1566 /* Set the timeout to an effectively infinite value when the process starts */
1567 timeout = IP_NEIGHBOR_PROCESS_SLEEP_LONG;
1574 vlib_process_wait_for_event (vm);
1576 vlib_process_wait_for_event_or_clock (vm, timeout);
1578 event_type = vlib_process_get_events (vm, &event_data);
1579 vec_reset_length (event_data);
1581 now = vlib_time_now (vm);
1588 ip_neighbor_elt_t *elt, *head;
1591 timeout = ip_neighbor_db[type].ipndb_age;
1592 head = pool_elt_at_index (ip_neighbor_elt_pool,
1593 ip_neighbor_list_head[type]);
1596 /* the list is time sorted, newest first, so start from the back
1597 * and work forwards. Stop when we get to one that is alive */
1599 clib_llist_foreach_reverse(ip_neighbor_elt_pool,
1600 ipne_anchor, head, elt,
1602 ip_neighbor_age_state_t res;
1604 res = ip_neighbour_age_out(elt->ipne_index, now, &wait);
1606 if (IP_NEIGHBOR_AGE_ALIVE == res) {
1607 /* the oldest neighbor has not yet expired, go back to sleep */
1608 timeout = clib_min (wait, timeout);
1611 else if (IP_NEIGHBOR_AGE_DEAD == res) {
1612 /* the oldest neighbor is dead, pop it, then restart the walk
1613 * again from the back */
1614 ip_neighbor_destroy (ip_neighbor_get(elt->ipne_index));
1618 timeout = clib_min (wait, timeout);
1623 case IP_NEIGHBOR_AGE_PROCESS_WAKEUP:
1626 if (!ip_neighbor_db[type].ipndb_age)
1628 /* aging has been disabled */
1632 ip_neighbor_elt_t *elt, *head;
1634 head = pool_elt_at_index (ip_neighbor_elt_pool,
1635 ip_neighbor_list_head[type]);
1636 /* no neighbors yet */
1637 if (clib_llist_is_empty (ip_neighbor_elt_pool, ipne_anchor, head))
1639 timeout = ip_neighbor_db[type].ipndb_age;
1643 /* poke the oldset neighbour for aging, which returns how long we sleep for */
1644 elt = clib_llist_prev (ip_neighbor_elt_pool, ipne_anchor, head);
1645 ip_neighbour_age_out (elt->ipne_index, now, &timeout);
1654 ip4_neighbor_age_process (vlib_main_t * vm,
1655 vlib_node_runtime_t * rt, vlib_frame_t * f)
1657 return (ip_neighbor_age_loop (vm, rt, f, IP46_TYPE_IP4));
1661 ip6_neighbor_age_process (vlib_main_t * vm,
1662 vlib_node_runtime_t * rt, vlib_frame_t * f)
1664 return (ip_neighbor_age_loop (vm, rt, f, IP46_TYPE_IP6));
1668 VLIB_REGISTER_NODE (ip4_neighbor_age_process_node,static) = {
1669 .function = ip4_neighbor_age_process,
1670 .type = VLIB_NODE_TYPE_PROCESS,
1671 .name = "ip4-neighbor-age-process",
1673 VLIB_REGISTER_NODE (ip6_neighbor_age_process_node,static) = {
1674 .function = ip6_neighbor_age_process,
1675 .type = VLIB_NODE_TYPE_PROCESS,
1676 .name = "ip6-neighbor-age-process",
1681 ip_neighbor_config (ip46_type_t type, u32 limit, u32 age, bool recycle)
1683 ip_neighbor_db[type].ipndb_limit = limit;
1684 ip_neighbor_db[type].ipndb_recycle = recycle;
1685 ip_neighbor_db[type].ipndb_age = age;
1687 vlib_process_signal_event (vlib_get_main (),
1688 (IP46_TYPE_IP4 == type ?
1689 ip4_neighbor_age_process_node.index :
1690 ip6_neighbor_age_process_node.index),
1691 IP_NEIGHBOR_AGE_PROCESS_WAKEUP, 0);
1696 static clib_error_t *
1697 ip_neighbor_config_show (vlib_main_t * vm,
1698 unformat_input_t * input, vlib_cli_command_t * cmd)
1703 FOREACH_IP46_TYPE(type) {
1704 vlib_cli_output (vm, "%U:", format_ip46_type, type);
1705 vlib_cli_output (vm, " limit:%d, age:%d, recycle:%d",
1706 ip_neighbor_db[type].ipndb_limit,
1707 ip_neighbor_db[type].ipndb_age,
1708 ip_neighbor_db[type].ipndb_recycle);
1716 VLIB_CLI_COMMAND (show_ip_neighbor_cfg_cmd_node, static) = {
1717 .path = "show ip neighbor-config",
1718 .function = ip_neighbor_config_show,
1719 .short_help = "show ip neighbor-config",
1723 static clib_error_t *
1724 ip_neighbor_init (vlib_main_t * vm)
1727 ip4_add_del_interface_address_callback_t cb = {
1728 .function = ip_neighbor_add_del_interface_address_v4,
1730 vec_add1 (ip4_main.add_del_interface_address_callbacks, cb);
1733 ip6_add_del_interface_address_callback_t cb = {
1734 .function = ip_neighbor_add_del_interface_address_v6,
1736 vec_add1 (ip6_main.add_del_interface_address_callbacks, cb);
1739 ip4_table_bind_callback_t cb = {
1740 .function = ip_neighbor_table_bind_v4,
1742 vec_add1 (ip4_main.table_bind_callbacks, cb);
1745 ip6_table_bind_callback_t cb = {
1746 .function = ip_neighbor_table_bind_v6,
1748 vec_add1 (ip6_main.table_bind_callbacks, cb);
1751 ethernet_address_change_ctx_t ctx = {
1752 .function = ip_neighbor_ethernet_change_mac,
1753 .function_opaque = 0,
1755 vec_add1 (ethernet_main.address_change_callbacks, ctx);
1758 ipn_logger = vlib_log_register_class ("ip", "neighbor");
1762 FOREACH_IP46_TYPE (type)
1763 ip_neighbor_list_head[type] =
1764 clib_llist_make_head (ip_neighbor_elt_pool, ipne_anchor);
1770 VLIB_INIT_FUNCTION (ip_neighbor_init) =
1772 .runs_after = VLIB_INITS("ip_main_init"),
1777 * fd.io coding-style-patch-verification: ON
1780 * eval: (c-set-style "gnu")