LCOV - code coverage report
Current view: top level - vnet/adj - adj_mcast.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 100 128 78.1 %
Date: 2023-07-05 22:20:52 Functions: 20 22 90.9 %

          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_mcast.h>
      17             : #include <vnet/adj/adj_internal.h>
      18             : #include <vnet/fib/fib_walk.h>
      19             : #include <vnet/ip/ip.h>
      20             : 
      21             : /*
      22             :  * The 'DB' of all mcast adjs.
      23             :  * There is only one mcast per-interface per-protocol, so this is a per-interface
      24             :  * vector
      25             :  */
      26             : static adj_index_t *adj_mcasts[FIB_PROTOCOL_MAX];
      27             : 
      28             : static u32
      29        3886 : adj_get_mcast_node (fib_protocol_t proto)
      30             : {
      31        3886 :     switch (proto) {
      32          88 :     case FIB_PROTOCOL_IP4:
      33          88 :         return (ip4_rewrite_mcast_node.index);
      34        3798 :     case FIB_PROTOCOL_IP6:
      35        3798 :         return (ip6_rewrite_mcast_node.index);
      36           0 :     case FIB_PROTOCOL_MPLS:
      37           0 :         break;
      38             :     }
      39           0 :     ASSERT(0);
      40           0 :     return (0);
      41             : }
      42             : 
      43             : /*
      44             :  * adj_mcast_add_or_lock
      45             :  *
      46             :  * The next_hop address here is used for source address selection in the DP.
      47             :  * The mcast adj is added to an interface's connected prefix, the next-hop
      48             :  * passed here is the local prefix on the same interface.
      49             :  */
      50             : adj_index_t
      51        7283 : adj_mcast_add_or_lock (fib_protocol_t proto,
      52             :                        vnet_link_t link_type,
      53             :                        u32 sw_if_index)
      54             : {
      55             :     ip_adjacency_t * adj;
      56             : 
      57        8772 :     vec_validate_init_empty(adj_mcasts[proto], sw_if_index, ADJ_INDEX_INVALID);
      58             : 
      59        7283 :     if (ADJ_INDEX_INVALID == adj_mcasts[proto][sw_if_index])
      60             :     {
      61             :         vnet_main_t *vnm;
      62             : 
      63        1943 :         vnm = vnet_get_main();
      64        1943 :         adj = adj_alloc(proto);
      65             : 
      66        1943 :         adj->lookup_next_index = IP_LOOKUP_NEXT_MCAST;
      67        1943 :         adj->ia_nh_proto = proto;
      68        1943 :         adj->ia_link = link_type;
      69        1943 :         adj->ia_node_index = adj_get_mcast_node(proto);
      70        1943 :         adj_mcasts[proto][sw_if_index] = adj_get_index(adj);
      71        1943 :         adj_lock(adj_get_index(adj));
      72             : 
      73        1943 :         vnet_rewrite_init(vnm, sw_if_index, link_type,
      74             :                           adj->ia_node_index,
      75             :                           vnet_tx_node_index_for_sw_interface(vnm, sw_if_index),
      76             :                           &adj->rewrite_header);
      77             : 
      78             :         /*
      79             :          * we need a rewrite where the destination IP address is converted
      80             :          * to the appropriate link-layer address. This is interface specific.
      81             :          * So ask the interface to do it.
      82             :          */
      83        1943 :         vnet_update_adjacency_for_sw_interface(vnm, sw_if_index,
      84             :                                                adj_get_index(adj));
      85             : 
      86        1943 :         adj_delegate_adj_created(adj);
      87             :     }
      88             :     else
      89             :     {
      90        5340 :         adj = adj_get(adj_mcasts[proto][sw_if_index]);
      91        5340 :         adj_lock(adj_get_index(adj));
      92             :     }
      93             : 
      94        7283 :     return (adj_get_index(adj));
      95             : }
      96             : 
      97             : /**
      98             :  * adj_mcast_update_rewrite
      99             :  *
     100             :  * Update the adjacency's rewrite string. A NULL string implies the
     101             :  * rewrite is reset (i.e. when ARP/ND entry is gone).
     102             :  * NB: the adj being updated may be handling traffic in the DP.
     103             :  */
     104             : void
     105        1943 : adj_mcast_update_rewrite (adj_index_t adj_index,
     106             :                           u8 *rewrite,
     107             :                           u8 offset)
     108             : {
     109             :     ip_adjacency_t *adj;
     110             : 
     111        1943 :     ASSERT(ADJ_INDEX_INVALID != adj_index);
     112             : 
     113        1943 :     adj = adj_get(adj_index);
     114             : 
     115             :     /*
     116             :      * update the adj's rewrite string and build the arc
     117             :      * from the rewrite node to the interface's TX node
     118             :      */
     119        3886 :     adj_nbr_update_rewrite_internal(adj, IP_LOOKUP_NEXT_MCAST,
     120        1943 :                                     adj_get_mcast_node(adj->ia_nh_proto),
     121             :                                     vnet_tx_node_index_for_sw_interface(
     122        1943 :                                         vnet_get_main(),
     123             :                                         adj->rewrite_header.sw_if_index),
     124             :                                     rewrite);
     125             :     /*
     126             :      * set the offset corresponding to the mcast IP address rewrite
     127             :      */
     128        1943 :     adj->rewrite_header.dst_mcast_offset = offset;
     129        1943 : }
     130             : 
     131             : /**
     132             :  * adj_mcast_midchain_update_rewrite
     133             :  *
     134             :  * Update the adjacency's rewrite string. A NULL string implies the
     135             :  * rewrite is reset (i.e. when ARP/ND entry is gone).
     136             :  * NB: the adj being updated may be handling traffic in the DP.
     137             :  */
     138             : void
     139           0 : adj_mcast_midchain_update_rewrite (adj_index_t adj_index,
     140             :                                    adj_midchain_fixup_t fixup,
     141             :                                    const void *fixup_data,
     142             :                                    adj_flags_t flags,
     143             :                                    u8 *rewrite,
     144             :                                    u8 offset,
     145             :                                    u32 mask)
     146             : {
     147             :     ip_adjacency_t *adj;
     148             : 
     149           0 :     ASSERT(ADJ_INDEX_INVALID != adj_index);
     150             : 
     151           0 :     adj = adj_get(adj_index);
     152             : 
     153             :     /*
     154             :      * one time only update. since we don't support changing the tunnel
     155             :      * src,dst, this is all we need.
     156             :      */
     157           0 :     ASSERT(adj->lookup_next_index == IP_LOOKUP_NEXT_MCAST);
     158             :     /*
     159             :      * tunnels can always provide a rewrite.
     160             :      */
     161           0 :     ASSERT(NULL != rewrite);
     162             : 
     163           0 :     adj_midchain_setup(adj_index, fixup, fixup_data, flags);
     164             : 
     165             :     /*
     166             :      * update the adj's rewrite string and build the arc
     167             :      * from the rewrite node to the interface's TX node
     168             :      */
     169           0 :     adj_nbr_update_rewrite_internal(adj, IP_LOOKUP_NEXT_MCAST_MIDCHAIN,
     170           0 :                                     adj_get_mcast_node(adj->ia_nh_proto),
     171             :                                     vnet_tx_node_index_for_sw_interface(
     172           0 :                                         vnet_get_main(),
     173             :                                         adj->rewrite_header.sw_if_index),
     174             :                                     rewrite);
     175             : 
     176           0 :     adj->rewrite_header.dst_mcast_offset = offset;
     177           0 : }
     178             : 
     179             : void
     180        1761 : adj_mcast_remove (fib_protocol_t proto,
     181             :                   u32 sw_if_index)
     182             : {
     183        1761 :     ASSERT(sw_if_index < vec_len(adj_mcasts[proto]));
     184             : 
     185        1761 :     adj_mcasts[proto][sw_if_index] = ADJ_INDEX_INVALID;
     186        1761 : }
     187             : 
     188             : static clib_error_t *
     189       26450 : adj_mcast_interface_state_change (vnet_main_t * vnm,
     190             :                                   u32 sw_if_index,
     191             :                                   u32 flags)
     192             : {
     193             :     /*
     194             :      * for each mcast on the interface trigger a walk back to the children
     195             :      */
     196             :     fib_protocol_t proto;
     197             :     ip_adjacency_t *adj;
     198             : 
     199             : 
     200       79350 :     for (proto = FIB_PROTOCOL_IP4; proto <= FIB_PROTOCOL_IP6; proto++)
     201             :     {
     202       52900 :         if (sw_if_index >= vec_len(adj_mcasts[proto]) ||
     203        7938 :             ADJ_INDEX_INVALID == adj_mcasts[proto][sw_if_index])
     204       50402 :             continue;
     205             : 
     206        2498 :         adj = adj_get(adj_mcasts[proto][sw_if_index]);
     207             : 
     208        4996 :         fib_node_back_walk_ctx_t bw_ctx = {
     209        2498 :             .fnbw_reason = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ?
     210        2498 :                             FIB_NODE_BW_REASON_FLAG_INTERFACE_UP :
     211             :                             FIB_NODE_BW_REASON_FLAG_INTERFACE_DOWN),
     212             :         };
     213             : 
     214        2498 :         fib_walk_sync(FIB_NODE_TYPE_ADJ, adj_get_index(adj), &bw_ctx);
     215             :     }
     216             : 
     217       26450 :     return (NULL);
     218             : }
     219             : 
     220        2801 : VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION(adj_mcast_interface_state_change);
     221             : 
     222             : /**
     223             :  * @brief Invoked on each SW interface of a HW interface when the
     224             :  * HW interface state changes
     225             :  */
     226             : static walk_rc_t
     227       13182 : adj_mcast_hw_sw_interface_state_change (vnet_main_t * vnm,
     228             :                                         u32 sw_if_index,
     229             :                                         void *arg)
     230             : {
     231       13182 :     adj_mcast_interface_state_change(vnm, sw_if_index, (uword) arg);
     232             : 
     233       13182 :     return (WALK_CONTINUE);
     234             : }
     235             : 
     236             : /**
     237             :  * @brief Registered callback for HW interface state changes
     238             :  */
     239             : static clib_error_t *
     240       13092 : adj_mcast_hw_interface_state_change (vnet_main_t * vnm,
     241             :                                      u32 hw_if_index,
     242             :                                      u32 flags)
     243             : {
     244             :     /*
     245             :      * walk SW interfaces on the HW
     246             :      */
     247             :     uword sw_flags;
     248             : 
     249       13092 :     sw_flags = ((flags & VNET_HW_INTERFACE_FLAG_LINK_UP) ?
     250       13092 :                 VNET_SW_INTERFACE_FLAG_ADMIN_UP :
     251             :                 0);
     252             : 
     253       13092 :     vnet_hw_interface_walk_sw(vnm, hw_if_index,
     254             :                               adj_mcast_hw_sw_interface_state_change,
     255             :                               (void*) sw_flags);
     256             : 
     257       13092 :     return (NULL);
     258             : }
     259             : 
     260        2801 : VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION(
     261             :     adj_mcast_hw_interface_state_change);
     262             : 
     263             : static clib_error_t *
     264       11597 : adj_mcast_interface_delete (vnet_main_t * vnm,
     265             :                             u32 sw_if_index,
     266             :                             u32 is_add)
     267             : {
     268             :     /*
     269             :      * for each mcast on the interface trigger a walk back to the children
     270             :      */
     271             :     fib_protocol_t proto;
     272             :     ip_adjacency_t *adj;
     273             : 
     274       11597 :     if (is_add)
     275             :     {
     276             :         /*
     277             :          * not interested in interface additions. we will not back walk
     278             :          * to resolve paths through newly added interfaces. Why? The control
     279             :          * plane should have the brains to add interfaces first, then routes.
     280             :          * So the case where there are paths with a interface that matches
     281             :          * one just created is the case where the path resolved through an
     282             :          * interface that was deleted, and still has not been removed. The
     283             :          * new interface added, is NO GUARANTEE that the interface being
     284             :          * added now, even though it may have the same sw_if_index, is the
     285             :          * same interface that the path needs. So tough!
     286             :          * If the control plane wants these routes to resolve it needs to
     287             :          * remove and add them again.
     288             :          */
     289        7418 :         return (NULL);
     290             :     }
     291             : 
     292       12537 :     for (proto = FIB_PROTOCOL_IP4; proto <= FIB_PROTOCOL_IP6; proto++)
     293             :     {
     294        8358 :         if (sw_if_index >= vec_len(adj_mcasts[proto]) ||
     295         580 :             ADJ_INDEX_INVALID == adj_mcasts[proto][sw_if_index])
     296        8358 :             continue;
     297             : 
     298           0 :         adj = adj_get(adj_mcasts[proto][sw_if_index]);
     299             : 
     300           0 :         fib_node_back_walk_ctx_t bw_ctx = {
     301             :             .fnbw_reason =  FIB_NODE_BW_REASON_FLAG_INTERFACE_DELETE,
     302             :         };
     303             : 
     304           0 :         fib_walk_sync(FIB_NODE_TYPE_ADJ, adj_get_index(adj), &bw_ctx);
     305             :     }
     306             : 
     307        4179 :     return (NULL);
     308             : }
     309             : 
     310        3363 : VNET_SW_INTERFACE_ADD_DEL_FUNCTION(adj_mcast_interface_delete);
     311             : 
     312             : /**
     313             :  * @brief Walk the multicast Adjacencies on a given interface
     314             :  */
     315             : void
     316      274800 : adj_mcast_walk (u32 sw_if_index,
     317             :                 fib_protocol_t proto,
     318             :                 adj_walk_cb_t cb,
     319             :                 void *ctx)
     320             : {
     321      274800 :     if (vec_len(adj_mcasts[proto]) > sw_if_index)
     322             :     {
     323       42414 :         if (ADJ_INDEX_INVALID != adj_mcasts[proto][sw_if_index])
     324             :         {
     325       23002 :             cb(adj_mcasts[proto][sw_if_index], ctx);
     326             :         }
     327             :     }
     328      274800 : }
     329             : 
     330             : u8*
     331        2191 : format_adj_mcast (u8* s, va_list *ap)
     332             : {
     333        2191 :     index_t index = va_arg(*ap, index_t);
     334        2191 :     CLIB_UNUSED(u32 indent) = va_arg(*ap, u32);
     335             :     ip_adjacency_t * adj;
     336             : 
     337        2191 :     if (!adj_is_valid(index))
     338        1616 :       return format(s, "<invalid adjacency>");
     339             : 
     340         575 :     adj = adj_get(index);
     341             : 
     342         575 :     s = format(s, "%U-mcast: ",
     343         575 :                format_fib_protocol, adj->ia_nh_proto);
     344         575 :     if (adj->rewrite_header.flags & VNET_REWRITE_HAS_FEATURES)
     345           0 :         s = format(s, "[features] ");
     346         575 :     s = format (s, "%U",
     347             :                 format_vnet_rewrite,
     348             :                 &adj->rewrite_header, sizeof (adj->rewrite_data), 0);
     349             : 
     350         575 :     return (s);
     351             : }
     352             : 
     353             : u8*
     354           0 : format_adj_mcast_midchain (u8* s, va_list *ap)
     355             : {
     356           0 :     index_t index = va_arg(*ap, index_t);
     357           0 :     CLIB_UNUSED(u32 indent) = va_arg(*ap, u32);
     358           0 :     ip_adjacency_t * adj = adj_get(index);
     359             : 
     360           0 :     s = format(s, "%U-mcast-midchain: ",
     361           0 :                format_fib_protocol, adj->ia_nh_proto);
     362           0 :     s = format (s, "%U",
     363             :                 format_vnet_rewrite,
     364             :                 &adj->rewrite_header,
     365             :                 sizeof (adj->rewrite_data), 0);
     366           0 :     s = format (s, "\n%Ustacked-on:\n%U%U",
     367             :                 format_white_space, indent,
     368             :                 format_white_space, indent+2,
     369             :                 format_dpo_id, &adj->sub_type.midchain.next_dpo, indent+2);
     370             : 
     371           0 :     return (s);
     372             : }
     373             : 
     374             : 
     375             : static void
     376       16766 : adj_dpo_lock (dpo_id_t *dpo)
     377             : {
     378       16766 :     adj_lock(dpo->dpoi_index);
     379       16766 : }
     380             : static void
     381       14262 : adj_dpo_unlock (dpo_id_t *dpo)
     382             : {
     383       14262 :     adj_unlock(dpo->dpoi_index);
     384       14262 : }
     385             : 
     386             : const static dpo_vft_t adj_mcast_dpo_vft = {
     387             :     .dv_lock = adj_dpo_lock,
     388             :     .dv_unlock = adj_dpo_unlock,
     389             :     .dv_format = format_adj_mcast,
     390             :     .dv_get_urpf = adj_dpo_get_urpf,
     391             :     .dv_get_mtu = adj_dpo_get_mtu,
     392             : };
     393             : const static dpo_vft_t adj_mcast_midchain_dpo_vft = {
     394             :     .dv_lock = adj_dpo_lock,
     395             :     .dv_unlock = adj_dpo_unlock,
     396             :     .dv_format = format_adj_mcast_midchain,
     397             :     .dv_get_urpf = adj_dpo_get_urpf,
     398             :     .dv_get_mtu = adj_dpo_get_mtu,
     399             : };
     400             : 
     401             : /**
     402             :  * @brief The per-protocol VLIB graph nodes that are assigned to a mcast
     403             :  *        object.
     404             :  *
     405             :  * this means that these graph nodes are ones from which a mcast is the
     406             :  * parent object in the DPO-graph.
     407             :  */
     408             : const static char* const adj_mcast_ip4_nodes[] =
     409             : {
     410             :     "ip4-rewrite-mcast",
     411             :     NULL,
     412             : };
     413             : const static char* const adj_mcast_ip6_nodes[] =
     414             : {
     415             :     "ip6-rewrite-mcast",
     416             :     NULL,
     417             : };
     418             : 
     419             : const static char* const * const adj_mcast_nodes[DPO_PROTO_NUM] =
     420             : {
     421             :     [DPO_PROTO_IP4]  = adj_mcast_ip4_nodes,
     422             :     [DPO_PROTO_IP6]  = adj_mcast_ip6_nodes,
     423             :     [DPO_PROTO_MPLS] = NULL,
     424             : };
     425             : 
     426             : /**
     427             :  * @brief The per-protocol VLIB graph nodes that are assigned to a mcast
     428             :  *        object.
     429             :  *
     430             :  * this means that these graph nodes are ones from which a mcast is the
     431             :  * parent object in the DPO-graph.
     432             :  */
     433             : const static char* const adj_mcast_midchain_ip4_nodes[] =
     434             : {
     435             :     "ip4-mcast-midchain",
     436             :     NULL,
     437             : };
     438             : const static char* const adj_mcast_midchain_ip6_nodes[] =
     439             : {
     440             :     "ip6-mcast-midchain",
     441             :     NULL,
     442             : };
     443             : 
     444             : const static char* const * const adj_mcast_midchain_nodes[DPO_PROTO_NUM] =
     445             : {
     446             :     [DPO_PROTO_IP4]  = adj_mcast_midchain_ip4_nodes,
     447             :     [DPO_PROTO_IP6]  = adj_mcast_midchain_ip6_nodes,
     448             :     [DPO_PROTO_MPLS] = NULL,
     449             : };
     450             : 
     451             : /**
     452             :  * @brief Return the size of the adj DB.
     453             :  * This is only for testing purposes so an efficient implementation is not needed
     454             :  */
     455             : u32
     456           6 : adj_mcast_db_size (void)
     457             : {
     458             :     u32 n_adjs, sw_if_index;
     459             :     fib_protocol_t proto;
     460             : 
     461           6 :     n_adjs = 0;
     462          18 :     for (proto = FIB_PROTOCOL_IP4; proto <= FIB_PROTOCOL_IP6; proto++)
     463             :     {
     464          74 :         for (sw_if_index = 0;
     465          62 :              sw_if_index < vec_len(adj_mcasts[proto]);
     466          50 :              sw_if_index++)
     467             :         {
     468          50 :             if (ADJ_INDEX_INVALID != adj_mcasts[proto][sw_if_index])
     469             :             {
     470           6 :                 n_adjs++;
     471             :             }
     472             :         }
     473             :     }
     474             :     
     475           6 :     return (n_adjs);
     476             : }
     477             : 
     478             : void
     479         559 : adj_mcast_module_init (void)
     480             : {
     481         559 :     dpo_register(DPO_ADJACENCY_MCAST,
     482             :                  &adj_mcast_dpo_vft,
     483             :                  adj_mcast_nodes);
     484         559 :     dpo_register(DPO_ADJACENCY_MCAST_MIDCHAIN,
     485             :                  &adj_mcast_midchain_dpo_vft,
     486             :                  adj_mcast_midchain_nodes);
     487         559 : }

Generated by: LCOV version 1.14