LCOV - code coverage report
Current view: top level - vnet/adj - adj_midchain.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 155 171 90.6 %
Date: 2023-10-26 01:39:38 Functions: 18 18 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_nbr.h>
      17             : #include <vnet/adj/adj_internal.h>
      18             : #include <vnet/adj/adj_l2.h>
      19             : #include <vnet/adj/adj_nsh.h>
      20             : #include <vnet/adj/adj_midchain.h>
      21             : #include <vnet/dpo/drop_dpo.h>
      22             : #include <vnet/dpo/load_balance.h>
      23             : #include <vnet/fib/fib_walk.h>
      24             : #include <vnet/fib/fib_entry.h>
      25             : #include <vnet/ip/ip4_inlines.h>
      26             : #include <vnet/ip/ip6_inlines.h>
      27             : 
      28             : u8
      29       47098 : adj_is_midchain (adj_index_t ai)
      30             : {
      31             :     ip_adjacency_t *adj;
      32             : 
      33       47098 :     adj = adj_get(ai);
      34             : 
      35       47098 :     switch (adj->lookup_next_index)
      36             :     {
      37         463 :     case IP_LOOKUP_NEXT_MIDCHAIN:
      38             :     case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
      39         463 :         return (1);
      40       46635 :     case IP_LOOKUP_NEXT_ARP:
      41             :     case IP_LOOKUP_NEXT_GLEAN:
      42             :     case IP_LOOKUP_NEXT_BCAST:
      43             :     case IP_LOOKUP_NEXT_MCAST:
      44             :     case IP_LOOKUP_NEXT_DROP:
      45             :     case IP_LOOKUP_NEXT_PUNT:
      46             :     case IP_LOOKUP_NEXT_LOCAL:
      47             :     case IP_LOOKUP_NEXT_REWRITE:
      48             :     case IP_LOOKUP_NEXT_ICMP_ERROR:
      49             :     case IP_LOOKUP_N_NEXT:
      50       46635 :         return (0);
      51             :     }
      52             : 
      53           0 :     return (0);
      54             : }
      55             : 
      56             : static inline u32
      57         671 : adj_get_midchain_node (vnet_link_t link)
      58             : {
      59         671 :     switch (link) {
      60         419 :     case VNET_LINK_IP4:
      61         419 :         return (ip4_midchain_node.index);
      62         227 :     case VNET_LINK_IP6:
      63         227 :         return (ip6_midchain_node.index);
      64           9 :     case VNET_LINK_MPLS:
      65           9 :         return (mpls_midchain_node.index);
      66          16 :     case VNET_LINK_ETHERNET:
      67          16 :         return (adj_l2_midchain_node.index);
      68           0 :     case VNET_LINK_NSH:
      69           0 :         return (adj_nsh_midchain_node.index);
      70           0 :     case VNET_LINK_ARP:
      71           0 :         break;
      72             :     }
      73           0 :     ASSERT(0);
      74           0 :     return (0);
      75             : }
      76             : 
      77             : static u8
      78         972 : adj_midchain_get_feature_arc_index (const ip_adjacency_t *adj)
      79             : {
      80         972 :     switch (adj->ia_link)
      81             :     {
      82         573 :     case VNET_LINK_IP4:
      83         573 :         return ip4_main.lookup_main.output_feature_arc_index;
      84         360 :     case VNET_LINK_IP6:
      85         360 :         return ip6_main.lookup_main.output_feature_arc_index;
      86          11 :     case VNET_LINK_MPLS:
      87          11 :         return mpls_main.output_feature_arc_index;
      88          28 :     case VNET_LINK_ETHERNET:
      89          28 :         return ethernet_main.output_feature_arc_index;
      90           0 :     case VNET_LINK_NSH:
      91             :     case VNET_LINK_ARP:
      92           0 :         break;
      93             :     }
      94           0 :     ASSERT (0);
      95           0 :     return (0);
      96             : }
      97             : 
      98             : static u32
      99        1236 : adj_nbr_midchain_get_tx_node (ip_adjacency_t *adj)
     100             : {
     101        1236 :     return (adj_midchain_tx.index);
     102             : }
     103             : 
     104             : static u32
     105         972 : adj_nbr_midchain_get_next_node (ip_adjacency_t *adj)
     106             : {
     107         972 :     return (vnet_feature_get_end_node(adj_midchain_get_feature_arc_index(adj),
     108             :                                       adj->rewrite_header.sw_if_index));
     109             : }
     110             : 
     111             : /**
     112             :  * adj_midchain_setup
     113             :  *
     114             :  * Setup the adj as a mid-chain
     115             :  */
     116             : void
     117         477 : adj_midchain_teardown (ip_adjacency_t *adj)
     118             : {
     119         477 :     dpo_reset(&adj->sub_type.midchain.next_dpo);
     120         477 : }
     121             : 
     122             : /**
     123             :  * adj_midchain_setup
     124             :  *
     125             :  * Setup the adj as a mid-chain
     126             :  */
     127             : void
     128         479 : adj_midchain_setup (adj_index_t adj_index,
     129             :                     adj_midchain_fixup_t fixup,
     130             :                     const void *data,
     131             :                     adj_flags_t flags)
     132             : {
     133             :     ip_adjacency_t *adj;
     134             : 
     135         479 :     ASSERT(ADJ_INDEX_INVALID != adj_index);
     136             : 
     137         479 :     adj = adj_get(adj_index);
     138             : 
     139         479 :     adj->sub_type.midchain.fixup_func = fixup;
     140         479 :     adj->sub_type.midchain.fixup_data = data;
     141         479 :     adj->sub_type.midchain.fei = FIB_NODE_INDEX_INVALID;
     142         479 :     adj->ia_flags |= flags;
     143             : 
     144         479 :     if (flags & ADJ_FLAG_MIDCHAIN_FIXUP_IP4O4_HDR)
     145             :     {
     146          54 :         adj->rewrite_header.flags |= VNET_REWRITE_FIXUP_IP4_O_4;
     147             :     }
     148             :     else
     149             :     {
     150         425 :         adj->rewrite_header.flags &= ~VNET_REWRITE_FIXUP_IP4_O_4;
     151             :     }
     152         479 :     if (!(flags & ADJ_FLAG_MIDCHAIN_FIXUP_FLOW_HASH))
     153             :     {
     154         479 :         adj->rewrite_header.flags &= ~VNET_REWRITE_FIXUP_FLOW_HASH;
     155             :     }
     156             : 
     157             :     /*
     158             :      * stack the midchain on the drop so it's ready to forward in the adj-midchain-tx.
     159             :      * The graph arc used/created here is from the midchain-tx node to the
     160             :      * child's registered node. This is because post adj processing the next
     161             :      * node are any output features, then the midchain-tx.  from there we
     162             :      * need to get to the stacked child's node.
     163             :      */
     164         479 :     dpo_stack_from_node(adj_nbr_midchain_get_tx_node(adj),
     165             :                         &adj->sub_type.midchain.next_dpo,
     166         479 :                         drop_dpo_get(vnet_link_to_dpo_proto(adj->ia_link)));
     167         479 : }
     168             : 
     169             : /**
     170             :  * adj_nbr_midchain_update_rewrite
     171             :  *
     172             :  * Update the adjacency's rewrite string. A NULL string implies the
     173             :  * rewrite is reset (i.e. when ARP/ND entry is gone).
     174             :  * NB: the adj being updated may be handling traffic in the DP.
     175             :  */
     176             : void
     177         671 : adj_nbr_midchain_update_rewrite (adj_index_t adj_index,
     178             :                                  adj_midchain_fixup_t fixup,
     179             :                                  const void *fixup_data,
     180             :                                  adj_flags_t flags,
     181             :                                  u8 *rewrite)
     182             : {
     183             :     ip_adjacency_t *adj;
     184             : 
     185         671 :     ASSERT(ADJ_INDEX_INVALID != adj_index);
     186             : 
     187         671 :     adj = adj_get(adj_index);
     188             : 
     189             :     /*
     190             :      * one time only update. since we don't support changing the tunnel
     191             :      * src,dst, this is all we need.
     192             :      */
     193         671 :     if (adj->lookup_next_index != IP_LOOKUP_NEXT_MIDCHAIN &&
     194         479 :         adj->lookup_next_index != IP_LOOKUP_NEXT_MCAST_MIDCHAIN)
     195             :     {
     196         479 :         adj_midchain_setup(adj_index, fixup, fixup_data, flags);
     197             :     }
     198             : 
     199             :     /*
     200             :      * update the rewrite with the workers paused.
     201             :      */
     202        1342 :     adj_nbr_update_rewrite_internal(adj,
     203             :                                     IP_LOOKUP_NEXT_MIDCHAIN,
     204         671 :                                     adj_get_midchain_node(adj->ia_link),
     205             :                                     adj_nbr_midchain_get_next_node(adj),
     206             :                                     rewrite);
     207         671 : }
     208             : 
     209             : void
     210         369 : adj_nbr_midchain_update_next_node (adj_index_t adj_index,
     211             :                                    u32 next_node)
     212             : {
     213             :     ip_adjacency_t *adj;
     214             :     vlib_main_t * vm;
     215             : 
     216         369 :     ASSERT(ADJ_INDEX_INVALID != adj_index);
     217             : 
     218         369 :     adj = adj_get(adj_index);
     219         369 :     vm = vlib_get_main();
     220             : 
     221         369 :     vlib_worker_thread_barrier_sync(vm);
     222             : 
     223         369 :     adj->rewrite_header.next_index = vlib_node_add_next(vlib_get_main(),
     224         369 :                                                         adj->ia_node_index,
     225             :                                                         next_node);
     226             : 
     227         369 :     vlib_worker_thread_barrier_release(vm);
     228         369 : }
     229             : 
     230             : void
     231         301 : adj_nbr_midchain_reset_next_node (adj_index_t adj_index)
     232             : {
     233             :     ip_adjacency_t *adj;
     234             :     vlib_main_t * vm;
     235             : 
     236         301 :     ASSERT(ADJ_INDEX_INVALID != adj_index);
     237             : 
     238         301 :     adj = adj_get(adj_index);
     239         301 :     vm = vlib_get_main();
     240             : 
     241         301 :     vlib_worker_thread_barrier_sync(vm);
     242             : 
     243         301 :     adj->rewrite_header.next_index =
     244         301 :         vlib_node_add_next(vlib_get_main(),
     245         301 :                            adj->ia_node_index,
     246         301 :                            adj_nbr_midchain_get_next_node(adj));
     247             : 
     248         301 :     vlib_worker_thread_barrier_release(vm);
     249         301 : }
     250             : 
     251             : /**
     252             :  * adj_nbr_midchain_unstack
     253             :  *
     254             :  * Unstack the adj. stack it on drop
     255             :  */
     256             : void
     257         387 : adj_nbr_midchain_unstack (adj_index_t adj_index)
     258             : {
     259             :     fib_node_index_t *entry_indicies, tmp;
     260             :     ip_adjacency_t *adj;
     261             : 
     262         387 :     ASSERT(ADJ_INDEX_INVALID != adj_index);
     263         387 :     adj = adj_get (adj_index);
     264             : 
     265             :     /*
     266             :      * check to see if this unstacking breaks a recursion loop
     267             :      */
     268         387 :     entry_indicies = NULL;
     269         387 :     tmp = adj->sub_type.midchain.fei;
     270         387 :     adj->sub_type.midchain.fei = FIB_NODE_INDEX_INVALID;
     271             : 
     272         387 :     if (FIB_NODE_INDEX_INVALID != tmp)
     273             :     {
     274         303 :         fib_entry_recursive_loop_detect(tmp, &entry_indicies);
     275         303 :         vec_free(entry_indicies);
     276             :     }
     277             : 
     278             :     /*
     279             :      * stack on the drop
     280             :      */
     281         774 :     dpo_stack(DPO_ADJACENCY_MIDCHAIN,
     282         387 :               vnet_link_to_dpo_proto(adj->ia_link),
     283             :               &adj->sub_type.midchain.next_dpo,
     284         387 :               drop_dpo_get(vnet_link_to_dpo_proto(adj->ia_link)));
     285         387 :     CLIB_MEMORY_BARRIER();
     286         387 : }
     287             : 
     288             : void
     289         697 : adj_nbr_midchain_stack_on_fib_entry (adj_index_t ai,
     290             :                                      fib_node_index_t fei,
     291             :                                      fib_forward_chain_type_t fct)
     292             : {
     293             :     fib_node_index_t *entry_indicies;
     294         697 :     dpo_id_t tmp = DPO_INVALID;
     295             :     ip_adjacency_t *adj;
     296             : 
     297         697 :     adj = adj_get (ai);
     298             : 
     299             :     /*
     300             :      * check to see if this stacking will form a recursion loop
     301             :      */
     302         697 :     entry_indicies = NULL;
     303         697 :     adj->sub_type.midchain.fei = fei;
     304             : 
     305         697 :     if (fib_entry_recursive_loop_detect(adj->sub_type.midchain.fei, &entry_indicies))
     306             :     {
     307             :         /*
     308             :          * loop formed, stack on the drop.
     309             :          */
     310           1 :         dpo_copy(&tmp, drop_dpo_get(fib_forw_chain_type_to_dpo_proto(fct)));
     311             :     }
     312             :     else
     313             :     {
     314         696 :         fib_entry_contribute_forwarding (fei, fct, &tmp);
     315             : 
     316         696 :         if (DPO_LOAD_BALANCE == tmp.dpoi_type)
     317             :         {
     318             :             load_balance_t *lb;
     319             : 
     320         650 :             lb = load_balance_get (tmp.dpoi_index);
     321             : 
     322         650 :             if ((adj->ia_flags & ADJ_FLAG_MIDCHAIN_IP_STACK) ||
     323         130 :                 lb->lb_n_buckets == 1)
     324         650 :             {
     325             :                 /*
     326             :                  * do that hash now and stack on the choice.
     327             :                  * If the choice is an incomplete adj then we will need a poke when
     328             :                  * it becomes complete. This happens since the adj update walk propagates
     329             :                  * as far a recursive paths.
     330             :                  */
     331             :                 const dpo_id_t *choice;
     332             :                 int hash;
     333             : 
     334         650 :                 if (FIB_FORW_CHAIN_TYPE_UNICAST_IP4 == fct)
     335             :                 {
     336         414 :                     hash = ip4_compute_flow_hash ((ip4_header_t *) adj_get_rewrite (ai),
     337             :                                                   lb->lb_hash_config);
     338             :                 }
     339         236 :                 else if (FIB_FORW_CHAIN_TYPE_UNICAST_IP6 == fct)
     340             :                 {
     341         236 :                     hash = ip6_compute_flow_hash ((ip6_header_t *) adj_get_rewrite (ai),
     342             :                                                   lb->lb_hash_config);
     343             :                 }
     344             :                 else
     345             :                 {
     346           0 :                     hash = 0;
     347           0 :                     ASSERT(0);
     348             :                 }
     349             : 
     350         650 :                 choice = load_balance_get_bucket_i (lb, hash & lb->lb_n_buckets_minus_1);
     351         650 :                 dpo_copy (&tmp, choice);
     352             :             }
     353           0 :             else if (lb->lb_n_buckets > 1)
     354             :             {
     355             :                 /*
     356             :                  * the client has chosen not to use the stacking to select a
     357             :                  * bucket, and there are more than one buckets. there's no
     358             :                  * value in using the midchain's fixed rewrite string to select
     359             :                  * the path, so force a flow hash on the inner.
     360             :                  */
     361           0 :                 adj->rewrite_header.flags |= VNET_REWRITE_FIXUP_FLOW_HASH;
     362             :             }
     363             : 
     364         650 :             if (adj->ia_flags & ADJ_FLAG_MIDCHAIN_FIXUP_FLOW_HASH)
     365             :             {
     366             :                 /*
     367             :                  * The client, for reasons unbeknownst to adj, wants to force
     368             :                  * a flow hash on the inner, we will oblige.
     369             :                  */
     370           0 :                 adj->rewrite_header.flags |= VNET_REWRITE_FIXUP_FLOW_HASH;
     371             :             }
     372             :         }
     373             :     }
     374         697 :     adj_nbr_midchain_stack (ai, &tmp);
     375         697 :     dpo_reset(&tmp);
     376         697 :     vec_free(entry_indicies);
     377         697 : }
     378             : 
     379             : /**
     380             :  * adj_nbr_midchain_stack
     381             :  */
     382             : void
     383         757 : adj_nbr_midchain_stack (adj_index_t adj_index,
     384             :                         const dpo_id_t *next)
     385             : {
     386             :     ip_adjacency_t *adj;
     387             : 
     388         757 :     ASSERT(ADJ_INDEX_INVALID != adj_index);
     389             : 
     390         757 :     adj = adj_get(adj_index);
     391             : 
     392         757 :     ASSERT((IP_LOOKUP_NEXT_MIDCHAIN == adj->lookup_next_index) ||
     393             :            (IP_LOOKUP_NEXT_MCAST_MIDCHAIN == adj->lookup_next_index));
     394             : 
     395         757 :     dpo_stack_from_node(adj_nbr_midchain_get_tx_node(adj),
     396             :                         &adj->sub_type.midchain.next_dpo,
     397             :                         next);
     398         757 : }
     399             : 
     400             : int
     401        2130 : adj_ndr_midchain_recursive_loop_detect (adj_index_t ai,
     402             :                                         fib_node_index_t **entry_indicies)
     403             : {
     404             :     fib_node_index_t *entry_index, *entries;
     405             :     ip_adjacency_t * adj;
     406             : 
     407        2130 :     adj = adj_get(ai);
     408        2130 :     entries = *entry_indicies;
     409             : 
     410        2449 :     vec_foreach(entry_index, entries)
     411             :     {
     412         320 :         if (*entry_index == adj->sub_type.midchain.fei)
     413             :         {
     414             :             /*
     415             :              * The entry this midchain links to is already in the set
     416             :              * of visited entries, this is a loop
     417             :              */
     418           1 :             adj->ia_flags |= ADJ_FLAG_MIDCHAIN_LOOPED;
     419           1 :             return (1);
     420             :         }
     421             :     }
     422             : 
     423        2129 :     adj->ia_flags &= ~ADJ_FLAG_MIDCHAIN_LOOPED;
     424        2129 :     return (0);
     425             : }
     426             : 
     427             : u8*
     428       42682 : format_adj_midchain (u8* s, va_list *ap)
     429             : {
     430       42682 :     index_t index = va_arg(*ap, index_t);
     431       42682 :     u32 indent = va_arg(*ap, u32);
     432       42682 :     ip_adjacency_t * adj = adj_get(index);
     433             : 
     434       42682 :     s = format (s, "%U", format_vnet_link, adj->ia_link);
     435       42682 :     if (adj->rewrite_header.flags & VNET_REWRITE_HAS_FEATURES)
     436        6978 :         s = format(s, " [features]");
     437       42682 :     s = format (s, " via %U",
     438             :                 format_ip46_address, &adj->sub_type.nbr.next_hop,
     439       42682 :                 adj_proto_to_46(adj->ia_nh_proto));
     440       42682 :     s = format (s, " %U",
     441             :                 format_vnet_rewrite,
     442             :                 &adj->rewrite_header, sizeof (adj->rewrite_data), indent);
     443       42682 :     s = format (s, "\n%Ustacked-on",
     444             :                 format_white_space, indent);
     445             : 
     446       42682 :     if (FIB_NODE_INDEX_INVALID != adj->sub_type.midchain.fei)
     447             :     {
     448       34470 :         s = format (s, " entry:%d", adj->sub_type.midchain.fei);
     449             : 
     450             :     }
     451       42682 :     s = format (s, ":\n%U%U",
     452             :                 format_white_space, indent+2,
     453             :                 format_dpo_id, &adj->sub_type.midchain.next_dpo, indent+2);
     454             : 
     455       42682 :     return (s);
     456             : }
     457             : 
     458             : static void
     459        5804 : adj_dpo_lock (dpo_id_t *dpo)
     460             : {
     461        5804 :     adj_lock(dpo->dpoi_index);
     462        5804 : }
     463             : static void
     464        5802 : adj_dpo_unlock (dpo_id_t *dpo)
     465             : {
     466        5802 :     adj_unlock(dpo->dpoi_index);
     467        5802 : }
     468             : 
     469             : const static dpo_vft_t adj_midchain_dpo_vft = {
     470             :     .dv_lock = adj_dpo_lock,
     471             :     .dv_unlock = adj_dpo_unlock,
     472             :     .dv_format = format_adj_midchain,
     473             :     .dv_get_urpf = adj_dpo_get_urpf,
     474             :     .dv_get_mtu = adj_dpo_get_mtu,
     475             : };
     476             : 
     477             : /**
     478             :  * @brief The per-protocol VLIB graph nodes that are assigned to a midchain
     479             :  *        object.
     480             :  *
     481             :  * this means that these graph nodes are ones from which a midchain is the
     482             :  * parent object in the DPO-graph.
     483             :  */
     484             : const static char* const midchain_ip4_nodes[] =
     485             : {
     486             :     "ip4-midchain",
     487             :     NULL,
     488             : };
     489             : const static char* const midchain_ip6_nodes[] =
     490             : {
     491             :     "ip6-midchain",
     492             :     NULL,
     493             : };
     494             : const static char* const midchain_mpls_nodes[] =
     495             : {
     496             :     "mpls-midchain",
     497             :     NULL,
     498             : };
     499             : const static char* const midchain_ethernet_nodes[] =
     500             : {
     501             :     "adj-l2-midchain",
     502             :     NULL,
     503             : };
     504             : const static char* const midchain_nsh_nodes[] =
     505             : {
     506             :     "adj-nsh-midchain",
     507             :     NULL,
     508             : };
     509             : 
     510             : const static char* const * const midchain_nodes[DPO_PROTO_NUM] =
     511             : {
     512             :     [DPO_PROTO_IP4]  = midchain_ip4_nodes,
     513             :     [DPO_PROTO_IP6]  = midchain_ip6_nodes,
     514             :     [DPO_PROTO_MPLS] = midchain_mpls_nodes,
     515             :     [DPO_PROTO_ETHERNET] = midchain_ethernet_nodes,
     516             :     [DPO_PROTO_NSH] = midchain_nsh_nodes,
     517             : };
     518             : 
     519             : void
     520         575 : adj_midchain_module_init (void)
     521             : {
     522         575 :     dpo_register(DPO_ADJACENCY_MIDCHAIN, &adj_midchain_dpo_vft, midchain_nodes);
     523         575 : }

Generated by: LCOV version 1.14