ip-neighbor: add ip neighbor flush
[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, is_flush = 0;
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, "flush"))
815         is_flush = 1;
816       else if (unformat (input, "static"))
817         {
818           flags |= IP_NEIGHBOR_FLAG_STATIC;
819           flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC;
820         }
821       else if (unformat (input, "no-fib-entry"))
822         flags |= IP_NEIGHBOR_FLAG_NO_FIB_ENTRY;
823       else if (unformat (input, "count %d", &count))
824         ;
825       else
826         break;
827     }
828
829   if (is_flush)
830     {
831       ip_neighbor_del_all (AF_IP4, sw_if_index);
832       ip_neighbor_del_all (AF_IP6, sw_if_index);
833       return NULL;
834     }
835
836   if (sw_if_index == ~0 ||
837       ip_address_is_zero (&ip) || mac_address_is_zero (&mac))
838     return clib_error_return (0,
839                               "specify interface, IP address and MAC: `%U'",
840                               format_unformat_error, input);
841
842   while (count)
843     {
844       if (is_add)
845         ip_neighbor_add (&ip, &mac, sw_if_index, flags, NULL);
846       else
847         ip_neighbor_del (&ip, sw_if_index);
848
849       ip_address_increment (&ip);
850       mac_address_increment (&mac);
851
852       --count;
853     }
854
855   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 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 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 ip_neighbor_sort (void *a1, void *a2)
908 {
909   index_t *ipni1 = a1, *ipni2 = a2;
910   ip_neighbor_t *ipn1, *ipn2;
911   int cmp;
912
913   ipn1 = ip_neighbor_get (*ipni1);
914   ipn2 = ip_neighbor_get (*ipni2);
915
916   cmp = vnet_sw_interface_compare (vnet_get_main (),
917                                    ipn1->ipn_key->ipnk_sw_if_index,
918                                    ipn2->ipn_key->ipnk_sw_if_index);
919   if (!cmp)
920     cmp = ip_address_cmp (&ipn1->ipn_key->ipnk_ip, &ipn2->ipn_key->ipnk_ip);
921   return cmp;
922 }
923
924 static index_t *
925 ip_neighbor_entries (u32 sw_if_index, ip_address_family_t af)
926 {
927   index_t *ipnis = NULL;
928   ip_neighbor_t *ipn;
929
930   /* *INDENT-OFF* */
931   pool_foreach (ipn, ip_neighbor_pool)
932    {
933     if ((sw_if_index == ~0 ||
934         ipn->ipn_key->ipnk_sw_if_index == sw_if_index) &&
935         (N_AF == af ||
936          ip_neighbor_get_af(ipn) == af))
937        vec_add1 (ipnis, ip_neighbor_get_index(ipn));
938   }
939
940   /* *INDENT-ON* */
941
942   if (ipnis)
943     vec_sort_with_function (ipnis, ip_neighbor_sort);
944   return ipnis;
945 }
946
947 static clib_error_t *
948 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   head = pool_elt_at_index (ip_neighbor_elt_pool, ip_neighbor_list_head[af]);
955
956
957   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   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   return (NULL);
971 }
972
973 static clib_error_t *
974 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   index_t *ipni, *ipnis = NULL;
979   u32 sw_if_index;
980
981   /* Filter entries by interface if given. */
982   sw_if_index = ~0;
983   (void) unformat_user (input, unformat_vnet_sw_interface, vnet_get_main (),
984                         &sw_if_index);
985
986   ipnis = ip_neighbor_entries (sw_if_index, af);
987
988   if (ipnis)
989     vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP",
990                      "Flags", "Ethernet", "Interface");
991
992   vec_foreach (ipni, ipnis)
993   {
994     vlib_cli_output (vm, "%U", format_ip_neighbor, *ipni);
995   }
996   vec_free (ipnis);
997
998   return (NULL);
999 }
1000
1001 static clib_error_t *
1002 ip_neighbor_show (vlib_main_t * vm,
1003                   unformat_input_t * input, vlib_cli_command_t * cmd)
1004 {
1005   return (ip_neighbor_show_i (vm, input, cmd, N_AF));
1006 }
1007
1008 static clib_error_t *
1009 ip6_neighbor_show (vlib_main_t * vm,
1010                    unformat_input_t * input, vlib_cli_command_t * cmd)
1011 {
1012   return (ip_neighbor_show_i (vm, input, cmd, AF_IP6));
1013 }
1014
1015 static clib_error_t *
1016 ip4_neighbor_show (vlib_main_t * vm,
1017                    unformat_input_t * input, vlib_cli_command_t * cmd)
1018 {
1019   return (ip_neighbor_show_i (vm, input, cmd, AF_IP4));
1020 }
1021
1022 static clib_error_t *
1023 ip6_neighbor_show_sorted (vlib_main_t * vm,
1024                           unformat_input_t * input, vlib_cli_command_t * cmd)
1025 {
1026   return (ip_neighbor_show_sorted_i (vm, input, cmd, AF_IP6));
1027 }
1028
1029 static clib_error_t *
1030 ip4_neighbor_show_sorted (vlib_main_t * vm,
1031                           unformat_input_t * input, vlib_cli_command_t * cmd)
1032 {
1033   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 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 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 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 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 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 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 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 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 ip_neighbor_register (ip_address_family_t af, const ip_neighbor_vft_t * vft)
1097 {
1098   ip_nbr_vfts[af] = *vft;
1099 }
1100
1101 void
1102 ip_neighbor_probe_dst (u32 sw_if_index, u32 thread_index,
1103                        ip_address_family_t af, const ip46_address_t *dst)
1104 {
1105   if (!vnet_sw_interface_is_admin_up (vnet_get_main (), sw_if_index))
1106     return;
1107
1108   switch (af)
1109     {
1110     case AF_IP6:
1111       ip6_neighbor_probe_dst (sw_if_index, thread_index, &dst->ip6);
1112       break;
1113     case AF_IP4:
1114       ip4_neighbor_probe_dst (sw_if_index, thread_index, &dst->ip4);
1115       break;
1116     }
1117 }
1118
1119 void
1120 ip_neighbor_probe (const ip_adjacency_t * adj)
1121 {
1122   ip_neighbor_probe_dst (adj->rewrite_header.sw_if_index,
1123                          vlib_get_thread_index (),
1124                          ip_address_family_from_fib_proto (adj->ia_nh_proto),
1125                          &adj->sub_type.nbr.next_hop);
1126 }
1127
1128 void
1129 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   if (~0 == sw_if_index)
1136     {
1137       uword **hash;
1138
1139       vec_foreach (hash, ip_neighbor_db[af].ipndb_hash)
1140       {
1141           /* *INDENT-OFF* */
1142           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       if (vec_len (ip_neighbor_db[af].ipndb_hash) <= sw_if_index)
1155         return;
1156       hash = ip_neighbor_db[af].ipndb_hash[sw_if_index];
1157
1158       /* *INDENT-OFF* */
1159       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 ip4_neighbor_proxy_add (u32 fib_index,
1170                         const ip4_address_t * start,
1171                         const ip4_address_t * end)
1172 {
1173   if (ip_nbr_vfts[AF_IP4].inv_proxy4_add)
1174     {
1175       return (ip_nbr_vfts[AF_IP4].inv_proxy4_add (fib_index, start, end));
1176     }
1177
1178   return (-1);
1179 }
1180
1181 int
1182 ip4_neighbor_proxy_delete (u32 fib_index,
1183                            const ip4_address_t * start,
1184                            const ip4_address_t * end)
1185 {
1186   if (ip_nbr_vfts[AF_IP4].inv_proxy4_del)
1187     {
1188       return (ip_nbr_vfts[AF_IP4].inv_proxy4_del (fib_index, start, end));
1189     }
1190   return -1;
1191 }
1192
1193 int
1194 ip4_neighbor_proxy_enable (u32 sw_if_index)
1195 {
1196   if (ip_nbr_vfts[AF_IP4].inv_proxy4_enable)
1197     {
1198       return (ip_nbr_vfts[AF_IP4].inv_proxy4_enable (sw_if_index));
1199     }
1200   return -1;
1201 }
1202
1203 int
1204 ip4_neighbor_proxy_disable (u32 sw_if_index)
1205 {
1206   if (ip_nbr_vfts[AF_IP4].inv_proxy4_disable)
1207     {
1208       return (ip_nbr_vfts[AF_IP4].inv_proxy4_disable (sw_if_index));
1209     }
1210   return -1;
1211 }
1212
1213 int
1214 ip6_neighbor_proxy_add (u32 sw_if_index, const ip6_address_t * addr)
1215 {
1216   if (ip_nbr_vfts[AF_IP6].inv_proxy6_add)
1217     {
1218       return (ip_nbr_vfts[AF_IP6].inv_proxy6_add (sw_if_index, addr));
1219     }
1220   return -1;
1221 }
1222
1223 int
1224 ip6_neighbor_proxy_del (u32 sw_if_index, const ip6_address_t * addr)
1225 {
1226   if (ip_nbr_vfts[AF_IP6].inv_proxy6_del)
1227     {
1228       return (ip_nbr_vfts[AF_IP6].inv_proxy6_del (sw_if_index, addr));
1229     }
1230   return -1;
1231 }
1232
1233 void
1234 ip_neighbor_populate (ip_address_family_t af, u32 sw_if_index)
1235 {
1236   index_t *ipnis = NULL, *ipni;
1237   ip_neighbor_t *ipn;
1238
1239   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   pool_foreach (ipn, ip_neighbor_pool)
1245    {
1246     if (ip_neighbor_get_af(ipn) == af &&
1247         ipn->ipn_key->ipnk_sw_if_index == sw_if_index)
1248       vec_add1 (ipnis, ipn - ip_neighbor_pool);
1249   }
1250   /* *INDENT-ON* */
1251
1252   vec_foreach (ipni, ipnis)
1253   {
1254     ipn = ip_neighbor_get (*ipni);
1255
1256     adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
1257                      ip_address_family_to_fib_proto (ip_neighbor_get_af
1258                                                      (ipn)),
1259                      &ip_addr_46 (&ipn->ipn_key->ipnk_ip),
1260                      ip_neighbor_mk_complete_walk, ipn);
1261   }
1262   vec_free (ipnis);
1263 }
1264
1265 void
1266 ip_neighbor_flush (ip_address_family_t af, u32 sw_if_index)
1267 {
1268   index_t *ipnis = NULL, *ipni;
1269   ip_neighbor_t *ipn;
1270
1271
1272   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   pool_foreach (ipn, ip_neighbor_pool)
1278    {
1279     if (ip_neighbor_get_af(ipn) == af &&
1280         ipn->ipn_key->ipnk_sw_if_index == sw_if_index &&
1281         ip_neighbor_is_dynamic (ipn))
1282       vec_add1 (ipnis, ipn - ip_neighbor_pool);
1283   }
1284   /* *INDENT-ON* */
1285
1286   vec_foreach (ipni, ipnis) ip_neighbor_destroy (ip_neighbor_get (*ipni));
1287   vec_free (ipnis);
1288 }
1289
1290 walk_rc_t
1291 ip_neighbor_mark_one (index_t ipni, void *ctx)
1292 {
1293   ip_neighbor_t *ipn;
1294
1295   ipn = ip_neighbor_get (ipni);
1296
1297   ipn->ipn_flags |= IP_NEIGHBOR_FLAG_STALE;
1298
1299   return (WALK_CONTINUE);
1300 }
1301
1302 void
1303 ip_neighbor_mark (ip_address_family_t af)
1304 {
1305   ip_neighbor_walk (af, ~0, ip_neighbor_mark_one, NULL);
1306 }
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 ip_neighbor_sweep_one (index_t ipni, void *arg)
1315 {
1316   ip_neighbor_sweep_ctx_t *ctx = arg;
1317   ip_neighbor_t *ipn;
1318
1319   ipn = ip_neighbor_get (ipni);
1320
1321   if (ipn->ipn_flags & IP_NEIGHBOR_FLAG_STALE)
1322     {
1323       vec_add1 (ctx->ipnsc_stale, ipni);
1324     }
1325
1326   return (WALK_CONTINUE);
1327 }
1328
1329 void
1330 ip_neighbor_sweep (ip_address_family_t af)
1331 {
1332   ip_neighbor_sweep_ctx_t ctx = { };
1333   index_t *ipni;
1334
1335   ip_neighbor_walk (af, ~0, ip_neighbor_sweep_one, &ctx);
1336
1337   vec_foreach (ipni, ctx.ipnsc_stale)
1338   {
1339     ip_neighbor_destroy (ip_neighbor_get (*ipni));
1340   }
1341   vec_free (ctx.ipnsc_stale);
1342 }
1343
1344 /*
1345  * Remove any arp entries associated with the specified interface
1346  */
1347 static clib_error_t *
1348 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   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   if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
1359     {
1360       FOR_EACH_IP_ADDRESS_FAMILY (af) ip_neighbor_populate (af, sw_if_index);
1361     }
1362   else
1363     {
1364       /* admin down, flush all neighbours */
1365       FOR_EACH_IP_ADDRESS_FAMILY (af) ip_neighbor_flush (af, sw_if_index);
1366     }
1367
1368   return (NULL);
1369 }
1370
1371 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 ip_neighbor_add_del_sw_interface (vnet_main_t *vnm, u32 sw_if_index,
1378                                   u32 is_add)
1379 {
1380   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   if (!is_add && sw_if_index != ~0)
1385     {
1386       ip_address_family_t af;
1387
1388       FOR_EACH_IP_ADDRESS_FAMILY (af) ip_neighbor_flush (af, sw_if_index);
1389     }
1390
1391   if (is_add)
1392     {
1393       ip_neighbor_alloc_ctr (&ip_neighbor_counters[AF_IP4], sw_if_index);
1394       ip_neighbor_alloc_ctr (&ip_neighbor_counters[AF_IP6], sw_if_index);
1395     }
1396
1397   return (NULL);
1398 }
1399
1400 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 ip_neighbor_walk_covered (index_t ipni, void *arg)
1411 {
1412   ip_neighbor_walk_covered_ctx_t *ctx = arg;
1413   ip_neighbor_t *ipn;
1414
1415   ipn = ip_neighbor_get (ipni);
1416
1417   if (AF_IP4 == ip_addr_version (&ctx->addr))
1418     {
1419       if (ip4_destination_matches_route (&ip4_main,
1420                                          &ip_addr_v4 (&ipn->ipn_key->ipnk_ip),
1421                                          &ip_addr_v4 (&ctx->addr),
1422                                          ctx->length) &&
1423           ip_neighbor_is_dynamic (ipn))
1424         {
1425           vec_add1 (ctx->ipnis, ip_neighbor_get_index (ipn));
1426         }
1427     }
1428   else if (AF_IP6 == ip_addr_version (&ctx->addr))
1429     {
1430       if (ip6_destination_matches_route (&ip6_main,
1431                                          &ip_addr_v6 (&ipn->ipn_key->ipnk_ip),
1432                                          &ip_addr_v6 (&ctx->addr),
1433                                          ctx->length) &&
1434           ip_neighbor_is_dynamic (ipn))
1435         {
1436           vec_add1 (ctx->ipnis, ip_neighbor_get_index (ipn));
1437         }
1438     }
1439   return (WALK_CONTINUE);
1440 }
1441
1442
1443 /*
1444  * callback when an interface address is added or deleted
1445  */
1446 static void
1447 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   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   if (is_del)
1463     {
1464       /* *INDENT-OFF* */
1465       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       ip_neighbor_walk (AF_IP4, sw_if_index, ip_neighbor_walk_covered, &ctx);
1476
1477       vec_foreach (ipni, ctx.ipnis)
1478         ip_neighbor_destroy (ip_neighbor_get (*ipni));
1479
1480       vec_free (ctx.ipnis);
1481     }
1482 }
1483
1484 /*
1485  * callback when an interface address is added or deleted
1486  */
1487 static void
1488 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   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   if (is_del)
1505     {
1506       /* *INDENT-OFF* */
1507       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       ip_neighbor_walk (AF_IP6, sw_if_index, ip_neighbor_walk_covered, &ctx);
1518
1519       vec_foreach (ipni, ctx.ipnis)
1520         ip_neighbor_destroy (ip_neighbor_get (*ipni));
1521
1522       vec_free (ctx.ipnis);
1523     }
1524 }
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 ip_neighbor_walk_table_bind (index_t ipni, void *arg)
1534 {
1535   ip_neighbor_table_bind_ctx_t *ctx = arg;
1536   ip_neighbor_t *ipn;
1537
1538   ipn = ip_neighbor_get (ipni);
1539   ip_neighbor_adj_fib_remove (ipn, ctx->old_fib_index);
1540   ip_neighbor_adj_fib_add (ipn, ctx->new_fib_index);
1541
1542   return (WALK_CONTINUE);
1543 }
1544
1545 static void
1546 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   ip_neighbor_table_bind_ctx_t ctx = {
1552     .old_fib_index = old_fib_index,
1553     .new_fib_index = new_fib_index,
1554   };
1555
1556   ip_neighbor_walk (AF_IP4, sw_if_index, ip_neighbor_walk_table_bind, &ctx);
1557 }
1558
1559 static void
1560 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   ip_neighbor_table_bind_ctx_t ctx = {
1566     .old_fib_index = old_fib_index,
1567     .new_fib_index = new_fib_index,
1568   };
1569
1570   ip_neighbor_walk (AF_IP6, sw_if_index, ip_neighbor_walk_table_bind, &ctx);
1571 }
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 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   ipn = ip_neighbor_get (ipni);
1591   af = ip_neighbor_get_af (ipn);
1592   ipndb_age = ip_neighbor_db[af].ipndb_age;
1593   ttl = now - ipn->ipn_time_last_updated;
1594   *wait = ipndb_age;
1595
1596   if (ttl > ipndb_age)
1597     {
1598       IP_NEIGHBOR_DBG ("aged: %U @%f - %f > %d",
1599                        format_ip_neighbor, ipni, now,
1600                        ipn->ipn_time_last_updated, ipndb_age);
1601       if (ipn->ipn_n_probes > 2)
1602         {
1603           /* 3 strikes and yea-re out */
1604           IP_NEIGHBOR_DBG ("dead: %U", format_ip_neighbor, ipni);
1605           *wait = 1;
1606           return (IP_NEIGHBOR_AGE_DEAD);
1607         }
1608       else
1609         {
1610           ip_neighbor_probe_dst (ip_neighbor_get_sw_if_index (ipn),
1611                                  vlib_get_thread_index (), af,
1612                                  &ip_addr_46 (&ipn->ipn_key->ipnk_ip));
1613
1614           ipn->ipn_n_probes++;
1615           *wait = 1;
1616         }
1617     }
1618   else
1619     {
1620       /* here we are sure that ttl <= ipndb_age */
1621       *wait = ipndb_age - ttl + 1;
1622       return (IP_NEIGHBOR_AGE_ALIVE);
1623     }
1624
1625   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 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   uword event_type, *event_data = NULL;
1639   f64 timeout;
1640
1641   /* Set the timeout to an effectively infinite value when the process starts */
1642   timeout = IP_NEIGHBOR_PROCESS_SLEEP_LONG;
1643
1644   while (1)
1645     {
1646       f64 now;
1647
1648       if (!timeout)
1649         vlib_process_wait_for_event (vm);
1650       else
1651         vlib_process_wait_for_event_or_clock (vm, timeout);
1652
1653       event_type = vlib_process_get_events (vm, &event_data);
1654       vec_reset_length (event_data);
1655
1656       now = vlib_time_now (vm);
1657
1658       switch (event_type)
1659         {
1660         case ~0:
1661           {
1662             /* timer expired */
1663             ip_neighbor_elt_t *elt, *head;
1664             f64 wait;
1665
1666             timeout = ip_neighbor_db[af].ipndb_age;
1667             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           restart:
1674           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             break;
1697           }
1698         case IP_NEIGHBOR_AGE_PROCESS_WAKEUP:
1699           {
1700
1701             if (!ip_neighbor_db[af].ipndb_age)
1702               {
1703                 /* aging has been disabled */
1704                 timeout = 0;
1705                 break;
1706               }
1707             ip_neighbor_elt_t *elt, *head;
1708
1709             head = pool_elt_at_index (ip_neighbor_elt_pool,
1710                                       ip_neighbor_list_head[af]);
1711             /* no neighbors yet */
1712             if (clib_llist_is_empty (ip_neighbor_elt_pool, ipne_anchor, head))
1713               {
1714                 timeout = ip_neighbor_db[af].ipndb_age;
1715                 break;
1716               }
1717
1718             /* poke the oldset neighbour for aging, which returns how long we sleep for */
1719             elt = clib_llist_prev (ip_neighbor_elt_pool, ipne_anchor, head);
1720             ip_neighbour_age_out (elt->ipne_index, now, &timeout);
1721             break;
1722           }
1723         }
1724     }
1725   return 0;
1726 }
1727
1728 static uword
1729 ip4_neighbor_age_process (vlib_main_t * vm,
1730                           vlib_node_runtime_t * rt, vlib_frame_t * f)
1731 {
1732   return (ip_neighbor_age_loop (vm, rt, f, AF_IP4));
1733 }
1734
1735 static uword
1736 ip6_neighbor_age_process (vlib_main_t * vm,
1737                           vlib_node_runtime_t * rt, vlib_frame_t * f)
1738 {
1739   return (ip_neighbor_age_loop (vm, rt, f, AF_IP6));
1740 }
1741
1742 /* *INDENT-OFF* */
1743 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 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 ip_neighbor_config (ip_address_family_t af, u32 limit, u32 age, bool recycle)
1757 {
1758   ip_neighbor_db[af].ipndb_limit = limit;
1759   ip_neighbor_db[af].ipndb_recycle = recycle;
1760   ip_neighbor_db[af].ipndb_age = age;
1761
1762   vlib_process_signal_event (vlib_get_main (),
1763                              (AF_IP4 == af ?
1764                               ip4_neighbor_age_process_node.index :
1765                               ip6_neighbor_age_process_node.index),
1766                              IP_NEIGHBOR_AGE_PROCESS_WAKEUP, 0);
1767
1768   return (0);
1769 }
1770
1771 int
1772 ip_neighbor_get_config (ip_address_family_t af, u32 *limit, u32 *age,
1773                         bool *recycle)
1774 {
1775   *limit = ip_neighbor_db[af].ipndb_limit;
1776   *age = ip_neighbor_db[af].ipndb_age;
1777   *recycle = ip_neighbor_db[af].ipndb_recycle;
1778
1779   return (0);
1780 }
1781
1782 static clib_error_t *
1783 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   FOR_EACH_IP_ADDRESS_FAMILY(af) {
1790     vlib_cli_output (vm, "%U:", format_ip_address_family, af);
1791     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                      ip_neighbor_db[af].ipndb_recycle);
1795   }
1796
1797   /* *INDENT-ON* */
1798   return (NULL);
1799 }
1800
1801 static clib_error_t *
1802 ip_neighbor_config_set (vlib_main_t *vm, unformat_input_t *input,
1803                         vlib_cli_command_t *cmd)
1804 {
1805   unformat_input_t _line_input, *line_input = &_line_input;
1806   clib_error_t *error = NULL;
1807   ip_address_family_t af;
1808   u32 limit, age;
1809   bool recycle;
1810
1811   if (!unformat_user (input, unformat_line_input, line_input))
1812     return 0;
1813
1814   if (!unformat (line_input, "%U", unformat_ip_address_family, &af))
1815     {
1816       error = unformat_parse_error (line_input);
1817       goto done;
1818     }
1819
1820   limit = ip_neighbor_db[af].ipndb_limit;
1821   age = ip_neighbor_db[af].ipndb_age;
1822   recycle = ip_neighbor_db[af].ipndb_recycle;
1823
1824   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1825     {
1826       if (unformat (line_input, "limit %u", &limit))
1827         ;
1828       else if (unformat (line_input, "age %u", &age))
1829         ;
1830       else if (unformat (line_input, "recycle"))
1831         recycle = true;
1832       else if (unformat (line_input, "norecycle"))
1833         recycle = false;
1834       else
1835         {
1836           error = unformat_parse_error (line_input);
1837           goto done;
1838         }
1839     }
1840
1841   ip_neighbor_config (af, limit, age, recycle);
1842
1843 done:
1844   unformat_free (line_input);
1845   return error;
1846 }
1847
1848 static void
1849 ip_neighbor_stats_show_one (vlib_main_t *vm, vnet_main_t *vnm, u32 sw_if_index)
1850 {
1851   vlib_cli_output (vm, "  %U", format_vnet_sw_if_index_name, vnm, sw_if_index);
1852   vlib_cli_output (vm, "    arp:%U", format_ip_neighbor_counters,
1853                    &ip_neighbor_counters[AF_IP4], sw_if_index);
1854   vlib_cli_output (vm, "    nd: %U", format_ip_neighbor_counters,
1855                    &ip_neighbor_counters[AF_IP6], sw_if_index);
1856 }
1857
1858 static walk_rc_t
1859 ip_neighbor_stats_show_cb (vnet_main_t *vnm, vnet_sw_interface_t *si,
1860                            void *ctx)
1861 {
1862   ip_neighbor_stats_show_one (ctx, vnm, si->sw_if_index);
1863
1864   return (WALK_CONTINUE);
1865 }
1866
1867 static clib_error_t *
1868 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   vnm = vnet_get_main ();
1875   sw_if_index = ~0;
1876   (void) unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index);
1877
1878   if (~0 == sw_if_index)
1879     {
1880       vnet_sw_interface_walk (vnm, ip_neighbor_stats_show_cb, vm);
1881     }
1882   else
1883     {
1884       ip_neighbor_stats_show_one (vm, vnm, sw_if_index);
1885     }
1886   return (NULL);
1887 }
1888
1889 /* *INDENT-OFF* */
1890 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 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 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 ip_neighbor_init (vlib_main_t * vm)
1910 {
1911   {
1912     ip4_add_del_interface_address_callback_t cb = {
1913       .function = ip_neighbor_add_del_interface_address_v4,
1914     };
1915     vec_add1 (ip4_main.add_del_interface_address_callbacks, cb);
1916   }
1917   {
1918     ip6_add_del_interface_address_callback_t cb = {
1919       .function = ip_neighbor_add_del_interface_address_v6,
1920     };
1921     vec_add1 (ip6_main.add_del_interface_address_callbacks, cb);
1922   }
1923   {
1924     ip4_table_bind_callback_t cb = {
1925       .function = ip_neighbor_table_bind_v4,
1926     };
1927     vec_add1 (ip4_main.table_bind_callbacks, cb);
1928   }
1929   {
1930     ip6_table_bind_callback_t cb = {
1931       .function = ip_neighbor_table_bind_v6,
1932     };
1933     vec_add1 (ip6_main.table_bind_callbacks, cb);
1934   }
1935   ipn_logger = vlib_log_register_class ("ip", "neighbor");
1936
1937   ip_address_family_t af;
1938
1939   FOR_EACH_IP_ADDRESS_FAMILY (af)
1940     ip_neighbor_list_head[af] =
1941     clib_llist_make_head (ip_neighbor_elt_pool, ipne_anchor);
1942
1943   return (NULL);
1944 }
1945
1946 /* *INDENT-OFF* */
1947 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  */