LCOV - code coverage report
Current view: top level - vnet/adj - adj_glean.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 168 176 95.5 %
Date: 2023-10-26 01:39:38 Functions: 32 32 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2016 Cisco and/or its affiliates.
       3             :  * Licensed under the Apache License, Version 2.0 (the "License");
       4             :  * you may not use this file except in compliance with the License.
       5             :  * You may obtain a copy of the License at:
       6             :  *
       7             :  *     http://www.apache.org/licenses/LICENSE-2.0
       8             :  *
       9             :  * Unless required by applicable law or agreed to in writing, software
      10             :  * distributed under the License is distributed on an "AS IS" BASIS,
      11             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             :  * See the License for the specific language governing permissions and
      13             :  * limitations under the License.
      14             :  */
      15             : 
      16             : #include <vnet/adj/adj.h>
      17             : #include <vnet/adj/adj_internal.h>
      18             : #include <vnet/fib/fib_walk.h>
      19             : 
      20             : /*
      21             :  * The 'DB' of all glean adjs.
      22             :  * There is one glean per-{interface, protocol, connected prefix}
      23             :  */
      24             : static uword **adj_gleans[FIB_PROTOCOL_IP_MAX];
      25             : 
      26             : static inline u32
      27        8527 : adj_get_glean_node (fib_protocol_t proto)
      28             : {
      29        8527 :     switch (proto) {
      30        4623 :     case FIB_PROTOCOL_IP4:
      31        4623 :         return (ip4_glean_node.index);
      32        3904 :     case FIB_PROTOCOL_IP6:
      33        3904 :         return (ip6_glean_node.index);
      34           0 :     case FIB_PROTOCOL_MPLS:
      35           0 :         break;
      36             :     }
      37           0 :     ASSERT(0);
      38           0 :     return (~0);
      39             : }
      40             : 
      41             : static adj_index_t
      42       42601 : adj_glean_db_lookup (fib_protocol_t proto,
      43             :                      u32 sw_if_index,
      44             :                      const ip46_address_t *nh_addr)
      45             : {
      46             :     uword *p;
      47             : 
      48       42601 :     if (vec_len(adj_gleans[proto]) <= sw_if_index)
      49        2344 :         return (ADJ_INDEX_INVALID);
      50             : 
      51       40257 :     p = hash_get_mem (adj_gleans[proto][sw_if_index], nh_addr);
      52             : 
      53       40257 :     if (p)
      54       34074 :         return (p[0]);
      55             : 
      56        6183 :     return (ADJ_INDEX_INVALID);
      57             : }
      58             : 
      59             : static void
      60        8527 : adj_glean_db_insert (fib_protocol_t proto,
      61             :                      u32 sw_if_index,
      62             :                      const ip46_address_t *nh_addr,
      63             :                      adj_index_t ai)
      64             : {
      65        8527 :     vlib_main_t *vm = vlib_get_main();
      66             : 
      67        8527 :     vlib_worker_thread_barrier_sync(vm);
      68             : 
      69        8527 :     vec_validate(adj_gleans[proto], sw_if_index);
      70             : 
      71        8527 :     if (NULL == adj_gleans[proto][sw_if_index])
      72             :     {
      73        4247 :         adj_gleans[proto][sw_if_index] =
      74        4247 :             hash_create_mem (0, sizeof(ip46_address_t), sizeof(adj_index_t));
      75             :     }
      76             : 
      77        8527 :     hash_set_mem_alloc (&adj_gleans[proto][sw_if_index],
      78             :                         nh_addr, ai);
      79             : 
      80        8527 :     vlib_worker_thread_barrier_release(vm);
      81        8527 : }
      82             : 
      83             : static void
      84        7714 : adj_glean_db_remove (fib_protocol_t proto,
      85             :                      u32 sw_if_index,
      86             :                      const ip46_address_t *nh_addr)
      87             : {
      88        7714 :     vlib_main_t *vm = vlib_get_main();
      89             : 
      90        7714 :     vlib_worker_thread_barrier_sync(vm);
      91             : 
      92        7714 :     ASSERT(ADJ_INDEX_INVALID != adj_glean_db_lookup(proto, sw_if_index, nh_addr));
      93        7714 :     hash_unset_mem_free (&adj_gleans[proto][sw_if_index],
      94             :                          nh_addr);
      95             : 
      96        7714 :     if (0 == hash_elts(adj_gleans[proto][sw_if_index]))
      97             :     {
      98        3831 :         hash_free(adj_gleans[proto][sw_if_index]);
      99        3831 :         adj_gleans[proto][sw_if_index] = NULL;
     100             :     }
     101        7714 :     vlib_worker_thread_barrier_release(vm);
     102        7714 : }
     103             : 
     104             : /*
     105             :  * adj_glean_add_or_lock
     106             :  *
     107             :  * The next_hop address here is used for source address selection in the DP.
     108             :  * The glean adj is added to an interface's connected prefix, the next-hop
     109             :  * passed here is the local prefix on the same interface.
     110             :  */
     111             : adj_index_t
     112       34887 : adj_glean_add_or_lock (fib_protocol_t proto,
     113             :                        vnet_link_t linkt,
     114             :                        u32 sw_if_index,
     115             :                        const fib_prefix_t *conn)
     116             : {
     117             :     ip_adjacency_t * adj;
     118             :     adj_index_t ai;
     119             : 
     120       34887 :     ai = adj_glean_db_lookup(proto, sw_if_index, &conn->fp_addr);
     121             : 
     122       34887 :     if (ADJ_INDEX_INVALID == ai)
     123             :     {
     124        8527 :         adj = adj_alloc(proto);
     125             : 
     126        8527 :         adj->lookup_next_index = IP_LOOKUP_NEXT_GLEAN;
     127        8527 :         adj->ia_nh_proto = proto;
     128        8527 :         adj->ia_link = linkt;
     129        8527 :         adj->ia_node_index = adj_get_glean_node(proto);
     130        8527 :         ai = adj_get_index(adj);
     131        8527 :         adj_lock(ai);
     132             : 
     133        8527 :         ASSERT(conn);
     134        8527 :         fib_prefix_normalize(conn, &adj->sub_type.glean.rx_pfx);
     135        8527 :         adj->rewrite_header.sw_if_index = sw_if_index;
     136        8527 :         adj->rewrite_header.data_bytes = 0;
     137        8527 :         adj->rewrite_header.max_l3_packet_bytes =
     138        8527 :           vnet_sw_interface_get_mtu(vnet_get_main(), sw_if_index,
     139             :                                     vnet_link_to_mtu(linkt));
     140             : 
     141        8527 :         vnet_update_adjacency_for_sw_interface(vnet_get_main(),
     142             :                                                sw_if_index,
     143             :                                                ai);
     144             : 
     145        8527 :         adj_glean_db_insert(proto, sw_if_index,
     146        8527 :                             &adj->sub_type.glean.rx_pfx.fp_addr, ai);
     147             :     }
     148             :     else
     149             :     {
     150       26360 :         adj = adj_get(ai);
     151       26360 :         adj_lock(ai);
     152             :     }
     153             : 
     154       34887 :     adj_delegate_adj_created(adj);
     155             : 
     156       34887 :     return (ai);
     157             : }
     158             : 
     159             : /**
     160             :  * adj_glean_update_rewrite
     161             :  */
     162             : void
     163        8543 : adj_glean_update_rewrite (adj_index_t adj_index)
     164             : {
     165             :     ip_adjacency_t *adj;
     166             : 
     167        8543 :     ASSERT(ADJ_INDEX_INVALID != adj_index);
     168             : 
     169        8543 :     adj = adj_get(adj_index);
     170             : 
     171        8543 :     vnet_rewrite_for_sw_interface(vnet_get_main(),
     172        8543 :                                   adj_fib_proto_2_nd(adj->ia_nh_proto),
     173             :                                   adj->rewrite_header.sw_if_index,
     174             :                                   adj->ia_node_index,
     175             :                                   VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST,
     176             :                                   &adj->rewrite_header,
     177             :                                   sizeof (adj->rewrite_data));
     178        8543 : }
     179             : 
     180             : static adj_walk_rc_t
     181          16 : adj_glean_update_rewrite_walk (adj_index_t ai,
     182             :                                void *data)
     183             : {
     184          16 :     adj_glean_update_rewrite(ai);
     185             : 
     186          16 :     return (ADJ_WALK_RC_CONTINUE);
     187             : }
     188             : 
     189             : static void
     190       64169 : adj_glean_walk_proto (fib_protocol_t proto,
     191             :                       u32 sw_if_index,
     192             :                       adj_walk_cb_t cb,
     193             :                       void *data)
     194             : {
     195       64169 :     adj_index_t ai, *aip, *ais = NULL;
     196             :     ip46_address_t *conn;
     197             : 
     198       64169 :     if (vec_len(adj_gleans[proto]) <= sw_if_index ||
     199       19335 :         NULL == adj_gleans[proto][sw_if_index])
     200       58984 :         return;
     201             : 
     202             :     /*
     203             :      * Walk first to collect the indices
     204             :      * then walk the collection. This is safe
     205             :      * to modifications of the hash table
     206             :      */
     207      344617 :     hash_foreach_mem(conn, ai, adj_gleans[proto][sw_if_index],
     208             :     ({
     209             :         vec_add1(ais, ai);
     210             :     }));
     211             : 
     212       12777 :     vec_foreach(aip, ais)
     213             :     {
     214        7592 :         if (ADJ_WALK_RC_STOP == cb(*aip, data))
     215           0 :             return;
     216             :     }
     217        5185 :     vec_free(ais);
     218             : }
     219             : 
     220             : void
     221       31223 : adj_glean_walk (u32 sw_if_index,
     222             :                 adj_walk_cb_t cb,
     223             :                 void *data)
     224             : {
     225             :     fib_protocol_t proto;
     226             : 
     227       93669 :     FOR_EACH_FIB_IP_PROTOCOL(proto)
     228             :     {
     229       62446 :       adj_glean_walk_proto (proto, sw_if_index, cb, data);
     230             :     }
     231       31223 : }
     232             : 
     233             : adj_index_t
     234         810 : adj_glean_get (fib_protocol_t proto,
     235             :                u32 sw_if_index,
     236             :                const ip46_address_t *nh)
     237             : {
     238         810 :     if (NULL != nh)
     239             :     {
     240           0 :         return adj_glean_db_lookup(proto, sw_if_index, nh);
     241             :     }
     242             :     else
     243             :     {
     244             :         ip46_address_t *conn;
     245             :         adj_index_t ai;
     246             : 
     247         810 :         if (vec_len(adj_gleans[proto]) <= sw_if_index ||
     248         801 :             NULL == adj_gleans[proto][sw_if_index])
     249          31 :             return (ADJ_INDEX_INVALID);
     250             : 
     251       21709 :         hash_foreach_mem(conn, ai, adj_gleans[proto][sw_if_index],
     252             :         ({
     253             :             return (ai);
     254             :         }));
     255             :     }
     256           0 :     return (ADJ_INDEX_INVALID);
     257             : }
     258             : 
     259             : const ip46_address_t *
     260        1173 : adj_glean_get_src (fib_protocol_t proto,
     261             :                    u32 sw_if_index,
     262             :                    const ip46_address_t *nh)
     263             : {
     264             :     const ip46_address_t *conn, *source;
     265             :     const ip_adjacency_t *adj;
     266             :     adj_index_t ai;
     267             : 
     268        1173 :     if (vec_len(adj_gleans[proto]) <= sw_if_index ||
     269        1173 :         NULL == adj_gleans[proto][sw_if_index])
     270           4 :         return (NULL);
     271             : 
     272        2338 :     fib_prefix_t pfx = {
     273        1169 :         .fp_len = fib_prefix_get_host_length(proto),
     274             :         .fp_proto = proto,
     275             :     };
     276             : 
     277        1169 :     if (nh)
     278        1101 :         pfx.fp_addr = *nh;
     279             : 
     280             :     /*
     281             :      * An interface can have more than one glean address. Where
     282             :      * possible we want to return a source address from the same
     283             :      * subnet as the destination. If this is not possible then any address
     284             :      * will do.
     285             :      */
     286        1169 :     source = NULL;
     287             : 
     288       58337 :     hash_foreach_mem(conn, ai, adj_gleans[proto][sw_if_index],
     289             :     ({
     290             :         adj = adj_get(ai);
     291             : 
     292             :         if (adj->sub_type.glean.rx_pfx.fp_len > 0)
     293             :         {
     294             :             source = &adj->sub_type.glean.rx_pfx.fp_addr;
     295             : 
     296             :             /* if no destination is specified use the just glean */
     297             :             if (NULL == nh)
     298             :                 return (source);
     299             : 
     300             :             /* check the clean covers the desintation */
     301             :             if (fib_prefix_is_cover(&adj->sub_type.glean.rx_pfx, &pfx))
     302             :                 return (source);
     303             :         }
     304             :     }));
     305             : 
     306          63 :     return (source);
     307             : }
     308             : 
     309             : void
     310        7714 : adj_glean_remove (ip_adjacency_t *adj)
     311             : {
     312             :     fib_prefix_t norm;
     313             : 
     314        7714 :     fib_prefix_normalize(&adj->sub_type.glean.rx_pfx,
     315             :                          &norm);
     316        7714 :     adj_glean_db_remove(adj->ia_nh_proto,
     317             :                         adj->rewrite_header.sw_if_index,
     318             :                         &norm.fp_addr);
     319        7714 : }
     320             : 
     321             : static adj_walk_rc_t
     322        7576 : adj_glean_start_backwalk (adj_index_t ai,
     323             :                           void *data)
     324             : {
     325        7576 :     fib_node_back_walk_ctx_t bw_ctx = *(fib_node_back_walk_ctx_t*) data;
     326             : 
     327        7576 :     fib_walk_sync(FIB_NODE_TYPE_ADJ, ai, &bw_ctx);
     328             : 
     329        7576 :     return (ADJ_WALK_RC_CONTINUE);
     330             : }
     331             : 
     332             : static clib_error_t *
     333       26940 : adj_glean_interface_state_change (vnet_main_t * vnm,
     334             :                                   u32 sw_if_index,
     335             :                                   u32 flags)
     336             : {
     337             :     /*
     338             :      * for each glean on the interface trigger a walk back to the children
     339             :      */
     340       53880 :     fib_node_back_walk_ctx_t bw_ctx = {
     341       26940 :         .fnbw_reason = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ?
     342       26940 :                         FIB_NODE_BW_REASON_FLAG_INTERFACE_UP :
     343             :                         FIB_NODE_BW_REASON_FLAG_INTERFACE_DOWN),
     344             :     };
     345             : 
     346       26940 :     adj_glean_walk (sw_if_index, adj_glean_start_backwalk, &bw_ctx);
     347             : 
     348       26940 :     return (NULL);
     349             : }
     350             : 
     351        2881 : VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION(adj_glean_interface_state_change);
     352             : 
     353             : /**
     354             :  * @brief Invoked on each SW interface of a HW interface when the
     355             :  * HW interface state changes
     356             :  */
     357             : static walk_rc_t
     358       13426 : adj_nbr_hw_sw_interface_state_change (vnet_main_t * vnm,
     359             :                                       u32 sw_if_index,
     360             :                                       void *arg)
     361             : {
     362       13426 :     adj_glean_interface_state_change(vnm, sw_if_index, (uword) arg);
     363             : 
     364       13426 :     return (WALK_CONTINUE);
     365             : }
     366             : 
     367             : /**
     368             :  * @brief Registered callback for HW interface state changes
     369             :  */
     370             : static clib_error_t *
     371       13336 : adj_glean_hw_interface_state_change (vnet_main_t * vnm,
     372             :                                      u32 hw_if_index,
     373             :                                      u32 flags)
     374             : {
     375             :     /*
     376             :      * walk SW interfaces on the HW
     377             :      */
     378             :     uword sw_flags;
     379             : 
     380       13336 :     sw_flags = ((flags & VNET_HW_INTERFACE_FLAG_LINK_UP) ?
     381       13336 :                 VNET_SW_INTERFACE_FLAG_ADMIN_UP :
     382             :                 0);
     383             : 
     384       13336 :     vnet_hw_interface_walk_sw(vnm, hw_if_index,
     385             :                               adj_nbr_hw_sw_interface_state_change,
     386             :                               (void*) sw_flags);
     387             : 
     388       13336 :     return (NULL);
     389             : }
     390             : 
     391        2881 : VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION(
     392             :     adj_glean_hw_interface_state_change);
     393             : 
     394             : static clib_error_t *
     395       11798 : adj_glean_interface_delete (vnet_main_t * vnm,
     396             :                             u32 sw_if_index,
     397             :                             u32 is_add)
     398             : {
     399       11798 :     if (is_add)
     400             :     {
     401             :         /*
     402             :          * not interested in interface additions. we will not back walk
     403             :          * to resolve paths through newly added interfaces. Why? The control
     404             :          * plane should have the brains to add interfaces first, then routes.
     405             :          * So the case where there are paths with a interface that matches
     406             :          * one just created is the case where the path resolved through an
     407             :          * interface that was deleted, and still has not been removed. The
     408             :          * new interface added, is NO GUARANTEE that the interface being
     409             :          * added now, even though it may have the same sw_if_index, is the
     410             :          * same interface that the path needs. So tough!
     411             :          * If the control plane wants these routes to resolve it needs to
     412             :          * remove and add them again.
     413             :          */
     414        7547 :         return (NULL);
     415             :     }
     416             : 
     417             :     /*
     418             :      * for each glean on the interface trigger a walk back to the children
     419             :      */
     420        4251 :     fib_node_back_walk_ctx_t bw_ctx = {
     421             :         .fnbw_reason =  FIB_NODE_BW_REASON_FLAG_INTERFACE_DELETE,
     422             :     };
     423             : 
     424        4251 :     adj_glean_walk (sw_if_index, adj_glean_start_backwalk, &bw_ctx);
     425             : 
     426        4251 :     return (NULL);
     427             : }
     428             : 
     429        3459 : VNET_SW_INTERFACE_ADD_DEL_FUNCTION(adj_glean_interface_delete);
     430             : 
     431             : /**
     432             :  * Callback function invoked when an interface's MAC Address changes
     433             :  */
     434             : static void
     435          32 : adj_glean_ethernet_change_mac (ethernet_main_t * em,
     436             :                                u32 sw_if_index,
     437             :                                uword opaque)
     438             : {
     439          32 :     adj_glean_walk (sw_if_index, adj_glean_update_rewrite_walk, NULL);
     440          32 : }
     441             : 
     442             : static void
     443        1723 : adj_glean_table_bind (fib_protocol_t fproto,
     444             :                       u32 sw_if_index,
     445             :                       u32 itf_fib_index)
     446             : {
     447             :     /*
     448             :      * for each glean on the interface trigger a walk back to the children
     449             :      */
     450        1723 :     fib_node_back_walk_ctx_t bw_ctx = {
     451             :         .fnbw_reason =  FIB_NODE_BW_REASON_FLAG_INTERFACE_BIND,
     452             :         .interface_bind = {
     453             :             .fnbw_to_fib_index = itf_fib_index,
     454             :         },
     455             :     };
     456             : 
     457        1723 :     adj_glean_walk_proto (fproto, sw_if_index, adj_glean_start_backwalk, &bw_ctx);
     458        1723 : }
     459             : 
     460             : 
     461             : /**
     462             :  * Callback function invoked when an interface's IPv6 Table
     463             :  * binding changes
     464             :  */
     465             : static void
     466         777 : adj_glean_ip6_table_bind (ip6_main_t * im,
     467             :                           uword opaque,
     468             :                           u32 sw_if_index,
     469             :                           u32 new_fib_index,
     470             :                           u32 old_fib_index)
     471             : {
     472         777 :   adj_glean_table_bind (FIB_PROTOCOL_IP6, sw_if_index, new_fib_index);
     473         777 : }
     474             : 
     475             : /**
     476             :  * Callback function invoked when an interface's IPv4 Table
     477             :  * binding changes
     478             :  */
     479             : static void
     480         946 : adj_glean_ip4_table_bind (ip4_main_t * im,
     481             :                           uword opaque,
     482             :                           u32 sw_if_index,
     483             :                           u32 new_fib_index,
     484             :                           u32 old_fib_index)
     485             : {
     486         946 :   adj_glean_table_bind (FIB_PROTOCOL_IP4, sw_if_index, new_fib_index);
     487         946 : }
     488             : 
     489             : u8*
     490         608 : format_adj_glean (u8* s, va_list *ap)
     491             : {
     492         608 :     index_t index = va_arg(*ap, index_t);
     493         608 :     CLIB_UNUSED(u32 indent) = va_arg(*ap, u32);
     494         608 :     ip_adjacency_t * adj = adj_get(index);
     495             : 
     496         608 :     s = format(s, "%U-glean: [src:%U] %U",
     497         608 :                format_fib_protocol, adj->ia_nh_proto,
     498             :                format_fib_prefix, &adj->sub_type.glean.rx_pfx,
     499             :                format_vnet_rewrite,
     500             :                &adj->rewrite_header, sizeof (adj->rewrite_data), 0);
     501             : 
     502         608 :     return (s);
     503             : }
     504             : 
     505             : u32
     506           1 : adj_glean_db_size (void)
     507             : {
     508             :     fib_protocol_t proto;
     509           1 :     u32 sw_if_index = 0;
     510           1 :     u64 count = 0;
     511             : 
     512           3 :     FOR_EACH_FIB_IP_PROTOCOL(proto)
     513             :     {
     514           7 :         vec_foreach_index(sw_if_index, adj_gleans[proto])
     515             :         {
     516           5 :             if (NULL != adj_gleans[proto][sw_if_index])
     517             :             {
     518           0 :                 count += hash_elts(adj_gleans[proto][sw_if_index]);
     519             :             }
     520             :         }
     521             :     }
     522           1 :     return (count);
     523             : }
     524             : 
     525             : static void
     526      134105 : adj_dpo_lock (dpo_id_t *dpo)
     527             : {
     528      134105 :     adj_lock(dpo->dpoi_index);
     529      134105 : }
     530             : static void
     531      131808 : adj_dpo_unlock (dpo_id_t *dpo)
     532             : {
     533      131808 :     adj_unlock(dpo->dpoi_index);
     534      131808 : }
     535             : 
     536             : const static dpo_vft_t adj_glean_dpo_vft = {
     537             :     .dv_lock = adj_dpo_lock,
     538             :     .dv_unlock = adj_dpo_unlock,
     539             :     .dv_format = format_adj_glean,
     540             :     .dv_get_urpf = adj_dpo_get_urpf,
     541             :     .dv_get_mtu = adj_dpo_get_mtu,
     542             : };
     543             : 
     544             : /**
     545             :  * @brief The per-protocol VLIB graph nodes that are assigned to a glean
     546             :  *        object.
     547             :  *
     548             :  * this means that these graph nodes are ones from which a glean is the
     549             :  * parent object in the DPO-graph.
     550             :  */
     551             : const static char* const glean_ip4_nodes[] =
     552             : {
     553             :     "ip4-glean",
     554             :     NULL,
     555             : };
     556             : const static char* const glean_ip6_nodes[] =
     557             : {
     558             :     "ip6-glean",
     559             :     NULL,
     560             : };
     561             : 
     562             : const static char* const * const glean_nodes[DPO_PROTO_NUM] =
     563             : {
     564             :     [DPO_PROTO_IP4]  = glean_ip4_nodes,
     565             :     [DPO_PROTO_IP6]  = glean_ip6_nodes,
     566             :     [DPO_PROTO_MPLS] = NULL,
     567             : };
     568             : 
     569             : void
     570         575 : adj_glean_module_init (void)
     571             : {
     572         575 :     dpo_register(DPO_ADJACENCY_GLEAN, &adj_glean_dpo_vft, glean_nodes);
     573             : 
     574         575 :     ethernet_address_change_ctx_t ctx = {
     575             :         .function = adj_glean_ethernet_change_mac,
     576             :         .function_opaque = 0,
     577             :     };
     578         575 :     vec_add1 (ethernet_main.address_change_callbacks, ctx);
     579             : 
     580         575 :     ip6_table_bind_callback_t cbt6 = {
     581             :         .function = adj_glean_ip6_table_bind,
     582             :     };
     583         575 :     vec_add1 (ip6_main.table_bind_callbacks, cbt6);
     584             : 
     585         575 :     ip4_table_bind_callback_t cbt4 = {
     586             :         .function = adj_glean_ip4_table_bind,
     587             :     };
     588         575 :     vec_add1 (ip4_main.table_bind_callbacks, cbt4);
     589         575 : }

Generated by: LCOV version 1.14