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