Line data Source code
1 : /*
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/ip/ip46_address.h>
27 : #include <vnet/fib/fib_table.h>
28 : #include <vnet/adj/adj_mcast.h>
29 :
30 : ip_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 :
98 : /** Pool for All IP neighbors */
99 : static ip_neighbor_t *ip_neighbor_pool;
100 :
101 : /** protocol specific lists of time sorted neighbors */
102 : index_t ip_neighbor_list_head[N_AF];
103 :
104 : typedef 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 */
111 : ip_neighbor_elt_t *ip_neighbor_elt_pool;
112 :
113 : typedef 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 :
130 : static vlib_log_class_t ipn_logger;
131 :
132 : /* DBs of neighbours one per AF */
133 : /* *INDENT-OFF* */
134 : static ip_neighbor_db_t ip_neighbor_db[N_AF] = {
135 : [AF_IP4] = {
136 : .ipndb_limit = 50000,
137 : /* Default to not aging and not recycling */
138 : .ipndb_age = 0,
139 : .ipndb_recycle = false,
140 : },
141 : [AF_IP6] = {
142 : .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 :
156 : ip_neighbor_t *
157 327956 : ip_neighbor_get (index_t ipni)
158 : {
159 327956 : if (pool_is_free_index (ip_neighbor_pool, ipni))
160 0 : return (NULL);
161 :
162 327956 : return (pool_elt_at_index (ip_neighbor_pool, ipni));
163 : }
164 :
165 : static index_t
166 64475 : ip_neighbor_get_index (const ip_neighbor_t * ipn)
167 : {
168 64475 : return (ipn - ip_neighbor_pool);
169 : }
170 :
171 : static void
172 9949 : ip_neighbor_touch (ip_neighbor_t * ipn)
173 : {
174 9949 : ipn->ipn_flags &= ~IP_NEIGHBOR_FLAG_STALE;
175 9949 : }
176 :
177 : static bool
178 14472 : ip_neighbor_is_dynamic (const ip_neighbor_t * ipn)
179 : {
180 14472 : return (ipn->ipn_flags & IP_NEIGHBOR_FLAG_DYNAMIC);
181 : }
182 :
183 : const ip_address_t *
184 1 : ip_neighbor_get_ip (const ip_neighbor_t * ipn)
185 : {
186 1 : return (&ipn->ipn_key->ipnk_ip);
187 : }
188 :
189 : ip_address_family_t
190 127158 : ip_neighbor_get_af (const ip_neighbor_t * ipn)
191 : {
192 127158 : return (ip_addr_version (&ipn->ipn_key->ipnk_ip));
193 : }
194 :
195 : const mac_address_t *
196 0 : ip_neighbor_get_mac (const ip_neighbor_t * ipn)
197 : {
198 0 : return (&ipn->ipn_mac);
199 : }
200 :
201 : const u32
202 630 : ip_neighbor_get_sw_if_index (const ip_neighbor_t * ipn)
203 : {
204 630 : return (ipn->ipn_key->ipnk_sw_if_index);
205 : }
206 :
207 : static void
208 5429 : ip_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 5429 : if (~0 != ipn->ipn_elt)
215 : {
216 5354 : elt = pool_elt_at_index (ip_neighbor_elt_pool, ipn->ipn_elt);
217 :
218 5354 : clib_llist_remove (ip_neighbor_elt_pool, ipne_anchor, elt);
219 :
220 5354 : ipn->ipn_elt = ~0;
221 : }
222 5429 : }
223 :
224 : static void
225 9671 : ip_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 :
231 9671 : ip_neighbor_touch (ipn);
232 9671 : ipn->ipn_time_last_updated = vlib_time_now (vlib_get_main ());
233 9671 : ipn->ipn_n_probes = 0;
234 :
235 9671 : if (ip_neighbor_is_dynamic (ipn))
236 : {
237 9594 : if (~0 == ipn->ipn_elt)
238 : /* first time insertion */
239 9318 : pool_get_zero (ip_neighbor_elt_pool, elt);
240 : else
241 : {
242 : /* already inserted - extract first */
243 276 : elt = pool_elt_at_index (ip_neighbor_elt_pool, ipn->ipn_elt);
244 :
245 276 : clib_llist_remove (ip_neighbor_elt_pool, ipne_anchor, elt);
246 : }
247 9594 : head = pool_elt_at_index (ip_neighbor_elt_pool,
248 : ip_neighbor_list_head[ip_neighbor_get_af
249 : (ipn)]);
250 :
251 9594 : elt->ipne_index = ip_neighbor_get_index (ipn);
252 9594 : clib_llist_add (ip_neighbor_elt_pool, ipne_anchor, elt, head);
253 9594 : ipn->ipn_elt = elt - ip_neighbor_elt_pool;
254 : }
255 9671 : }
256 :
257 : static void
258 9393 : ip_neighbor_db_add (const ip_neighbor_t * ipn)
259 : {
260 : ip_address_family_t af;
261 : u32 sw_if_index;
262 :
263 9393 : af = ip_neighbor_get_af (ipn);
264 9393 : sw_if_index = ipn->ipn_key->ipnk_sw_if_index;
265 :
266 9393 : vec_validate (ip_neighbor_db[af].ipndb_hash, sw_if_index);
267 :
268 9393 : if (!ip_neighbor_db[af].ipndb_hash[sw_if_index])
269 2199 : ip_neighbor_db[af].ipndb_hash[sw_if_index]
270 2199 : = hash_create_mem (0, sizeof (ip_neighbor_key_t), sizeof (index_t));
271 :
272 18786 : hash_set_mem (ip_neighbor_db[af].ipndb_hash[sw_if_index],
273 : ipn->ipn_key, ip_neighbor_get_index (ipn));
274 :
275 9393 : ip_neighbor_db[af].ipndb_n_elts++;
276 9393 : }
277 :
278 : static void
279 5427 : ip_neighbor_db_remove (const ip_neighbor_t * ipn)
280 : {
281 : ip_address_family_t af;
282 : u32 sw_if_index;
283 :
284 5427 : af = ip_neighbor_get_af (ipn);
285 5427 : sw_if_index = ipn->ipn_key->ipnk_sw_if_index;
286 :
287 5427 : vec_validate (ip_neighbor_db[af].ipndb_hash, sw_if_index);
288 :
289 10854 : hash_unset_mem (ip_neighbor_db[af].ipndb_hash[sw_if_index], ipn->ipn_key);
290 :
291 5427 : ip_neighbor_db[af].ipndb_n_elts--;
292 5427 : }
293 :
294 : static ip_neighbor_t *
295 19680 : ip_neighbor_db_find (const ip_neighbor_key_t * key)
296 : {
297 : ip_address_family_t af;
298 : uword *p;
299 :
300 19680 : af = ip_addr_version (&key->ipnk_ip);
301 :
302 19680 : if (key->ipnk_sw_if_index >= vec_len (ip_neighbor_db[af].ipndb_hash))
303 2298 : return NULL;
304 :
305 17382 : p = hash_get_mem (ip_neighbor_db[af].ipndb_hash
306 : [key->ipnk_sw_if_index], key);
307 :
308 17382 : if (p)
309 10047 : return ip_neighbor_get (p[0]);
310 :
311 7335 : return (NULL);
312 : }
313 :
314 : static u8
315 14814 : ip_af_type_pfx_len (ip_address_family_t type)
316 : {
317 14814 : return (type == AF_IP4 ? 32 : 128);
318 : }
319 :
320 : static void
321 9394 : ip_neighbor_adj_fib_add (ip_neighbor_t * ipn, u32 fib_index)
322 : {
323 : ip_address_family_t af;
324 :
325 9394 : af = ip_neighbor_get_af (ipn);
326 :
327 13645 : if (af == AF_IP6 &&
328 4251 : ip6_address_is_link_local_unicast (&ip_addr_v6
329 : (&ipn->ipn_key->ipnk_ip)))
330 4 : {
331 4 : ip6_ll_prefix_t pfx = {
332 4 : .ilp_addr = ip_addr_v6 (&ipn->ipn_key->ipnk_ip),
333 4 : .ilp_sw_if_index = ipn->ipn_key->ipnk_sw_if_index,
334 : };
335 4 : ipn->ipn_fib_entry_index =
336 4 : ip6_ll_table_entry_update (&pfx, FIB_ROUTE_PATH_FLAG_NONE);
337 : }
338 : else
339 : {
340 : fib_protocol_t fproto;
341 :
342 9390 : fproto = ip_address_family_to_fib_proto (af);
343 :
344 18780 : fib_prefix_t pfx = {
345 9390 : .fp_len = ip_af_type_pfx_len (af),
346 : .fp_proto = fproto,
347 9390 : .fp_addr = ip_addr_46 (&ipn->ipn_key->ipnk_ip),
348 : };
349 :
350 9390 : ipn->ipn_fib_entry_index =
351 9390 : fib_table_entry_path_add (fib_index, &pfx, FIB_SOURCE_ADJ,
352 : FIB_ENTRY_FLAG_ATTACHED,
353 9390 : fib_proto_to_dpo (fproto),
354 : &pfx.fp_addr,
355 9390 : ipn->ipn_key->ipnk_sw_if_index,
356 : ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
357 :
358 9390 : vec_validate (ip_neighbor_db[af].ipndb_n_elts_per_fib, fib_index);
359 :
360 9390 : ip_neighbor_db[af].ipndb_n_elts_per_fib[fib_index]++;
361 :
362 9390 : if (1 == ip_neighbor_db[af].ipndb_n_elts_per_fib[fib_index])
363 1617 : fib_table_lock (fib_index, fproto, FIB_SOURCE_ADJ);
364 : }
365 9394 : }
366 :
367 : static void
368 5431 : ip_neighbor_adj_fib_remove (ip_neighbor_t * ipn, u32 fib_index)
369 : {
370 : ip_address_family_t af;
371 :
372 5431 : af = ip_neighbor_get_af (ipn);
373 :
374 5431 : if (FIB_NODE_INDEX_INVALID != ipn->ipn_fib_entry_index)
375 : {
376 7909 : if (AF_IP6 == af &&
377 2481 : ip6_address_is_link_local_unicast (&ip_addr_v6
378 : (&ipn->ipn_key->ipnk_ip)))
379 4 : {
380 4 : ip6_ll_prefix_t pfx = {
381 4 : .ilp_addr = ip_addr_v6 (&ipn->ipn_key->ipnk_ip),
382 4 : .ilp_sw_if_index = ipn->ipn_key->ipnk_sw_if_index,
383 : };
384 4 : ip6_ll_table_entry_delete (&pfx);
385 : }
386 : else
387 : {
388 : fib_protocol_t fproto;
389 :
390 5424 : fproto = ip_address_family_to_fib_proto (af);
391 :
392 10848 : fib_prefix_t pfx = {
393 5424 : .fp_len = ip_af_type_pfx_len (af),
394 : .fp_proto = fproto,
395 5424 : .fp_addr = ip_addr_46 (&ipn->ipn_key->ipnk_ip),
396 : };
397 :
398 5424 : fib_table_entry_path_remove (fib_index,
399 : &pfx,
400 : FIB_SOURCE_ADJ,
401 5424 : fib_proto_to_dpo (fproto),
402 : &pfx.fp_addr,
403 5424 : ipn->ipn_key->ipnk_sw_if_index,
404 : ~0, 1, FIB_ROUTE_PATH_FLAG_NONE);
405 :
406 5424 : ip_neighbor_db[af].ipndb_n_elts_per_fib[fib_index]--;
407 :
408 5424 : if (0 == ip_neighbor_db[af].ipndb_n_elts_per_fib[fib_index])
409 1469 : fib_table_unlock (fib_index, fproto, FIB_SOURCE_ADJ);
410 : }
411 : }
412 5431 : }
413 :
414 : static void
415 19526 : ip_neighbor_mk_complete (adj_index_t ai, ip_neighbor_t * ipn)
416 : {
417 19526 : adj_nbr_update_rewrite (ai, ADJ_NBR_REWRITE_FLAG_COMPLETE,
418 : ethernet_build_rewrite (vnet_get_main (),
419 19526 : ipn->
420 : ipn_key->ipnk_sw_if_index,
421 19526 : adj_get_link_type (ai),
422 19526 : ipn->ipn_mac.bytes));
423 19526 : }
424 :
425 : static void
426 5448 : ip_neighbor_mk_incomplete (adj_index_t ai)
427 : {
428 5448 : ip_adjacency_t *adj = adj_get (ai);
429 :
430 5448 : 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,
435 : VNET_LINK_ARP,
436 : VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
437 5448 : }
438 :
439 : static adj_walk_rc_t
440 19275 : ip_neighbor_mk_complete_walk (adj_index_t ai, void *ctx)
441 : {
442 19275 : ip_neighbor_t *ipn = ctx;
443 :
444 19275 : ip_neighbor_mk_complete (ai, ipn);
445 :
446 19275 : return (ADJ_WALK_RC_CONTINUE);
447 : }
448 :
449 : static adj_walk_rc_t
450 5448 : ip_neighbor_mk_incomplete_walk (adj_index_t ai, void *ctx)
451 : {
452 5448 : ip_neighbor_mk_incomplete (ai);
453 :
454 5448 : return (ADJ_WALK_RC_CONTINUE);
455 : }
456 :
457 : static void
458 5427 : ip_neighbor_destroy (ip_neighbor_t * ipn)
459 : {
460 : ip_address_family_t af;
461 :
462 5427 : af = ip_neighbor_get_af (ipn);
463 :
464 5427 : IP_NEIGHBOR_DBG ("free: %U", format_ip_neighbor,
465 : ip_neighbor_get_index (ipn));
466 :
467 5427 : ip_neighbor_publish (ip_neighbor_get_index (ipn),
468 : IP_NEIGHBOR_EVENT_REMOVED);
469 :
470 5427 : adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
471 5427 : ip_address_family_to_fib_proto (af),
472 5427 : &ip_addr_46 (&ipn->ipn_key->ipnk_ip),
473 : ip_neighbor_mk_incomplete_walk, ipn);
474 5427 : ip_neighbor_adj_fib_remove
475 : (ipn,
476 : fib_table_get_index_for_sw_if_index
477 5427 : (ip_address_family_to_fib_proto (af), ipn->ipn_key->ipnk_sw_if_index));
478 :
479 5427 : ip_neighbor_list_remove (ipn);
480 5427 : ip_neighbor_db_remove (ipn);
481 5427 : clib_mem_free (ipn->ipn_key);
482 :
483 5427 : pool_put (ip_neighbor_pool, ipn);
484 5427 : }
485 :
486 : static bool
487 2 : ip_neighbor_force_reuse (ip_address_family_t af)
488 : {
489 2 : if (!ip_neighbor_db[af].ipndb_recycle)
490 1 : 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 :
495 1 : head = pool_elt_at_index (ip_neighbor_elt_pool, ip_neighbor_list_head[af]);
496 :
497 1 : if (clib_llist_is_empty (ip_neighbor_elt_pool, ipne_anchor, head))
498 0 : return (false);
499 :
500 1 : elt = clib_llist_prev (ip_neighbor_elt_pool, ipne_anchor, head);
501 1 : ip_neighbor_destroy (ip_neighbor_get (elt->ipne_index));
502 :
503 1 : return (true);
504 : }
505 :
506 : static ip_neighbor_t *
507 9394 : ip_neighbor_alloc (const ip_neighbor_key_t * key,
508 : const mac_address_t * mac, ip_neighbor_flags_t flags)
509 : {
510 : ip_address_family_t af;
511 : ip_neighbor_t *ipn;
512 :
513 9394 : af = ip_addr_version (&key->ipnk_ip);
514 :
515 9394 : if (ip_neighbor_db[af].ipndb_limit &&
516 9394 : (ip_neighbor_db[af].ipndb_n_elts >= ip_neighbor_db[af].ipndb_limit))
517 : {
518 2 : if (!ip_neighbor_force_reuse (af))
519 1 : return (NULL);
520 : }
521 :
522 9393 : pool_get_zero (ip_neighbor_pool, ipn);
523 :
524 9393 : ipn->ipn_key = clib_mem_alloc (sizeof (*ipn->ipn_key));
525 9393 : clib_memcpy (ipn->ipn_key, key, sizeof (*ipn->ipn_key));
526 :
527 9393 : ipn->ipn_fib_entry_index = FIB_NODE_INDEX_INVALID;
528 9393 : ipn->ipn_flags = flags;
529 9393 : ipn->ipn_elt = ~0;
530 :
531 9393 : mac_address_copy (&ipn->ipn_mac, mac);
532 :
533 9393 : ip_neighbor_db_add (ipn);
534 :
535 : /* create the adj-fib. the entry in the FIB table for the peer's interface */
536 9393 : if (!(ipn->ipn_flags & IP_NEIGHBOR_FLAG_NO_FIB_ENTRY))
537 9390 : ip_neighbor_adj_fib_add
538 : (ipn, fib_table_get_index_for_sw_if_index
539 9390 : (ip_address_family_to_fib_proto (af), ipn->ipn_key->ipnk_sw_if_index));
540 :
541 9393 : return (ipn);
542 : }
543 :
544 : int
545 9672 : ip_neighbor_add (const ip_address_t * ip,
546 : 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 9672 : ASSERT (0 == vlib_get_thread_index ());
555 :
556 9672 : fproto = ip_address_family_to_fib_proto (ip_addr_version (ip));
557 :
558 9672 : const ip_neighbor_key_t key = {
559 : .ipnk_ip = *ip,
560 : .ipnk_sw_if_index = sw_if_index,
561 : };
562 :
563 9672 : ipn = ip_neighbor_db_find (&key);
564 :
565 9672 : if (ipn)
566 : {
567 278 : IP_NEIGHBOR_DBG ("update: %U, %U",
568 : format_vnet_sw_if_index_name, vnet_get_main (),
569 : sw_if_index, format_ip_address, ip,
570 : format_ip_neighbor_flags, flags, format_mac_address_t,
571 : mac);
572 :
573 278 : ip_neighbor_touch (ipn);
574 :
575 : /* Refuse to over-write static neighbor entry. */
576 278 : if (!(flags & IP_NEIGHBOR_FLAG_STATIC) &&
577 276 : (ipn->ipn_flags & IP_NEIGHBOR_FLAG_STATIC))
578 : {
579 : /* if MAC address match, still check to send event */
580 0 : if (0 == mac_address_cmp (&ipn->ipn_mac, mac))
581 0 : goto check_customers;
582 0 : return -2;
583 : }
584 :
585 : /* 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 278 : if ((flags & IP_NEIGHBOR_FLAG_STATIC) &&
589 2 : !(ipn->ipn_flags & IP_NEIGHBOR_FLAG_STATIC))
590 : {
591 2 : ip_neighbor_list_remove (ipn);
592 2 : ipn->ipn_flags |= IP_NEIGHBOR_FLAG_STATIC;
593 2 : ipn->ipn_flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC;
594 : }
595 :
596 : /*
597 : * prevent a DoS attack from the data-plane that
598 : * spams us with no-op updates to the MAC address
599 : */
600 278 : if (0 == mac_address_cmp (&ipn->ipn_mac, mac))
601 : {
602 275 : ip_neighbor_refresh (ipn);
603 275 : goto check_customers;
604 : }
605 :
606 3 : mac_address_copy (&ipn->ipn_mac, mac);
607 : }
608 : else
609 : {
610 9394 : IP_NEIGHBOR_INFO ("add: %U, %U",
611 : format_vnet_sw_if_index_name, vnet_get_main (),
612 : sw_if_index, format_ip_address, ip,
613 : format_ip_neighbor_flags, flags, format_mac_address_t,
614 : mac);
615 :
616 9394 : ipn = ip_neighbor_alloc (&key, mac, flags);
617 :
618 9394 : if (NULL == ipn)
619 1 : return VNET_API_ERROR_LIMIT_EXCEEDED;
620 : }
621 :
622 : /* Update time stamp and flags. */
623 9396 : ip_neighbor_refresh (ipn);
624 :
625 9396 : adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
626 9396 : fproto, &ip_addr_46 (&ipn->ipn_key->ipnk_ip),
627 : ip_neighbor_mk_complete_walk, ipn);
628 :
629 9671 : check_customers:
630 : /* Customer(s) requesting event for this address? */
631 9671 : ip_neighbor_publish (ip_neighbor_get_index (ipn), IP_NEIGHBOR_EVENT_ADDED);
632 :
633 9671 : if (stats_index)
634 5834 : *stats_index = adj_nbr_find (fproto,
635 5834 : fib_proto_to_link (fproto),
636 5834 : &ip_addr_46 (&ipn->ipn_key->ipnk_ip),
637 5834 : ipn->ipn_key->ipnk_sw_if_index);
638 9671 : return 0;
639 : }
640 :
641 : int
642 103 : ip_neighbor_del (const ip_address_t * ip, u32 sw_if_index)
643 : {
644 : ip_neighbor_t *ipn;
645 :
646 : /* main thread only */
647 103 : ASSERT (0 == vlib_get_thread_index ());
648 :
649 103 : IP_NEIGHBOR_INFO ("delete: %U, %U",
650 : format_vnet_sw_if_index_name, vnet_get_main (),
651 : sw_if_index, format_ip_address, ip);
652 :
653 103 : const ip_neighbor_key_t key = {
654 : .ipnk_ip = *ip,
655 : .ipnk_sw_if_index = sw_if_index,
656 : };
657 :
658 103 : ipn = ip_neighbor_db_find (&key);
659 :
660 103 : if (NULL == ipn)
661 0 : return (VNET_API_ERROR_NO_SUCH_ENTRY);
662 :
663 103 : ip_neighbor_destroy (ipn);
664 :
665 103 : return (0);
666 : }
667 :
668 : typedef struct ip_neighbor_del_all_ctx_t_
669 : {
670 : index_t *ipn_del;
671 : } ip_neighbor_del_all_ctx_t;
672 :
673 : static walk_rc_t
674 131 : ip_neighbor_del_all_walk_cb (index_t ipni, void *arg)
675 : {
676 131 : ip_neighbor_del_all_ctx_t *ctx = arg;
677 :
678 131 : vec_add1 (ctx->ipn_del, ipni);
679 :
680 131 : return (WALK_CONTINUE);
681 : }
682 :
683 : void
684 9 : ip_neighbor_del_all (ip_address_family_t af, u32 sw_if_index)
685 : {
686 9 : IP_NEIGHBOR_INFO ("delete-all: %U, %U",
687 : format_ip_address_family, af,
688 : format_vnet_sw_if_index_name, vnet_get_main (),
689 : sw_if_index);
690 :
691 9 : ip_neighbor_del_all_ctx_t ctx = {
692 : .ipn_del = NULL,
693 : };
694 : index_t *ipni;
695 :
696 9 : ip_neighbor_walk (af, sw_if_index, ip_neighbor_del_all_walk_cb, &ctx);
697 :
698 140 : vec_foreach (ipni,
699 131 : ctx.ipn_del) ip_neighbor_destroy (ip_neighbor_get (*ipni));
700 9 : vec_free (ctx.ipn_del);
701 9 : }
702 :
703 : void
704 9905 : ip_neighbor_update (vnet_main_t * vnm, adj_index_t ai)
705 : {
706 : ip_neighbor_t *ipn;
707 : ip_adjacency_t *adj;
708 :
709 9905 : adj = adj_get (ai);
710 :
711 9905 : ip_neighbor_key_t key = {
712 9905 : .ipnk_sw_if_index = adj->rewrite_header.sw_if_index,
713 : };
714 :
715 9905 : ip_address_from_46 (&adj->sub_type.nbr.next_hop,
716 9905 : adj->ia_nh_proto, &key.ipnk_ip);
717 :
718 9905 : ipn = ip_neighbor_db_find (&key);
719 :
720 9905 : switch (adj->lookup_next_index)
721 : {
722 9654 : case IP_LOOKUP_NEXT_ARP:
723 9654 : if (NULL != ipn)
724 : {
725 9415 : adj_nbr_walk_nh (adj->rewrite_header.sw_if_index,
726 9415 : adj->ia_nh_proto,
727 9415 : &adj->sub_type.nbr.next_hop,
728 : 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 239 : 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 : */
753 : /*
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 239 : adj = adj_get (ai);
760 239 : ip_neighbor_probe (adj);
761 : }
762 9654 : break;
763 251 : case IP_LOOKUP_NEXT_REWRITE:
764 : /* Update of an existing rewrite adjacency happens e.g. when the
765 : * interface's MAC address changes */
766 251 : if (NULL != ipn)
767 251 : ip_neighbor_mk_complete (ai, ipn);
768 251 : break;
769 0 : 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:
775 : 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 0 : ASSERT (0);
780 0 : break;
781 : }
782 9905 : }
783 :
784 : void
785 3832 : ip_neighbor_learn (const ip_neighbor_learn_t * l)
786 : {
787 3832 : ip_neighbor_add (&l->ip, &l->mac, l->sw_if_index,
788 : IP_NEIGHBOR_FLAG_DYNAMIC, NULL);
789 3832 : }
790 :
791 : static clib_error_t *
792 5 : ip_neighbor_cmd (vlib_main_t * vm,
793 : unformat_input_t * input, vlib_cli_command_t * cmd)
794 : {
795 5 : ip_address_t ip = IP_ADDRESS_V6_ALL_0S;
796 5 : mac_address_t mac = ZERO_MAC_ADDRESS;
797 5 : vnet_main_t *vnm = vnet_get_main ();
798 : ip_neighbor_flags_t flags;
799 5 : u32 sw_if_index = ~0;
800 5 : int is_add = 1, is_flush = 0;
801 5 : int count = 1;
802 :
803 5 : flags = IP_NEIGHBOR_FLAG_DYNAMIC;
804 :
805 12 : 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 7 : if (unformat (input, "%U %U %U",
809 : unformat_vnet_sw_interface, vnm, &sw_if_index,
810 : unformat_ip_address, &ip, unformat_mac_address_t, &mac))
811 : ;
812 2 : else if (unformat (input, "delete") || unformat (input, "del"))
813 0 : is_add = 0;
814 2 : else if (unformat (input, "flush"))
815 0 : is_flush = 1;
816 2 : else if (unformat (input, "static"))
817 : {
818 2 : flags |= IP_NEIGHBOR_FLAG_STATIC;
819 2 : flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC;
820 : }
821 0 : else if (unformat (input, "no-fib-entry"))
822 0 : flags |= IP_NEIGHBOR_FLAG_NO_FIB_ENTRY;
823 0 : else if (unformat (input, "count %d", &count))
824 : ;
825 : else
826 0 : break;
827 : }
828 :
829 5 : if (is_flush)
830 : {
831 0 : ip_neighbor_del_all (AF_IP4, sw_if_index);
832 0 : ip_neighbor_del_all (AF_IP6, sw_if_index);
833 0 : return NULL;
834 : }
835 :
836 10 : if (sw_if_index == ~0 ||
837 10 : ip_address_is_zero (&ip) || mac_address_is_zero (&mac))
838 0 : return clib_error_return (0,
839 : "specify interface, IP address and MAC: `%U'",
840 : format_unformat_error, input);
841 :
842 10 : while (count)
843 : {
844 5 : if (is_add)
845 5 : ip_neighbor_add (&ip, &mac, sw_if_index, flags, NULL);
846 : else
847 0 : ip_neighbor_del (&ip, sw_if_index);
848 :
849 5 : ip_address_increment (&ip);
850 5 : mac_address_increment (&mac);
851 :
852 5 : --count;
853 : }
854 :
855 5 : return NULL;
856 : }
857 :
858 : /* *INDENT-OFF* */
859 : /*?
860 : * Add or delete IPv4 ARP cache entries.
861 : *
862 : * @note 'set ip neighbor' options (e.g. delete, static,
863 : * 'count <number>', 'interface ip4_addr mac_addr') can be added in
864 : * any order and combination.
865 : *
866 : * @cliexpar
867 : * @parblock
868 : * Add or delete IPv4 ARP cache entries as follows. MAC Address can be in
869 : * either aa:bb:cc:dd:ee:ff format or aabb.ccdd.eeff format.
870 : * @cliexcmd{set ip neighbor GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
871 : * @cliexcmd{set ip neighbor delete GigabitEthernet2/0/0 6.0.0.3
872 : * de:ad:be:ef:ba:be}
873 : *
874 : * To add or delete an IPv4 ARP cache entry
875 : * table:
876 : * @cliexcmd{set ip neighbor GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
877 : * @cliexcmd{set ip neighbor delete GigabitEthernet2/0/0 6.0.0.3
878 : * dead.beef.babe}
879 : *
880 : * Add or delete IPv4 static ARP cache entries as follows:
881 : * @cliexcmd{set ip neighbor static GigabitEthernet2/0/0 6.0.0.3
882 : * dead.beef.babe}
883 : * @cliexcmd{set ip neighbor static delete GigabitEthernet2/0/0 6.0.0.3
884 : * dead.beef.babe}
885 : *
886 : * For testing / debugging purposes, the 'set ip neighbor' command can add or
887 : * delete multiple entries. Supply the 'count N' parameter:
888 : * @cliexcmd{set ip neighbor count 10 GigabitEthernet2/0/0 6.0.0.3
889 : * dead.beef.babe}
890 : * @endparblock
891 : ?*/
892 285289 : VLIB_CLI_COMMAND (ip_neighbor_command, static) = {
893 : .path = "set ip neighbor",
894 : .short_help = "set ip neighbor [del] <intfc> <ip-address> <mac-address> "
895 : "[static] [no-fib-entry] [count <count>]",
896 : .function = ip_neighbor_cmd,
897 : };
898 285289 : VLIB_CLI_COMMAND (ip_neighbor_command2, static) = {
899 : .path = "ip neighbor",
900 : .short_help = "ip neighbor [del] [flush] <intfc> <ip-address> <mac-address> "
901 : "[static] [no-fib-entry] [count <count>]",
902 : .function = ip_neighbor_cmd,
903 : };
904 : /* *INDENT-ON* */
905 :
906 : static int
907 132429 : ip_neighbor_sort (void *a1, void *a2)
908 : {
909 132429 : index_t *ipni1 = a1, *ipni2 = a2;
910 : ip_neighbor_t *ipn1, *ipn2;
911 : int cmp;
912 :
913 132429 : ipn1 = ip_neighbor_get (*ipni1);
914 132429 : ipn2 = ip_neighbor_get (*ipni2);
915 :
916 132429 : cmp = vnet_sw_interface_compare (vnet_get_main (),
917 132429 : ipn1->ipn_key->ipnk_sw_if_index,
918 132429 : ipn2->ipn_key->ipnk_sw_if_index);
919 132429 : if (!cmp)
920 117802 : cmp = ip_address_cmp (&ipn1->ipn_key->ipnk_ip, &ipn2->ipn_key->ipnk_ip);
921 132429 : return cmp;
922 : }
923 :
924 : static index_t *
925 145 : ip_neighbor_entries (u32 sw_if_index, ip_address_family_t af)
926 : {
927 145 : index_t *ipnis = NULL;
928 : ip_neighbor_t *ipn;
929 :
930 : /* *INDENT-OFF* */
931 23270 : pool_foreach (ipn, ip_neighbor_pool)
932 : {
933 23125 : if ((sw_if_index == ~0 ||
934 23125 : ipn->ipn_key->ipnk_sw_if_index == sw_if_index) &&
935 1579 : (N_AF == af ||
936 1579 : ip_neighbor_get_af(ipn) == af))
937 23020 : vec_add1 (ipnis, ip_neighbor_get_index(ipn));
938 : }
939 :
940 : /* *INDENT-ON* */
941 :
942 145 : if (ipnis)
943 127 : vec_sort_with_function (ipnis, ip_neighbor_sort);
944 145 : return ipnis;
945 : }
946 :
947 : static clib_error_t *
948 1 : ip_neighbor_show_sorted_i (vlib_main_t * vm,
949 : unformat_input_t * input,
950 : vlib_cli_command_t * cmd, ip_address_family_t af)
951 : {
952 : ip_neighbor_elt_t *elt, *head;
953 :
954 1 : head = pool_elt_at_index (ip_neighbor_elt_pool, ip_neighbor_list_head[af]);
955 :
956 :
957 1 : vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP",
958 : "Flags", "Ethernet", "Interface");
959 :
960 : /* *INDENT-OFF*/
961 : /* the list is time sorted, newest first, so start from the back
962 : * and work forwards. Stop when we get to one that is alive */
963 201 : clib_llist_foreach_reverse(ip_neighbor_elt_pool,
964 : ipne_anchor, head, elt,
965 : ({
966 : vlib_cli_output (vm, "%U", format_ip_neighbor, elt->ipne_index);
967 : }));
968 : /* *INDENT-ON*/
969 :
970 1 : return (NULL);
971 : }
972 :
973 : static clib_error_t *
974 145 : ip_neighbor_show_i (vlib_main_t * vm,
975 : unformat_input_t * input,
976 : vlib_cli_command_t * cmd, ip_address_family_t af)
977 : {
978 145 : index_t *ipni, *ipnis = NULL;
979 : u32 sw_if_index;
980 :
981 : /* Filter entries by interface if given. */
982 145 : sw_if_index = ~0;
983 145 : (void) unformat_user (input, unformat_vnet_sw_interface, vnet_get_main (),
984 : &sw_if_index);
985 :
986 145 : ipnis = ip_neighbor_entries (sw_if_index, af);
987 :
988 145 : if (ipnis)
989 127 : vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP",
990 : "Flags", "Ethernet", "Interface");
991 :
992 23165 : vec_foreach (ipni, ipnis)
993 : {
994 23020 : vlib_cli_output (vm, "%U", format_ip_neighbor, *ipni);
995 : }
996 145 : vec_free (ipnis);
997 :
998 145 : return (NULL);
999 : }
1000 :
1001 : static clib_error_t *
1002 60 : ip_neighbor_show (vlib_main_t * vm,
1003 : unformat_input_t * input, vlib_cli_command_t * cmd)
1004 : {
1005 60 : return (ip_neighbor_show_i (vm, input, cmd, N_AF));
1006 : }
1007 :
1008 : static clib_error_t *
1009 61 : ip6_neighbor_show (vlib_main_t * vm,
1010 : unformat_input_t * input, vlib_cli_command_t * cmd)
1011 : {
1012 61 : return (ip_neighbor_show_i (vm, input, cmd, AF_IP6));
1013 : }
1014 :
1015 : static clib_error_t *
1016 24 : ip4_neighbor_show (vlib_main_t * vm,
1017 : unformat_input_t * input, vlib_cli_command_t * cmd)
1018 : {
1019 24 : return (ip_neighbor_show_i (vm, input, cmd, AF_IP4));
1020 : }
1021 :
1022 : static clib_error_t *
1023 0 : ip6_neighbor_show_sorted (vlib_main_t * vm,
1024 : unformat_input_t * input, vlib_cli_command_t * cmd)
1025 : {
1026 0 : return (ip_neighbor_show_sorted_i (vm, input, cmd, AF_IP6));
1027 : }
1028 :
1029 : static clib_error_t *
1030 1 : ip4_neighbor_show_sorted (vlib_main_t * vm,
1031 : unformat_input_t * input, vlib_cli_command_t * cmd)
1032 : {
1033 1 : return (ip_neighbor_show_sorted_i (vm, input, cmd, AF_IP4));
1034 : }
1035 :
1036 : /*?
1037 : * Display all the IP neighbor entries.
1038 : *
1039 : * @cliexpar
1040 : * Example of how to display the IPv4 ARP table:
1041 : * @cliexstart{show ip neighbor}
1042 : * Time FIB IP4 Flags Ethernet Interface
1043 : * 346.3028 0 6.1.1.3 de:ad:be:ef:ba:be GigabitEthernet2/0/0
1044 : * 3077.4271 0 6.1.1.4 S de:ad:be:ef:ff:ff GigabitEthernet2/0/0
1045 : * 2998.6409 1 6.2.2.3 de:ad:be:ef:00:01 GigabitEthernet2/0/0
1046 : * Proxy arps enabled for:
1047 : * Fib_index 0 6.0.0.1 - 6.0.0.11
1048 : * @cliexend
1049 : ?*/
1050 : /* *INDENT-OFF* */
1051 285289 : VLIB_CLI_COMMAND (show_ip_neighbors_cmd_node, static) = {
1052 : .path = "show ip neighbors",
1053 : .function = ip_neighbor_show,
1054 : .short_help = "show ip neighbors [interface]",
1055 : };
1056 285289 : VLIB_CLI_COMMAND (show_ip4_neighbors_cmd_node, static) = {
1057 : .path = "show ip4 neighbors",
1058 : .function = ip4_neighbor_show,
1059 : .short_help = "show ip4 neighbors [interface]",
1060 : };
1061 285289 : VLIB_CLI_COMMAND (show_ip6_neighbors_cmd_node, static) = {
1062 : .path = "show ip6 neighbors",
1063 : .function = ip6_neighbor_show,
1064 : .short_help = "show ip6 neighbors [interface]",
1065 : };
1066 285289 : VLIB_CLI_COMMAND (show_ip_neighbor_cmd_node, static) = {
1067 : .path = "show ip neighbor",
1068 : .function = ip_neighbor_show,
1069 : .short_help = "show ip neighbor [interface]",
1070 : };
1071 285289 : VLIB_CLI_COMMAND (show_ip4_neighbor_cmd_node, static) = {
1072 : .path = "show ip4 neighbor",
1073 : .function = ip4_neighbor_show,
1074 : .short_help = "show ip4 neighbor [interface]",
1075 : };
1076 285289 : VLIB_CLI_COMMAND (show_ip6_neighbor_cmd_node, static) = {
1077 : .path = "show ip6 neighbor",
1078 : .function = ip6_neighbor_show,
1079 : .short_help = "show ip6 neighbor [interface]",
1080 : };
1081 285289 : VLIB_CLI_COMMAND (show_ip4_neighbor_sorted_cmd_node, static) = {
1082 : .path = "show ip4 neighbor-sorted",
1083 : .function = ip4_neighbor_show_sorted,
1084 : .short_help = "show ip4 neighbor-sorted",
1085 : };
1086 285289 : VLIB_CLI_COMMAND (show_ip6_neighbor_sorted_cmd_node, static) = {
1087 : .path = "show ip6 neighbor-sorted",
1088 : .function = ip6_neighbor_show_sorted,
1089 : .short_help = "show ip6 neighbor-sorted",
1090 : };
1091 : /* *INDENT-ON* */
1092 :
1093 : static ip_neighbor_vft_t ip_nbr_vfts[N_AF];
1094 :
1095 : void
1096 1150 : ip_neighbor_register (ip_address_family_t af, const ip_neighbor_vft_t * vft)
1097 : {
1098 1150 : ip_nbr_vfts[af] = *vft;
1099 1150 : }
1100 :
1101 : void
1102 869 : ip_neighbor_probe_dst (u32 sw_if_index, u32 thread_index,
1103 : ip_address_family_t af, const ip46_address_t *dst)
1104 : {
1105 869 : if (!vnet_sw_interface_is_admin_up (vnet_get_main (), sw_if_index))
1106 26 : return;
1107 :
1108 843 : switch (af)
1109 : {
1110 45 : case AF_IP6:
1111 45 : ip6_neighbor_probe_dst (sw_if_index, thread_index, &dst->ip6);
1112 45 : break;
1113 798 : case AF_IP4:
1114 798 : ip4_neighbor_probe_dst (sw_if_index, thread_index, &dst->ip4);
1115 798 : break;
1116 : }
1117 : }
1118 :
1119 : void
1120 239 : ip_neighbor_probe (const ip_adjacency_t * adj)
1121 : {
1122 239 : ip_neighbor_probe_dst (adj->rewrite_header.sw_if_index,
1123 239 : vlib_get_thread_index (),
1124 239 : ip_address_family_from_fib_proto (adj->ia_nh_proto),
1125 : &adj->sub_type.nbr.next_hop);
1126 239 : }
1127 :
1128 : void
1129 6996 : ip_neighbor_walk (ip_address_family_t af,
1130 : u32 sw_if_index, ip_neighbor_walk_cb_t cb, void *ctx)
1131 : {
1132 : ip_neighbor_key_t *key;
1133 : index_t ipni;
1134 :
1135 6996 : if (~0 == sw_if_index)
1136 : {
1137 : uword **hash;
1138 :
1139 73 : vec_foreach (hash, ip_neighbor_db[af].ipndb_hash)
1140 : {
1141 : /* *INDENT-OFF* */
1142 3517 : hash_foreach (key, ipni, *hash,
1143 : ({
1144 : if (WALK_STOP == cb (ipni, ctx))
1145 : break;
1146 : }));
1147 : /* *INDENT-ON* */
1148 : }
1149 : }
1150 : else
1151 : {
1152 : uword *hash;
1153 :
1154 6981 : if (vec_len (ip_neighbor_db[af].ipndb_hash) <= sw_if_index)
1155 1008 : return;
1156 5973 : hash = ip_neighbor_db[af].ipndb_hash[sw_if_index];
1157 :
1158 : /* *INDENT-OFF* */
1159 406839 : hash_foreach (key, ipni, hash,
1160 : ({
1161 : if (WALK_STOP == cb (ipni, ctx))
1162 : break;
1163 : }));
1164 : /* *INDENT-ON* */
1165 : }
1166 : }
1167 :
1168 : int
1169 0 : ip4_neighbor_proxy_add (u32 fib_index,
1170 : const ip4_address_t * start,
1171 : const ip4_address_t * end)
1172 : {
1173 0 : if (ip_nbr_vfts[AF_IP4].inv_proxy4_add)
1174 : {
1175 0 : return (ip_nbr_vfts[AF_IP4].inv_proxy4_add (fib_index, start, end));
1176 : }
1177 :
1178 0 : return (-1);
1179 : }
1180 :
1181 : int
1182 0 : ip4_neighbor_proxy_delete (u32 fib_index,
1183 : const ip4_address_t * start,
1184 : const ip4_address_t * end)
1185 : {
1186 0 : if (ip_nbr_vfts[AF_IP4].inv_proxy4_del)
1187 : {
1188 0 : return (ip_nbr_vfts[AF_IP4].inv_proxy4_del (fib_index, start, end));
1189 : }
1190 0 : return -1;
1191 : }
1192 :
1193 : int
1194 0 : ip4_neighbor_proxy_enable (u32 sw_if_index)
1195 : {
1196 0 : if (ip_nbr_vfts[AF_IP4].inv_proxy4_enable)
1197 : {
1198 0 : return (ip_nbr_vfts[AF_IP4].inv_proxy4_enable (sw_if_index));
1199 : }
1200 0 : return -1;
1201 : }
1202 :
1203 : int
1204 0 : ip4_neighbor_proxy_disable (u32 sw_if_index)
1205 : {
1206 0 : if (ip_nbr_vfts[AF_IP4].inv_proxy4_disable)
1207 : {
1208 0 : return (ip_nbr_vfts[AF_IP4].inv_proxy4_disable (sw_if_index));
1209 : }
1210 0 : return -1;
1211 : }
1212 :
1213 : int
1214 0 : ip6_neighbor_proxy_add (u32 sw_if_index, const ip6_address_t * addr)
1215 : {
1216 0 : if (ip_nbr_vfts[AF_IP6].inv_proxy6_add)
1217 : {
1218 0 : return (ip_nbr_vfts[AF_IP6].inv_proxy6_add (sw_if_index, addr));
1219 : }
1220 0 : return -1;
1221 : }
1222 :
1223 : int
1224 0 : ip6_neighbor_proxy_del (u32 sw_if_index, const ip6_address_t * addr)
1225 : {
1226 0 : if (ip_nbr_vfts[AF_IP6].inv_proxy6_del)
1227 : {
1228 0 : return (ip_nbr_vfts[AF_IP6].inv_proxy6_del (sw_if_index, addr));
1229 : }
1230 0 : return -1;
1231 : }
1232 :
1233 : void
1234 14020 : ip_neighbor_populate (ip_address_family_t af, u32 sw_if_index)
1235 : {
1236 14020 : index_t *ipnis = NULL, *ipni;
1237 : ip_neighbor_t *ipn;
1238 :
1239 14020 : IP_NEIGHBOR_DBG ("populate: %U %U",
1240 : format_vnet_sw_if_index_name, vnet_get_main (),
1241 : sw_if_index, format_ip_address_family, af);
1242 :
1243 : /* *INDENT-OFF* */
1244 43658 : pool_foreach (ipn, ip_neighbor_pool)
1245 : {
1246 29638 : if (ip_neighbor_get_af(ipn) == af &&
1247 14819 : ipn->ipn_key->ipnk_sw_if_index == sw_if_index)
1248 403 : vec_add1 (ipnis, ipn - ip_neighbor_pool);
1249 : }
1250 : /* *INDENT-ON* */
1251 :
1252 14423 : vec_foreach (ipni, ipnis)
1253 : {
1254 403 : ipn = ip_neighbor_get (*ipni);
1255 :
1256 403 : adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
1257 403 : ip_address_family_to_fib_proto (ip_neighbor_get_af
1258 : (ipn)),
1259 403 : &ip_addr_46 (&ipn->ipn_key->ipnk_ip),
1260 : ip_neighbor_mk_complete_walk, ipn);
1261 : }
1262 14020 : vec_free (ipnis);
1263 14020 : }
1264 :
1265 : void
1266 21510 : ip_neighbor_flush (ip_address_family_t af, u32 sw_if_index)
1267 : {
1268 21510 : index_t *ipnis = NULL, *ipni;
1269 : ip_neighbor_t *ipn;
1270 :
1271 :
1272 21510 : IP_NEIGHBOR_DBG ("flush: %U %U",
1273 : format_vnet_sw_if_index_name, vnet_get_main (),
1274 : sw_if_index, format_ip_address_family, af);
1275 :
1276 : /* *INDENT-OFF* */
1277 71531 : pool_foreach (ipn, ip_neighbor_pool)
1278 : {
1279 50021 : if (ip_neighbor_get_af(ipn) == af &&
1280 28657 : ipn->ipn_key->ipnk_sw_if_index == sw_if_index &&
1281 2853 : ip_neighbor_is_dynamic (ipn))
1282 2847 : vec_add1 (ipnis, ipn - ip_neighbor_pool);
1283 : }
1284 : /* *INDENT-ON* */
1285 :
1286 24357 : vec_foreach (ipni, ipnis) ip_neighbor_destroy (ip_neighbor_get (*ipni));
1287 21510 : vec_free (ipnis);
1288 21510 : }
1289 :
1290 : walk_rc_t
1291 256 : ip_neighbor_mark_one (index_t ipni, void *ctx)
1292 : {
1293 : ip_neighbor_t *ipn;
1294 :
1295 256 : ipn = ip_neighbor_get (ipni);
1296 :
1297 256 : ipn->ipn_flags |= IP_NEIGHBOR_FLAG_STALE;
1298 :
1299 256 : return (WALK_CONTINUE);
1300 : }
1301 :
1302 : void
1303 4 : ip_neighbor_mark (ip_address_family_t af)
1304 : {
1305 4 : ip_neighbor_walk (af, ~0, ip_neighbor_mark_one, NULL);
1306 4 : }
1307 :
1308 : typedef struct ip_neighbor_sweep_ctx_t_
1309 : {
1310 : index_t *ipnsc_stale;
1311 : } ip_neighbor_sweep_ctx_t;
1312 :
1313 : static walk_rc_t
1314 256 : ip_neighbor_sweep_one (index_t ipni, void *arg)
1315 : {
1316 256 : ip_neighbor_sweep_ctx_t *ctx = arg;
1317 : ip_neighbor_t *ipn;
1318 :
1319 256 : ipn = ip_neighbor_get (ipni);
1320 :
1321 256 : if (ipn->ipn_flags & IP_NEIGHBOR_FLAG_STALE)
1322 : {
1323 192 : vec_add1 (ctx->ipnsc_stale, ipni);
1324 : }
1325 :
1326 256 : return (WALK_CONTINUE);
1327 : }
1328 :
1329 : void
1330 4 : ip_neighbor_sweep (ip_address_family_t af)
1331 : {
1332 4 : ip_neighbor_sweep_ctx_t ctx = { };
1333 : index_t *ipni;
1334 :
1335 4 : ip_neighbor_walk (af, ~0, ip_neighbor_sweep_one, &ctx);
1336 :
1337 196 : vec_foreach (ipni, ctx.ipnsc_stale)
1338 : {
1339 192 : ip_neighbor_destroy (ip_neighbor_get (*ipni));
1340 : }
1341 4 : vec_free (ctx.ipnsc_stale);
1342 4 : }
1343 :
1344 : /*
1345 : * Remove any arp entries associated with the specified interface
1346 : */
1347 : static clib_error_t *
1348 13514 : ip_neighbor_interface_admin_change (vnet_main_t * vnm,
1349 : u32 sw_if_index, u32 flags)
1350 : {
1351 : ip_address_family_t af;
1352 :
1353 13514 : IP_NEIGHBOR_DBG ("interface-admin: %U %s",
1354 : format_vnet_sw_if_index_name, vnet_get_main (),
1355 : sw_if_index,
1356 : (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ? "up" : "down"));
1357 :
1358 13514 : if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
1359 : {
1360 21030 : FOR_EACH_IP_ADDRESS_FAMILY (af) ip_neighbor_populate (af, sw_if_index);
1361 : }
1362 : else
1363 : {
1364 : /* admin down, flush all neighbours */
1365 19512 : FOR_EACH_IP_ADDRESS_FAMILY (af) ip_neighbor_flush (af, sw_if_index);
1366 : }
1367 :
1368 13514 : return (NULL);
1369 : }
1370 :
1371 2881 : VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ip_neighbor_interface_admin_change);
1372 :
1373 : /*
1374 : * Remove any arp entries associated with the specified interface
1375 : */
1376 : static clib_error_t *
1377 11798 : ip_neighbor_add_del_sw_interface (vnet_main_t *vnm, u32 sw_if_index,
1378 : u32 is_add)
1379 : {
1380 11798 : IP_NEIGHBOR_DBG ("interface-change: %U %s",
1381 : format_vnet_sw_if_index_name, vnet_get_main (),
1382 : sw_if_index, (is_add ? "add" : "del"));
1383 :
1384 11798 : if (!is_add && sw_if_index != ~0)
1385 : {
1386 : ip_address_family_t af;
1387 :
1388 12753 : FOR_EACH_IP_ADDRESS_FAMILY (af) ip_neighbor_flush (af, sw_if_index);
1389 : }
1390 :
1391 11798 : if (is_add)
1392 : {
1393 7547 : ip_neighbor_alloc_ctr (&ip_neighbor_counters[AF_IP4], sw_if_index);
1394 7547 : ip_neighbor_alloc_ctr (&ip_neighbor_counters[AF_IP6], sw_if_index);
1395 : }
1396 :
1397 11798 : return (NULL);
1398 : }
1399 :
1400 3459 : VNET_SW_INTERFACE_ADD_DEL_FUNCTION (ip_neighbor_add_del_sw_interface);
1401 :
1402 : typedef struct ip_neighbor_walk_covered_ctx_t_
1403 : {
1404 : ip_address_t addr;
1405 : u32 length;
1406 : index_t *ipnis;
1407 : } ip_neighbor_walk_covered_ctx_t;
1408 :
1409 : static walk_rc_t
1410 1961 : ip_neighbor_walk_covered (index_t ipni, void *arg)
1411 : {
1412 1961 : ip_neighbor_walk_covered_ctx_t *ctx = arg;
1413 : ip_neighbor_t *ipn;
1414 :
1415 1961 : ipn = ip_neighbor_get (ipni);
1416 :
1417 1961 : if (AF_IP4 == ip_addr_version (&ctx->addr))
1418 : {
1419 935 : if (ip4_destination_matches_route (&ip4_main,
1420 935 : &ip_addr_v4 (&ipn->ipn_key->ipnk_ip),
1421 935 : &ip_addr_v4 (&ctx->addr),
1422 1864 : ctx->length) &&
1423 929 : ip_neighbor_is_dynamic (ipn))
1424 : {
1425 924 : vec_add1 (ctx->ipnis, ip_neighbor_get_index (ipn));
1426 : }
1427 : }
1428 1026 : else if (AF_IP6 == ip_addr_version (&ctx->addr))
1429 : {
1430 1026 : if (ip6_destination_matches_route (&ip6_main,
1431 1026 : &ip_addr_v6 (&ipn->ipn_key->ipnk_ip),
1432 1026 : &ip_addr_v6 (&ctx->addr),
1433 2045 : ctx->length) &&
1434 1019 : ip_neighbor_is_dynamic (ipn))
1435 : {
1436 1019 : vec_add1 (ctx->ipnis, ip_neighbor_get_index (ipn));
1437 : }
1438 : }
1439 1961 : return (WALK_CONTINUE);
1440 : }
1441 :
1442 :
1443 : /*
1444 : * callback when an interface address is added or deleted
1445 : */
1446 : static void
1447 4838 : ip_neighbor_add_del_interface_address_v4 (ip4_main_t * im,
1448 : uword opaque,
1449 : u32 sw_if_index,
1450 : ip4_address_t * address,
1451 : u32 address_length,
1452 : u32 if_address_index, u32 is_del)
1453 : {
1454 : /*
1455 : * Flush the ARP cache of all entries covered by the address
1456 : * that is being removed.
1457 : */
1458 4838 : IP_NEIGHBOR_DBG ("addr-%s: %U, %U/%d", (is_del ? "del" : "add"),
1459 : format_vnet_sw_if_index_name, vnet_get_main (), sw_if_index,
1460 : format_ip4_address, address, address_length);
1461 :
1462 4838 : if (is_del)
1463 : {
1464 : /* *INDENT-OFF* */
1465 2297 : ip_neighbor_walk_covered_ctx_t ctx = {
1466 : .addr = {
1467 : .ip.ip4 = *address,
1468 : .version = AF_IP4,
1469 : },
1470 : .length = address_length,
1471 : };
1472 : /* *INDENT-ON* */
1473 : index_t *ipni;
1474 :
1475 2297 : ip_neighbor_walk (AF_IP4, sw_if_index, ip_neighbor_walk_covered, &ctx);
1476 :
1477 3221 : vec_foreach (ipni, ctx.ipnis)
1478 924 : ip_neighbor_destroy (ip_neighbor_get (*ipni));
1479 :
1480 2297 : vec_free (ctx.ipnis);
1481 : }
1482 4838 : }
1483 :
1484 : /*
1485 : * callback when an interface address is added or deleted
1486 : */
1487 : static void
1488 4115 : ip_neighbor_add_del_interface_address_v6 (ip6_main_t * im,
1489 : uword opaque,
1490 : u32 sw_if_index,
1491 : ip6_address_t * address,
1492 : u32 address_length,
1493 : u32 if_address_index, u32 is_del)
1494 : {
1495 : /*
1496 : * Flush the ARP cache of all entries covered by the address
1497 : * that is being removed.
1498 : */
1499 4115 : IP_NEIGHBOR_DBG ("addr-change: %U, %U/%d %s",
1500 : format_vnet_sw_if_index_name, vnet_get_main (),
1501 : sw_if_index, format_ip6_address, address, address_length,
1502 : (is_del ? "del" : "add"));
1503 :
1504 4115 : if (is_del)
1505 : {
1506 : /* *INDENT-OFF* */
1507 1974 : ip_neighbor_walk_covered_ctx_t ctx = {
1508 : .addr = {
1509 : .ip.ip6 = *address,
1510 : .version = AF_IP6,
1511 : },
1512 : .length = address_length,
1513 : };
1514 : /* *INDENT-ON* */
1515 : index_t *ipni;
1516 :
1517 1974 : ip_neighbor_walk (AF_IP6, sw_if_index, ip_neighbor_walk_covered, &ctx);
1518 :
1519 2993 : vec_foreach (ipni, ctx.ipnis)
1520 1019 : ip_neighbor_destroy (ip_neighbor_get (*ipni));
1521 :
1522 1974 : vec_free (ctx.ipnis);
1523 : }
1524 4115 : }
1525 :
1526 : typedef struct ip_neighbor_table_bind_ctx_t_
1527 : {
1528 : u32 new_fib_index;
1529 : u32 old_fib_index;
1530 : } ip_neighbor_table_bind_ctx_t;
1531 :
1532 : static walk_rc_t
1533 4 : ip_neighbor_walk_table_bind (index_t ipni, void *arg)
1534 : {
1535 4 : ip_neighbor_table_bind_ctx_t *ctx = arg;
1536 : ip_neighbor_t *ipn;
1537 :
1538 4 : ipn = ip_neighbor_get (ipni);
1539 4 : ip_neighbor_adj_fib_remove (ipn, ctx->old_fib_index);
1540 4 : ip_neighbor_adj_fib_add (ipn, ctx->new_fib_index);
1541 :
1542 4 : return (WALK_CONTINUE);
1543 : }
1544 :
1545 : static void
1546 946 : ip_neighbor_table_bind_v4 (ip4_main_t * im,
1547 : uword opaque,
1548 : u32 sw_if_index,
1549 : u32 new_fib_index, u32 old_fib_index)
1550 : {
1551 946 : ip_neighbor_table_bind_ctx_t ctx = {
1552 : .old_fib_index = old_fib_index,
1553 : .new_fib_index = new_fib_index,
1554 : };
1555 :
1556 946 : ip_neighbor_walk (AF_IP4, sw_if_index, ip_neighbor_walk_table_bind, &ctx);
1557 946 : }
1558 :
1559 : static void
1560 777 : ip_neighbor_table_bind_v6 (ip6_main_t * im,
1561 : uword opaque,
1562 : u32 sw_if_index,
1563 : u32 new_fib_index, u32 old_fib_index)
1564 : {
1565 777 : ip_neighbor_table_bind_ctx_t ctx = {
1566 : .old_fib_index = old_fib_index,
1567 : .new_fib_index = new_fib_index,
1568 : };
1569 :
1570 777 : ip_neighbor_walk (AF_IP6, sw_if_index, ip_neighbor_walk_table_bind, &ctx);
1571 777 : }
1572 :
1573 : typedef enum ip_neighbor_age_state_t_
1574 : {
1575 : IP_NEIGHBOR_AGE_ALIVE,
1576 : IP_NEIGHBOR_AGE_PROBE,
1577 : IP_NEIGHBOR_AGE_DEAD,
1578 : } ip_neighbor_age_state_t;
1579 :
1580 : #define IP_NEIGHBOR_PROCESS_SLEEP_LONG (0)
1581 :
1582 : static ip_neighbor_age_state_t
1583 851 : ip_neighbour_age_out (index_t ipni, f64 now, f64 * wait)
1584 : {
1585 : ip_address_family_t af;
1586 : ip_neighbor_t *ipn;
1587 : u32 ipndb_age;
1588 : u32 ttl;
1589 :
1590 851 : ipn = ip_neighbor_get (ipni);
1591 851 : af = ip_neighbor_get_af (ipn);
1592 851 : ipndb_age = ip_neighbor_db[af].ipndb_age;
1593 851 : ttl = now - ipn->ipn_time_last_updated;
1594 851 : *wait = ipndb_age;
1595 :
1596 851 : if (ttl > ipndb_age)
1597 : {
1598 840 : IP_NEIGHBOR_DBG ("aged: %U @%f - %f > %d",
1599 : format_ip_neighbor, ipni, now,
1600 : ipn->ipn_time_last_updated, ipndb_age);
1601 840 : if (ipn->ipn_n_probes > 2)
1602 : {
1603 : /* 3 strikes and yea-re out */
1604 210 : IP_NEIGHBOR_DBG ("dead: %U", format_ip_neighbor, ipni);
1605 210 : *wait = 1;
1606 210 : return (IP_NEIGHBOR_AGE_DEAD);
1607 : }
1608 : else
1609 : {
1610 630 : ip_neighbor_probe_dst (ip_neighbor_get_sw_if_index (ipn),
1611 630 : vlib_get_thread_index (), af,
1612 630 : &ip_addr_46 (&ipn->ipn_key->ipnk_ip));
1613 :
1614 630 : ipn->ipn_n_probes++;
1615 630 : *wait = 1;
1616 : }
1617 : }
1618 : else
1619 : {
1620 : /* here we are sure that ttl <= ipndb_age */
1621 11 : *wait = ipndb_age - ttl + 1;
1622 11 : return (IP_NEIGHBOR_AGE_ALIVE);
1623 : }
1624 :
1625 630 : return (IP_NEIGHBOR_AGE_PROBE);
1626 : }
1627 :
1628 : typedef enum ip_neighbor_process_event_t_
1629 : {
1630 : IP_NEIGHBOR_AGE_PROCESS_WAKEUP,
1631 : } ip_neighbor_process_event_t;
1632 :
1633 : static uword
1634 1150 : ip_neighbor_age_loop (vlib_main_t * vm,
1635 : vlib_node_runtime_t * rt,
1636 : vlib_frame_t * f, ip_address_family_t af)
1637 : {
1638 1150 : uword event_type, *event_data = NULL;
1639 : f64 timeout;
1640 :
1641 : /* Set the timeout to an effectively infinite value when the process starts */
1642 1150 : timeout = IP_NEIGHBOR_PROCESS_SLEEP_LONG;
1643 :
1644 : while (1)
1645 15 : {
1646 : f64 now;
1647 :
1648 1165 : if (!timeout)
1649 1153 : vlib_process_wait_for_event (vm);
1650 : else
1651 12 : vlib_process_wait_for_event_or_clock (vm, timeout);
1652 :
1653 15 : event_type = vlib_process_get_events (vm, &event_data);
1654 15 : vec_reset_length (event_data);
1655 :
1656 15 : now = vlib_time_now (vm);
1657 :
1658 15 : switch (event_type)
1659 : {
1660 10 : case ~0:
1661 : {
1662 : /* timer expired */
1663 : ip_neighbor_elt_t *elt, *head;
1664 : f64 wait;
1665 :
1666 10 : timeout = ip_neighbor_db[af].ipndb_age;
1667 10 : head = pool_elt_at_index (ip_neighbor_elt_pool,
1668 : ip_neighbor_list_head[af]);
1669 :
1670 : /* *INDENT-OFF*/
1671 : /* the list is time sorted, newest first, so start from the back
1672 : * and work forwards. Stop when we get to one that is alive */
1673 220 : restart:
1674 860 : clib_llist_foreach_reverse(ip_neighbor_elt_pool,
1675 : ipne_anchor, head, elt,
1676 : ({
1677 : ip_neighbor_age_state_t res;
1678 :
1679 : res = ip_neighbour_age_out(elt->ipne_index, now, &wait);
1680 :
1681 : if (IP_NEIGHBOR_AGE_ALIVE == res) {
1682 : /* the oldest neighbor has not yet expired, go back to sleep */
1683 : timeout = clib_min (wait, timeout);
1684 : break;
1685 : }
1686 : else if (IP_NEIGHBOR_AGE_DEAD == res) {
1687 : /* the oldest neighbor is dead, pop it, then restart the walk
1688 : * again from the back */
1689 : ip_neighbor_destroy (ip_neighbor_get(elt->ipne_index));
1690 : goto restart;
1691 : }
1692 :
1693 : timeout = clib_min (wait, timeout);
1694 : }));
1695 : /* *INDENT-ON* */
1696 10 : break;
1697 : }
1698 5 : case IP_NEIGHBOR_AGE_PROCESS_WAKEUP:
1699 : {
1700 :
1701 5 : if (!ip_neighbor_db[af].ipndb_age)
1702 : {
1703 : /* aging has been disabled */
1704 3 : timeout = 0;
1705 3 : break;
1706 : }
1707 : ip_neighbor_elt_t *elt, *head;
1708 :
1709 2 : head = pool_elt_at_index (ip_neighbor_elt_pool,
1710 : ip_neighbor_list_head[af]);
1711 : /* no neighbors yet */
1712 2 : if (clib_llist_is_empty (ip_neighbor_elt_pool, ipne_anchor, head))
1713 : {
1714 1 : timeout = ip_neighbor_db[af].ipndb_age;
1715 1 : break;
1716 : }
1717 :
1718 : /* poke the oldset neighbour for aging, which returns how long we sleep for */
1719 1 : elt = clib_llist_prev (ip_neighbor_elt_pool, ipne_anchor, head);
1720 1 : ip_neighbour_age_out (elt->ipne_index, now, &timeout);
1721 1 : break;
1722 : }
1723 : }
1724 15 : }
1725 : return 0;
1726 : }
1727 :
1728 : static uword
1729 575 : ip4_neighbor_age_process (vlib_main_t * vm,
1730 : vlib_node_runtime_t * rt, vlib_frame_t * f)
1731 : {
1732 575 : return (ip_neighbor_age_loop (vm, rt, f, AF_IP4));
1733 : }
1734 :
1735 : static uword
1736 575 : ip6_neighbor_age_process (vlib_main_t * vm,
1737 : vlib_node_runtime_t * rt, vlib_frame_t * f)
1738 : {
1739 575 : return (ip_neighbor_age_loop (vm, rt, f, AF_IP6));
1740 : }
1741 :
1742 : /* *INDENT-OFF* */
1743 183788 : VLIB_REGISTER_NODE (ip4_neighbor_age_process_node,static) = {
1744 : .function = ip4_neighbor_age_process,
1745 : .type = VLIB_NODE_TYPE_PROCESS,
1746 : .name = "ip4-neighbor-age-process",
1747 : };
1748 183788 : VLIB_REGISTER_NODE (ip6_neighbor_age_process_node,static) = {
1749 : .function = ip6_neighbor_age_process,
1750 : .type = VLIB_NODE_TYPE_PROCESS,
1751 : .name = "ip6-neighbor-age-process",
1752 : };
1753 : /* *INDENT-ON* */
1754 :
1755 : int
1756 5 : ip_neighbor_config (ip_address_family_t af, u32 limit, u32 age, bool recycle)
1757 : {
1758 5 : ip_neighbor_db[af].ipndb_limit = limit;
1759 5 : ip_neighbor_db[af].ipndb_recycle = recycle;
1760 5 : ip_neighbor_db[af].ipndb_age = age;
1761 :
1762 10 : vlib_process_signal_event (vlib_get_main (),
1763 : (AF_IP4 == af ?
1764 5 : ip4_neighbor_age_process_node.index :
1765 0 : ip6_neighbor_age_process_node.index),
1766 : IP_NEIGHBOR_AGE_PROCESS_WAKEUP, 0);
1767 :
1768 5 : return (0);
1769 : }
1770 :
1771 : int
1772 6 : ip_neighbor_get_config (ip_address_family_t af, u32 *limit, u32 *age,
1773 : bool *recycle)
1774 : {
1775 6 : *limit = ip_neighbor_db[af].ipndb_limit;
1776 6 : *age = ip_neighbor_db[af].ipndb_age;
1777 6 : *recycle = ip_neighbor_db[af].ipndb_recycle;
1778 :
1779 6 : return (0);
1780 : }
1781 :
1782 : static clib_error_t *
1783 1 : ip_neighbor_config_show (vlib_main_t * vm,
1784 : unformat_input_t * input, vlib_cli_command_t * cmd)
1785 : {
1786 : ip_address_family_t af;
1787 :
1788 : /* *INDENT-OFF* */
1789 3 : FOR_EACH_IP_ADDRESS_FAMILY(af) {
1790 2 : vlib_cli_output (vm, "%U:", format_ip_address_family, af);
1791 2 : vlib_cli_output (vm, " limit:%d, age:%d, recycle:%d",
1792 : ip_neighbor_db[af].ipndb_limit,
1793 : ip_neighbor_db[af].ipndb_age,
1794 2 : ip_neighbor_db[af].ipndb_recycle);
1795 : }
1796 :
1797 : /* *INDENT-ON* */
1798 1 : return (NULL);
1799 : }
1800 :
1801 : static clib_error_t *
1802 0 : ip_neighbor_config_set (vlib_main_t *vm, unformat_input_t *input,
1803 : vlib_cli_command_t *cmd)
1804 : {
1805 0 : unformat_input_t _line_input, *line_input = &_line_input;
1806 0 : clib_error_t *error = NULL;
1807 : ip_address_family_t af;
1808 : u32 limit, age;
1809 : bool recycle;
1810 :
1811 0 : if (!unformat_user (input, unformat_line_input, line_input))
1812 0 : return 0;
1813 :
1814 0 : if (!unformat (line_input, "%U", unformat_ip_address_family, &af))
1815 : {
1816 0 : error = unformat_parse_error (line_input);
1817 0 : goto done;
1818 : }
1819 :
1820 0 : limit = ip_neighbor_db[af].ipndb_limit;
1821 0 : age = ip_neighbor_db[af].ipndb_age;
1822 0 : recycle = ip_neighbor_db[af].ipndb_recycle;
1823 :
1824 0 : while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1825 : {
1826 0 : if (unformat (line_input, "limit %u", &limit))
1827 : ;
1828 0 : else if (unformat (line_input, "age %u", &age))
1829 : ;
1830 0 : else if (unformat (line_input, "recycle"))
1831 0 : recycle = true;
1832 0 : else if (unformat (line_input, "norecycle"))
1833 0 : recycle = false;
1834 : else
1835 : {
1836 0 : error = unformat_parse_error (line_input);
1837 0 : goto done;
1838 : }
1839 : }
1840 :
1841 0 : ip_neighbor_config (af, limit, age, recycle);
1842 :
1843 0 : done:
1844 0 : unformat_free (line_input);
1845 0 : return error;
1846 : }
1847 :
1848 : static void
1849 7 : ip_neighbor_stats_show_one (vlib_main_t *vm, vnet_main_t *vnm, u32 sw_if_index)
1850 : {
1851 7 : vlib_cli_output (vm, " %U", format_vnet_sw_if_index_name, vnm, sw_if_index);
1852 7 : vlib_cli_output (vm, " arp:%U", format_ip_neighbor_counters,
1853 : &ip_neighbor_counters[AF_IP4], sw_if_index);
1854 7 : vlib_cli_output (vm, " nd: %U", format_ip_neighbor_counters,
1855 : &ip_neighbor_counters[AF_IP6], sw_if_index);
1856 7 : }
1857 :
1858 : static walk_rc_t
1859 5 : ip_neighbor_stats_show_cb (vnet_main_t *vnm, vnet_sw_interface_t *si,
1860 : void *ctx)
1861 : {
1862 5 : ip_neighbor_stats_show_one (ctx, vnm, si->sw_if_index);
1863 :
1864 5 : return (WALK_CONTINUE);
1865 : }
1866 :
1867 : static clib_error_t *
1868 3 : ip_neighbor_stats_show (vlib_main_t *vm, unformat_input_t *input,
1869 : vlib_cli_command_t *cmd)
1870 : {
1871 : vnet_main_t *vnm;
1872 : u32 sw_if_index;
1873 :
1874 3 : vnm = vnet_get_main ();
1875 3 : sw_if_index = ~0;
1876 3 : (void) unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index);
1877 :
1878 3 : if (~0 == sw_if_index)
1879 : {
1880 1 : vnet_sw_interface_walk (vnm, ip_neighbor_stats_show_cb, vm);
1881 : }
1882 : else
1883 : {
1884 2 : ip_neighbor_stats_show_one (vm, vnm, sw_if_index);
1885 : }
1886 3 : return (NULL);
1887 : }
1888 :
1889 : /* *INDENT-OFF* */
1890 285289 : VLIB_CLI_COMMAND (show_ip_neighbor_cfg_cmd_node, static) = {
1891 : .path = "show ip neighbor-config",
1892 : .function = ip_neighbor_config_show,
1893 : .short_help = "show ip neighbor-config",
1894 : };
1895 285289 : VLIB_CLI_COMMAND (set_ip_neighbor_cfg_cmd_node, static) = {
1896 : .path = "set ip neighbor-config",
1897 : .function = ip_neighbor_config_set,
1898 : .short_help = "set ip neighbor-config ip4|ip6 [limit <limit>] [age <age>] "
1899 : "[recycle|norecycle]",
1900 : };
1901 285289 : VLIB_CLI_COMMAND (show_ip_neighbor_stats_cmd_node, static) = {
1902 : .path = "show ip neighbor-stats",
1903 : .function = ip_neighbor_stats_show,
1904 : .short_help = "show ip neighbor-stats [interface]",
1905 : };
1906 : /* *INDENT-ON* */
1907 :
1908 : static clib_error_t *
1909 575 : ip_neighbor_init (vlib_main_t * vm)
1910 : {
1911 : {
1912 575 : ip4_add_del_interface_address_callback_t cb = {
1913 : .function = ip_neighbor_add_del_interface_address_v4,
1914 : };
1915 575 : vec_add1 (ip4_main.add_del_interface_address_callbacks, cb);
1916 : }
1917 : {
1918 575 : ip6_add_del_interface_address_callback_t cb = {
1919 : .function = ip_neighbor_add_del_interface_address_v6,
1920 : };
1921 575 : vec_add1 (ip6_main.add_del_interface_address_callbacks, cb);
1922 : }
1923 : {
1924 575 : ip4_table_bind_callback_t cb = {
1925 : .function = ip_neighbor_table_bind_v4,
1926 : };
1927 575 : vec_add1 (ip4_main.table_bind_callbacks, cb);
1928 : }
1929 : {
1930 575 : ip6_table_bind_callback_t cb = {
1931 : .function = ip_neighbor_table_bind_v6,
1932 : };
1933 575 : vec_add1 (ip6_main.table_bind_callbacks, cb);
1934 : }
1935 575 : ipn_logger = vlib_log_register_class ("ip", "neighbor");
1936 :
1937 : ip_address_family_t af;
1938 :
1939 1725 : FOR_EACH_IP_ADDRESS_FAMILY (af)
1940 1150 : ip_neighbor_list_head[af] =
1941 1150 : clib_llist_make_head (ip_neighbor_elt_pool, ipne_anchor);
1942 :
1943 575 : return (NULL);
1944 : }
1945 :
1946 : /* *INDENT-OFF* */
1947 47807 : VLIB_INIT_FUNCTION (ip_neighbor_init) =
1948 : {
1949 : .runs_after = VLIB_INITS("ip_main_init"),
1950 : };
1951 : /* *INDENT-ON* */
1952 :
1953 : /*
1954 : * fd.io coding-style-patch-verification: ON
1955 : *
1956 : * Local Variables:
1957 : * eval: (c-set-style "gnu")
1958 : * End:
1959 : */
|