LCOV - code coverage report
Current view: top level - vnet/ip-neighbor - ip_neighbor.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 567 640 88.6 %
Date: 2023-10-26 01:39:38 Functions: 102 111 91.9 %

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

Generated by: LCOV version 1.14