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/ip/ip46_address.h>
27 #include <vnet/fib/fib_table.h>
28 #include <vnet/adj/adj_mcast.h>
30 /** Pool for All IP neighbors */
31 static ip_neighbor_t *ip_neighbor_pool;
33 /** protocol specific lists of time sorted neighbors */
34 index_t ip_neighbor_list_head[N_AF];
36 typedef struct ip_neighbor_elt_t_
38 clib_llist_anchor_t ipne_anchor;
42 /** Pool of linked list elemeents */
43 ip_neighbor_elt_t *ip_neighbor_elt_pool;
45 typedef struct ip_neighbor_db_t_
47 /** per interface hash */
49 /** per-protocol limit - max number of neighbors*/
51 /** max age of a neighbor before it's forcibly evicted */
53 /** when the limit is reached and new neighbors are created, should
54 * we recycle an old one */
56 /** per-protocol number of elements */
58 /** per-protocol number of elements per-fib-index*/
59 u32 *ipndb_n_elts_per_fib;
62 static vlib_log_class_t ipn_logger;
64 /* DBs of neighbours one per AF */
66 static ip_neighbor_db_t ip_neighbor_db[N_AF] = {
69 /* Default to not aging and not recycling */
71 .ipndb_recycle = false,
75 /* Default to not aging and not recycling */
77 .ipndb_recycle = false,
82 #define IP_NEIGHBOR_DBG(...) \
83 vlib_log_debug (ipn_logger, __VA_ARGS__);
85 #define IP_NEIGHBOR_INFO(...) \
86 vlib_log_notice (ipn_logger, __VA_ARGS__);
89 ip_neighbor_get (index_t ipni)
91 if (pool_is_free_index (ip_neighbor_pool, ipni))
94 return (pool_elt_at_index (ip_neighbor_pool, ipni));
98 ip_neighbor_get_index (const ip_neighbor_t * ipn)
100 return (ipn - ip_neighbor_pool);
104 ip_neighbor_touch (ip_neighbor_t * ipn)
106 ipn->ipn_flags &= ~IP_NEIGHBOR_FLAG_STALE;
110 ip_neighbor_is_dynamic (const ip_neighbor_t * ipn)
112 return (ipn->ipn_flags & IP_NEIGHBOR_FLAG_DYNAMIC);
116 ip_neighbor_get_ip (const ip_neighbor_t * ipn)
118 return (&ipn->ipn_key->ipnk_ip);
122 ip_neighbor_get_af (const ip_neighbor_t * ipn)
124 return (ip_addr_version (&ipn->ipn_key->ipnk_ip));
127 const mac_address_t *
128 ip_neighbor_get_mac (const ip_neighbor_t * ipn)
130 return (&ipn->ipn_mac);
134 ip_neighbor_get_sw_if_index (const ip_neighbor_t * ipn)
136 return (ipn->ipn_key->ipnk_sw_if_index);
140 ip_neighbor_list_remove (ip_neighbor_t * ipn)
142 /* new neighbours, are added to the head of the list, since the
143 * list is time sorted, newest first */
144 ip_neighbor_elt_t *elt;
146 if (~0 != ipn->ipn_elt)
148 elt = pool_elt_at_index (ip_neighbor_elt_pool, ipn->ipn_elt);
150 clib_llist_remove (ip_neighbor_elt_pool, ipne_anchor, elt);
157 ip_neighbor_refresh (ip_neighbor_t * ipn)
159 /* new neighbours, are added to the head of the list, since the
160 * list is time sorted, newest first */
161 ip_neighbor_elt_t *elt, *head;
163 ip_neighbor_touch (ipn);
164 ipn->ipn_time_last_updated = vlib_time_now (vlib_get_main ());
165 ipn->ipn_n_probes = 0;
167 if (ip_neighbor_is_dynamic (ipn))
169 if (~0 == ipn->ipn_elt)
170 /* first time insertion */
171 pool_get_zero (ip_neighbor_elt_pool, elt);
174 /* already inserted - extract first */
175 elt = pool_elt_at_index (ip_neighbor_elt_pool, ipn->ipn_elt);
177 clib_llist_remove (ip_neighbor_elt_pool, ipne_anchor, elt);
179 head = pool_elt_at_index (ip_neighbor_elt_pool,
180 ip_neighbor_list_head[ip_neighbor_get_af
183 elt->ipne_index = ip_neighbor_get_index (ipn);
184 clib_llist_add (ip_neighbor_elt_pool, ipne_anchor, elt, head);
185 ipn->ipn_elt = elt - ip_neighbor_elt_pool;
190 ip_neighbor_db_add (const ip_neighbor_t * ipn)
192 ip_address_family_t af;
195 af = ip_neighbor_get_af (ipn);
196 sw_if_index = ipn->ipn_key->ipnk_sw_if_index;
198 vec_validate (ip_neighbor_db[af].ipndb_hash, sw_if_index);
200 if (!ip_neighbor_db[af].ipndb_hash[sw_if_index])
201 ip_neighbor_db[af].ipndb_hash[sw_if_index]
202 = hash_create_mem (0, sizeof (ip_neighbor_key_t), sizeof (index_t));
204 hash_set_mem (ip_neighbor_db[af].ipndb_hash[sw_if_index],
205 ipn->ipn_key, ip_neighbor_get_index (ipn));
207 ip_neighbor_db[af].ipndb_n_elts++;
211 ip_neighbor_db_remove (const ip_neighbor_t * ipn)
213 ip_address_family_t af;
216 af = ip_neighbor_get_af (ipn);
217 sw_if_index = ipn->ipn_key->ipnk_sw_if_index;
219 vec_validate (ip_neighbor_db[af].ipndb_hash, sw_if_index);
221 hash_unset_mem (ip_neighbor_db[af].ipndb_hash[sw_if_index], ipn->ipn_key);
223 ip_neighbor_db[af].ipndb_n_elts--;
226 static ip_neighbor_t *
227 ip_neighbor_db_find (const ip_neighbor_key_t * key)
229 ip_address_family_t af;
232 af = ip_addr_version (&key->ipnk_ip);
234 if (key->ipnk_sw_if_index >= vec_len (ip_neighbor_db[af].ipndb_hash))
237 p = hash_get_mem (ip_neighbor_db[af].ipndb_hash
238 [key->ipnk_sw_if_index], key);
241 return ip_neighbor_get (p[0]);
247 ip_af_type_pfx_len (ip_address_family_t type)
249 return (type == AF_IP4 ? 32 : 128);
253 ip_neighbor_adj_fib_add (ip_neighbor_t * ipn, u32 fib_index)
255 ip_address_family_t af;
257 af = ip_neighbor_get_af (ipn);
260 ip6_address_is_link_local_unicast (&ip_addr_v6
261 (&ipn->ipn_key->ipnk_ip)))
263 ip6_ll_prefix_t pfx = {
264 .ilp_addr = ip_addr_v6 (&ipn->ipn_key->ipnk_ip),
265 .ilp_sw_if_index = ipn->ipn_key->ipnk_sw_if_index,
267 ipn->ipn_fib_entry_index =
268 ip6_ll_table_entry_update (&pfx, FIB_ROUTE_PATH_FLAG_NONE);
272 fib_protocol_t fproto;
274 fproto = ip_address_family_to_fib_proto (af);
277 .fp_len = ip_af_type_pfx_len (af),
279 .fp_addr = ip_addr_46 (&ipn->ipn_key->ipnk_ip),
282 ipn->ipn_fib_entry_index =
283 fib_table_entry_path_add (fib_index, &pfx, FIB_SOURCE_ADJ,
284 FIB_ENTRY_FLAG_ATTACHED,
285 fib_proto_to_dpo (fproto),
287 ipn->ipn_key->ipnk_sw_if_index,
288 ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
290 vec_validate (ip_neighbor_db[af].ipndb_n_elts_per_fib, fib_index);
292 ip_neighbor_db[af].ipndb_n_elts_per_fib[fib_index]++;
294 if (1 == ip_neighbor_db[af].ipndb_n_elts_per_fib[fib_index])
295 fib_table_lock (fib_index, fproto, FIB_SOURCE_ADJ);
300 ip_neighbor_adj_fib_remove (ip_neighbor_t * ipn, u32 fib_index)
302 ip_address_family_t af;
304 af = ip_neighbor_get_af (ipn);
306 if (FIB_NODE_INDEX_INVALID != ipn->ipn_fib_entry_index)
309 ip6_address_is_link_local_unicast (&ip_addr_v6
310 (&ipn->ipn_key->ipnk_ip)))
312 ip6_ll_prefix_t pfx = {
313 .ilp_addr = ip_addr_v6 (&ipn->ipn_key->ipnk_ip),
314 .ilp_sw_if_index = ipn->ipn_key->ipnk_sw_if_index,
316 ip6_ll_table_entry_delete (&pfx);
320 fib_protocol_t fproto;
322 fproto = ip_address_family_to_fib_proto (af);
325 .fp_len = ip_af_type_pfx_len (af),
327 .fp_addr = ip_addr_46 (&ipn->ipn_key->ipnk_ip),
330 fib_table_entry_path_remove (fib_index,
333 fib_proto_to_dpo (fproto),
335 ipn->ipn_key->ipnk_sw_if_index,
336 ~0, 1, FIB_ROUTE_PATH_FLAG_NONE);
338 ip_neighbor_db[af].ipndb_n_elts_per_fib[fib_index]--;
340 if (0 == ip_neighbor_db[af].ipndb_n_elts_per_fib[fib_index])
341 fib_table_unlock (fib_index, fproto, FIB_SOURCE_ADJ);
347 ip_neighbor_mk_complete (adj_index_t ai, ip_neighbor_t * ipn)
349 adj_nbr_update_rewrite (ai, ADJ_NBR_REWRITE_FLAG_COMPLETE,
350 ethernet_build_rewrite (vnet_get_main (),
352 ipn_key->ipnk_sw_if_index,
353 adj_get_link_type (ai),
354 ipn->ipn_mac.bytes));
358 ip_neighbor_mk_incomplete (adj_index_t ai)
360 ip_adjacency_t *adj = adj_get (ai);
362 adj_nbr_update_rewrite (ai,
363 ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
364 ethernet_build_rewrite (vnet_get_main (),
366 rewrite_header.sw_if_index,
368 VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
372 ip_neighbor_mk_complete_walk (adj_index_t ai, void *ctx)
374 ip_neighbor_t *ipn = ctx;
376 ip_neighbor_mk_complete (ai, ipn);
378 return (ADJ_WALK_RC_CONTINUE);
382 ip_neighbor_mk_incomplete_walk (adj_index_t ai, void *ctx)
384 ip_neighbor_mk_incomplete (ai);
386 return (ADJ_WALK_RC_CONTINUE);
390 ip_neighbor_destroy (ip_neighbor_t * ipn)
392 ip_address_family_t af;
394 af = ip_neighbor_get_af (ipn);
396 IP_NEIGHBOR_DBG ("free: %U", format_ip_neighbor,
397 ip_neighbor_get_index (ipn));
399 ip_neighbor_publish (ip_neighbor_get_index (ipn),
400 IP_NEIGHBOR_EVENT_REMOVED);
402 adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
403 ip_address_family_to_fib_proto (af),
404 &ip_addr_46 (&ipn->ipn_key->ipnk_ip),
405 ip_neighbor_mk_incomplete_walk, ipn);
406 ip_neighbor_adj_fib_remove
408 fib_table_get_index_for_sw_if_index
409 (ip_address_family_to_fib_proto (af), ipn->ipn_key->ipnk_sw_if_index));
411 ip_neighbor_list_remove (ipn);
412 ip_neighbor_db_remove (ipn);
413 clib_mem_free (ipn->ipn_key);
415 pool_put (ip_neighbor_pool, ipn);
419 ip_neighbor_force_reuse (ip_address_family_t af)
421 if (!ip_neighbor_db[af].ipndb_recycle)
424 /* pluck the oldest entry, which is the one from the end of the list */
425 ip_neighbor_elt_t *elt, *head;
427 head = pool_elt_at_index (ip_neighbor_elt_pool, ip_neighbor_list_head[af]);
429 if (clib_llist_is_empty (ip_neighbor_elt_pool, ipne_anchor, head))
432 elt = clib_llist_prev (ip_neighbor_elt_pool, ipne_anchor, head);
433 ip_neighbor_destroy (ip_neighbor_get (elt->ipne_index));
438 static ip_neighbor_t *
439 ip_neighbor_alloc (const ip_neighbor_key_t * key,
440 const mac_address_t * mac, ip_neighbor_flags_t flags)
442 ip_address_family_t af;
445 af = ip_addr_version (&key->ipnk_ip);
447 if (ip_neighbor_db[af].ipndb_limit &&
448 (ip_neighbor_db[af].ipndb_n_elts >= ip_neighbor_db[af].ipndb_limit))
450 if (!ip_neighbor_force_reuse (af))
454 pool_get_zero (ip_neighbor_pool, ipn);
456 ipn->ipn_key = clib_mem_alloc (sizeof (*ipn->ipn_key));
457 clib_memcpy (ipn->ipn_key, key, sizeof (*ipn->ipn_key));
459 ipn->ipn_fib_entry_index = FIB_NODE_INDEX_INVALID;
460 ipn->ipn_flags = flags;
463 mac_address_copy (&ipn->ipn_mac, mac);
465 ip_neighbor_db_add (ipn);
467 /* create the adj-fib. the entry in the FIB table for the peer's interface */
468 if (!(ipn->ipn_flags & IP_NEIGHBOR_FLAG_NO_FIB_ENTRY))
469 ip_neighbor_adj_fib_add
470 (ipn, fib_table_get_index_for_sw_if_index
471 (ip_address_family_to_fib_proto (af), ipn->ipn_key->ipnk_sw_if_index));
477 ip_neighbor_add (const ip_address_t * ip,
478 const mac_address_t * mac,
480 ip_neighbor_flags_t flags, u32 * stats_index)
482 fib_protocol_t fproto;
485 /* main thread only */
486 ASSERT (0 == vlib_get_thread_index ());
488 fproto = ip_address_family_to_fib_proto (ip_addr_version (ip));
490 const ip_neighbor_key_t key = {
492 .ipnk_sw_if_index = sw_if_index,
495 ipn = ip_neighbor_db_find (&key);
499 IP_NEIGHBOR_DBG ("update: %U, %U",
500 format_vnet_sw_if_index_name, vnet_get_main (),
501 sw_if_index, format_ip_address, ip,
502 format_ip_neighbor_flags, flags, format_mac_address_t,
505 ip_neighbor_touch (ipn);
507 /* Refuse to over-write static neighbor entry. */
508 if (!(flags & IP_NEIGHBOR_FLAG_STATIC) &&
509 (ipn->ipn_flags & IP_NEIGHBOR_FLAG_STATIC))
511 /* if MAC address match, still check to send event */
512 if (0 == mac_address_cmp (&ipn->ipn_mac, mac))
513 goto check_customers;
517 /* A dynamic entry can become static, but not vice-versa.
518 * i.e. since if it was programmed by the CP then it must
519 * be removed by the CP */
520 if ((flags & IP_NEIGHBOR_FLAG_STATIC) &&
521 !(ipn->ipn_flags & IP_NEIGHBOR_FLAG_STATIC))
523 ip_neighbor_list_remove (ipn);
524 ipn->ipn_flags |= IP_NEIGHBOR_FLAG_STATIC;
525 ipn->ipn_flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC;
529 * prevent a DoS attack from the data-plane that
530 * spams us with no-op updates to the MAC address
532 if (0 == mac_address_cmp (&ipn->ipn_mac, mac))
534 ip_neighbor_refresh (ipn);
535 goto check_customers;
538 mac_address_copy (&ipn->ipn_mac, mac);
542 IP_NEIGHBOR_INFO ("add: %U, %U",
543 format_vnet_sw_if_index_name, vnet_get_main (),
544 sw_if_index, format_ip_address, ip,
545 format_ip_neighbor_flags, flags, format_mac_address_t,
548 ipn = ip_neighbor_alloc (&key, mac, flags);
551 return VNET_API_ERROR_LIMIT_EXCEEDED;
554 /* Update time stamp and flags. */
555 ip_neighbor_refresh (ipn);
557 adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
558 fproto, &ip_addr_46 (&ipn->ipn_key->ipnk_ip),
559 ip_neighbor_mk_complete_walk, ipn);
562 /* Customer(s) requesting event for this address? */
563 ip_neighbor_publish (ip_neighbor_get_index (ipn), IP_NEIGHBOR_EVENT_ADDED);
566 *stats_index = adj_nbr_find (fproto,
567 fib_proto_to_link (fproto),
568 &ip_addr_46 (&ipn->ipn_key->ipnk_ip),
569 ipn->ipn_key->ipnk_sw_if_index);
574 ip_neighbor_del (const ip_address_t * ip, u32 sw_if_index)
578 /* main thread only */
579 ASSERT (0 == vlib_get_thread_index ());
581 IP_NEIGHBOR_INFO ("delete: %U, %U",
582 format_vnet_sw_if_index_name, vnet_get_main (),
583 sw_if_index, format_ip_address, ip);
585 const ip_neighbor_key_t key = {
587 .ipnk_sw_if_index = sw_if_index,
590 ipn = ip_neighbor_db_find (&key);
593 return (VNET_API_ERROR_NO_SUCH_ENTRY);
595 ip_neighbor_destroy (ipn);
600 typedef struct ip_neighbor_del_all_ctx_t_
603 } ip_neighbor_del_all_ctx_t;
606 ip_neighbor_del_all_walk_cb (index_t ipni, void *arg)
608 ip_neighbor_del_all_ctx_t *ctx = arg;
610 vec_add1 (ctx->ipn_del, ipni);
612 return (WALK_CONTINUE);
616 ip_neighbor_del_all (ip_address_family_t af, u32 sw_if_index)
618 IP_NEIGHBOR_INFO ("delete-all: %U, %U",
619 format_ip_address_family, af,
620 format_vnet_sw_if_index_name, vnet_get_main (),
623 ip_neighbor_del_all_ctx_t ctx = {
628 ip_neighbor_walk (af, sw_if_index, ip_neighbor_del_all_walk_cb, &ctx);
631 ctx.ipn_del) ip_neighbor_destroy (ip_neighbor_get (*ipni));
632 vec_free (ctx.ipn_del);
636 ip_neighbor_update (vnet_main_t * vnm, adj_index_t ai)
643 ip_neighbor_key_t key = {
644 .ipnk_sw_if_index = adj->rewrite_header.sw_if_index,
647 ip_address_from_46 (&adj->sub_type.nbr.next_hop,
648 adj->ia_nh_proto, &key.ipnk_ip);
650 ipn = ip_neighbor_db_find (&key);
652 switch (adj->lookup_next_index)
654 case IP_LOOKUP_NEXT_ARP:
657 adj_nbr_walk_nh (adj->rewrite_header.sw_if_index,
659 &adj->sub_type.nbr.next_hop,
660 ip_neighbor_mk_complete_walk, ipn);
665 * no matching ARP entry.
666 * construct the rewrite required to for an ARP packet, and stick
667 * that in the adj's pipe to smoke.
669 adj_nbr_update_rewrite
671 ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
672 ethernet_build_rewrite
674 adj->rewrite_header.sw_if_index,
676 VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
679 * since the FIB has added this adj for a route, it makes sense it
680 * may want to forward traffic sometime soon. Let's send a
681 * speculative ARP. just one. If we were to do periodically that
682 * wouldn't be bad either, but that's more code than i'm prepared to
683 * write at this time for relatively little reward.
686 * adj_nbr_update_rewrite may actually call fib_walk_sync.
687 * fib_walk_sync may allocate a new adjacency and potentially cause
688 * a realloc for adj_pool. When that happens, adj pointer is no
689 * longer valid here.x We refresh adj pointer accordingly.
692 ip_neighbor_probe (adj);
695 case IP_LOOKUP_NEXT_REWRITE:
696 /* Update of an existing rewrite adjacency happens e.g. when the
697 * interface's MAC address changes */
699 ip_neighbor_mk_complete (ai, ipn);
701 case IP_LOOKUP_NEXT_GLEAN:
702 case IP_LOOKUP_NEXT_BCAST:
703 case IP_LOOKUP_NEXT_MCAST:
704 case IP_LOOKUP_NEXT_DROP:
705 case IP_LOOKUP_NEXT_PUNT:
706 case IP_LOOKUP_NEXT_LOCAL:
707 case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
708 case IP_LOOKUP_NEXT_MIDCHAIN:
709 case IP_LOOKUP_NEXT_ICMP_ERROR:
710 case IP_LOOKUP_N_NEXT:
717 ip_neighbor_learn (const ip_neighbor_learn_t * l)
719 ip_neighbor_add (&l->ip, &l->mac, l->sw_if_index,
720 IP_NEIGHBOR_FLAG_DYNAMIC, NULL);
723 static clib_error_t *
724 ip_neighbor_cmd (vlib_main_t * vm,
725 unformat_input_t * input, vlib_cli_command_t * cmd)
727 ip_address_t ip = IP_ADDRESS_V6_ALL_0S;
728 mac_address_t mac = ZERO_MAC_ADDRESS;
729 vnet_main_t *vnm = vnet_get_main ();
730 ip_neighbor_flags_t flags;
731 u32 sw_if_index = ~0;
735 flags = IP_NEIGHBOR_FLAG_DYNAMIC;
737 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
739 /* set ip arp TenGigE1/1/0/1 1.2.3.4 aa:bb:... or aabb.ccdd... */
740 if (unformat (input, "%U %U %U",
741 unformat_vnet_sw_interface, vnm, &sw_if_index,
742 unformat_ip_address, &ip, unformat_mac_address_t, &mac))
744 else if (unformat (input, "delete") || unformat (input, "del"))
746 else if (unformat (input, "static"))
748 flags |= IP_NEIGHBOR_FLAG_STATIC;
749 flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC;
751 else if (unformat (input, "no-fib-entry"))
752 flags |= IP_NEIGHBOR_FLAG_NO_FIB_ENTRY;
753 else if (unformat (input, "count %d", &count))
759 if (sw_if_index == ~0 ||
760 ip_address_is_zero (&ip) || mac_address_is_zero (&mac))
761 return clib_error_return (0,
762 "specify interface, IP address and MAC: `%U'",
763 format_unformat_error, input);
768 ip_neighbor_add (&ip, &mac, sw_if_index, flags, NULL);
770 ip_neighbor_del (&ip, sw_if_index);
772 ip_address_increment (&ip);
773 mac_address_increment (&mac);
783 * Add or delete IPv4 ARP cache entries.
785 * @note 'set ip neighbor' options (e.g. delete, static, 'fib-id <id>',
786 * 'count <number>', 'interface ip4_addr mac_addr') can be added in
787 * any order and combination.
791 * Add or delete IPv4 ARP cache entries as follows. MAC Address can be in
792 * either aa:bb:cc:dd:ee:ff format or aabb.ccdd.eeff format.
793 * @cliexcmd{set ip neighbor GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
794 * @cliexcmd{set ip neighbor delete GigabitEthernet2/0/0 6.0.0.3 de:ad:be:ef:ba:be}
796 * To add or delete an IPv4 ARP cache entry to or from a specific fib
798 * @cliexcmd{set ip neighbor fib-id 1 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
799 * @cliexcmd{set ip neighbor fib-id 1 delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
801 * Add or delete IPv4 static ARP cache entries as follows:
802 * @cliexcmd{set ip neighbor static GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
803 * @cliexcmd{set ip neighbor static delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
805 * For testing / debugging purposes, the 'set ip neighbor' command can add or
806 * delete multiple entries. Supply the 'count N' parameter:
807 * @cliexcmd{set ip neighbor count 10 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
810 VLIB_CLI_COMMAND (ip_neighbor_command, static) = {
811 .path = "set ip neighbor",
813 "set ip neighbor [del] <intfc> <ip-address> <mac-address> [static] [no-fib-entry] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
814 .function = ip_neighbor_cmd,
816 VLIB_CLI_COMMAND (ip_neighbor_command2, static) = {
817 .path = "ip neighbor",
819 "ip neighbor [del] <intfc> <ip-address> <mac-address> [static] [no-fib-entry] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
820 .function = ip_neighbor_cmd,
825 ip_neighbor_sort (void *a1, void *a2)
827 index_t *ipni1 = a1, *ipni2 = a2;
828 ip_neighbor_t *ipn1, *ipn2;
831 ipn1 = ip_neighbor_get (*ipni1);
832 ipn2 = ip_neighbor_get (*ipni2);
834 cmp = vnet_sw_interface_compare (vnet_get_main (),
835 ipn1->ipn_key->ipnk_sw_if_index,
836 ipn2->ipn_key->ipnk_sw_if_index);
838 cmp = ip_address_cmp (&ipn1->ipn_key->ipnk_ip, &ipn2->ipn_key->ipnk_ip);
843 ip_neighbor_entries (u32 sw_if_index, ip_address_family_t af)
845 index_t *ipnis = NULL;
849 pool_foreach (ipn, ip_neighbor_pool)
851 if ((sw_if_index == ~0 ||
852 ipn->ipn_key->ipnk_sw_if_index == sw_if_index) &&
854 ip_neighbor_get_af(ipn) == af))
855 vec_add1 (ipnis, ip_neighbor_get_index(ipn));
861 vec_sort_with_function (ipnis, ip_neighbor_sort);
865 static clib_error_t *
866 ip_neighbor_show_sorted_i (vlib_main_t * vm,
867 unformat_input_t * input,
868 vlib_cli_command_t * cmd, ip_address_family_t af)
870 ip_neighbor_elt_t *elt, *head;
872 head = pool_elt_at_index (ip_neighbor_elt_pool, ip_neighbor_list_head[af]);
875 vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP",
876 "Flags", "Ethernet", "Interface");
879 /* the list is time sorted, newest first, so start from the back
880 * and work forwards. Stop when we get to one that is alive */
881 clib_llist_foreach_reverse(ip_neighbor_elt_pool,
882 ipne_anchor, head, elt,
884 vlib_cli_output (vm, "%U", format_ip_neighbor, elt->ipne_index);
891 static clib_error_t *
892 ip_neighbor_show_i (vlib_main_t * vm,
893 unformat_input_t * input,
894 vlib_cli_command_t * cmd, ip_address_family_t af)
896 index_t *ipni, *ipnis = NULL;
899 /* Filter entries by interface if given. */
901 (void) unformat_user (input, unformat_vnet_sw_interface, vnet_get_main (),
904 ipnis = ip_neighbor_entries (sw_if_index, af);
907 vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP",
908 "Flags", "Ethernet", "Interface");
910 vec_foreach (ipni, ipnis)
912 vlib_cli_output (vm, "%U", format_ip_neighbor, *ipni);
919 static clib_error_t *
920 ip_neighbor_show (vlib_main_t * vm,
921 unformat_input_t * input, vlib_cli_command_t * cmd)
923 return (ip_neighbor_show_i (vm, input, cmd, N_AF));
926 static clib_error_t *
927 ip6_neighbor_show (vlib_main_t * vm,
928 unformat_input_t * input, vlib_cli_command_t * cmd)
930 return (ip_neighbor_show_i (vm, input, cmd, AF_IP6));
933 static clib_error_t *
934 ip4_neighbor_show (vlib_main_t * vm,
935 unformat_input_t * input, vlib_cli_command_t * cmd)
937 return (ip_neighbor_show_i (vm, input, cmd, AF_IP4));
940 static clib_error_t *
941 ip6_neighbor_show_sorted (vlib_main_t * vm,
942 unformat_input_t * input, vlib_cli_command_t * cmd)
944 return (ip_neighbor_show_sorted_i (vm, input, cmd, AF_IP6));
947 static clib_error_t *
948 ip4_neighbor_show_sorted (vlib_main_t * vm,
949 unformat_input_t * input, vlib_cli_command_t * cmd)
951 return (ip_neighbor_show_sorted_i (vm, input, cmd, AF_IP4));
955 * Display all the IP neighbor entries.
958 * Example of how to display the IPv4 ARP table:
959 * @cliexstart{show ip neighbor}
960 * Time FIB IP4 Flags Ethernet Interface
961 * 346.3028 0 6.1.1.3 de:ad:be:ef:ba:be GigabitEthernet2/0/0
962 * 3077.4271 0 6.1.1.4 S de:ad:be:ef:ff:ff GigabitEthernet2/0/0
963 * 2998.6409 1 6.2.2.3 de:ad:be:ef:00:01 GigabitEthernet2/0/0
964 * Proxy arps enabled for:
965 * Fib_index 0 6.0.0.1 - 6.0.0.11
969 VLIB_CLI_COMMAND (show_ip_neighbors_cmd_node, static) = {
970 .path = "show ip neighbors",
971 .function = ip_neighbor_show,
972 .short_help = "show ip neighbors [interface]",
974 VLIB_CLI_COMMAND (show_ip4_neighbors_cmd_node, static) = {
975 .path = "show ip4 neighbors",
976 .function = ip4_neighbor_show,
977 .short_help = "show ip4 neighbors [interface]",
979 VLIB_CLI_COMMAND (show_ip6_neighbors_cmd_node, static) = {
980 .path = "show ip6 neighbors",
981 .function = ip6_neighbor_show,
982 .short_help = "show ip6 neighbors [interface]",
984 VLIB_CLI_COMMAND (show_ip_neighbor_cmd_node, static) = {
985 .path = "show ip neighbor",
986 .function = ip_neighbor_show,
987 .short_help = "show ip neighbor [interface]",
989 VLIB_CLI_COMMAND (show_ip4_neighbor_cmd_node, static) = {
990 .path = "show ip4 neighbor",
991 .function = ip4_neighbor_show,
992 .short_help = "show ip4 neighbor [interface]",
994 VLIB_CLI_COMMAND (show_ip6_neighbor_cmd_node, static) = {
995 .path = "show ip6 neighbor",
996 .function = ip6_neighbor_show,
997 .short_help = "show ip6 neighbor [interface]",
999 VLIB_CLI_COMMAND (show_ip4_neighbor_sorted_cmd_node, static) = {
1000 .path = "show ip4 neighbor-sorted",
1001 .function = ip4_neighbor_show_sorted,
1002 .short_help = "show ip4 neighbor-sorted",
1004 VLIB_CLI_COMMAND (show_ip6_neighbor_sorted_cmd_node, static) = {
1005 .path = "show ip6 neighbor-sorted",
1006 .function = ip6_neighbor_show_sorted,
1007 .short_help = "show ip6 neighbor-sorted",
1011 static ip_neighbor_vft_t ip_nbr_vfts[N_AF];
1014 ip_neighbor_register (ip_address_family_t af, const ip_neighbor_vft_t * vft)
1016 ip_nbr_vfts[af] = *vft;
1020 ip_neighbor_probe_dst (u32 sw_if_index,
1021 ip_address_family_t af, const ip46_address_t * dst)
1023 if (!vnet_sw_interface_is_admin_up (vnet_get_main (), sw_if_index))
1029 ip6_neighbor_probe_dst (sw_if_index, &dst->ip6);
1032 ip4_neighbor_probe_dst (sw_if_index, &dst->ip4);
1038 ip_neighbor_probe (const ip_adjacency_t * adj)
1040 ip_neighbor_probe_dst (adj->rewrite_header.sw_if_index,
1041 ip_address_family_from_fib_proto (adj->ia_nh_proto),
1042 &adj->sub_type.nbr.next_hop);
1046 ip_neighbor_walk (ip_address_family_t af,
1047 u32 sw_if_index, ip_neighbor_walk_cb_t cb, void *ctx)
1049 ip_neighbor_key_t *key;
1052 if (~0 == sw_if_index)
1056 vec_foreach (hash, ip_neighbor_db[af].ipndb_hash)
1059 hash_foreach (key, ipni, *hash,
1061 if (WALK_STOP == cb (ipni, ctx))
1071 if (vec_len (ip_neighbor_db[af].ipndb_hash) <= sw_if_index)
1073 hash = ip_neighbor_db[af].ipndb_hash[sw_if_index];
1076 hash_foreach (key, ipni, hash,
1078 if (WALK_STOP == cb (ipni, ctx))
1086 ip4_neighbor_proxy_add (u32 fib_index,
1087 const ip4_address_t * start,
1088 const ip4_address_t * end)
1090 if (ip_nbr_vfts[AF_IP4].inv_proxy4_add)
1092 return (ip_nbr_vfts[AF_IP4].inv_proxy4_add (fib_index, start, end));
1099 ip4_neighbor_proxy_delete (u32 fib_index,
1100 const ip4_address_t * start,
1101 const ip4_address_t * end)
1103 if (ip_nbr_vfts[AF_IP4].inv_proxy4_del)
1105 return (ip_nbr_vfts[AF_IP4].inv_proxy4_del (fib_index, start, end));
1111 ip4_neighbor_proxy_enable (u32 sw_if_index)
1113 if (ip_nbr_vfts[AF_IP4].inv_proxy4_enable)
1115 return (ip_nbr_vfts[AF_IP4].inv_proxy4_enable (sw_if_index));
1121 ip4_neighbor_proxy_disable (u32 sw_if_index)
1123 if (ip_nbr_vfts[AF_IP4].inv_proxy4_disable)
1125 return (ip_nbr_vfts[AF_IP4].inv_proxy4_disable (sw_if_index));
1131 ip6_neighbor_proxy_add (u32 sw_if_index, const ip6_address_t * addr)
1133 if (ip_nbr_vfts[AF_IP6].inv_proxy6_add)
1135 return (ip_nbr_vfts[AF_IP6].inv_proxy6_add (sw_if_index, addr));
1141 ip6_neighbor_proxy_del (u32 sw_if_index, const ip6_address_t * addr)
1143 if (ip_nbr_vfts[AF_IP6].inv_proxy6_del)
1145 return (ip_nbr_vfts[AF_IP6].inv_proxy6_del (sw_if_index, addr));
1151 ip_neighbor_populate (ip_address_family_t af, u32 sw_if_index)
1153 index_t *ipnis = NULL, *ipni;
1156 IP_NEIGHBOR_DBG ("populate: %U %U",
1157 format_vnet_sw_if_index_name, vnet_get_main (),
1158 sw_if_index, format_ip_address_family, af);
1161 pool_foreach (ipn, ip_neighbor_pool)
1163 if (ip_neighbor_get_af(ipn) == af &&
1164 ipn->ipn_key->ipnk_sw_if_index == sw_if_index)
1165 vec_add1 (ipnis, ipn - ip_neighbor_pool);
1169 vec_foreach (ipni, ipnis)
1171 ipn = ip_neighbor_get (*ipni);
1173 adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
1174 ip_address_family_to_fib_proto (ip_neighbor_get_af
1176 &ip_addr_46 (&ipn->ipn_key->ipnk_ip),
1177 ip_neighbor_mk_complete_walk, ipn);
1183 ip_neighbor_flush (ip_address_family_t af, u32 sw_if_index)
1185 index_t *ipnis = NULL, *ipni;
1189 IP_NEIGHBOR_DBG ("flush: %U %U",
1190 format_vnet_sw_if_index_name, vnet_get_main (),
1191 sw_if_index, format_ip_address_family, af);
1194 pool_foreach (ipn, ip_neighbor_pool)
1196 if (ip_neighbor_get_af(ipn) == af &&
1197 ipn->ipn_key->ipnk_sw_if_index == sw_if_index &&
1198 ip_neighbor_is_dynamic (ipn))
1199 vec_add1 (ipnis, ipn - ip_neighbor_pool);
1203 vec_foreach (ipni, ipnis) ip_neighbor_destroy (ip_neighbor_get (*ipni));
1208 ip_neighbor_mark_one (index_t ipni, void *ctx)
1212 ipn = ip_neighbor_get (ipni);
1214 ipn->ipn_flags |= IP_NEIGHBOR_FLAG_STALE;
1216 return (WALK_CONTINUE);
1220 ip_neighbor_mark (ip_address_family_t af)
1222 ip_neighbor_walk (af, ~0, ip_neighbor_mark_one, NULL);
1225 typedef struct ip_neighbor_sweep_ctx_t_
1227 index_t *ipnsc_stale;
1228 } ip_neighbor_sweep_ctx_t;
1231 ip_neighbor_sweep_one (index_t ipni, void *arg)
1233 ip_neighbor_sweep_ctx_t *ctx = arg;
1236 ipn = ip_neighbor_get (ipni);
1238 if (ipn->ipn_flags & IP_NEIGHBOR_FLAG_STALE)
1240 vec_add1 (ctx->ipnsc_stale, ipni);
1243 return (WALK_CONTINUE);
1247 ip_neighbor_sweep (ip_address_family_t af)
1249 ip_neighbor_sweep_ctx_t ctx = { };
1252 ip_neighbor_walk (af, ~0, ip_neighbor_sweep_one, &ctx);
1254 vec_foreach (ipni, ctx.ipnsc_stale)
1256 ip_neighbor_destroy (ip_neighbor_get (*ipni));
1258 vec_free (ctx.ipnsc_stale);
1262 * Remove any arp entries associated with the specified interface
1264 static clib_error_t *
1265 ip_neighbor_interface_admin_change (vnet_main_t * vnm,
1266 u32 sw_if_index, u32 flags)
1268 ip_address_family_t af;
1270 IP_NEIGHBOR_DBG ("interface-admin: %U %s",
1271 format_vnet_sw_if_index_name, vnet_get_main (),
1273 (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ? "up" : "down"));
1275 if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
1277 FOR_EACH_IP_ADDRESS_FAMILY (af) ip_neighbor_populate (af, sw_if_index);
1281 /* admin down, flush all neighbours */
1282 FOR_EACH_IP_ADDRESS_FAMILY (af) ip_neighbor_flush (af, sw_if_index);
1288 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ip_neighbor_interface_admin_change);
1291 * Remove any arp entries associated with the specified interface
1293 static clib_error_t *
1294 ip_neighbor_delete_sw_interface (vnet_main_t * vnm,
1295 u32 sw_if_index, u32 is_add)
1297 IP_NEIGHBOR_DBG ("interface-change: %U %s",
1298 format_vnet_sw_if_index_name, vnet_get_main (),
1299 sw_if_index, (is_add ? "add" : "del"));
1301 if (!is_add && sw_if_index != ~0)
1303 ip_address_family_t af;
1305 FOR_EACH_IP_ADDRESS_FAMILY (af) ip_neighbor_flush (af, sw_if_index);
1311 VNET_SW_INTERFACE_ADD_DEL_FUNCTION (ip_neighbor_delete_sw_interface);
1313 typedef struct ip_neighbor_walk_covered_ctx_t_
1318 } ip_neighbor_walk_covered_ctx_t;
1321 ip_neighbor_walk_covered (index_t ipni, void *arg)
1323 ip_neighbor_walk_covered_ctx_t *ctx = arg;
1326 ipn = ip_neighbor_get (ipni);
1328 if (AF_IP4 == ip_addr_version (&ctx->addr))
1330 if (ip4_destination_matches_route (&ip4_main,
1331 &ip_addr_v4 (&ipn->ipn_key->ipnk_ip),
1332 &ip_addr_v4 (&ctx->addr),
1334 ip_neighbor_is_dynamic (ipn))
1336 vec_add1 (ctx->ipnis, ip_neighbor_get_index (ipn));
1339 else if (AF_IP6 == ip_addr_version (&ctx->addr))
1341 if (ip6_destination_matches_route (&ip6_main,
1342 &ip_addr_v6 (&ipn->ipn_key->ipnk_ip),
1343 &ip_addr_v6 (&ctx->addr),
1345 ip_neighbor_is_dynamic (ipn))
1347 vec_add1 (ctx->ipnis, ip_neighbor_get_index (ipn));
1350 return (WALK_CONTINUE);
1355 * callback when an interface address is added or deleted
1358 ip_neighbor_add_del_interface_address_v4 (ip4_main_t * im,
1361 ip4_address_t * address,
1363 u32 if_address_index, u32 is_del)
1366 * Flush the ARP cache of all entries covered by the address
1367 * that is being removed.
1369 IP_NEIGHBOR_DBG ("addr-%d: %U, %U/%d",
1370 (is_del ? "del" : "add"),
1371 format_vnet_sw_if_index_name, vnet_get_main (),
1372 sw_if_index, format_ip4_address, address, address_length);
1377 ip_neighbor_walk_covered_ctx_t ctx = {
1382 .length = address_length,
1387 ip_neighbor_walk (AF_IP4, sw_if_index, ip_neighbor_walk_covered, &ctx);
1389 vec_foreach (ipni, ctx.ipnis)
1390 ip_neighbor_destroy (ip_neighbor_get (*ipni));
1392 vec_free (ctx.ipnis);
1397 * callback when an interface address is added or deleted
1400 ip_neighbor_add_del_interface_address_v6 (ip6_main_t * im,
1403 ip6_address_t * address,
1405 u32 if_address_index, u32 is_del)
1408 * Flush the ARP cache of all entries covered by the address
1409 * that is being removed.
1411 IP_NEIGHBOR_DBG ("addr-change: %U, %U/%d %s",
1412 format_vnet_sw_if_index_name, vnet_get_main (),
1413 sw_if_index, format_ip6_address, address, address_length,
1414 (is_del ? "del" : "add"));
1419 ip_neighbor_walk_covered_ctx_t ctx = {
1424 .length = address_length,
1429 ip_neighbor_walk (AF_IP6, sw_if_index, ip_neighbor_walk_covered, &ctx);
1431 vec_foreach (ipni, ctx.ipnis)
1432 ip_neighbor_destroy (ip_neighbor_get (*ipni));
1434 vec_free (ctx.ipnis);
1438 typedef struct ip_neighbor_table_bind_ctx_t_
1442 } ip_neighbor_table_bind_ctx_t;
1445 ip_neighbor_walk_table_bind (index_t ipni, void *arg)
1447 ip_neighbor_table_bind_ctx_t *ctx = arg;
1450 ipn = ip_neighbor_get (ipni);
1451 ip_neighbor_adj_fib_remove (ipn, ctx->old_fib_index);
1452 ip_neighbor_adj_fib_add (ipn, ctx->new_fib_index);
1454 return (WALK_CONTINUE);
1458 ip_neighbor_table_bind_v4 (ip4_main_t * im,
1461 u32 new_fib_index, u32 old_fib_index)
1463 ip_neighbor_table_bind_ctx_t ctx = {
1464 .old_fib_index = old_fib_index,
1465 .new_fib_index = new_fib_index,
1468 ip_neighbor_walk (AF_IP4, sw_if_index, ip_neighbor_walk_table_bind, &ctx);
1472 ip_neighbor_table_bind_v6 (ip6_main_t * im,
1475 u32 new_fib_index, u32 old_fib_index)
1477 ip_neighbor_table_bind_ctx_t ctx = {
1478 .old_fib_index = old_fib_index,
1479 .new_fib_index = new_fib_index,
1482 ip_neighbor_walk (AF_IP6, sw_if_index, ip_neighbor_walk_table_bind, &ctx);
1485 typedef enum ip_neighbor_age_state_t_
1487 IP_NEIGHBOR_AGE_ALIVE,
1488 IP_NEIGHBOR_AGE_PROBE,
1489 IP_NEIGHBOR_AGE_DEAD,
1490 } ip_neighbor_age_state_t;
1492 #define IP_NEIGHBOR_PROCESS_SLEEP_LONG (0)
1494 static ip_neighbor_age_state_t
1495 ip_neighbour_age_out (index_t ipni, f64 now, f64 * wait)
1497 ip_address_family_t af;
1502 ipn = ip_neighbor_get (ipni);
1503 af = ip_neighbor_get_af (ipn);
1504 ipndb_age = ip_neighbor_db[af].ipndb_age;
1505 ttl = now - ipn->ipn_time_last_updated;
1508 if (ttl > ipndb_age)
1510 IP_NEIGHBOR_DBG ("aged: %U @%f - %f > %d",
1511 format_ip_neighbor, ipni, now,
1512 ipn->ipn_time_last_updated, ipndb_age);
1513 if (ipn->ipn_n_probes > 2)
1515 /* 3 strikes and yea-re out */
1516 IP_NEIGHBOR_DBG ("dead: %U", format_ip_neighbor, ipni);
1518 return (IP_NEIGHBOR_AGE_DEAD);
1522 ip_neighbor_probe_dst (ip_neighbor_get_sw_if_index (ipn),
1523 af, &ip_addr_46 (&ipn->ipn_key->ipnk_ip));
1525 ipn->ipn_n_probes++;
1531 /* here we are sure that ttl <= ipndb_age */
1532 *wait = ipndb_age - ttl + 1;
1533 return (IP_NEIGHBOR_AGE_ALIVE);
1536 return (IP_NEIGHBOR_AGE_PROBE);
1539 typedef enum ip_neighbor_process_event_t_
1541 IP_NEIGHBOR_AGE_PROCESS_WAKEUP,
1542 } ip_neighbor_process_event_t;
1545 ip_neighbor_age_loop (vlib_main_t * vm,
1546 vlib_node_runtime_t * rt,
1547 vlib_frame_t * f, ip_address_family_t af)
1549 uword event_type, *event_data = NULL;
1552 /* Set the timeout to an effectively infinite value when the process starts */
1553 timeout = IP_NEIGHBOR_PROCESS_SLEEP_LONG;
1560 vlib_process_wait_for_event (vm);
1562 vlib_process_wait_for_event_or_clock (vm, timeout);
1564 event_type = vlib_process_get_events (vm, &event_data);
1565 vec_reset_length (event_data);
1567 now = vlib_time_now (vm);
1574 ip_neighbor_elt_t *elt, *head;
1577 timeout = ip_neighbor_db[af].ipndb_age;
1578 head = pool_elt_at_index (ip_neighbor_elt_pool,
1579 ip_neighbor_list_head[af]);
1582 /* the list is time sorted, newest first, so start from the back
1583 * and work forwards. Stop when we get to one that is alive */
1585 clib_llist_foreach_reverse(ip_neighbor_elt_pool,
1586 ipne_anchor, head, elt,
1588 ip_neighbor_age_state_t res;
1590 res = ip_neighbour_age_out(elt->ipne_index, now, &wait);
1592 if (IP_NEIGHBOR_AGE_ALIVE == res) {
1593 /* the oldest neighbor has not yet expired, go back to sleep */
1594 timeout = clib_min (wait, timeout);
1597 else if (IP_NEIGHBOR_AGE_DEAD == res) {
1598 /* the oldest neighbor is dead, pop it, then restart the walk
1599 * again from the back */
1600 ip_neighbor_destroy (ip_neighbor_get(elt->ipne_index));
1604 timeout = clib_min (wait, timeout);
1609 case IP_NEIGHBOR_AGE_PROCESS_WAKEUP:
1612 if (!ip_neighbor_db[af].ipndb_age)
1614 /* aging has been disabled */
1618 ip_neighbor_elt_t *elt, *head;
1620 head = pool_elt_at_index (ip_neighbor_elt_pool,
1621 ip_neighbor_list_head[af]);
1622 /* no neighbors yet */
1623 if (clib_llist_is_empty (ip_neighbor_elt_pool, ipne_anchor, head))
1625 timeout = ip_neighbor_db[af].ipndb_age;
1629 /* poke the oldset neighbour for aging, which returns how long we sleep for */
1630 elt = clib_llist_prev (ip_neighbor_elt_pool, ipne_anchor, head);
1631 ip_neighbour_age_out (elt->ipne_index, now, &timeout);
1640 ip4_neighbor_age_process (vlib_main_t * vm,
1641 vlib_node_runtime_t * rt, vlib_frame_t * f)
1643 return (ip_neighbor_age_loop (vm, rt, f, AF_IP4));
1647 ip6_neighbor_age_process (vlib_main_t * vm,
1648 vlib_node_runtime_t * rt, vlib_frame_t * f)
1650 return (ip_neighbor_age_loop (vm, rt, f, AF_IP6));
1654 VLIB_REGISTER_NODE (ip4_neighbor_age_process_node,static) = {
1655 .function = ip4_neighbor_age_process,
1656 .type = VLIB_NODE_TYPE_PROCESS,
1657 .name = "ip4-neighbor-age-process",
1659 VLIB_REGISTER_NODE (ip6_neighbor_age_process_node,static) = {
1660 .function = ip6_neighbor_age_process,
1661 .type = VLIB_NODE_TYPE_PROCESS,
1662 .name = "ip6-neighbor-age-process",
1667 ip_neighbor_config (ip_address_family_t af, u32 limit, u32 age, bool recycle)
1669 ip_neighbor_db[af].ipndb_limit = limit;
1670 ip_neighbor_db[af].ipndb_recycle = recycle;
1671 ip_neighbor_db[af].ipndb_age = age;
1673 vlib_process_signal_event (vlib_get_main (),
1675 ip4_neighbor_age_process_node.index :
1676 ip6_neighbor_age_process_node.index),
1677 IP_NEIGHBOR_AGE_PROCESS_WAKEUP, 0);
1682 static clib_error_t *
1683 ip_neighbor_config_show (vlib_main_t * vm,
1684 unformat_input_t * input, vlib_cli_command_t * cmd)
1686 ip_address_family_t af;
1689 FOR_EACH_IP_ADDRESS_FAMILY(af) {
1690 vlib_cli_output (vm, "%U:", format_ip_address_family, af);
1691 vlib_cli_output (vm, " limit:%d, age:%d, recycle:%d",
1692 ip_neighbor_db[af].ipndb_limit,
1693 ip_neighbor_db[af].ipndb_age,
1694 ip_neighbor_db[af].ipndb_recycle);
1701 static clib_error_t *
1702 ip_neighbor_config_set (vlib_main_t *vm, unformat_input_t *input,
1703 vlib_cli_command_t *cmd)
1705 unformat_input_t _line_input, *line_input = &_line_input;
1706 clib_error_t *error = NULL;
1707 ip_address_family_t af;
1711 if (!unformat_user (input, unformat_line_input, line_input))
1714 if (!unformat (line_input, "%U", unformat_ip_address_family, &af))
1716 error = unformat_parse_error (line_input);
1720 limit = ip_neighbor_db[af].ipndb_limit;
1721 age = ip_neighbor_db[af].ipndb_age;
1722 recycle = ip_neighbor_db[af].ipndb_recycle;
1724 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1726 if (unformat (line_input, "limit %u", &limit))
1728 else if (unformat (line_input, "age %u", &age))
1730 else if (unformat (line_input, "recycle"))
1732 else if (unformat (line_input, "norecycle"))
1736 error = unformat_parse_error (line_input);
1741 ip_neighbor_config (af, limit, age, recycle);
1744 unformat_free (line_input);
1749 VLIB_CLI_COMMAND (show_ip_neighbor_cfg_cmd_node, static) = {
1750 .path = "show ip neighbor-config",
1751 .function = ip_neighbor_config_show,
1752 .short_help = "show ip neighbor-config",
1754 VLIB_CLI_COMMAND (set_ip_neighbor_cfg_cmd_node, static) = {
1755 .path = "set ip neighbor-config",
1756 .function = ip_neighbor_config_set,
1757 .short_help = "set ip neighbor-config ip4|ip6 [limit <limit>] [age <age>] "
1758 "[recycle|norecycle]",
1762 static clib_error_t *
1763 ip_neighbor_init (vlib_main_t * vm)
1766 ip4_add_del_interface_address_callback_t cb = {
1767 .function = ip_neighbor_add_del_interface_address_v4,
1769 vec_add1 (ip4_main.add_del_interface_address_callbacks, cb);
1772 ip6_add_del_interface_address_callback_t cb = {
1773 .function = ip_neighbor_add_del_interface_address_v6,
1775 vec_add1 (ip6_main.add_del_interface_address_callbacks, cb);
1778 ip4_table_bind_callback_t cb = {
1779 .function = ip_neighbor_table_bind_v4,
1781 vec_add1 (ip4_main.table_bind_callbacks, cb);
1784 ip6_table_bind_callback_t cb = {
1785 .function = ip_neighbor_table_bind_v6,
1787 vec_add1 (ip6_main.table_bind_callbacks, cb);
1789 ipn_logger = vlib_log_register_class ("ip", "neighbor");
1791 ip_address_family_t af;
1793 FOR_EACH_IP_ADDRESS_FAMILY (af)
1794 ip_neighbor_list_head[af] =
1795 clib_llist_make_head (ip_neighbor_elt_pool, ipne_anchor);
1801 VLIB_INIT_FUNCTION (ip_neighbor_init) =
1803 .runs_after = VLIB_INITS("ip_main_init"),
1808 * fd.io coding-style-patch-verification: ON
1811 * eval: (c-set-style "gnu")