blob: b33ca8a3538a52e213e4f3f454f6161eb81a5707 [file] [log] [blame]
Neale Rannscbe25aa2019-09-30 10:53:31 +00001/*
2 * src/vnet/ip/ip_neighboor.c: ip neighbor generic handling
3 *
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:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
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.
16 */
17
18#include <vppinfra/llist.h>
19
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>
24
25#include <vnet/ip/ip6_ll_table.h>
Ivan Shvedunovf86b9672021-02-19 23:32:18 +030026#include <vnet/ip/ip46_address.h>
Neale Rannscbe25aa2019-09-30 10:53:31 +000027#include <vnet/fib/fib_table.h>
28#include <vnet/adj/adj_mcast.h>
29
Neale Rannsfd2417b2021-07-16 14:00:16 +000030ip_neighbor_counters_t ip_neighbor_counters[] =
31{
32 [AF_IP4] = {
33 .ipnc = {
34 [VLIB_RX] = {
35 [IP_NEIGHBOR_CTR_REPLY] = {
36 .name = "arp-rx-replies",
37 .stat_segment_name = "/net/arp/rx/replies",
38 },
39 [IP_NEIGHBOR_CTR_REQUEST] = {
40 .name = "arp-rx-requests",
41 .stat_segment_name = "/net/arp/rx/requests",
42 },
43 [IP_NEIGHBOR_CTR_GRAT] = {
44 .name = "arp-rx-gratuitous",
45 .stat_segment_name = "/net/arp/rx/gratuitous",
46 },
47 },
48 [VLIB_TX] = {
49 [IP_NEIGHBOR_CTR_REPLY] = {
50 .name = "arp-tx-replies",
51 .stat_segment_name = "/net/arp/tx/replies",
52 },
53 [IP_NEIGHBOR_CTR_REQUEST] = {
54 .name = "arp-tx-requests",
55 .stat_segment_name = "/net/arp/tx/requests",
56 },
57 [IP_NEIGHBOR_CTR_GRAT] = {
58 .name = "arp-tx-gratuitous",
59 .stat_segment_name = "/net/arp/tx/gratuitous",
60 },
61 },
62 },
63 },
64 [AF_IP6] = {
65 .ipnc = {
66 [VLIB_RX] = {
67 [IP_NEIGHBOR_CTR_REPLY] = {
68 .name = "ip6-nd-rx-replies",
69 .stat_segment_name = "/net/ip6-nd/rx/replies",
70 },
71 [IP_NEIGHBOR_CTR_REQUEST] = {
72 .name = "ip6-nd-rx-requests",
73 .stat_segment_name = "/net/ip6-nd/rx/requests",
74 },
75 [IP_NEIGHBOR_CTR_GRAT] = {
76 .name = "ip6-nd-rx-gratuitous",
77 .stat_segment_name = "/net/ip6-nd/rx/gratuitous",
78 },
79 },
80 [VLIB_TX] = {
81 [IP_NEIGHBOR_CTR_REPLY] = {
82 .name = "ip6-nd-tx-replies",
83 .stat_segment_name = "/net/ip6-nd/tx/replies",
84 },
85 [IP_NEIGHBOR_CTR_REQUEST] = {
86 .name = "ip6-nd-tx-requests",
87 .stat_segment_name = "/net/ip6-nd/tx/requests",
88 },
89 [IP_NEIGHBOR_CTR_GRAT] = {
90 .name = "ip6-nd-tx-gratuitous",
91 .stat_segment_name = "/net/ip6-nd/tx/gratuitous",
92 },
93 },
94 },
95 },
96};
97
Neale Rannscbe25aa2019-09-30 10:53:31 +000098/** Pool for All IP neighbors */
99static ip_neighbor_t *ip_neighbor_pool;
100
101/** protocol specific lists of time sorted neighbors */
Neale Rannsdc617b82020-08-20 08:22:56 +0000102index_t ip_neighbor_list_head[N_AF];
Neale Rannscbe25aa2019-09-30 10:53:31 +0000103
104typedef struct ip_neighbor_elt_t_
105{
106 clib_llist_anchor_t ipne_anchor;
107 index_t ipne_index;
108} ip_neighbor_elt_t;
109
110/** Pool of linked list elemeents */
111ip_neighbor_elt_t *ip_neighbor_elt_pool;
112
113typedef struct ip_neighbor_db_t_
114{
115 /** per interface hash */
116 uword **ipndb_hash;
117 /** per-protocol limit - max number of neighbors*/
118 u32 ipndb_limit;
119 /** max age of a neighbor before it's forcibly evicted */
120 u32 ipndb_age;
121 /** when the limit is reached and new neighbors are created, should
122 * we recycle an old one */
123 bool ipndb_recycle;
124 /** per-protocol number of elements */
125 u32 ipndb_n_elts;
126 /** per-protocol number of elements per-fib-index*/
127 u32 *ipndb_n_elts_per_fib;
128} ip_neighbor_db_t;
129
130static vlib_log_class_t ipn_logger;
131
132/* DBs of neighbours one per AF */
133/* *INDENT-OFF* */
Neale Rannsdc617b82020-08-20 08:22:56 +0000134static ip_neighbor_db_t ip_neighbor_db[N_AF] = {
135 [AF_IP4] = {
Neale Rannscbe25aa2019-09-30 10:53:31 +0000136 .ipndb_limit = 50000,
137 /* Default to not aging and not recycling */
138 .ipndb_age = 0,
139 .ipndb_recycle = false,
140 },
Neale Rannsdc617b82020-08-20 08:22:56 +0000141 [AF_IP6] = {
Neale Rannscbe25aa2019-09-30 10:53:31 +0000142 .ipndb_limit = 50000,
143 /* Default to not aging and not recycling */
144 .ipndb_age = 0,
145 .ipndb_recycle = false,
146 }
147};
148/* *INDENT-ON* */
149
150#define IP_NEIGHBOR_DBG(...) \
151 vlib_log_debug (ipn_logger, __VA_ARGS__);
152
153#define IP_NEIGHBOR_INFO(...) \
154 vlib_log_notice (ipn_logger, __VA_ARGS__);
155
156ip_neighbor_t *
157ip_neighbor_get (index_t ipni)
158{
159 if (pool_is_free_index (ip_neighbor_pool, ipni))
160 return (NULL);
161
162 return (pool_elt_at_index (ip_neighbor_pool, ipni));
163}
164
165static index_t
166ip_neighbor_get_index (const ip_neighbor_t * ipn)
167{
168 return (ipn - ip_neighbor_pool);
169}
170
Neale Rannsc87fbb42020-04-02 17:08:28 +0000171static void
172ip_neighbor_touch (ip_neighbor_t * ipn)
173{
174 ipn->ipn_flags &= ~IP_NEIGHBOR_FLAG_STALE;
175}
176
Neale Rannscbe25aa2019-09-30 10:53:31 +0000177static bool
178ip_neighbor_is_dynamic (const ip_neighbor_t * ipn)
179{
180 return (ipn->ipn_flags & IP_NEIGHBOR_FLAG_DYNAMIC);
181}
182
Neale Rannsdc617b82020-08-20 08:22:56 +0000183const ip_address_t *
Neale Rannscbe25aa2019-09-30 10:53:31 +0000184ip_neighbor_get_ip (const ip_neighbor_t * ipn)
185{
186 return (&ipn->ipn_key->ipnk_ip);
187}
188
Neale Rannsdc617b82020-08-20 08:22:56 +0000189ip_address_family_t
190ip_neighbor_get_af (const ip_neighbor_t * ipn)
191{
192 return (ip_addr_version (&ipn->ipn_key->ipnk_ip));
193}
194
Neale Rannscbe25aa2019-09-30 10:53:31 +0000195const mac_address_t *
196ip_neighbor_get_mac (const ip_neighbor_t * ipn)
197{
198 return (&ipn->ipn_mac);
199}
200
201const u32
202ip_neighbor_get_sw_if_index (const ip_neighbor_t * ipn)
203{
204 return (ipn->ipn_key->ipnk_sw_if_index);
205}
206
207static void
208ip_neighbor_list_remove (ip_neighbor_t * ipn)
209{
210 /* new neighbours, are added to the head of the list, since the
211 * list is time sorted, newest first */
212 ip_neighbor_elt_t *elt;
213
214 if (~0 != ipn->ipn_elt)
215 {
216 elt = pool_elt_at_index (ip_neighbor_elt_pool, ipn->ipn_elt);
217
218 clib_llist_remove (ip_neighbor_elt_pool, ipne_anchor, elt);
Vladimir Isaevb2f44bd2020-07-16 17:05:18 +0300219
220 ipn->ipn_elt = ~0;
Neale Rannscbe25aa2019-09-30 10:53:31 +0000221 }
222}
223
224static void
225ip_neighbor_refresh (ip_neighbor_t * ipn)
226{
227 /* new neighbours, are added to the head of the list, since the
228 * list is time sorted, newest first */
229 ip_neighbor_elt_t *elt, *head;
230
Neale Rannsc87fbb42020-04-02 17:08:28 +0000231 ip_neighbor_touch (ipn);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000232 ipn->ipn_time_last_updated = vlib_time_now (vlib_get_main ());
233 ipn->ipn_n_probes = 0;
234
235 if (ip_neighbor_is_dynamic (ipn))
236 {
237 if (~0 == ipn->ipn_elt)
238 /* first time insertion */
239 pool_get_zero (ip_neighbor_elt_pool, elt);
240 else
241 {
242 /* already inserted - extract first */
243 elt = pool_elt_at_index (ip_neighbor_elt_pool, ipn->ipn_elt);
244
245 clib_llist_remove (ip_neighbor_elt_pool, ipne_anchor, elt);
246 }
247 head = pool_elt_at_index (ip_neighbor_elt_pool,
Neale Rannsdc617b82020-08-20 08:22:56 +0000248 ip_neighbor_list_head[ip_neighbor_get_af
249 (ipn)]);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000250
251 elt->ipne_index = ip_neighbor_get_index (ipn);
252 clib_llist_add (ip_neighbor_elt_pool, ipne_anchor, elt, head);
253 ipn->ipn_elt = elt - ip_neighbor_elt_pool;
254 }
255}
256
257static void
258ip_neighbor_db_add (const ip_neighbor_t * ipn)
259{
Neale Rannsdc617b82020-08-20 08:22:56 +0000260 ip_address_family_t af;
261 u32 sw_if_index;
Neale Rannscbe25aa2019-09-30 10:53:31 +0000262
Neale Rannsdc617b82020-08-20 08:22:56 +0000263 af = ip_neighbor_get_af (ipn);
264 sw_if_index = ipn->ipn_key->ipnk_sw_if_index;
265
266 vec_validate (ip_neighbor_db[af].ipndb_hash, sw_if_index);
267
268 if (!ip_neighbor_db[af].ipndb_hash[sw_if_index])
269 ip_neighbor_db[af].ipndb_hash[sw_if_index]
Neale Rannscbe25aa2019-09-30 10:53:31 +0000270 = hash_create_mem (0, sizeof (ip_neighbor_key_t), sizeof (index_t));
271
Neale Rannsdc617b82020-08-20 08:22:56 +0000272 hash_set_mem (ip_neighbor_db[af].ipndb_hash[sw_if_index],
273 ipn->ipn_key, ip_neighbor_get_index (ipn));
Neale Rannscbe25aa2019-09-30 10:53:31 +0000274
Neale Rannsdc617b82020-08-20 08:22:56 +0000275 ip_neighbor_db[af].ipndb_n_elts++;
Neale Rannscbe25aa2019-09-30 10:53:31 +0000276}
277
278static void
Neale Rannsdc617b82020-08-20 08:22:56 +0000279ip_neighbor_db_remove (const ip_neighbor_t * ipn)
Neale Rannscbe25aa2019-09-30 10:53:31 +0000280{
Neale Rannsdc617b82020-08-20 08:22:56 +0000281 ip_address_family_t af;
282 u32 sw_if_index;
Neale Rannscbe25aa2019-09-30 10:53:31 +0000283
Neale Rannsdc617b82020-08-20 08:22:56 +0000284 af = ip_neighbor_get_af (ipn);
285 sw_if_index = ipn->ipn_key->ipnk_sw_if_index;
Neale Rannscbe25aa2019-09-30 10:53:31 +0000286
Neale Rannsdc617b82020-08-20 08:22:56 +0000287 vec_validate (ip_neighbor_db[af].ipndb_hash, sw_if_index);
288
289 hash_unset_mem (ip_neighbor_db[af].ipndb_hash[sw_if_index], ipn->ipn_key);
290
291 ip_neighbor_db[af].ipndb_n_elts--;
Neale Rannscbe25aa2019-09-30 10:53:31 +0000292}
293
294static ip_neighbor_t *
295ip_neighbor_db_find (const ip_neighbor_key_t * key)
296{
Neale Rannsdc617b82020-08-20 08:22:56 +0000297 ip_address_family_t af;
Neale Rannscbe25aa2019-09-30 10:53:31 +0000298 uword *p;
299
Neale Rannsdc617b82020-08-20 08:22:56 +0000300 af = ip_addr_version (&key->ipnk_ip);
301
302 if (key->ipnk_sw_if_index >= vec_len (ip_neighbor_db[af].ipndb_hash))
Neale Rannscbe25aa2019-09-30 10:53:31 +0000303 return NULL;
304
Neale Rannsdc617b82020-08-20 08:22:56 +0000305 p = hash_get_mem (ip_neighbor_db[af].ipndb_hash
306 [key->ipnk_sw_if_index], key);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000307
308 if (p)
309 return ip_neighbor_get (p[0]);
310
311 return (NULL);
312}
313
314static u8
Neale Rannsdc617b82020-08-20 08:22:56 +0000315ip_af_type_pfx_len (ip_address_family_t type)
Neale Rannscbe25aa2019-09-30 10:53:31 +0000316{
Neale Rannsdc617b82020-08-20 08:22:56 +0000317 return (type == AF_IP4 ? 32 : 128);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000318}
319
320static void
321ip_neighbor_adj_fib_add (ip_neighbor_t * ipn, u32 fib_index)
322{
Neale Rannsdc617b82020-08-20 08:22:56 +0000323 ip_address_family_t af;
324
325 af = ip_neighbor_get_af (ipn);
326
327 if (af == AF_IP6 &&
328 ip6_address_is_link_local_unicast (&ip_addr_v6
329 (&ipn->ipn_key->ipnk_ip)))
Neale Rannscbe25aa2019-09-30 10:53:31 +0000330 {
331 ip6_ll_prefix_t pfx = {
Neale Rannsdc617b82020-08-20 08:22:56 +0000332 .ilp_addr = ip_addr_v6 (&ipn->ipn_key->ipnk_ip),
Neale Rannscbe25aa2019-09-30 10:53:31 +0000333 .ilp_sw_if_index = ipn->ipn_key->ipnk_sw_if_index,
334 };
335 ipn->ipn_fib_entry_index =
336 ip6_ll_table_entry_update (&pfx, FIB_ROUTE_PATH_FLAG_NONE);
337 }
338 else
339 {
340 fib_protocol_t fproto;
341
Neale Rannsdc617b82020-08-20 08:22:56 +0000342 fproto = ip_address_family_to_fib_proto (af);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000343
344 fib_prefix_t pfx = {
Neale Rannsdc617b82020-08-20 08:22:56 +0000345 .fp_len = ip_af_type_pfx_len (af),
Neale Rannscbe25aa2019-09-30 10:53:31 +0000346 .fp_proto = fproto,
Neale Rannsdc617b82020-08-20 08:22:56 +0000347 .fp_addr = ip_addr_46 (&ipn->ipn_key->ipnk_ip),
Neale Rannscbe25aa2019-09-30 10:53:31 +0000348 };
349
350 ipn->ipn_fib_entry_index =
351 fib_table_entry_path_add (fib_index, &pfx, FIB_SOURCE_ADJ,
352 FIB_ENTRY_FLAG_ATTACHED,
353 fib_proto_to_dpo (fproto),
354 &pfx.fp_addr,
355 ipn->ipn_key->ipnk_sw_if_index,
356 ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
357
Neale Rannsdc617b82020-08-20 08:22:56 +0000358 vec_validate (ip_neighbor_db[af].ipndb_n_elts_per_fib, fib_index);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000359
Neale Rannsdc617b82020-08-20 08:22:56 +0000360 ip_neighbor_db[af].ipndb_n_elts_per_fib[fib_index]++;
Neale Rannscbe25aa2019-09-30 10:53:31 +0000361
Neale Rannsdc617b82020-08-20 08:22:56 +0000362 if (1 == ip_neighbor_db[af].ipndb_n_elts_per_fib[fib_index])
Neale Rannscbe25aa2019-09-30 10:53:31 +0000363 fib_table_lock (fib_index, fproto, FIB_SOURCE_ADJ);
364 }
365}
366
367static void
368ip_neighbor_adj_fib_remove (ip_neighbor_t * ipn, u32 fib_index)
369{
Neale Rannsdc617b82020-08-20 08:22:56 +0000370 ip_address_family_t af;
371
372 af = ip_neighbor_get_af (ipn);
373
Neale Rannscbe25aa2019-09-30 10:53:31 +0000374 if (FIB_NODE_INDEX_INVALID != ipn->ipn_fib_entry_index)
375 {
Neale Rannsdc617b82020-08-20 08:22:56 +0000376 if (AF_IP6 == af &&
377 ip6_address_is_link_local_unicast (&ip_addr_v6
378 (&ipn->ipn_key->ipnk_ip)))
Neale Rannscbe25aa2019-09-30 10:53:31 +0000379 {
380 ip6_ll_prefix_t pfx = {
Neale Rannsdc617b82020-08-20 08:22:56 +0000381 .ilp_addr = ip_addr_v6 (&ipn->ipn_key->ipnk_ip),
Neale Rannscbe25aa2019-09-30 10:53:31 +0000382 .ilp_sw_if_index = ipn->ipn_key->ipnk_sw_if_index,
383 };
384 ip6_ll_table_entry_delete (&pfx);
385 }
386 else
387 {
388 fib_protocol_t fproto;
389
Neale Rannsdc617b82020-08-20 08:22:56 +0000390 fproto = ip_address_family_to_fib_proto (af);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000391
392 fib_prefix_t pfx = {
Neale Rannsdc617b82020-08-20 08:22:56 +0000393 .fp_len = ip_af_type_pfx_len (af),
Neale Rannscbe25aa2019-09-30 10:53:31 +0000394 .fp_proto = fproto,
Neale Rannsdc617b82020-08-20 08:22:56 +0000395 .fp_addr = ip_addr_46 (&ipn->ipn_key->ipnk_ip),
Neale Rannscbe25aa2019-09-30 10:53:31 +0000396 };
397
398 fib_table_entry_path_remove (fib_index,
399 &pfx,
400 FIB_SOURCE_ADJ,
401 fib_proto_to_dpo (fproto),
402 &pfx.fp_addr,
403 ipn->ipn_key->ipnk_sw_if_index,
404 ~0, 1, FIB_ROUTE_PATH_FLAG_NONE);
405
Neale Rannsdc617b82020-08-20 08:22:56 +0000406 ip_neighbor_db[af].ipndb_n_elts_per_fib[fib_index]--;
Neale Rannscbe25aa2019-09-30 10:53:31 +0000407
Neale Rannsdc617b82020-08-20 08:22:56 +0000408 if (0 == ip_neighbor_db[af].ipndb_n_elts_per_fib[fib_index])
Neale Rannscbe25aa2019-09-30 10:53:31 +0000409 fib_table_unlock (fib_index, fproto, FIB_SOURCE_ADJ);
410 }
411 }
412}
413
414static void
415ip_neighbor_mk_complete (adj_index_t ai, ip_neighbor_t * ipn)
416{
417 adj_nbr_update_rewrite (ai, ADJ_NBR_REWRITE_FLAG_COMPLETE,
418 ethernet_build_rewrite (vnet_get_main (),
419 ipn->
420 ipn_key->ipnk_sw_if_index,
421 adj_get_link_type (ai),
422 ipn->ipn_mac.bytes));
423}
424
425static void
426ip_neighbor_mk_incomplete (adj_index_t ai)
427{
428 ip_adjacency_t *adj = adj_get (ai);
429
430 adj_nbr_update_rewrite (ai,
431 ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
432 ethernet_build_rewrite (vnet_get_main (),
433 adj->
434 rewrite_header.sw_if_index,
Neale Rannsfca3c6a2019-12-31 03:49:34 +0000435 VNET_LINK_ARP,
Neale Rannscbe25aa2019-09-30 10:53:31 +0000436 VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
437}
438
439static adj_walk_rc_t
440ip_neighbor_mk_complete_walk (adj_index_t ai, void *ctx)
441{
442 ip_neighbor_t *ipn = ctx;
443
444 ip_neighbor_mk_complete (ai, ipn);
445
446 return (ADJ_WALK_RC_CONTINUE);
447}
448
449static adj_walk_rc_t
450ip_neighbor_mk_incomplete_walk (adj_index_t ai, void *ctx)
451{
452 ip_neighbor_mk_incomplete (ai);
453
454 return (ADJ_WALK_RC_CONTINUE);
455}
456
457static void
Neale Ranns4ac36bc2020-11-20 13:05:59 +0000458ip_neighbor_destroy (ip_neighbor_t * ipn)
Neale Rannscbe25aa2019-09-30 10:53:31 +0000459{
Neale Rannsdc617b82020-08-20 08:22:56 +0000460 ip_address_family_t af;
461
462 af = ip_neighbor_get_af (ipn);
463
Neale Rannscbe25aa2019-09-30 10:53:31 +0000464 IP_NEIGHBOR_DBG ("free: %U", format_ip_neighbor,
465 ip_neighbor_get_index (ipn));
466
Neale Ranns4ac36bc2020-11-20 13:05:59 +0000467 ip_neighbor_publish (ip_neighbor_get_index (ipn),
468 IP_NEIGHBOR_EVENT_REMOVED);
469
Neale Rannscbe25aa2019-09-30 10:53:31 +0000470 adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
Neale Rannsdc617b82020-08-20 08:22:56 +0000471 ip_address_family_to_fib_proto (af),
472 &ip_addr_46 (&ipn->ipn_key->ipnk_ip),
Neale Rannscbe25aa2019-09-30 10:53:31 +0000473 ip_neighbor_mk_incomplete_walk, ipn);
474 ip_neighbor_adj_fib_remove
475 (ipn,
476 fib_table_get_index_for_sw_if_index
Neale Rannsdc617b82020-08-20 08:22:56 +0000477 (ip_address_family_to_fib_proto (af), ipn->ipn_key->ipnk_sw_if_index));
Neale Rannscbe25aa2019-09-30 10:53:31 +0000478
479 ip_neighbor_list_remove (ipn);
Neale Rannsdc617b82020-08-20 08:22:56 +0000480 ip_neighbor_db_remove (ipn);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000481 clib_mem_free (ipn->ipn_key);
482
483 pool_put (ip_neighbor_pool, ipn);
484}
485
486static bool
Neale Rannsdc617b82020-08-20 08:22:56 +0000487ip_neighbor_force_reuse (ip_address_family_t af)
Neale Rannscbe25aa2019-09-30 10:53:31 +0000488{
Neale Rannsdc617b82020-08-20 08:22:56 +0000489 if (!ip_neighbor_db[af].ipndb_recycle)
Neale Rannscbe25aa2019-09-30 10:53:31 +0000490 return false;
491
492 /* pluck the oldest entry, which is the one from the end of the list */
493 ip_neighbor_elt_t *elt, *head;
494
Neale Rannsdc617b82020-08-20 08:22:56 +0000495 head = pool_elt_at_index (ip_neighbor_elt_pool, ip_neighbor_list_head[af]);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000496
497 if (clib_llist_is_empty (ip_neighbor_elt_pool, ipne_anchor, head))
498 return (false);
499
500 elt = clib_llist_prev (ip_neighbor_elt_pool, ipne_anchor, head);
Neale Ranns4ac36bc2020-11-20 13:05:59 +0000501 ip_neighbor_destroy (ip_neighbor_get (elt->ipne_index));
Neale Rannscbe25aa2019-09-30 10:53:31 +0000502
503 return (true);
504}
505
506static ip_neighbor_t *
507ip_neighbor_alloc (const ip_neighbor_key_t * key,
508 const mac_address_t * mac, ip_neighbor_flags_t flags)
509{
Neale Rannsdc617b82020-08-20 08:22:56 +0000510 ip_address_family_t af;
Neale Rannscbe25aa2019-09-30 10:53:31 +0000511 ip_neighbor_t *ipn;
512
Neale Rannsdc617b82020-08-20 08:22:56 +0000513 af = ip_addr_version (&key->ipnk_ip);
514
515 if (ip_neighbor_db[af].ipndb_limit &&
516 (ip_neighbor_db[af].ipndb_n_elts >= ip_neighbor_db[af].ipndb_limit))
Neale Rannscbe25aa2019-09-30 10:53:31 +0000517 {
Neale Rannsdc617b82020-08-20 08:22:56 +0000518 if (!ip_neighbor_force_reuse (af))
Neale Rannscbe25aa2019-09-30 10:53:31 +0000519 return (NULL);
520 }
521
522 pool_get_zero (ip_neighbor_pool, ipn);
523
524 ipn->ipn_key = clib_mem_alloc (sizeof (*ipn->ipn_key));
525 clib_memcpy (ipn->ipn_key, key, sizeof (*ipn->ipn_key));
526
527 ipn->ipn_fib_entry_index = FIB_NODE_INDEX_INVALID;
528 ipn->ipn_flags = flags;
529 ipn->ipn_elt = ~0;
530
531 mac_address_copy (&ipn->ipn_mac, mac);
532
533 ip_neighbor_db_add (ipn);
534
535 /* create the adj-fib. the entry in the FIB table for the peer's interface */
536 if (!(ipn->ipn_flags & IP_NEIGHBOR_FLAG_NO_FIB_ENTRY))
537 ip_neighbor_adj_fib_add
538 (ipn, fib_table_get_index_for_sw_if_index
Neale Rannsdc617b82020-08-20 08:22:56 +0000539 (ip_address_family_to_fib_proto (af), ipn->ipn_key->ipnk_sw_if_index));
Neale Rannscbe25aa2019-09-30 10:53:31 +0000540
541 return (ipn);
542}
543
544int
Neale Rannsdc617b82020-08-20 08:22:56 +0000545ip_neighbor_add (const ip_address_t * ip,
Neale Rannscbe25aa2019-09-30 10:53:31 +0000546 const mac_address_t * mac,
547 u32 sw_if_index,
548 ip_neighbor_flags_t flags, u32 * stats_index)
549{
550 fib_protocol_t fproto;
551 ip_neighbor_t *ipn;
552
553 /* main thread only */
554 ASSERT (0 == vlib_get_thread_index ());
555
Neale Rannsdc617b82020-08-20 08:22:56 +0000556 fproto = ip_address_family_to_fib_proto (ip_addr_version (ip));
Neale Rannscbe25aa2019-09-30 10:53:31 +0000557
558 const ip_neighbor_key_t key = {
559 .ipnk_ip = *ip,
560 .ipnk_sw_if_index = sw_if_index,
Neale Rannscbe25aa2019-09-30 10:53:31 +0000561 };
562
563 ipn = ip_neighbor_db_find (&key);
564
565 if (ipn)
566 {
567 IP_NEIGHBOR_DBG ("update: %U, %U",
568 format_vnet_sw_if_index_name, vnet_get_main (),
Neale Rannsdc617b82020-08-20 08:22:56 +0000569 sw_if_index, format_ip_address, ip,
Neale Rannscbe25aa2019-09-30 10:53:31 +0000570 format_ip_neighbor_flags, flags, format_mac_address_t,
571 mac);
572
Neale Rannsc87fbb42020-04-02 17:08:28 +0000573 ip_neighbor_touch (ipn);
574
Neale Rannscbe25aa2019-09-30 10:53:31 +0000575 /* Refuse to over-write static neighbor entry. */
576 if (!(flags & IP_NEIGHBOR_FLAG_STATIC) &&
577 (ipn->ipn_flags & IP_NEIGHBOR_FLAG_STATIC))
578 {
579 /* if MAC address match, still check to send event */
580 if (0 == mac_address_cmp (&ipn->ipn_mac, mac))
581 goto check_customers;
582 return -2;
583 }
584
Vladimir Isaevb2f44bd2020-07-16 17:05:18 +0300585 /* A dynamic entry can become static, but not vice-versa.
586 * i.e. since if it was programmed by the CP then it must
587 * be removed by the CP */
588 if ((flags & IP_NEIGHBOR_FLAG_STATIC) &&
589 !(ipn->ipn_flags & IP_NEIGHBOR_FLAG_STATIC))
590 {
591 ip_neighbor_list_remove (ipn);
592 ipn->ipn_flags |= IP_NEIGHBOR_FLAG_STATIC;
593 ipn->ipn_flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC;
594 }
595
Neale Rannscbe25aa2019-09-30 10:53:31 +0000596 /*
597 * prevent a DoS attack from the data-plane that
598 * spams us with no-op updates to the MAC address
599 */
600 if (0 == mac_address_cmp (&ipn->ipn_mac, mac))
601 {
602 ip_neighbor_refresh (ipn);
603 goto check_customers;
604 }
605
606 mac_address_copy (&ipn->ipn_mac, mac);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000607 }
608 else
609 {
610 IP_NEIGHBOR_INFO ("add: %U, %U",
611 format_vnet_sw_if_index_name, vnet_get_main (),
Neale Rannsdc617b82020-08-20 08:22:56 +0000612 sw_if_index, format_ip_address, ip,
Neale Rannscbe25aa2019-09-30 10:53:31 +0000613 format_ip_neighbor_flags, flags, format_mac_address_t,
614 mac);
615
616 ipn = ip_neighbor_alloc (&key, mac, flags);
617
618 if (NULL == ipn)
619 return VNET_API_ERROR_LIMIT_EXCEEDED;
620 }
621
622 /* Update time stamp and flags. */
623 ip_neighbor_refresh (ipn);
624
625 adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
Neale Rannsdc617b82020-08-20 08:22:56 +0000626 fproto, &ip_addr_46 (&ipn->ipn_key->ipnk_ip),
Neale Rannscbe25aa2019-09-30 10:53:31 +0000627 ip_neighbor_mk_complete_walk, ipn);
628
629check_customers:
630 /* Customer(s) requesting event for this address? */
Neale Ranns4ac36bc2020-11-20 13:05:59 +0000631 ip_neighbor_publish (ip_neighbor_get_index (ipn), IP_NEIGHBOR_EVENT_ADDED);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000632
633 if (stats_index)
634 *stats_index = adj_nbr_find (fproto,
635 fib_proto_to_link (fproto),
Neale Rannsdc617b82020-08-20 08:22:56 +0000636 &ip_addr_46 (&ipn->ipn_key->ipnk_ip),
Neale Rannscbe25aa2019-09-30 10:53:31 +0000637 ipn->ipn_key->ipnk_sw_if_index);
638 return 0;
639}
640
641int
Neale Rannsdc617b82020-08-20 08:22:56 +0000642ip_neighbor_del (const ip_address_t * ip, u32 sw_if_index)
Neale Rannscbe25aa2019-09-30 10:53:31 +0000643{
644 ip_neighbor_t *ipn;
645
646 /* main thread only */
647 ASSERT (0 == vlib_get_thread_index ());
648
649 IP_NEIGHBOR_INFO ("delete: %U, %U",
650 format_vnet_sw_if_index_name, vnet_get_main (),
Neale Rannsdc617b82020-08-20 08:22:56 +0000651 sw_if_index, format_ip_address, ip);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000652
653 const ip_neighbor_key_t key = {
654 .ipnk_ip = *ip,
655 .ipnk_sw_if_index = sw_if_index,
Neale Rannscbe25aa2019-09-30 10:53:31 +0000656 };
657
658 ipn = ip_neighbor_db_find (&key);
659
660 if (NULL == ipn)
661 return (VNET_API_ERROR_NO_SUCH_ENTRY);
662
Neale Ranns4ac36bc2020-11-20 13:05:59 +0000663 ip_neighbor_destroy (ipn);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000664
665 return (0);
666}
667
Neale Ranns240dcb22020-04-23 09:04:59 +0000668typedef struct ip_neighbor_del_all_ctx_t_
669{
670 index_t *ipn_del;
671} ip_neighbor_del_all_ctx_t;
672
673static walk_rc_t
674ip_neighbor_del_all_walk_cb (index_t ipni, void *arg)
675{
676 ip_neighbor_del_all_ctx_t *ctx = arg;
677
678 vec_add1 (ctx->ipn_del, ipni);
679
680 return (WALK_CONTINUE);
681}
682
683void
Neale Rannsdc617b82020-08-20 08:22:56 +0000684ip_neighbor_del_all (ip_address_family_t af, u32 sw_if_index)
Neale Ranns240dcb22020-04-23 09:04:59 +0000685{
686 IP_NEIGHBOR_INFO ("delete-all: %U, %U",
Neale Rannsdc617b82020-08-20 08:22:56 +0000687 format_ip_address_family, af,
Neale Ranns240dcb22020-04-23 09:04:59 +0000688 format_vnet_sw_if_index_name, vnet_get_main (),
689 sw_if_index);
690
691 ip_neighbor_del_all_ctx_t ctx = {
692 .ipn_del = NULL,
693 };
694 index_t *ipni;
695
Neale Rannsdc617b82020-08-20 08:22:56 +0000696 ip_neighbor_walk (af, sw_if_index, ip_neighbor_del_all_walk_cb, &ctx);
Neale Ranns240dcb22020-04-23 09:04:59 +0000697
Neale Ranns4ac36bc2020-11-20 13:05:59 +0000698 vec_foreach (ipni,
699 ctx.ipn_del) ip_neighbor_destroy (ip_neighbor_get (*ipni));
Neale Ranns240dcb22020-04-23 09:04:59 +0000700 vec_free (ctx.ipn_del);
701}
702
Neale Rannscbe25aa2019-09-30 10:53:31 +0000703void
704ip_neighbor_update (vnet_main_t * vnm, adj_index_t ai)
705{
706 ip_neighbor_t *ipn;
707 ip_adjacency_t *adj;
708
709 adj = adj_get (ai);
710
711 ip_neighbor_key_t key = {
Neale Rannscbe25aa2019-09-30 10:53:31 +0000712 .ipnk_sw_if_index = adj->rewrite_header.sw_if_index,
713 };
Neale Rannsdc617b82020-08-20 08:22:56 +0000714
715 ip_address_from_46 (&adj->sub_type.nbr.next_hop,
716 adj->ia_nh_proto, &key.ipnk_ip);
717
Neale Rannscbe25aa2019-09-30 10:53:31 +0000718 ipn = ip_neighbor_db_find (&key);
719
720 switch (adj->lookup_next_index)
721 {
722 case IP_LOOKUP_NEXT_ARP:
723 if (NULL != ipn)
724 {
725 adj_nbr_walk_nh (adj->rewrite_header.sw_if_index,
726 adj->ia_nh_proto,
Neale Rannsdc617b82020-08-20 08:22:56 +0000727 &adj->sub_type.nbr.next_hop,
Neale Rannscbe25aa2019-09-30 10:53:31 +0000728 ip_neighbor_mk_complete_walk, ipn);
729 }
730 else
731 {
732 /*
733 * no matching ARP entry.
734 * construct the rewrite required to for an ARP packet, and stick
735 * that in the adj's pipe to smoke.
736 */
737 adj_nbr_update_rewrite
738 (ai,
739 ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
740 ethernet_build_rewrite
741 (vnm,
742 adj->rewrite_header.sw_if_index,
743 VNET_LINK_ARP,
744 VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
745
746 /*
747 * since the FIB has added this adj for a route, it makes sense it
748 * may want to forward traffic sometime soon. Let's send a
749 * speculative ARP. just one. If we were to do periodically that
750 * wouldn't be bad either, but that's more code than i'm prepared to
751 * write at this time for relatively little reward.
752 */
Steven Luong3d5f6222020-01-30 09:11:18 -0800753 /*
754 * adj_nbr_update_rewrite may actually call fib_walk_sync.
755 * fib_walk_sync may allocate a new adjacency and potentially cause
756 * a realloc for adj_pool. When that happens, adj pointer is no
757 * longer valid here.x We refresh adj pointer accordingly.
758 */
759 adj = adj_get (ai);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000760 ip_neighbor_probe (adj);
761 }
762 break;
Neale Rannsea8adf72021-08-13 08:10:59 +0000763 case IP_LOOKUP_NEXT_REWRITE:
764 /* Update of an existing rewrite adjacency happens e.g. when the
765 * interface's MAC address changes */
766 if (NULL != ipn)
767 ip_neighbor_mk_complete (ai, ipn);
768 break;
Neale Rannscbe25aa2019-09-30 10:53:31 +0000769 case IP_LOOKUP_NEXT_GLEAN:
770 case IP_LOOKUP_NEXT_BCAST:
771 case IP_LOOKUP_NEXT_MCAST:
772 case IP_LOOKUP_NEXT_DROP:
773 case IP_LOOKUP_NEXT_PUNT:
774 case IP_LOOKUP_NEXT_LOCAL:
Neale Rannscbe25aa2019-09-30 10:53:31 +0000775 case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
776 case IP_LOOKUP_NEXT_MIDCHAIN:
777 case IP_LOOKUP_NEXT_ICMP_ERROR:
778 case IP_LOOKUP_N_NEXT:
779 ASSERT (0);
780 break;
781 }
782}
783
784void
785ip_neighbor_learn (const ip_neighbor_learn_t * l)
786{
Neale Rannsdc617b82020-08-20 08:22:56 +0000787 ip_neighbor_add (&l->ip, &l->mac, l->sw_if_index,
Neale Rannscbe25aa2019-09-30 10:53:31 +0000788 IP_NEIGHBOR_FLAG_DYNAMIC, NULL);
789}
790
791static clib_error_t *
792ip_neighbor_cmd (vlib_main_t * vm,
793 unformat_input_t * input, vlib_cli_command_t * cmd)
794{
Neale Rannsdc617b82020-08-20 08:22:56 +0000795 ip_address_t ip = IP_ADDRESS_V6_ALL_0S;
Neale Rannscbe25aa2019-09-30 10:53:31 +0000796 mac_address_t mac = ZERO_MAC_ADDRESS;
797 vnet_main_t *vnm = vnet_get_main ();
798 ip_neighbor_flags_t flags;
799 u32 sw_if_index = ~0;
800 int is_add = 1;
801 int count = 1;
802
803 flags = IP_NEIGHBOR_FLAG_DYNAMIC;
804
805 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
806 {
807 /* set ip arp TenGigE1/1/0/1 1.2.3.4 aa:bb:... or aabb.ccdd... */
808 if (unformat (input, "%U %U %U",
809 unformat_vnet_sw_interface, vnm, &sw_if_index,
Neale Rannsdc617b82020-08-20 08:22:56 +0000810 unformat_ip_address, &ip, unformat_mac_address_t, &mac))
Neale Rannscbe25aa2019-09-30 10:53:31 +0000811 ;
812 else if (unformat (input, "delete") || unformat (input, "del"))
813 is_add = 0;
814 else if (unformat (input, "static"))
815 {
816 flags |= IP_NEIGHBOR_FLAG_STATIC;
817 flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC;
818 }
819 else if (unformat (input, "no-fib-entry"))
820 flags |= IP_NEIGHBOR_FLAG_NO_FIB_ENTRY;
821 else if (unformat (input, "count %d", &count))
822 ;
823 else
824 break;
825 }
826
827 if (sw_if_index == ~0 ||
Neale Rannsdc617b82020-08-20 08:22:56 +0000828 ip_address_is_zero (&ip) || mac_address_is_zero (&mac))
Neale Rannscbe25aa2019-09-30 10:53:31 +0000829 return clib_error_return (0,
830 "specify interface, IP address and MAC: `%U'",
831 format_unformat_error, input);
832
833 while (count)
834 {
835 if (is_add)
Neale Rannsdc617b82020-08-20 08:22:56 +0000836 ip_neighbor_add (&ip, &mac, sw_if_index, flags, NULL);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000837 else
Neale Rannsdc617b82020-08-20 08:22:56 +0000838 ip_neighbor_del (&ip, sw_if_index);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000839
Neale Rannsdc617b82020-08-20 08:22:56 +0000840 ip_address_increment (&ip);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000841 mac_address_increment (&mac);
842
843 --count;
844 }
845
846 return NULL;
847}
848
849/* *INDENT-OFF* */
850/*?
851 * Add or delete IPv4 ARP cache entries.
852 *
853 * @note 'set ip neighbor' options (e.g. delete, static, 'fib-id <id>',
854 * 'count <number>', 'interface ip4_addr mac_addr') can be added in
855 * any order and combination.
856 *
857 * @cliexpar
858 * @parblock
859 * Add or delete IPv4 ARP cache entries as follows. MAC Address can be in
860 * either aa:bb:cc:dd:ee:ff format or aabb.ccdd.eeff format.
861 * @cliexcmd{set ip neighbor GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
862 * @cliexcmd{set ip neighbor delete GigabitEthernet2/0/0 6.0.0.3 de:ad:be:ef:ba:be}
863 *
864 * To add or delete an IPv4 ARP cache entry to or from a specific fib
865 * table:
866 * @cliexcmd{set ip neighbor fib-id 1 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
867 * @cliexcmd{set ip neighbor fib-id 1 delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
868 *
869 * Add or delete IPv4 static ARP cache entries as follows:
870 * @cliexcmd{set ip neighbor static GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
871 * @cliexcmd{set ip neighbor static delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
872 *
873 * For testing / debugging purposes, the 'set ip neighbor' command can add or
874 * delete multiple entries. Supply the 'count N' parameter:
875 * @cliexcmd{set ip neighbor count 10 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
876 * @endparblock
877 ?*/
878VLIB_CLI_COMMAND (ip_neighbor_command, static) = {
879 .path = "set ip neighbor",
880 .short_help =
881 "set ip neighbor [del] <intfc> <ip-address> <mac-address> [static] [no-fib-entry] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
882 .function = ip_neighbor_cmd,
883};
884VLIB_CLI_COMMAND (ip_neighbor_command2, static) = {
885 .path = "ip neighbor",
886 .short_help =
887 "ip neighbor [del] <intfc> <ip-address> <mac-address> [static] [no-fib-entry] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
888 .function = ip_neighbor_cmd,
889};
890/* *INDENT-ON* */
891
892static int
893ip_neighbor_sort (void *a1, void *a2)
894{
895 index_t *ipni1 = a1, *ipni2 = a2;
896 ip_neighbor_t *ipn1, *ipn2;
897 int cmp;
898
899 ipn1 = ip_neighbor_get (*ipni1);
900 ipn2 = ip_neighbor_get (*ipni2);
901
902 cmp = vnet_sw_interface_compare (vnet_get_main (),
903 ipn1->ipn_key->ipnk_sw_if_index,
904 ipn2->ipn_key->ipnk_sw_if_index);
905 if (!cmp)
Neale Rannsdc617b82020-08-20 08:22:56 +0000906 cmp = ip_address_cmp (&ipn1->ipn_key->ipnk_ip, &ipn2->ipn_key->ipnk_ip);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000907 return cmp;
908}
909
910static index_t *
Neale Rannsdc617b82020-08-20 08:22:56 +0000911ip_neighbor_entries (u32 sw_if_index, ip_address_family_t af)
Neale Rannscbe25aa2019-09-30 10:53:31 +0000912{
913 index_t *ipnis = NULL;
914 ip_neighbor_t *ipn;
915
916 /* *INDENT-OFF* */
Damjan Marionb2c31b62020-12-13 21:47:40 +0100917 pool_foreach (ipn, ip_neighbor_pool)
918 {
Michael Yubedf48a2020-05-08 16:42:58 +0800919 if ((sw_if_index == ~0 ||
920 ipn->ipn_key->ipnk_sw_if_index == sw_if_index) &&
Neale Rannsdc617b82020-08-20 08:22:56 +0000921 (N_AF == af ||
922 ip_neighbor_get_af(ipn) == af))
Michael Yubedf48a2020-05-08 16:42:58 +0800923 vec_add1 (ipnis, ip_neighbor_get_index(ipn));
Damjan Marionb2c31b62020-12-13 21:47:40 +0100924 }
Neale Rannscbe25aa2019-09-30 10:53:31 +0000925
926 /* *INDENT-ON* */
927
928 if (ipnis)
929 vec_sort_with_function (ipnis, ip_neighbor_sort);
930 return ipnis;
931}
932
933static clib_error_t *
934ip_neighbor_show_sorted_i (vlib_main_t * vm,
935 unformat_input_t * input,
Neale Rannsdc617b82020-08-20 08:22:56 +0000936 vlib_cli_command_t * cmd, ip_address_family_t af)
Neale Rannscbe25aa2019-09-30 10:53:31 +0000937{
938 ip_neighbor_elt_t *elt, *head;
939
Neale Rannsdc617b82020-08-20 08:22:56 +0000940 head = pool_elt_at_index (ip_neighbor_elt_pool, ip_neighbor_list_head[af]);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000941
942
943 vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP",
944 "Flags", "Ethernet", "Interface");
945
946 /* *INDENT-OFF*/
947 /* the list is time sorted, newest first, so start from the back
948 * and work forwards. Stop when we get to one that is alive */
949 clib_llist_foreach_reverse(ip_neighbor_elt_pool,
950 ipne_anchor, head, elt,
951 ({
952 vlib_cli_output (vm, "%U", format_ip_neighbor, elt->ipne_index);
953 }));
954 /* *INDENT-ON*/
955
956 return (NULL);
957}
958
959static clib_error_t *
960ip_neighbor_show_i (vlib_main_t * vm,
961 unformat_input_t * input,
Neale Rannsdc617b82020-08-20 08:22:56 +0000962 vlib_cli_command_t * cmd, ip_address_family_t af)
Neale Rannscbe25aa2019-09-30 10:53:31 +0000963{
964 index_t *ipni, *ipnis = NULL;
965 u32 sw_if_index;
966
967 /* Filter entries by interface if given. */
968 sw_if_index = ~0;
969 (void) unformat_user (input, unformat_vnet_sw_interface, vnet_get_main (),
970 &sw_if_index);
971
Neale Rannsdc617b82020-08-20 08:22:56 +0000972 ipnis = ip_neighbor_entries (sw_if_index, af);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000973
974 if (ipnis)
975 vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP",
976 "Flags", "Ethernet", "Interface");
977
978 vec_foreach (ipni, ipnis)
979 {
980 vlib_cli_output (vm, "%U", format_ip_neighbor, *ipni);
981 }
982 vec_free (ipnis);
983
984 return (NULL);
985}
986
987static clib_error_t *
988ip_neighbor_show (vlib_main_t * vm,
989 unformat_input_t * input, vlib_cli_command_t * cmd)
990{
Neale Rannsdc617b82020-08-20 08:22:56 +0000991 return (ip_neighbor_show_i (vm, input, cmd, N_AF));
Neale Rannscbe25aa2019-09-30 10:53:31 +0000992}
993
994static clib_error_t *
995ip6_neighbor_show (vlib_main_t * vm,
996 unformat_input_t * input, vlib_cli_command_t * cmd)
997{
Neale Rannsdc617b82020-08-20 08:22:56 +0000998 return (ip_neighbor_show_i (vm, input, cmd, AF_IP6));
Neale Rannscbe25aa2019-09-30 10:53:31 +0000999}
1000
1001static clib_error_t *
1002ip4_neighbor_show (vlib_main_t * vm,
1003 unformat_input_t * input, vlib_cli_command_t * cmd)
1004{
Neale Rannsdc617b82020-08-20 08:22:56 +00001005 return (ip_neighbor_show_i (vm, input, cmd, AF_IP4));
Neale Rannscbe25aa2019-09-30 10:53:31 +00001006}
1007
1008static clib_error_t *
1009ip6_neighbor_show_sorted (vlib_main_t * vm,
1010 unformat_input_t * input, vlib_cli_command_t * cmd)
1011{
Neale Rannsdc617b82020-08-20 08:22:56 +00001012 return (ip_neighbor_show_sorted_i (vm, input, cmd, AF_IP6));
Neale Rannscbe25aa2019-09-30 10:53:31 +00001013}
1014
1015static clib_error_t *
1016ip4_neighbor_show_sorted (vlib_main_t * vm,
1017 unformat_input_t * input, vlib_cli_command_t * cmd)
1018{
Neale Rannsdc617b82020-08-20 08:22:56 +00001019 return (ip_neighbor_show_sorted_i (vm, input, cmd, AF_IP4));
Neale Rannscbe25aa2019-09-30 10:53:31 +00001020}
1021
1022/*?
1023 * Display all the IP neighbor entries.
1024 *
1025 * @cliexpar
1026 * Example of how to display the IPv4 ARP table:
1027 * @cliexstart{show ip neighbor}
1028 * Time FIB IP4 Flags Ethernet Interface
1029 * 346.3028 0 6.1.1.3 de:ad:be:ef:ba:be GigabitEthernet2/0/0
1030 * 3077.4271 0 6.1.1.4 S de:ad:be:ef:ff:ff GigabitEthernet2/0/0
1031 * 2998.6409 1 6.2.2.3 de:ad:be:ef:00:01 GigabitEthernet2/0/0
1032 * Proxy arps enabled for:
1033 * Fib_index 0 6.0.0.1 - 6.0.0.11
1034 * @cliexend
1035 ?*/
1036/* *INDENT-OFF* */
1037VLIB_CLI_COMMAND (show_ip_neighbors_cmd_node, static) = {
1038 .path = "show ip neighbors",
1039 .function = ip_neighbor_show,
1040 .short_help = "show ip neighbors [interface]",
1041};
1042VLIB_CLI_COMMAND (show_ip4_neighbors_cmd_node, static) = {
1043 .path = "show ip4 neighbors",
1044 .function = ip4_neighbor_show,
1045 .short_help = "show ip4 neighbors [interface]",
1046};
1047VLIB_CLI_COMMAND (show_ip6_neighbors_cmd_node, static) = {
1048 .path = "show ip6 neighbors",
1049 .function = ip6_neighbor_show,
1050 .short_help = "show ip6 neighbors [interface]",
1051};
1052VLIB_CLI_COMMAND (show_ip_neighbor_cmd_node, static) = {
1053 .path = "show ip neighbor",
1054 .function = ip_neighbor_show,
1055 .short_help = "show ip neighbor [interface]",
1056};
1057VLIB_CLI_COMMAND (show_ip4_neighbor_cmd_node, static) = {
1058 .path = "show ip4 neighbor",
1059 .function = ip4_neighbor_show,
1060 .short_help = "show ip4 neighbor [interface]",
1061};
1062VLIB_CLI_COMMAND (show_ip6_neighbor_cmd_node, static) = {
1063 .path = "show ip6 neighbor",
1064 .function = ip6_neighbor_show,
1065 .short_help = "show ip6 neighbor [interface]",
1066};
1067VLIB_CLI_COMMAND (show_ip4_neighbor_sorted_cmd_node, static) = {
1068 .path = "show ip4 neighbor-sorted",
1069 .function = ip4_neighbor_show_sorted,
1070 .short_help = "show ip4 neighbor-sorted",
1071};
1072VLIB_CLI_COMMAND (show_ip6_neighbor_sorted_cmd_node, static) = {
1073 .path = "show ip6 neighbor-sorted",
1074 .function = ip6_neighbor_show_sorted,
1075 .short_help = "show ip6 neighbor-sorted",
1076};
1077/* *INDENT-ON* */
1078
Neale Rannsdc617b82020-08-20 08:22:56 +00001079static ip_neighbor_vft_t ip_nbr_vfts[N_AF];
Neale Rannscbe25aa2019-09-30 10:53:31 +00001080
1081void
Neale Rannsdc617b82020-08-20 08:22:56 +00001082ip_neighbor_register (ip_address_family_t af, const ip_neighbor_vft_t * vft)
Neale Rannscbe25aa2019-09-30 10:53:31 +00001083{
Neale Rannsdc617b82020-08-20 08:22:56 +00001084 ip_nbr_vfts[af] = *vft;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001085}
1086
1087void
Neale Rannsfd2417b2021-07-16 14:00:16 +00001088ip_neighbor_probe_dst (u32 sw_if_index, u32 thread_index,
1089 ip_address_family_t af, const ip46_address_t *dst)
Neale Rannscbe25aa2019-09-30 10:53:31 +00001090{
Neale Rannse2fe0972020-11-26 08:37:27 +00001091 if (!vnet_sw_interface_is_admin_up (vnet_get_main (), sw_if_index))
Neale Rannscbe25aa2019-09-30 10:53:31 +00001092 return;
1093
Neale Rannse2fe0972020-11-26 08:37:27 +00001094 switch (af)
Neale Rannscbe25aa2019-09-30 10:53:31 +00001095 {
Neale Rannse2fe0972020-11-26 08:37:27 +00001096 case AF_IP6:
Neale Rannsfd2417b2021-07-16 14:00:16 +00001097 ip6_neighbor_probe_dst (sw_if_index, thread_index, &dst->ip6);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001098 break;
Neale Rannse2fe0972020-11-26 08:37:27 +00001099 case AF_IP4:
Neale Rannsfd2417b2021-07-16 14:00:16 +00001100 ip4_neighbor_probe_dst (sw_if_index, thread_index, &dst->ip4);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001101 break;
1102 }
1103}
1104
1105void
1106ip_neighbor_probe (const ip_adjacency_t * adj)
1107{
Neale Rannse2fe0972020-11-26 08:37:27 +00001108 ip_neighbor_probe_dst (adj->rewrite_header.sw_if_index,
Neale Rannsfd2417b2021-07-16 14:00:16 +00001109 vlib_get_thread_index (),
Neale Rannse2fe0972020-11-26 08:37:27 +00001110 ip_address_family_from_fib_proto (adj->ia_nh_proto),
1111 &adj->sub_type.nbr.next_hop);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001112}
1113
1114void
Neale Rannsdc617b82020-08-20 08:22:56 +00001115ip_neighbor_walk (ip_address_family_t af,
Neale Rannscbe25aa2019-09-30 10:53:31 +00001116 u32 sw_if_index, ip_neighbor_walk_cb_t cb, void *ctx)
1117{
1118 ip_neighbor_key_t *key;
1119 index_t ipni;
1120
1121 if (~0 == sw_if_index)
1122 {
1123 uword **hash;
1124
Neale Rannsdc617b82020-08-20 08:22:56 +00001125 vec_foreach (hash, ip_neighbor_db[af].ipndb_hash)
Neale Rannscbe25aa2019-09-30 10:53:31 +00001126 {
1127 /* *INDENT-OFF* */
1128 hash_foreach (key, ipni, *hash,
1129 ({
Ruslan Babayev24b417c2020-02-02 17:30:31 -08001130 if (WALK_STOP == cb (ipni, ctx))
1131 break;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001132 }));
1133 /* *INDENT-ON* */
1134 }
1135 }
1136 else
1137 {
1138 uword *hash;
1139
Neale Rannsdc617b82020-08-20 08:22:56 +00001140 if (vec_len (ip_neighbor_db[af].ipndb_hash) <= sw_if_index)
Neale Rannscbe25aa2019-09-30 10:53:31 +00001141 return;
Neale Rannsdc617b82020-08-20 08:22:56 +00001142 hash = ip_neighbor_db[af].ipndb_hash[sw_if_index];
Neale Rannscbe25aa2019-09-30 10:53:31 +00001143
1144 /* *INDENT-OFF* */
1145 hash_foreach (key, ipni, hash,
1146 ({
Ruslan Babayev24b417c2020-02-02 17:30:31 -08001147 if (WALK_STOP == cb (ipni, ctx))
1148 break;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001149 }));
1150 /* *INDENT-ON* */
1151 }
1152}
1153
1154int
1155ip4_neighbor_proxy_add (u32 fib_index,
1156 const ip4_address_t * start,
1157 const ip4_address_t * end)
1158{
Neale Rannsdc617b82020-08-20 08:22:56 +00001159 if (ip_nbr_vfts[AF_IP4].inv_proxy4_add)
Neale Rannscbe25aa2019-09-30 10:53:31 +00001160 {
Neale Rannsdc617b82020-08-20 08:22:56 +00001161 return (ip_nbr_vfts[AF_IP4].inv_proxy4_add (fib_index, start, end));
Neale Rannscbe25aa2019-09-30 10:53:31 +00001162 }
1163
1164 return (-1);
1165}
1166
1167int
1168ip4_neighbor_proxy_delete (u32 fib_index,
1169 const ip4_address_t * start,
1170 const ip4_address_t * end)
1171{
Neale Rannsdc617b82020-08-20 08:22:56 +00001172 if (ip_nbr_vfts[AF_IP4].inv_proxy4_del)
Neale Rannscbe25aa2019-09-30 10:53:31 +00001173 {
Neale Rannsdc617b82020-08-20 08:22:56 +00001174 return (ip_nbr_vfts[AF_IP4].inv_proxy4_del (fib_index, start, end));
Neale Rannscbe25aa2019-09-30 10:53:31 +00001175 }
1176 return -1;
1177}
1178
1179int
1180ip4_neighbor_proxy_enable (u32 sw_if_index)
1181{
Neale Rannsdc617b82020-08-20 08:22:56 +00001182 if (ip_nbr_vfts[AF_IP4].inv_proxy4_enable)
Neale Rannscbe25aa2019-09-30 10:53:31 +00001183 {
Neale Rannsdc617b82020-08-20 08:22:56 +00001184 return (ip_nbr_vfts[AF_IP4].inv_proxy4_enable (sw_if_index));
Neale Rannscbe25aa2019-09-30 10:53:31 +00001185 }
1186 return -1;
1187}
1188
1189int
1190ip4_neighbor_proxy_disable (u32 sw_if_index)
1191{
Neale Rannsdc617b82020-08-20 08:22:56 +00001192 if (ip_nbr_vfts[AF_IP4].inv_proxy4_disable)
Neale Rannscbe25aa2019-09-30 10:53:31 +00001193 {
Neale Rannsdc617b82020-08-20 08:22:56 +00001194 return (ip_nbr_vfts[AF_IP4].inv_proxy4_disable (sw_if_index));
Neale Rannscbe25aa2019-09-30 10:53:31 +00001195 }
1196 return -1;
1197}
1198
1199int
1200ip6_neighbor_proxy_add (u32 sw_if_index, const ip6_address_t * addr)
1201{
Neale Rannsdc617b82020-08-20 08:22:56 +00001202 if (ip_nbr_vfts[AF_IP6].inv_proxy6_add)
Neale Rannscbe25aa2019-09-30 10:53:31 +00001203 {
Neale Rannsdc617b82020-08-20 08:22:56 +00001204 return (ip_nbr_vfts[AF_IP6].inv_proxy6_add (sw_if_index, addr));
Neale Rannscbe25aa2019-09-30 10:53:31 +00001205 }
1206 return -1;
1207}
1208
1209int
1210ip6_neighbor_proxy_del (u32 sw_if_index, const ip6_address_t * addr)
1211{
Neale Rannsdc617b82020-08-20 08:22:56 +00001212 if (ip_nbr_vfts[AF_IP6].inv_proxy6_del)
Neale Rannscbe25aa2019-09-30 10:53:31 +00001213 {
Neale Rannsdc617b82020-08-20 08:22:56 +00001214 return (ip_nbr_vfts[AF_IP6].inv_proxy6_del (sw_if_index, addr));
Neale Rannscbe25aa2019-09-30 10:53:31 +00001215 }
1216 return -1;
1217}
1218
Neale Rannscbe25aa2019-09-30 10:53:31 +00001219void
Neale Rannsdc617b82020-08-20 08:22:56 +00001220ip_neighbor_populate (ip_address_family_t af, u32 sw_if_index)
Neale Rannscbe25aa2019-09-30 10:53:31 +00001221{
1222 index_t *ipnis = NULL, *ipni;
1223 ip_neighbor_t *ipn;
1224
1225 IP_NEIGHBOR_DBG ("populate: %U %U",
1226 format_vnet_sw_if_index_name, vnet_get_main (),
Neale Rannsdc617b82020-08-20 08:22:56 +00001227 sw_if_index, format_ip_address_family, af);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001228
1229 /* *INDENT-OFF* */
Damjan Marionb2c31b62020-12-13 21:47:40 +01001230 pool_foreach (ipn, ip_neighbor_pool)
1231 {
Neale Rannsdc617b82020-08-20 08:22:56 +00001232 if (ip_neighbor_get_af(ipn) == af &&
Neale Rannscbe25aa2019-09-30 10:53:31 +00001233 ipn->ipn_key->ipnk_sw_if_index == sw_if_index)
1234 vec_add1 (ipnis, ipn - ip_neighbor_pool);
Damjan Marionb2c31b62020-12-13 21:47:40 +01001235 }
Neale Rannscbe25aa2019-09-30 10:53:31 +00001236 /* *INDENT-ON* */
1237
1238 vec_foreach (ipni, ipnis)
1239 {
1240 ipn = ip_neighbor_get (*ipni);
1241
1242 adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
Neale Rannsdc617b82020-08-20 08:22:56 +00001243 ip_address_family_to_fib_proto (ip_neighbor_get_af
1244 (ipn)),
1245 &ip_addr_46 (&ipn->ipn_key->ipnk_ip),
Neale Rannscbe25aa2019-09-30 10:53:31 +00001246 ip_neighbor_mk_complete_walk, ipn);
1247 }
1248 vec_free (ipnis);
1249}
1250
1251void
Neale Rannsdc617b82020-08-20 08:22:56 +00001252ip_neighbor_flush (ip_address_family_t af, u32 sw_if_index)
Neale Rannscbe25aa2019-09-30 10:53:31 +00001253{
1254 index_t *ipnis = NULL, *ipni;
1255 ip_neighbor_t *ipn;
1256
Neale Rannsdc617b82020-08-20 08:22:56 +00001257
Neale Rannscbe25aa2019-09-30 10:53:31 +00001258 IP_NEIGHBOR_DBG ("flush: %U %U",
1259 format_vnet_sw_if_index_name, vnet_get_main (),
Neale Rannsdc617b82020-08-20 08:22:56 +00001260 sw_if_index, format_ip_address_family, af);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001261
1262 /* *INDENT-OFF* */
Damjan Marionb2c31b62020-12-13 21:47:40 +01001263 pool_foreach (ipn, ip_neighbor_pool)
1264 {
Neale Rannsdc617b82020-08-20 08:22:56 +00001265 if (ip_neighbor_get_af(ipn) == af &&
Neale Rannscbe25aa2019-09-30 10:53:31 +00001266 ipn->ipn_key->ipnk_sw_if_index == sw_if_index &&
1267 ip_neighbor_is_dynamic (ipn))
1268 vec_add1 (ipnis, ipn - ip_neighbor_pool);
Damjan Marionb2c31b62020-12-13 21:47:40 +01001269 }
Neale Rannscbe25aa2019-09-30 10:53:31 +00001270 /* *INDENT-ON* */
1271
Neale Ranns4ac36bc2020-11-20 13:05:59 +00001272 vec_foreach (ipni, ipnis) ip_neighbor_destroy (ip_neighbor_get (*ipni));
Neale Rannscbe25aa2019-09-30 10:53:31 +00001273 vec_free (ipnis);
1274}
1275
Stanislav Zaikin3bad8b62022-04-25 19:11:36 +02001276walk_rc_t
Neale Rannsc87fbb42020-04-02 17:08:28 +00001277ip_neighbor_mark_one (index_t ipni, void *ctx)
1278{
1279 ip_neighbor_t *ipn;
1280
1281 ipn = ip_neighbor_get (ipni);
1282
1283 ipn->ipn_flags |= IP_NEIGHBOR_FLAG_STALE;
1284
1285 return (WALK_CONTINUE);
1286}
1287
1288void
Neale Rannsdc617b82020-08-20 08:22:56 +00001289ip_neighbor_mark (ip_address_family_t af)
Neale Rannsc87fbb42020-04-02 17:08:28 +00001290{
Neale Rannsdc617b82020-08-20 08:22:56 +00001291 ip_neighbor_walk (af, ~0, ip_neighbor_mark_one, NULL);
Neale Rannsc87fbb42020-04-02 17:08:28 +00001292}
1293
1294typedef struct ip_neighbor_sweep_ctx_t_
1295{
1296 index_t *ipnsc_stale;
1297} ip_neighbor_sweep_ctx_t;
1298
1299static walk_rc_t
1300ip_neighbor_sweep_one (index_t ipni, void *arg)
1301{
1302 ip_neighbor_sweep_ctx_t *ctx = arg;
1303 ip_neighbor_t *ipn;
1304
1305 ipn = ip_neighbor_get (ipni);
1306
1307 if (ipn->ipn_flags & IP_NEIGHBOR_FLAG_STALE)
1308 {
1309 vec_add1 (ctx->ipnsc_stale, ipni);
1310 }
1311
1312 return (WALK_CONTINUE);
1313}
1314
1315void
Neale Rannsdc617b82020-08-20 08:22:56 +00001316ip_neighbor_sweep (ip_address_family_t af)
Neale Rannsc87fbb42020-04-02 17:08:28 +00001317{
1318 ip_neighbor_sweep_ctx_t ctx = { };
1319 index_t *ipni;
1320
Neale Rannsdc617b82020-08-20 08:22:56 +00001321 ip_neighbor_walk (af, ~0, ip_neighbor_sweep_one, &ctx);
Neale Rannsc87fbb42020-04-02 17:08:28 +00001322
1323 vec_foreach (ipni, ctx.ipnsc_stale)
1324 {
Neale Ranns4ac36bc2020-11-20 13:05:59 +00001325 ip_neighbor_destroy (ip_neighbor_get (*ipni));
Neale Rannsc87fbb42020-04-02 17:08:28 +00001326 }
1327 vec_free (ctx.ipnsc_stale);
1328}
1329
Neale Rannscbe25aa2019-09-30 10:53:31 +00001330/*
1331 * Remove any arp entries associated with the specified interface
1332 */
1333static clib_error_t *
1334ip_neighbor_interface_admin_change (vnet_main_t * vnm,
1335 u32 sw_if_index, u32 flags)
1336{
Neale Rannsdc617b82020-08-20 08:22:56 +00001337 ip_address_family_t af;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001338
1339 IP_NEIGHBOR_DBG ("interface-admin: %U %s",
1340 format_vnet_sw_if_index_name, vnet_get_main (),
1341 sw_if_index,
1342 (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ? "up" : "down"));
1343
1344 if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
1345 {
Neale Rannsdc617b82020-08-20 08:22:56 +00001346 FOR_EACH_IP_ADDRESS_FAMILY (af) ip_neighbor_populate (af, sw_if_index);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001347 }
1348 else
1349 {
1350 /* admin down, flush all neighbours */
Neale Rannsdc617b82020-08-20 08:22:56 +00001351 FOR_EACH_IP_ADDRESS_FAMILY (af) ip_neighbor_flush (af, sw_if_index);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001352 }
1353
1354 return (NULL);
1355}
1356
1357VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ip_neighbor_interface_admin_change);
1358
1359/*
1360 * Remove any arp entries associated with the specified interface
1361 */
1362static clib_error_t *
Neale Rannsfd2417b2021-07-16 14:00:16 +00001363ip_neighbor_add_del_sw_interface (vnet_main_t *vnm, u32 sw_if_index,
1364 u32 is_add)
Neale Rannscbe25aa2019-09-30 10:53:31 +00001365{
1366 IP_NEIGHBOR_DBG ("interface-change: %U %s",
1367 format_vnet_sw_if_index_name, vnet_get_main (),
1368 sw_if_index, (is_add ? "add" : "del"));
1369
1370 if (!is_add && sw_if_index != ~0)
1371 {
Neale Rannsdc617b82020-08-20 08:22:56 +00001372 ip_address_family_t af;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001373
Neale Rannsdc617b82020-08-20 08:22:56 +00001374 FOR_EACH_IP_ADDRESS_FAMILY (af) ip_neighbor_flush (af, sw_if_index);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001375 }
1376
Neale Rannsfd2417b2021-07-16 14:00:16 +00001377 if (is_add)
1378 {
1379 ip_neighbor_alloc_ctr (&ip_neighbor_counters[AF_IP4], sw_if_index);
1380 ip_neighbor_alloc_ctr (&ip_neighbor_counters[AF_IP6], sw_if_index);
1381 }
1382
Neale Rannscbe25aa2019-09-30 10:53:31 +00001383 return (NULL);
1384}
1385
Neale Rannsfd2417b2021-07-16 14:00:16 +00001386VNET_SW_INTERFACE_ADD_DEL_FUNCTION (ip_neighbor_add_del_sw_interface);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001387
1388typedef struct ip_neighbor_walk_covered_ctx_t_
1389{
Neale Rannsdc617b82020-08-20 08:22:56 +00001390 ip_address_t addr;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001391 u32 length;
1392 index_t *ipnis;
1393} ip_neighbor_walk_covered_ctx_t;
1394
1395static walk_rc_t
1396ip_neighbor_walk_covered (index_t ipni, void *arg)
1397{
1398 ip_neighbor_walk_covered_ctx_t *ctx = arg;
1399 ip_neighbor_t *ipn;
1400
1401 ipn = ip_neighbor_get (ipni);
1402
Neale Rannsdc617b82020-08-20 08:22:56 +00001403 if (AF_IP4 == ip_addr_version (&ctx->addr))
Neale Rannscbe25aa2019-09-30 10:53:31 +00001404 {
1405 if (ip4_destination_matches_route (&ip4_main,
Neale Rannsdc617b82020-08-20 08:22:56 +00001406 &ip_addr_v4 (&ipn->ipn_key->ipnk_ip),
1407 &ip_addr_v4 (&ctx->addr),
1408 ctx->length) &&
1409 ip_neighbor_is_dynamic (ipn))
1410 {
1411 vec_add1 (ctx->ipnis, ip_neighbor_get_index (ipn));
1412 }
1413 }
1414 else if (AF_IP6 == ip_addr_version (&ctx->addr))
1415 {
1416 if (ip6_destination_matches_route (&ip6_main,
1417 &ip_addr_v6 (&ipn->ipn_key->ipnk_ip),
1418 &ip_addr_v6 (&ctx->addr),
Neale Rannscbe25aa2019-09-30 10:53:31 +00001419 ctx->length) &&
1420 ip_neighbor_is_dynamic (ipn))
1421 {
1422 vec_add1 (ctx->ipnis, ip_neighbor_get_index (ipn));
1423 }
1424 }
1425 return (WALK_CONTINUE);
1426}
1427
1428
1429/*
1430 * callback when an interface address is added or deleted
1431 */
1432static void
1433ip_neighbor_add_del_interface_address_v4 (ip4_main_t * im,
1434 uword opaque,
1435 u32 sw_if_index,
1436 ip4_address_t * address,
1437 u32 address_length,
1438 u32 if_address_index, u32 is_del)
1439{
1440 /*
1441 * Flush the ARP cache of all entries covered by the address
1442 * that is being removed.
1443 */
luoyaozub3778cc2022-09-05 22:16:01 +08001444 IP_NEIGHBOR_DBG ("addr-%s: %U, %U/%d", (is_del ? "del" : "add"),
1445 format_vnet_sw_if_index_name, vnet_get_main (), sw_if_index,
1446 format_ip4_address, address, address_length);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001447
1448 if (is_del)
1449 {
Neale Rannsdc617b82020-08-20 08:22:56 +00001450 /* *INDENT-OFF* */
Neale Rannscbe25aa2019-09-30 10:53:31 +00001451 ip_neighbor_walk_covered_ctx_t ctx = {
Neale Rannsdc617b82020-08-20 08:22:56 +00001452 .addr = {
1453 .ip.ip4 = *address,
1454 .version = AF_IP4,
1455 },
Neale Rannscbe25aa2019-09-30 10:53:31 +00001456 .length = address_length,
1457 };
Neale Rannsdc617b82020-08-20 08:22:56 +00001458 /* *INDENT-ON* */
Neale Rannscbe25aa2019-09-30 10:53:31 +00001459 index_t *ipni;
1460
Neale Rannsdc617b82020-08-20 08:22:56 +00001461 ip_neighbor_walk (AF_IP4, sw_if_index, ip_neighbor_walk_covered, &ctx);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001462
1463 vec_foreach (ipni, ctx.ipnis)
Neale Ranns4ac36bc2020-11-20 13:05:59 +00001464 ip_neighbor_destroy (ip_neighbor_get (*ipni));
Neale Rannscbe25aa2019-09-30 10:53:31 +00001465
1466 vec_free (ctx.ipnis);
1467 }
1468}
1469
1470/*
1471 * callback when an interface address is added or deleted
1472 */
1473static void
1474ip_neighbor_add_del_interface_address_v6 (ip6_main_t * im,
1475 uword opaque,
1476 u32 sw_if_index,
1477 ip6_address_t * address,
1478 u32 address_length,
1479 u32 if_address_index, u32 is_del)
1480{
1481 /*
1482 * Flush the ARP cache of all entries covered by the address
1483 * that is being removed.
1484 */
1485 IP_NEIGHBOR_DBG ("addr-change: %U, %U/%d %s",
1486 format_vnet_sw_if_index_name, vnet_get_main (),
1487 sw_if_index, format_ip6_address, address, address_length,
1488 (is_del ? "del" : "add"));
1489
1490 if (is_del)
1491 {
Neale Rannsdc617b82020-08-20 08:22:56 +00001492 /* *INDENT-OFF* */
Neale Rannscbe25aa2019-09-30 10:53:31 +00001493 ip_neighbor_walk_covered_ctx_t ctx = {
Neale Rannsdc617b82020-08-20 08:22:56 +00001494 .addr = {
1495 .ip.ip6 = *address,
1496 .version = AF_IP6,
1497 },
Neale Rannscbe25aa2019-09-30 10:53:31 +00001498 .length = address_length,
1499 };
Neale Rannsdc617b82020-08-20 08:22:56 +00001500 /* *INDENT-ON* */
Neale Rannscbe25aa2019-09-30 10:53:31 +00001501 index_t *ipni;
1502
Neale Rannsdc617b82020-08-20 08:22:56 +00001503 ip_neighbor_walk (AF_IP6, sw_if_index, ip_neighbor_walk_covered, &ctx);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001504
1505 vec_foreach (ipni, ctx.ipnis)
Neale Ranns4ac36bc2020-11-20 13:05:59 +00001506 ip_neighbor_destroy (ip_neighbor_get (*ipni));
Neale Rannscbe25aa2019-09-30 10:53:31 +00001507
1508 vec_free (ctx.ipnis);
1509 }
1510}
1511
1512typedef struct ip_neighbor_table_bind_ctx_t_
1513{
1514 u32 new_fib_index;
1515 u32 old_fib_index;
1516} ip_neighbor_table_bind_ctx_t;
1517
1518static walk_rc_t
1519ip_neighbor_walk_table_bind (index_t ipni, void *arg)
1520{
1521 ip_neighbor_table_bind_ctx_t *ctx = arg;
1522 ip_neighbor_t *ipn;
1523
1524 ipn = ip_neighbor_get (ipni);
1525 ip_neighbor_adj_fib_remove (ipn, ctx->old_fib_index);
1526 ip_neighbor_adj_fib_add (ipn, ctx->new_fib_index);
1527
1528 return (WALK_CONTINUE);
1529}
1530
1531static void
1532ip_neighbor_table_bind_v4 (ip4_main_t * im,
1533 uword opaque,
1534 u32 sw_if_index,
1535 u32 new_fib_index, u32 old_fib_index)
1536{
1537 ip_neighbor_table_bind_ctx_t ctx = {
1538 .old_fib_index = old_fib_index,
1539 .new_fib_index = new_fib_index,
1540 };
1541
Neale Rannsdc617b82020-08-20 08:22:56 +00001542 ip_neighbor_walk (AF_IP4, sw_if_index, ip_neighbor_walk_table_bind, &ctx);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001543}
1544
1545static void
1546ip_neighbor_table_bind_v6 (ip6_main_t * im,
1547 uword opaque,
1548 u32 sw_if_index,
1549 u32 new_fib_index, u32 old_fib_index)
1550{
1551 ip_neighbor_table_bind_ctx_t ctx = {
1552 .old_fib_index = old_fib_index,
1553 .new_fib_index = new_fib_index,
1554 };
1555
Neale Rannsdc617b82020-08-20 08:22:56 +00001556 ip_neighbor_walk (AF_IP6, sw_if_index, ip_neighbor_walk_table_bind, &ctx);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001557}
1558
1559typedef enum ip_neighbor_age_state_t_
1560{
1561 IP_NEIGHBOR_AGE_ALIVE,
1562 IP_NEIGHBOR_AGE_PROBE,
1563 IP_NEIGHBOR_AGE_DEAD,
1564} ip_neighbor_age_state_t;
1565
1566#define IP_NEIGHBOR_PROCESS_SLEEP_LONG (0)
1567
1568static ip_neighbor_age_state_t
1569ip_neighbour_age_out (index_t ipni, f64 now, f64 * wait)
1570{
Neale Rannsdc617b82020-08-20 08:22:56 +00001571 ip_address_family_t af;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001572 ip_neighbor_t *ipn;
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001573 u32 ipndb_age;
1574 u32 ttl;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001575
1576 ipn = ip_neighbor_get (ipni);
Neale Rannsdc617b82020-08-20 08:22:56 +00001577 af = ip_neighbor_get_af (ipn);
1578 ipndb_age = ip_neighbor_db[af].ipndb_age;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001579 ttl = now - ipn->ipn_time_last_updated;
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001580 *wait = ipndb_age;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001581
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001582 if (ttl > ipndb_age)
Neale Rannscbe25aa2019-09-30 10:53:31 +00001583 {
1584 IP_NEIGHBOR_DBG ("aged: %U @%f - %f > %d",
1585 format_ip_neighbor, ipni, now,
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001586 ipn->ipn_time_last_updated, ipndb_age);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001587 if (ipn->ipn_n_probes > 2)
1588 {
1589 /* 3 strikes and yea-re out */
1590 IP_NEIGHBOR_DBG ("dead: %U", format_ip_neighbor, ipni);
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001591 *wait = 1;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001592 return (IP_NEIGHBOR_AGE_DEAD);
1593 }
1594 else
1595 {
Neale Rannsfd2417b2021-07-16 14:00:16 +00001596 ip_neighbor_probe_dst (ip_neighbor_get_sw_if_index (ipn), af,
1597 vlib_get_thread_index (),
1598 &ip_addr_46 (&ipn->ipn_key->ipnk_ip));
Neale Rannscbe25aa2019-09-30 10:53:31 +00001599
1600 ipn->ipn_n_probes++;
1601 *wait = 1;
1602 }
1603 }
1604 else
1605 {
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001606 /* here we are sure that ttl <= ipndb_age */
1607 *wait = ipndb_age - ttl + 1;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001608 return (IP_NEIGHBOR_AGE_ALIVE);
1609 }
1610
1611 return (IP_NEIGHBOR_AGE_PROBE);
1612}
1613
1614typedef enum ip_neighbor_process_event_t_
1615{
1616 IP_NEIGHBOR_AGE_PROCESS_WAKEUP,
1617} ip_neighbor_process_event_t;
1618
1619static uword
1620ip_neighbor_age_loop (vlib_main_t * vm,
1621 vlib_node_runtime_t * rt,
Neale Rannsdc617b82020-08-20 08:22:56 +00001622 vlib_frame_t * f, ip_address_family_t af)
Neale Rannscbe25aa2019-09-30 10:53:31 +00001623{
1624 uword event_type, *event_data = NULL;
1625 f64 timeout;
1626
1627 /* Set the timeout to an effectively infinite value when the process starts */
1628 timeout = IP_NEIGHBOR_PROCESS_SLEEP_LONG;
1629
1630 while (1)
1631 {
1632 f64 now;
1633
1634 if (!timeout)
1635 vlib_process_wait_for_event (vm);
1636 else
1637 vlib_process_wait_for_event_or_clock (vm, timeout);
1638
1639 event_type = vlib_process_get_events (vm, &event_data);
1640 vec_reset_length (event_data);
1641
1642 now = vlib_time_now (vm);
1643
1644 switch (event_type)
1645 {
1646 case ~0:
1647 {
1648 /* timer expired */
1649 ip_neighbor_elt_t *elt, *head;
1650 f64 wait;
1651
Neale Rannsdc617b82020-08-20 08:22:56 +00001652 timeout = ip_neighbor_db[af].ipndb_age;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001653 head = pool_elt_at_index (ip_neighbor_elt_pool,
Neale Rannsdc617b82020-08-20 08:22:56 +00001654 ip_neighbor_list_head[af]);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001655
1656 /* *INDENT-OFF*/
1657 /* the list is time sorted, newest first, so start from the back
1658 * and work forwards. Stop when we get to one that is alive */
1659 restart:
1660 clib_llist_foreach_reverse(ip_neighbor_elt_pool,
1661 ipne_anchor, head, elt,
1662 ({
1663 ip_neighbor_age_state_t res;
1664
1665 res = ip_neighbour_age_out(elt->ipne_index, now, &wait);
1666
1667 if (IP_NEIGHBOR_AGE_ALIVE == res) {
1668 /* the oldest neighbor has not yet expired, go back to sleep */
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001669 timeout = clib_min (wait, timeout);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001670 break;
1671 }
1672 else if (IP_NEIGHBOR_AGE_DEAD == res) {
1673 /* the oldest neighbor is dead, pop it, then restart the walk
1674 * again from the back */
Neale Ranns4ac36bc2020-11-20 13:05:59 +00001675 ip_neighbor_destroy (ip_neighbor_get(elt->ipne_index));
Neale Rannscbe25aa2019-09-30 10:53:31 +00001676 goto restart;
1677 }
1678
1679 timeout = clib_min (wait, timeout);
1680 }));
1681 /* *INDENT-ON* */
1682 break;
1683 }
1684 case IP_NEIGHBOR_AGE_PROCESS_WAKEUP:
1685 {
1686
Neale Rannsdc617b82020-08-20 08:22:56 +00001687 if (!ip_neighbor_db[af].ipndb_age)
Neale Rannscbe25aa2019-09-30 10:53:31 +00001688 {
1689 /* aging has been disabled */
1690 timeout = 0;
1691 break;
1692 }
1693 ip_neighbor_elt_t *elt, *head;
1694
1695 head = pool_elt_at_index (ip_neighbor_elt_pool,
Neale Rannsdc617b82020-08-20 08:22:56 +00001696 ip_neighbor_list_head[af]);
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001697 /* no neighbors yet */
1698 if (clib_llist_is_empty (ip_neighbor_elt_pool, ipne_anchor, head))
1699 {
Neale Rannsdc617b82020-08-20 08:22:56 +00001700 timeout = ip_neighbor_db[af].ipndb_age;
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001701 break;
1702 }
Neale Rannscbe25aa2019-09-30 10:53:31 +00001703
1704 /* poke the oldset neighbour for aging, which returns how long we sleep for */
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001705 elt = clib_llist_prev (ip_neighbor_elt_pool, ipne_anchor, head);
1706 ip_neighbour_age_out (elt->ipne_index, now, &timeout);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001707 break;
1708 }
1709 }
1710 }
1711 return 0;
1712}
1713
1714static uword
1715ip4_neighbor_age_process (vlib_main_t * vm,
1716 vlib_node_runtime_t * rt, vlib_frame_t * f)
1717{
Neale Rannsdc617b82020-08-20 08:22:56 +00001718 return (ip_neighbor_age_loop (vm, rt, f, AF_IP4));
Neale Rannscbe25aa2019-09-30 10:53:31 +00001719}
1720
1721static uword
1722ip6_neighbor_age_process (vlib_main_t * vm,
1723 vlib_node_runtime_t * rt, vlib_frame_t * f)
1724{
Neale Rannsdc617b82020-08-20 08:22:56 +00001725 return (ip_neighbor_age_loop (vm, rt, f, AF_IP6));
Neale Rannscbe25aa2019-09-30 10:53:31 +00001726}
1727
1728/* *INDENT-OFF* */
1729VLIB_REGISTER_NODE (ip4_neighbor_age_process_node,static) = {
1730 .function = ip4_neighbor_age_process,
1731 .type = VLIB_NODE_TYPE_PROCESS,
1732 .name = "ip4-neighbor-age-process",
1733};
1734VLIB_REGISTER_NODE (ip6_neighbor_age_process_node,static) = {
1735 .function = ip6_neighbor_age_process,
1736 .type = VLIB_NODE_TYPE_PROCESS,
1737 .name = "ip6-neighbor-age-process",
1738};
1739/* *INDENT-ON* */
1740
1741int
Neale Rannsdc617b82020-08-20 08:22:56 +00001742ip_neighbor_config (ip_address_family_t af, u32 limit, u32 age, bool recycle)
Neale Rannscbe25aa2019-09-30 10:53:31 +00001743{
Neale Rannsdc617b82020-08-20 08:22:56 +00001744 ip_neighbor_db[af].ipndb_limit = limit;
1745 ip_neighbor_db[af].ipndb_recycle = recycle;
1746 ip_neighbor_db[af].ipndb_age = age;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001747
1748 vlib_process_signal_event (vlib_get_main (),
Neale Rannsdc617b82020-08-20 08:22:56 +00001749 (AF_IP4 == af ?
Neale Rannscbe25aa2019-09-30 10:53:31 +00001750 ip4_neighbor_age_process_node.index :
1751 ip6_neighbor_age_process_node.index),
1752 IP_NEIGHBOR_AGE_PROCESS_WAKEUP, 0);
1753
1754 return (0);
1755}
1756
1757static clib_error_t *
1758ip_neighbor_config_show (vlib_main_t * vm,
1759 unformat_input_t * input, vlib_cli_command_t * cmd)
1760{
Neale Rannsdc617b82020-08-20 08:22:56 +00001761 ip_address_family_t af;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001762
1763 /* *INDENT-OFF* */
Neale Rannsdc617b82020-08-20 08:22:56 +00001764 FOR_EACH_IP_ADDRESS_FAMILY(af) {
1765 vlib_cli_output (vm, "%U:", format_ip_address_family, af);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001766 vlib_cli_output (vm, " limit:%d, age:%d, recycle:%d",
Neale Rannsdc617b82020-08-20 08:22:56 +00001767 ip_neighbor_db[af].ipndb_limit,
1768 ip_neighbor_db[af].ipndb_age,
1769 ip_neighbor_db[af].ipndb_recycle);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001770 }
1771
1772 /* *INDENT-ON* */
1773 return (NULL);
1774}
1775
Ivan Shvedunovf86b9672021-02-19 23:32:18 +03001776static clib_error_t *
1777ip_neighbor_config_set (vlib_main_t *vm, unformat_input_t *input,
1778 vlib_cli_command_t *cmd)
1779{
1780 unformat_input_t _line_input, *line_input = &_line_input;
1781 clib_error_t *error = NULL;
1782 ip_address_family_t af;
1783 u32 limit, age;
1784 bool recycle;
1785
1786 if (!unformat_user (input, unformat_line_input, line_input))
1787 return 0;
1788
1789 if (!unformat (line_input, "%U", unformat_ip_address_family, &af))
1790 {
1791 error = unformat_parse_error (line_input);
1792 goto done;
1793 }
1794
1795 limit = ip_neighbor_db[af].ipndb_limit;
1796 age = ip_neighbor_db[af].ipndb_age;
1797 recycle = ip_neighbor_db[af].ipndb_recycle;
1798
1799 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1800 {
1801 if (unformat (line_input, "limit %u", &limit))
1802 ;
1803 else if (unformat (line_input, "age %u", &age))
1804 ;
1805 else if (unformat (line_input, "recycle"))
1806 recycle = true;
1807 else if (unformat (line_input, "norecycle"))
1808 recycle = false;
1809 else
1810 {
1811 error = unformat_parse_error (line_input);
1812 goto done;
1813 }
1814 }
1815
1816 ip_neighbor_config (af, limit, age, recycle);
1817
1818done:
1819 unformat_free (line_input);
1820 return error;
1821}
1822
Neale Rannsfd2417b2021-07-16 14:00:16 +00001823static void
1824ip_neighbor_stats_show_one (vlib_main_t *vm, vnet_main_t *vnm, u32 sw_if_index)
1825{
1826 vlib_cli_output (vm, " %U", format_vnet_sw_if_index_name, vnm, sw_if_index);
1827 vlib_cli_output (vm, " arp:%U", format_ip_neighbor_counters,
1828 &ip_neighbor_counters[AF_IP4], sw_if_index);
1829 vlib_cli_output (vm, " nd: %U", format_ip_neighbor_counters,
1830 &ip_neighbor_counters[AF_IP6], sw_if_index);
1831}
1832
1833static walk_rc_t
1834ip_neighbor_stats_show_cb (vnet_main_t *vnm, vnet_sw_interface_t *si,
1835 void *ctx)
1836{
1837 ip_neighbor_stats_show_one (ctx, vnm, si->sw_if_index);
1838
1839 return (WALK_CONTINUE);
1840}
1841
1842static clib_error_t *
1843ip_neighbor_stats_show (vlib_main_t *vm, unformat_input_t *input,
1844 vlib_cli_command_t *cmd)
1845{
1846 vnet_main_t *vnm;
1847 u32 sw_if_index;
1848
1849 vnm = vnet_get_main ();
1850 sw_if_index = ~0;
1851 (void) unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index);
1852
1853 if (~0 == sw_if_index)
1854 {
1855 vnet_sw_interface_walk (vnm, ip_neighbor_stats_show_cb, vm);
1856 }
1857 else
1858 {
1859 ip_neighbor_stats_show_one (vm, vnm, sw_if_index);
1860 }
1861 return (NULL);
1862}
1863
Neale Rannscbe25aa2019-09-30 10:53:31 +00001864/* *INDENT-OFF* */
1865VLIB_CLI_COMMAND (show_ip_neighbor_cfg_cmd_node, static) = {
1866 .path = "show ip neighbor-config",
1867 .function = ip_neighbor_config_show,
1868 .short_help = "show ip neighbor-config",
1869};
Ivan Shvedunovf86b9672021-02-19 23:32:18 +03001870VLIB_CLI_COMMAND (set_ip_neighbor_cfg_cmd_node, static) = {
1871 .path = "set ip neighbor-config",
1872 .function = ip_neighbor_config_set,
1873 .short_help = "set ip neighbor-config ip4|ip6 [limit <limit>] [age <age>] "
1874 "[recycle|norecycle]",
1875};
Neale Rannsfd2417b2021-07-16 14:00:16 +00001876VLIB_CLI_COMMAND (show_ip_neighbor_stats_cmd_node, static) = {
1877 .path = "show ip neighbor-stats",
1878 .function = ip_neighbor_stats_show,
1879 .short_help = "show ip neighbor-stats [interface]",
1880};
Neale Rannscbe25aa2019-09-30 10:53:31 +00001881/* *INDENT-ON* */
1882
1883static clib_error_t *
1884ip_neighbor_init (vlib_main_t * vm)
1885{
1886 {
1887 ip4_add_del_interface_address_callback_t cb = {
1888 .function = ip_neighbor_add_del_interface_address_v4,
1889 };
1890 vec_add1 (ip4_main.add_del_interface_address_callbacks, cb);
1891 }
1892 {
1893 ip6_add_del_interface_address_callback_t cb = {
1894 .function = ip_neighbor_add_del_interface_address_v6,
1895 };
1896 vec_add1 (ip6_main.add_del_interface_address_callbacks, cb);
1897 }
1898 {
1899 ip4_table_bind_callback_t cb = {
1900 .function = ip_neighbor_table_bind_v4,
1901 };
1902 vec_add1 (ip4_main.table_bind_callbacks, cb);
1903 }
1904 {
1905 ip6_table_bind_callback_t cb = {
1906 .function = ip_neighbor_table_bind_v6,
1907 };
1908 vec_add1 (ip6_main.table_bind_callbacks, cb);
1909 }
Neale Rannscbe25aa2019-09-30 10:53:31 +00001910 ipn_logger = vlib_log_register_class ("ip", "neighbor");
1911
Neale Rannsdc617b82020-08-20 08:22:56 +00001912 ip_address_family_t af;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001913
Neale Rannsdc617b82020-08-20 08:22:56 +00001914 FOR_EACH_IP_ADDRESS_FAMILY (af)
1915 ip_neighbor_list_head[af] =
Neale Rannscbe25aa2019-09-30 10:53:31 +00001916 clib_llist_make_head (ip_neighbor_elt_pool, ipne_anchor);
1917
1918 return (NULL);
1919}
1920
1921/* *INDENT-OFF* */
1922VLIB_INIT_FUNCTION (ip_neighbor_init) =
1923{
1924 .runs_after = VLIB_INITS("ip_main_init"),
1925};
1926/* *INDENT-ON* */
1927
1928/*
1929 * fd.io coding-style-patch-verification: ON
1930 *
1931 * Local Variables:
1932 * eval: (c-set-style "gnu")
1933 * End:
1934 */