blob: 09b56058f72226bfb0b84b67f6bd715a75cb74bb [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>
26#include <vnet/fib/fib_table.h>
27#include <vnet/adj/adj_mcast.h>
28
29/** Pool for All IP neighbors */
30static ip_neighbor_t *ip_neighbor_pool;
31
32/** protocol specific lists of time sorted neighbors */
33index_t ip_neighbor_list_head[IP46_N_TYPES];
34
35typedef struct ip_neighbor_elt_t_
36{
37 clib_llist_anchor_t ipne_anchor;
38 index_t ipne_index;
39} ip_neighbor_elt_t;
40
41/** Pool of linked list elemeents */
42ip_neighbor_elt_t *ip_neighbor_elt_pool;
43
44typedef struct ip_neighbor_db_t_
45{
46 /** per interface hash */
47 uword **ipndb_hash;
48 /** per-protocol limit - max number of neighbors*/
49 u32 ipndb_limit;
50 /** max age of a neighbor before it's forcibly evicted */
51 u32 ipndb_age;
52 /** when the limit is reached and new neighbors are created, should
53 * we recycle an old one */
54 bool ipndb_recycle;
55 /** per-protocol number of elements */
56 u32 ipndb_n_elts;
57 /** per-protocol number of elements per-fib-index*/
58 u32 *ipndb_n_elts_per_fib;
59} ip_neighbor_db_t;
60
61static vlib_log_class_t ipn_logger;
62
63/* DBs of neighbours one per AF */
64/* *INDENT-OFF* */
65static ip_neighbor_db_t ip_neighbor_db[IP46_N_TYPES] = {
66 [IP46_TYPE_IP4] = {
67 .ipndb_limit = 50000,
68 /* Default to not aging and not recycling */
69 .ipndb_age = 0,
70 .ipndb_recycle = false,
71 },
72 [IP46_TYPE_IP6] = {
73 .ipndb_limit = 50000,
74 /* Default to not aging and not recycling */
75 .ipndb_age = 0,
76 .ipndb_recycle = false,
77 }
78};
79/* *INDENT-ON* */
80
81#define IP_NEIGHBOR_DBG(...) \
82 vlib_log_debug (ipn_logger, __VA_ARGS__);
83
84#define IP_NEIGHBOR_INFO(...) \
85 vlib_log_notice (ipn_logger, __VA_ARGS__);
86
87ip_neighbor_t *
88ip_neighbor_get (index_t ipni)
89{
90 if (pool_is_free_index (ip_neighbor_pool, ipni))
91 return (NULL);
92
93 return (pool_elt_at_index (ip_neighbor_pool, ipni));
94}
95
96static index_t
97ip_neighbor_get_index (const ip_neighbor_t * ipn)
98{
99 return (ipn - ip_neighbor_pool);
100}
101
Neale Rannsc87fbb42020-04-02 17:08:28 +0000102static void
103ip_neighbor_touch (ip_neighbor_t * ipn)
104{
105 ipn->ipn_flags &= ~IP_NEIGHBOR_FLAG_STALE;
106}
107
Neale Rannscbe25aa2019-09-30 10:53:31 +0000108static bool
109ip_neighbor_is_dynamic (const ip_neighbor_t * ipn)
110{
111 return (ipn->ipn_flags & IP_NEIGHBOR_FLAG_DYNAMIC);
112}
113
114const ip46_address_t *
115ip_neighbor_get_ip (const ip_neighbor_t * ipn)
116{
117 return (&ipn->ipn_key->ipnk_ip);
118}
119
120const mac_address_t *
121ip_neighbor_get_mac (const ip_neighbor_t * ipn)
122{
123 return (&ipn->ipn_mac);
124}
125
126const u32
127ip_neighbor_get_sw_if_index (const ip_neighbor_t * ipn)
128{
129 return (ipn->ipn_key->ipnk_sw_if_index);
130}
131
132static void
133ip_neighbor_list_remove (ip_neighbor_t * ipn)
134{
135 /* new neighbours, are added to the head of the list, since the
136 * list is time sorted, newest first */
137 ip_neighbor_elt_t *elt;
138
139 if (~0 != ipn->ipn_elt)
140 {
141 elt = pool_elt_at_index (ip_neighbor_elt_pool, ipn->ipn_elt);
142
143 clib_llist_remove (ip_neighbor_elt_pool, ipne_anchor, elt);
144 }
145}
146
147static void
148ip_neighbor_refresh (ip_neighbor_t * ipn)
149{
150 /* new neighbours, are added to the head of the list, since the
151 * list is time sorted, newest first */
152 ip_neighbor_elt_t *elt, *head;
153
Neale Rannsc87fbb42020-04-02 17:08:28 +0000154 ip_neighbor_touch (ipn);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000155 ipn->ipn_time_last_updated = vlib_time_now (vlib_get_main ());
156 ipn->ipn_n_probes = 0;
157
158 if (ip_neighbor_is_dynamic (ipn))
159 {
160 if (~0 == ipn->ipn_elt)
161 /* first time insertion */
162 pool_get_zero (ip_neighbor_elt_pool, elt);
163 else
164 {
165 /* already inserted - extract first */
166 elt = pool_elt_at_index (ip_neighbor_elt_pool, ipn->ipn_elt);
167
168 clib_llist_remove (ip_neighbor_elt_pool, ipne_anchor, elt);
169 }
170 head = pool_elt_at_index (ip_neighbor_elt_pool,
171 ip_neighbor_list_head[ipn->
172 ipn_key->ipnk_type]);
173
174 elt->ipne_index = ip_neighbor_get_index (ipn);
175 clib_llist_add (ip_neighbor_elt_pool, ipne_anchor, elt, head);
176 ipn->ipn_elt = elt - ip_neighbor_elt_pool;
177 }
178}
179
180static void
181ip_neighbor_db_add (const ip_neighbor_t * ipn)
182{
183 vec_validate (ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_hash,
184 ipn->ipn_key->ipnk_sw_if_index);
185
186 if (!ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_hash
187 [ipn->ipn_key->ipnk_sw_if_index])
188 ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_hash[ipn->
189 ipn_key->ipnk_sw_if_index]
190 = hash_create_mem (0, sizeof (ip_neighbor_key_t), sizeof (index_t));
191
192 hash_set_mem (ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_hash
193 [ipn->ipn_key->ipnk_sw_if_index], ipn->ipn_key,
194 ip_neighbor_get_index (ipn));
195
196 ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_n_elts++;
197}
198
199static void
200ip_neighbor_db_remove (const ip_neighbor_key_t * key)
201{
202 vec_validate (ip_neighbor_db[key->ipnk_type].ipndb_hash,
203 key->ipnk_sw_if_index);
204
205 hash_unset_mem (ip_neighbor_db[key->ipnk_type].ipndb_hash
206 [key->ipnk_sw_if_index], key);
207
208 ip_neighbor_db[key->ipnk_type].ipndb_n_elts--;
209}
210
211static ip_neighbor_t *
212ip_neighbor_db_find (const ip_neighbor_key_t * key)
213{
214 uword *p;
215
216 if (key->ipnk_sw_if_index >=
217 vec_len (ip_neighbor_db[key->ipnk_type].ipndb_hash))
218 return NULL;
219
220 p =
221 hash_get_mem (ip_neighbor_db[key->ipnk_type].ipndb_hash
222 [key->ipnk_sw_if_index], key);
223
224 if (p)
225 return ip_neighbor_get (p[0]);
226
227 return (NULL);
228}
229
230static u8
231ip46_type_pfx_len (ip46_type_t type)
232{
233 return (type == IP46_TYPE_IP4 ? 32 : 128);
234}
235
236static void
237ip_neighbor_adj_fib_add (ip_neighbor_t * ipn, u32 fib_index)
238{
239 if (ipn->ipn_key->ipnk_type == IP46_TYPE_IP6 &&
240 ip6_address_is_link_local_unicast (&ipn->ipn_key->ipnk_ip.ip6))
241 {
242 ip6_ll_prefix_t pfx = {
243 .ilp_addr = ipn->ipn_key->ipnk_ip.ip6,
244 .ilp_sw_if_index = ipn->ipn_key->ipnk_sw_if_index,
245 };
246 ipn->ipn_fib_entry_index =
247 ip6_ll_table_entry_update (&pfx, FIB_ROUTE_PATH_FLAG_NONE);
248 }
249 else
250 {
251 fib_protocol_t fproto;
252
253 fproto = fib_proto_from_ip46 (ipn->ipn_key->ipnk_type);
254
255 fib_prefix_t pfx = {
256 .fp_len = ip46_type_pfx_len (ipn->ipn_key->ipnk_type),
257 .fp_proto = fproto,
258 .fp_addr = ipn->ipn_key->ipnk_ip,
259 };
260
261 ipn->ipn_fib_entry_index =
262 fib_table_entry_path_add (fib_index, &pfx, FIB_SOURCE_ADJ,
263 FIB_ENTRY_FLAG_ATTACHED,
264 fib_proto_to_dpo (fproto),
265 &pfx.fp_addr,
266 ipn->ipn_key->ipnk_sw_if_index,
267 ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
268
269 vec_validate (ip_neighbor_db
270 [ipn->ipn_key->ipnk_type].ipndb_n_elts_per_fib,
271 fib_index);
272
273 ip_neighbor_db[ipn->ipn_key->
274 ipnk_type].ipndb_n_elts_per_fib[fib_index]++;
275
276 if (1 ==
277 ip_neighbor_db[ipn->ipn_key->
278 ipnk_type].ipndb_n_elts_per_fib[fib_index])
279 fib_table_lock (fib_index, fproto, FIB_SOURCE_ADJ);
280 }
281}
282
283static void
284ip_neighbor_adj_fib_remove (ip_neighbor_t * ipn, u32 fib_index)
285{
286 if (FIB_NODE_INDEX_INVALID != ipn->ipn_fib_entry_index)
287 {
288 if (ipn->ipn_key->ipnk_type == IP46_TYPE_IP6 &&
289 ip6_address_is_link_local_unicast (&ipn->ipn_key->ipnk_ip.ip6))
290 {
291 ip6_ll_prefix_t pfx = {
292 .ilp_addr = ipn->ipn_key->ipnk_ip.ip6,
293 .ilp_sw_if_index = ipn->ipn_key->ipnk_sw_if_index,
294 };
295 ip6_ll_table_entry_delete (&pfx);
296 }
297 else
298 {
299 fib_protocol_t fproto;
300
301 fproto = fib_proto_from_ip46 (ipn->ipn_key->ipnk_type);
302
303 fib_prefix_t pfx = {
304 .fp_len = ip46_type_pfx_len (ipn->ipn_key->ipnk_type),
305 .fp_proto = fproto,
306 .fp_addr = ipn->ipn_key->ipnk_ip,
307 };
308
309 fib_table_entry_path_remove (fib_index,
310 &pfx,
311 FIB_SOURCE_ADJ,
312 fib_proto_to_dpo (fproto),
313 &pfx.fp_addr,
314 ipn->ipn_key->ipnk_sw_if_index,
315 ~0, 1, FIB_ROUTE_PATH_FLAG_NONE);
316
317 ip_neighbor_db[ipn->ipn_key->
318 ipnk_type].ipndb_n_elts_per_fib[fib_index]--;
319
320 if (0 ==
321 ip_neighbor_db[ipn->ipn_key->
322 ipnk_type].ipndb_n_elts_per_fib[fib_index])
323 fib_table_unlock (fib_index, fproto, FIB_SOURCE_ADJ);
324 }
325 }
326}
327
328static void
329ip_neighbor_mk_complete (adj_index_t ai, ip_neighbor_t * ipn)
330{
331 adj_nbr_update_rewrite (ai, ADJ_NBR_REWRITE_FLAG_COMPLETE,
332 ethernet_build_rewrite (vnet_get_main (),
333 ipn->
334 ipn_key->ipnk_sw_if_index,
335 adj_get_link_type (ai),
336 ipn->ipn_mac.bytes));
337}
338
339static void
340ip_neighbor_mk_incomplete (adj_index_t ai)
341{
342 ip_adjacency_t *adj = adj_get (ai);
343
344 adj_nbr_update_rewrite (ai,
345 ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
346 ethernet_build_rewrite (vnet_get_main (),
347 adj->
348 rewrite_header.sw_if_index,
Neale Rannsfca3c6a2019-12-31 03:49:34 +0000349 VNET_LINK_ARP,
Neale Rannscbe25aa2019-09-30 10:53:31 +0000350 VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
351}
352
353static adj_walk_rc_t
354ip_neighbor_mk_complete_walk (adj_index_t ai, void *ctx)
355{
356 ip_neighbor_t *ipn = ctx;
357
358 ip_neighbor_mk_complete (ai, ipn);
359
360 return (ADJ_WALK_RC_CONTINUE);
361}
362
363static adj_walk_rc_t
364ip_neighbor_mk_incomplete_walk (adj_index_t ai, void *ctx)
365{
366 ip_neighbor_mk_incomplete (ai);
367
368 return (ADJ_WALK_RC_CONTINUE);
369}
370
371static void
372ip_neighbor_free (ip_neighbor_t * ipn)
373{
374 IP_NEIGHBOR_DBG ("free: %U", format_ip_neighbor,
375 ip_neighbor_get_index (ipn));
376
377 adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
378 fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
379 &ipn->ipn_key->ipnk_ip,
380 ip_neighbor_mk_incomplete_walk, ipn);
381 ip_neighbor_adj_fib_remove
382 (ipn,
383 fib_table_get_index_for_sw_if_index
384 (fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
385 ipn->ipn_key->ipnk_sw_if_index));
386
387 ip_neighbor_list_remove (ipn);
388 ip_neighbor_db_remove (ipn->ipn_key);
389 clib_mem_free (ipn->ipn_key);
390
391 pool_put (ip_neighbor_pool, ipn);
392}
393
394static bool
395ip_neighbor_force_reuse (ip46_type_t type)
396{
397 if (!ip_neighbor_db[type].ipndb_recycle)
398 return false;
399
400 /* pluck the oldest entry, which is the one from the end of the list */
401 ip_neighbor_elt_t *elt, *head;
402
403 head =
404 pool_elt_at_index (ip_neighbor_elt_pool, ip_neighbor_list_head[type]);
405
406 if (clib_llist_is_empty (ip_neighbor_elt_pool, ipne_anchor, head))
407 return (false);
408
409 elt = clib_llist_prev (ip_neighbor_elt_pool, ipne_anchor, head);
410 ip_neighbor_free (ip_neighbor_get (elt->ipne_index));
411
412 return (true);
413}
414
415static ip_neighbor_t *
416ip_neighbor_alloc (const ip_neighbor_key_t * key,
417 const mac_address_t * mac, ip_neighbor_flags_t flags)
418{
419 ip_neighbor_t *ipn;
420
421 if (ip_neighbor_db[key->ipnk_type].ipndb_limit &&
422 (ip_neighbor_db[key->ipnk_type].ipndb_n_elts >=
423 ip_neighbor_db[key->ipnk_type].ipndb_limit))
424 {
425 if (!ip_neighbor_force_reuse (key->ipnk_type))
426 return (NULL);
427 }
428
429 pool_get_zero (ip_neighbor_pool, ipn);
430
431 ipn->ipn_key = clib_mem_alloc (sizeof (*ipn->ipn_key));
432 clib_memcpy (ipn->ipn_key, key, sizeof (*ipn->ipn_key));
433
434 ipn->ipn_fib_entry_index = FIB_NODE_INDEX_INVALID;
435 ipn->ipn_flags = flags;
436 ipn->ipn_elt = ~0;
437
438 mac_address_copy (&ipn->ipn_mac, mac);
439
440 ip_neighbor_db_add (ipn);
441
442 /* create the adj-fib. the entry in the FIB table for the peer's interface */
443 if (!(ipn->ipn_flags & IP_NEIGHBOR_FLAG_NO_FIB_ENTRY))
444 ip_neighbor_adj_fib_add
445 (ipn, fib_table_get_index_for_sw_if_index
446 (fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
447 ipn->ipn_key->ipnk_sw_if_index));
448
449 return (ipn);
450}
451
452int
453ip_neighbor_add (const ip46_address_t * ip,
454 ip46_type_t type,
455 const mac_address_t * mac,
456 u32 sw_if_index,
457 ip_neighbor_flags_t flags, u32 * stats_index)
458{
459 fib_protocol_t fproto;
460 ip_neighbor_t *ipn;
461
462 /* main thread only */
463 ASSERT (0 == vlib_get_thread_index ());
464
465 fproto = fib_proto_from_ip46 (type);
466
467 const ip_neighbor_key_t key = {
468 .ipnk_ip = *ip,
469 .ipnk_sw_if_index = sw_if_index,
470 .ipnk_type = type,
471 };
472
473 ipn = ip_neighbor_db_find (&key);
474
475 if (ipn)
476 {
477 IP_NEIGHBOR_DBG ("update: %U, %U",
478 format_vnet_sw_if_index_name, vnet_get_main (),
479 sw_if_index, format_ip46_address, ip, type,
480 format_ip_neighbor_flags, flags, format_mac_address_t,
481 mac);
482
Neale Rannsc87fbb42020-04-02 17:08:28 +0000483 ip_neighbor_touch (ipn);
484
Neale Rannscbe25aa2019-09-30 10:53:31 +0000485 /* Refuse to over-write static neighbor entry. */
486 if (!(flags & IP_NEIGHBOR_FLAG_STATIC) &&
487 (ipn->ipn_flags & IP_NEIGHBOR_FLAG_STATIC))
488 {
489 /* if MAC address match, still check to send event */
490 if (0 == mac_address_cmp (&ipn->ipn_mac, mac))
491 goto check_customers;
492 return -2;
493 }
494
495 /*
496 * prevent a DoS attack from the data-plane that
497 * spams us with no-op updates to the MAC address
498 */
499 if (0 == mac_address_cmp (&ipn->ipn_mac, mac))
500 {
501 ip_neighbor_refresh (ipn);
502 goto check_customers;
503 }
504
505 mac_address_copy (&ipn->ipn_mac, mac);
506
507 /* A dynamic entry can become static, but not vice-versa.
508 * i.e. since if it was programmed by the CP then it must
509 * be removed by the CP */
510 if ((flags & IP_NEIGHBOR_FLAG_STATIC) &&
511 !(ipn->ipn_flags & IP_NEIGHBOR_FLAG_STATIC))
512 {
513 ip_neighbor_list_remove (ipn);
514 ipn->ipn_flags |= IP_NEIGHBOR_FLAG_STATIC;
515 ipn->ipn_flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC;
516 }
517 }
518 else
519 {
520 IP_NEIGHBOR_INFO ("add: %U, %U",
521 format_vnet_sw_if_index_name, vnet_get_main (),
522 sw_if_index, format_ip46_address, ip, type,
523 format_ip_neighbor_flags, flags, format_mac_address_t,
524 mac);
525
526 ipn = ip_neighbor_alloc (&key, mac, flags);
527
528 if (NULL == ipn)
529 return VNET_API_ERROR_LIMIT_EXCEEDED;
530 }
531
532 /* Update time stamp and flags. */
533 ip_neighbor_refresh (ipn);
534
535 adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
536 fproto, &ipn->ipn_key->ipnk_ip,
537 ip_neighbor_mk_complete_walk, ipn);
538
539check_customers:
540 /* Customer(s) requesting event for this address? */
541 ip_neighbor_publish (ip_neighbor_get_index (ipn));
542
543 if (stats_index)
544 *stats_index = adj_nbr_find (fproto,
545 fib_proto_to_link (fproto),
546 &ipn->ipn_key->ipnk_ip,
547 ipn->ipn_key->ipnk_sw_if_index);
548 return 0;
549}
550
551int
552ip_neighbor_del (const ip46_address_t * ip, ip46_type_t type, u32 sw_if_index)
553{
554 ip_neighbor_t *ipn;
555
556 /* main thread only */
557 ASSERT (0 == vlib_get_thread_index ());
558
559 IP_NEIGHBOR_INFO ("delete: %U, %U",
560 format_vnet_sw_if_index_name, vnet_get_main (),
561 sw_if_index, format_ip46_address, ip, type);
562
563 const ip_neighbor_key_t key = {
564 .ipnk_ip = *ip,
565 .ipnk_sw_if_index = sw_if_index,
566 .ipnk_type = type,
567 };
568
569 ipn = ip_neighbor_db_find (&key);
570
571 if (NULL == ipn)
572 return (VNET_API_ERROR_NO_SUCH_ENTRY);
573
574 ip_neighbor_free (ipn);
575
576 return (0);
577}
578
Neale Ranns240dcb22020-04-23 09:04:59 +0000579typedef struct ip_neighbor_del_all_ctx_t_
580{
581 index_t *ipn_del;
582} ip_neighbor_del_all_ctx_t;
583
584static walk_rc_t
585ip_neighbor_del_all_walk_cb (index_t ipni, void *arg)
586{
587 ip_neighbor_del_all_ctx_t *ctx = arg;
588
589 vec_add1 (ctx->ipn_del, ipni);
590
591 return (WALK_CONTINUE);
592}
593
594void
595ip_neighbor_del_all (ip46_type_t type, u32 sw_if_index)
596{
597 IP_NEIGHBOR_INFO ("delete-all: %U, %U",
598 format_ip46_type, type,
599 format_vnet_sw_if_index_name, vnet_get_main (),
600 sw_if_index);
601
602 ip_neighbor_del_all_ctx_t ctx = {
603 .ipn_del = NULL,
604 };
605 index_t *ipni;
606
607 ip_neighbor_walk (type, sw_if_index, ip_neighbor_del_all_walk_cb, &ctx);
608
609 vec_foreach (ipni, ctx.ipn_del) ip_neighbor_free (ip_neighbor_get (*ipni));
610 vec_free (ctx.ipn_del);
611}
612
Neale Rannscbe25aa2019-09-30 10:53:31 +0000613void
614ip_neighbor_update (vnet_main_t * vnm, adj_index_t ai)
615{
616 ip_neighbor_t *ipn;
617 ip_adjacency_t *adj;
618
619 adj = adj_get (ai);
620
621 ip_neighbor_key_t key = {
622 .ipnk_ip = adj->sub_type.nbr.next_hop,
623 .ipnk_type = fib_proto_to_ip46 (adj->ia_nh_proto),
624 .ipnk_sw_if_index = adj->rewrite_header.sw_if_index,
625 };
626 ipn = ip_neighbor_db_find (&key);
627
628 switch (adj->lookup_next_index)
629 {
630 case IP_LOOKUP_NEXT_ARP:
631 if (NULL != ipn)
632 {
633 adj_nbr_walk_nh (adj->rewrite_header.sw_if_index,
634 adj->ia_nh_proto,
635 &ipn->ipn_key->ipnk_ip,
636 ip_neighbor_mk_complete_walk, ipn);
637 }
638 else
639 {
640 /*
641 * no matching ARP entry.
642 * construct the rewrite required to for an ARP packet, and stick
643 * that in the adj's pipe to smoke.
644 */
645 adj_nbr_update_rewrite
646 (ai,
647 ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
648 ethernet_build_rewrite
649 (vnm,
650 adj->rewrite_header.sw_if_index,
651 VNET_LINK_ARP,
652 VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
653
654 /*
655 * since the FIB has added this adj for a route, it makes sense it
656 * may want to forward traffic sometime soon. Let's send a
657 * speculative ARP. just one. If we were to do periodically that
658 * wouldn't be bad either, but that's more code than i'm prepared to
659 * write at this time for relatively little reward.
660 */
Steven Luong3d5f6222020-01-30 09:11:18 -0800661 /*
662 * adj_nbr_update_rewrite may actually call fib_walk_sync.
663 * fib_walk_sync may allocate a new adjacency and potentially cause
664 * a realloc for adj_pool. When that happens, adj pointer is no
665 * longer valid here.x We refresh adj pointer accordingly.
666 */
667 adj = adj_get (ai);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000668 ip_neighbor_probe (adj);
669 }
670 break;
671 case IP_LOOKUP_NEXT_GLEAN:
672 case IP_LOOKUP_NEXT_BCAST:
673 case IP_LOOKUP_NEXT_MCAST:
674 case IP_LOOKUP_NEXT_DROP:
675 case IP_LOOKUP_NEXT_PUNT:
676 case IP_LOOKUP_NEXT_LOCAL:
677 case IP_LOOKUP_NEXT_REWRITE:
678 case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
679 case IP_LOOKUP_NEXT_MIDCHAIN:
680 case IP_LOOKUP_NEXT_ICMP_ERROR:
681 case IP_LOOKUP_N_NEXT:
682 ASSERT (0);
683 break;
684 }
685}
686
687void
688ip_neighbor_learn (const ip_neighbor_learn_t * l)
689{
690 ip_neighbor_add (&l->ip, l->type, &l->mac, l->sw_if_index,
691 IP_NEIGHBOR_FLAG_DYNAMIC, NULL);
692}
693
694static clib_error_t *
695ip_neighbor_cmd (vlib_main_t * vm,
696 unformat_input_t * input, vlib_cli_command_t * cmd)
697{
698 ip46_address_t ip = ip46_address_initializer;
699 mac_address_t mac = ZERO_MAC_ADDRESS;
700 vnet_main_t *vnm = vnet_get_main ();
701 ip_neighbor_flags_t flags;
702 u32 sw_if_index = ~0;
703 int is_add = 1;
704 int count = 1;
705
706 flags = IP_NEIGHBOR_FLAG_DYNAMIC;
707
708 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
709 {
710 /* set ip arp TenGigE1/1/0/1 1.2.3.4 aa:bb:... or aabb.ccdd... */
711 if (unformat (input, "%U %U %U",
712 unformat_vnet_sw_interface, vnm, &sw_if_index,
713 unformat_ip46_address, &ip, IP46_TYPE_ANY,
714 unformat_mac_address_t, &mac))
715 ;
716 else if (unformat (input, "delete") || unformat (input, "del"))
717 is_add = 0;
718 else if (unformat (input, "static"))
719 {
720 flags |= IP_NEIGHBOR_FLAG_STATIC;
721 flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC;
722 }
723 else if (unformat (input, "no-fib-entry"))
724 flags |= IP_NEIGHBOR_FLAG_NO_FIB_ENTRY;
725 else if (unformat (input, "count %d", &count))
726 ;
727 else
728 break;
729 }
730
731 if (sw_if_index == ~0 ||
732 ip46_address_is_zero (&ip) || mac_address_is_zero (&mac))
733 return clib_error_return (0,
734 "specify interface, IP address and MAC: `%U'",
735 format_unformat_error, input);
736
737 while (count)
738 {
739 if (is_add)
740 ip_neighbor_add (&ip, ip46_address_get_type (&ip), &mac, sw_if_index,
741 flags, NULL);
742 else
743 ip_neighbor_del (&ip, ip46_address_get_type (&ip), sw_if_index);
744
745 ip46_address_increment (ip46_address_get_type (&ip), &ip);
746 mac_address_increment (&mac);
747
748 --count;
749 }
750
751 return NULL;
752}
753
754/* *INDENT-OFF* */
755/*?
756 * Add or delete IPv4 ARP cache entries.
757 *
758 * @note 'set ip neighbor' options (e.g. delete, static, 'fib-id <id>',
759 * 'count <number>', 'interface ip4_addr mac_addr') can be added in
760 * any order and combination.
761 *
762 * @cliexpar
763 * @parblock
764 * Add or delete IPv4 ARP cache entries as follows. MAC Address can be in
765 * either aa:bb:cc:dd:ee:ff format or aabb.ccdd.eeff format.
766 * @cliexcmd{set ip neighbor GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
767 * @cliexcmd{set ip neighbor delete GigabitEthernet2/0/0 6.0.0.3 de:ad:be:ef:ba:be}
768 *
769 * To add or delete an IPv4 ARP cache entry to or from a specific fib
770 * table:
771 * @cliexcmd{set ip neighbor fib-id 1 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
772 * @cliexcmd{set ip neighbor fib-id 1 delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
773 *
774 * Add or delete IPv4 static ARP cache entries as follows:
775 * @cliexcmd{set ip neighbor static GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
776 * @cliexcmd{set ip neighbor static delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
777 *
778 * For testing / debugging purposes, the 'set ip neighbor' command can add or
779 * delete multiple entries. Supply the 'count N' parameter:
780 * @cliexcmd{set ip neighbor count 10 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
781 * @endparblock
782 ?*/
783VLIB_CLI_COMMAND (ip_neighbor_command, static) = {
784 .path = "set ip neighbor",
785 .short_help =
786 "set ip neighbor [del] <intfc> <ip-address> <mac-address> [static] [no-fib-entry] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
787 .function = ip_neighbor_cmd,
788};
789VLIB_CLI_COMMAND (ip_neighbor_command2, static) = {
790 .path = "ip neighbor",
791 .short_help =
792 "ip neighbor [del] <intfc> <ip-address> <mac-address> [static] [no-fib-entry] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
793 .function = ip_neighbor_cmd,
794};
795/* *INDENT-ON* */
796
797static int
798ip_neighbor_sort (void *a1, void *a2)
799{
800 index_t *ipni1 = a1, *ipni2 = a2;
801 ip_neighbor_t *ipn1, *ipn2;
802 int cmp;
803
804 ipn1 = ip_neighbor_get (*ipni1);
805 ipn2 = ip_neighbor_get (*ipni2);
806
807 cmp = vnet_sw_interface_compare (vnet_get_main (),
808 ipn1->ipn_key->ipnk_sw_if_index,
809 ipn2->ipn_key->ipnk_sw_if_index);
810 if (!cmp)
811 cmp = ip46_address_cmp (&ipn1->ipn_key->ipnk_ip, &ipn2->ipn_key->ipnk_ip);
812 return cmp;
813}
814
815static index_t *
816ip_neighbor_entries (u32 sw_if_index, ip46_type_t type)
817{
818 index_t *ipnis = NULL;
819 ip_neighbor_t *ipn;
820
821 /* *INDENT-OFF* */
822 pool_foreach (ipn, ip_neighbor_pool,
823 ({
Michael Yubedf48a2020-05-08 16:42:58 +0800824 if ((sw_if_index == ~0 ||
825 ipn->ipn_key->ipnk_sw_if_index == sw_if_index) &&
Neale Rannscbe25aa2019-09-30 10:53:31 +0000826 (IP46_TYPE_ANY == type ||
Michael Yubedf48a2020-05-08 16:42:58 +0800827 ipn->ipn_key->ipnk_type == type))
828 vec_add1 (ipnis, ip_neighbor_get_index(ipn));
Neale Rannscbe25aa2019-09-30 10:53:31 +0000829 }));
830
831 /* *INDENT-ON* */
832
833 if (ipnis)
834 vec_sort_with_function (ipnis, ip_neighbor_sort);
835 return ipnis;
836}
837
838static clib_error_t *
839ip_neighbor_show_sorted_i (vlib_main_t * vm,
840 unformat_input_t * input,
841 vlib_cli_command_t * cmd, ip46_type_t type)
842{
843 ip_neighbor_elt_t *elt, *head;
844
845 head = pool_elt_at_index (ip_neighbor_elt_pool,
846 ip_neighbor_list_head[type]);
847
848
849 vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP",
850 "Flags", "Ethernet", "Interface");
851
852 /* *INDENT-OFF*/
853 /* the list is time sorted, newest first, so start from the back
854 * and work forwards. Stop when we get to one that is alive */
855 clib_llist_foreach_reverse(ip_neighbor_elt_pool,
856 ipne_anchor, head, elt,
857 ({
858 vlib_cli_output (vm, "%U", format_ip_neighbor, elt->ipne_index);
859 }));
860 /* *INDENT-ON*/
861
862 return (NULL);
863}
864
865static clib_error_t *
866ip_neighbor_show_i (vlib_main_t * vm,
867 unformat_input_t * input,
868 vlib_cli_command_t * cmd, ip46_type_t type)
869{
870 index_t *ipni, *ipnis = NULL;
871 u32 sw_if_index;
872
873 /* Filter entries by interface if given. */
874 sw_if_index = ~0;
875 (void) unformat_user (input, unformat_vnet_sw_interface, vnet_get_main (),
876 &sw_if_index);
877
878 ipnis = ip_neighbor_entries (sw_if_index, type);
879
880 if (ipnis)
881 vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP",
882 "Flags", "Ethernet", "Interface");
883
884 vec_foreach (ipni, ipnis)
885 {
886 vlib_cli_output (vm, "%U", format_ip_neighbor, *ipni);
887 }
888 vec_free (ipnis);
889
890 return (NULL);
891}
892
893static clib_error_t *
894ip_neighbor_show (vlib_main_t * vm,
895 unformat_input_t * input, vlib_cli_command_t * cmd)
896{
897 return (ip_neighbor_show_i (vm, input, cmd, IP46_TYPE_ANY));
898}
899
900static clib_error_t *
901ip6_neighbor_show (vlib_main_t * vm,
902 unformat_input_t * input, vlib_cli_command_t * cmd)
903{
904 return (ip_neighbor_show_i (vm, input, cmd, IP46_TYPE_IP6));
905}
906
907static clib_error_t *
908ip4_neighbor_show (vlib_main_t * vm,
909 unformat_input_t * input, vlib_cli_command_t * cmd)
910{
911 return (ip_neighbor_show_i (vm, input, cmd, IP46_TYPE_IP4));
912}
913
914static clib_error_t *
915ip6_neighbor_show_sorted (vlib_main_t * vm,
916 unformat_input_t * input, vlib_cli_command_t * cmd)
917{
918 return (ip_neighbor_show_sorted_i (vm, input, cmd, IP46_TYPE_IP6));
919}
920
921static clib_error_t *
922ip4_neighbor_show_sorted (vlib_main_t * vm,
923 unformat_input_t * input, vlib_cli_command_t * cmd)
924{
925 return (ip_neighbor_show_sorted_i (vm, input, cmd, IP46_TYPE_IP4));
926}
927
928/*?
929 * Display all the IP neighbor entries.
930 *
931 * @cliexpar
932 * Example of how to display the IPv4 ARP table:
933 * @cliexstart{show ip neighbor}
934 * Time FIB IP4 Flags Ethernet Interface
935 * 346.3028 0 6.1.1.3 de:ad:be:ef:ba:be GigabitEthernet2/0/0
936 * 3077.4271 0 6.1.1.4 S de:ad:be:ef:ff:ff GigabitEthernet2/0/0
937 * 2998.6409 1 6.2.2.3 de:ad:be:ef:00:01 GigabitEthernet2/0/0
938 * Proxy arps enabled for:
939 * Fib_index 0 6.0.0.1 - 6.0.0.11
940 * @cliexend
941 ?*/
942/* *INDENT-OFF* */
943VLIB_CLI_COMMAND (show_ip_neighbors_cmd_node, static) = {
944 .path = "show ip neighbors",
945 .function = ip_neighbor_show,
946 .short_help = "show ip neighbors [interface]",
947};
948VLIB_CLI_COMMAND (show_ip4_neighbors_cmd_node, static) = {
949 .path = "show ip4 neighbors",
950 .function = ip4_neighbor_show,
951 .short_help = "show ip4 neighbors [interface]",
952};
953VLIB_CLI_COMMAND (show_ip6_neighbors_cmd_node, static) = {
954 .path = "show ip6 neighbors",
955 .function = ip6_neighbor_show,
956 .short_help = "show ip6 neighbors [interface]",
957};
958VLIB_CLI_COMMAND (show_ip_neighbor_cmd_node, static) = {
959 .path = "show ip neighbor",
960 .function = ip_neighbor_show,
961 .short_help = "show ip neighbor [interface]",
962};
963VLIB_CLI_COMMAND (show_ip4_neighbor_cmd_node, static) = {
964 .path = "show ip4 neighbor",
965 .function = ip4_neighbor_show,
966 .short_help = "show ip4 neighbor [interface]",
967};
968VLIB_CLI_COMMAND (show_ip6_neighbor_cmd_node, static) = {
969 .path = "show ip6 neighbor",
970 .function = ip6_neighbor_show,
971 .short_help = "show ip6 neighbor [interface]",
972};
973VLIB_CLI_COMMAND (show_ip4_neighbor_sorted_cmd_node, static) = {
974 .path = "show ip4 neighbor-sorted",
975 .function = ip4_neighbor_show_sorted,
976 .short_help = "show ip4 neighbor-sorted",
977};
978VLIB_CLI_COMMAND (show_ip6_neighbor_sorted_cmd_node, static) = {
979 .path = "show ip6 neighbor-sorted",
980 .function = ip6_neighbor_show_sorted,
981 .short_help = "show ip6 neighbor-sorted",
982};
983/* *INDENT-ON* */
984
985static ip_neighbor_vft_t ip_nbr_vfts[IP46_N_TYPES];
986
987void
988ip_neighbor_register (ip46_type_t type, const ip_neighbor_vft_t * vft)
989{
990 ip_nbr_vfts[type] = *vft;
991}
992
993void
994ip_neighbor_probe_dst (const ip_adjacency_t * adj, const ip46_address_t * dst)
995{
996 if (!vnet_sw_interface_is_admin_up (vnet_get_main (),
997 adj->rewrite_header.sw_if_index))
998 return;
999
1000 switch (adj->ia_nh_proto)
1001 {
1002 case FIB_PROTOCOL_IP6:
1003 ip6_neighbor_probe_dst (adj, &dst->ip6);
1004 break;
1005 case FIB_PROTOCOL_IP4:
1006 ip4_neighbor_probe_dst (adj, &dst->ip4);
1007 break;
1008 case FIB_PROTOCOL_MPLS:
1009 ASSERT (0);
1010 break;
1011 }
1012}
1013
1014void
1015ip_neighbor_probe (const ip_adjacency_t * adj)
1016{
1017 ip_neighbor_probe_dst (adj, &adj->sub_type.nbr.next_hop);
1018}
1019
1020void
1021ip_neighbor_advertise (vlib_main_t * vm,
1022 ip46_type_t type,
1023 const ip46_address_t * addr, u32 sw_if_index)
1024{
1025 vnet_main_t *vnm = vnet_get_main ();
1026
1027 if (type == IP46_TYPE_IP4 || type == IP46_TYPE_BOTH)
Matthew Smith5002e7f2019-12-23 16:25:11 -06001028 ip4_neighbor_advertise (vm, vnm, sw_if_index, (addr) ? &addr->ip4 : NULL);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001029 if (type == IP46_TYPE_IP6 || type == IP46_TYPE_BOTH)
Matthew Smith5002e7f2019-12-23 16:25:11 -06001030 ip6_neighbor_advertise (vm, vnm, sw_if_index, (addr) ? &addr->ip6 : NULL);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001031}
1032
1033void
1034ip_neighbor_walk (ip46_type_t type,
1035 u32 sw_if_index, ip_neighbor_walk_cb_t cb, void *ctx)
1036{
1037 ip_neighbor_key_t *key;
1038 index_t ipni;
1039
1040 if (~0 == sw_if_index)
1041 {
1042 uword **hash;
1043
1044 vec_foreach (hash, ip_neighbor_db[type].ipndb_hash)
1045 {
1046 /* *INDENT-OFF* */
1047 hash_foreach (key, ipni, *hash,
1048 ({
Ruslan Babayev24b417c2020-02-02 17:30:31 -08001049 if (WALK_STOP == cb (ipni, ctx))
1050 break;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001051 }));
1052 /* *INDENT-ON* */
1053 }
1054 }
1055 else
1056 {
1057 uword *hash;
1058
1059 if (vec_len (ip_neighbor_db[type].ipndb_hash) <= sw_if_index)
1060 return;
1061 hash = ip_neighbor_db[type].ipndb_hash[sw_if_index];
1062
1063 /* *INDENT-OFF* */
1064 hash_foreach (key, ipni, hash,
1065 ({
Ruslan Babayev24b417c2020-02-02 17:30:31 -08001066 if (WALK_STOP == cb (ipni, ctx))
1067 break;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001068 }));
1069 /* *INDENT-ON* */
1070 }
1071}
1072
1073int
1074ip4_neighbor_proxy_add (u32 fib_index,
1075 const ip4_address_t * start,
1076 const ip4_address_t * end)
1077{
1078 if (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_add)
1079 {
1080 return (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_add
1081 (fib_index, start, end));
1082 }
1083
1084 return (-1);
1085}
1086
1087int
1088ip4_neighbor_proxy_delete (u32 fib_index,
1089 const ip4_address_t * start,
1090 const ip4_address_t * end)
1091{
1092 if (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_del)
1093 {
1094 return (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_del
1095 (fib_index, start, end));
1096 }
1097 return -1;
1098}
1099
1100int
1101ip4_neighbor_proxy_enable (u32 sw_if_index)
1102{
1103 if (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_enable)
1104 {
1105 return (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_enable (sw_if_index));
1106 }
1107 return -1;
1108}
1109
1110int
1111ip4_neighbor_proxy_disable (u32 sw_if_index)
1112{
1113 if (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_disable)
1114 {
1115 return (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_disable (sw_if_index));
1116 }
1117 return -1;
1118}
1119
1120int
1121ip6_neighbor_proxy_add (u32 sw_if_index, const ip6_address_t * addr)
1122{
1123 if (ip_nbr_vfts[IP46_TYPE_IP6].inv_proxy6_add)
1124 {
1125 return (ip_nbr_vfts[IP46_TYPE_IP6].inv_proxy6_add (sw_if_index, addr));
1126 }
1127 return -1;
1128}
1129
1130int
1131ip6_neighbor_proxy_del (u32 sw_if_index, const ip6_address_t * addr)
1132{
1133 if (ip_nbr_vfts[IP46_TYPE_IP6].inv_proxy6_del)
1134 {
1135 return (ip_nbr_vfts[IP46_TYPE_IP6].inv_proxy6_del (sw_if_index, addr));
1136 }
1137 return -1;
1138}
1139
1140static void
1141ip_neighbor_ethernet_change_mac (ethernet_main_t * em,
1142 u32 sw_if_index, uword opaque)
1143{
1144 ip_neighbor_t *ipn;
1145 adj_index_t ai;
1146
1147 IP_NEIGHBOR_DBG ("mac-change: %U",
1148 format_vnet_sw_if_index_name, vnet_get_main (),
1149 sw_if_index);
1150
1151 /* *INDENT-OFF* */
1152 pool_foreach (ipn, ip_neighbor_pool,
1153 ({
1154 if (ipn->ipn_key->ipnk_sw_if_index == sw_if_index)
1155 adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
1156 fib_proto_from_ip46(ipn->ipn_key->ipnk_type),
1157 &ipn->ipn_key->ipnk_ip,
1158 ip_neighbor_mk_complete_walk,
1159 ipn);
1160 }));
1161 /* *INDENT-ON* */
1162
1163 ai = adj_glean_get (FIB_PROTOCOL_IP4, sw_if_index);
1164
1165 if (ADJ_INDEX_INVALID != ai)
1166 adj_glean_update_rewrite (ai);
1167}
1168
1169void
1170ip_neighbor_populate (ip46_type_t type, u32 sw_if_index)
1171{
1172 index_t *ipnis = NULL, *ipni;
1173 ip_neighbor_t *ipn;
1174
1175 IP_NEIGHBOR_DBG ("populate: %U %U",
1176 format_vnet_sw_if_index_name, vnet_get_main (),
1177 sw_if_index, format_ip46_type, type);
1178
1179 /* *INDENT-OFF* */
1180 pool_foreach (ipn, ip_neighbor_pool,
1181 ({
1182 if (ipn->ipn_key->ipnk_type == type &&
1183 ipn->ipn_key->ipnk_sw_if_index == sw_if_index)
1184 vec_add1 (ipnis, ipn - ip_neighbor_pool);
1185 }));
1186 /* *INDENT-ON* */
1187
1188 vec_foreach (ipni, ipnis)
1189 {
1190 ipn = ip_neighbor_get (*ipni);
1191
1192 adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
1193 fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
1194 &ipn->ipn_key->ipnk_ip,
1195 ip_neighbor_mk_complete_walk, ipn);
1196 }
1197 vec_free (ipnis);
1198}
1199
1200void
1201ip_neighbor_flush (ip46_type_t type, u32 sw_if_index)
1202{
1203 index_t *ipnis = NULL, *ipni;
1204 ip_neighbor_t *ipn;
1205
1206 IP_NEIGHBOR_DBG ("flush: %U %U",
1207 format_vnet_sw_if_index_name, vnet_get_main (),
1208 sw_if_index, format_ip46_type, type);
1209
1210 /* *INDENT-OFF* */
1211 pool_foreach (ipn, ip_neighbor_pool,
1212 ({
1213 if (ipn->ipn_key->ipnk_type == type &&
1214 ipn->ipn_key->ipnk_sw_if_index == sw_if_index &&
1215 ip_neighbor_is_dynamic (ipn))
1216 vec_add1 (ipnis, ipn - ip_neighbor_pool);
1217 }));
1218 /* *INDENT-ON* */
1219
1220 vec_foreach (ipni, ipnis) ip_neighbor_free (ip_neighbor_get (*ipni));
1221 vec_free (ipnis);
1222}
1223
Neale Rannsc87fbb42020-04-02 17:08:28 +00001224static walk_rc_t
1225ip_neighbor_mark_one (index_t ipni, void *ctx)
1226{
1227 ip_neighbor_t *ipn;
1228
1229 ipn = ip_neighbor_get (ipni);
1230
1231 ipn->ipn_flags |= IP_NEIGHBOR_FLAG_STALE;
1232
1233 return (WALK_CONTINUE);
1234}
1235
1236void
1237ip_neighbor_mark (ip46_type_t type)
1238{
1239 ip_neighbor_walk (type, ~0, ip_neighbor_mark_one, NULL);
1240}
1241
1242typedef struct ip_neighbor_sweep_ctx_t_
1243{
1244 index_t *ipnsc_stale;
1245} ip_neighbor_sweep_ctx_t;
1246
1247static walk_rc_t
1248ip_neighbor_sweep_one (index_t ipni, void *arg)
1249{
1250 ip_neighbor_sweep_ctx_t *ctx = arg;
1251 ip_neighbor_t *ipn;
1252
1253 ipn = ip_neighbor_get (ipni);
1254
1255 if (ipn->ipn_flags & IP_NEIGHBOR_FLAG_STALE)
1256 {
1257 vec_add1 (ctx->ipnsc_stale, ipni);
1258 }
1259
1260 return (WALK_CONTINUE);
1261}
1262
1263void
1264ip_neighbor_sweep (ip46_type_t type)
1265{
1266 ip_neighbor_sweep_ctx_t ctx = { };
1267 index_t *ipni;
1268
1269 ip_neighbor_walk (type, ~0, ip_neighbor_sweep_one, &ctx);
1270
1271 vec_foreach (ipni, ctx.ipnsc_stale)
1272 {
1273 ip_neighbor_free (ip_neighbor_get (*ipni));
1274 }
1275 vec_free (ctx.ipnsc_stale);
1276}
1277
Neale Rannscbe25aa2019-09-30 10:53:31 +00001278/*
1279 * Remove any arp entries associated with the specified interface
1280 */
1281static clib_error_t *
1282ip_neighbor_interface_admin_change (vnet_main_t * vnm,
1283 u32 sw_if_index, u32 flags)
1284{
1285 ip46_type_t type;
1286
1287 IP_NEIGHBOR_DBG ("interface-admin: %U %s",
1288 format_vnet_sw_if_index_name, vnet_get_main (),
1289 sw_if_index,
1290 (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ? "up" : "down"));
1291
1292 if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
1293 {
1294 FOREACH_IP46_TYPE (type) ip_neighbor_populate (type, sw_if_index);
1295 }
1296 else
1297 {
1298 /* admin down, flush all neighbours */
1299 FOREACH_IP46_TYPE (type) ip_neighbor_flush (type, sw_if_index);
1300 }
1301
1302 return (NULL);
1303}
1304
1305VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ip_neighbor_interface_admin_change);
1306
1307/*
1308 * Remove any arp entries associated with the specified interface
1309 */
1310static clib_error_t *
1311ip_neighbor_delete_sw_interface (vnet_main_t * vnm,
1312 u32 sw_if_index, u32 is_add)
1313{
1314 IP_NEIGHBOR_DBG ("interface-change: %U %s",
1315 format_vnet_sw_if_index_name, vnet_get_main (),
1316 sw_if_index, (is_add ? "add" : "del"));
1317
1318 if (!is_add && sw_if_index != ~0)
1319 {
1320 ip46_type_t type;
1321
1322 FOREACH_IP46_TYPE (type) ip_neighbor_flush (type, sw_if_index);
1323 }
1324
1325 return (NULL);
1326}
1327
1328VNET_SW_INTERFACE_ADD_DEL_FUNCTION (ip_neighbor_delete_sw_interface);
1329
1330typedef struct ip_neighbor_walk_covered_ctx_t_
1331{
1332 ip46_type_t type;
1333 ip46_address_t addr;
1334 u32 length;
1335 index_t *ipnis;
1336} ip_neighbor_walk_covered_ctx_t;
1337
1338static walk_rc_t
1339ip_neighbor_walk_covered (index_t ipni, void *arg)
1340{
1341 ip_neighbor_walk_covered_ctx_t *ctx = arg;
1342 ip_neighbor_t *ipn;
1343
1344 ipn = ip_neighbor_get (ipni);
1345
1346 ASSERT (ipn->ipn_key->ipnk_type == ctx->type);
1347
1348 if (IP46_TYPE_IP4 == ctx->type)
1349 {
1350 if (ip4_destination_matches_route (&ip4_main,
1351 &ipn->ipn_key->ipnk_ip.ip4,
1352 &ctx->addr.ip4,
1353 ctx->length) &&
1354 ip_neighbor_is_dynamic (ipn))
1355 {
1356 vec_add1 (ctx->ipnis, ip_neighbor_get_index (ipn));
1357 }
1358 }
1359 return (WALK_CONTINUE);
1360}
1361
1362
1363/*
1364 * callback when an interface address is added or deleted
1365 */
1366static void
1367ip_neighbor_add_del_interface_address_v4 (ip4_main_t * im,
1368 uword opaque,
1369 u32 sw_if_index,
1370 ip4_address_t * address,
1371 u32 address_length,
1372 u32 if_address_index, u32 is_del)
1373{
1374 /*
1375 * Flush the ARP cache of all entries covered by the address
1376 * that is being removed.
1377 */
1378 IP_NEIGHBOR_DBG ("addr-%d: %U, %U/%d",
1379 (is_del ? "del" : "add"),
1380 format_vnet_sw_if_index_name, vnet_get_main (),
1381 sw_if_index, format_ip4_address, address, address_length);
1382
1383 if (is_del)
1384 {
1385 ip_neighbor_walk_covered_ctx_t ctx = {
1386 .addr.ip4 = *address,
1387 .type = IP46_TYPE_IP4,
1388 .length = address_length,
1389 };
1390 index_t *ipni;
1391
1392 ip_neighbor_walk (IP46_TYPE_IP4, sw_if_index,
1393 ip_neighbor_walk_covered, &ctx);
1394
1395 vec_foreach (ipni, ctx.ipnis)
1396 ip_neighbor_free (ip_neighbor_get (*ipni));
1397
1398 vec_free (ctx.ipnis);
1399 }
1400}
1401
1402/*
1403 * callback when an interface address is added or deleted
1404 */
1405static void
1406ip_neighbor_add_del_interface_address_v6 (ip6_main_t * im,
1407 uword opaque,
1408 u32 sw_if_index,
1409 ip6_address_t * address,
1410 u32 address_length,
1411 u32 if_address_index, u32 is_del)
1412{
1413 /*
1414 * Flush the ARP cache of all entries covered by the address
1415 * that is being removed.
1416 */
1417 IP_NEIGHBOR_DBG ("addr-change: %U, %U/%d %s",
1418 format_vnet_sw_if_index_name, vnet_get_main (),
1419 sw_if_index, format_ip6_address, address, address_length,
1420 (is_del ? "del" : "add"));
1421
1422 if (is_del)
1423 {
1424 ip_neighbor_walk_covered_ctx_t ctx = {
1425 .addr.ip6 = *address,
1426 .type = IP46_TYPE_IP6,
1427 .length = address_length,
1428 };
1429 index_t *ipni;
1430
1431 ip_neighbor_walk (IP46_TYPE_IP6, sw_if_index,
1432 ip_neighbor_walk_covered, &ctx);
1433
1434 vec_foreach (ipni, ctx.ipnis)
1435 ip_neighbor_free (ip_neighbor_get (*ipni));
1436
1437 vec_free (ctx.ipnis);
1438 }
1439}
1440
1441typedef struct ip_neighbor_table_bind_ctx_t_
1442{
1443 u32 new_fib_index;
1444 u32 old_fib_index;
1445} ip_neighbor_table_bind_ctx_t;
1446
1447static walk_rc_t
1448ip_neighbor_walk_table_bind (index_t ipni, void *arg)
1449{
1450 ip_neighbor_table_bind_ctx_t *ctx = arg;
1451 ip_neighbor_t *ipn;
1452
1453 ipn = ip_neighbor_get (ipni);
1454 ip_neighbor_adj_fib_remove (ipn, ctx->old_fib_index);
1455 ip_neighbor_adj_fib_add (ipn, ctx->new_fib_index);
1456
1457 return (WALK_CONTINUE);
1458}
1459
1460static void
1461ip_neighbor_table_bind_v4 (ip4_main_t * im,
1462 uword opaque,
1463 u32 sw_if_index,
1464 u32 new_fib_index, u32 old_fib_index)
1465{
1466 ip_neighbor_table_bind_ctx_t ctx = {
1467 .old_fib_index = old_fib_index,
1468 .new_fib_index = new_fib_index,
1469 };
1470
1471 ip_neighbor_walk (IP46_TYPE_IP4, sw_if_index,
1472 ip_neighbor_walk_table_bind, &ctx);
1473}
1474
1475static void
1476ip_neighbor_table_bind_v6 (ip6_main_t * im,
1477 uword opaque,
1478 u32 sw_if_index,
1479 u32 new_fib_index, u32 old_fib_index)
1480{
1481 ip_neighbor_table_bind_ctx_t ctx = {
1482 .old_fib_index = old_fib_index,
1483 .new_fib_index = new_fib_index,
1484 };
1485
1486 ip_neighbor_walk (IP46_TYPE_IP6, sw_if_index,
1487 ip_neighbor_walk_table_bind, &ctx);
1488}
1489
1490typedef enum ip_neighbor_age_state_t_
1491{
1492 IP_NEIGHBOR_AGE_ALIVE,
1493 IP_NEIGHBOR_AGE_PROBE,
1494 IP_NEIGHBOR_AGE_DEAD,
1495} ip_neighbor_age_state_t;
1496
1497#define IP_NEIGHBOR_PROCESS_SLEEP_LONG (0)
1498
1499static ip_neighbor_age_state_t
1500ip_neighbour_age_out (index_t ipni, f64 now, f64 * wait)
1501{
1502 ip_neighbor_t *ipn;
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001503 u32 ipndb_age;
1504 u32 ttl;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001505
1506 ipn = ip_neighbor_get (ipni);
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001507 ipndb_age = ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_age;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001508 ttl = now - ipn->ipn_time_last_updated;
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001509 *wait = ipndb_age;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001510
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001511 if (ttl > ipndb_age)
Neale Rannscbe25aa2019-09-30 10:53:31 +00001512 {
1513 IP_NEIGHBOR_DBG ("aged: %U @%f - %f > %d",
1514 format_ip_neighbor, ipni, now,
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001515 ipn->ipn_time_last_updated, ipndb_age);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001516 if (ipn->ipn_n_probes > 2)
1517 {
1518 /* 3 strikes and yea-re out */
1519 IP_NEIGHBOR_DBG ("dead: %U", format_ip_neighbor, ipni);
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001520 *wait = 1;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001521 return (IP_NEIGHBOR_AGE_DEAD);
1522 }
1523 else
1524 {
1525 adj_index_t ai;
1526
1527 ai = adj_glean_get (fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
1528 ip_neighbor_get_sw_if_index (ipn));
1529
1530 if (ADJ_INDEX_INVALID != ai)
1531 ip_neighbor_probe_dst (adj_get (ai), ip_neighbor_get_ip (ipn));
1532
1533 ipn->ipn_n_probes++;
1534 *wait = 1;
1535 }
1536 }
1537 else
1538 {
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001539 /* here we are sure that ttl <= ipndb_age */
1540 *wait = ipndb_age - ttl + 1;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001541 return (IP_NEIGHBOR_AGE_ALIVE);
1542 }
1543
1544 return (IP_NEIGHBOR_AGE_PROBE);
1545}
1546
1547typedef enum ip_neighbor_process_event_t_
1548{
1549 IP_NEIGHBOR_AGE_PROCESS_WAKEUP,
1550} ip_neighbor_process_event_t;
1551
1552static uword
1553ip_neighbor_age_loop (vlib_main_t * vm,
1554 vlib_node_runtime_t * rt,
1555 vlib_frame_t * f, ip46_type_t type)
1556{
1557 uword event_type, *event_data = NULL;
1558 f64 timeout;
1559
1560 /* Set the timeout to an effectively infinite value when the process starts */
1561 timeout = IP_NEIGHBOR_PROCESS_SLEEP_LONG;
1562
1563 while (1)
1564 {
1565 f64 now;
1566
1567 if (!timeout)
1568 vlib_process_wait_for_event (vm);
1569 else
1570 vlib_process_wait_for_event_or_clock (vm, timeout);
1571
1572 event_type = vlib_process_get_events (vm, &event_data);
1573 vec_reset_length (event_data);
1574
1575 now = vlib_time_now (vm);
1576
1577 switch (event_type)
1578 {
1579 case ~0:
1580 {
1581 /* timer expired */
1582 ip_neighbor_elt_t *elt, *head;
1583 f64 wait;
1584
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001585 timeout = ip_neighbor_db[type].ipndb_age;
Neale Rannscbe25aa2019-09-30 10:53:31 +00001586 head = pool_elt_at_index (ip_neighbor_elt_pool,
1587 ip_neighbor_list_head[type]);
1588
1589 /* *INDENT-OFF*/
1590 /* the list is time sorted, newest first, so start from the back
1591 * and work forwards. Stop when we get to one that is alive */
1592 restart:
1593 clib_llist_foreach_reverse(ip_neighbor_elt_pool,
1594 ipne_anchor, head, elt,
1595 ({
1596 ip_neighbor_age_state_t res;
1597
1598 res = ip_neighbour_age_out(elt->ipne_index, now, &wait);
1599
1600 if (IP_NEIGHBOR_AGE_ALIVE == res) {
1601 /* the oldest neighbor has not yet expired, go back to sleep */
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001602 timeout = clib_min (wait, timeout);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001603 break;
1604 }
1605 else if (IP_NEIGHBOR_AGE_DEAD == res) {
1606 /* the oldest neighbor is dead, pop it, then restart the walk
1607 * again from the back */
1608 ip_neighbor_free (ip_neighbor_get(elt->ipne_index));
1609 goto restart;
1610 }
1611
1612 timeout = clib_min (wait, timeout);
1613 }));
1614 /* *INDENT-ON* */
1615 break;
1616 }
1617 case IP_NEIGHBOR_AGE_PROCESS_WAKEUP:
1618 {
1619
1620 if (!ip_neighbor_db[type].ipndb_age)
1621 {
1622 /* aging has been disabled */
1623 timeout = 0;
1624 break;
1625 }
1626 ip_neighbor_elt_t *elt, *head;
1627
1628 head = pool_elt_at_index (ip_neighbor_elt_pool,
1629 ip_neighbor_list_head[type]);
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001630 /* no neighbors yet */
1631 if (clib_llist_is_empty (ip_neighbor_elt_pool, ipne_anchor, head))
1632 {
1633 timeout = ip_neighbor_db[type].ipndb_age;
1634 break;
1635 }
Neale Rannscbe25aa2019-09-30 10:53:31 +00001636
1637 /* poke the oldset neighbour for aging, which returns how long we sleep for */
Vladimir Isaev1284f8c2020-02-18 15:26:12 +03001638 elt = clib_llist_prev (ip_neighbor_elt_pool, ipne_anchor, head);
1639 ip_neighbour_age_out (elt->ipne_index, now, &timeout);
Neale Rannscbe25aa2019-09-30 10:53:31 +00001640 break;
1641 }
1642 }
1643 }
1644 return 0;
1645}
1646
1647static uword
1648ip4_neighbor_age_process (vlib_main_t * vm,
1649 vlib_node_runtime_t * rt, vlib_frame_t * f)
1650{
1651 return (ip_neighbor_age_loop (vm, rt, f, IP46_TYPE_IP4));
1652}
1653
1654static uword
1655ip6_neighbor_age_process (vlib_main_t * vm,
1656 vlib_node_runtime_t * rt, vlib_frame_t * f)
1657{
1658 return (ip_neighbor_age_loop (vm, rt, f, IP46_TYPE_IP6));
1659}
1660
1661/* *INDENT-OFF* */
1662VLIB_REGISTER_NODE (ip4_neighbor_age_process_node,static) = {
1663 .function = ip4_neighbor_age_process,
1664 .type = VLIB_NODE_TYPE_PROCESS,
1665 .name = "ip4-neighbor-age-process",
1666};
1667VLIB_REGISTER_NODE (ip6_neighbor_age_process_node,static) = {
1668 .function = ip6_neighbor_age_process,
1669 .type = VLIB_NODE_TYPE_PROCESS,
1670 .name = "ip6-neighbor-age-process",
1671};
1672/* *INDENT-ON* */
1673
1674int
1675ip_neighbor_config (ip46_type_t type, u32 limit, u32 age, bool recycle)
1676{
1677 ip_neighbor_db[type].ipndb_limit = limit;
1678 ip_neighbor_db[type].ipndb_recycle = recycle;
1679 ip_neighbor_db[type].ipndb_age = age;
1680
1681 vlib_process_signal_event (vlib_get_main (),
1682 (IP46_TYPE_IP4 == type ?
1683 ip4_neighbor_age_process_node.index :
1684 ip6_neighbor_age_process_node.index),
1685 IP_NEIGHBOR_AGE_PROCESS_WAKEUP, 0);
1686
1687 return (0);
1688}
1689
1690static clib_error_t *
1691ip_neighbor_config_show (vlib_main_t * vm,
1692 unformat_input_t * input, vlib_cli_command_t * cmd)
1693{
1694 ip46_type_t type;
1695
1696 /* *INDENT-OFF* */
1697 FOREACH_IP46_TYPE(type) {
1698 vlib_cli_output (vm, "%U:", format_ip46_type, type);
1699 vlib_cli_output (vm, " limit:%d, age:%d, recycle:%d",
1700 ip_neighbor_db[type].ipndb_limit,
1701 ip_neighbor_db[type].ipndb_age,
1702 ip_neighbor_db[type].ipndb_recycle);
1703 }
1704
1705 /* *INDENT-ON* */
1706 return (NULL);
1707}
1708
1709/* *INDENT-OFF* */
1710VLIB_CLI_COMMAND (show_ip_neighbor_cfg_cmd_node, static) = {
1711 .path = "show ip neighbor-config",
1712 .function = ip_neighbor_config_show,
1713 .short_help = "show ip neighbor-config",
1714};
1715/* *INDENT-ON* */
1716
1717static clib_error_t *
1718ip_neighbor_init (vlib_main_t * vm)
1719{
1720 {
1721 ip4_add_del_interface_address_callback_t cb = {
1722 .function = ip_neighbor_add_del_interface_address_v4,
1723 };
1724 vec_add1 (ip4_main.add_del_interface_address_callbacks, cb);
1725 }
1726 {
1727 ip6_add_del_interface_address_callback_t cb = {
1728 .function = ip_neighbor_add_del_interface_address_v6,
1729 };
1730 vec_add1 (ip6_main.add_del_interface_address_callbacks, cb);
1731 }
1732 {
1733 ip4_table_bind_callback_t cb = {
1734 .function = ip_neighbor_table_bind_v4,
1735 };
1736 vec_add1 (ip4_main.table_bind_callbacks, cb);
1737 }
1738 {
1739 ip6_table_bind_callback_t cb = {
1740 .function = ip_neighbor_table_bind_v6,
1741 };
1742 vec_add1 (ip6_main.table_bind_callbacks, cb);
1743 }
1744 {
1745 ethernet_address_change_ctx_t ctx = {
1746 .function = ip_neighbor_ethernet_change_mac,
1747 .function_opaque = 0,
1748 };
1749 vec_add1 (ethernet_main.address_change_callbacks, ctx);
1750 }
1751
1752 ipn_logger = vlib_log_register_class ("ip", "neighbor");
1753
1754 ip46_type_t type;
1755
1756 FOREACH_IP46_TYPE (type)
1757 ip_neighbor_list_head[type] =
1758 clib_llist_make_head (ip_neighbor_elt_pool, ipne_anchor);
1759
1760 return (NULL);
1761}
1762
1763/* *INDENT-OFF* */
1764VLIB_INIT_FUNCTION (ip_neighbor_init) =
1765{
1766 .runs_after = VLIB_INITS("ip_main_init"),
1767};
1768/* *INDENT-ON* */
1769
1770/*
1771 * fd.io coding-style-patch-verification: ON
1772 *
1773 * Local Variables:
1774 * eval: (c-set-style "gnu")
1775 * End:
1776 */