LCOV - code coverage report
Current view: top level - vnet/ipsec - ipsec_tun.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 299 322 92.9 %
Date: 2023-10-26 01:39:38 Functions: 31 32 96.9 %

          Line data    Source code
       1             : /*
       2             :  * ipsec_tun.h : IPSEC tunnel protection
       3             :  *
       4             :  * Copyright (c) 2015 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/ipsec/ipsec_tun.h>
      19             : #include <vnet/ipsec/ipsec_itf.h>
      20             : #include <vnet/ipsec/esp.h>
      21             : #include <vnet/udp/udp_local.h>
      22             : #include <vnet/adj/adj_delegate.h>
      23             : #include <vnet/adj/adj_midchain.h>
      24             : #include <vnet/teib/teib.h>
      25             : #include <vnet/mpls/mpls.h>
      26             : 
      27             : /* instantiate the bihash functions */
      28             : #include <vppinfra/bihash_8_16.h>
      29             : #include <vppinfra/bihash_template.c>
      30             : #include <vppinfra/bihash_24_16.h>
      31             : #include <vppinfra/bihash_template.c>
      32             : 
      33             : #define IPSEC_TUN_DEFAULT_HASH_NUM_BUCKETS (64 * 1024)
      34             : #define IPSEC_TUN_DEFAULT_HASH_MEMORY_SIZE 512 << 20
      35             : 
      36             : /**
      37             :  * The logger
      38             :  */
      39             : vlib_log_class_t ipsec_tun_protect_logger;
      40             : 
      41             : /**
      42             :  * Pool of tunnel protection objects
      43             :  */
      44             : ipsec_tun_protect_t *ipsec_tun_protect_pool;
      45             : 
      46             : /**
      47             :  * Adj delegate registered type
      48             :  */
      49             : static adj_delegate_type_t ipsec_tun_adj_delegate_type;
      50             : 
      51             : /**
      52             :  * Adj index to TX SA mapping
      53             :  */
      54             : index_t *ipsec_tun_protect_sa_by_adj_index;
      55             : 
      56             : const ip_address_t IP_ADDR_ALL_0 = IP_ADDRESS_V4_ALL_0S;
      57             : 
      58             : /**
      59             :  * The DB of all added per-nh tunnel protectiond
      60             :  */
      61             : typedef struct ipsec_tun_protect_itf_db_t_
      62             : {
      63             :   /** A hash table key'd on IP (4 or 6) address */
      64             :   uword *id_hash;
      65             :   /** If the interface is P2P then there is only one protect
      66             :    * object associated with the auto-adj for each NH proto */
      67             :   index_t id_itp;
      68             : } ipsec_tun_protect_itf_db_t;
      69             : 
      70             : typedef struct ipsec_tun_protect_db_t_
      71             : {
      72             :   /** Per-interface vector */
      73             :   ipsec_tun_protect_itf_db_t *id_itf;
      74             : } ipsec_tun_protect_db_t;
      75             : 
      76             : static ipsec_tun_protect_db_t itp_db;
      77             : 
      78             : const static ipsec_tun_protect_itf_db_t IPSEC_TUN_PROTECT_DEFAULT_DB_ENTRY = {
      79             :   .id_itp = INDEX_INVALID,
      80             : };
      81             : 
      82             : #define ITP_DBG(_itp, _fmt, _args...)                   \
      83             : {                                                       \
      84             :   vlib_log_debug(ipsec_tun_protect_logger,              \
      85             :                  "[%U]: " _fmt,                         \
      86             :                  format_ipsec_tun_protect,              \
      87             :                  _itp, ##_args);                        \
      88             : }
      89             : 
      90             : #define ITP_DBG2(_fmt, _args...)                        \
      91             : {                                                       \
      92             :   vlib_log_debug(ipsec_tun_protect_logger,              \
      93             :                  _fmt, ##_args);                        \
      94             : }
      95             : 
      96             : static u32 ipsec_tun_node_regs[N_AF];
      97             : 
      98             : void
      99         374 : ipsec_tun_register_nodes (ip_address_family_t af)
     100             : {
     101         374 :   if (0 == ipsec_tun_node_regs[af]++)
     102             :     {
     103         168 :       if (AF_IP4 == af)
     104         103 :         ip4_register_protocol (IP_PROTOCOL_IPSEC_ESP,
     105             :                                ipsec4_tun_input_node.index);
     106             :       else
     107          65 :         ip6_register_protocol (IP_PROTOCOL_IPSEC_ESP,
     108             :                                ipsec6_tun_input_node.index);
     109         168 :       ipsec_register_udp_port (UDP_DST_PORT_ipsec, (AF_IP4 == af));
     110             :     }
     111         374 : }
     112             : 
     113             : void
     114         355 : ipsec_tun_unregister_nodes (ip_address_family_t af)
     115             : {
     116         355 :   ASSERT (0 != ipsec_tun_node_regs[af]);
     117         355 :   if (0 == --ipsec_tun_node_regs[af])
     118             :     {
     119         157 :       if (AF_IP4 == af)
     120          97 :         ip4_unregister_protocol (IP_PROTOCOL_IPSEC_ESP);
     121             :       else
     122          60 :         ip6_unregister_protocol (IP_PROTOCOL_IPSEC_ESP);
     123         157 :       ipsec_unregister_udp_port (UDP_DST_PORT_ipsec, (AF_IP4 == af));
     124             :     }
     125         355 : }
     126             : 
     127             : static inline const ipsec_tun_protect_t *
     128           1 : ipsec_tun_protect_from_const_base (const adj_delegate_t * ad)
     129             : {
     130           1 :   if (ad == NULL)
     131           0 :     return (NULL);
     132           1 :   return (pool_elt_at_index (ipsec_tun_protect_pool, ad->ad_index));
     133             : }
     134             : 
     135             : static u32
     136         876 : ipsec_tun_protect_get_adj_next (vnet_link_t linkt,
     137             :                                 const ipsec_tun_protect_t *itp)
     138             : {
     139             :   ipsec_main_t *im;
     140             :   u32 next;
     141             : 
     142         876 :   im = &ipsec_main;
     143         876 :   next = 0;
     144             : 
     145         876 :   if (!(itp->itp_flags & IPSEC_PROTECT_ITF))
     146             :     {
     147         801 :       if (ip46_address_is_ip4 (&itp->itp_tun.src))
     148         597 :         linkt = VNET_LINK_IP4;
     149             :       else
     150         204 :         linkt = VNET_LINK_IP6;
     151             :     }
     152             : 
     153         876 :   switch (linkt)
     154             :     {
     155         636 :     case VNET_LINK_IP4:
     156         636 :       next = im->esp4_encrypt_tun_node_index;
     157         636 :       break;
     158         226 :     case VNET_LINK_IP6:
     159         226 :       next = im->esp6_encrypt_tun_node_index;
     160         226 :       break;
     161          14 :     case VNET_LINK_MPLS:
     162          14 :       next = im->esp_mpls_encrypt_tun_node_index;
     163          14 :       break;
     164           0 :     case VNET_LINK_ARP:
     165             :     case VNET_LINK_NSH:
     166             :     case VNET_LINK_ETHERNET:
     167           0 :       ASSERT (0);
     168           0 :       break;
     169             :     }
     170             : 
     171         876 :   return (next);
     172             : }
     173             : 
     174             : static void
     175         169 : ipsec_tun_setup_tx_nodes (u32 sw_if_index, const ipsec_tun_protect_t *itp)
     176             : {
     177         169 :   vnet_feature_modify_end_node (
     178         169 :     ip4_main.lookup_main.output_feature_arc_index, sw_if_index,
     179             :     ipsec_tun_protect_get_adj_next (VNET_LINK_IP4, itp));
     180         169 :   vnet_feature_modify_end_node (
     181         169 :     ip6_main.lookup_main.output_feature_arc_index, sw_if_index,
     182             :     ipsec_tun_protect_get_adj_next (VNET_LINK_IP6, itp));
     183         169 :   vnet_feature_modify_end_node (
     184         169 :     mpls_main.output_feature_arc_index, sw_if_index,
     185             :     ipsec_tun_protect_get_adj_next (VNET_LINK_MPLS, itp));
     186         169 : }
     187             : 
     188             : static void
     189         670 : ipsec_tun_protect_add_adj (adj_index_t ai, const ipsec_tun_protect_t * itp)
     190             : {
     191         766 :   vec_validate_init_empty (ipsec_tun_protect_sa_by_adj_index, ai,
     192             :                            INDEX_INVALID);
     193             : 
     194         670 :   if (NULL == itp)
     195             :     {
     196         301 :       ipsec_tun_protect_sa_by_adj_index[ai] = INDEX_INVALID;
     197         301 :       adj_nbr_midchain_reset_next_node (ai);
     198             :     }
     199             :   else
     200             :     {
     201         369 :       ipsec_tun_protect_sa_by_adj_index[ai] = itp->itp_out_sa;
     202         369 :       adj_nbr_midchain_update_next_node (
     203         369 :         ai, ipsec_tun_protect_get_adj_next (adj_get_link_type (ai), itp));
     204             :     }
     205         670 : }
     206             : 
     207             : static index_t
     208        1165 : ipsec_tun_protect_find (u32 sw_if_index, const ip_address_t * nh)
     209             : {
     210             :   ipsec_tun_protect_itf_db_t *idi;
     211             :   uword *p;
     212             : 
     213        1165 :   if (vec_len (itp_db.id_itf) <= sw_if_index)
     214         588 :     return INDEX_INVALID;
     215             : 
     216         577 :   if (vnet_sw_interface_is_p2p (vnet_get_main (), sw_if_index))
     217         319 :     return (itp_db.id_itf[sw_if_index].id_itp);
     218             : 
     219         258 :   idi = &itp_db.id_itf[sw_if_index];
     220         258 :   p = hash_get_mem (idi->id_hash, nh);
     221             : 
     222         258 :   if (NULL == p)
     223             :     {
     224         100 :       return INDEX_INVALID;
     225             :     }
     226         158 :   return (p[0]);
     227             : }
     228             : 
     229             : static void
     230         263 : ipsec_tun_protect_rx_db_add (ipsec_main_t * im,
     231             :                              const ipsec_tun_protect_t * itp)
     232             : {
     233             :   const ipsec_sa_t *sa;
     234             :   u32 sai;
     235             : 
     236         263 :   if (ip46_address_is_zero (&itp->itp_crypto.dst))
     237          16 :     return;
     238             : 
     239             :   /* *INDENT-OFF* */
     240         504 :   FOR_EACH_IPSEC_PROTECT_INPUT_SAI(itp, sai,
     241             :   ({
     242             :       sa = ipsec_sa_get (sai);
     243             : 
     244             :       ipsec_tun_lkup_result_t res = {
     245             :         .tun_index = itp - ipsec_tun_protect_pool,
     246             :         .sa_index = sai,
     247             :         .flags = itp->itp_flags,
     248             :         .sw_if_index = itp->itp_sw_if_index,
     249             :       };
     250             : 
     251             :       /*
     252             :        * The key is formed from the tunnel's destination
     253             :        * as the packet lookup is done from the packet's source
     254             :        */
     255             :       if (ip46_address_is_ip4 (&itp->itp_crypto.dst))
     256             :         {
     257             :           ipsec4_tunnel_kv_t key = {
     258             :             .value = res,
     259             :           };
     260             :           clib_bihash_kv_8_16_t *bkey = (clib_bihash_kv_8_16_t*)&key;
     261             : 
     262             :           ipsec4_tunnel_mk_key(&key, &itp->itp_crypto.dst.ip4,
     263             :                                clib_host_to_net_u32 (sa->spi));
     264             : 
     265             :           if (!clib_bihash_is_initialised_8_16 (&im->tun4_protect_by_key))
     266             :             clib_bihash_init_8_16 (&im->tun4_protect_by_key,
     267             :                                    "IPSec IPv4 tunnels",
     268             :                                    IPSEC_TUN_DEFAULT_HASH_NUM_BUCKETS,
     269             :                                    IPSEC_TUN_DEFAULT_HASH_MEMORY_SIZE);
     270             : 
     271             :           clib_bihash_add_del_8_16 (&im->tun4_protect_by_key, bkey, 1);
     272             :           ipsec_tun_register_nodes (AF_IP4);
     273             :         }
     274             :       else
     275             :         {
     276             :           ipsec6_tunnel_kv_t key = {
     277             :             .key = {
     278             :               .remote_ip = itp->itp_crypto.dst.ip6,
     279             :               .spi = clib_host_to_net_u32 (sa->spi),
     280             :             },
     281             :             .value = res,
     282             :           };
     283             :           clib_bihash_kv_24_16_t *bkey = (clib_bihash_kv_24_16_t*)&key;
     284             : 
     285             :           if (!clib_bihash_is_initialised_24_16 (&im->tun6_protect_by_key))
     286             :             clib_bihash_init_24_16 (&im->tun6_protect_by_key,
     287             :                                     "IPSec IPv6 tunnels",
     288             :                                     IPSEC_TUN_DEFAULT_HASH_NUM_BUCKETS,
     289             :                                     IPSEC_TUN_DEFAULT_HASH_MEMORY_SIZE);
     290             :           clib_bihash_add_del_24_16 (&im->tun6_protect_by_key, bkey, 1);
     291             :           ipsec_tun_register_nodes (AF_IP6);
     292             :         }
     293             :   }))
     294             :   /* *INDENT-ON* */
     295             : }
     296             : 
     297             : static adj_walk_rc_t
     298         301 : ipsec_tun_protect_adj_add (adj_index_t ai, void *arg)
     299             : {
     300         301 :   ipsec_tun_protect_t *itp = arg;
     301         301 :   adj_delegate_add (adj_get (ai), ipsec_tun_adj_delegate_type,
     302         301 :                     itp - ipsec_tun_protect_pool);
     303         301 :   ipsec_tun_protect_add_adj (ai, itp);
     304             : 
     305         301 :   if (itp->itp_flags & IPSEC_PROTECT_ITF)
     306          39 :     ipsec_itf_adj_stack (ai, itp->itp_out_sa);
     307             : 
     308         301 :   return (ADJ_WALK_RC_CONTINUE);
     309             : }
     310             : 
     311             : static void
     312         215 : ipsec_tun_protect_tx_db_add (ipsec_tun_protect_t * itp)
     313             : {
     314             :   /*
     315             :    * add the delegate to the adj
     316             :    */
     317             :   ipsec_tun_protect_itf_db_t *idi;
     318             :   fib_protocol_t nh_proto;
     319             :   ip46_address_t nh;
     320             : 
     321         502 :   vec_validate_init_empty (itp_db.id_itf,
     322             :                            itp->itp_sw_if_index,
     323             :                            IPSEC_TUN_PROTECT_DEFAULT_DB_ENTRY);
     324             : 
     325         215 :   idi = &itp_db.id_itf[itp->itp_sw_if_index];
     326             : 
     327         215 :   if (vnet_sw_interface_is_p2p (vnet_get_main (), itp->itp_sw_if_index))
     328             :     {
     329         154 :       if (INDEX_INVALID == idi->id_itp)
     330             :         {
     331         154 :           ipsec_tun_setup_tx_nodes (itp->itp_sw_if_index, itp);
     332             :         }
     333         154 :       idi->id_itp = itp - ipsec_tun_protect_pool;
     334             : 
     335         462 :       FOR_EACH_FIB_IP_PROTOCOL (nh_proto)
     336         308 :         adj_nbr_walk (itp->itp_sw_if_index,
     337             :                       nh_proto, ipsec_tun_protect_adj_add, itp);
     338             :     }
     339             :   else
     340             :     {
     341          61 :       if (NULL == idi->id_hash)
     342             :         {
     343          15 :           idi->id_hash =
     344          15 :             hash_create_mem (0, sizeof (ip_address_t), sizeof (uword));
     345             :           /*
     346             :            * enable the encrypt feature for egress if this is the first addition
     347             :            * on this interface
     348             :            */
     349          15 :           ipsec_tun_setup_tx_nodes (itp->itp_sw_if_index, itp);
     350             :         }
     351             : 
     352         122 :       hash_set_mem (idi->id_hash, itp->itp_key, itp - ipsec_tun_protect_pool);
     353             : 
     354             :       /*
     355             :        * walk all the adjs with the same nh on this interface
     356             :        * to associate them with this protection
     357             :        */
     358          61 :       nh_proto = ip_address_to_46 (itp->itp_key, &nh);
     359             : 
     360          61 :       adj_nbr_walk_nh (itp->itp_sw_if_index,
     361             :                        nh_proto, &nh, ipsec_tun_protect_adj_add, itp);
     362             : 
     363          61 :       ipsec_tun_register_nodes (FIB_PROTOCOL_IP6 == nh_proto ?
     364             :                                 AF_IP6 : AF_IP4);
     365             :     }
     366         215 : }
     367             : 
     368             : static void
     369         295 : ipsec_tun_protect_rx_db_remove (ipsec_main_t * im,
     370             :                                 const ipsec_tun_protect_t * itp)
     371             : {
     372             :   const ipsec_sa_t *sa;
     373             : 
     374             :   /* *INDENT-OFF* */
     375         600 :   FOR_EACH_IPSEC_PROTECT_INPUT_SA(itp, sa,
     376             :   ({
     377             :     if (ip46_address_is_ip4 (&itp->itp_crypto.dst))
     378             :       {
     379             :           ipsec4_tunnel_kv_t key;
     380             :           clib_bihash_kv_8_16_t res, *bkey = (clib_bihash_kv_8_16_t*)&key;
     381             : 
     382             :           ipsec4_tunnel_mk_key(&key, &itp->itp_crypto.dst.ip4,
     383             :                                clib_host_to_net_u32 (sa->spi));
     384             : 
     385             :           if (!clib_bihash_search_8_16 (&im->tun4_protect_by_key, bkey, &res))
     386             :             {
     387             :               clib_bihash_add_del_8_16 (&im->tun4_protect_by_key, bkey, 0);
     388             :               ipsec_tun_unregister_nodes(AF_IP4);
     389             :             }
     390             :       }
     391             :     else
     392             :       {
     393             :         ipsec6_tunnel_kv_t key = {
     394             :           .key = {
     395             :             .remote_ip = itp->itp_crypto.dst.ip6,
     396             :             .spi = clib_host_to_net_u32 (sa->spi),
     397             :           },
     398             :         };
     399             :         clib_bihash_kv_24_16_t res, *bkey = (clib_bihash_kv_24_16_t*)&key;
     400             : 
     401             :         if (!clib_bihash_search_24_16 (&im->tun6_protect_by_key, bkey, &res))
     402             :           {
     403             :             clib_bihash_add_del_24_16 (&im->tun6_protect_by_key, bkey, 0);
     404             :             ipsec_tun_unregister_nodes(AF_IP6);
     405             :           }
     406             :       }
     407             :   }));
     408             :   /* *INDENT-ON* */
     409         295 : }
     410             : 
     411             : static adj_walk_rc_t
     412         259 : ipsec_tun_protect_adj_remove (adj_index_t ai, void *arg)
     413             : {
     414         259 :   ipsec_tun_protect_t *itp = arg;
     415             : 
     416         259 :   adj_delegate_remove (ai, ipsec_tun_adj_delegate_type);
     417         259 :   ipsec_tun_protect_add_adj (ai, NULL);
     418             : 
     419         259 :   if (itp->itp_flags & IPSEC_PROTECT_ITF)
     420          23 :     ipsec_itf_adj_unstack (ai);
     421             : 
     422         259 :   return (ADJ_WALK_RC_CONTINUE);
     423             : }
     424             : 
     425             : static void
     426         215 : ipsec_tun_protect_tx_db_remove (ipsec_tun_protect_t * itp)
     427             : {
     428             :   ipsec_tun_protect_itf_db_t *idi;
     429             :   fib_protocol_t nh_proto;
     430             :   ip46_address_t nh;
     431             : 
     432         215 :   nh_proto = ip_address_to_46 (itp->itp_key, &nh);
     433         215 :   idi = &itp_db.id_itf[itp->itp_sw_if_index];
     434             : 
     435         215 :   if (vnet_sw_interface_is_p2p (vnet_get_main (), itp->itp_sw_if_index))
     436             :     {
     437         154 :       ipsec_itf_reset_tx_nodes (itp->itp_sw_if_index);
     438         154 :       idi->id_itp = INDEX_INVALID;
     439             : 
     440         462 :       FOR_EACH_FIB_IP_PROTOCOL (nh_proto)
     441         308 :         adj_nbr_walk (itp->itp_sw_if_index,
     442             :                       nh_proto, ipsec_tun_protect_adj_remove, itp);
     443             :     }
     444             :   else
     445             :     {
     446          61 :       adj_nbr_walk_nh (itp->itp_sw_if_index,
     447             :                        nh_proto, &nh, ipsec_tun_protect_adj_remove, itp);
     448             : 
     449         122 :       hash_unset_mem (idi->id_hash, itp->itp_key);
     450             : 
     451          61 :       if (0 == hash_elts (idi->id_hash))
     452             :         {
     453          15 :           ipsec_itf_reset_tx_nodes (itp->itp_sw_if_index);
     454          15 :           hash_free (idi->id_hash);
     455          15 :           idi->id_hash = NULL;
     456             :         }
     457          61 :       ipsec_tun_unregister_nodes (FIB_PROTOCOL_IP6 == nh_proto ?
     458             :                                   AF_IP6 : AF_IP4);
     459             :     }
     460         215 : }
     461             : 
     462             : static void
     463         279 : ipsec_tun_protect_set_crypto_addr (ipsec_tun_protect_t * itp)
     464             : {
     465             :   ipsec_sa_t *sa;
     466             : 
     467             :   /* *INDENT-OFF* */
     468         568 :   FOR_EACH_IPSEC_PROTECT_INPUT_SA(itp, sa,
     469             :   ({
     470             :     if (ipsec_sa_is_set_IS_TUNNEL (sa))
     471             :       {
     472             :         itp->itp_crypto.src = ip_addr_46 (&sa->tunnel.t_dst);
     473             :         itp->itp_crypto.dst = ip_addr_46 (&sa->tunnel.t_src);
     474             :         if (!(itp->itp_flags & IPSEC_PROTECT_ITF))
     475             :           {
     476             :             ipsec_sa_set_IS_PROTECT (sa);
     477             :             itp->itp_flags |= IPSEC_PROTECT_ENCAPED;
     478             :           }
     479             :       }
     480             :     else
     481             :       {
     482             :         itp->itp_crypto.src = itp->itp_tun.src;
     483             :         itp->itp_crypto.dst = itp->itp_tun.dst;
     484             :         itp->itp_flags &= ~IPSEC_PROTECT_ENCAPED;
     485             :       }
     486             :   }));
     487             :   /* *INDENT-ON* */
     488         279 : }
     489             : 
     490             : static void
     491         215 : ipsec_tun_protect_config (ipsec_main_t * im,
     492             :                           ipsec_tun_protect_t * itp, u32 sa_out, u32 * sas_in)
     493             : {
     494             :   index_t sai;
     495             :   u32 ii;
     496             : 
     497         215 :   itp->itp_n_sa_in = vec_len (sas_in);
     498         440 :   for (ii = 0; ii < itp->itp_n_sa_in; ii++)
     499         225 :     itp->itp_in_sas[ii] = sas_in[ii];
     500         215 :   itp->itp_out_sa = sa_out;
     501             : 
     502         215 :   ipsec_sa_lock (itp->itp_out_sa);
     503             : 
     504         215 :   if (itp->itp_flags & IPSEC_PROTECT_ITF)
     505          28 :     ipsec_sa_set_NO_ALGO_NO_DROP (ipsec_sa_get (itp->itp_out_sa));
     506             : 
     507             :   /* *INDENT-OFF* */
     508         440 :   FOR_EACH_IPSEC_PROTECT_INPUT_SAI(itp, sai,
     509             :   ({
     510             :     ipsec_sa_lock(sai);
     511             :   }));
     512         215 :   ipsec_tun_protect_set_crypto_addr(itp);
     513             :   /* *INDENT-ON* */
     514             : 
     515             :   /*
     516             :    * add to the DB against each SA
     517             :    */
     518         215 :   ipsec_tun_protect_rx_db_add (im, itp);
     519         215 :   ipsec_tun_protect_tx_db_add (itp);
     520             : 
     521         215 :   ITP_DBG (itp, "configured");
     522         215 : }
     523             : 
     524             : static void
     525         215 : ipsec_tun_protect_unconfig (ipsec_main_t * im, ipsec_tun_protect_t * itp)
     526             : {
     527             :   ipsec_sa_t *sa;
     528             :   index_t sai;
     529             : 
     530             :   /* *INDENT-OFF* */
     531         440 :   FOR_EACH_IPSEC_PROTECT_INPUT_SA(itp, sa,
     532             :   ({
     533             :     ipsec_sa_unset_IS_PROTECT (sa);
     534             :   }));
     535             : 
     536         215 :   ipsec_tun_protect_rx_db_remove (im, itp);
     537         215 :   ipsec_tun_protect_tx_db_remove (itp);
     538             : 
     539         215 :   ipsec_sa_unset_NO_ALGO_NO_DROP (ipsec_sa_get (itp->itp_out_sa));
     540         215 :   ipsec_sa_unlock(itp->itp_out_sa);
     541             : 
     542         440 :   FOR_EACH_IPSEC_PROTECT_INPUT_SAI(itp, sai,
     543             :   ({
     544             :     ipsec_sa_unlock(sai);
     545             :   }));
     546             :   /* *INDENT-ON* */
     547         215 :   ITP_DBG (itp, "unconfigured");
     548         215 : }
     549             : 
     550             : static void
     551          96 : ipsec_tun_protect_update_from_teib (ipsec_tun_protect_t * itp,
     552             :                                     const teib_entry_t * ne)
     553             : {
     554          96 :   if (NULL != ne)
     555             :     {
     556             :       const fib_prefix_t *pfx;
     557             : 
     558          48 :       pfx = teib_entry_get_nh (ne);
     559             : 
     560          48 :       ip46_address_copy (&itp->itp_tun.dst, &pfx->fp_addr);
     561             :     }
     562             :   else
     563          48 :     ip46_address_reset (&itp->itp_tun.dst);
     564          96 : }
     565             : 
     566             : int
     567         215 : ipsec_tun_protect_update (u32 sw_if_index,
     568             :                           const ip_address_t * nh, u32 sa_out, u32 * sas_in)
     569             : {
     570             :   ipsec_tun_protect_t *itp;
     571             :   u32 itpi, ii, *saip;
     572             :   ipsec_main_t *im;
     573             :   int rv;
     574             : 
     575         215 :   if (NULL == nh)
     576          29 :     nh = &IP_ADDR_ALL_0;
     577             : 
     578         215 :   ITP_DBG2 ("update: %U/%U",
     579             :             format_vnet_sw_if_index_name, vnet_get_main (), sw_if_index,
     580             :             format_ip_address, nh);
     581             : 
     582         215 :   if (vec_len (sas_in) > ITP_MAX_N_SA_IN)
     583             :     {
     584           0 :       rv = VNET_API_ERROR_LIMIT_EXCEEDED;
     585           0 :       goto out;
     586             :     }
     587             : 
     588         215 :   rv = 0;
     589         215 :   im = &ipsec_main;
     590         215 :   itpi = ipsec_tun_protect_find (sw_if_index, nh);
     591             : 
     592         440 :   vec_foreach_index (ii, sas_in)
     593             :   {
     594         225 :     sas_in[ii] = ipsec_sa_find_and_lock (sas_in[ii]);
     595         225 :     if (~0 == sas_in[ii])
     596             :       {
     597           0 :         rv = VNET_API_ERROR_INVALID_VALUE;
     598           0 :         goto out;
     599             :       }
     600             :   }
     601             : 
     602         215 :   sa_out = ipsec_sa_find_and_lock (sa_out);
     603             : 
     604         215 :   if (~0 == sa_out)
     605             :     {
     606           0 :       rv = VNET_API_ERROR_INVALID_VALUE;
     607           0 :       goto out;
     608             :     }
     609             : 
     610         215 :   if (INDEX_INVALID == itpi)
     611             :     {
     612             :       vnet_device_class_t *dev_class;
     613             :       vnet_hw_interface_t *hi;
     614             :       vnet_main_t *vnm;
     615             :       u8 is_l2;
     616             : 
     617         183 :       vnm = vnet_get_main ();
     618         183 :       hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
     619         183 :       dev_class = vnet_get_device_class (vnm, hi->dev_class_index);
     620             : 
     621         183 :       if (NULL == dev_class->ip_tun_desc)
     622             :         {
     623           0 :           rv = VNET_API_ERROR_INVALID_SW_IF_INDEX;
     624           0 :           goto out;
     625             :         }
     626             : 
     627         183 :       pool_get_zero (ipsec_tun_protect_pool, itp);
     628             : 
     629         183 :       itp->itp_sw_if_index = sw_if_index;
     630         183 :       itp->itp_ai = ADJ_INDEX_INVALID;
     631             : 
     632         183 :       itp->itp_n_sa_in = vec_len (sas_in);
     633         374 :       for (ii = 0; ii < itp->itp_n_sa_in; ii++)
     634         191 :         itp->itp_in_sas[ii] = sas_in[ii];
     635         183 :       itp->itp_out_sa = sa_out;
     636             : 
     637         183 :       itp->itp_key = clib_mem_alloc (sizeof (*itp->itp_key));
     638         183 :       ip_address_copy (itp->itp_key, nh);
     639             : 
     640         183 :       rv = dev_class->ip_tun_desc (sw_if_index,
     641         183 :                                    &itp->itp_tun.src,
     642         183 :                                    &itp->itp_tun.dst, &is_l2);
     643             : 
     644         183 :       if (rv)
     645           0 :         goto out;
     646             : 
     647         183 :       if (ip46_address_is_zero (&itp->itp_tun.src))
     648             :         {
     649             :           /*
     650             :            * must be one of those pesky ipsec interfaces that has no encap.
     651             :            * the encap then MUST come from the tunnel mode SA.
     652             :            */
     653             :           ipsec_sa_t *sa;
     654             : 
     655          26 :           sa = ipsec_sa_get (itp->itp_out_sa);
     656             : 
     657          26 :           if (!ipsec_sa_is_set_IS_TUNNEL (sa))
     658             :             {
     659           0 :               rv = VNET_API_ERROR_INVALID_DST_ADDRESS;
     660           0 :               goto out;
     661             :             }
     662             : 
     663          26 :           itp->itp_flags |= IPSEC_PROTECT_ITF;
     664             :         }
     665         157 :       else if (ip46_address_is_zero (&itp->itp_tun.dst))
     666             :         {
     667             :           /* tunnel has no destination address, presumably because it's p2mp
     668             :              in which case we use the nh that this is protection for */
     669          32 :           ipsec_tun_protect_update_from_teib
     670          32 :             (itp, teib_entry_find (sw_if_index, nh));
     671             :         }
     672             : 
     673         183 :       if (is_l2)
     674          12 :         itp->itp_flags |= IPSEC_PROTECT_L2;
     675             : 
     676             :       /*
     677             :        * add to the tunnel DB for ingress
     678             :        *  - if the SA is in trasnport mode, then the packates will arrive
     679             :        *    with the IP src,dst of the protected tunnel, in which case we can
     680             :        *    simply strip the IP header and hand the payload to the protocol
     681             :        *    appropriate input handler
     682             :        *  - if the SA is in tunnel mode then there are two IP headers present
     683             :        *    one for the crytpo tunnel endpoints (described in the SA) and one
     684             :        *    for the tunnel endpoints. The outer IP headers in the srriving
     685             :        *    packets will have the crypto endpoints. So the DB needs to contain
     686             :        *    the crpto endpoint. Once the crypto header is stripped, revealing,
     687             :        *    the tunnel-IP we have 2 choices:
     688             :        *     1) do a tunnel lookup based on the revealed header
     689             :        *     2) skip the tunnel lookup and assume that the packet matches the
     690             :        *        one that is protected here.
     691             :        *    If we did 1) then we would allow our peer to use the SA for tunnel
     692             :        *    X to inject traffic onto tunnel Y, this is not good. If we do 2)
     693             :        *    then we don't verify that the peer is indeed using SA for tunnel
     694             :        *    X and addressing tunnel X. So we take a compromise, once the SA
     695             :        *    matches to tunnel X we veriy that the inner IP matches the value
     696             :        *    of the tunnel we are protecting, else it's dropped.
     697             :        */
     698         183 :       ipsec_tun_protect_config (im, itp, sa_out, sas_in);
     699             :     }
     700             :   else
     701             :     {
     702             :       /* updating SAs only */
     703          32 :       itp = pool_elt_at_index (ipsec_tun_protect_pool, itpi);
     704             : 
     705          32 :       ipsec_tun_protect_unconfig (im, itp);
     706          32 :       ipsec_tun_protect_config (im, itp, sa_out, sas_in);
     707             :     }
     708             : 
     709         215 :   ipsec_sa_unlock (sa_out);
     710         440 :   vec_foreach (saip, sas_in) ipsec_sa_unlock (*saip);
     711         215 :   vec_free (sas_in);
     712             : 
     713         215 : out:
     714         215 :   return (rv);
     715             : }
     716             : 
     717             : int
     718         183 : ipsec_tun_protect_del (u32 sw_if_index, const ip_address_t * nh)
     719             : {
     720             :   ipsec_tun_protect_t *itp;
     721             :   ipsec_main_t *im;
     722             :   index_t itpi;
     723             : 
     724         183 :   ITP_DBG2 ("delete: %U/%U",
     725             :             format_vnet_sw_if_index_name, vnet_get_main (), sw_if_index,
     726             :             format_ip_address, nh);
     727             : 
     728         183 :   im = &ipsec_main;
     729         183 :   if (NULL == nh)
     730          27 :     nh = &IP_ADDR_ALL_0;
     731             : 
     732         183 :   itpi = ipsec_tun_protect_find (sw_if_index, nh);
     733             : 
     734         183 :   if (INDEX_INVALID == itpi)
     735           0 :     return (VNET_API_ERROR_NO_SUCH_ENTRY);
     736             : 
     737         183 :   itp = ipsec_tun_protect_get (itpi);
     738         183 :   ipsec_tun_protect_unconfig (im, itp);
     739             : 
     740         183 :   if (ADJ_INDEX_INVALID != itp->itp_ai)
     741           0 :     adj_unlock (itp->itp_ai);
     742             : 
     743         183 :   clib_mem_free (itp->itp_key);
     744         183 :   pool_put (ipsec_tun_protect_pool, itp);
     745             : 
     746         183 :   return (0);
     747             : }
     748             : 
     749             : void
     750        3625 : ipsec_tun_protect_walk (ipsec_tun_protect_walk_cb_t fn, void *ctx)
     751             : {
     752             :   index_t itpi;
     753             : 
     754             :   /* *INDENT-OFF* */
     755        5732 :   pool_foreach_index (itpi, ipsec_tun_protect_pool)
     756             :    {
     757        2107 :     fn (itpi, ctx);
     758             :   }
     759             :   /* *INDENT-ON* */
     760        3625 : }
     761             : 
     762             : void
     763         312 : ipsec_tun_protect_walk_itf (u32 sw_if_index,
     764             :                             ipsec_tun_protect_walk_cb_t fn, void *ctx)
     765             : {
     766             :   ipsec_tun_protect_itf_db_t *idi;
     767             :   ip_address_t *key;
     768             :   index_t itpi;
     769             : 
     770         312 :   if (vec_len (itp_db.id_itf) <= sw_if_index)
     771           6 :     return;
     772             : 
     773         306 :   idi = &itp_db.id_itf[sw_if_index];
     774             : 
     775             :   /* *INDENT-OFF* */
     776        7806 :   hash_foreach(key, itpi, idi->id_hash,
     777             :   ({
     778             :     fn (itpi, ctx);
     779             :   }));
     780             :   /* *INDENT-ON* */
     781         306 :   if (INDEX_INVALID != idi->id_itp)
     782          78 :     fn (idi->id_itp, ctx);
     783             : }
     784             : 
     785             : static void
     786      132306 : ipsec_tun_feature_update (u32 sw_if_index, u8 arc_index, u8 is_enable,
     787             :                           void *data)
     788             : {
     789             :   ipsec_tun_protect_t *itp;
     790             :   index_t itpi;
     791             : 
     792      132306 :   if (arc_index != feature_main.device_input_feature_arc_index)
     793      132172 :     return;
     794             : 
     795             :   /* Only p2p tunnels supported */
     796         134 :   itpi = ipsec_tun_protect_find (sw_if_index, &IP_ADDR_ALL_0);
     797         134 :   if (itpi == INDEX_INVALID)
     798         118 :     return;
     799             : 
     800          16 :   itp = ipsec_tun_protect_get (itpi);
     801             : 
     802          16 :   if (is_enable)
     803             :     {
     804           8 :       u32 decrypt_tun = ip46_address_is_ip4 (&itp->itp_crypto.dst) ?
     805           8 :                           ipsec_main.esp4_decrypt_tun_node_index :
     806             :                           ipsec_main.esp6_decrypt_tun_node_index;
     807             : 
     808           8 :       if (!(itp->itp_flags & IPSEC_PROTECT_FEAT))
     809             :         {
     810           4 :           itp->itp_flags |= IPSEC_PROTECT_FEAT;
     811           4 :           vnet_feature_modify_end_node (
     812           4 :             feature_main.device_input_feature_arc_index, sw_if_index,
     813             :             decrypt_tun);
     814             :         }
     815             :     }
     816             :   else
     817             :     {
     818           8 :       if (itp->itp_flags & IPSEC_PROTECT_FEAT)
     819             :         {
     820           4 :           itp->itp_flags &= ~IPSEC_PROTECT_FEAT;
     821             : 
     822           4 :           u32 eth_in =
     823           4 :             vlib_get_node_by_name (vlib_get_main (), (u8 *) "ethernet-input")
     824             :               ->index;
     825             : 
     826           4 :           vnet_feature_modify_end_node (
     827           4 :             feature_main.device_input_feature_arc_index, sw_if_index, eth_in);
     828             :         }
     829             :     }
     830             : 
     831             :   /* Propagate flag change into lookup entries */
     832          16 :   ipsec_tun_protect_rx_db_remove (&ipsec_main, itp);
     833          16 :   ipsec_tun_protect_rx_db_add (&ipsec_main, itp);
     834             : }
     835             : 
     836             : static void
     837          42 : ipsec_tun_protect_adj_delegate_adj_deleted (adj_delegate_t * ad)
     838             : {
     839             :   /* remove our delegate */
     840          42 :   ipsec_tun_protect_add_adj (ad->ad_adj_index, NULL);
     841          42 :   adj_delegate_remove (ad->ad_adj_index, ipsec_tun_adj_delegate_type);
     842          42 : }
     843             : 
     844             : static void
     845          68 : ipsec_tun_protect_adj_delegate_adj_modified (adj_delegate_t * ad)
     846             : {
     847          68 :   ipsec_tun_protect_add_adj (ad->ad_adj_index,
     848          68 :                              ipsec_tun_protect_get (ad->ad_index));
     849          68 : }
     850             : 
     851             : static void
     852       47098 : ipsec_tun_protect_adj_delegate_adj_created (adj_index_t ai)
     853             : {
     854             :   /* add our delegate if there is protection for this neighbour */
     855       47098 :   ip_address_t ip = IP_ADDRESS_V4_ALL_0S;
     856             :   ip_adjacency_t *adj;
     857             :   index_t itpi;
     858             : 
     859       47098 :   if (!adj_is_midchain (ai))
     860       46635 :     return;
     861             : 
     862        1877 :   vec_validate_init_empty (ipsec_tun_protect_sa_by_adj_index, ai,
     863             :                            INDEX_INVALID);
     864             : 
     865         463 :   adj = adj_get (ai);
     866             : 
     867         463 :   ip_address_from_46 (&adj->sub_type.midchain.next_hop,
     868         463 :                       adj->ia_nh_proto, &ip);
     869             : 
     870         463 :   itpi = ipsec_tun_protect_find (adj->rewrite_header.sw_if_index, &ip);
     871             : 
     872         463 :   if (INDEX_INVALID != itpi)
     873          45 :     ipsec_tun_protect_adj_add (ai, ipsec_tun_protect_get (itpi));
     874             : }
     875             : 
     876             : static u8 *
     877           1 : ipsec_tun_protect_adj_delegate_format (const adj_delegate_t * aed, u8 * s)
     878             : {
     879             :   const ipsec_tun_protect_t *itp;
     880             : 
     881           1 :   itp = ipsec_tun_protect_from_const_base (aed);
     882           1 :   s = format (s, "ipsec-tun-protect:\n%U", format_ipsec_tun_protect, itp);
     883             : 
     884           1 :   return (s);
     885             : }
     886             : 
     887             : static void
     888          85 : ipsec_tun_teib_entry_added (const teib_entry_t * ne)
     889             : {
     890             :   ipsec_tun_protect_t *itp;
     891             :   index_t itpi;
     892             : 
     893          85 :   itpi = ipsec_tun_protect_find (teib_entry_get_sw_if_index (ne),
     894             :                                  teib_entry_get_peer (ne));
     895             : 
     896          85 :   if (INDEX_INVALID == itpi)
     897          53 :     return;
     898             : 
     899          32 :   itp = ipsec_tun_protect_get (itpi);
     900          32 :   ipsec_tun_protect_rx_db_remove (&ipsec_main, itp);
     901          32 :   ipsec_tun_protect_update_from_teib (itp, ne);
     902          32 :   ipsec_tun_protect_set_crypto_addr (itp);
     903          32 :   ipsec_tun_protect_rx_db_add (&ipsec_main, itp);
     904             : 
     905          32 :   ITP_DBG (itp, "teib-added");
     906             : }
     907             : 
     908             : static void
     909          85 : ipsec_tun_teib_entry_deleted (const teib_entry_t * ne)
     910             : {
     911             :   ipsec_tun_protect_t *itp;
     912             :   index_t itpi;
     913             : 
     914          85 :   itpi = ipsec_tun_protect_find (teib_entry_get_sw_if_index (ne),
     915             :                                  teib_entry_get_peer (ne));
     916             : 
     917          85 :   if (INDEX_INVALID == itpi)
     918          53 :     return;
     919             : 
     920          32 :   itp = ipsec_tun_protect_get (itpi);
     921          32 :   ipsec_tun_protect_rx_db_remove (&ipsec_main, itp);
     922          32 :   ipsec_tun_protect_update_from_teib (itp, NULL);
     923          32 :   ipsec_tun_protect_set_crypto_addr (itp);
     924             : 
     925          32 :   ITP_DBG (itp, "teib-removed");
     926             : }
     927             : 
     928             : /**
     929             :  * VFT registered with the adjacency delegate
     930             :  */
     931             : const static adj_delegate_vft_t ipsec_tun_adj_delegate_vft = {
     932             :   .adv_adj_deleted = ipsec_tun_protect_adj_delegate_adj_deleted,
     933             :   .adv_adj_created = ipsec_tun_protect_adj_delegate_adj_created,
     934             :   .adv_adj_modified = ipsec_tun_protect_adj_delegate_adj_modified,
     935             :   .adv_format = ipsec_tun_protect_adj_delegate_format,
     936             : };
     937             : 
     938             : const static teib_vft_t ipsec_tun_teib_vft = {
     939             :   .nv_added = ipsec_tun_teib_entry_added,
     940             :   .nv_deleted = ipsec_tun_teib_entry_deleted,
     941             : };
     942             : 
     943             : void
     944           0 : ipsec_tun_table_init (ip_address_family_t af, uword table_size, u32 n_buckets)
     945             : {
     946             :   ipsec_main_t *im;
     947             : 
     948           0 :   im = &ipsec_main;
     949             : 
     950           0 :   if (AF_IP4 == af)
     951           0 :     clib_bihash_init_8_16 (&im->tun4_protect_by_key,
     952             :                            "IPSec IPv4 tunnels", n_buckets, table_size);
     953             :   else
     954           0 :     clib_bihash_init_24_16 (&im->tun6_protect_by_key,
     955             :                             "IPSec IPv6 tunnels", n_buckets, table_size);
     956           0 : }
     957             : 
     958             : static clib_error_t *
     959         575 : ipsec_tunnel_protect_init (vlib_main_t *vm)
     960             : {
     961             :   ipsec_main_t *im;
     962             : 
     963         575 :   im = &ipsec_main;
     964         575 :   clib_bihash_init_24_16 (&im->tun6_protect_by_key,
     965             :                           "IPSec IPv6 tunnels",
     966             :                           IPSEC_TUN_DEFAULT_HASH_NUM_BUCKETS,
     967             :                           IPSEC_TUN_DEFAULT_HASH_MEMORY_SIZE);
     968         575 :   clib_bihash_init_8_16 (&im->tun4_protect_by_key,
     969             :                          "IPSec IPv4 tunnels",
     970             :                          IPSEC_TUN_DEFAULT_HASH_NUM_BUCKETS,
     971             :                          IPSEC_TUN_DEFAULT_HASH_MEMORY_SIZE);
     972             : 
     973         575 :   ipsec_tun_adj_delegate_type =
     974         575 :     adj_delegate_register_new_type (&ipsec_tun_adj_delegate_vft);
     975             : 
     976         575 :   ipsec_tun_protect_logger = vlib_log_register_class ("ipsec", "tun");
     977             : 
     978         575 :   teib_register (&ipsec_tun_teib_vft);
     979             : 
     980         575 :   vnet_feature_register (ipsec_tun_feature_update, NULL);
     981             : 
     982         575 :   return 0;
     983             : }
     984             : 
     985       54143 : VLIB_INIT_FUNCTION (ipsec_tunnel_protect_init);
     986             : 
     987             : /*
     988             :  * fd.io coding-style-patch-verification: ON
     989             :  *
     990             :  * Local Variables:
     991             :  * eval: (c-set-style "gnu")
     992             :  * End:
     993             :  */

Generated by: LCOV version 1.14