LCOV - code coverage report
Current view: top level - vnet/mpls - mpls_tunnel.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 255 348 73.3 %
Date: 2023-10-26 01:39:38 Functions: 40 43 93.0 %

          Line data    Source code
       1             : /*
       2             :  * mpls_tunnel.c: MPLS tunnel interfaces (i.e. for RSVP-TE)
       3             :  *
       4             :  * Copyright (c) 2012 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 <vnet/vnet.h>
      19             : #include <vnet/mpls/mpls_tunnel.h>
      20             : #include <vnet/mpls/mpls_types.h>
      21             : #include <vnet/ip/ip.h>
      22             : #include <vnet/fib/fib_path_list.h>
      23             : #include <vnet/adj/adj_midchain.h>
      24             : #include <vnet/adj/adj_mcast.h>
      25             : #include <vnet/dpo/replicate_dpo.h>
      26             : #include <vnet/fib/mpls_fib.h>
      27             : 
      28             : /**
      29             :  * @brief pool of tunnel instances
      30             :  */
      31             : static mpls_tunnel_t *mpls_tunnel_pool;
      32             : 
      33             : /**
      34             :  * @brief DB of SW index to tunnel index
      35             :  */
      36             : static u32 *mpls_tunnel_db;
      37             : 
      38             : /**
      39             :  * @brief MPLS tunnel flags strings
      40             :  */
      41             : static const char *mpls_tunnel_attribute_names[] = MPLS_TUNNEL_ATTRIBUTES;
      42             : 
      43             : /**
      44             :  * @brief Packet trace structure
      45             :  */
      46             : typedef struct mpls_tunnel_trace_t_
      47             : {
      48             :     /**
      49             :    * Tunnel-id / index in tunnel vector
      50             :    */
      51             :   u32 tunnel_id;
      52             : } mpls_tunnel_trace_t;
      53             : 
      54             : static u8 *
      55         222 : format_mpls_tunnel_tx_trace (u8 * s,
      56             :                              va_list * args)
      57             : {
      58         222 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
      59         222 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
      60         222 :   mpls_tunnel_trace_t * t = va_arg (*args, mpls_tunnel_trace_t *);
      61             : 
      62         222 :   s = format (s, "MPLS: tunnel %d", t->tunnel_id);
      63         222 :   return s;
      64             : }
      65             : 
      66             : typedef enum
      67             : {
      68             :   MPLS_TUNNEL_ENCAP_NEXT_L2_MIDCHAIN,
      69             :   MPLS_TUNNEL_ENCAP_N_NEXT,
      70             : } mpls_tunnel_encap_next_t;
      71             : 
      72             : /**
      73             :  * @brief TX function. Only called L2. L3 traffic uses the adj-midchains
      74             :  */
      75         581 : VLIB_NODE_FN (mpls_tunnel_tx) (vlib_main_t * vm,
      76             :                                vlib_node_runtime_t * node,
      77             :                                vlib_frame_t * frame)
      78             : {
      79           6 :   u32 *from = vlib_frame_vector_args (frame);
      80             :   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
      81             :   u16 nexts[VLIB_FRAME_SIZE], *next;
      82             :   u32 n_left;
      83             : 
      84           6 :   n_left = frame->n_vectors;
      85           6 :   b = bufs;
      86           6 :   next = nexts;
      87             : 
      88           6 :   vlib_get_buffers (vm, from, bufs, n_left);
      89             : 
      90         105 :   while (n_left > 2)
      91             :     {
      92             :       const mpls_tunnel_t *mt0, *mt1;
      93             :       u32 sw_if_index0, sw_if_index1;
      94             : 
      95          99 :       sw_if_index0 = vnet_buffer(b[0])->sw_if_index[VLIB_TX];
      96          99 :       sw_if_index1 = vnet_buffer(b[1])->sw_if_index[VLIB_TX];
      97             : 
      98          99 :       mt0 = pool_elt_at_index(mpls_tunnel_pool,
      99             :                               mpls_tunnel_db[sw_if_index0]);
     100          99 :       mt1 = pool_elt_at_index(mpls_tunnel_pool,
     101             :                               mpls_tunnel_db[sw_if_index1]);
     102             : 
     103          99 :       vnet_buffer(b[0])->ip.adj_index[VLIB_TX] = mt0->mt_l2_lb.dpoi_index;
     104          99 :       vnet_buffer(b[1])->ip.adj_index[VLIB_TX] = mt1->mt_l2_lb.dpoi_index;
     105          99 :       next[0] = mt0->mt_l2_lb.dpoi_next_node;
     106          99 :       next[1] = mt1->mt_l2_lb.dpoi_next_node;
     107             : 
     108             :       /* since we are coming out of the L2 world, where the vlib_buffer
     109             :        * union is used for other things, make sure it is clean for
     110             :        * MPLS from now on.
     111             :        */
     112          99 :       vnet_buffer(b[0])->mpls.first = 0;
     113          99 :       vnet_buffer(b[1])->mpls.first = 0;
     114             : 
     115          99 :       if (PREDICT_FALSE(b[0]->flags & VLIB_BUFFER_IS_TRACED))
     116             :       {
     117          99 :           mpls_tunnel_trace_t *tr = vlib_add_trace (vm, node,
     118             :                                                     b[0], sizeof (*tr));
     119          99 :           tr->tunnel_id = mpls_tunnel_db[sw_if_index0];
     120             :       }
     121          99 :       if (PREDICT_FALSE(b[1]->flags & VLIB_BUFFER_IS_TRACED))
     122             :       {
     123          99 :           mpls_tunnel_trace_t *tr = vlib_add_trace (vm, node,
     124          99 :                                                     b[1], sizeof (*tr));
     125          99 :           tr->tunnel_id = mpls_tunnel_db[sw_if_index1];
     126             :       }
     127             : 
     128          99 :       b += 2;
     129          99 :       n_left -= 2;
     130          99 :       next += 2;
     131             :     }
     132          14 :   while (n_left)
     133             :     {
     134             :       const mpls_tunnel_t *mt0;
     135             :       u32 sw_if_index0;
     136             : 
     137           8 :       sw_if_index0 = vnet_buffer(b[0])->sw_if_index[VLIB_TX];
     138           8 :       mt0 = pool_elt_at_index(mpls_tunnel_pool,
     139             :                               mpls_tunnel_db[sw_if_index0]);
     140             : 
     141           8 :       vnet_buffer(b[0])->ip.adj_index[VLIB_TX] = mt0->mt_l2_lb.dpoi_index;
     142           8 :       next[0] = mt0->mt_l2_lb.dpoi_next_node;
     143             : 
     144             :       /* since we are coming out of the L2 world, where the vlib_buffer
     145             :        * union is used for other things, make sure it is clean for
     146             :        * MPLS from now on.
     147             :        */
     148           8 :       vnet_buffer(b[0])->mpls.first = 0;
     149             : 
     150           8 :       if (PREDICT_FALSE(b[0]->flags & VLIB_BUFFER_IS_TRACED))
     151             :         {
     152           8 :           mpls_tunnel_trace_t *tr = vlib_add_trace (vm, node,
     153             :                                                     b[0], sizeof (*tr));
     154           8 :           tr->tunnel_id = mpls_tunnel_db[sw_if_index0];
     155             :         }
     156             : 
     157           8 :       b += 1;
     158           8 :       n_left -= 1;
     159           8 :       next += 1;
     160             :     }
     161             : 
     162           6 :   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
     163             : 
     164           6 :   return frame->n_vectors;
     165             : }
     166             : 
     167      183788 : VLIB_REGISTER_NODE (mpls_tunnel_tx) =
     168             : {
     169             :   .name = "mpls-tunnel-tx",
     170             :   .vector_size = sizeof (u32),
     171             :   .format_trace = format_mpls_tunnel_tx_trace,
     172             :   .type = VLIB_NODE_TYPE_INTERNAL,
     173             :   .n_errors = 0,
     174             :   .n_next_nodes = 0,
     175             :   /* MPLS_TUNNEL_ENCAP_N_NEXT, */
     176             :   /* .next_nodes = { */
     177             :   /*   [MPLS_TUNNEL_ENCAP_NEXT_L2_MIDCHAIN] = "mpls-load-balance", */
     178             :   /* }, */
     179             : };
     180             : 
     181             : /**
     182             :  * @brief Get a tunnel object from a SW interface index
     183             :  */
     184             : static mpls_tunnel_t*
     185        1504 : mpls_tunnel_get_from_sw_if_index (u32 sw_if_index)
     186             : {
     187        1504 :     if ((vec_len(mpls_tunnel_db) <= sw_if_index) ||
     188        1504 :         (~0 == mpls_tunnel_db[sw_if_index]))
     189           0 :         return (NULL);
     190             : 
     191        1504 :     return (pool_elt_at_index(mpls_tunnel_pool,
     192             :                               mpls_tunnel_db[sw_if_index]));
     193             : }
     194             : 
     195             : /**
     196             :  * @brief Build a rewrite string for the MPLS tunnel.
     197             :  */
     198             : static u8*
     199           9 : mpls_tunnel_build_rewrite_i (void)
     200             : {
     201             :     /*
     202             :      * passing the adj code a NULL rewrite means 'i don't have one cos
     203             :      * t'other end is unresolved'. That's not the case here. For the mpls
     204             :      * tunnel there are just no bytes of encap to apply in the adj. We'll impose
     205             :      * the label stack once we choose a path. So return a zero length rewrite.
     206             :      */
     207           9 :     u8 *rewrite = NULL;
     208             : 
     209           9 :     vec_validate(rewrite, 0);
     210           9 :     vec_reset_length(rewrite);
     211             : 
     212           9 :     return (rewrite);
     213             : }
     214             : 
     215             : /**
     216             :  * @brief Build a rewrite string for the MPLS tunnel.
     217             :  */
     218             : static u8*
     219           0 : mpls_tunnel_build_rewrite (vnet_main_t * vnm,
     220             :                            u32 sw_if_index,
     221             :                            vnet_link_t link_type,
     222             :                            const void *dst_address)
     223             : {
     224           0 :     return (mpls_tunnel_build_rewrite_i());
     225             : }
     226             : 
     227             : typedef struct mpls_tunnel_collect_forwarding_ctx_t_
     228             : {
     229             :     load_balance_path_t * next_hops;
     230             :     const mpls_tunnel_t *mt;
     231             :     fib_forward_chain_type_t fct;
     232             : } mpls_tunnel_collect_forwarding_ctx_t;
     233             : 
     234             : static fib_path_list_walk_rc_t
     235         581 : mpls_tunnel_collect_forwarding (fib_node_index_t pl_index,
     236             :                                 fib_node_index_t path_index,
     237             :                                 void *arg)
     238             : {
     239             :     mpls_tunnel_collect_forwarding_ctx_t *ctx;
     240             :     fib_path_ext_t *path_ext;
     241             : 
     242         581 :     ctx = arg;
     243             : 
     244             :     /*
     245             :      * if the path is not resolved, don't include it.
     246             :      */
     247         581 :     if (!fib_path_is_resolved(path_index))
     248             :     {
     249         237 :         return (FIB_PATH_LIST_WALK_CONTINUE);
     250             :     }
     251             : 
     252             :     /*
     253             :      * get the matching path-extension for the path being visited.
     254             :      */
     255         344 :     path_ext = fib_path_ext_list_find_by_path_index(&ctx->mt->mt_path_exts,
     256             :                                                     path_index);
     257             : 
     258             :     /*
     259             :      * we don't want IP TTL decrements for packets hitting the MPLS labels
     260             :      * we stack on, since the IP TTL decrement is done by the adj
     261             :      */
     262         344 :     path_ext->fpe_mpls_flags |= FIB_PATH_EXT_MPLS_FLAG_NO_IP_TTL_DECR;
     263             : 
     264             :     /*
     265             :      * found a matching extension. stack it to obtain the forwarding
     266             :      * info for this path.
     267             :      */
     268         344 :     ctx->next_hops =
     269         344 :       fib_path_ext_stack (path_ext, DPO_PROTO_MPLS, ctx->fct, ctx->next_hops);
     270             : 
     271         344 :     return (FIB_PATH_LIST_WALK_CONTINUE);
     272             : }
     273             : 
     274             : static void
     275         674 : mpls_tunnel_mk_lb (mpls_tunnel_t *mt,
     276             :                    vnet_link_t linkt,
     277             :                    fib_forward_chain_type_t fct,
     278             :                    dpo_id_t *dpo_lb)
     279             : {
     280             :     dpo_proto_t lb_proto;
     281             : 
     282             :     /*
     283             :      * If the entry has path extensions then we construct a load-balance
     284             :      * by stacking the extensions on the forwarding chains of the paths.
     285             :      * Otherwise we use the load-balance of the path-list
     286             :      */
     287         674 :     mpls_tunnel_collect_forwarding_ctx_t ctx = {
     288             :         .mt = mt,
     289             :         .next_hops = NULL,
     290             :         .fct = fct,
     291             :     };
     292             : 
     293             :     /*
     294             :      * As an optimisation we allocate the vector of next-hops to be sized
     295             :      * equal to the maximum nuber of paths we will need, which is also the
     296             :      * most likely number we will need, since in most cases the paths are 'up'.
     297             :      */
     298         674 :     vec_validate(ctx.next_hops, fib_path_list_get_n_paths(mt->mt_path_list));
     299         674 :     vec_reset_length(ctx.next_hops);
     300             : 
     301         674 :     lb_proto = fib_forw_chain_type_to_dpo_proto(fct);
     302             : 
     303         674 :     if (FIB_NODE_INDEX_INVALID != mt->mt_path_list)
     304             :     {
     305         570 :         fib_path_list_walk(mt->mt_path_list,
     306             :                            mpls_tunnel_collect_forwarding,
     307             :                            &ctx);
     308             :     }
     309             : 
     310         674 :     if (!dpo_id_is_valid(dpo_lb))
     311             :     {
     312             :         /*
     313             :          * first time create
     314             :          */
     315         674 :         if (mt->mt_flags & MPLS_TUNNEL_FLAG_MCAST)
     316             :         {
     317          11 :             dpo_set(dpo_lb,
     318             :                     DPO_REPLICATE,
     319             :                     lb_proto,
     320             :                     replicate_create(0, lb_proto));
     321             :         }
     322             :         else
     323             :         {
     324             :             flow_hash_config_t fhc;
     325             : 
     326         663 :             switch (linkt)
     327             :             {
     328         648 :             case VNET_LINK_MPLS:
     329         648 :                 fhc = MPLS_FLOW_HASH_DEFAULT;
     330         648 :                 break;
     331          15 :             case VNET_LINK_IP4:
     332             :             case VNET_LINK_IP6:
     333          15 :                 fhc = IP_FLOW_HASH_DEFAULT;
     334          15 :                 break;
     335           0 :             default:
     336           0 :                 fhc = 0;
     337           0 :                 break;
     338             :             }
     339             : 
     340         663 :             dpo_set(dpo_lb,
     341             :                     DPO_LOAD_BALANCE,
     342             :                     lb_proto,
     343             :                     load_balance_create(0, lb_proto, fhc));
     344             :         }
     345             :     }
     346             : 
     347         674 :     if (mt->mt_flags & MPLS_TUNNEL_FLAG_MCAST)
     348             :     {
     349             :         /*
     350             :          * MPLS multicast
     351             :          */
     352          11 :         replicate_multipath_update(dpo_lb, ctx.next_hops);
     353             :     }
     354             :     else
     355             :     {
     356         663 :         load_balance_multipath_update(dpo_lb,
     357         663 :                                       ctx.next_hops,
     358             :                                       LOAD_BALANCE_FLAG_NONE);
     359         663 :         vec_free(ctx.next_hops);
     360             :     }
     361         674 : }
     362             : 
     363             : /**
     364             :  * mpls_tunnel_stack
     365             :  *
     366             :  * 'stack' (resolve the recursion for) the tunnel's midchain adjacency
     367             :  */
     368             : static void
     369          41 : mpls_tunnel_stack (adj_index_t ai)
     370             : {
     371             :     ip_adjacency_t *adj;
     372             :     mpls_tunnel_t *mt;
     373             :     u32 sw_if_index;
     374             : 
     375          41 :     adj = adj_get(ai);
     376          41 :     sw_if_index = adj->rewrite_header.sw_if_index;
     377             : 
     378          41 :     mt = mpls_tunnel_get_from_sw_if_index(sw_if_index);
     379             : 
     380          41 :     if (NULL == mt || FIB_NODE_INDEX_INVALID == mt->mt_path_list)
     381           0 :         return;
     382             : 
     383          41 :     if (FIB_NODE_INDEX_INVALID == mt->mt_path_list)
     384             :     {
     385           0 :         adj_nbr_midchain_unstack(ai);
     386           0 :         return;
     387             :     }
     388             : 
     389             :     /*
     390             :      * while we're stacking the adj, remove the tunnel from the child list
     391             :      * of the path list. this breaks a circular dependency of walk updates
     392             :      * where the create of adjacencies in the children can lead to walks
     393             :      * that get back here.
     394             :      */
     395          41 :     fib_path_list_lock(mt->mt_path_list);
     396             : 
     397          41 :     fib_path_list_child_remove(mt->mt_path_list,
     398             :                                mt->mt_sibling_index);
     399             : 
     400             :     /*
     401             :      * Construct the DPO (load-balance or replicate) that we can stack
     402             :      * the tunnel's midchain on
     403             :      */
     404          41 :     if (vnet_hw_interface_get_flags(vnet_get_main(),
     405          41 :                                     mt->mt_hw_if_index) &
     406             :         VNET_HW_INTERFACE_FLAG_LINK_UP)
     407             :     {
     408          41 :         dpo_id_t dpo = DPO_INVALID;
     409             : 
     410          41 :         mpls_tunnel_mk_lb(mt,
     411          41 :                           adj->ia_link,
     412          41 :                           fib_forw_chain_type_from_link_type(
     413          41 :                               adj_get_link_type(ai)),
     414             :                           &dpo);
     415             : 
     416          41 :         adj_nbr_midchain_stack(ai, &dpo);
     417          41 :         dpo_reset(&dpo);
     418             :     }
     419             :     else
     420             :     {
     421           0 :         adj_nbr_midchain_unstack(ai);
     422             :     }
     423             : 
     424          82 :     mt->mt_sibling_index = fib_path_list_child_add(mt->mt_path_list,
     425             :                                                    FIB_NODE_TYPE_MPLS_TUNNEL,
     426          41 :                                                    mt - mpls_tunnel_pool);
     427             : 
     428          41 :     fib_path_list_unlock(mt->mt_path_list);
     429             : }
     430             : 
     431             : /**
     432             :  * @brief Call back when restacking all adjacencies on a MPLS interface
     433             :  */
     434             : static adj_walk_rc_t
     435          32 : mpls_adj_walk_cb (adj_index_t ai,
     436             :                  void *ctx)
     437             : {
     438          32 :     mpls_tunnel_stack(ai);
     439             : 
     440          32 :     return (ADJ_WALK_RC_CONTINUE);
     441             : }
     442             : 
     443             : static void
     444        1532 : mpls_tunnel_restack (mpls_tunnel_t *mt)
     445             : {
     446             :     fib_protocol_t proto;
     447             : 
     448             :     /*
     449             :      * walk all the adjacencies on the MPLS interface and restack them
     450             :      */
     451        1532 :     if (mt->mt_flags & MPLS_TUNNEL_FLAG_L2)
     452             :     {
     453             :         /*
     454             :          * Stack a load-balance that drops, whilst we have no paths
     455             :          */
     456         633 :         dpo_id_t dpo = DPO_INVALID;
     457             : 
     458         633 :         mpls_tunnel_mk_lb(mt,
     459             :                           VNET_LINK_MPLS,
     460             :                           FIB_FORW_CHAIN_TYPE_ETHERNET,
     461             :                           &dpo);
     462             : 
     463         633 :         dpo_stack_from_node(mpls_tunnel_tx.index,
     464             :                             &mt->mt_l2_lb,
     465             :                             &dpo);
     466         633 :         dpo_reset(&dpo);
     467             :     }
     468             :     else
     469             :     {
     470        2697 :         FOR_EACH_FIB_IP_PROTOCOL(proto)
     471             :         {
     472        1798 :             adj_nbr_walk(mt->mt_sw_if_index,
     473             :                          proto,
     474             :                          mpls_adj_walk_cb,
     475             :                          NULL);
     476             :         }
     477             :     }
     478        1532 : }
     479             : 
     480             : static clib_error_t *
     481         418 : mpls_tunnel_admin_up_down (vnet_main_t * vnm,
     482             :                            u32 hw_if_index,
     483             :                            u32 flags)
     484             : {
     485             :     vnet_hw_interface_t * hi;
     486             :     mpls_tunnel_t *mt;
     487             : 
     488         418 :     hi = vnet_get_hw_interface (vnm, hw_if_index);
     489             : 
     490         418 :     mt = mpls_tunnel_get_from_sw_if_index(hi->sw_if_index);
     491             : 
     492         418 :     if (NULL == mt)
     493           0 :         return (NULL);
     494             : 
     495         418 :     if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
     496         209 :         vnet_hw_interface_set_flags (vnm, hw_if_index,
     497             :                                      VNET_HW_INTERFACE_FLAG_LINK_UP);
     498             :     else
     499         209 :         vnet_hw_interface_set_flags (vnm, hw_if_index, 0 /* down */);
     500             : 
     501         418 :     mpls_tunnel_restack(mt);
     502             : 
     503         418 :     return (NULL);
     504             : }
     505             : 
     506             : /**
     507             :  * @brief Fixup the adj rewrite post encap. This is a no-op since the
     508             :  * rewrite is a stack of labels.
     509             :  */
     510             : static void
     511        3598 : mpls_tunnel_fixup (vlib_main_t *vm,
     512             :                    const ip_adjacency_t *adj,
     513             :                    vlib_buffer_t *b0,
     514             :                    const void*data)
     515             : {
     516             :     /*
     517             :      * A no-op w.r.t. the header. but reset the 'have we pushed any
     518             :      * MPLS labels onto the packet' flag. That way when we enter the
     519             :      * tunnel we'll get a TTL set to 255
     520             :      */
     521        3598 :     vnet_buffer(b0)->mpls.first = 0;
     522        3598 : }
     523             : 
     524             : static void
     525           9 : mpls_tunnel_update_adj (vnet_main_t * vnm,
     526             :                         u32 sw_if_index,
     527             :                         adj_index_t ai)
     528             : {
     529             :     ip_adjacency_t *adj;
     530             : 
     531           9 :     ASSERT(ADJ_INDEX_INVALID != ai);
     532             : 
     533           9 :     adj = adj_get(ai);
     534             : 
     535           9 :     switch (adj->lookup_next_index)
     536             :     {
     537           9 :     case IP_LOOKUP_NEXT_ARP:
     538             :     case IP_LOOKUP_NEXT_GLEAN:
     539             :     case IP_LOOKUP_NEXT_BCAST:
     540           9 :         adj_nbr_midchain_update_rewrite(ai, mpls_tunnel_fixup,
     541             :                                         NULL,
     542             :                                         ADJ_FLAG_NONE,
     543             :                                         mpls_tunnel_build_rewrite_i());
     544           9 :         break;
     545           0 :     case IP_LOOKUP_NEXT_MCAST:
     546             :         /*
     547             :          * Construct a partial rewrite from the known ethernet mcast dest MAC
     548             :          * There's no MAC fixup, so the last 2 parameters are 0
     549             :          */
     550           0 :         adj_mcast_midchain_update_rewrite(ai, mpls_tunnel_fixup,
     551             :                                           NULL,
     552             :                                           ADJ_FLAG_NONE,
     553             :                                           mpls_tunnel_build_rewrite_i(),
     554             :                                           0, 0);
     555           0 :         break;
     556             : 
     557           0 :     case IP_LOOKUP_NEXT_DROP:
     558             :     case IP_LOOKUP_NEXT_PUNT:
     559             :     case IP_LOOKUP_NEXT_LOCAL:
     560             :     case IP_LOOKUP_NEXT_REWRITE:
     561             :     case IP_LOOKUP_NEXT_MIDCHAIN:
     562             :     case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
     563             :     case IP_LOOKUP_NEXT_ICMP_ERROR:
     564             :     case IP_LOOKUP_N_NEXT:
     565           0 :       ASSERT (0);
     566           0 :       break;
     567             :     }
     568             : 
     569           9 :     mpls_tunnel_stack(ai);
     570           9 : }
     571             : 
     572             : static u8 *
     573         416 : format_mpls_tunnel_name (u8 * s, va_list * args)
     574             : {
     575         416 :   u32 dev_instance = va_arg (*args, u32);
     576         416 :   return format (s, "mpls-tunnel%d", dev_instance);
     577             : }
     578             : 
     579             : static u8 *
     580         207 : format_mpls_tunnel_device (u8 * s, va_list * args)
     581             : {
     582         207 :   u32 dev_instance = va_arg (*args, u32);
     583         207 :   CLIB_UNUSED (int verbose) = va_arg (*args, int);
     584             : 
     585         207 :   return (format (s, "MPLS-tunnel: id %d\n", dev_instance));
     586             : }
     587             : 
     588       12095 : VNET_DEVICE_CLASS (mpls_tunnel_class) = {
     589             :     .name = "MPLS tunnel device",
     590             :     .format_device_name = format_mpls_tunnel_name,
     591             :     .format_device = format_mpls_tunnel_device,
     592             :     .format_tx_trace = format_mpls_tunnel_tx_trace,
     593             :     .admin_up_down_function = mpls_tunnel_admin_up_down,
     594             : };
     595             : 
     596        8063 : VNET_HW_INTERFACE_CLASS (mpls_tunnel_hw_interface_class) = {
     597             :   .name = "MPLS-Tunnel",
     598             :   .update_adjacency = mpls_tunnel_update_adj,
     599             :   .build_rewrite = mpls_tunnel_build_rewrite,
     600             :   .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
     601             : };
     602             : 
     603             : const mpls_tunnel_t *
     604       40009 : mpls_tunnel_get (u32 mti)
     605             : {
     606       40009 :     return (pool_elt_at_index(mpls_tunnel_pool, mti));
     607             : }
     608             : 
     609             : /**
     610             :  * @brief Walk all the MPLS tunnels
     611             :  */
     612             : void
     613         416 : mpls_tunnel_walk (mpls_tunnel_walk_cb_t cb,
     614             :                   void *ctx)
     615             : {
     616             :     u32 mti;
     617             : 
     618       40425 :     pool_foreach_index (mti, mpls_tunnel_pool)
     619             :      {
     620       40009 :         cb(mti, ctx);
     621             :     }
     622         416 : }
     623             : 
     624             : void
     625         209 : vnet_mpls_tunnel_del (u32 sw_if_index)
     626             : {
     627             :     mpls_tunnel_t *mt;
     628             : 
     629         209 :     mt = mpls_tunnel_get_from_sw_if_index(sw_if_index);
     630             : 
     631         209 :     if (NULL == mt)
     632           0 :         return;
     633             : 
     634         209 :     if (FIB_NODE_INDEX_INVALID != mt->mt_path_list)
     635           0 :         fib_path_list_child_remove(mt->mt_path_list,
     636             :                                    mt->mt_sibling_index);
     637         209 :     dpo_reset(&mt->mt_l2_lb);
     638             : 
     639         209 :     vnet_reset_interface_l3_output_node (vlib_get_main (), mt->mt_sw_if_index);
     640         209 :     vnet_delete_hw_interface (vnet_get_main(), mt->mt_hw_if_index);
     641             : 
     642         209 :     pool_put(mpls_tunnel_pool, mt);
     643         209 :     mpls_tunnel_db[sw_if_index] = ~0;
     644             : }
     645             : 
     646             : u32
     647         209 : vnet_mpls_tunnel_create (u8 l2_only,
     648             :                          u8 is_multicast,
     649             :                          u8 *tag)
     650             : {
     651             :     vnet_hw_interface_t * hi;
     652             :     mpls_tunnel_t *mt;
     653             :     vnet_main_t * vnm;
     654             :     u32 mti;
     655             : 
     656         209 :     vnm = vnet_get_main();
     657         209 :     pool_get(mpls_tunnel_pool, mt);
     658         209 :     clib_memset (mt, 0, sizeof (*mt));
     659         209 :     mti = mt - mpls_tunnel_pool;
     660         209 :     fib_node_init(&mt->mt_node, FIB_NODE_TYPE_MPLS_TUNNEL);
     661         209 :     mt->mt_path_list = FIB_NODE_INDEX_INVALID;
     662         209 :     mt->mt_sibling_index = FIB_NODE_INDEX_INVALID;
     663             : 
     664         209 :     if (is_multicast)
     665           1 :         mt->mt_flags |= MPLS_TUNNEL_FLAG_MCAST;
     666         209 :     if (l2_only)
     667         104 :         mt->mt_flags |= MPLS_TUNNEL_FLAG_L2;
     668         209 :     if (tag)
     669         209 :         memcpy(mt->mt_tag, tag, sizeof(mt->mt_tag));
     670             :     else
     671           0 :         mt->mt_tag[0] = '\0';
     672             : 
     673             :     /*
     674             :      * Create a new tunnel HW interface
     675             :      */
     676         209 :     mt->mt_hw_if_index = vnet_register_interface(
     677             :         vnm,
     678             :         mpls_tunnel_class.index,
     679             :         mti,
     680             :         mpls_tunnel_hw_interface_class.index,
     681             :         mti);
     682         209 :     hi = vnet_get_hw_interface (vnm, mt->mt_hw_if_index);
     683             : 
     684         209 :     if (mt->mt_flags & MPLS_TUNNEL_FLAG_L2)
     685         104 :         vnet_set_interface_output_node (vnm, mt->mt_hw_if_index,
     686             :                                         mpls_tunnel_tx.index);
     687             :     else
     688         105 :       vnet_set_interface_l3_output_node (vnm->vlib_main, hi->sw_if_index,
     689             :                                          (u8 *) "tunnel-output");
     690             : 
     691             :     /* Standard default MPLS tunnel MTU. */
     692         209 :     vnet_sw_interface_set_mtu (vnm, hi->sw_if_index, 9000);
     693             : 
     694             :     /*
     695             :      * Add the new tunnel to the tunnel DB - key:SW if index
     696             :      */
     697         209 :     mt->mt_sw_if_index = hi->sw_if_index;
     698         434 :     vec_validate_init_empty(mpls_tunnel_db, mt->mt_sw_if_index, ~0);
     699         209 :     mpls_tunnel_db[mt->mt_sw_if_index] = mti;
     700             : 
     701         209 :     return (mt->mt_sw_if_index);
     702             : }
     703             : 
     704             : void
     705         209 : vnet_mpls_tunnel_path_add (u32 sw_if_index,
     706             :                            fib_route_path_t *rpaths)
     707             : {
     708             :     fib_route_path_t *rpath;
     709             :     mpls_tunnel_t *mt;
     710             :     u32 mti;
     711             : 
     712         209 :     mt = mpls_tunnel_get_from_sw_if_index(sw_if_index);
     713             : 
     714         209 :     if (NULL == mt)
     715           0 :         return;
     716             : 
     717         209 :     mti = mt - mpls_tunnel_pool;
     718             : 
     719             :     /*
     720             :      * construct a path-list from the path provided
     721             :      */
     722         209 :     if (FIB_NODE_INDEX_INVALID == mt->mt_path_list)
     723             :     {
     724         209 :         mt->mt_path_list = fib_path_list_create(FIB_PATH_LIST_FLAG_SHARED, rpaths);
     725         209 :         mt->mt_sibling_index = fib_path_list_child_add(mt->mt_path_list,
     726             :                                                        FIB_NODE_TYPE_MPLS_TUNNEL,
     727             :                                                        mti);
     728             :     }
     729             :     else
     730             :     {
     731             :         fib_node_index_t old_pl_index;
     732             : 
     733           0 :         old_pl_index = mt->mt_path_list;
     734             : 
     735           0 :         mt->mt_path_list =
     736           0 :             fib_path_list_copy_and_path_add(old_pl_index,
     737             :                                             FIB_PATH_LIST_FLAG_SHARED,
     738             :                                             rpaths);
     739             : 
     740           0 :         fib_path_list_child_remove(old_pl_index,
     741             :                                    mt->mt_sibling_index);
     742           0 :         mt->mt_sibling_index = fib_path_list_child_add(mt->mt_path_list,
     743             :                                                        FIB_NODE_TYPE_MPLS_TUNNEL,
     744             :                                                        mti);
     745             :         /*
     746             :          * re-resolve all the path-extensions with the new path-list
     747             :          */
     748           0 :         fib_path_ext_list_resolve(&mt->mt_path_exts, mt->mt_path_list);
     749             :     }
     750         419 :     vec_foreach(rpath, rpaths)
     751             :     {
     752         210 :         fib_path_ext_list_insert(&mt->mt_path_exts,
     753             :                                  mt->mt_path_list,
     754             :                                  FIB_PATH_EXT_MPLS,
     755             :                                  rpath);
     756             :     }
     757         209 :     mpls_tunnel_restack(mt);
     758             : }
     759             : 
     760             : int
     761         209 : vnet_mpls_tunnel_path_remove (u32 sw_if_index,
     762             :                               fib_route_path_t *rpaths)
     763             : {
     764             :     mpls_tunnel_t *mt;
     765             :     u32 mti;
     766             : 
     767         209 :     mt = mpls_tunnel_get_from_sw_if_index(sw_if_index);
     768             : 
     769         209 :     if (NULL == mt)
     770           0 :         return (0);
     771             : 
     772         209 :     mti = mt - mpls_tunnel_pool;
     773             : 
     774             :     /*
     775             :      * construct a path-list from the path provided
     776             :      */
     777         209 :     if (FIB_NODE_INDEX_INVALID == mt->mt_path_list)
     778             :     {
     779             :         /* can't remove a path if we have onoe */
     780           0 :         return (0);
     781             :     }
     782             :     else
     783             :     {
     784             :         fib_node_index_t old_pl_index;
     785             : 
     786         209 :         old_pl_index = mt->mt_path_list;
     787             : 
     788         209 :         fib_path_list_lock(old_pl_index);
     789         209 :         mt->mt_path_list =
     790         209 :             fib_path_list_copy_and_path_remove(old_pl_index,
     791             :                                                FIB_PATH_LIST_FLAG_SHARED,
     792             :                                                rpaths);
     793             : 
     794         209 :         fib_path_list_child_remove(old_pl_index,
     795             :                                    mt->mt_sibling_index);
     796             : 
     797         209 :         if (FIB_NODE_INDEX_INVALID == mt->mt_path_list)
     798             :         {
     799             :             /* no paths left */
     800         209 :             fib_path_list_unlock(old_pl_index);
     801         209 :             return (0);
     802             :         }
     803             :         else
     804             :         {
     805           0 :             mt->mt_sibling_index =
     806           0 :                 fib_path_list_child_add(mt->mt_path_list,
     807             :                                         FIB_NODE_TYPE_MPLS_TUNNEL,
     808             :                                         mti);
     809             :         }
     810             :         /*
     811             :          * find the matching path extension and remove it
     812             :          */
     813           0 :         fib_path_ext_list_remove(&mt->mt_path_exts,
     814             :                                   FIB_PATH_EXT_MPLS,
     815             :                                   rpaths);
     816             : 
     817             :         /*
     818             :          * re-resolve all the path-extensions with the new path-list
     819             :          */
     820           0 :         fib_path_ext_list_resolve(&mt->mt_path_exts,
     821             :                                   mt->mt_path_list);
     822             : 
     823           0 :         mpls_tunnel_restack(mt);
     824           0 :         fib_path_list_unlock(old_pl_index);
     825             :    }
     826             : 
     827           0 :     return (fib_path_list_get_n_paths(mt->mt_path_list));
     828             : }
     829             : 
     830             : int
     831         418 : vnet_mpls_tunnel_get_index (u32 sw_if_index)
     832             : {
     833             :     mpls_tunnel_t *mt;
     834             : 
     835         418 :     mt = mpls_tunnel_get_from_sw_if_index(sw_if_index);
     836             : 
     837         418 :     if (NULL == mt)
     838           0 :         return (~0);
     839             : 
     840         418 :     return (mt - mpls_tunnel_pool);
     841             : }
     842             : 
     843             : static clib_error_t *
     844           0 : vnet_create_mpls_tunnel_command_fn (vlib_main_t * vm,
     845             :                                     unformat_input_t * input,
     846             :                                     vlib_cli_command_t * cmd)
     847             : {
     848           0 :     unformat_input_t _line_input, * line_input = &_line_input;
     849           0 :     vnet_main_t * vnm = vnet_get_main();
     850           0 :     u8 is_del = 0, l2_only = 0, is_multicast =0;
     851           0 :     fib_route_path_t rpath, *rpaths = NULL;
     852           0 :     u32 sw_if_index = ~0, payload_proto;
     853           0 :     clib_error_t *error = NULL;
     854             : 
     855           0 :     clib_memset(&rpath, 0, sizeof(rpath));
     856           0 :     payload_proto = DPO_PROTO_MPLS;
     857             : 
     858             :     /* Get a line of input. */
     859           0 :     if (! unformat_user (input, unformat_line_input, line_input))
     860           0 :         return 0;
     861             : 
     862           0 :     while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
     863             :     {
     864           0 :         if (unformat (line_input, "del %U",
     865             :                       unformat_vnet_sw_interface, vnm,
     866             :                       &sw_if_index))
     867           0 :             is_del = 1;
     868           0 :         else if (unformat (line_input, "add %U",
     869             :                            unformat_vnet_sw_interface, vnm,
     870             :                            &sw_if_index))
     871           0 :             is_del = 0;
     872           0 :         else if (unformat (line_input, "add"))
     873           0 :             is_del = 0;
     874           0 :         else if (unformat (line_input, "l2-only"))
     875           0 :             l2_only = 1;
     876           0 :         else if (unformat (line_input, "multicast"))
     877           0 :             is_multicast = 1;
     878           0 :         else if (unformat (line_input, "via %U",
     879             :                            unformat_fib_route_path,
     880             :                            &rpath, &payload_proto))
     881           0 :             vec_add1(rpaths, rpath);
     882             :         else
     883             :         {
     884           0 :             error = clib_error_return (0, "unknown input '%U'",
     885             :                                        format_unformat_error, line_input);
     886           0 :             goto done;
     887             :         }
     888             :     }
     889             : 
     890           0 :     if (is_del)
     891             :     {
     892           0 :         if (NULL == rpaths)
     893             :         {
     894           0 :             vnet_mpls_tunnel_del(sw_if_index);
     895             :         }
     896           0 :         else if (!vnet_mpls_tunnel_path_remove(sw_if_index, rpaths))
     897             :         {
     898           0 :             vnet_mpls_tunnel_del(sw_if_index);
     899             :         }
     900             :     }
     901             :     else
     902             :     {
     903           0 :         if (0 == vec_len(rpath.frp_label_stack))
     904             :         {
     905           0 :             error = clib_error_return (0, "No Output Labels '%U'",
     906             :                                        format_unformat_error, line_input);
     907           0 :             goto done;
     908             :         }
     909             : 
     910           0 :         if (~0 == sw_if_index)
     911             :         {
     912           0 :             sw_if_index = vnet_mpls_tunnel_create(l2_only, is_multicast, NULL);
     913             :         }
     914           0 :         vnet_mpls_tunnel_path_add(sw_if_index, rpaths);
     915             :     }
     916             : 
     917           0 : done:
     918           0 :     vec_free(rpaths);
     919           0 :     unformat_free (line_input);
     920             : 
     921           0 :     return error;
     922             : }
     923             : 
     924             : /*?
     925             :  * This command create a uni-directional MPLS tunnel
     926             :  *
     927             :  * @cliexpar
     928             :  * @cliexstart{create mpls tunnel}
     929             :  *  create mpls tunnel via 10.0.0.1 GigEthernet0/8/0 out-label 33 out-label 34
     930             :  * @cliexend
     931             :  ?*/
     932      285289 : VLIB_CLI_COMMAND (create_mpls_tunnel_command, static) = {
     933             :   .path = "mpls tunnel",
     934             :   .short_help =
     935             :   "mpls tunnel [multicast] [l2-only] via [next-hop-address] [next-hop-interface] [next-hop-table <value>] [weight <value>] [preference <value>] [udp-encap-id <value>] [ip4-lookup-in-table <value>] [ip6-lookup-in-table <value>] [mpls-lookup-in-table <value>] [resolve-via-host] [resolve-via-connected] [rx-ip4 <interface>] [out-labels <value value value>]",
     936             :   .function = vnet_create_mpls_tunnel_command_fn,
     937             : };
     938             : 
     939             : static u8 *
     940           1 : format_mpls_tunnel (u8 * s, va_list * args)
     941             : {
     942           1 :     mpls_tunnel_t *mt = va_arg (*args, mpls_tunnel_t *);
     943             :     mpls_tunnel_attribute_t attr;
     944             : 
     945           1 :     s = format(s, "mpls-tunnel%d: sw_if_index:%d hw_if_index:%d",
     946           1 :                mt - mpls_tunnel_pool,
     947             :                mt->mt_sw_if_index,
     948             :                mt->mt_hw_if_index);
     949           1 :     if (MPLS_TUNNEL_FLAG_NONE != mt->mt_flags) {
     950           0 :         s = format(s, " \n flags:");
     951           0 :         FOR_EACH_MPLS_TUNNEL_ATTRIBUTE(attr) {
     952           0 :             if ((1<<attr) & mt->mt_flags) {
     953           0 :                 s = format (s, "%s,", mpls_tunnel_attribute_names[attr]);
     954             :             }
     955             :         }
     956             :     }
     957           1 :     s = format(s, "\n via:\n");
     958           1 :     s = fib_path_list_format(mt->mt_path_list, s);
     959           1 :     s = format(s, "%U", format_fib_path_ext_list, &mt->mt_path_exts);
     960           1 :     s = format(s, "\n");
     961             : 
     962           1 :     if (mt->mt_flags & MPLS_TUNNEL_FLAG_L2)
     963             :     {
     964           0 :         s = format(s, " forwarding: %U\n",
     965             :                    format_fib_forw_chain_type,
     966             :                    FIB_FORW_CHAIN_TYPE_ETHERNET);
     967           0 :         s = format(s, " %U\n", format_dpo_id, &mt->mt_l2_lb, 2);
     968             :     }
     969             : 
     970           1 :     return (s);
     971             : }
     972             : 
     973             : static clib_error_t *
     974           1 : show_mpls_tunnel_command_fn (vlib_main_t * vm,
     975             :                              unformat_input_t * input,
     976             :                              vlib_cli_command_t * cmd)
     977             : {
     978             :     mpls_tunnel_t * mt;
     979           1 :     u32 mti = ~0;
     980             : 
     981           1 :     if (pool_elts (mpls_tunnel_pool) == 0)
     982           0 :         vlib_cli_output (vm, "No MPLS tunnels configured...");
     983             : 
     984           2 :     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     985             :     {
     986           1 :         if (unformat (input, "%d", &mti))
     987             :             ;
     988             :         else
     989           0 :             break;
     990             :     }
     991             : 
     992           1 :     if (~0 == mti)
     993             :     {
     994           0 :         pool_foreach (mt, mpls_tunnel_pool)
     995             :          {
     996           0 :             vlib_cli_output (vm, "[@%d] %U",
     997           0 :                              mt - mpls_tunnel_pool,
     998             :                              format_mpls_tunnel, mt);
     999             :         }
    1000             :     }
    1001             :     else
    1002             :     {
    1003           1 :         if (pool_is_free_index(mpls_tunnel_pool, mti))
    1004           0 :             return clib_error_return (0, "Not a tunnel index %d", mti);
    1005             : 
    1006           1 :         mt = pool_elt_at_index(mpls_tunnel_pool, mti);
    1007             : 
    1008           1 :         vlib_cli_output (vm, "[@%d] %U",
    1009           1 :                          mt - mpls_tunnel_pool,
    1010             :                          format_mpls_tunnel, mt);
    1011             :     }
    1012             : 
    1013           1 :     return 0;
    1014             : }
    1015             : 
    1016             : /*?
    1017             :  * This command to show MPLS tunnels
    1018             :  *
    1019             :  * @cliexpar
    1020             :  * @cliexstart{sh mpls tunnel 2}
    1021             :  * [@2] mpls_tunnel2: sw_if_index:5 hw_if_index:5
    1022             :  *  label-stack:
    1023             :  *    3,
    1024             :  *  via:
    1025             :  *   index:26 locks:1 proto:ipv4 uPRF-list:26 len:1 itfs:[2, ]
    1026             :  *     index:26 pl-index:26 ipv4 weight=1 attached-nexthop:  oper-flags:resolved,
    1027             :  *      10.0.0.2 loop0
    1028             :  *         [@0]: ipv4 via 10.0.0.2 loop0: IP4: de:ad:00:00:00:00 -> 00:00:11:aa:bb:cc
    1029             :  * @cliexend
    1030             :  ?*/
    1031      285289 : VLIB_CLI_COMMAND (show_mpls_tunnel_command, static) = {
    1032             :     .path = "show mpls tunnel",
    1033             :     .function = show_mpls_tunnel_command_fn,
    1034             : };
    1035             : 
    1036             : static mpls_tunnel_t *
    1037         905 : mpls_tunnel_from_fib_node (fib_node_t *node)
    1038             : {
    1039         905 :     ASSERT(FIB_NODE_TYPE_MPLS_TUNNEL == node->fn_type);
    1040         905 :     return ((mpls_tunnel_t*) (((char*)node) -
    1041             :                              STRUCT_OFFSET_OF(mpls_tunnel_t, mt_node)));
    1042             : }
    1043             : 
    1044             : /**
    1045             :  * Function definition to backwalk a FIB node
    1046             :  */
    1047             : static fib_node_back_walk_rc_t
    1048         905 : mpls_tunnel_back_walk (fib_node_t *node,
    1049             :                       fib_node_back_walk_ctx_t *ctx)
    1050             : {
    1051         905 :     mpls_tunnel_restack(mpls_tunnel_from_fib_node(node));
    1052             : 
    1053         905 :     return (FIB_NODE_BACK_WALK_CONTINUE);
    1054             : }
    1055             : 
    1056             : /**
    1057             :  * Function definition to get a FIB node from its index
    1058             :  */
    1059             : static fib_node_t*
    1060         905 : mpls_tunnel_fib_node_get (fib_node_index_t index)
    1061             : {
    1062             :     mpls_tunnel_t * mt;
    1063             : 
    1064         905 :     mt = pool_elt_at_index(mpls_tunnel_pool, index);
    1065             : 
    1066         905 :     return (&mt->mt_node);
    1067             : }
    1068             : 
    1069             : /**
    1070             :  * Function definition to inform the FIB node that its last lock has gone.
    1071             :  */
    1072             : static void
    1073           0 : mpls_tunnel_last_lock_gone (fib_node_t *node)
    1074             : {
    1075             :     /*
    1076             :      * The MPLS MPLS tunnel is a root of the graph. As such
    1077             :      * it never has children and thus is never locked.
    1078             :      */
    1079           0 :     ASSERT(0);
    1080           0 : }
    1081             : 
    1082             : /*
    1083             :  * Virtual function table registered by MPLS MPLS tunnels
    1084             :  * for participation in the FIB object graph.
    1085             :  */
    1086             : const static fib_node_vft_t mpls_vft = {
    1087             :     .fnv_get = mpls_tunnel_fib_node_get,
    1088             :     .fnv_last_lock = mpls_tunnel_last_lock_gone,
    1089             :     .fnv_back_walk = mpls_tunnel_back_walk,
    1090             : };
    1091             : 
    1092             : static clib_error_t *
    1093         575 : mpls_tunnel_init (vlib_main_t *vm)
    1094             : {
    1095         575 :   fib_node_register_type(FIB_NODE_TYPE_MPLS_TUNNEL, &mpls_vft);
    1096             : 
    1097         575 :   return 0;
    1098             : }
    1099       65087 : VLIB_INIT_FUNCTION(mpls_tunnel_init);

Generated by: LCOV version 1.14