LCOV - code coverage report
Current view: top level - plugins/gtpu - gtpu.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 259 498 52.0 %
Date: 2023-07-05 22:20:52 Functions: 37 49 75.5 %

          Line data    Source code
       1             : /*
       2             :  *------------------------------------------------------------------
       3             :  * Copyright (c) 2017 Intel and/or its affiliates.
       4             :  * Licensed under the Apache License, Version 2.0 (the "License");
       5             :  * you may not use this file except in compliance with the License.
       6             :  * You may obtain a copy of the License at:
       7             :  *
       8             :  *     http://www.apache.org/licenses/LICENSE-2.0
       9             :  *
      10             :  * Unless required by applicable law or agreed to in writing, software
      11             :  * distributed under the License is distributed on an "AS IS" BASIS,
      12             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13             :  * See the License for the specific language governing permissions and
      14             :  * limitations under the License.
      15             :  *------------------------------------------------------------------
      16             :  */
      17             : #include <stdint.h>
      18             : #include <net/if.h>
      19             : #include <sys/ioctl.h>
      20             : #include <inttypes.h>
      21             : 
      22             : #include <vlib/vlib.h>
      23             : #include <vlib/unix/unix.h>
      24             : #include <vnet/ethernet/ethernet.h>
      25             : #include <vnet/fib/fib_entry.h>
      26             : #include <vnet/fib/fib_table.h>
      27             : #include <vnet/fib/fib_entry_track.h>
      28             : #include <vnet/mfib/mfib_table.h>
      29             : #include <vnet/adj/adj_mcast.h>
      30             : #include <vnet/dpo/dpo.h>
      31             : #include <vnet/plugin/plugin.h>
      32             : #include <vpp/app/version.h>
      33             : #include <gtpu/gtpu.h>
      34             : #include <vnet/flow/flow.h>
      35             : 
      36             : gtpu_main_t gtpu_main;
      37             : 
      38             : /* *INDENT-OFF* */
      39       50423 : VNET_FEATURE_INIT (ip4_gtpu_bypass, static) = {
      40             :   .arc_name = "ip4-unicast",
      41             :   .node_name = "ip4-gtpu-bypass",
      42             :   .runs_before = VNET_FEATURES ("ip4-lookup"),
      43             : };
      44             : 
      45       50423 : VNET_FEATURE_INIT (ip6_gtpu_bypass, static) = {
      46             :   .arc_name = "ip6-unicast",
      47             :   .node_name = "ip6-gtpu-bypass",
      48             :   .runs_before = VNET_FEATURES ("ip6-lookup"),
      49             : };
      50             : /* *INDENT-on* */
      51             : 
      52          24 : u8 * format_gtpu_encap_trace (u8 * s, va_list * args)
      53             : {
      54          24 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
      55          24 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
      56          24 :   gtpu_encap_trace_t * t
      57             :       = va_arg (*args, gtpu_encap_trace_t *);
      58             : 
      59          24 :   s = format (s, "GTPU encap to gtpu_tunnel%d tteid %d",
      60             :               t->tunnel_index, t->tteid);
      61          24 :   return s;
      62             : }
      63             : 
      64             : static u8 *
      65         110 : format_decap_next (u8 * s, va_list * args)
      66             : {
      67         110 :   u32 next_index = va_arg (*args, u32);
      68             : 
      69         110 :   switch (next_index)
      70             :     {
      71           0 :     case GTPU_INPUT_NEXT_DROP:
      72           0 :       return format (s, "drop");
      73         110 :     case GTPU_INPUT_NEXT_L2_INPUT:
      74         110 :       return format (s, "l2");
      75           0 :     case GTPU_INPUT_NEXT_IP4_INPUT:
      76           0 :       return format (s, "ip4");
      77           0 :     case GTPU_INPUT_NEXT_IP6_INPUT:
      78           0 :       return format (s, "ip6");
      79           0 :     default:
      80           0 :       return format (s, "index %d", next_index);
      81             :     }
      82             :   return s;
      83             : }
      84             : 
      85             : u8 *
      86         110 : format_gtpu_tunnel (u8 * s, va_list * args)
      87             : {
      88         110 :   gtpu_tunnel_t *t = va_arg (*args, gtpu_tunnel_t *);
      89         110 :   gtpu_main_t *ngm = &gtpu_main;
      90         110 :   ip4_main_t *im4 = &ip4_main;
      91         110 :   ip6_main_t *im6 = &ip6_main;
      92         110 :   u8 is_ipv6 = !ip46_address_is_ip4 (&t->dst);
      93             : 
      94         110 :   u32 encap_vrf_id =
      95         110 :     is_ipv6 ? im6->fibs[t->encap_fib_index].ft_table_id :
      96         110 :     im4->fibs[t->encap_fib_index].ft_table_id;
      97             : 
      98         110 :   s = format (s, "[%d] src %U dst %U teid %d tteid %d "
      99             :               "encap-vrf-id %d sw-if-idx %d ",
     100         110 :               t - ngm->tunnels,
     101             :               format_ip46_address, &t->src, IP46_TYPE_ANY,
     102             :               format_ip46_address, &t->dst, IP46_TYPE_ANY,
     103             :               t->teid, t->tteid, encap_vrf_id, t->sw_if_index);
     104             : 
     105         110 :   s = format (s, "encap-dpo-idx %d ", t->next_dpo.dpoi_index);
     106         110 :   s = format (s, "decap-next-%U ", format_decap_next, t->decap_next_index);
     107             : 
     108         110 :   if (PREDICT_FALSE (ip46_address_is_multicast (&t->dst)))
     109           5 :     s = format (s, "mcast-sw-if-idx %d ", t->mcast_sw_if_index);
     110             : 
     111         110 :   return s;
     112             : }
     113             : 
     114             : static u8 *
     115         316 : format_gtpu_name (u8 * s, va_list * args)
     116             : {
     117         316 :   u32 dev_instance = va_arg (*args, u32);
     118         316 :   return format (s, "gtpu_tunnel%d", dev_instance);
     119             : }
     120             : 
     121             : static clib_error_t *
     122         106 : gtpu_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
     123             : {
     124         106 :   u32 hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ?
     125             :     VNET_HW_INTERFACE_FLAG_LINK_UP : 0;
     126         106 :   vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
     127             : 
     128         106 :   return /* no error */ 0;
     129             : }
     130             : 
     131             : /* *INDENT-OFF* */
     132        7839 : VNET_DEVICE_CLASS (gtpu_device_class,static) = {
     133             :   .name = "GTPU",
     134             :   .format_device_name = format_gtpu_name,
     135             :   .format_tx_trace = format_gtpu_encap_trace,
     136             :   .admin_up_down_function = gtpu_interface_admin_up_down,
     137             : };
     138             : /* *INDENT-ON* */
     139             : 
     140             : static u8 *
     141           0 : format_gtpu_header_with_length (u8 * s, va_list * args)
     142             : {
     143           0 :   u32 dev_instance = va_arg (*args, u32);
     144           0 :   s = format (s, "unimplemented dev %u", dev_instance);
     145           0 :   return s;
     146             : }
     147             : 
     148             : /* *INDENT-OFF* */
     149        4479 : VNET_HW_INTERFACE_CLASS (gtpu_hw_class) =
     150             : {
     151             :   .name = "GTPU",
     152             :   .format_header = format_gtpu_header_with_length,
     153             :   .build_rewrite = default_build_rewrite,
     154             :   .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
     155             : };
     156             : /* *INDENT-ON* */
     157             : 
     158             : static void
     159          33 : gtpu_tunnel_restack_dpo (gtpu_tunnel_t * t)
     160             : {
     161          33 :   dpo_id_t dpo = DPO_INVALID;
     162          33 :   u32 encap_index = ip46_address_is_ip4 (&t->dst) ?
     163          33 :     gtpu4_encap_node.index : gtpu6_encap_node.index;
     164          33 :   fib_forward_chain_type_t forw_type = ip46_address_is_ip4 (&t->dst) ?
     165          33 :     FIB_FORW_CHAIN_TYPE_UNICAST_IP4 : FIB_FORW_CHAIN_TYPE_UNICAST_IP6;
     166             : 
     167          33 :   fib_entry_contribute_forwarding (t->fib_entry_index, forw_type, &dpo);
     168          33 :   dpo_stack_from_node (encap_index, &t->next_dpo, &dpo);
     169          33 :   dpo_reset (&dpo);
     170          33 : }
     171             : 
     172             : static gtpu_tunnel_t *
     173          10 : gtpu_tunnel_from_fib_node (fib_node_t * node)
     174             : {
     175          10 :   return ((gtpu_tunnel_t *) (((char *) node) -
     176             :                              STRUCT_OFFSET_OF (gtpu_tunnel_t, node)));
     177             : }
     178             : 
     179             : /**
     180             :  * Function definition to backwalk a FIB node -
     181             :  * Here we will restack the new dpo of GTPU DIP to encap node.
     182             :  */
     183             : static fib_node_back_walk_rc_t
     184          10 : gtpu_tunnel_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
     185             : {
     186          10 :   gtpu_tunnel_restack_dpo (gtpu_tunnel_from_fib_node (node));
     187          10 :   return (FIB_NODE_BACK_WALK_CONTINUE);
     188             : }
     189             : 
     190             : /**
     191             :  * Function definition to get a FIB node from its index
     192             :  */
     193             : static fib_node_t *
     194          10 : gtpu_tunnel_fib_node_get (fib_node_index_t index)
     195             : {
     196             :   gtpu_tunnel_t *t;
     197          10 :   gtpu_main_t *gtm = &gtpu_main;
     198             : 
     199          10 :   t = pool_elt_at_index (gtm->tunnels, index);
     200             : 
     201          10 :   return (&t->node);
     202             : }
     203             : 
     204             : /**
     205             :  * Function definition to inform the FIB node that its last lock has gone.
     206             :  */
     207             : static void
     208           0 : gtpu_tunnel_last_lock_gone (fib_node_t * node)
     209             : {
     210             :   /*
     211             :    * The GTPU tunnel is a root of the graph. As such
     212             :    * it never has children and thus is never locked.
     213             :    */
     214           0 :   ASSERT (0);
     215           0 : }
     216             : 
     217             : /*
     218             :  * Virtual function table registered by GTPU tunnels
     219             :  * for participation in the FIB object graph.
     220             :  */
     221             : const static fib_node_vft_t gtpu_vft = {
     222             :   .fnv_get = gtpu_tunnel_fib_node_get,
     223             :   .fnv_last_lock = gtpu_tunnel_last_lock_gone,
     224             :   .fnv_back_walk = gtpu_tunnel_back_walk,
     225             : };
     226             : 
     227             : 
     228             : #define foreach_copy_field                      \
     229             : _(teid)                                         \
     230             : _(tteid)                                        \
     231             : _(mcast_sw_if_index)                            \
     232             : _(encap_fib_index)                              \
     233             : _(decap_next_index)                             \
     234             : _(src)                                          \
     235             : _(dst)
     236             : 
     237             : static void
     238          64 : ip_udp_gtpu_rewrite (gtpu_tunnel_t * t, bool is_ip6)
     239             : {
     240             :   union
     241             :   {
     242             :     ip4_gtpu_header_t *h4;
     243             :     ip6_gtpu_header_t *h6;
     244             :     u8 *rw;
     245          64 :   } r =
     246             :   {
     247             :   .rw = 0};
     248          64 :   int len = is_ip6 ? sizeof *r.h6 : sizeof *r.h4;
     249             : 
     250          64 :   vec_validate_aligned (r.rw, len - 1, CLIB_CACHE_LINE_BYTES);
     251             : 
     252             :   udp_header_t *udp;
     253             :   gtpu_header_t *gtpu;
     254             :   /* Fixed portion of the (outer) ip header */
     255          64 :   if (!is_ip6)
     256             :     {
     257          63 :       ip4_header_t *ip = &r.h4->ip4;
     258          63 :       udp = &r.h4->udp;
     259          63 :       gtpu = &r.h4->gtpu;
     260          63 :       ip->ip_version_and_header_length = 0x45;
     261          63 :       ip->ttl = 254;
     262          63 :       ip->protocol = IP_PROTOCOL_UDP;
     263             : 
     264          63 :       ip->src_address = t->src.ip4;
     265          63 :       ip->dst_address = t->dst.ip4;
     266             : 
     267             :       /* we fix up the ip4 header length and checksum after-the-fact */
     268          63 :       ip->checksum = ip4_header_checksum (ip);
     269             :     }
     270             :   else
     271             :     {
     272           1 :       ip6_header_t *ip = &r.h6->ip6;
     273           1 :       udp = &r.h6->udp;
     274           1 :       gtpu = &r.h6->gtpu;
     275           1 :       ip->ip_version_traffic_class_and_flow_label =
     276           1 :         clib_host_to_net_u32 (6 << 28);
     277           1 :       ip->hop_limit = 255;
     278           1 :       ip->protocol = IP_PROTOCOL_UDP;
     279             : 
     280           1 :       ip->src_address = t->src.ip6;
     281           1 :       ip->dst_address = t->dst.ip6;
     282             :     }
     283             : 
     284             :   /* UDP header, randomize src port on something, maybe? */
     285          64 :   udp->src_port = clib_host_to_net_u16 (2152);
     286          64 :   udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_GTPU);
     287             : 
     288             :   /* GTPU header */
     289          64 :   gtpu->ver_flags = GTPU_V1_VER | GTPU_PT_GTP;
     290          64 :   gtpu->type = GTPU_TYPE_GTPU;
     291          64 :   gtpu->teid = clib_host_to_net_u32 (t->tteid);
     292             : 
     293          64 :   t->rewrite = r.rw;
     294             :   /* Now only support 8-byte gtpu header. TBD */
     295          64 :   vec_set_len (t->rewrite, sizeof (ip4_gtpu_header_t) - 4);
     296             : 
     297          64 :   return;
     298             : }
     299             : 
     300             : static bool
     301          64 : gtpu_decap_next_is_valid (gtpu_main_t * gtm, u32 is_ip6, u32 decap_next_index)
     302             : {
     303          64 :   vlib_main_t *vm = gtm->vlib_main;
     304          64 :   u32 input_idx = (!is_ip6) ? gtpu4_input_node.index : gtpu6_input_node.index;
     305          64 :   vlib_node_runtime_t *r = vlib_node_get_runtime (vm, input_idx);
     306             : 
     307          64 :   return decap_next_index < r->n_next_nodes;
     308             : }
     309             : 
     310             : typedef CLIB_PACKED (union
     311             :                      {
     312             :                      struct
     313             :                      {
     314             :                      fib_node_index_t mfib_entry_index;
     315             :                      adj_index_t mcast_adj_index;
     316             :                      }; u64 as_u64;
     317             :                      }) mcast_shared_t;
     318             : 
     319             : static inline mcast_shared_t
     320          61 : mcast_shared_get (ip46_address_t * ip)
     321             : {
     322          61 :   ASSERT (ip46_address_is_multicast (ip));
     323          61 :   uword *p = hash_get_mem (gtpu_main.mcast_shared, ip);
     324          61 :   ALWAYS_ASSERT (p);
     325         122 :   return (mcast_shared_t)
     326             :   {
     327          61 :   .as_u64 = *p};
     328             : }
     329             : 
     330             : static inline void
     331          21 : mcast_shared_add (ip46_address_t * dst, fib_node_index_t mfei, adj_index_t ai)
     332             : {
     333          21 :   mcast_shared_t new_ep = {
     334             :     .mcast_adj_index = ai,
     335             :     .mfib_entry_index = mfei,
     336             :   };
     337             : 
     338          21 :   hash_set_mem_alloc (&gtpu_main.mcast_shared, dst, new_ep.as_u64);
     339          21 : }
     340             : 
     341             : static inline void
     342          20 : mcast_shared_remove (ip46_address_t * dst)
     343             : {
     344          20 :   mcast_shared_t ep = mcast_shared_get (dst);
     345             : 
     346          20 :   adj_unlock (ep.mcast_adj_index);
     347          20 :   mfib_table_entry_delete_index (ep.mfib_entry_index, MFIB_SOURCE_GTPU);
     348             : 
     349          20 :   hash_unset_mem_free (&gtpu_main.mcast_shared, dst);
     350          20 : }
     351             : 
     352         106 : int vnet_gtpu_add_mod_del_tunnel
     353             :   (vnet_gtpu_add_mod_del_tunnel_args_t * a, u32 * sw_if_indexp)
     354             : {
     355         106 :   gtpu_main_t *gtm = &gtpu_main;
     356         106 :   gtpu_tunnel_t *t = 0;
     357         106 :   vnet_main_t *vnm = gtm->vnet_main;
     358             :   uword *p;
     359         106 :   u32 hw_if_index = ~0;
     360         106 :   u32 sw_if_index = ~0;
     361             :   gtpu4_tunnel_key_t key4;
     362             :   gtpu6_tunnel_key_t key6;
     363         106 :   bool is_ip6 = !ip46_address_is_ip4 (&a->dst);
     364             : 
     365         106 :   if (!is_ip6)
     366             :     {
     367         104 :       key4.src = a->dst.ip4.as_u32;  /* decap src in key is encap dst in config */
     368         104 :       key4.teid = clib_host_to_net_u32 (a->teid);
     369         104 :       p = hash_get (gtm->gtpu4_tunnel_by_key, key4.as_u64);
     370             :     }
     371             :   else
     372             :     {
     373           2 :       key6.src = a->dst.ip6;
     374           2 :       key6.teid = clib_host_to_net_u32 (a->teid);
     375           2 :       p = hash_get_mem (gtm->gtpu6_tunnel_by_key, &key6);
     376             :     }
     377             : 
     378         106 :   if (a->opn == GTPU_ADD_TUNNEL)
     379             :     {
     380          64 :       l2input_main_t *l2im = &l2input_main;
     381             : 
     382             :       /* adding a tunnel: tunnel must not already exist */
     383          64 :       if (p)
     384           0 :         return VNET_API_ERROR_TUNNEL_EXIST;
     385             : 
     386             :       /*if not set explicitly, default to l2 */
     387          64 :       if (a->decap_next_index == ~0)
     388          64 :         a->decap_next_index = GTPU_INPUT_NEXT_L2_INPUT;
     389          64 :       if (!gtpu_decap_next_is_valid (gtm, is_ip6, a->decap_next_index))
     390           0 :         return VNET_API_ERROR_INVALID_DECAP_NEXT;
     391             : 
     392          64 :       pool_get_aligned (gtm->tunnels, t, CLIB_CACHE_LINE_BYTES);
     393          64 :       clib_memset (t, 0, sizeof (*t));
     394             : 
     395             :       /* copy from arg structure */
     396             : #define _(x) t->x = a->x;
     397          64 :       foreach_copy_field;
     398             : #undef _
     399             : 
     400             :       /* default to same as local rx teid */
     401          64 :       if (t->tteid == 0)
     402          64 :         t->tteid = t->teid;
     403             : 
     404          64 :       ip_udp_gtpu_rewrite (t, is_ip6);
     405             : 
     406             :       /* clear the flow index */
     407          64 :       t->flow_index = ~0;
     408             : 
     409             :       /* copy the key */
     410          64 :       if (is_ip6)
     411           1 :         hash_set_mem_alloc (&gtm->gtpu6_tunnel_by_key, &key6,
     412           1 :                             t - gtm->tunnels);
     413             :       else
     414          63 :         hash_set (gtm->gtpu4_tunnel_by_key, key4.as_u64, t - gtm->tunnels);
     415             : 
     416             :       vnet_hw_interface_t *hi;
     417          64 :       if (vec_len (gtm->free_gtpu_tunnel_hw_if_indices) > 0)
     418          10 :         {
     419          10 :           vnet_interface_main_t *im = &vnm->interface_main;
     420          20 :           hw_if_index = gtm->free_gtpu_tunnel_hw_if_indices
     421          10 :             [vec_len (gtm->free_gtpu_tunnel_hw_if_indices) - 1];
     422          10 :           vec_dec_len (gtm->free_gtpu_tunnel_hw_if_indices, 1);
     423             : 
     424          10 :           hi = vnet_get_hw_interface (vnm, hw_if_index);
     425          10 :           hi->dev_instance = t - gtm->tunnels;
     426          10 :           hi->hw_instance = hi->dev_instance;
     427             : 
     428             :           /* clear old stats of freed tunnel before reuse */
     429          10 :           sw_if_index = hi->sw_if_index;
     430          10 :           vnet_interface_counter_lock (im);
     431          10 :           vlib_zero_combined_counter
     432          10 :             (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_TX],
     433             :              sw_if_index);
     434          10 :           vlib_zero_combined_counter (&im->combined_sw_if_counters
     435             :                                       [VNET_INTERFACE_COUNTER_RX],
     436             :                                       sw_if_index);
     437          10 :           vlib_zero_simple_counter (&im->sw_if_counters
     438             :                                     [VNET_INTERFACE_COUNTER_DROP],
     439             :                                     sw_if_index);
     440          10 :           vnet_interface_counter_unlock (im);
     441             :         }
     442             :       else
     443             :         {
     444          54 :           hw_if_index = vnet_register_interface
     445          54 :             (vnm, gtpu_device_class.index, t - gtm->tunnels,
     446          54 :              gtpu_hw_class.index, t - gtm->tunnels);
     447          54 :           hi = vnet_get_hw_interface (vnm, hw_if_index);
     448             :         }
     449             : 
     450             :       /* Set gtpu tunnel output node */
     451         128 :       u32 encap_index = !is_ip6 ?
     452          64 :         gtpu4_encap_node.index : gtpu6_encap_node.index;
     453          64 :       vnet_set_interface_output_node (vnm, hw_if_index, encap_index);
     454             : 
     455          64 :       t->hw_if_index = hw_if_index;
     456          64 :       t->sw_if_index = sw_if_index = hi->sw_if_index;
     457             : 
     458         125 :       vec_validate_init_empty (gtm->tunnel_index_by_sw_if_index, sw_if_index,
     459             :                                ~0);
     460          64 :       gtm->tunnel_index_by_sw_if_index[sw_if_index] = t - gtm->tunnels;
     461             : 
     462             :       /* setup l2 input config with l2 feature and bd 0 to drop packet */
     463          64 :       vec_validate (l2im->configs, sw_if_index);
     464          64 :       l2im->configs[sw_if_index].feature_bitmap = L2INPUT_FEAT_DROP;
     465          64 :       l2im->configs[sw_if_index].bd_index = 0;
     466             : 
     467          64 :       vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index);
     468          64 :       si->flags &= ~VNET_SW_INTERFACE_FLAG_HIDDEN;
     469          64 :       vnet_sw_interface_set_flags (vnm, sw_if_index,
     470             :                                    VNET_SW_INTERFACE_FLAG_ADMIN_UP);
     471             : 
     472          64 :       fib_node_init (&t->node, gtm->fib_node_type);
     473             :       fib_prefix_t tun_dst_pfx;
     474          64 :       vnet_flood_class_t flood_class = VNET_FLOOD_CLASS_TUNNEL_NORMAL;
     475             : 
     476          64 :       fib_protocol_t fp = fib_ip_proto (is_ip6);
     477          64 :       fib_prefix_from_ip46_addr (fp, &t->dst, &tun_dst_pfx);
     478          64 :       if (!ip46_address_is_multicast (&t->dst))
     479             :         {
     480             :           /* Unicast tunnel -
     481             :            * Track the FIB entry for the tunnel's destination.
     482             :            * The tunnel will then get poked
     483             :            * when the forwarding for the entry updates, and the tunnel can
     484             :            * re-stack accordingly
     485             :            */
     486          23 :           vtep_addr_ref (&gtm->vtep_table, t->encap_fib_index, &t->src);
     487          46 :           t->fib_entry_index = fib_entry_track (t->encap_fib_index,
     488             :                                                 &tun_dst_pfx,
     489          23 :                                                 gtm->fib_node_type,
     490          23 :                                                 t - gtm->tunnels,
     491          23 :                                                 &t->sibling_index);
     492          23 :           gtpu_tunnel_restack_dpo (t);
     493             :         }
     494             :       else
     495             :         {
     496             :           /* Multicast tunnel -
     497             :            * as the same mcast group can be used for multiple mcast tunnels
     498             :            * with different VNIs, create the output adjacency only if
     499             :            * it does not already exist
     500             :            */
     501          41 :           if (vtep_addr_ref (&gtm->vtep_table,
     502          41 :                              t->encap_fib_index, &t->dst) == 1)
     503             :             {
     504             :               fib_node_index_t mfei;
     505             :               adj_index_t ai;
     506          42 :               fib_route_path_t path = {
     507          21 :                 .frp_proto = fib_proto_to_dpo (fp),
     508             :                 .frp_addr = zero_addr,
     509             :                 .frp_sw_if_index = 0xffffffff,
     510             :                 .frp_fib_index = ~0,
     511             :                 .frp_weight = 1,
     512             :                 .frp_flags = FIB_ROUTE_PATH_LOCAL,
     513             :                 .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD,
     514             :               };
     515          21 :               const mfib_prefix_t mpfx = {
     516             :                 .fp_proto = fp,
     517             :                 .fp_len = (is_ip6 ? 128 : 32),
     518             :                 .fp_grp_addr = tun_dst_pfx.fp_addr,
     519             :               };
     520             : 
     521             :               /*
     522             :                * Setup the (*,G) to receive traffic on the mcast group
     523             :                *  - the forwarding interface is for-us
     524             :                *  - the accepting interface is that from the API
     525             :                */
     526          21 :               mfib_table_entry_path_update (t->encap_fib_index, &mpfx,
     527             :                                             MFIB_SOURCE_GTPU,
     528             :                                             MFIB_ENTRY_FLAG_NONE, &path);
     529             : 
     530          21 :               path.frp_sw_if_index = a->mcast_sw_if_index;
     531          21 :               path.frp_flags = FIB_ROUTE_PATH_FLAG_NONE;
     532          21 :               path.frp_mitf_flags = MFIB_ITF_FLAG_ACCEPT;
     533          21 :               mfei = mfib_table_entry_path_update (
     534          21 :                 t->encap_fib_index, &mpfx, MFIB_SOURCE_GTPU,
     535             :                 MFIB_ENTRY_FLAG_NONE, &path);
     536             : 
     537             :               /*
     538             :                * Create the mcast adjacency to send traffic to the group
     539             :                */
     540          21 :               ai = adj_mcast_add_or_lock (fp,
     541          21 :                                           fib_proto_to_link (fp),
     542             :                                           a->mcast_sw_if_index);
     543             : 
     544             :               /*
     545             :                * create a new end-point
     546             :                */
     547          21 :               mcast_shared_add (&t->dst, mfei, ai);
     548             :             }
     549             : 
     550          41 :           dpo_id_t dpo = DPO_INVALID;
     551          41 :           mcast_shared_t ep = mcast_shared_get (&t->dst);
     552             : 
     553             :           /* Stack shared mcast dst mac addr rewrite on encap */
     554          41 :           dpo_set (&dpo, DPO_ADJACENCY_MCAST,
     555          41 :                    fib_proto_to_dpo (fp), ep.mcast_adj_index);
     556             : 
     557          41 :           dpo_stack_from_node (encap_index, &t->next_dpo, &dpo);
     558             : 
     559          41 :           dpo_reset (&dpo);
     560          41 :           flood_class = VNET_FLOOD_CLASS_TUNNEL_MASTER;
     561             :         }
     562             : 
     563          64 :       vnet_get_sw_interface (vnet_get_main (), sw_if_index)->flood_class =
     564             :         flood_class;
     565             :     }
     566             :   else
     567             :     {
     568             :       /* mod-tteid or deleting a tunnel: tunnel must exist */
     569          42 :       if (!p)
     570           0 :         return VNET_API_ERROR_NO_SUCH_ENTRY;
     571             : 
     572          42 :       t = pool_elt_at_index (gtm->tunnels, p[0]);
     573          42 :       sw_if_index = t->sw_if_index;
     574             : 
     575          42 :       if (a->opn == GTPU_UPD_TTEID)
     576             :         {
     577           0 :           if (a->tteid == 0)
     578           0 :             return VNET_API_ERROR_INVALID_VALUE;
     579           0 :           t->tteid = a->tteid;
     580           0 :           vec_free (t->rewrite);
     581           0 :           ip_udp_gtpu_rewrite (t, is_ip6);
     582           0 :           return 0;
     583             :         }
     584             : 
     585             :       /* delete tunnel */
     586          42 :       vnet_sw_interface_set_flags (vnm, t->sw_if_index, 0 /* down */ );
     587          42 :       vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, t->sw_if_index);
     588          42 :       si->flags |= VNET_SW_INTERFACE_FLAG_HIDDEN;
     589             : 
     590             :       /* make sure tunnel is removed from l2 bd or xconnect */
     591          42 :       set_int_l2_mode (gtm->vlib_main, vnm, MODE_L3, t->sw_if_index, 0,
     592             :                        L2_BD_PORT_TYPE_NORMAL, 0, 0);
     593          42 :       vec_add1 (gtm->free_gtpu_tunnel_hw_if_indices, t->hw_if_index);
     594             : 
     595          42 :       gtm->tunnel_index_by_sw_if_index[t->sw_if_index] = ~0;
     596             : 
     597          42 :       if (!is_ip6)
     598          41 :         hash_unset (gtm->gtpu4_tunnel_by_key, key4.as_u64);
     599             :       else
     600           1 :         hash_unset_mem_free (&gtm->gtpu6_tunnel_by_key, &key6);
     601             : 
     602          42 :       if (!ip46_address_is_multicast (&t->dst))
     603             :         {
     604           2 :           if (t->flow_index != ~0)
     605           0 :             vnet_flow_del (vnm, t->flow_index);
     606             : 
     607           2 :           vtep_addr_unref (&gtm->vtep_table, t->encap_fib_index, &t->src);
     608           2 :           fib_entry_untrack (t->fib_entry_index, t->sibling_index);
     609             :         }
     610          40 :       else if (vtep_addr_unref (&gtm->vtep_table,
     611          40 :                                 t->encap_fib_index, &t->dst) == 0)
     612             :         {
     613          20 :           mcast_shared_remove (&t->dst);
     614             :         }
     615             : 
     616          42 :       fib_node_deinit (&t->node);
     617          42 :       vec_free (t->rewrite);
     618          42 :       pool_put (gtm->tunnels, t);
     619             :     }
     620             : 
     621         106 :   if (sw_if_indexp)
     622         106 :     *sw_if_indexp = sw_if_index;
     623             : 
     624         106 :   if (a->opn == GTPU_ADD_TUNNEL)
     625             :     {
     626             :       /* register udp ports */
     627          64 :       if (!is_ip6 && !udp_is_valid_dst_port (UDP_DST_PORT_GTPU, 1))
     628           2 :         udp_register_dst_port (gtm->vlib_main, UDP_DST_PORT_GTPU,
     629             :                                gtpu4_input_node.index, /* is_ip4 */ 1);
     630          64 :       if (is_ip6 && !udp_is_valid_dst_port (UDP_DST_PORT_GTPU6, 0))
     631           1 :         udp_register_dst_port (gtm->vlib_main, UDP_DST_PORT_GTPU6,
     632             :                                gtpu6_input_node.index, /* is_ip4 */ 0);
     633             :     }
     634             : 
     635         106 :   return 0;
     636             : }
     637             : 
     638             : static uword
     639           0 : get_decap_next_for_node (u32 node_index, u32 ipv4_set)
     640             : {
     641           0 :   gtpu_main_t *gtm = &gtpu_main;
     642           0 :   vlib_main_t *vm = gtm->vlib_main;
     643           0 :   uword input_node = (ipv4_set) ? gtpu4_input_node.index :
     644           0 :     gtpu6_input_node.index;
     645             : 
     646           0 :   return vlib_node_add_next (vm, input_node, node_index);
     647             : }
     648             : 
     649             : static uword
     650           0 : unformat_decap_next (unformat_input_t * input, va_list * args)
     651             : {
     652           0 :   u32 *result = va_arg (*args, u32 *);
     653           0 :   u32 ipv4_set = va_arg (*args, int);
     654           0 :   gtpu_main_t *gtm = &gtpu_main;
     655           0 :   vlib_main_t *vm = gtm->vlib_main;
     656             :   u32 node_index;
     657             :   u32 tmp;
     658             : 
     659           0 :   if (unformat (input, "l2"))
     660           0 :     *result = GTPU_INPUT_NEXT_L2_INPUT;
     661           0 :   else if (unformat (input, "ip4"))
     662           0 :     *result = GTPU_INPUT_NEXT_IP4_INPUT;
     663           0 :   else if (unformat (input, "ip6"))
     664           0 :     *result = GTPU_INPUT_NEXT_IP6_INPUT;
     665           0 :   else if (unformat (input, "node %U", unformat_vlib_node, vm, &node_index))
     666           0 :     *result = get_decap_next_for_node (node_index, ipv4_set);
     667           0 :   else if (unformat (input, "%d", &tmp))
     668           0 :     *result = tmp;
     669             :   else
     670           0 :     return 0;
     671             : 
     672           0 :   return 1;
     673             : }
     674             : 
     675             : static clib_error_t *
     676           0 : gtpu_add_del_tunnel_command_fn (vlib_main_t * vm,
     677             :                                 unformat_input_t * input,
     678             :                                 vlib_cli_command_t * cmd)
     679             : {
     680           0 :   unformat_input_t _line_input, *line_input = &_line_input;
     681             :   ip46_address_t src, dst;
     682           0 :   u8 opn = GTPU_ADD_TUNNEL;
     683           0 :   u8 src_set = 0;
     684           0 :   u8 dst_set = 0;
     685           0 :   u8 grp_set = 0;
     686           0 :   u8 ipv4_set = 0;
     687           0 :   u8 ipv6_set = 0;
     688           0 :   u32 encap_fib_index = 0;
     689           0 :   u32 mcast_sw_if_index = ~0;
     690           0 :   u32 decap_next_index = GTPU_INPUT_NEXT_L2_INPUT;
     691           0 :   u32 teid = 0, tteid = 0;
     692             :   u32 tmp;
     693             :   int rv;
     694           0 :   vnet_gtpu_add_mod_del_tunnel_args_t _a, *a = &_a;
     695             :   u32 tunnel_sw_if_index;
     696           0 :   clib_error_t *error = NULL;
     697             : 
     698             :   /* Cant "universally zero init" (={0}) due to GCC bug 53119 */
     699           0 :   clib_memset (&src, 0, sizeof src);
     700           0 :   clib_memset (&dst, 0, sizeof dst);
     701             : 
     702             :   /* Get a line of input. */
     703           0 :   if (!unformat_user (input, unformat_line_input, line_input))
     704           0 :     return 0;
     705             : 
     706           0 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
     707             :     {
     708           0 :       if (unformat (line_input, "del"))
     709             :         {
     710           0 :           opn = GTPU_DEL_TUNNEL;
     711             :         }
     712           0 :       else if (unformat (line_input, "src %U",
     713             :                          unformat_ip4_address, &src.ip4))
     714             :         {
     715           0 :           src_set = 1;
     716           0 :           ipv4_set = 1;
     717             :         }
     718           0 :       else if (unformat (line_input, "dst %U",
     719             :                          unformat_ip4_address, &dst.ip4))
     720             :         {
     721           0 :           dst_set = 1;
     722           0 :           ipv4_set = 1;
     723             :         }
     724           0 :       else if (unformat (line_input, "src %U",
     725             :                          unformat_ip6_address, &src.ip6))
     726             :         {
     727           0 :           src_set = 1;
     728           0 :           ipv6_set = 1;
     729             :         }
     730           0 :       else if (unformat (line_input, "dst %U",
     731             :                          unformat_ip6_address, &dst.ip6))
     732             :         {
     733           0 :           dst_set = 1;
     734           0 :           ipv6_set = 1;
     735             :         }
     736           0 :       else if (unformat (line_input, "group %U %U",
     737             :                          unformat_ip4_address, &dst.ip4,
     738             :                          unformat_vnet_sw_interface,
     739             :                          vnet_get_main (), &mcast_sw_if_index))
     740             :         {
     741           0 :           grp_set = dst_set = 1;
     742           0 :           ipv4_set = 1;
     743             :         }
     744           0 :       else if (unformat (line_input, "group %U %U",
     745             :                          unformat_ip6_address, &dst.ip6,
     746             :                          unformat_vnet_sw_interface,
     747             :                          vnet_get_main (), &mcast_sw_if_index))
     748             :         {
     749           0 :           grp_set = dst_set = 1;
     750           0 :           ipv6_set = 1;
     751             :         }
     752           0 :       else if (unformat (line_input, "encap-vrf-id %d", &tmp))
     753             :         {
     754           0 :           encap_fib_index = fib_table_find (fib_ip_proto (ipv6_set), tmp);
     755           0 :           if (encap_fib_index == ~0)
     756             :             {
     757             :               error =
     758           0 :                 clib_error_return (0, "nonexistent encap-vrf-id %d", tmp);
     759           0 :               goto done;
     760             :             }
     761             :         }
     762           0 :       else if (unformat (line_input, "decap-next %U", unformat_decap_next,
     763             :                          &decap_next_index, ipv4_set))
     764             :         ;
     765           0 :       else if (unformat (line_input, "teid %d", &teid))
     766             :         ;
     767           0 :       else if (unformat (line_input, "tteid %d", &tteid))
     768             :         ;
     769           0 :       else if (unformat (line_input, "upd-tteid %d", &tteid))
     770           0 :         opn = GTPU_UPD_TTEID;
     771             :       else
     772             :         {
     773           0 :           error = clib_error_return (0, "parse error: '%U'",
     774             :                                      format_unformat_error, line_input);
     775           0 :           goto done;
     776             :         }
     777             :     }
     778             : 
     779           0 :   if (teid == 0)
     780             :     {
     781           0 :       error = clib_error_return (0, "tunnel teid specified");
     782           0 :       goto done;
     783             :     }
     784             : 
     785           0 :   if (src_set == 0 && opn == GTPU_ADD_TUNNEL)
     786             :     {
     787           0 :       error = clib_error_return (0, "tunnel src address not specified");
     788           0 :       goto done;
     789             :     }
     790             : 
     791           0 :   if (dst_set == 0)
     792             :     {
     793           0 :       error = clib_error_return (0, "tunnel dst address not specified");
     794           0 :       goto done;
     795             :     }
     796             : 
     797           0 :   if (grp_set && !ip46_address_is_multicast (&dst))
     798             :     {
     799           0 :       error = clib_error_return (0, "tunnel group address not multicast");
     800           0 :       goto done;
     801             :     }
     802             : 
     803           0 :   if (grp_set == 0 && ip46_address_is_multicast (&dst))
     804             :     {
     805           0 :       error = clib_error_return (0, "dst address must be unicast");
     806           0 :       goto done;
     807             :     }
     808             : 
     809           0 :   if (grp_set && mcast_sw_if_index == ~0)
     810             :     {
     811           0 :       error = clib_error_return (0, "tunnel nonexistent multicast device");
     812           0 :       goto done;
     813             :     }
     814             : 
     815           0 :   if (ipv4_set && ipv6_set)
     816             :     {
     817           0 :       error = clib_error_return (0, "both IPv4 and IPv6 addresses specified");
     818           0 :       goto done;
     819             :     }
     820             : 
     821           0 :   if (ip46_address_cmp (&src, &dst) == 0)
     822             :     {
     823           0 :       error = clib_error_return (0, "src and dst addresses are identical");
     824           0 :       goto done;
     825             :     }
     826             : 
     827           0 :   if (decap_next_index == ~0)
     828             :     {
     829           0 :       error = clib_error_return (0, "next node not found");
     830           0 :       goto done;
     831             :     }
     832             : 
     833           0 :   clib_memset (a, 0, sizeof (*a));
     834             : 
     835           0 :   a->opn = opn;
     836             : 
     837             : #define _(x) a->x = x;
     838           0 :   foreach_copy_field;
     839             : #undef _
     840             : 
     841           0 :   rv = vnet_gtpu_add_mod_del_tunnel (a, &tunnel_sw_if_index);
     842             : 
     843           0 :   switch (rv)
     844             :     {
     845           0 :     case 0:
     846           0 :       if (opn == GTPU_ADD_TUNNEL)
     847           0 :         vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name,
     848             :                          vnet_get_main (), tunnel_sw_if_index);
     849           0 :       break;
     850             : 
     851           0 :     case VNET_API_ERROR_TUNNEL_EXIST:
     852           0 :       error = clib_error_return (0, "tunnel already exists...");
     853           0 :       goto done;
     854             : 
     855           0 :     case VNET_API_ERROR_NO_SUCH_ENTRY:
     856           0 :       error = clib_error_return (0, "tunnel does not exist...");
     857           0 :       goto done;
     858             : 
     859           0 :     case VNET_API_ERROR_INVALID_VALUE:
     860           0 :       error = clib_error_return (0, "tx teid not specified...");
     861           0 :       goto done;
     862             : 
     863           0 :     default:
     864           0 :       error = clib_error_return
     865             :         (0, "vnet_gtpu_add_del_tunnel returned %d", rv);
     866           0 :       goto done;
     867             :     }
     868             : 
     869           0 : done:
     870           0 :   unformat_free (line_input);
     871             : 
     872           0 :   return error;
     873             : }
     874             : 
     875             : /*?
     876             :  * Add or delete a GTPU Tunnel.
     877             :  *
     878             :  * GTPU can be used to transport Ethernet packets as its PDU type to
     879             :  * provides allow L2 network or bridge domains (BDs)
     880             :  * to span multiple servers. This is done by building an L2 overlay on
     881             :  * top of an L3 network underlay using GTPU tunnels.
     882             :  *
     883             :  * GTPU can also be used to transport IP packets as its PDU type to
     884             :  * allow IP forwarding over underlay network, e.g. between RAN and UPF
     885             :  * for mobility deployments.
     886             :  *
     887             :  * @cliexpar
     888             :  * Example of how to create a GTPU Tunnel:
     889             :  * @cliexcmd{create gtpu tunnel src 10.0.3.1 dst 10.0.3.3 teid 13 tteid 55
     890             :  * encap-vrf-id 7}
     891             :  * Example of how to delete a GTPU Tunnel:
     892             :  * @cliexcmd{create gtpu tunnel src 10.0.3.1 dst 10.0.3.3 teid 13 encap-vrf-id
     893             :  * 7 del}
     894             :  * Example of how to update tx TEID of a GTPU Tunnel:
     895             :  * @cliexcmd{create gtpu tunnel src 10.0.3.1 dst 10.0.3.3 encap-vrf-id 7
     896             :  * upd-tteid 55}
     897             :  ?*/
     898             : /* *INDENT-OFF* */
     899      207367 : VLIB_CLI_COMMAND (create_gtpu_tunnel_command, static) = {
     900             :   .path = "create gtpu tunnel",
     901             :   .short_help =
     902             :   "create gtpu tunnel src <local-tep-addr>"
     903             :   " {dst <remote-tep-addr>|group <mcast-addr> <intf-name>}"
     904             :   " teid <nn> [tteid <nn>] [encap-vrf-id <nn>]"
     905             :   " [decap-next [l2|ip4|ip6|node <name>]] [del | upd-tteid <nn>]",
     906             :   .function = gtpu_add_del_tunnel_command_fn,
     907             : };
     908             : /* *INDENT-ON* */
     909             : 
     910             : static clib_error_t *
     911           5 : show_gtpu_tunnel_command_fn (vlib_main_t * vm,
     912             :                              unformat_input_t * input,
     913             :                              vlib_cli_command_t * cmd)
     914             : {
     915           5 :   gtpu_main_t *gtm = &gtpu_main;
     916             :   gtpu_tunnel_t *t;
     917             : 
     918           5 :   if (pool_elts (gtm->tunnels) == 0)
     919           0 :     vlib_cli_output (vm, "No gtpu tunnels configured...");
     920             : 
     921         115 :   pool_foreach (t, gtm->tunnels)
     922             :   {
     923         110 :     vlib_cli_output (vm, "%U", format_gtpu_tunnel, t);
     924             :   }
     925             : 
     926           5 :   return 0;
     927             : }
     928             : 
     929             : /*?
     930             :  * Display all the GTPU Tunnel entries.
     931             :  *
     932             :  * @cliexpar
     933             :  * Example of how to display the GTPU Tunnel entries:
     934             :  * @cliexstart{show gtpu tunnel}
     935             :  * [0] src 10.0.3.1 dst 10.0.3.3 teid 13 tx-teid 55 encap_fib_index 0 sw_if_index 5 decap_next l2
     936             :  * @cliexend
     937             :  ?*/
     938             : /* *INDENT-OFF* */
     939      207367 : VLIB_CLI_COMMAND (show_gtpu_tunnel_command, static) = {
     940             :     .path = "show gtpu tunnel",
     941             :     .short_help = "show gtpu tunnel",
     942             :     .function = show_gtpu_tunnel_command_fn,
     943             : };
     944             : /* *INDENT-ON* */
     945             : 
     946             : void
     947           0 : vnet_int_gtpu_bypass_mode (u32 sw_if_index, u8 is_ip6, u8 is_enable)
     948             : {
     949           0 :   if (is_ip6)
     950           0 :     vnet_feature_enable_disable ("ip6-unicast", "ip6-gtpu-bypass",
     951             :                                  sw_if_index, is_enable, 0, 0);
     952             :   else
     953           0 :     vnet_feature_enable_disable ("ip4-unicast", "ip4-gtpu-bypass",
     954             :                                  sw_if_index, is_enable, 0, 0);
     955           0 : }
     956             : 
     957             : static clib_error_t *
     958           0 : set_ip_gtpu_bypass (u32 is_ip6,
     959             :                     unformat_input_t * input, vlib_cli_command_t * cmd)
     960             : {
     961           0 :   unformat_input_t _line_input, *line_input = &_line_input;
     962           0 :   vnet_main_t *vnm = vnet_get_main ();
     963           0 :   clib_error_t *error = 0;
     964             :   u32 sw_if_index, is_enable;
     965             : 
     966           0 :   sw_if_index = ~0;
     967           0 :   is_enable = 1;
     968             : 
     969           0 :   if (!unformat_user (input, unformat_line_input, line_input))
     970           0 :     return 0;
     971             : 
     972           0 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
     973             :     {
     974           0 :       if (unformat_user
     975             :           (line_input, unformat_vnet_sw_interface, vnm, &sw_if_index))
     976             :         ;
     977           0 :       else if (unformat (line_input, "del"))
     978           0 :         is_enable = 0;
     979             :       else
     980             :         {
     981           0 :           error = unformat_parse_error (line_input);
     982           0 :           goto done;
     983             :         }
     984             :     }
     985             : 
     986           0 :   if (~0 == sw_if_index)
     987             :     {
     988           0 :       error = clib_error_return (0, "unknown interface `%U'",
     989             :                                  format_unformat_error, line_input);
     990           0 :       goto done;
     991             :     }
     992             : 
     993           0 :   vnet_int_gtpu_bypass_mode (sw_if_index, is_ip6, is_enable);
     994             : 
     995           0 : done:
     996           0 :   unformat_free (line_input);
     997             : 
     998           0 :   return error;
     999             : }
    1000             : 
    1001             : static clib_error_t *
    1002           0 : set_ip4_gtpu_bypass (vlib_main_t * vm,
    1003             :                      unformat_input_t * input, vlib_cli_command_t * cmd)
    1004             : {
    1005           0 :   return set_ip_gtpu_bypass (0, input, cmd);
    1006             : }
    1007             : 
    1008             : /*?
    1009             :  * This command adds the 'ip4-gtpu-bypass' graph node for a given interface.
    1010             :  * By adding the IPv4 gtpu-bypass graph node to an interface, the node checks
    1011             :  * for and validate input gtpu packet and bypass ip4-lookup, ip4-local,
    1012             :  * ip4-udp-lookup nodes to speedup gtpu packet forwarding. This node will
    1013             :  * cause extra overhead to for non-gtpu packets which is kept at a minimum.
    1014             :  *
    1015             :  * @cliexpar
    1016             :  * @parblock
    1017             :  * Example of graph node before ip4-gtpu-bypass is enabled:
    1018             :  * @cliexstart{show vlib graph ip4-gtpu-bypass}
    1019             :  *            Name                      Next                    Previous
    1020             :  * ip4-gtpu-bypass                error-drop [0]
    1021             :  *                                gtpu4-input [1]
    1022             :  *                                 ip4-lookup [2]
    1023             :  * @cliexend
    1024             :  *
    1025             :  * Example of how to enable ip4-gtpu-bypass on an interface:
    1026             :  * @cliexcmd{set interface ip gtpu-bypass GigabitEthernet2/0/0}
    1027             :  *
    1028             :  * Example of graph node after ip4-gtpu-bypass is enabled:
    1029             :  * @cliexstart{show vlib graph ip4-gtpu-bypass}
    1030             :  *            Name                      Next                    Previous
    1031             :  * ip4-gtpu-bypass                error-drop [0]               ip4-input
    1032             :  *                                gtpu4-input [1]        ip4-input-no-checksum
    1033             :  *                                 ip4-lookup [2]
    1034             :  * @cliexend
    1035             :  *
    1036             :  * Example of how to display the feature enabled on an interface:
    1037             :  * @cliexstart{show ip interface features GigabitEthernet2/0/0}
    1038             :  * IP feature paths configured on GigabitEthernet2/0/0...
    1039             :  * ...
    1040             :  * ipv4 unicast:
    1041             :  *   ip4-gtpu-bypass
    1042             :  *   ip4-lookup
    1043             :  * ...
    1044             :  * @cliexend
    1045             :  *
    1046             :  * Example of how to disable ip4-gtpu-bypass on an interface:
    1047             :  * @cliexcmd{set interface ip gtpu-bypass GigabitEthernet2/0/0 del}
    1048             :  * @endparblock
    1049             : ?*/
    1050             : /* *INDENT-OFF* */
    1051      207367 : VLIB_CLI_COMMAND (set_interface_ip_gtpu_bypass_command, static) = {
    1052             :   .path = "set interface ip gtpu-bypass",
    1053             :   .function = set_ip4_gtpu_bypass,
    1054             :   .short_help = "set interface ip gtpu-bypass <interface> [del]",
    1055             : };
    1056             : /* *INDENT-ON* */
    1057             : 
    1058             : static clib_error_t *
    1059           0 : set_ip6_gtpu_bypass (vlib_main_t * vm,
    1060             :                      unformat_input_t * input, vlib_cli_command_t * cmd)
    1061             : {
    1062           0 :   return set_ip_gtpu_bypass (1, input, cmd);
    1063             : }
    1064             : 
    1065             : /*?
    1066             :  * This command adds the 'ip6-gtpu-bypass' graph node for a given interface.
    1067             :  * By adding the IPv6 gtpu-bypass graph node to an interface, the node checks
    1068             :  * for and validate input gtpu packet and bypass ip6-lookup, ip6-local,
    1069             :  * ip6-udp-lookup nodes to speedup gtpu packet forwarding. This node will
    1070             :  * cause extra overhead to for non-gtpu packets which is kept at a minimum.
    1071             :  *
    1072             :  * @cliexpar
    1073             :  * @parblock
    1074             :  * Example of graph node before ip6-gtpu-bypass is enabled:
    1075             :  * @cliexstart{show vlib graph ip6-gtpu-bypass}
    1076             :  *            Name                      Next                    Previous
    1077             :  * ip6-gtpu-bypass                error-drop [0]
    1078             :  *                                gtpu6-input [1]
    1079             :  *                                 ip6-lookup [2]
    1080             :  * @cliexend
    1081             :  *
    1082             :  * Example of how to enable ip6-gtpu-bypass on an interface:
    1083             :  * @cliexcmd{set interface ip6 gtpu-bypass GigabitEthernet2/0/0}
    1084             :  *
    1085             :  * Example of graph node after ip6-gtpu-bypass is enabled:
    1086             :  * @cliexstart{show vlib graph ip6-gtpu-bypass}
    1087             :  *            Name                      Next                    Previous
    1088             :  * ip6-gtpu-bypass                error-drop [0]               ip6-input
    1089             :  *                                gtpu6-input [1]        ip4-input-no-checksum
    1090             :  *                                 ip6-lookup [2]
    1091             :  * @cliexend
    1092             :  *
    1093             :  * Example of how to display the feature enabled on an interface:
    1094             :  * @cliexstart{show ip interface features GigabitEthernet2/0/0}
    1095             :  * IP feature paths configured on GigabitEthernet2/0/0...
    1096             :  * ...
    1097             :  * ipv6 unicast:
    1098             :  *   ip6-gtpu-bypass
    1099             :  *   ip6-lookup
    1100             :  * ...
    1101             :  * @cliexend
    1102             :  *
    1103             :  * Example of how to disable ip6-gtpu-bypass on an interface:
    1104             :  * @cliexcmd{set interface ip6 gtpu-bypass GigabitEthernet2/0/0 del}
    1105             :  * @endparblock
    1106             : ?*/
    1107             : /* *INDENT-OFF* */
    1108      207367 : VLIB_CLI_COMMAND (set_interface_ip6_gtpu_bypass_command, static) = {
    1109             :   .path = "set interface ip6 gtpu-bypass",
    1110             :   .function = set_ip6_gtpu_bypass,
    1111             :   .short_help = "set interface ip6 gtpu-bypass <interface> [del]",
    1112             : };
    1113             : /* *INDENT-ON* */
    1114             : 
    1115             : int
    1116           0 : vnet_gtpu_add_del_rx_flow (u32 hw_if_index, u32 t_index, int is_add)
    1117             : {
    1118           0 :   gtpu_main_t *gtm = &gtpu_main;
    1119           0 :   gtpu_tunnel_t *t = pool_elt_at_index (gtm->tunnels, t_index);
    1120           0 :   vnet_main_t *vnm = vnet_get_main ();
    1121           0 :   if (is_add)
    1122             :     {
    1123           0 :       if (t->flow_index == ~0)
    1124             :         {
    1125           0 :           vnet_flow_t flow = {
    1126             :             .actions =
    1127             :               VNET_FLOW_ACTION_REDIRECT_TO_NODE | VNET_FLOW_ACTION_MARK |
    1128             :               VNET_FLOW_ACTION_BUFFER_ADVANCE,
    1129           0 :             .mark_flow_id = t_index + gtm->flow_id_start,
    1130           0 :             .redirect_node_index = gtpu4_flow_input_node.index,
    1131             :             .buffer_advance = sizeof (ethernet_header_t)
    1132             :               + sizeof (ip4_header_t) + sizeof (udp_header_t),
    1133             :             .type = VNET_FLOW_TYPE_IP4_GTPU,
    1134             :             .ip4_gtpu = {
    1135             :                          .protocol.prot = IP_PROTOCOL_UDP,
    1136             :                          .src_addr.addr = t->dst.ip4,
    1137             :                          .src_addr.mask.as_u32 = ~0,
    1138             :                          .dst_addr.addr = t->src.ip4,
    1139             :                          .dst_addr.mask.as_u32 = ~0,
    1140           0 :                          .teid = t->teid,
    1141             :                          }
    1142             :             ,
    1143             :           };
    1144           0 :           vnet_flow_add (vnm, &flow, &t->flow_index);
    1145             :         }
    1146             : 
    1147           0 :       return vnet_flow_enable (vnm, t->flow_index, hw_if_index);
    1148             :     }
    1149             : 
    1150             :   /* flow index is removed when the tunnel is deleted */
    1151           0 :   return vnet_flow_disable (vnm, t->flow_index, hw_if_index);
    1152             : }
    1153             : 
    1154             : u32
    1155           0 : vnet_gtpu_get_tunnel_index (u32 sw_if_index)
    1156             : {
    1157           0 :   gtpu_main_t *gtm = &gtpu_main;
    1158             : 
    1159           0 :   if (sw_if_index >= vec_len (gtm->tunnel_index_by_sw_if_index))
    1160           0 :     return ~0;
    1161           0 :   return gtm->tunnel_index_by_sw_if_index[sw_if_index];
    1162             : }
    1163             : 
    1164             : static clib_error_t *
    1165           0 : gtpu_offload_command_fn (vlib_main_t * vm,
    1166             :                          unformat_input_t * input, vlib_cli_command_t * cmd)
    1167             : {
    1168           0 :   unformat_input_t _line_input, *line_input = &_line_input;
    1169             : 
    1170             :   /* Get a line of input. */
    1171           0 :   if (!unformat_user (input, unformat_line_input, line_input))
    1172           0 :     return 0;
    1173             : 
    1174           0 :   vnet_main_t *vnm = vnet_get_main ();
    1175           0 :   u32 rx_sw_if_index = ~0;
    1176           0 :   u32 hw_if_index = ~0;
    1177           0 :   int is_add = 1;
    1178             : 
    1179           0 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    1180             :     {
    1181           0 :       if (unformat (line_input, "hw %U", unformat_vnet_hw_interface, vnm,
    1182             :                     &hw_if_index))
    1183           0 :         continue;
    1184           0 :       if (unformat (line_input, "rx %U", unformat_vnet_sw_interface, vnm,
    1185             :                     &rx_sw_if_index))
    1186           0 :         continue;
    1187           0 :       if (unformat (line_input, "del"))
    1188             :         {
    1189           0 :           is_add = 0;
    1190           0 :           continue;
    1191             :         }
    1192           0 :       return clib_error_return (0, "unknown input `%U'",
    1193             :                                 format_unformat_error, line_input);
    1194             :     }
    1195             : 
    1196           0 :   if (rx_sw_if_index == ~0)
    1197           0 :     return clib_error_return (0, "missing rx interface");
    1198           0 :   if (hw_if_index == ~0)
    1199           0 :     return clib_error_return (0, "missing hw interface");
    1200             : 
    1201           0 :   u32 t_index = vnet_gtpu_get_tunnel_index (rx_sw_if_index);;
    1202           0 :   if (t_index == ~0)
    1203           0 :     return clib_error_return (0, "%U is not a gtpu tunnel",
    1204             :                               format_vnet_sw_if_index_name, vnm,
    1205             :                               rx_sw_if_index);
    1206             : 
    1207           0 :   gtpu_main_t *gtm = &gtpu_main;
    1208           0 :   gtpu_tunnel_t *t = pool_elt_at_index (gtm->tunnels, t_index);
    1209             : 
    1210             :   /* first support ipv4 hw offload */
    1211           0 :   if (!ip46_address_is_ip4 (&t->dst))
    1212           0 :     return clib_error_return (0, "currently only IPV4 tunnels are supported");
    1213             : 
    1214             :   /* inner protocol should be IPv4/IPv6 */
    1215           0 :   if ((t->decap_next_index != GTPU_INPUT_NEXT_IP4_INPUT) &&
    1216           0 :       (t->decap_next_index != GTPU_INPUT_NEXT_IP6_INPUT))
    1217           0 :     return clib_error_return (0,
    1218             :                               "currently only inner IPv4/IPv6 protocol is supported");
    1219             : 
    1220           0 :   vnet_hw_interface_t *hw_if = vnet_get_hw_interface (vnm, hw_if_index);
    1221           0 :   ip4_main_t *im = &ip4_main;
    1222           0 :   u32 rx_fib_index =
    1223           0 :     vec_elt (im->fib_index_by_sw_if_index, hw_if->sw_if_index);
    1224             : 
    1225           0 :   if (t->encap_fib_index != rx_fib_index)
    1226           0 :     return clib_error_return (0, "interface/tunnel fib mismatch");
    1227             : 
    1228           0 :   if (vnet_gtpu_add_del_rx_flow (hw_if_index, t_index, is_add))
    1229           0 :     return clib_error_return (0, "error %s flow",
    1230             :                               is_add ? "enabling" : "disabling");
    1231             : 
    1232           0 :   return 0;
    1233             : }
    1234             : 
    1235             : 
    1236             : /* *INDENT-OFF* */
    1237      207367 : VLIB_CLI_COMMAND (gtpu_offload_command, static) = {
    1238             :     .path = "set flow-offload gtpu",
    1239             :     .short_help =
    1240             :     "set flow-offload gtpu hw <inerface-name> rx <tunnel-name> [del]",
    1241             :     .function = gtpu_offload_command_fn,
    1242             : };
    1243             : /* *INDENT-ON* */
    1244             : 
    1245             : clib_error_t *
    1246         559 : gtpu_init (vlib_main_t * vm)
    1247             : {
    1248         559 :   gtpu_main_t *gtm = &gtpu_main;
    1249             : 
    1250         559 :   gtm->vnet_main = vnet_get_main ();
    1251         559 :   gtm->vlib_main = vm;
    1252             : 
    1253         559 :   vnet_flow_get_range (gtm->vnet_main, "gtpu", 1024 * 1024,
    1254             :                        &gtm->flow_id_start);
    1255             : 
    1256             :   /* initialize the ip6 hash */
    1257         559 :   gtm->gtpu6_tunnel_by_key = hash_create_mem (0,
    1258             :                                               sizeof (gtpu6_tunnel_key_t),
    1259             :                                               sizeof (uword));
    1260         559 :   gtm->vtep_table = vtep_table_create ();
    1261         559 :   gtm->mcast_shared = hash_create_mem (0,
    1262             :                                        sizeof (ip46_address_t),
    1263             :                                        sizeof (mcast_shared_t));
    1264             : 
    1265         559 :   gtm->fib_node_type = fib_node_register_new_type ("gtpu", &gtpu_vft);
    1266             : 
    1267         559 :   return 0;
    1268             : }
    1269             : 
    1270        1119 : VLIB_INIT_FUNCTION (gtpu_init);
    1271             : 
    1272             : /* *INDENT-OFF* */
    1273             : VLIB_PLUGIN_REGISTER () = {
    1274             :     .version = VPP_BUILD_VER,
    1275             :     .description = "GPRS Tunnelling Protocol, User Data (GTPv1-U)",
    1276             : };
    1277             : /* *INDENT-ON* */
    1278             : 
    1279             : /*
    1280             :  * fd.io coding-style-patch-verification: ON
    1281             :  *
    1282             :  * Local Variables:
    1283             :  * eval: (c-set-style "gnu")
    1284             :  * End:
    1285             :  */

Generated by: LCOV version 1.14