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