LCOV - code coverage report
Current view: top level - vnet/ip-neighbor - ip_neighbor.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 563 634 88.8 %
Date: 2023-07-05 22:20:52 Functions: 101 111 91.0 %

          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      327293 : ip_neighbor_get (index_t ipni)
     158             : {
     159      327293 :   if (pool_is_free_index (ip_neighbor_pool, ipni))
     160           0 :     return (NULL);
     161             : 
     162      327293 :   return (pool_elt_at_index (ip_neighbor_pool, ipni));
     163             : }
     164             : 
     165             : static index_t
     166       63676 : ip_neighbor_get_index (const ip_neighbor_t * ipn)
     167             : {
     168       63676 :   return (ipn - ip_neighbor_pool);
     169             : }
     170             : 
     171             : static void
     172        9741 : ip_neighbor_touch (ip_neighbor_t * ipn)
     173             : {
     174        9741 :   ipn->ipn_flags &= ~IP_NEIGHBOR_FLAG_STALE;
     175        9741 : }
     176             : 
     177             : static bool
     178       14155 : ip_neighbor_is_dynamic (const ip_neighbor_t * ipn)
     179             : {
     180       14155 :   return (ipn->ipn_flags & IP_NEIGHBOR_FLAG_DYNAMIC);
     181             : }
     182             : 
     183             : const ip_address_t *
     184           0 : ip_neighbor_get_ip (const ip_neighbor_t * ipn)
     185             : {
     186           0 :   return (&ipn->ipn_key->ipnk_ip);
     187             : }
     188             : 
     189             : ip_address_family_t
     190      125648 : ip_neighbor_get_af (const ip_neighbor_t * ipn)
     191             : {
     192      125648 :   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        5285 : 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        5285 :   if (~0 != ipn->ipn_elt)
     215             :     {
     216        5210 :       elt = pool_elt_at_index (ip_neighbor_elt_pool, ipn->ipn_elt);
     217             : 
     218        5210 :       clib_llist_remove (ip_neighbor_elt_pool, ipne_anchor, elt);
     219             : 
     220        5210 :       ipn->ipn_elt = ~0;
     221             :     }
     222        5285 : }
     223             : 
     224             : static void
     225        9498 : 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        9498 :   ip_neighbor_touch (ipn);
     232        9498 :   ipn->ipn_time_last_updated = vlib_time_now (vlib_get_main ());
     233        9498 :   ipn->ipn_n_probes = 0;
     234             : 
     235        9498 :   if (ip_neighbor_is_dynamic (ipn))
     236             :     {
     237        9421 :       if (~0 == ipn->ipn_elt)
     238             :         /* first time insertion */
     239        9180 :         pool_get_zero (ip_neighbor_elt_pool, elt);
     240             :       else
     241             :         {
     242             :           /* already inserted - extract first */
     243         241 :           elt = pool_elt_at_index (ip_neighbor_elt_pool, ipn->ipn_elt);
     244             : 
     245         241 :           clib_llist_remove (ip_neighbor_elt_pool, ipne_anchor, elt);
     246             :         }
     247        9421 :       head = pool_elt_at_index (ip_neighbor_elt_pool,
     248             :                                 ip_neighbor_list_head[ip_neighbor_get_af
     249             :                                                       (ipn)]);
     250             : 
     251        9421 :       elt->ipne_index = ip_neighbor_get_index (ipn);
     252        9421 :       clib_llist_add (ip_neighbor_elt_pool, ipne_anchor, elt, head);
     253        9421 :       ipn->ipn_elt = elt - ip_neighbor_elt_pool;
     254             :     }
     255        9498 : }
     256             : 
     257             : static void
     258        9255 : ip_neighbor_db_add (const ip_neighbor_t * ipn)
     259             : {
     260             :   ip_address_family_t af;
     261             :   u32 sw_if_index;
     262             : 
     263        9255 :   af = ip_neighbor_get_af (ipn);
     264        9255 :   sw_if_index = ipn->ipn_key->ipnk_sw_if_index;
     265             : 
     266        9255 :   vec_validate (ip_neighbor_db[af].ipndb_hash, sw_if_index);
     267             : 
     268        9255 :   if (!ip_neighbor_db[af].ipndb_hash[sw_if_index])
     269        2122 :     ip_neighbor_db[af].ipndb_hash[sw_if_index]
     270        2122 :       = hash_create_mem (0, sizeof (ip_neighbor_key_t), sizeof (index_t));
     271             : 
     272       18510 :   hash_set_mem (ip_neighbor_db[af].ipndb_hash[sw_if_index],
     273             :                 ipn->ipn_key, ip_neighbor_get_index (ipn));
     274             : 
     275        9255 :   ip_neighbor_db[af].ipndb_n_elts++;
     276        9255 : }
     277             : 
     278             : static void
     279        5283 : ip_neighbor_db_remove (const ip_neighbor_t * ipn)
     280             : {
     281             :   ip_address_family_t af;
     282             :   u32 sw_if_index;
     283             : 
     284        5283 :   af = ip_neighbor_get_af (ipn);
     285        5283 :   sw_if_index = ipn->ipn_key->ipnk_sw_if_index;
     286             : 
     287        5283 :   vec_validate (ip_neighbor_db[af].ipndb_hash, sw_if_index);
     288             : 
     289       10566 :   hash_unset_mem (ip_neighbor_db[af].ipndb_hash[sw_if_index], ipn->ipn_key);
     290             : 
     291        5283 :   ip_neighbor_db[af].ipndb_n_elts--;
     292        5283 : }
     293             : 
     294             : static ip_neighbor_t *
     295       19374 : ip_neighbor_db_find (const ip_neighbor_key_t * key)
     296             : {
     297             :   ip_address_family_t af;
     298             :   uword *p;
     299             : 
     300       19374 :   af = ip_addr_version (&key->ipnk_ip);
     301             : 
     302       19374 :   if (key->ipnk_sw_if_index >= vec_len (ip_neighbor_db[af].ipndb_hash))
     303        2222 :     return NULL;
     304             : 
     305       17152 :   p = hash_get_mem (ip_neighbor_db[af].ipndb_hash
     306             :                     [key->ipnk_sw_if_index], key);
     307             : 
     308       17152 :   if (p)
     309        9879 :     return ip_neighbor_get (p[0]);
     310             : 
     311        7273 :   return (NULL);
     312             : }
     313             : 
     314             : static u8
     315       14534 : ip_af_type_pfx_len (ip_address_family_t type)
     316             : {
     317       14534 :   return (type == AF_IP4 ? 32 : 128);
     318             : }
     319             : 
     320             : static void
     321        9256 : ip_neighbor_adj_fib_add (ip_neighbor_t * ipn, u32 fib_index)
     322             : {
     323             :   ip_address_family_t af;
     324             : 
     325        9256 :   af = ip_neighbor_get_af (ipn);
     326             : 
     327       13441 :   if (af == AF_IP6 &&
     328        4185 :       ip6_address_is_link_local_unicast (&ip_addr_v6
     329             :                                          (&ipn->ipn_key->ipnk_ip)))
     330           3 :     {
     331           3 :       ip6_ll_prefix_t pfx = {
     332           3 :         .ilp_addr = ip_addr_v6 (&ipn->ipn_key->ipnk_ip),
     333           3 :         .ilp_sw_if_index = ipn->ipn_key->ipnk_sw_if_index,
     334             :       };
     335           3 :       ipn->ipn_fib_entry_index =
     336           3 :         ip6_ll_table_entry_update (&pfx, FIB_ROUTE_PATH_FLAG_NONE);
     337             :     }
     338             :   else
     339             :     {
     340             :       fib_protocol_t fproto;
     341             : 
     342        9253 :       fproto = ip_address_family_to_fib_proto (af);
     343             : 
     344       18506 :       fib_prefix_t pfx = {
     345        9253 :         .fp_len = ip_af_type_pfx_len (af),
     346             :         .fp_proto = fproto,
     347        9253 :         .fp_addr = ip_addr_46 (&ipn->ipn_key->ipnk_ip),
     348             :       };
     349             : 
     350        9253 :       ipn->ipn_fib_entry_index =
     351        9253 :         fib_table_entry_path_add (fib_index, &pfx, FIB_SOURCE_ADJ,
     352             :                                   FIB_ENTRY_FLAG_ATTACHED,
     353        9253 :                                   fib_proto_to_dpo (fproto),
     354             :                                   &pfx.fp_addr,
     355        9253 :                                   ipn->ipn_key->ipnk_sw_if_index,
     356             :                                   ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
     357             : 
     358        9253 :       vec_validate (ip_neighbor_db[af].ipndb_n_elts_per_fib, fib_index);
     359             : 
     360        9253 :       ip_neighbor_db[af].ipndb_n_elts_per_fib[fib_index]++;
     361             : 
     362        9253 :       if (1 == ip_neighbor_db[af].ipndb_n_elts_per_fib[fib_index])
     363        1564 :         fib_table_lock (fib_index, fproto, FIB_SOURCE_ADJ);
     364             :     }
     365        9256 : }
     366             : 
     367             : static void
     368        5287 : ip_neighbor_adj_fib_remove (ip_neighbor_t * ipn, u32 fib_index)
     369             : {
     370             :   ip_address_family_t af;
     371             : 
     372        5287 :   af = ip_neighbor_get_af (ipn);
     373             : 
     374        5287 :   if (FIB_NODE_INDEX_INVALID != ipn->ipn_fib_entry_index)
     375             :     {
     376        7697 :       if (AF_IP6 == af &&
     377        2413 :           ip6_address_is_link_local_unicast (&ip_addr_v6
     378             :                                              (&ipn->ipn_key->ipnk_ip)))
     379           3 :         {
     380           3 :           ip6_ll_prefix_t pfx = {
     381           3 :             .ilp_addr = ip_addr_v6 (&ipn->ipn_key->ipnk_ip),
     382           3 :             .ilp_sw_if_index = ipn->ipn_key->ipnk_sw_if_index,
     383             :           };
     384           3 :           ip6_ll_table_entry_delete (&pfx);
     385             :         }
     386             :       else
     387             :         {
     388             :           fib_protocol_t fproto;
     389             : 
     390        5281 :           fproto = ip_address_family_to_fib_proto (af);
     391             : 
     392       10562 :           fib_prefix_t pfx = {
     393        5281 :             .fp_len = ip_af_type_pfx_len (af),
     394             :             .fp_proto = fproto,
     395        5281 :             .fp_addr = ip_addr_46 (&ipn->ipn_key->ipnk_ip),
     396             :           };
     397             : 
     398        5281 :           fib_table_entry_path_remove (fib_index,
     399             :                                        &pfx,
     400             :                                        FIB_SOURCE_ADJ,
     401        5281 :                                        fib_proto_to_dpo (fproto),
     402             :                                        &pfx.fp_addr,
     403        5281 :                                        ipn->ipn_key->ipnk_sw_if_index,
     404             :                                        ~0, 1, FIB_ROUTE_PATH_FLAG_NONE);
     405             : 
     406        5281 :           ip_neighbor_db[af].ipndb_n_elts_per_fib[fib_index]--;
     407             : 
     408        5281 :           if (0 == ip_neighbor_db[af].ipndb_n_elts_per_fib[fib_index])
     409        1414 :             fib_table_unlock (fib_index, fproto, FIB_SOURCE_ADJ);
     410             :         }
     411             :     }
     412        5287 : }
     413             : 
     414             : static void
     415       19256 : ip_neighbor_mk_complete (adj_index_t ai, ip_neighbor_t * ipn)
     416             : {
     417       19256 :   adj_nbr_update_rewrite (ai, ADJ_NBR_REWRITE_FLAG_COMPLETE,
     418             :                           ethernet_build_rewrite (vnet_get_main (),
     419       19256 :                                                   ipn->
     420             :                                                   ipn_key->ipnk_sw_if_index,
     421       19256 :                                                   adj_get_link_type (ai),
     422       19256 :                                                   ipn->ipn_mac.bytes));
     423       19256 : }
     424             : 
     425             : static void
     426        5305 : ip_neighbor_mk_incomplete (adj_index_t ai)
     427             : {
     428        5305 :   ip_adjacency_t *adj = adj_get (ai);
     429             : 
     430        5305 :   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        5305 : }
     438             : 
     439             : static adj_walk_rc_t
     440       19005 : ip_neighbor_mk_complete_walk (adj_index_t ai, void *ctx)
     441             : {
     442       19005 :   ip_neighbor_t *ipn = ctx;
     443             : 
     444       19005 :   ip_neighbor_mk_complete (ai, ipn);
     445             : 
     446       19005 :   return (ADJ_WALK_RC_CONTINUE);
     447             : }
     448             : 
     449             : static adj_walk_rc_t
     450        5305 : ip_neighbor_mk_incomplete_walk (adj_index_t ai, void *ctx)
     451             : {
     452        5305 :   ip_neighbor_mk_incomplete (ai);
     453             : 
     454        5305 :   return (ADJ_WALK_RC_CONTINUE);
     455             : }
     456             : 
     457             : static void
     458        5283 : ip_neighbor_destroy (ip_neighbor_t * ipn)
     459             : {
     460             :   ip_address_family_t af;
     461             : 
     462        5283 :   af = ip_neighbor_get_af (ipn);
     463             : 
     464        5283 :   IP_NEIGHBOR_DBG ("free: %U", format_ip_neighbor,
     465             :                    ip_neighbor_get_index (ipn));
     466             : 
     467        5283 :   ip_neighbor_publish (ip_neighbor_get_index (ipn),
     468             :                        IP_NEIGHBOR_EVENT_REMOVED);
     469             : 
     470        5283 :   adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
     471        5283 :                    ip_address_family_to_fib_proto (af),
     472        5283 :                    &ip_addr_46 (&ipn->ipn_key->ipnk_ip),
     473             :                    ip_neighbor_mk_incomplete_walk, ipn);
     474        5283 :   ip_neighbor_adj_fib_remove
     475             :     (ipn,
     476             :      fib_table_get_index_for_sw_if_index
     477        5283 :      (ip_address_family_to_fib_proto (af), ipn->ipn_key->ipnk_sw_if_index));
     478             : 
     479        5283 :   ip_neighbor_list_remove (ipn);
     480        5283 :   ip_neighbor_db_remove (ipn);
     481        5283 :   clib_mem_free (ipn->ipn_key);
     482             : 
     483        5283 :   pool_put (ip_neighbor_pool, ipn);
     484        5283 : }
     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        9256 : 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        9256 :   af = ip_addr_version (&key->ipnk_ip);
     514             : 
     515        9256 :   if (ip_neighbor_db[af].ipndb_limit &&
     516        9256 :       (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        9255 :   pool_get_zero (ip_neighbor_pool, ipn);
     523             : 
     524        9255 :   ipn->ipn_key = clib_mem_alloc (sizeof (*ipn->ipn_key));
     525        9255 :   clib_memcpy (ipn->ipn_key, key, sizeof (*ipn->ipn_key));
     526             : 
     527        9255 :   ipn->ipn_fib_entry_index = FIB_NODE_INDEX_INVALID;
     528        9255 :   ipn->ipn_flags = flags;
     529        9255 :   ipn->ipn_elt = ~0;
     530             : 
     531        9255 :   mac_address_copy (&ipn->ipn_mac, mac);
     532             : 
     533        9255 :   ip_neighbor_db_add (ipn);
     534             : 
     535             :   /* create the adj-fib. the entry in the FIB table for the peer's interface */
     536        9255 :   if (!(ipn->ipn_flags & IP_NEIGHBOR_FLAG_NO_FIB_ENTRY))
     537        9252 :     ip_neighbor_adj_fib_add
     538             :       (ipn, fib_table_get_index_for_sw_if_index
     539        9252 :        (ip_address_family_to_fib_proto (af), ipn->ipn_key->ipnk_sw_if_index));
     540             : 
     541        9255 :   return (ipn);
     542             : }
     543             : 
     544             : int
     545        9499 : 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        9499 :   ASSERT (0 == vlib_get_thread_index ());
     555             : 
     556        9499 :   fproto = ip_address_family_to_fib_proto (ip_addr_version (ip));
     557             : 
     558        9499 :   const ip_neighbor_key_t key = {
     559             :     .ipnk_ip = *ip,
     560             :     .ipnk_sw_if_index = sw_if_index,
     561             :   };
     562             : 
     563        9499 :   ipn = ip_neighbor_db_find (&key);
     564             : 
     565        9499 :   if (ipn)
     566             :     {
     567         243 :       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         243 :       ip_neighbor_touch (ipn);
     574             : 
     575             :       /* Refuse to over-write static neighbor entry. */
     576         243 :       if (!(flags & IP_NEIGHBOR_FLAG_STATIC) &&
     577         241 :           (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         243 :       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         243 :       if (0 == mac_address_cmp (&ipn->ipn_mac, mac))
     601             :         {
     602         240 :           ip_neighbor_refresh (ipn);
     603         240 :           goto check_customers;
     604             :         }
     605             : 
     606           3 :       mac_address_copy (&ipn->ipn_mac, mac);
     607             :     }
     608             :   else
     609             :     {
     610        9256 :       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        9256 :       ipn = ip_neighbor_alloc (&key, mac, flags);
     617             : 
     618        9256 :       if (NULL == ipn)
     619           1 :         return VNET_API_ERROR_LIMIT_EXCEEDED;
     620             :     }
     621             : 
     622             :   /* Update time stamp and flags. */
     623        9258 :   ip_neighbor_refresh (ipn);
     624             : 
     625        9258 :   adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
     626        9258 :                    fproto, &ip_addr_46 (&ipn->ipn_key->ipnk_ip),
     627             :                    ip_neighbor_mk_complete_walk, ipn);
     628             : 
     629        9498 : check_customers:
     630             :   /* Customer(s) requesting event for this address? */
     631        9498 :   ip_neighbor_publish (ip_neighbor_get_index (ipn), IP_NEIGHBOR_EVENT_ADDED);
     632             : 
     633        9498 :   if (stats_index)
     634        5823 :     *stats_index = adj_nbr_find (fproto,
     635        5823 :                                  fib_proto_to_link (fproto),
     636        5823 :                                  &ip_addr_46 (&ipn->ipn_key->ipnk_ip),
     637        5823 :                                  ipn->ipn_key->ipnk_sw_if_index);
     638        9498 :   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        9772 : ip_neighbor_update (vnet_main_t * vnm, adj_index_t ai)
     705             : {
     706             :   ip_neighbor_t *ipn;
     707             :   ip_adjacency_t *adj;
     708             : 
     709        9772 :   adj = adj_get (ai);
     710             : 
     711        9772 :   ip_neighbor_key_t key = {
     712        9772 :     .ipnk_sw_if_index = adj->rewrite_header.sw_if_index,
     713             :   };
     714             : 
     715        9772 :   ip_address_from_46 (&adj->sub_type.nbr.next_hop,
     716        9772 :                       adj->ia_nh_proto, &key.ipnk_ip);
     717             : 
     718        9772 :   ipn = ip_neighbor_db_find (&key);
     719             : 
     720        9772 :   switch (adj->lookup_next_index)
     721             :     {
     722        9521 :     case IP_LOOKUP_NEXT_ARP:
     723        9521 :       if (NULL != ipn)
     724             :         {
     725        9282 :           adj_nbr_walk_nh (adj->rewrite_header.sw_if_index,
     726        9282 :                            adj->ia_nh_proto,
     727        9282 :                            &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        9521 :       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        9772 : }
     783             : 
     784             : void
     785        3671 : ip_neighbor_learn (const ip_neighbor_learn_t * l)
     786             : {
     787        3671 :   ip_neighbor_add (&l->ip, &l->mac, l->sw_if_index,
     788             :                    IP_NEIGHBOR_FLAG_DYNAMIC, NULL);
     789        3671 : }
     790             : 
     791             : static clib_error_t *
     792           4 : ip_neighbor_cmd (vlib_main_t * vm,
     793             :                  unformat_input_t * input, vlib_cli_command_t * cmd)
     794             : {
     795           4 :   ip_address_t ip = IP_ADDRESS_V6_ALL_0S;
     796           4 :   mac_address_t mac = ZERO_MAC_ADDRESS;
     797           4 :   vnet_main_t *vnm = vnet_get_main ();
     798             :   ip_neighbor_flags_t flags;
     799           4 :   u32 sw_if_index = ~0;
     800           4 :   int is_add = 1;
     801           4 :   int count = 1;
     802             : 
     803           4 :   flags = IP_NEIGHBOR_FLAG_DYNAMIC;
     804             : 
     805          10 :   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           6 :       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, "static"))
     815             :         {
     816           2 :           flags |= IP_NEIGHBOR_FLAG_STATIC;
     817           2 :           flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC;
     818             :         }
     819           0 :       else if (unformat (input, "no-fib-entry"))
     820           0 :         flags |= IP_NEIGHBOR_FLAG_NO_FIB_ENTRY;
     821           0 :       else if (unformat (input, "count %d", &count))
     822             :         ;
     823             :       else
     824           0 :         break;
     825             :     }
     826             : 
     827           8 :   if (sw_if_index == ~0 ||
     828           8 :       ip_address_is_zero (&ip) || mac_address_is_zero (&mac))
     829           0 :     return clib_error_return (0,
     830             :                               "specify interface, IP address and MAC: `%U'",
     831             :                               format_unformat_error, input);
     832             : 
     833           8 :   while (count)
     834             :     {
     835           4 :       if (is_add)
     836           4 :         ip_neighbor_add (&ip, &mac, sw_if_index, flags, NULL);
     837             :       else
     838           0 :         ip_neighbor_del (&ip, sw_if_index);
     839             : 
     840           4 :       ip_address_increment (&ip);
     841           4 :       mac_address_increment (&mac);
     842             : 
     843           4 :       --count;
     844             :     }
     845             : 
     846           4 :   return NULL;
     847             : }
     848             : 
     849             : /* *INDENT-OFF* */
     850             : /*?
     851             :  * Add or delete IPv4 ARP cache entries.
     852             :  *
     853             :  * @note 'set ip neighbor' options (e.g. delete, static,
     854             :  * 'count <number>', 'interface ip4_addr mac_addr') can be added in
     855             :  * any order and combination.
     856             :  *
     857             :  * @cliexpar
     858             :  * @parblock
     859             :  * Add or delete IPv4 ARP cache entries as follows. MAC Address can be in
     860             :  * either aa:bb:cc:dd:ee:ff format or aabb.ccdd.eeff format.
     861             :  * @cliexcmd{set ip neighbor GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
     862             :  * @cliexcmd{set ip neighbor delete GigabitEthernet2/0/0 6.0.0.3
     863             :  * de:ad:be:ef:ba:be}
     864             :  *
     865             :  * To add or delete an IPv4 ARP cache entry
     866             :  * table:
     867             :  * @cliexcmd{set ip neighbor GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
     868             :  * @cliexcmd{set ip neighbor delete GigabitEthernet2/0/0 6.0.0.3
     869             :  * dead.beef.babe}
     870             :  *
     871             :  * Add or delete IPv4 static ARP cache entries as follows:
     872             :  * @cliexcmd{set ip neighbor static GigabitEthernet2/0/0 6.0.0.3
     873             :  * dead.beef.babe}
     874             :  * @cliexcmd{set ip neighbor static delete GigabitEthernet2/0/0 6.0.0.3
     875             :  * dead.beef.babe}
     876             :  *
     877             :  * For testing / debugging purposes, the 'set ip neighbor' command can add or
     878             :  * delete multiple entries. Supply the 'count N' parameter:
     879             :  * @cliexcmd{set ip neighbor count 10 GigabitEthernet2/0/0 6.0.0.3
     880             :  * dead.beef.babe}
     881             :  * @endparblock
     882             :  ?*/
     883      272887 : VLIB_CLI_COMMAND (ip_neighbor_command, static) = {
     884             :   .path = "set ip neighbor",
     885             :   .short_help = "set ip neighbor [del] <intfc> <ip-address> <mac-address> "
     886             :                 "[static] [no-fib-entry] [count <count>]",
     887             :   .function = ip_neighbor_cmd,
     888             : };
     889      272887 : VLIB_CLI_COMMAND (ip_neighbor_command2, static) = {
     890             :   .path = "ip neighbor",
     891             :   .short_help = "ip neighbor [del] <intfc> <ip-address> <mac-address> "
     892             :                 "[static] [no-fib-entry] [count <count>]",
     893             :   .function = ip_neighbor_cmd,
     894             : };
     895             : /* *INDENT-ON* */
     896             : 
     897             : static int
     898      132429 : ip_neighbor_sort (void *a1, void *a2)
     899             : {
     900      132429 :   index_t *ipni1 = a1, *ipni2 = a2;
     901             :   ip_neighbor_t *ipn1, *ipn2;
     902             :   int cmp;
     903             : 
     904      132429 :   ipn1 = ip_neighbor_get (*ipni1);
     905      132429 :   ipn2 = ip_neighbor_get (*ipni2);
     906             : 
     907      132429 :   cmp = vnet_sw_interface_compare (vnet_get_main (),
     908      132429 :                                    ipn1->ipn_key->ipnk_sw_if_index,
     909      132429 :                                    ipn2->ipn_key->ipnk_sw_if_index);
     910      132429 :   if (!cmp)
     911      117802 :     cmp = ip_address_cmp (&ipn1->ipn_key->ipnk_ip, &ipn2->ipn_key->ipnk_ip);
     912      132429 :   return cmp;
     913             : }
     914             : 
     915             : static index_t *
     916         145 : ip_neighbor_entries (u32 sw_if_index, ip_address_family_t af)
     917             : {
     918         145 :   index_t *ipnis = NULL;
     919             :   ip_neighbor_t *ipn;
     920             : 
     921             :   /* *INDENT-OFF* */
     922       23270 :   pool_foreach (ipn, ip_neighbor_pool)
     923             :    {
     924       23125 :     if ((sw_if_index == ~0 ||
     925       23125 :         ipn->ipn_key->ipnk_sw_if_index == sw_if_index) &&
     926        1579 :         (N_AF == af ||
     927        1579 :          ip_neighbor_get_af(ipn) == af))
     928       23020 :        vec_add1 (ipnis, ip_neighbor_get_index(ipn));
     929             :   }
     930             : 
     931             :   /* *INDENT-ON* */
     932             : 
     933         145 :   if (ipnis)
     934         127 :     vec_sort_with_function (ipnis, ip_neighbor_sort);
     935         145 :   return ipnis;
     936             : }
     937             : 
     938             : static clib_error_t *
     939           1 : ip_neighbor_show_sorted_i (vlib_main_t * vm,
     940             :                            unformat_input_t * input,
     941             :                            vlib_cli_command_t * cmd, ip_address_family_t af)
     942             : {
     943             :   ip_neighbor_elt_t *elt, *head;
     944             : 
     945           1 :   head = pool_elt_at_index (ip_neighbor_elt_pool, ip_neighbor_list_head[af]);
     946             : 
     947             : 
     948           1 :   vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP",
     949             :                    "Flags", "Ethernet", "Interface");
     950             : 
     951             :   /* *INDENT-OFF*/
     952             :   /* the list is time sorted, newest first, so start from the back
     953             :    * and work forwards. Stop when we get to one that is alive */
     954         201 :   clib_llist_foreach_reverse(ip_neighbor_elt_pool,
     955             :                              ipne_anchor, head, elt,
     956             :   ({
     957             :     vlib_cli_output (vm, "%U", format_ip_neighbor, elt->ipne_index);
     958             :   }));
     959             :   /* *INDENT-ON*/
     960             : 
     961           1 :   return (NULL);
     962             : }
     963             : 
     964             : static clib_error_t *
     965         145 : ip_neighbor_show_i (vlib_main_t * vm,
     966             :                     unformat_input_t * input,
     967             :                     vlib_cli_command_t * cmd, ip_address_family_t af)
     968             : {
     969         145 :   index_t *ipni, *ipnis = NULL;
     970             :   u32 sw_if_index;
     971             : 
     972             :   /* Filter entries by interface if given. */
     973         145 :   sw_if_index = ~0;
     974         145 :   (void) unformat_user (input, unformat_vnet_sw_interface, vnet_get_main (),
     975             :                         &sw_if_index);
     976             : 
     977         145 :   ipnis = ip_neighbor_entries (sw_if_index, af);
     978             : 
     979         145 :   if (ipnis)
     980         127 :     vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP",
     981             :                      "Flags", "Ethernet", "Interface");
     982             : 
     983       23165 :   vec_foreach (ipni, ipnis)
     984             :   {
     985       23020 :     vlib_cli_output (vm, "%U", format_ip_neighbor, *ipni);
     986             :   }
     987         145 :   vec_free (ipnis);
     988             : 
     989         145 :   return (NULL);
     990             : }
     991             : 
     992             : static clib_error_t *
     993          60 : ip_neighbor_show (vlib_main_t * vm,
     994             :                   unformat_input_t * input, vlib_cli_command_t * cmd)
     995             : {
     996          60 :   return (ip_neighbor_show_i (vm, input, cmd, N_AF));
     997             : }
     998             : 
     999             : static clib_error_t *
    1000          61 : ip6_neighbor_show (vlib_main_t * vm,
    1001             :                    unformat_input_t * input, vlib_cli_command_t * cmd)
    1002             : {
    1003          61 :   return (ip_neighbor_show_i (vm, input, cmd, AF_IP6));
    1004             : }
    1005             : 
    1006             : static clib_error_t *
    1007          24 : ip4_neighbor_show (vlib_main_t * vm,
    1008             :                    unformat_input_t * input, vlib_cli_command_t * cmd)
    1009             : {
    1010          24 :   return (ip_neighbor_show_i (vm, input, cmd, AF_IP4));
    1011             : }
    1012             : 
    1013             : static clib_error_t *
    1014           0 : ip6_neighbor_show_sorted (vlib_main_t * vm,
    1015             :                           unformat_input_t * input, vlib_cli_command_t * cmd)
    1016             : {
    1017           0 :   return (ip_neighbor_show_sorted_i (vm, input, cmd, AF_IP6));
    1018             : }
    1019             : 
    1020             : static clib_error_t *
    1021           1 : ip4_neighbor_show_sorted (vlib_main_t * vm,
    1022             :                           unformat_input_t * input, vlib_cli_command_t * cmd)
    1023             : {
    1024           1 :   return (ip_neighbor_show_sorted_i (vm, input, cmd, AF_IP4));
    1025             : }
    1026             : 
    1027             : /*?
    1028             :  * Display all the IP neighbor entries.
    1029             :  *
    1030             :  * @cliexpar
    1031             :  * Example of how to display the IPv4 ARP table:
    1032             :  * @cliexstart{show ip neighbor}
    1033             :  *    Time      FIB        IP4       Flags      Ethernet              Interface
    1034             :  *    346.3028   0       6.1.1.3            de:ad:be:ef:ba:be   GigabitEthernet2/0/0
    1035             :  *   3077.4271   0       6.1.1.4       S    de:ad:be:ef:ff:ff   GigabitEthernet2/0/0
    1036             :  *   2998.6409   1       6.2.2.3            de:ad:be:ef:00:01   GigabitEthernet2/0/0
    1037             :  * Proxy arps enabled for:
    1038             :  * Fib_index 0   6.0.0.1 - 6.0.0.11
    1039             :  * @cliexend
    1040             :  ?*/
    1041             : /* *INDENT-OFF* */
    1042      272887 : VLIB_CLI_COMMAND (show_ip_neighbors_cmd_node, static) = {
    1043             :   .path = "show ip neighbors",
    1044             :   .function = ip_neighbor_show,
    1045             :   .short_help = "show ip neighbors [interface]",
    1046             : };
    1047      272887 : VLIB_CLI_COMMAND (show_ip4_neighbors_cmd_node, static) = {
    1048             :   .path = "show ip4 neighbors",
    1049             :   .function = ip4_neighbor_show,
    1050             :   .short_help = "show ip4 neighbors [interface]",
    1051             : };
    1052      272887 : VLIB_CLI_COMMAND (show_ip6_neighbors_cmd_node, static) = {
    1053             :   .path = "show ip6 neighbors",
    1054             :   .function = ip6_neighbor_show,
    1055             :   .short_help = "show ip6 neighbors [interface]",
    1056             : };
    1057      272887 : VLIB_CLI_COMMAND (show_ip_neighbor_cmd_node, static) = {
    1058             :   .path = "show ip neighbor",
    1059             :   .function = ip_neighbor_show,
    1060             :   .short_help = "show ip neighbor [interface]",
    1061             : };
    1062      272887 : VLIB_CLI_COMMAND (show_ip4_neighbor_cmd_node, static) = {
    1063             :   .path = "show ip4 neighbor",
    1064             :   .function = ip4_neighbor_show,
    1065             :   .short_help = "show ip4 neighbor [interface]",
    1066             : };
    1067      272887 : VLIB_CLI_COMMAND (show_ip6_neighbor_cmd_node, static) = {
    1068             :   .path = "show ip6 neighbor",
    1069             :   .function = ip6_neighbor_show,
    1070             :   .short_help = "show ip6 neighbor [interface]",
    1071             : };
    1072      272887 : VLIB_CLI_COMMAND (show_ip4_neighbor_sorted_cmd_node, static) = {
    1073             :   .path = "show ip4 neighbor-sorted",
    1074             :   .function = ip4_neighbor_show_sorted,
    1075             :   .short_help = "show ip4 neighbor-sorted",
    1076             : };
    1077      272887 : VLIB_CLI_COMMAND (show_ip6_neighbor_sorted_cmd_node, static) = {
    1078             :   .path = "show ip6 neighbor-sorted",
    1079             :   .function = ip6_neighbor_show_sorted,
    1080             :   .short_help = "show ip6 neighbor-sorted",
    1081             : };
    1082             : /* *INDENT-ON* */
    1083             : 
    1084             : static ip_neighbor_vft_t ip_nbr_vfts[N_AF];
    1085             : 
    1086             : void
    1087        1118 : ip_neighbor_register (ip_address_family_t af, const ip_neighbor_vft_t * vft)
    1088             : {
    1089        1118 :   ip_nbr_vfts[af] = *vft;
    1090        1118 : }
    1091             : 
    1092             : void
    1093         869 : ip_neighbor_probe_dst (u32 sw_if_index, u32 thread_index,
    1094             :                        ip_address_family_t af, const ip46_address_t *dst)
    1095             : {
    1096         869 :   if (!vnet_sw_interface_is_admin_up (vnet_get_main (), sw_if_index))
    1097          26 :     return;
    1098             : 
    1099         843 :   switch (af)
    1100             :     {
    1101          44 :     case AF_IP6:
    1102          44 :       ip6_neighbor_probe_dst (sw_if_index, thread_index, &dst->ip6);
    1103          44 :       break;
    1104         799 :     case AF_IP4:
    1105         799 :       ip4_neighbor_probe_dst (sw_if_index, thread_index, &dst->ip4);
    1106         799 :       break;
    1107             :     }
    1108             : }
    1109             : 
    1110             : void
    1111         239 : ip_neighbor_probe (const ip_adjacency_t * adj)
    1112             : {
    1113         239 :   ip_neighbor_probe_dst (adj->rewrite_header.sw_if_index,
    1114         239 :                          vlib_get_thread_index (),
    1115         239 :                          ip_address_family_from_fib_proto (adj->ia_nh_proto),
    1116             :                          &adj->sub_type.nbr.next_hop);
    1117         239 : }
    1118             : 
    1119             : void
    1120        6754 : ip_neighbor_walk (ip_address_family_t af,
    1121             :                   u32 sw_if_index, ip_neighbor_walk_cb_t cb, void *ctx)
    1122             : {
    1123             :   ip_neighbor_key_t *key;
    1124             :   index_t ipni;
    1125             : 
    1126        6754 :   if (~0 == sw_if_index)
    1127             :     {
    1128             :       uword **hash;
    1129             : 
    1130          70 :       vec_foreach (hash, ip_neighbor_db[af].ipndb_hash)
    1131             :       {
    1132             :           /* *INDENT-OFF* */
    1133        3450 :           hash_foreach (key, ipni, *hash,
    1134             :           ({
    1135             :             if (WALK_STOP == cb (ipni, ctx))
    1136             :               break;
    1137             :           }));
    1138             :           /* *INDENT-ON* */
    1139             :       }
    1140             :     }
    1141             :   else
    1142             :     {
    1143             :       uword *hash;
    1144             : 
    1145        6740 :       if (vec_len (ip_neighbor_db[af].ipndb_hash) <= sw_if_index)
    1146        1004 :         return;
    1147        5736 :       hash = ip_neighbor_db[af].ipndb_hash[sw_if_index];
    1148             : 
    1149             :       /* *INDENT-OFF* */
    1150      391406 :       hash_foreach (key, ipni, hash,
    1151             :       ({
    1152             :         if (WALK_STOP == cb (ipni, ctx))
    1153             :           break;
    1154             :       }));
    1155             :       /* *INDENT-ON* */
    1156             :     }
    1157             : }
    1158             : 
    1159             : int
    1160           0 : ip4_neighbor_proxy_add (u32 fib_index,
    1161             :                         const ip4_address_t * start,
    1162             :                         const ip4_address_t * end)
    1163             : {
    1164           0 :   if (ip_nbr_vfts[AF_IP4].inv_proxy4_add)
    1165             :     {
    1166           0 :       return (ip_nbr_vfts[AF_IP4].inv_proxy4_add (fib_index, start, end));
    1167             :     }
    1168             : 
    1169           0 :   return (-1);
    1170             : }
    1171             : 
    1172             : int
    1173           0 : ip4_neighbor_proxy_delete (u32 fib_index,
    1174             :                            const ip4_address_t * start,
    1175             :                            const ip4_address_t * end)
    1176             : {
    1177           0 :   if (ip_nbr_vfts[AF_IP4].inv_proxy4_del)
    1178             :     {
    1179           0 :       return (ip_nbr_vfts[AF_IP4].inv_proxy4_del (fib_index, start, end));
    1180             :     }
    1181           0 :   return -1;
    1182             : }
    1183             : 
    1184             : int
    1185           0 : ip4_neighbor_proxy_enable (u32 sw_if_index)
    1186             : {
    1187           0 :   if (ip_nbr_vfts[AF_IP4].inv_proxy4_enable)
    1188             :     {
    1189           0 :       return (ip_nbr_vfts[AF_IP4].inv_proxy4_enable (sw_if_index));
    1190             :     }
    1191           0 :   return -1;
    1192             : }
    1193             : 
    1194             : int
    1195           0 : ip4_neighbor_proxy_disable (u32 sw_if_index)
    1196             : {
    1197           0 :   if (ip_nbr_vfts[AF_IP4].inv_proxy4_disable)
    1198             :     {
    1199           0 :       return (ip_nbr_vfts[AF_IP4].inv_proxy4_disable (sw_if_index));
    1200             :     }
    1201           0 :   return -1;
    1202             : }
    1203             : 
    1204             : int
    1205           0 : ip6_neighbor_proxy_add (u32 sw_if_index, const ip6_address_t * addr)
    1206             : {
    1207           0 :   if (ip_nbr_vfts[AF_IP6].inv_proxy6_add)
    1208             :     {
    1209           0 :       return (ip_nbr_vfts[AF_IP6].inv_proxy6_add (sw_if_index, addr));
    1210             :     }
    1211           0 :   return -1;
    1212             : }
    1213             : 
    1214             : int
    1215           0 : ip6_neighbor_proxy_del (u32 sw_if_index, const ip6_address_t * addr)
    1216             : {
    1217           0 :   if (ip_nbr_vfts[AF_IP6].inv_proxy6_del)
    1218             :     {
    1219           0 :       return (ip_nbr_vfts[AF_IP6].inv_proxy6_del (sw_if_index, addr));
    1220             :     }
    1221           0 :   return -1;
    1222             : }
    1223             : 
    1224             : void
    1225       13782 : ip_neighbor_populate (ip_address_family_t af, u32 sw_if_index)
    1226             : {
    1227       13782 :   index_t *ipnis = NULL, *ipni;
    1228             :   ip_neighbor_t *ipn;
    1229             : 
    1230       13782 :   IP_NEIGHBOR_DBG ("populate: %U %U",
    1231             :                    format_vnet_sw_if_index_name, vnet_get_main (),
    1232             :                    sw_if_index, format_ip_address_family, af);
    1233             : 
    1234             :   /* *INDENT-OFF* */
    1235       43228 :   pool_foreach (ipn, ip_neighbor_pool)
    1236             :    {
    1237       29446 :     if (ip_neighbor_get_af(ipn) == af &&
    1238       14723 :         ipn->ipn_key->ipnk_sw_if_index == sw_if_index)
    1239         404 :       vec_add1 (ipnis, ipn - ip_neighbor_pool);
    1240             :   }
    1241             :   /* *INDENT-ON* */
    1242             : 
    1243       14186 :   vec_foreach (ipni, ipnis)
    1244             :   {
    1245         404 :     ipn = ip_neighbor_get (*ipni);
    1246             : 
    1247         404 :     adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
    1248         404 :                      ip_address_family_to_fib_proto (ip_neighbor_get_af
    1249             :                                                      (ipn)),
    1250         404 :                      &ip_addr_46 (&ipn->ipn_key->ipnk_ip),
    1251             :                      ip_neighbor_mk_complete_walk, ipn);
    1252             :   }
    1253       13782 :   vec_free (ipnis);
    1254       13782 : }
    1255             : 
    1256             : void
    1257       21112 : ip_neighbor_flush (ip_address_family_t af, u32 sw_if_index)
    1258             : {
    1259       21112 :   index_t *ipnis = NULL, *ipni;
    1260             :   ip_neighbor_t *ipn;
    1261             : 
    1262             : 
    1263       21112 :   IP_NEIGHBOR_DBG ("flush: %U %U",
    1264             :                    format_vnet_sw_if_index_name, vnet_get_main (),
    1265             :                    sw_if_index, format_ip_address_family, af);
    1266             : 
    1267             :   /* *INDENT-OFF* */
    1268       70695 :   pool_foreach (ipn, ip_neighbor_pool)
    1269             :    {
    1270       49583 :     if (ip_neighbor_get_af(ipn) == af &&
    1271       28291 :         ipn->ipn_key->ipnk_sw_if_index == sw_if_index &&
    1272        2736 :         ip_neighbor_is_dynamic (ipn))
    1273        2730 :       vec_add1 (ipnis, ipn - ip_neighbor_pool);
    1274             :   }
    1275             :   /* *INDENT-ON* */
    1276             : 
    1277       23842 :   vec_foreach (ipni, ipnis) ip_neighbor_destroy (ip_neighbor_get (*ipni));
    1278       21112 :   vec_free (ipnis);
    1279       21112 : }
    1280             : 
    1281             : walk_rc_t
    1282         256 : ip_neighbor_mark_one (index_t ipni, void *ctx)
    1283             : {
    1284             :   ip_neighbor_t *ipn;
    1285             : 
    1286         256 :   ipn = ip_neighbor_get (ipni);
    1287             : 
    1288         256 :   ipn->ipn_flags |= IP_NEIGHBOR_FLAG_STALE;
    1289             : 
    1290         256 :   return (WALK_CONTINUE);
    1291             : }
    1292             : 
    1293             : void
    1294           4 : ip_neighbor_mark (ip_address_family_t af)
    1295             : {
    1296           4 :   ip_neighbor_walk (af, ~0, ip_neighbor_mark_one, NULL);
    1297           4 : }
    1298             : 
    1299             : typedef struct ip_neighbor_sweep_ctx_t_
    1300             : {
    1301             :   index_t *ipnsc_stale;
    1302             : } ip_neighbor_sweep_ctx_t;
    1303             : 
    1304             : static walk_rc_t
    1305         256 : ip_neighbor_sweep_one (index_t ipni, void *arg)
    1306             : {
    1307         256 :   ip_neighbor_sweep_ctx_t *ctx = arg;
    1308             :   ip_neighbor_t *ipn;
    1309             : 
    1310         256 :   ipn = ip_neighbor_get (ipni);
    1311             : 
    1312         256 :   if (ipn->ipn_flags & IP_NEIGHBOR_FLAG_STALE)
    1313             :     {
    1314         192 :       vec_add1 (ctx->ipnsc_stale, ipni);
    1315             :     }
    1316             : 
    1317         256 :   return (WALK_CONTINUE);
    1318             : }
    1319             : 
    1320             : void
    1321           4 : ip_neighbor_sweep (ip_address_family_t af)
    1322             : {
    1323           4 :   ip_neighbor_sweep_ctx_t ctx = { };
    1324             :   index_t *ipni;
    1325             : 
    1326           4 :   ip_neighbor_walk (af, ~0, ip_neighbor_sweep_one, &ctx);
    1327             : 
    1328         196 :   vec_foreach (ipni, ctx.ipnsc_stale)
    1329             :   {
    1330         192 :     ip_neighbor_destroy (ip_neighbor_get (*ipni));
    1331             :   }
    1332           4 :   vec_free (ctx.ipnsc_stale);
    1333           4 : }
    1334             : 
    1335             : /*
    1336             :  * Remove any arp entries associated with the specified interface
    1337             :  */
    1338             : static clib_error_t *
    1339       13268 : ip_neighbor_interface_admin_change (vnet_main_t * vnm,
    1340             :                                     u32 sw_if_index, u32 flags)
    1341             : {
    1342             :   ip_address_family_t af;
    1343             : 
    1344       13268 :   IP_NEIGHBOR_DBG ("interface-admin: %U  %s",
    1345             :                    format_vnet_sw_if_index_name, vnet_get_main (),
    1346             :                    sw_if_index,
    1347             :                    (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ? "up" : "down"));
    1348             : 
    1349       13268 :   if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
    1350             :     {
    1351       20673 :       FOR_EACH_IP_ADDRESS_FAMILY (af) ip_neighbor_populate (af, sw_if_index);
    1352             :     }
    1353             :   else
    1354             :     {
    1355             :       /* admin down, flush all neighbours */
    1356       19131 :       FOR_EACH_IP_ADDRESS_FAMILY (af) ip_neighbor_flush (af, sw_if_index);
    1357             :     }
    1358             : 
    1359       13268 :   return (NULL);
    1360             : }
    1361             : 
    1362        2801 : VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ip_neighbor_interface_admin_change);
    1363             : 
    1364             : /*
    1365             :  * Remove any arp entries associated with the specified interface
    1366             :  */
    1367             : static clib_error_t *
    1368       11597 : ip_neighbor_add_del_sw_interface (vnet_main_t *vnm, u32 sw_if_index,
    1369             :                                   u32 is_add)
    1370             : {
    1371       11597 :   IP_NEIGHBOR_DBG ("interface-change: %U  %s",
    1372             :                    format_vnet_sw_if_index_name, vnet_get_main (),
    1373             :                    sw_if_index, (is_add ? "add" : "del"));
    1374             : 
    1375       11597 :   if (!is_add && sw_if_index != ~0)
    1376             :     {
    1377             :       ip_address_family_t af;
    1378             : 
    1379       12537 :       FOR_EACH_IP_ADDRESS_FAMILY (af) ip_neighbor_flush (af, sw_if_index);
    1380             :     }
    1381             : 
    1382       11597 :   if (is_add)
    1383             :     {
    1384        7418 :       ip_neighbor_alloc_ctr (&ip_neighbor_counters[AF_IP4], sw_if_index);
    1385        7418 :       ip_neighbor_alloc_ctr (&ip_neighbor_counters[AF_IP6], sw_if_index);
    1386             :     }
    1387             : 
    1388       11597 :   return (NULL);
    1389             : }
    1390             : 
    1391        3363 : VNET_SW_INTERFACE_ADD_DEL_FUNCTION (ip_neighbor_add_del_sw_interface);
    1392             : 
    1393             : typedef struct ip_neighbor_walk_covered_ctx_t_
    1394             : {
    1395             :   ip_address_t addr;
    1396             :   u32 length;
    1397             :   index_t *ipnis;
    1398             : } ip_neighbor_walk_covered_ctx_t;
    1399             : 
    1400             : static walk_rc_t
    1401        1933 : ip_neighbor_walk_covered (index_t ipni, void *arg)
    1402             : {
    1403        1933 :   ip_neighbor_walk_covered_ctx_t *ctx = arg;
    1404             :   ip_neighbor_t *ipn;
    1405             : 
    1406        1933 :   ipn = ip_neighbor_get (ipni);
    1407             : 
    1408        1933 :   if (AF_IP4 == ip_addr_version (&ctx->addr))
    1409             :     {
    1410         919 :       if (ip4_destination_matches_route (&ip4_main,
    1411         919 :                                          &ip_addr_v4 (&ipn->ipn_key->ipnk_ip),
    1412         919 :                                          &ip_addr_v4 (&ctx->addr),
    1413        1832 :                                          ctx->length) &&
    1414         913 :           ip_neighbor_is_dynamic (ipn))
    1415             :         {
    1416         908 :           vec_add1 (ctx->ipnis, ip_neighbor_get_index (ipn));
    1417             :         }
    1418             :     }
    1419        1014 :   else if (AF_IP6 == ip_addr_version (&ctx->addr))
    1420             :     {
    1421        1014 :       if (ip6_destination_matches_route (&ip6_main,
    1422        1014 :                                          &ip_addr_v6 (&ipn->ipn_key->ipnk_ip),
    1423        1014 :                                          &ip_addr_v6 (&ctx->addr),
    1424        2022 :                                          ctx->length) &&
    1425        1008 :           ip_neighbor_is_dynamic (ipn))
    1426             :         {
    1427        1008 :           vec_add1 (ctx->ipnis, ip_neighbor_get_index (ipn));
    1428             :         }
    1429             :     }
    1430        1933 :   return (WALK_CONTINUE);
    1431             : }
    1432             : 
    1433             : 
    1434             : /*
    1435             :  * callback when an interface address is added or deleted
    1436             :  */
    1437             : static void
    1438        4699 : ip_neighbor_add_del_interface_address_v4 (ip4_main_t * im,
    1439             :                                           uword opaque,
    1440             :                                           u32 sw_if_index,
    1441             :                                           ip4_address_t * address,
    1442             :                                           u32 address_length,
    1443             :                                           u32 if_address_index, u32 is_del)
    1444             : {
    1445             :   /*
    1446             :    * Flush the ARP cache of all entries covered by the address
    1447             :    * that is being removed.
    1448             :    */
    1449        4699 :   IP_NEIGHBOR_DBG ("addr-%s: %U, %U/%d", (is_del ? "del" : "add"),
    1450             :                    format_vnet_sw_if_index_name, vnet_get_main (), sw_if_index,
    1451             :                    format_ip4_address, address, address_length);
    1452             : 
    1453        4699 :   if (is_del)
    1454             :     {
    1455             :       /* *INDENT-OFF* */
    1456        2223 :       ip_neighbor_walk_covered_ctx_t ctx = {
    1457             :         .addr = {
    1458             :           .ip.ip4 = *address,
    1459             :           .version = AF_IP4,
    1460             :         },
    1461             :         .length = address_length,
    1462             :       };
    1463             :       /* *INDENT-ON* */
    1464             :       index_t *ipni;
    1465             : 
    1466        2223 :       ip_neighbor_walk (AF_IP4, sw_if_index, ip_neighbor_walk_covered, &ctx);
    1467             : 
    1468        3131 :       vec_foreach (ipni, ctx.ipnis)
    1469         908 :         ip_neighbor_destroy (ip_neighbor_get (*ipni));
    1470             : 
    1471        2223 :       vec_free (ctx.ipnis);
    1472             :     }
    1473        4699 : }
    1474             : 
    1475             : /*
    1476             :  * callback when an interface address is added or deleted
    1477             :  */
    1478             : static void
    1479        3979 : ip_neighbor_add_del_interface_address_v6 (ip6_main_t * im,
    1480             :                                           uword opaque,
    1481             :                                           u32 sw_if_index,
    1482             :                                           ip6_address_t * address,
    1483             :                                           u32 address_length,
    1484             :                                           u32 if_address_index, u32 is_del)
    1485             : {
    1486             :   /*
    1487             :    * Flush the ARP cache of all entries covered by the address
    1488             :    * that is being removed.
    1489             :    */
    1490        3979 :   IP_NEIGHBOR_DBG ("addr-change: %U, %U/%d %s",
    1491             :                    format_vnet_sw_if_index_name, vnet_get_main (),
    1492             :                    sw_if_index, format_ip6_address, address, address_length,
    1493             :                    (is_del ? "del" : "add"));
    1494             : 
    1495        3979 :   if (is_del)
    1496             :     {
    1497             :       /* *INDENT-OFF* */
    1498        1905 :       ip_neighbor_walk_covered_ctx_t ctx = {
    1499             :         .addr = {
    1500             :           .ip.ip6 = *address,
    1501             :           .version = AF_IP6,
    1502             :         },
    1503             :         .length = address_length,
    1504             :       };
    1505             :       /* *INDENT-ON* */
    1506             :       index_t *ipni;
    1507             : 
    1508        1905 :       ip_neighbor_walk (AF_IP6, sw_if_index, ip_neighbor_walk_covered, &ctx);
    1509             : 
    1510        2913 :       vec_foreach (ipni, ctx.ipnis)
    1511        1008 :         ip_neighbor_destroy (ip_neighbor_get (*ipni));
    1512             : 
    1513        1905 :       vec_free (ctx.ipnis);
    1514             :     }
    1515        3979 : }
    1516             : 
    1517             : typedef struct ip_neighbor_table_bind_ctx_t_
    1518             : {
    1519             :   u32 new_fib_index;
    1520             :   u32 old_fib_index;
    1521             : } ip_neighbor_table_bind_ctx_t;
    1522             : 
    1523             : static walk_rc_t
    1524           4 : ip_neighbor_walk_table_bind (index_t ipni, void *arg)
    1525             : {
    1526           4 :   ip_neighbor_table_bind_ctx_t *ctx = arg;
    1527             :   ip_neighbor_t *ipn;
    1528             : 
    1529           4 :   ipn = ip_neighbor_get (ipni);
    1530           4 :   ip_neighbor_adj_fib_remove (ipn, ctx->old_fib_index);
    1531           4 :   ip_neighbor_adj_fib_add (ipn, ctx->new_fib_index);
    1532             : 
    1533           4 :   return (WALK_CONTINUE);
    1534             : }
    1535             : 
    1536             : static void
    1537         896 : ip_neighbor_table_bind_v4 (ip4_main_t * im,
    1538             :                            uword opaque,
    1539             :                            u32 sw_if_index,
    1540             :                            u32 new_fib_index, u32 old_fib_index)
    1541             : {
    1542         896 :   ip_neighbor_table_bind_ctx_t ctx = {
    1543             :     .old_fib_index = old_fib_index,
    1544             :     .new_fib_index = new_fib_index,
    1545             :   };
    1546             : 
    1547         896 :   ip_neighbor_walk (AF_IP4, sw_if_index, ip_neighbor_walk_table_bind, &ctx);
    1548         896 : }
    1549             : 
    1550             : static void
    1551         729 : ip_neighbor_table_bind_v6 (ip6_main_t * im,
    1552             :                            uword opaque,
    1553             :                            u32 sw_if_index,
    1554             :                            u32 new_fib_index, u32 old_fib_index)
    1555             : {
    1556         729 :   ip_neighbor_table_bind_ctx_t ctx = {
    1557             :     .old_fib_index = old_fib_index,
    1558             :     .new_fib_index = new_fib_index,
    1559             :   };
    1560             : 
    1561         729 :   ip_neighbor_walk (AF_IP6, sw_if_index, ip_neighbor_walk_table_bind, &ctx);
    1562         729 : }
    1563             : 
    1564             : typedef enum ip_neighbor_age_state_t_
    1565             : {
    1566             :   IP_NEIGHBOR_AGE_ALIVE,
    1567             :   IP_NEIGHBOR_AGE_PROBE,
    1568             :   IP_NEIGHBOR_AGE_DEAD,
    1569             : } ip_neighbor_age_state_t;
    1570             : 
    1571             : #define IP_NEIGHBOR_PROCESS_SLEEP_LONG (0)
    1572             : 
    1573             : static ip_neighbor_age_state_t
    1574         851 : ip_neighbour_age_out (index_t ipni, f64 now, f64 * wait)
    1575             : {
    1576             :   ip_address_family_t af;
    1577             :   ip_neighbor_t *ipn;
    1578             :   u32 ipndb_age;
    1579             :   u32 ttl;
    1580             : 
    1581         851 :   ipn = ip_neighbor_get (ipni);
    1582         851 :   af = ip_neighbor_get_af (ipn);
    1583         851 :   ipndb_age = ip_neighbor_db[af].ipndb_age;
    1584         851 :   ttl = now - ipn->ipn_time_last_updated;
    1585         851 :   *wait = ipndb_age;
    1586             : 
    1587         851 :   if (ttl > ipndb_age)
    1588             :     {
    1589         840 :       IP_NEIGHBOR_DBG ("aged: %U @%f - %f > %d",
    1590             :                        format_ip_neighbor, ipni, now,
    1591             :                        ipn->ipn_time_last_updated, ipndb_age);
    1592         840 :       if (ipn->ipn_n_probes > 2)
    1593             :         {
    1594             :           /* 3 strikes and yea-re out */
    1595         210 :           IP_NEIGHBOR_DBG ("dead: %U", format_ip_neighbor, ipni);
    1596         210 :           *wait = 1;
    1597         210 :           return (IP_NEIGHBOR_AGE_DEAD);
    1598             :         }
    1599             :       else
    1600             :         {
    1601         630 :           ip_neighbor_probe_dst (ip_neighbor_get_sw_if_index (ipn),
    1602         630 :                                  vlib_get_thread_index (), af,
    1603         630 :                                  &ip_addr_46 (&ipn->ipn_key->ipnk_ip));
    1604             : 
    1605         630 :           ipn->ipn_n_probes++;
    1606         630 :           *wait = 1;
    1607             :         }
    1608             :     }
    1609             :   else
    1610             :     {
    1611             :       /* here we are sure that ttl <= ipndb_age */
    1612          11 :       *wait = ipndb_age - ttl + 1;
    1613          11 :       return (IP_NEIGHBOR_AGE_ALIVE);
    1614             :     }
    1615             : 
    1616         630 :   return (IP_NEIGHBOR_AGE_PROBE);
    1617             : }
    1618             : 
    1619             : typedef enum ip_neighbor_process_event_t_
    1620             : {
    1621             :   IP_NEIGHBOR_AGE_PROCESS_WAKEUP,
    1622             : } ip_neighbor_process_event_t;
    1623             : 
    1624             : static uword
    1625        1118 : ip_neighbor_age_loop (vlib_main_t * vm,
    1626             :                       vlib_node_runtime_t * rt,
    1627             :                       vlib_frame_t * f, ip_address_family_t af)
    1628             : {
    1629        1118 :   uword event_type, *event_data = NULL;
    1630             :   f64 timeout;
    1631             : 
    1632             :   /* Set the timeout to an effectively infinite value when the process starts */
    1633        1118 :   timeout = IP_NEIGHBOR_PROCESS_SLEEP_LONG;
    1634             : 
    1635             :   while (1)
    1636          15 :     {
    1637             :       f64 now;
    1638             : 
    1639        1133 :       if (!timeout)
    1640        1121 :         vlib_process_wait_for_event (vm);
    1641             :       else
    1642          12 :         vlib_process_wait_for_event_or_clock (vm, timeout);
    1643             : 
    1644          15 :       event_type = vlib_process_get_events (vm, &event_data);
    1645          15 :       vec_reset_length (event_data);
    1646             : 
    1647          15 :       now = vlib_time_now (vm);
    1648             : 
    1649          15 :       switch (event_type)
    1650             :         {
    1651          10 :         case ~0:
    1652             :           {
    1653             :             /* timer expired */
    1654             :             ip_neighbor_elt_t *elt, *head;
    1655             :             f64 wait;
    1656             : 
    1657          10 :             timeout = ip_neighbor_db[af].ipndb_age;
    1658          10 :             head = pool_elt_at_index (ip_neighbor_elt_pool,
    1659             :                                       ip_neighbor_list_head[af]);
    1660             : 
    1661             :           /* *INDENT-OFF*/
    1662             :           /* the list is time sorted, newest first, so start from the back
    1663             :            * and work forwards. Stop when we get to one that is alive */
    1664         220 :           restart:
    1665         860 :           clib_llist_foreach_reverse(ip_neighbor_elt_pool,
    1666             :                                      ipne_anchor, head, elt,
    1667             :           ({
    1668             :             ip_neighbor_age_state_t res;
    1669             : 
    1670             :             res = ip_neighbour_age_out(elt->ipne_index, now, &wait);
    1671             : 
    1672             :             if (IP_NEIGHBOR_AGE_ALIVE == res) {
    1673             :               /* the oldest neighbor has not yet expired, go back to sleep */
    1674             :               timeout = clib_min (wait, timeout);
    1675             :               break;
    1676             :             }
    1677             :             else if (IP_NEIGHBOR_AGE_DEAD == res) {
    1678             :               /* the oldest neighbor is dead, pop it, then restart the walk
    1679             :                * again from the back */
    1680             :               ip_neighbor_destroy (ip_neighbor_get(elt->ipne_index));
    1681             :               goto restart;
    1682             :             }
    1683             : 
    1684             :             timeout = clib_min (wait, timeout);
    1685             :           }));
    1686             :           /* *INDENT-ON* */
    1687          10 :             break;
    1688             :           }
    1689           5 :         case IP_NEIGHBOR_AGE_PROCESS_WAKEUP:
    1690             :           {
    1691             : 
    1692           5 :             if (!ip_neighbor_db[af].ipndb_age)
    1693             :               {
    1694             :                 /* aging has been disabled */
    1695           3 :                 timeout = 0;
    1696           3 :                 break;
    1697             :               }
    1698             :             ip_neighbor_elt_t *elt, *head;
    1699             : 
    1700           2 :             head = pool_elt_at_index (ip_neighbor_elt_pool,
    1701             :                                       ip_neighbor_list_head[af]);
    1702             :             /* no neighbors yet */
    1703           2 :             if (clib_llist_is_empty (ip_neighbor_elt_pool, ipne_anchor, head))
    1704             :               {
    1705           1 :                 timeout = ip_neighbor_db[af].ipndb_age;
    1706           1 :                 break;
    1707             :               }
    1708             : 
    1709             :             /* poke the oldset neighbour for aging, which returns how long we sleep for */
    1710           1 :             elt = clib_llist_prev (ip_neighbor_elt_pool, ipne_anchor, head);
    1711           1 :             ip_neighbour_age_out (elt->ipne_index, now, &timeout);
    1712           1 :             break;
    1713             :           }
    1714             :         }
    1715          15 :     }
    1716             :   return 0;
    1717             : }
    1718             : 
    1719             : static uword
    1720         559 : ip4_neighbor_age_process (vlib_main_t * vm,
    1721             :                           vlib_node_runtime_t * rt, vlib_frame_t * f)
    1722             : {
    1723         559 :   return (ip_neighbor_age_loop (vm, rt, f, AF_IP4));
    1724             : }
    1725             : 
    1726             : static uword
    1727         559 : ip6_neighbor_age_process (vlib_main_t * vm,
    1728             :                           vlib_node_runtime_t * rt, vlib_frame_t * f)
    1729             : {
    1730         559 :   return (ip_neighbor_age_loop (vm, rt, f, AF_IP6));
    1731             : }
    1732             : 
    1733             : /* *INDENT-OFF* */
    1734      178120 : VLIB_REGISTER_NODE (ip4_neighbor_age_process_node,static) = {
    1735             :   .function = ip4_neighbor_age_process,
    1736             :   .type = VLIB_NODE_TYPE_PROCESS,
    1737             :   .name = "ip4-neighbor-age-process",
    1738             : };
    1739      178120 : VLIB_REGISTER_NODE (ip6_neighbor_age_process_node,static) = {
    1740             :   .function = ip6_neighbor_age_process,
    1741             :   .type = VLIB_NODE_TYPE_PROCESS,
    1742             :   .name = "ip6-neighbor-age-process",
    1743             : };
    1744             : /* *INDENT-ON* */
    1745             : 
    1746             : int
    1747           5 : ip_neighbor_config (ip_address_family_t af, u32 limit, u32 age, bool recycle)
    1748             : {
    1749           5 :   ip_neighbor_db[af].ipndb_limit = limit;
    1750           5 :   ip_neighbor_db[af].ipndb_recycle = recycle;
    1751           5 :   ip_neighbor_db[af].ipndb_age = age;
    1752             : 
    1753          10 :   vlib_process_signal_event (vlib_get_main (),
    1754             :                              (AF_IP4 == af ?
    1755           5 :                               ip4_neighbor_age_process_node.index :
    1756           0 :                               ip6_neighbor_age_process_node.index),
    1757             :                              IP_NEIGHBOR_AGE_PROCESS_WAKEUP, 0);
    1758             : 
    1759           5 :   return (0);
    1760             : }
    1761             : 
    1762             : int
    1763           6 : ip_neighbor_get_config (ip_address_family_t af, u32 *limit, u32 *age,
    1764             :                         bool *recycle)
    1765             : {
    1766           6 :   *limit = ip_neighbor_db[af].ipndb_limit;
    1767           6 :   *age = ip_neighbor_db[af].ipndb_age;
    1768           6 :   *recycle = ip_neighbor_db[af].ipndb_recycle;
    1769             : 
    1770           6 :   return (0);
    1771             : }
    1772             : 
    1773             : static clib_error_t *
    1774           1 : ip_neighbor_config_show (vlib_main_t * vm,
    1775             :                          unformat_input_t * input, vlib_cli_command_t * cmd)
    1776             : {
    1777             :   ip_address_family_t af;
    1778             : 
    1779             :   /* *INDENT-OFF* */
    1780           3 :   FOR_EACH_IP_ADDRESS_FAMILY(af) {
    1781           2 :     vlib_cli_output (vm, "%U:", format_ip_address_family, af);
    1782           2 :     vlib_cli_output (vm, "  limit:%d, age:%d, recycle:%d",
    1783             :                      ip_neighbor_db[af].ipndb_limit,
    1784             :                      ip_neighbor_db[af].ipndb_age,
    1785           2 :                      ip_neighbor_db[af].ipndb_recycle);
    1786             :   }
    1787             : 
    1788             :   /* *INDENT-ON* */
    1789           1 :   return (NULL);
    1790             : }
    1791             : 
    1792             : static clib_error_t *
    1793           0 : ip_neighbor_config_set (vlib_main_t *vm, unformat_input_t *input,
    1794             :                         vlib_cli_command_t *cmd)
    1795             : {
    1796           0 :   unformat_input_t _line_input, *line_input = &_line_input;
    1797           0 :   clib_error_t *error = NULL;
    1798             :   ip_address_family_t af;
    1799             :   u32 limit, age;
    1800             :   bool recycle;
    1801             : 
    1802           0 :   if (!unformat_user (input, unformat_line_input, line_input))
    1803           0 :     return 0;
    1804             : 
    1805           0 :   if (!unformat (line_input, "%U", unformat_ip_address_family, &af))
    1806             :     {
    1807           0 :       error = unformat_parse_error (line_input);
    1808           0 :       goto done;
    1809             :     }
    1810             : 
    1811           0 :   limit = ip_neighbor_db[af].ipndb_limit;
    1812           0 :   age = ip_neighbor_db[af].ipndb_age;
    1813           0 :   recycle = ip_neighbor_db[af].ipndb_recycle;
    1814             : 
    1815           0 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    1816             :     {
    1817           0 :       if (unformat (line_input, "limit %u", &limit))
    1818             :         ;
    1819           0 :       else if (unformat (line_input, "age %u", &age))
    1820             :         ;
    1821           0 :       else if (unformat (line_input, "recycle"))
    1822           0 :         recycle = true;
    1823           0 :       else if (unformat (line_input, "norecycle"))
    1824           0 :         recycle = false;
    1825             :       else
    1826             :         {
    1827           0 :           error = unformat_parse_error (line_input);
    1828           0 :           goto done;
    1829             :         }
    1830             :     }
    1831             : 
    1832           0 :   ip_neighbor_config (af, limit, age, recycle);
    1833             : 
    1834           0 : done:
    1835           0 :   unformat_free (line_input);
    1836           0 :   return error;
    1837             : }
    1838             : 
    1839             : static void
    1840           7 : ip_neighbor_stats_show_one (vlib_main_t *vm, vnet_main_t *vnm, u32 sw_if_index)
    1841             : {
    1842           7 :   vlib_cli_output (vm, "  %U", format_vnet_sw_if_index_name, vnm, sw_if_index);
    1843           7 :   vlib_cli_output (vm, "    arp:%U", format_ip_neighbor_counters,
    1844             :                    &ip_neighbor_counters[AF_IP4], sw_if_index);
    1845           7 :   vlib_cli_output (vm, "    nd: %U", format_ip_neighbor_counters,
    1846             :                    &ip_neighbor_counters[AF_IP6], sw_if_index);
    1847           7 : }
    1848             : 
    1849             : static walk_rc_t
    1850           5 : ip_neighbor_stats_show_cb (vnet_main_t *vnm, vnet_sw_interface_t *si,
    1851             :                            void *ctx)
    1852             : {
    1853           5 :   ip_neighbor_stats_show_one (ctx, vnm, si->sw_if_index);
    1854             : 
    1855           5 :   return (WALK_CONTINUE);
    1856             : }
    1857             : 
    1858             : static clib_error_t *
    1859           3 : ip_neighbor_stats_show (vlib_main_t *vm, unformat_input_t *input,
    1860             :                         vlib_cli_command_t *cmd)
    1861             : {
    1862             :   vnet_main_t *vnm;
    1863             :   u32 sw_if_index;
    1864             : 
    1865           3 :   vnm = vnet_get_main ();
    1866           3 :   sw_if_index = ~0;
    1867           3 :   (void) unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index);
    1868             : 
    1869           3 :   if (~0 == sw_if_index)
    1870             :     {
    1871           1 :       vnet_sw_interface_walk (vnm, ip_neighbor_stats_show_cb, vm);
    1872             :     }
    1873             :   else
    1874             :     {
    1875           2 :       ip_neighbor_stats_show_one (vm, vnm, sw_if_index);
    1876             :     }
    1877           3 :   return (NULL);
    1878             : }
    1879             : 
    1880             : /* *INDENT-OFF* */
    1881      272887 : VLIB_CLI_COMMAND (show_ip_neighbor_cfg_cmd_node, static) = {
    1882             :   .path = "show ip neighbor-config",
    1883             :   .function = ip_neighbor_config_show,
    1884             :   .short_help = "show ip neighbor-config",
    1885             : };
    1886      272887 : VLIB_CLI_COMMAND (set_ip_neighbor_cfg_cmd_node, static) = {
    1887             :   .path = "set ip neighbor-config",
    1888             :   .function = ip_neighbor_config_set,
    1889             :   .short_help = "set ip neighbor-config ip4|ip6 [limit <limit>] [age <age>] "
    1890             :                 "[recycle|norecycle]",
    1891             : };
    1892      272887 : VLIB_CLI_COMMAND (show_ip_neighbor_stats_cmd_node, static) = {
    1893             :   .path = "show ip neighbor-stats",
    1894             :   .function = ip_neighbor_stats_show,
    1895             :   .short_help = "show ip neighbor-stats [interface]",
    1896             : };
    1897             : /* *INDENT-ON* */
    1898             : 
    1899             : static clib_error_t *
    1900         559 : ip_neighbor_init (vlib_main_t * vm)
    1901             : {
    1902             :   {
    1903         559 :     ip4_add_del_interface_address_callback_t cb = {
    1904             :       .function = ip_neighbor_add_del_interface_address_v4,
    1905             :     };
    1906         559 :     vec_add1 (ip4_main.add_del_interface_address_callbacks, cb);
    1907             :   }
    1908             :   {
    1909         559 :     ip6_add_del_interface_address_callback_t cb = {
    1910             :       .function = ip_neighbor_add_del_interface_address_v6,
    1911             :     };
    1912         559 :     vec_add1 (ip6_main.add_del_interface_address_callbacks, cb);
    1913             :   }
    1914             :   {
    1915         559 :     ip4_table_bind_callback_t cb = {
    1916             :       .function = ip_neighbor_table_bind_v4,
    1917             :     };
    1918         559 :     vec_add1 (ip4_main.table_bind_callbacks, cb);
    1919             :   }
    1920             :   {
    1921         559 :     ip6_table_bind_callback_t cb = {
    1922             :       .function = ip_neighbor_table_bind_v6,
    1923             :     };
    1924         559 :     vec_add1 (ip6_main.table_bind_callbacks, cb);
    1925             :   }
    1926         559 :   ipn_logger = vlib_log_register_class ("ip", "neighbor");
    1927             : 
    1928             :   ip_address_family_t af;
    1929             : 
    1930        1677 :   FOR_EACH_IP_ADDRESS_FAMILY (af)
    1931        1118 :     ip_neighbor_list_head[af] =
    1932        1118 :     clib_llist_make_head (ip_neighbor_elt_pool, ipne_anchor);
    1933             : 
    1934         559 :   return (NULL);
    1935             : }
    1936             : 
    1937             : /* *INDENT-OFF* */
    1938       47039 : VLIB_INIT_FUNCTION (ip_neighbor_init) =
    1939             : {
    1940             :   .runs_after = VLIB_INITS("ip_main_init"),
    1941             : };
    1942             : /* *INDENT-ON* */
    1943             : 
    1944             : /*
    1945             :  * fd.io coding-style-patch-verification: ON
    1946             :  *
    1947             :  * Local Variables:
    1948             :  * eval: (c-set-style "gnu")
    1949             :  * End:
    1950             :  */

Generated by: LCOV version 1.14