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