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