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_is_dynamic (const ip_neighbor_t * ipn)
105 return (ipn->ipn_flags & IP_NEIGHBOR_FLAG_DYNAMIC);
108 const ip46_address_t *
109 ip_neighbor_get_ip (const ip_neighbor_t * ipn)
111 return (&ipn->ipn_key->ipnk_ip);
114 const mac_address_t *
115 ip_neighbor_get_mac (const ip_neighbor_t * ipn)
117 return (&ipn->ipn_mac);
121 ip_neighbor_get_sw_if_index (const ip_neighbor_t * ipn)
123 return (ipn->ipn_key->ipnk_sw_if_index);
127 ip_neighbor_list_remove (ip_neighbor_t * ipn)
129 /* new neighbours, are added to the head of the list, since the
130 * list is time sorted, newest first */
131 ip_neighbor_elt_t *elt;
133 if (~0 != ipn->ipn_elt)
135 elt = pool_elt_at_index (ip_neighbor_elt_pool, ipn->ipn_elt);
137 clib_llist_remove (ip_neighbor_elt_pool, ipne_anchor, elt);
142 ip_neighbor_refresh (ip_neighbor_t * ipn)
144 /* new neighbours, are added to the head of the list, since the
145 * list is time sorted, newest first */
146 ip_neighbor_elt_t *elt, *head;
148 ipn->ipn_time_last_updated = vlib_time_now (vlib_get_main ());
149 ipn->ipn_n_probes = 0;
151 if (ip_neighbor_is_dynamic (ipn))
153 if (~0 == ipn->ipn_elt)
154 /* first time insertion */
155 pool_get_zero (ip_neighbor_elt_pool, elt);
158 /* already inserted - extract first */
159 elt = pool_elt_at_index (ip_neighbor_elt_pool, ipn->ipn_elt);
161 clib_llist_remove (ip_neighbor_elt_pool, ipne_anchor, elt);
163 head = pool_elt_at_index (ip_neighbor_elt_pool,
164 ip_neighbor_list_head[ipn->
165 ipn_key->ipnk_type]);
167 elt->ipne_index = ip_neighbor_get_index (ipn);
168 clib_llist_add (ip_neighbor_elt_pool, ipne_anchor, elt, head);
169 ipn->ipn_elt = elt - ip_neighbor_elt_pool;
174 ip_neighbor_db_add (const ip_neighbor_t * ipn)
176 vec_validate (ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_hash,
177 ipn->ipn_key->ipnk_sw_if_index);
179 if (!ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_hash
180 [ipn->ipn_key->ipnk_sw_if_index])
181 ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_hash[ipn->
182 ipn_key->ipnk_sw_if_index]
183 = hash_create_mem (0, sizeof (ip_neighbor_key_t), sizeof (index_t));
185 hash_set_mem (ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_hash
186 [ipn->ipn_key->ipnk_sw_if_index], ipn->ipn_key,
187 ip_neighbor_get_index (ipn));
189 ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_n_elts++;
193 ip_neighbor_db_remove (const ip_neighbor_key_t * key)
195 vec_validate (ip_neighbor_db[key->ipnk_type].ipndb_hash,
196 key->ipnk_sw_if_index);
198 hash_unset_mem (ip_neighbor_db[key->ipnk_type].ipndb_hash
199 [key->ipnk_sw_if_index], key);
201 ip_neighbor_db[key->ipnk_type].ipndb_n_elts--;
204 static ip_neighbor_t *
205 ip_neighbor_db_find (const ip_neighbor_key_t * key)
209 if (key->ipnk_sw_if_index >=
210 vec_len (ip_neighbor_db[key->ipnk_type].ipndb_hash))
214 hash_get_mem (ip_neighbor_db[key->ipnk_type].ipndb_hash
215 [key->ipnk_sw_if_index], key);
218 return ip_neighbor_get (p[0]);
224 ip46_type_pfx_len (ip46_type_t type)
226 return (type == IP46_TYPE_IP4 ? 32 : 128);
230 ip_neighbor_adj_fib_add (ip_neighbor_t * ipn, u32 fib_index)
232 if (ipn->ipn_key->ipnk_type == IP46_TYPE_IP6 &&
233 ip6_address_is_link_local_unicast (&ipn->ipn_key->ipnk_ip.ip6))
235 ip6_ll_prefix_t pfx = {
236 .ilp_addr = ipn->ipn_key->ipnk_ip.ip6,
237 .ilp_sw_if_index = ipn->ipn_key->ipnk_sw_if_index,
239 ipn->ipn_fib_entry_index =
240 ip6_ll_table_entry_update (&pfx, FIB_ROUTE_PATH_FLAG_NONE);
244 fib_protocol_t fproto;
246 fproto = fib_proto_from_ip46 (ipn->ipn_key->ipnk_type);
249 .fp_len = ip46_type_pfx_len (ipn->ipn_key->ipnk_type),
251 .fp_addr = ipn->ipn_key->ipnk_ip,
254 ipn->ipn_fib_entry_index =
255 fib_table_entry_path_add (fib_index, &pfx, FIB_SOURCE_ADJ,
256 FIB_ENTRY_FLAG_ATTACHED,
257 fib_proto_to_dpo (fproto),
259 ipn->ipn_key->ipnk_sw_if_index,
260 ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
262 vec_validate (ip_neighbor_db
263 [ipn->ipn_key->ipnk_type].ipndb_n_elts_per_fib,
266 ip_neighbor_db[ipn->ipn_key->
267 ipnk_type].ipndb_n_elts_per_fib[fib_index]++;
270 ip_neighbor_db[ipn->ipn_key->
271 ipnk_type].ipndb_n_elts_per_fib[fib_index])
272 fib_table_lock (fib_index, fproto, FIB_SOURCE_ADJ);
277 ip_neighbor_adj_fib_remove (ip_neighbor_t * ipn, u32 fib_index)
279 if (FIB_NODE_INDEX_INVALID != ipn->ipn_fib_entry_index)
281 if (ipn->ipn_key->ipnk_type == IP46_TYPE_IP6 &&
282 ip6_address_is_link_local_unicast (&ipn->ipn_key->ipnk_ip.ip6))
284 ip6_ll_prefix_t pfx = {
285 .ilp_addr = ipn->ipn_key->ipnk_ip.ip6,
286 .ilp_sw_if_index = ipn->ipn_key->ipnk_sw_if_index,
288 ip6_ll_table_entry_delete (&pfx);
292 fib_protocol_t fproto;
294 fproto = fib_proto_from_ip46 (ipn->ipn_key->ipnk_type);
297 .fp_len = ip46_type_pfx_len (ipn->ipn_key->ipnk_type),
299 .fp_addr = ipn->ipn_key->ipnk_ip,
302 fib_table_entry_path_remove (fib_index,
305 fib_proto_to_dpo (fproto),
307 ipn->ipn_key->ipnk_sw_if_index,
308 ~0, 1, FIB_ROUTE_PATH_FLAG_NONE);
310 ip_neighbor_db[ipn->ipn_key->
311 ipnk_type].ipndb_n_elts_per_fib[fib_index]--;
314 ip_neighbor_db[ipn->ipn_key->
315 ipnk_type].ipndb_n_elts_per_fib[fib_index])
316 fib_table_unlock (fib_index, fproto, FIB_SOURCE_ADJ);
322 ip_neighbor_mk_complete (adj_index_t ai, ip_neighbor_t * ipn)
324 adj_nbr_update_rewrite (ai, ADJ_NBR_REWRITE_FLAG_COMPLETE,
325 ethernet_build_rewrite (vnet_get_main (),
327 ipn_key->ipnk_sw_if_index,
328 adj_get_link_type (ai),
329 ipn->ipn_mac.bytes));
333 ip_neighbor_mk_incomplete (adj_index_t ai)
335 ip_adjacency_t *adj = adj_get (ai);
337 adj_nbr_update_rewrite (ai,
338 ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
339 ethernet_build_rewrite (vnet_get_main (),
341 rewrite_header.sw_if_index,
342 adj_get_link_type (ai),
343 VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
347 ip_neighbor_mk_complete_walk (adj_index_t ai, void *ctx)
349 ip_neighbor_t *ipn = ctx;
351 ip_neighbor_mk_complete (ai, ipn);
353 return (ADJ_WALK_RC_CONTINUE);
357 ip_neighbor_mk_incomplete_walk (adj_index_t ai, void *ctx)
359 ip_neighbor_mk_incomplete (ai);
361 return (ADJ_WALK_RC_CONTINUE);
365 ip_neighbor_free (ip_neighbor_t * ipn)
367 IP_NEIGHBOR_DBG ("free: %U", format_ip_neighbor,
368 ip_neighbor_get_index (ipn));
370 adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
371 fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
372 &ipn->ipn_key->ipnk_ip,
373 ip_neighbor_mk_incomplete_walk, ipn);
374 ip_neighbor_adj_fib_remove
376 fib_table_get_index_for_sw_if_index
377 (fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
378 ipn->ipn_key->ipnk_sw_if_index));
380 ip_neighbor_list_remove (ipn);
381 ip_neighbor_db_remove (ipn->ipn_key);
382 clib_mem_free (ipn->ipn_key);
384 pool_put (ip_neighbor_pool, ipn);
388 ip_neighbor_force_reuse (ip46_type_t type)
390 if (!ip_neighbor_db[type].ipndb_recycle)
393 /* pluck the oldest entry, which is the one from the end of the list */
394 ip_neighbor_elt_t *elt, *head;
397 pool_elt_at_index (ip_neighbor_elt_pool, ip_neighbor_list_head[type]);
399 if (clib_llist_is_empty (ip_neighbor_elt_pool, ipne_anchor, head))
402 elt = clib_llist_prev (ip_neighbor_elt_pool, ipne_anchor, head);
403 ip_neighbor_free (ip_neighbor_get (elt->ipne_index));
408 static ip_neighbor_t *
409 ip_neighbor_alloc (const ip_neighbor_key_t * key,
410 const mac_address_t * mac, ip_neighbor_flags_t flags)
414 if (ip_neighbor_db[key->ipnk_type].ipndb_limit &&
415 (ip_neighbor_db[key->ipnk_type].ipndb_n_elts >=
416 ip_neighbor_db[key->ipnk_type].ipndb_limit))
418 if (!ip_neighbor_force_reuse (key->ipnk_type))
422 pool_get_zero (ip_neighbor_pool, ipn);
424 ipn->ipn_key = clib_mem_alloc (sizeof (*ipn->ipn_key));
425 clib_memcpy (ipn->ipn_key, key, sizeof (*ipn->ipn_key));
427 ipn->ipn_fib_entry_index = FIB_NODE_INDEX_INVALID;
428 ipn->ipn_flags = flags;
431 mac_address_copy (&ipn->ipn_mac, mac);
433 ip_neighbor_db_add (ipn);
435 /* create the adj-fib. the entry in the FIB table for the peer's interface */
436 if (!(ipn->ipn_flags & IP_NEIGHBOR_FLAG_NO_FIB_ENTRY))
437 ip_neighbor_adj_fib_add
438 (ipn, fib_table_get_index_for_sw_if_index
439 (fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
440 ipn->ipn_key->ipnk_sw_if_index));
446 ip_neighbor_add (const ip46_address_t * ip,
448 const mac_address_t * mac,
450 ip_neighbor_flags_t flags, u32 * stats_index)
452 fib_protocol_t fproto;
455 /* main thread only */
456 ASSERT (0 == vlib_get_thread_index ());
458 fproto = fib_proto_from_ip46 (type);
460 const ip_neighbor_key_t key = {
462 .ipnk_sw_if_index = sw_if_index,
466 ipn = ip_neighbor_db_find (&key);
470 IP_NEIGHBOR_DBG ("update: %U, %U",
471 format_vnet_sw_if_index_name, vnet_get_main (),
472 sw_if_index, format_ip46_address, ip, type,
473 format_ip_neighbor_flags, flags, format_mac_address_t,
476 /* Refuse to over-write static neighbor entry. */
477 if (!(flags & IP_NEIGHBOR_FLAG_STATIC) &&
478 (ipn->ipn_flags & IP_NEIGHBOR_FLAG_STATIC))
480 /* if MAC address match, still check to send event */
481 if (0 == mac_address_cmp (&ipn->ipn_mac, mac))
482 goto check_customers;
487 * prevent a DoS attack from the data-plane that
488 * spams us with no-op updates to the MAC address
490 if (0 == mac_address_cmp (&ipn->ipn_mac, mac))
492 ip_neighbor_refresh (ipn);
493 goto check_customers;
496 mac_address_copy (&ipn->ipn_mac, mac);
498 /* A dynamic entry can become static, but not vice-versa.
499 * i.e. since if it was programmed by the CP then it must
500 * be removed by the CP */
501 if ((flags & IP_NEIGHBOR_FLAG_STATIC) &&
502 !(ipn->ipn_flags & IP_NEIGHBOR_FLAG_STATIC))
504 ip_neighbor_list_remove (ipn);
505 ipn->ipn_flags |= IP_NEIGHBOR_FLAG_STATIC;
506 ipn->ipn_flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC;
511 IP_NEIGHBOR_INFO ("add: %U, %U",
512 format_vnet_sw_if_index_name, vnet_get_main (),
513 sw_if_index, format_ip46_address, ip, type,
514 format_ip_neighbor_flags, flags, format_mac_address_t,
517 ipn = ip_neighbor_alloc (&key, mac, flags);
520 return VNET_API_ERROR_LIMIT_EXCEEDED;
523 /* Update time stamp and flags. */
524 ip_neighbor_refresh (ipn);
526 adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
527 fproto, &ipn->ipn_key->ipnk_ip,
528 ip_neighbor_mk_complete_walk, ipn);
531 /* Customer(s) requesting event for this address? */
532 ip_neighbor_publish (ip_neighbor_get_index (ipn));
535 *stats_index = adj_nbr_find (fproto,
536 fib_proto_to_link (fproto),
537 &ipn->ipn_key->ipnk_ip,
538 ipn->ipn_key->ipnk_sw_if_index);
543 ip_neighbor_del (const ip46_address_t * ip, ip46_type_t type, u32 sw_if_index)
547 /* main thread only */
548 ASSERT (0 == vlib_get_thread_index ());
550 IP_NEIGHBOR_INFO ("delete: %U, %U",
551 format_vnet_sw_if_index_name, vnet_get_main (),
552 sw_if_index, format_ip46_address, ip, type);
554 const ip_neighbor_key_t key = {
556 .ipnk_sw_if_index = sw_if_index,
560 ipn = ip_neighbor_db_find (&key);
563 return (VNET_API_ERROR_NO_SUCH_ENTRY);
565 ip_neighbor_free (ipn);
571 ip_neighbor_update (vnet_main_t * vnm, adj_index_t ai)
578 ip_neighbor_key_t key = {
579 .ipnk_ip = adj->sub_type.nbr.next_hop,
580 .ipnk_type = fib_proto_to_ip46 (adj->ia_nh_proto),
581 .ipnk_sw_if_index = adj->rewrite_header.sw_if_index,
583 ipn = ip_neighbor_db_find (&key);
585 switch (adj->lookup_next_index)
587 case IP_LOOKUP_NEXT_ARP:
590 adj_nbr_walk_nh (adj->rewrite_header.sw_if_index,
592 &ipn->ipn_key->ipnk_ip,
593 ip_neighbor_mk_complete_walk, ipn);
598 * no matching ARP entry.
599 * construct the rewrite required to for an ARP packet, and stick
600 * that in the adj's pipe to smoke.
602 adj_nbr_update_rewrite
604 ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
605 ethernet_build_rewrite
607 adj->rewrite_header.sw_if_index,
609 VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
612 * since the FIB has added this adj for a route, it makes sense it
613 * may want to forward traffic sometime soon. Let's send a
614 * speculative ARP. just one. If we were to do periodically that
615 * wouldn't be bad either, but that's more code than i'm prepared to
616 * write at this time for relatively little reward.
618 ip_neighbor_probe (adj);
621 case IP_LOOKUP_NEXT_GLEAN:
622 case IP_LOOKUP_NEXT_BCAST:
623 case IP_LOOKUP_NEXT_MCAST:
624 case IP_LOOKUP_NEXT_DROP:
625 case IP_LOOKUP_NEXT_PUNT:
626 case IP_LOOKUP_NEXT_LOCAL:
627 case IP_LOOKUP_NEXT_REWRITE:
628 case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
629 case IP_LOOKUP_NEXT_MIDCHAIN:
630 case IP_LOOKUP_NEXT_ICMP_ERROR:
631 case IP_LOOKUP_N_NEXT:
638 ip_neighbor_learn (const ip_neighbor_learn_t * l)
640 ip_neighbor_add (&l->ip, l->type, &l->mac, l->sw_if_index,
641 IP_NEIGHBOR_FLAG_DYNAMIC, NULL);
644 static clib_error_t *
645 ip_neighbor_cmd (vlib_main_t * vm,
646 unformat_input_t * input, vlib_cli_command_t * cmd)
648 ip46_address_t ip = ip46_address_initializer;
649 mac_address_t mac = ZERO_MAC_ADDRESS;
650 vnet_main_t *vnm = vnet_get_main ();
651 ip_neighbor_flags_t flags;
652 u32 sw_if_index = ~0;
656 flags = IP_NEIGHBOR_FLAG_DYNAMIC;
658 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
660 /* set ip arp TenGigE1/1/0/1 1.2.3.4 aa:bb:... or aabb.ccdd... */
661 if (unformat (input, "%U %U %U",
662 unformat_vnet_sw_interface, vnm, &sw_if_index,
663 unformat_ip46_address, &ip, IP46_TYPE_ANY,
664 unformat_mac_address_t, &mac))
666 else if (unformat (input, "delete") || unformat (input, "del"))
668 else if (unformat (input, "static"))
670 flags |= IP_NEIGHBOR_FLAG_STATIC;
671 flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC;
673 else if (unformat (input, "no-fib-entry"))
674 flags |= IP_NEIGHBOR_FLAG_NO_FIB_ENTRY;
675 else if (unformat (input, "count %d", &count))
681 if (sw_if_index == ~0 ||
682 ip46_address_is_zero (&ip) || mac_address_is_zero (&mac))
683 return clib_error_return (0,
684 "specify interface, IP address and MAC: `%U'",
685 format_unformat_error, input);
690 ip_neighbor_add (&ip, ip46_address_get_type (&ip), &mac, sw_if_index,
693 ip_neighbor_del (&ip, ip46_address_get_type (&ip), sw_if_index);
695 ip46_address_increment (ip46_address_get_type (&ip), &ip);
696 mac_address_increment (&mac);
706 * Add or delete IPv4 ARP cache entries.
708 * @note 'set ip neighbor' options (e.g. delete, static, 'fib-id <id>',
709 * 'count <number>', 'interface ip4_addr mac_addr') can be added in
710 * any order and combination.
714 * Add or delete IPv4 ARP cache entries as follows. MAC Address can be in
715 * either aa:bb:cc:dd:ee:ff format or aabb.ccdd.eeff format.
716 * @cliexcmd{set ip neighbor GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
717 * @cliexcmd{set ip neighbor delete GigabitEthernet2/0/0 6.0.0.3 de:ad:be:ef:ba:be}
719 * To add or delete an IPv4 ARP cache entry to or from a specific fib
721 * @cliexcmd{set ip neighbor fib-id 1 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
722 * @cliexcmd{set ip neighbor fib-id 1 delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
724 * Add or delete IPv4 static ARP cache entries as follows:
725 * @cliexcmd{set ip neighbor static GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
726 * @cliexcmd{set ip neighbor static delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
728 * For testing / debugging purposes, the 'set ip neighbor' command can add or
729 * delete multiple entries. Supply the 'count N' parameter:
730 * @cliexcmd{set ip neighbor count 10 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
733 VLIB_CLI_COMMAND (ip_neighbor_command, static) = {
734 .path = "set ip neighbor",
736 "set ip neighbor [del] <intfc> <ip-address> <mac-address> [static] [no-fib-entry] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
737 .function = ip_neighbor_cmd,
739 VLIB_CLI_COMMAND (ip_neighbor_command2, static) = {
740 .path = "ip neighbor",
742 "ip neighbor [del] <intfc> <ip-address> <mac-address> [static] [no-fib-entry] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
743 .function = ip_neighbor_cmd,
748 ip_neighbor_sort (void *a1, void *a2)
750 index_t *ipni1 = a1, *ipni2 = a2;
751 ip_neighbor_t *ipn1, *ipn2;
754 ipn1 = ip_neighbor_get (*ipni1);
755 ipn2 = ip_neighbor_get (*ipni2);
757 cmp = vnet_sw_interface_compare (vnet_get_main (),
758 ipn1->ipn_key->ipnk_sw_if_index,
759 ipn2->ipn_key->ipnk_sw_if_index);
761 cmp = ip46_address_cmp (&ipn1->ipn_key->ipnk_ip, &ipn2->ipn_key->ipnk_ip);
766 ip_neighbor_entries (u32 sw_if_index, ip46_type_t type)
768 index_t *ipnis = NULL;
772 pool_foreach (ipn, ip_neighbor_pool,
774 if (sw_if_index != ~0 &&
775 ipn->ipn_key->ipnk_sw_if_index != sw_if_index &&
776 (IP46_TYPE_ANY == type ||
777 (ipn->ipn_key->ipnk_type == type)))
779 vec_add1 (ipnis, ip_neighbor_get_index(ipn));
785 vec_sort_with_function (ipnis, ip_neighbor_sort);
789 static clib_error_t *
790 ip_neighbor_show_sorted_i (vlib_main_t * vm,
791 unformat_input_t * input,
792 vlib_cli_command_t * cmd, ip46_type_t type)
794 ip_neighbor_elt_t *elt, *head;
796 head = pool_elt_at_index (ip_neighbor_elt_pool,
797 ip_neighbor_list_head[type]);
800 vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP",
801 "Flags", "Ethernet", "Interface");
804 /* the list is time sorted, newest first, so start from the back
805 * and work forwards. Stop when we get to one that is alive */
806 clib_llist_foreach_reverse(ip_neighbor_elt_pool,
807 ipne_anchor, head, elt,
809 vlib_cli_output (vm, "%U", format_ip_neighbor, elt->ipne_index);
816 static clib_error_t *
817 ip_neighbor_show_i (vlib_main_t * vm,
818 unformat_input_t * input,
819 vlib_cli_command_t * cmd, ip46_type_t type)
821 index_t *ipni, *ipnis = NULL;
824 /* Filter entries by interface if given. */
826 (void) unformat_user (input, unformat_vnet_sw_interface, vnet_get_main (),
829 ipnis = ip_neighbor_entries (sw_if_index, type);
832 vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP",
833 "Flags", "Ethernet", "Interface");
835 vec_foreach (ipni, ipnis)
837 vlib_cli_output (vm, "%U", format_ip_neighbor, *ipni);
844 static clib_error_t *
845 ip_neighbor_show (vlib_main_t * vm,
846 unformat_input_t * input, vlib_cli_command_t * cmd)
848 return (ip_neighbor_show_i (vm, input, cmd, IP46_TYPE_ANY));
851 static clib_error_t *
852 ip6_neighbor_show (vlib_main_t * vm,
853 unformat_input_t * input, vlib_cli_command_t * cmd)
855 return (ip_neighbor_show_i (vm, input, cmd, IP46_TYPE_IP6));
858 static clib_error_t *
859 ip4_neighbor_show (vlib_main_t * vm,
860 unformat_input_t * input, vlib_cli_command_t * cmd)
862 return (ip_neighbor_show_i (vm, input, cmd, IP46_TYPE_IP4));
865 static clib_error_t *
866 ip6_neighbor_show_sorted (vlib_main_t * vm,
867 unformat_input_t * input, vlib_cli_command_t * cmd)
869 return (ip_neighbor_show_sorted_i (vm, input, cmd, IP46_TYPE_IP6));
872 static clib_error_t *
873 ip4_neighbor_show_sorted (vlib_main_t * vm,
874 unformat_input_t * input, vlib_cli_command_t * cmd)
876 return (ip_neighbor_show_sorted_i (vm, input, cmd, IP46_TYPE_IP4));
880 * Display all the IP neighbor entries.
883 * Example of how to display the IPv4 ARP table:
884 * @cliexstart{show ip neighbor}
885 * Time FIB IP4 Flags Ethernet Interface
886 * 346.3028 0 6.1.1.3 de:ad:be:ef:ba:be GigabitEthernet2/0/0
887 * 3077.4271 0 6.1.1.4 S de:ad:be:ef:ff:ff GigabitEthernet2/0/0
888 * 2998.6409 1 6.2.2.3 de:ad:be:ef:00:01 GigabitEthernet2/0/0
889 * Proxy arps enabled for:
890 * Fib_index 0 6.0.0.1 - 6.0.0.11
894 VLIB_CLI_COMMAND (show_ip_neighbors_cmd_node, static) = {
895 .path = "show ip neighbors",
896 .function = ip_neighbor_show,
897 .short_help = "show ip neighbors [interface]",
899 VLIB_CLI_COMMAND (show_ip4_neighbors_cmd_node, static) = {
900 .path = "show ip4 neighbors",
901 .function = ip4_neighbor_show,
902 .short_help = "show ip4 neighbors [interface]",
904 VLIB_CLI_COMMAND (show_ip6_neighbors_cmd_node, static) = {
905 .path = "show ip6 neighbors",
906 .function = ip6_neighbor_show,
907 .short_help = "show ip6 neighbors [interface]",
909 VLIB_CLI_COMMAND (show_ip_neighbor_cmd_node, static) = {
910 .path = "show ip neighbor",
911 .function = ip_neighbor_show,
912 .short_help = "show ip neighbor [interface]",
914 VLIB_CLI_COMMAND (show_ip4_neighbor_cmd_node, static) = {
915 .path = "show ip4 neighbor",
916 .function = ip4_neighbor_show,
917 .short_help = "show ip4 neighbor [interface]",
919 VLIB_CLI_COMMAND (show_ip6_neighbor_cmd_node, static) = {
920 .path = "show ip6 neighbor",
921 .function = ip6_neighbor_show,
922 .short_help = "show ip6 neighbor [interface]",
924 VLIB_CLI_COMMAND (show_ip4_neighbor_sorted_cmd_node, static) = {
925 .path = "show ip4 neighbor-sorted",
926 .function = ip4_neighbor_show_sorted,
927 .short_help = "show ip4 neighbor-sorted",
929 VLIB_CLI_COMMAND (show_ip6_neighbor_sorted_cmd_node, static) = {
930 .path = "show ip6 neighbor-sorted",
931 .function = ip6_neighbor_show_sorted,
932 .short_help = "show ip6 neighbor-sorted",
936 static ip_neighbor_vft_t ip_nbr_vfts[IP46_N_TYPES];
939 ip_neighbor_register (ip46_type_t type, const ip_neighbor_vft_t * vft)
941 ip_nbr_vfts[type] = *vft;
945 ip_neighbor_probe_dst (const ip_adjacency_t * adj, const ip46_address_t * dst)
947 if (!vnet_sw_interface_is_admin_up (vnet_get_main (),
948 adj->rewrite_header.sw_if_index))
951 switch (adj->ia_nh_proto)
953 case FIB_PROTOCOL_IP6:
954 ip6_neighbor_probe_dst (adj, &dst->ip6);
956 case FIB_PROTOCOL_IP4:
957 ip4_neighbor_probe_dst (adj, &dst->ip4);
959 case FIB_PROTOCOL_MPLS:
966 ip_neighbor_probe (const ip_adjacency_t * adj)
968 ip_neighbor_probe_dst (adj, &adj->sub_type.nbr.next_hop);
972 ip_neighbor_advertise (vlib_main_t * vm,
974 const ip46_address_t * addr, u32 sw_if_index)
976 vnet_main_t *vnm = vnet_get_main ();
978 if (type == IP46_TYPE_IP4 || type == IP46_TYPE_BOTH)
979 ip4_neighbor_advertise (vm, vnm, sw_if_index, &addr->ip4);
980 if (type == IP46_TYPE_IP6 || type == IP46_TYPE_BOTH)
981 ip6_neighbor_advertise (vm, vnm, sw_if_index, &addr->ip6);
985 ip_neighbor_walk (ip46_type_t type,
986 u32 sw_if_index, ip_neighbor_walk_cb_t cb, void *ctx)
988 ip_neighbor_key_t *key;
991 if (~0 == sw_if_index)
995 vec_foreach (hash, ip_neighbor_db[type].ipndb_hash)
998 hash_foreach (key, ipni, *hash,
1009 if (vec_len (ip_neighbor_db[type].ipndb_hash) <= sw_if_index)
1011 hash = ip_neighbor_db[type].ipndb_hash[sw_if_index];
1014 hash_foreach (key, ipni, hash,
1023 ip4_neighbor_proxy_add (u32 fib_index,
1024 const ip4_address_t * start,
1025 const ip4_address_t * end)
1027 if (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_add)
1029 return (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_add
1030 (fib_index, start, end));
1037 ip4_neighbor_proxy_delete (u32 fib_index,
1038 const ip4_address_t * start,
1039 const ip4_address_t * end)
1041 if (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_del)
1043 return (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_del
1044 (fib_index, start, end));
1050 ip4_neighbor_proxy_enable (u32 sw_if_index)
1052 if (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_enable)
1054 return (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_enable (sw_if_index));
1060 ip4_neighbor_proxy_disable (u32 sw_if_index)
1062 if (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_disable)
1064 return (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_disable (sw_if_index));
1070 ip6_neighbor_proxy_add (u32 sw_if_index, const ip6_address_t * addr)
1072 if (ip_nbr_vfts[IP46_TYPE_IP6].inv_proxy6_add)
1074 return (ip_nbr_vfts[IP46_TYPE_IP6].inv_proxy6_add (sw_if_index, addr));
1080 ip6_neighbor_proxy_del (u32 sw_if_index, const ip6_address_t * addr)
1082 if (ip_nbr_vfts[IP46_TYPE_IP6].inv_proxy6_del)
1084 return (ip_nbr_vfts[IP46_TYPE_IP6].inv_proxy6_del (sw_if_index, addr));
1090 ip_neighbor_ethernet_change_mac (ethernet_main_t * em,
1091 u32 sw_if_index, uword opaque)
1096 IP_NEIGHBOR_DBG ("mac-change: %U",
1097 format_vnet_sw_if_index_name, vnet_get_main (),
1101 pool_foreach (ipn, ip_neighbor_pool,
1103 if (ipn->ipn_key->ipnk_sw_if_index == sw_if_index)
1104 adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
1105 fib_proto_from_ip46(ipn->ipn_key->ipnk_type),
1106 &ipn->ipn_key->ipnk_ip,
1107 ip_neighbor_mk_complete_walk,
1112 ai = adj_glean_get (FIB_PROTOCOL_IP4, sw_if_index);
1114 if (ADJ_INDEX_INVALID != ai)
1115 adj_glean_update_rewrite (ai);
1119 ip_neighbor_populate (ip46_type_t type, u32 sw_if_index)
1121 index_t *ipnis = NULL, *ipni;
1124 IP_NEIGHBOR_DBG ("populate: %U %U",
1125 format_vnet_sw_if_index_name, vnet_get_main (),
1126 sw_if_index, format_ip46_type, type);
1129 pool_foreach (ipn, ip_neighbor_pool,
1131 if (ipn->ipn_key->ipnk_type == type &&
1132 ipn->ipn_key->ipnk_sw_if_index == sw_if_index)
1133 vec_add1 (ipnis, ipn - ip_neighbor_pool);
1137 vec_foreach (ipni, ipnis)
1139 ipn = ip_neighbor_get (*ipni);
1141 adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
1142 fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
1143 &ipn->ipn_key->ipnk_ip,
1144 ip_neighbor_mk_complete_walk, ipn);
1150 ip_neighbor_flush (ip46_type_t type, u32 sw_if_index)
1152 index_t *ipnis = NULL, *ipni;
1155 IP_NEIGHBOR_DBG ("flush: %U %U",
1156 format_vnet_sw_if_index_name, vnet_get_main (),
1157 sw_if_index, format_ip46_type, type);
1160 pool_foreach (ipn, ip_neighbor_pool,
1162 if (ipn->ipn_key->ipnk_type == type &&
1163 ipn->ipn_key->ipnk_sw_if_index == sw_if_index &&
1164 ip_neighbor_is_dynamic (ipn))
1165 vec_add1 (ipnis, ipn - ip_neighbor_pool);
1169 vec_foreach (ipni, ipnis) ip_neighbor_free (ip_neighbor_get (*ipni));
1174 * Remove any arp entries associated with the specified interface
1176 static clib_error_t *
1177 ip_neighbor_interface_admin_change (vnet_main_t * vnm,
1178 u32 sw_if_index, u32 flags)
1182 IP_NEIGHBOR_DBG ("interface-admin: %U %s",
1183 format_vnet_sw_if_index_name, vnet_get_main (),
1185 (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ? "up" : "down"));
1187 if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
1189 FOREACH_IP46_TYPE (type) ip_neighbor_populate (type, sw_if_index);
1193 /* admin down, flush all neighbours */
1194 FOREACH_IP46_TYPE (type) ip_neighbor_flush (type, sw_if_index);
1200 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ip_neighbor_interface_admin_change);
1203 * Remove any arp entries associated with the specified interface
1205 static clib_error_t *
1206 ip_neighbor_delete_sw_interface (vnet_main_t * vnm,
1207 u32 sw_if_index, u32 is_add)
1209 IP_NEIGHBOR_DBG ("interface-change: %U %s",
1210 format_vnet_sw_if_index_name, vnet_get_main (),
1211 sw_if_index, (is_add ? "add" : "del"));
1213 if (!is_add && sw_if_index != ~0)
1217 FOREACH_IP46_TYPE (type) ip_neighbor_flush (type, sw_if_index);
1223 VNET_SW_INTERFACE_ADD_DEL_FUNCTION (ip_neighbor_delete_sw_interface);
1225 typedef struct ip_neighbor_walk_covered_ctx_t_
1228 ip46_address_t addr;
1231 } ip_neighbor_walk_covered_ctx_t;
1234 ip_neighbor_walk_covered (index_t ipni, void *arg)
1236 ip_neighbor_walk_covered_ctx_t *ctx = arg;
1239 ipn = ip_neighbor_get (ipni);
1241 ASSERT (ipn->ipn_key->ipnk_type == ctx->type);
1243 if (IP46_TYPE_IP4 == ctx->type)
1245 if (ip4_destination_matches_route (&ip4_main,
1246 &ipn->ipn_key->ipnk_ip.ip4,
1249 ip_neighbor_is_dynamic (ipn))
1251 vec_add1 (ctx->ipnis, ip_neighbor_get_index (ipn));
1254 return (WALK_CONTINUE);
1259 * callback when an interface address is added or deleted
1262 ip_neighbor_add_del_interface_address_v4 (ip4_main_t * im,
1265 ip4_address_t * address,
1267 u32 if_address_index, u32 is_del)
1270 * Flush the ARP cache of all entries covered by the address
1271 * that is being removed.
1273 IP_NEIGHBOR_DBG ("addr-%d: %U, %U/%d",
1274 (is_del ? "del" : "add"),
1275 format_vnet_sw_if_index_name, vnet_get_main (),
1276 sw_if_index, format_ip4_address, address, address_length);
1280 ip_neighbor_walk_covered_ctx_t ctx = {
1281 .addr.ip4 = *address,
1282 .type = IP46_TYPE_IP4,
1283 .length = address_length,
1287 ip_neighbor_walk (IP46_TYPE_IP4, sw_if_index,
1288 ip_neighbor_walk_covered, &ctx);
1290 vec_foreach (ipni, ctx.ipnis)
1291 ip_neighbor_free (ip_neighbor_get (*ipni));
1293 vec_free (ctx.ipnis);
1298 * callback when an interface address is added or deleted
1301 ip_neighbor_add_del_interface_address_v6 (ip6_main_t * im,
1304 ip6_address_t * address,
1306 u32 if_address_index, u32 is_del)
1309 * Flush the ARP cache of all entries covered by the address
1310 * that is being removed.
1312 IP_NEIGHBOR_DBG ("addr-change: %U, %U/%d %s",
1313 format_vnet_sw_if_index_name, vnet_get_main (),
1314 sw_if_index, format_ip6_address, address, address_length,
1315 (is_del ? "del" : "add"));
1319 ip_neighbor_walk_covered_ctx_t ctx = {
1320 .addr.ip6 = *address,
1321 .type = IP46_TYPE_IP6,
1322 .length = address_length,
1326 ip_neighbor_walk (IP46_TYPE_IP6, sw_if_index,
1327 ip_neighbor_walk_covered, &ctx);
1329 vec_foreach (ipni, ctx.ipnis)
1330 ip_neighbor_free (ip_neighbor_get (*ipni));
1332 vec_free (ctx.ipnis);
1336 typedef struct ip_neighbor_table_bind_ctx_t_
1340 } ip_neighbor_table_bind_ctx_t;
1343 ip_neighbor_walk_table_bind (index_t ipni, void *arg)
1345 ip_neighbor_table_bind_ctx_t *ctx = arg;
1348 ipn = ip_neighbor_get (ipni);
1349 ip_neighbor_adj_fib_remove (ipn, ctx->old_fib_index);
1350 ip_neighbor_adj_fib_add (ipn, ctx->new_fib_index);
1352 return (WALK_CONTINUE);
1356 ip_neighbor_table_bind_v4 (ip4_main_t * im,
1359 u32 new_fib_index, u32 old_fib_index)
1361 ip_neighbor_table_bind_ctx_t ctx = {
1362 .old_fib_index = old_fib_index,
1363 .new_fib_index = new_fib_index,
1366 ip_neighbor_walk (IP46_TYPE_IP4, sw_if_index,
1367 ip_neighbor_walk_table_bind, &ctx);
1371 ip_neighbor_table_bind_v6 (ip6_main_t * im,
1374 u32 new_fib_index, u32 old_fib_index)
1376 ip_neighbor_table_bind_ctx_t ctx = {
1377 .old_fib_index = old_fib_index,
1378 .new_fib_index = new_fib_index,
1381 ip_neighbor_walk (IP46_TYPE_IP6, sw_if_index,
1382 ip_neighbor_walk_table_bind, &ctx);
1385 typedef enum ip_neighbor_age_state_t_
1387 IP_NEIGHBOR_AGE_ALIVE,
1388 IP_NEIGHBOR_AGE_PROBE,
1389 IP_NEIGHBOR_AGE_DEAD,
1390 } ip_neighbor_age_state_t;
1392 #define IP_NEIGHBOR_PROCESS_SLEEP_LONG (0)
1394 static ip_neighbor_age_state_t
1395 ip_neighbour_age_out (index_t ipni, f64 now, f64 * wait)
1400 ipn = ip_neighbor_get (ipni);
1401 ttl = now - ipn->ipn_time_last_updated;
1402 *wait = IP_NEIGHBOR_PROCESS_SLEEP_LONG;
1404 if (ttl > ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_age)
1406 IP_NEIGHBOR_DBG ("aged: %U @%f - %f > %d",
1407 format_ip_neighbor, ipni, now,
1408 ipn->ipn_time_last_updated,
1409 ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_age);
1410 if (ipn->ipn_n_probes > 2)
1412 /* 3 strikes and yea-re out */
1413 IP_NEIGHBOR_DBG ("dead: %U", format_ip_neighbor, ipni);
1414 return (IP_NEIGHBOR_AGE_DEAD);
1420 ai = adj_glean_get (fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
1421 ip_neighbor_get_sw_if_index (ipn));
1423 if (ADJ_INDEX_INVALID != ai)
1424 ip_neighbor_probe_dst (adj_get (ai), ip_neighbor_get_ip (ipn));
1426 ipn->ipn_n_probes++;
1433 return (IP_NEIGHBOR_AGE_ALIVE);
1436 return (IP_NEIGHBOR_AGE_PROBE);
1439 typedef enum ip_neighbor_process_event_t_
1441 IP_NEIGHBOR_AGE_PROCESS_WAKEUP,
1442 } ip_neighbor_process_event_t;
1445 ip_neighbor_age_loop (vlib_main_t * vm,
1446 vlib_node_runtime_t * rt,
1447 vlib_frame_t * f, ip46_type_t type)
1449 uword event_type, *event_data = NULL;
1452 /* Set the timeout to an effectively infinite value when the process starts */
1453 timeout = IP_NEIGHBOR_PROCESS_SLEEP_LONG;
1460 vlib_process_wait_for_event (vm);
1462 vlib_process_wait_for_event_or_clock (vm, timeout);
1464 event_type = vlib_process_get_events (vm, &event_data);
1465 vec_reset_length (event_data);
1467 now = vlib_time_now (vm);
1474 ip_neighbor_elt_t *elt, *head;
1478 head = pool_elt_at_index (ip_neighbor_elt_pool,
1479 ip_neighbor_list_head[type]);
1482 /* the list is time sorted, newest first, so start from the back
1483 * and work forwards. Stop when we get to one that is alive */
1485 clib_llist_foreach_reverse(ip_neighbor_elt_pool,
1486 ipne_anchor, head, elt,
1488 ip_neighbor_age_state_t res;
1490 res = ip_neighbour_age_out(elt->ipne_index, now, &wait);
1492 if (IP_NEIGHBOR_AGE_ALIVE == res) {
1493 /* the oldest neighbor has not yet expired, go back to sleep */
1496 else if (IP_NEIGHBOR_AGE_DEAD == res) {
1497 /* the oldest neighbor is dead, pop it, then restart the walk
1498 * again from the back */
1499 ip_neighbor_free (ip_neighbor_get(elt->ipne_index));
1503 timeout = clib_min (wait, timeout);
1508 case IP_NEIGHBOR_AGE_PROCESS_WAKEUP:
1511 if (!ip_neighbor_db[type].ipndb_age)
1513 /* aging has been disabled */
1517 ip_neighbor_elt_t *elt, *head;
1519 head = pool_elt_at_index (ip_neighbor_elt_pool,
1520 ip_neighbor_list_head[type]);
1521 elt = clib_llist_prev (ip_neighbor_elt_pool, ipne_anchor, head);
1523 /* poke the oldset neighbour for aging, which returns how long we sleep for */
1524 if (IP_NEIGHBOR_AGE_PROBE ==
1525 ip_neighbour_age_out (elt->ipne_index, now, &timeout))
1526 /* we probed for the oldest entry, sleep for a short time to get to the next */
1536 ip4_neighbor_age_process (vlib_main_t * vm,
1537 vlib_node_runtime_t * rt, vlib_frame_t * f)
1539 return (ip_neighbor_age_loop (vm, rt, f, IP46_TYPE_IP4));
1543 ip6_neighbor_age_process (vlib_main_t * vm,
1544 vlib_node_runtime_t * rt, vlib_frame_t * f)
1546 return (ip_neighbor_age_loop (vm, rt, f, IP46_TYPE_IP6));
1550 VLIB_REGISTER_NODE (ip4_neighbor_age_process_node,static) = {
1551 .function = ip4_neighbor_age_process,
1552 .type = VLIB_NODE_TYPE_PROCESS,
1553 .name = "ip4-neighbor-age-process",
1555 VLIB_REGISTER_NODE (ip6_neighbor_age_process_node,static) = {
1556 .function = ip6_neighbor_age_process,
1557 .type = VLIB_NODE_TYPE_PROCESS,
1558 .name = "ip6-neighbor-age-process",
1563 ip_neighbor_config (ip46_type_t type, u32 limit, u32 age, bool recycle)
1565 ip_neighbor_db[type].ipndb_limit = limit;
1566 ip_neighbor_db[type].ipndb_recycle = recycle;
1567 ip_neighbor_db[type].ipndb_age = age;
1569 vlib_process_signal_event (vlib_get_main (),
1570 (IP46_TYPE_IP4 == type ?
1571 ip4_neighbor_age_process_node.index :
1572 ip6_neighbor_age_process_node.index),
1573 IP_NEIGHBOR_AGE_PROCESS_WAKEUP, 0);
1578 static clib_error_t *
1579 ip_neighbor_config_show (vlib_main_t * vm,
1580 unformat_input_t * input, vlib_cli_command_t * cmd)
1585 FOREACH_IP46_TYPE(type) {
1586 vlib_cli_output (vm, "%U:", format_ip46_type, type);
1587 vlib_cli_output (vm, " limit:%d, age:%d, recycle:%d",
1588 ip_neighbor_db[type].ipndb_limit,
1589 ip_neighbor_db[type].ipndb_age,
1590 ip_neighbor_db[type].ipndb_recycle);
1598 VLIB_CLI_COMMAND (show_ip_neighbor_cfg_cmd_node, static) = {
1599 .path = "show ip neighbor-config",
1600 .function = ip_neighbor_config_show,
1601 .short_help = "show ip neighbor-config",
1605 static clib_error_t *
1606 ip_neighbor_init (vlib_main_t * vm)
1609 ip4_add_del_interface_address_callback_t cb = {
1610 .function = ip_neighbor_add_del_interface_address_v4,
1612 vec_add1 (ip4_main.add_del_interface_address_callbacks, cb);
1615 ip6_add_del_interface_address_callback_t cb = {
1616 .function = ip_neighbor_add_del_interface_address_v6,
1618 vec_add1 (ip6_main.add_del_interface_address_callbacks, cb);
1621 ip4_table_bind_callback_t cb = {
1622 .function = ip_neighbor_table_bind_v4,
1624 vec_add1 (ip4_main.table_bind_callbacks, cb);
1627 ip6_table_bind_callback_t cb = {
1628 .function = ip_neighbor_table_bind_v6,
1630 vec_add1 (ip6_main.table_bind_callbacks, cb);
1633 ethernet_address_change_ctx_t ctx = {
1634 .function = ip_neighbor_ethernet_change_mac,
1635 .function_opaque = 0,
1637 vec_add1 (ethernet_main.address_change_callbacks, ctx);
1640 ipn_logger = vlib_log_register_class ("ip", "neighbor");
1644 FOREACH_IP46_TYPE (type)
1645 ip_neighbor_list_head[type] =
1646 clib_llist_make_head (ip_neighbor_elt_pool, ipne_anchor);
1652 VLIB_INIT_FUNCTION (ip_neighbor_init) =
1654 .runs_after = VLIB_INITS("ip_main_init"),
1659 * fd.io coding-style-patch-verification: ON
1662 * eval: (c-set-style "gnu")