LCOV - code coverage report
Current view: top level - vnet/ipsec - ipsec_itf.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 122 173 70.5 %
Date: 2023-10-26 01:39:38 Functions: 30 35 85.7 %

          Line data    Source code
       1             : /*
       2             :  * ipsec_itf.c: IPSec dedicated interface type
       3             :  *
       4             :  * Copyright (c) 2020 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/ip/ip.h>
      19             : #include <vnet/ipsec/ipsec_itf.h>
      20             : #include <vnet/ipsec/ipsec_tun.h>
      21             : #include <vnet/ipsec/ipsec.h>
      22             : #include <vnet/adj/adj_midchain.h>
      23             : #include <vnet/ethernet/mac_address.h>
      24             : #include <vnet/mpls/mpls.h>
      25             : 
      26             : /* bitmap of Allocated IPSEC_ITF instances */
      27             : static uword *ipsec_itf_instances;
      28             : 
      29             : /* pool of interfaces */
      30             : static ipsec_itf_t *ipsec_itf_pool;
      31             : 
      32             : static u32 *ipsec_itf_index_by_sw_if_index;
      33             : 
      34             : ipsec_itf_t *
      35           0 : ipsec_itf_get (index_t ii)
      36             : {
      37           0 :   return (pool_elt_at_index (ipsec_itf_pool, ii));
      38             : }
      39             : 
      40             : u32
      41        1155 : ipsec_itf_count (void)
      42             : {
      43        1155 :   return (pool_elts (ipsec_itf_pool));
      44             : }
      45             : 
      46             : static ipsec_itf_t *
      47          35 : ipsec_itf_find_by_sw_if_index (u32 sw_if_index)
      48             : {
      49          35 :   if (vec_len (ipsec_itf_index_by_sw_if_index) <= sw_if_index)
      50           0 :     return NULL;
      51          35 :   u32 ti = ipsec_itf_index_by_sw_if_index[sw_if_index];
      52          35 :   if (ti == ~0)
      53           0 :     return NULL;
      54          35 :   return pool_elt_at_index (ipsec_itf_pool, ti);
      55             : }
      56             : 
      57             : static u8 *
      58          14 : format_ipsec_itf_name (u8 * s, va_list * args)
      59             : {
      60          14 :   u32 dev_instance = va_arg (*args, u32);
      61          14 :   return format (s, "ipsec%d", dev_instance);
      62             : }
      63             : 
      64             : void
      65          23 : ipsec_itf_adj_unstack (adj_index_t ai)
      66             : {
      67          23 :   adj_midchain_delegate_unstack (ai);
      68          23 : }
      69             : 
      70             : void
      71          47 : ipsec_itf_adj_stack (adj_index_t ai, u32 sai)
      72             : {
      73             :   const vnet_hw_interface_t *hw;
      74             : 
      75          47 :   hw = vnet_get_sup_hw_interface (vnet_get_main (), adj_get_sw_if_index (ai));
      76             : 
      77          47 :   if (hw->flags & VNET_HW_INTERFACE_FLAG_LINK_UP)
      78             :     {
      79             :       const ipsec_sa_t *sa;
      80             :       fib_prefix_t dst;
      81             : 
      82          43 :       sa = ipsec_sa_get (sai);
      83          43 :       ip_address_to_fib_prefix (&sa->tunnel.t_dst, &dst);
      84          43 :       adj_midchain_delegate_stack (ai, sa->tunnel.t_fib_index, &dst);
      85             :     }
      86             :   else
      87           4 :     adj_midchain_delegate_unstack (ai);
      88          47 : }
      89             : 
      90             : static adj_walk_rc_t
      91           8 : ipsec_itf_adj_stack_cb (adj_index_t ai, void *arg)
      92             : {
      93           8 :   ipsec_tun_protect_t *itp = arg;
      94             : 
      95           8 :   ipsec_itf_adj_stack (ai, itp->itp_out_sa);
      96             : 
      97           8 :   return (ADJ_WALK_RC_CONTINUE);
      98             : }
      99             : 
     100             : static void
     101           4 : ipsec_itf_restack (index_t itpi, const ipsec_itf_t * itf)
     102             : {
     103             :   ipsec_tun_protect_t *itp;
     104             :   fib_protocol_t proto;
     105             : 
     106           4 :   itp = ipsec_tun_protect_get (itpi);
     107             : 
     108             :   /*
     109             :    * walk all the adjacencies on the interface and restack them
     110             :    */
     111          12 :   FOR_EACH_FIB_IP_PROTOCOL (proto)
     112             :   {
     113           8 :     adj_nbr_walk (itf->ii_sw_if_index, proto, ipsec_itf_adj_stack_cb, itp);
     114             :   }
     115           4 : }
     116             : 
     117             : static walk_rc_t
     118           4 : ipsec_tun_protect_walk_state_change (index_t itpi, void *arg)
     119             : {
     120           4 :   const ipsec_itf_t *itf = arg;
     121             : 
     122           4 :   ipsec_itf_restack (itpi, itf);
     123             : 
     124           4 :   return (WALK_CONTINUE);
     125             : }
     126             : 
     127             : static clib_error_t *
     128          24 : ipsec_itf_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
     129             : {
     130             :   vnet_hw_interface_t *hi;
     131             :   ipsec_itf_t *itf;
     132             :   u32 hw_flags;
     133             : 
     134          24 :   hi = vnet_get_hw_interface (vnm, hw_if_index);
     135          24 :   hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ?
     136             :               VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
     137          24 :   vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
     138             : 
     139          24 :   itf = ipsec_itf_find_by_sw_if_index (hi->sw_if_index);
     140             : 
     141          24 :   if (itf)
     142          24 :     ipsec_tun_protect_walk_itf (itf->ii_sw_if_index,
     143             :                                 ipsec_tun_protect_walk_state_change, itf);
     144             : 
     145          24 :   return (NULL);
     146             : }
     147             : 
     148             : static int
     149          26 : ipsec_itf_tunnel_desc (u32 sw_if_index,
     150             :                        ip46_address_t * src, ip46_address_t * dst, u8 * is_l2)
     151             : {
     152          26 :   ip46_address_reset (src);
     153          26 :   ip46_address_reset (dst);
     154          26 :   *is_l2 = 0;
     155             : 
     156          26 :   return (0);
     157             : }
     158             : 
     159             : static u8 *
     160          37 : ipsec_itf_build_rewrite (void)
     161             : {
     162             :   /*
     163             :    * passing the adj code a NULL rewrite means 'i don't have one cos
     164             :    * t'other end is unresolved'. That's not the case here. For the ipsec
     165             :    * tunnel there are just no bytes of encap to apply in the adj.
     166             :    * So return a zero length rewrite. Encap will be added by a tunnel mode SA.
     167             :    */
     168          37 :   u8 *rewrite = NULL;
     169             : 
     170          37 :   vec_validate (rewrite, 0);
     171          37 :   vec_reset_length (rewrite);
     172             : 
     173          37 :   return (rewrite);
     174             : }
     175             : 
     176             : static u8 *
     177           0 : ipsec_itf_build_rewrite_i (vnet_main_t * vnm,
     178             :                            u32 sw_if_index,
     179             :                            vnet_link_t link_type, const void *dst_address)
     180             : {
     181           0 :   return (ipsec_itf_build_rewrite ());
     182             : }
     183             : 
     184             : void
     185          37 : ipsec_itf_update_adj (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai)
     186             : {
     187          37 :   adj_nbr_midchain_update_rewrite
     188             :     (ai, NULL, NULL, ADJ_FLAG_MIDCHAIN_IP_STACK, ipsec_itf_build_rewrite ());
     189          37 : }
     190             : 
     191             : /* *INDENT-OFF* */
     192       12095 : VNET_DEVICE_CLASS (ipsec_itf_device_class) = {
     193             :   .name = "IPSEC Tunnel",
     194             :   .format_device_name = format_ipsec_itf_name,
     195             :   .admin_up_down_function = ipsec_itf_admin_up_down,
     196             :   .ip_tun_desc = ipsec_itf_tunnel_desc,
     197             : };
     198             : 
     199        8063 : VNET_HW_INTERFACE_CLASS(ipsec_hw_interface_class) = {
     200             :   .name = "IPSec",
     201             :   .build_rewrite = ipsec_itf_build_rewrite_i,
     202             :   .update_adjacency = ipsec_itf_update_adj,
     203             :   .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
     204             : };
     205        8063 : VNET_HW_INTERFACE_CLASS(ipsec_p2mp_hw_interface_class) = {
     206             :   .name = "IPSec",
     207             :   .build_rewrite = ipsec_itf_build_rewrite_i,
     208             :   .update_adjacency = ipsec_itf_update_adj,
     209             :   .flags = VNET_HW_INTERFACE_CLASS_FLAG_NBMA,
     210             : };
     211             : /* *INDENT-ON* */
     212             : 
     213             : /*
     214             :  * Maintain a bitmap of allocated ipsec_itf instance numbers.
     215             :  */
     216             : #define IPSEC_ITF_MAX_INSTANCE          (16 * 1024)
     217             : 
     218             : static u32
     219          11 : ipsec_itf_instance_alloc (u32 want)
     220             : {
     221             :   /*
     222             :    * Check for dynamically allocated instance number.
     223             :    */
     224          11 :   if (~0 == want)
     225             :     {
     226             :       u32 bit;
     227             : 
     228          10 :       bit = clib_bitmap_first_clear (ipsec_itf_instances);
     229          10 :       if (bit >= IPSEC_ITF_MAX_INSTANCE)
     230             :         {
     231           0 :           return ~0;
     232             :         }
     233          10 :       ipsec_itf_instances = clib_bitmap_set (ipsec_itf_instances, bit, 1);
     234          10 :       return bit;
     235             :     }
     236             : 
     237             :   /*
     238             :    * In range?
     239             :    */
     240           1 :   if (want >= IPSEC_ITF_MAX_INSTANCE)
     241             :     {
     242           0 :       return ~0;
     243             :     }
     244             : 
     245             :   /*
     246             :    * Already in use?
     247             :    */
     248           1 :   if (clib_bitmap_get (ipsec_itf_instances, want))
     249             :     {
     250           0 :       return ~0;
     251             :     }
     252             : 
     253             :   /*
     254             :    * Grant allocation request.
     255             :    */
     256           1 :   ipsec_itf_instances = clib_bitmap_set (ipsec_itf_instances, want, 1);
     257             : 
     258           1 :   return want;
     259             : }
     260             : 
     261             : static int
     262          11 : ipsec_itf_instance_free (u32 instance)
     263             : {
     264          11 :   if (instance >= IPSEC_ITF_MAX_INSTANCE)
     265             :     {
     266           0 :       return -1;
     267             :     }
     268             : 
     269          11 :   if (clib_bitmap_get (ipsec_itf_instances, instance) == 0)
     270             :     {
     271           0 :       return -1;
     272             :     }
     273             : 
     274          11 :   ipsec_itf_instances = clib_bitmap_set (ipsec_itf_instances, instance, 0);
     275          11 :   return 0;
     276             : }
     277             : 
     278             : void
     279         180 : ipsec_itf_reset_tx_nodes (u32 sw_if_index)
     280             : {
     281         180 :   vnet_feature_modify_end_node (
     282         180 :     ip4_main.lookup_main.output_feature_arc_index, sw_if_index,
     283         180 :     vlib_get_node_by_name (vlib_get_main (), (u8 *) "ip4-drop")->index);
     284         180 :   vnet_feature_modify_end_node (
     285         180 :     ip6_main.lookup_main.output_feature_arc_index, sw_if_index,
     286         180 :     vlib_get_node_by_name (vlib_get_main (), (u8 *) "ip6-drop")->index);
     287         180 :   vnet_feature_modify_end_node (
     288         180 :     mpls_main.output_feature_arc_index, sw_if_index,
     289         180 :     vlib_get_node_by_name (vlib_get_main (), (u8 *) "mpls-drop")->index);
     290         180 : }
     291             : 
     292             : int
     293          11 : ipsec_itf_create (u32 user_instance, tunnel_mode_t mode, u32 * sw_if_indexp)
     294             : {
     295          11 :   vnet_main_t *vnm = vnet_get_main ();
     296             :   u32 instance, hw_if_index;
     297             :   vnet_hw_interface_t *hi;
     298             :   ipsec_itf_t *ipsec_itf;
     299             : 
     300          11 :   ASSERT (sw_if_indexp);
     301             : 
     302          11 :   *sw_if_indexp = (u32) ~ 0;
     303             : 
     304             :   /*
     305             :    * Allocate a ipsec_itf instance.  Either select on dynamically
     306             :    * or try to use the desired user_instance number.
     307             :    */
     308          11 :   instance = ipsec_itf_instance_alloc (user_instance);
     309          11 :   if (instance == ~0)
     310           0 :     return VNET_API_ERROR_INVALID_REGISTRATION;
     311             : 
     312          11 :   pool_get (ipsec_itf_pool, ipsec_itf);
     313             : 
     314             :   /* tunnel index (or instance) */
     315          11 :   u32 t_idx = ipsec_itf - ipsec_itf_pool;
     316             : 
     317          11 :   ipsec_itf->ii_mode = mode;
     318          11 :   ipsec_itf->ii_user_instance = instance;
     319             : 
     320          11 :   hw_if_index = vnet_register_interface (vnm,
     321             :                                          ipsec_itf_device_class.index,
     322          11 :                                          ipsec_itf->ii_user_instance,
     323             :                                          (mode == TUNNEL_MODE_P2P ?
     324             :                                           ipsec_hw_interface_class.index :
     325             :                                           ipsec_p2mp_hw_interface_class.index),
     326             :                                          t_idx);
     327             : 
     328          11 :   hi = vnet_get_hw_interface (vnm, hw_if_index);
     329          11 :   vnet_sw_interface_set_mtu (vnm, hi->sw_if_index, 9000);
     330             : 
     331          47 :   vec_validate_init_empty (ipsec_itf_index_by_sw_if_index, hi->sw_if_index,
     332             :                            INDEX_INVALID);
     333          11 :   ipsec_itf_index_by_sw_if_index[hi->sw_if_index] = t_idx;
     334             : 
     335          11 :   ipsec_itf->ii_sw_if_index = *sw_if_indexp = hi->sw_if_index;
     336          11 :   ipsec_itf_reset_tx_nodes (hi->sw_if_index);
     337             : 
     338          11 :   return 0;
     339             : }
     340             : 
     341             : int
     342          11 : ipsec_itf_delete (u32 sw_if_index)
     343             : {
     344          11 :   vnet_main_t *vnm = vnet_get_main ();
     345             : 
     346          11 :   if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
     347           0 :     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
     348             : 
     349          11 :   vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
     350          11 :   if (hw == 0 || hw->dev_class_index != ipsec_itf_device_class.index)
     351           0 :     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
     352             : 
     353             :   ipsec_itf_t *ipsec_itf;
     354          11 :   ipsec_itf = ipsec_itf_find_by_sw_if_index (sw_if_index);
     355          11 :   if (NULL == ipsec_itf)
     356           0 :     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
     357             : 
     358          11 :   if (ipsec_itf_instance_free (hw->dev_instance) < 0)
     359           0 :     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
     360             : 
     361          11 :   vnet_reset_interface_l3_output_node (vnm->vlib_main, sw_if_index);
     362             : 
     363          11 :   vnet_delete_hw_interface (vnm, hw->hw_if_index);
     364          11 :   pool_put (ipsec_itf_pool, ipsec_itf);
     365             : 
     366          11 :   return 0;
     367             : }
     368             : 
     369             : void
     370          24 : ipsec_itf_walk (ipsec_itf_walk_cb_t cb, void *ctx)
     371             : {
     372             :   ipsec_itf_t *itf;
     373             : 
     374          37 :   pool_foreach (itf, ipsec_itf_pool)
     375             :     {
     376          13 :       if (WALK_CONTINUE != cb (itf, ctx))
     377           0 :         break;
     378             :     }
     379          24 : }
     380             : 
     381             : static clib_error_t *
     382           0 : ipsec_itf_create_cli (vlib_main_t * vm,
     383             :                       unformat_input_t * input, vlib_cli_command_t * cmd)
     384             : {
     385           0 :   unformat_input_t _line_input, *line_input = &_line_input;
     386             :   u32 instance, sw_if_index;
     387             :   clib_error_t *error;
     388             :   mac_address_t mac;
     389             :   int rv;
     390             : 
     391           0 :   error = NULL;
     392           0 :   instance = sw_if_index = ~0;
     393           0 :   mac_address_set_zero (&mac);
     394             : 
     395           0 :   if (unformat_user (input, unformat_line_input, line_input))
     396             :     {
     397           0 :       while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
     398             :         {
     399           0 :           if (unformat (line_input, "instance %d", &instance))
     400             :             ;
     401             :           else
     402             :             {
     403           0 :               error = clib_error_return (0, "unknown input: %U",
     404             :                                          format_unformat_error, line_input);
     405           0 :               break;
     406             :             }
     407             :         }
     408             : 
     409           0 :       unformat_free (line_input);
     410             : 
     411           0 :       if (error)
     412           0 :         return error;
     413             :     }
     414             : 
     415           0 :   rv = ipsec_itf_create (instance, TUNNEL_MODE_P2P, &sw_if_index);
     416             : 
     417           0 :   if (rv)
     418           0 :     return clib_error_return (0, "iPSec interface create failed");
     419             : 
     420           0 :   vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main (),
     421             :                    sw_if_index);
     422           0 :   return 0;
     423             : }
     424             : 
     425             : /*?
     426             :  * Create a IPSec interface.
     427             :  *
     428             :  * @cliexpar
     429             :  * The following two command syntaxes are equivalent:
     430             :  * @cliexcmd{ipsec itf create [instance <instance>]}
     431             :  * Example of how to create a ipsec interface:
     432             :  * @cliexcmd{ipsec itf create}
     433             : ?*/
     434             : /* *INDENT-OFF* */
     435      285289 : VLIB_CLI_COMMAND (ipsec_itf_create_command, static) = {
     436             :   .path = "ipsec itf create",
     437             :   .short_help = "ipsec itf create [instance <instance>]",
     438             :   .function = ipsec_itf_create_cli,
     439             : };
     440             : /* *INDENT-ON* */
     441             : 
     442             : static clib_error_t *
     443           0 : ipsec_itf_delete_cli (vlib_main_t * vm,
     444             :                       unformat_input_t * input, vlib_cli_command_t * cmd)
     445             : {
     446             :   vnet_main_t *vnm;
     447             :   u32 sw_if_index;
     448             :   int rv;
     449             : 
     450           0 :   vnm = vnet_get_main ();
     451           0 :   sw_if_index = ~0;
     452             : 
     453           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     454             :     {
     455           0 :       if (unformat
     456             :           (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
     457             :         ;
     458             :       else
     459           0 :         break;
     460             :     }
     461             : 
     462           0 :   if (~0 != sw_if_index)
     463             :     {
     464           0 :       rv = ipsec_itf_delete (sw_if_index);
     465             : 
     466           0 :       if (rv)
     467           0 :         return clib_error_return (0, "ipsec interface delete failed");
     468             :     }
     469             :   else
     470           0 :     return clib_error_return (0, "no such interface: %U",
     471             :                               format_unformat_error, input);
     472             : 
     473           0 :   return 0;
     474             : }
     475             : 
     476             : /*?
     477             :  * Delete a IPSEC_ITF interface.
     478             :  *
     479             :  * @cliexpar
     480             :  * The following two command syntaxes are equivalent:
     481             :  * @cliexcmd{ipsec itf delete <interface>}
     482             :  * Example of how to create a ipsec_itf interface:
     483             :  * @cliexcmd{ipsec itf delete ipsec0}
     484             : ?*/
     485             : /* *INDENT-OFF* */
     486      285289 : VLIB_CLI_COMMAND (ipsec_itf_delete_command, static) = {
     487             :   .path = "ipsec itf delete",
     488             :   .short_help = "ipsec itf delete <interface>",
     489             :   .function = ipsec_itf_delete_cli,
     490             : };
     491             : /* *INDENT-ON* */
     492             : 
     493             : static clib_error_t *
     494           0 : ipsec_interface_show (vlib_main_t * vm,
     495             :                       unformat_input_t * input, vlib_cli_command_t * cmd)
     496             : {
     497             :   index_t ii;
     498             : 
     499             :   /* *INDENT-OFF* */
     500           0 :   pool_foreach_index (ii, ipsec_itf_pool)
     501             :    {
     502           0 :     vlib_cli_output (vm, "%U", format_ipsec_itf, ii);
     503             :   }
     504             :   /* *INDENT-ON* */
     505             : 
     506           0 :   return NULL;
     507             : }
     508             : 
     509             : /**
     510             :  * show IPSEC tunnel protection hash tables
     511             :  */
     512             : /* *INDENT-OFF* */
     513      285289 : VLIB_CLI_COMMAND (ipsec_interface_show_node, static) =
     514             : {
     515             :   .path = "show ipsec interface",
     516             :   .function = ipsec_interface_show,
     517             :   .short_help =  "show ipsec interface",
     518             : };
     519             : /* *INDENT-ON* */
     520             : 
     521             : /*
     522             :  * fd.io coding-style-patch-verification: ON
     523             :  *
     524             :  * Local Variables:
     525             :  * eval: (c-set-style "gnu")
     526             :  * End:
     527             :  */

Generated by: LCOV version 1.14