LCOV - code coverage report
Current view: top level - plugins/wireguard - wireguard_if.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 142 164 86.6 %
Date: 2023-07-05 22:20:52 Functions: 21 21 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2020 Cisco and/or its affiliates.
       3             :  * Copyright (c) 2020 Doc.ai 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 <vnet/adj/adj_midchain.h>
      18             : #include <vnet/udp/udp.h>
      19             : 
      20             : #include <wireguard/wireguard_messages.h>
      21             : #include <wireguard/wireguard_if.h>
      22             : #include <wireguard/wireguard.h>
      23             : #include <wireguard/wireguard_peer.h>
      24             : 
      25             : /* pool of interfaces */
      26             : wg_if_t *wg_if_pool;
      27             : 
      28             : /* bitmap of Allocated WG_ITF instances */
      29             : static uword *wg_if_instances;
      30             : 
      31             : /* vector of interfaces key'd on their sw_if_index */
      32             : static index_t *wg_if_index_by_sw_if_index;
      33             : 
      34             : /* vector of interfaces key'd on their UDP port (in network order) */
      35             : index_t **wg_if_indexes_by_port;
      36             : 
      37             : /* pool of ratelimit entries */
      38             : static ratelimit_entry_t *wg_ratelimit_pool;
      39             : 
      40             : static u8 *
      41          82 : format_wg_if_name (u8 * s, va_list * args)
      42             : {
      43          82 :   u32 dev_instance = va_arg (*args, u32);
      44          82 :   wg_if_t *wgi = wg_if_get (dev_instance);
      45          82 :   return format (s, "wg%d", wgi->user_instance);
      46             : }
      47             : 
      48             : u8 *
      49           4 : format_wg_if (u8 * s, va_list * args)
      50             : {
      51           4 :   index_t wgii = va_arg (*args, u32);
      52           4 :   wg_if_t *wgi = wg_if_get (wgii);
      53           4 :   noise_local_t *local = noise_local_get (wgi->local_idx);
      54             :   u8 key[NOISE_KEY_LEN_BASE64];
      55             : 
      56           4 :   s = format (s, "[%d] %U src:%U port:%d",
      57             :               wgii,
      58             :               format_vnet_sw_if_index_name, vnet_get_main (),
      59           4 :               wgi->sw_if_index, format_ip_address, &wgi->src_ip, wgi->port);
      60             : 
      61           4 :   key_to_base64 (local->l_private, NOISE_PUBLIC_KEY_LEN, key);
      62             : 
      63           4 :   s = format (s, " private-key:%s", key);
      64             :   s =
      65           4 :     format (s, " %U", format_hex_bytes, local->l_private,
      66             :             NOISE_PUBLIC_KEY_LEN);
      67             : 
      68           4 :   key_to_base64 (local->l_public, NOISE_PUBLIC_KEY_LEN, key);
      69             : 
      70           4 :   s = format (s, " public-key:%s", key);
      71             : 
      72             :   s =
      73           4 :     format (s, " %U", format_hex_bytes, local->l_public,
      74             :             NOISE_PUBLIC_KEY_LEN);
      75             : 
      76           4 :   s = format (s, " mac-key: %U", format_hex_bytes,
      77             :               &wgi->cookie_checker.cc_mac1_key, NOISE_PUBLIC_KEY_LEN);
      78             : 
      79           4 :   return (s);
      80             : }
      81             : 
      82             : index_t
      83         856 : wg_if_find_by_sw_if_index (u32 sw_if_index)
      84             : {
      85         856 :   if (vec_len (wg_if_index_by_sw_if_index) <= sw_if_index)
      86           0 :     return INDEX_INVALID;
      87         856 :   u32 ti = wg_if_index_by_sw_if_index[sw_if_index];
      88         856 :   if (ti == ~0)
      89           0 :     return INDEX_INVALID;
      90             : 
      91         856 :   return (ti);
      92             : }
      93             : 
      94             : static walk_rc_t
      95         813 : wg_if_find_peer_by_public_key (index_t peeri, void *data)
      96             : {
      97         813 :   uint8_t *public = data;
      98         813 :   wg_peer_t *peer = wg_peer_get (peeri);
      99             : 
     100         813 :   if (!memcmp (peer->remote.r_public, public, NOISE_PUBLIC_KEY_LEN))
     101         627 :     return (WALK_STOP);
     102         186 :   return (WALK_CONTINUE);
     103             : }
     104             : 
     105             : static noise_remote_t *
     106         627 : wg_remote_get (const uint8_t public[NOISE_PUBLIC_KEY_LEN])
     107             : {
     108             :   index_t peeri;
     109             : 
     110         627 :   peeri = wg_peer_walk (wg_if_find_peer_by_public_key, (void *) public);
     111             : 
     112         627 :   if (INDEX_INVALID != peeri)
     113         627 :     return &wg_peer_get (peeri)->remote;
     114             : 
     115           0 :   return NULL;
     116             : }
     117             : 
     118             : static uint32_t
     119         214 : wg_index_set (vlib_main_t *vm, noise_remote_t *remote)
     120             : {
     121         214 :   wg_main_t *wmp = &wg_main;
     122         214 :   u32 rnd_seed = (u32) (vlib_time_now (wmp->vlib_main) * 1e6);
     123             :   u32 ret =
     124         214 :     wg_index_table_add (vm, &wmp->index_table, remote->r_peer_idx, rnd_seed);
     125         214 :   return ret;
     126             : }
     127             : 
     128             : static void
     129         214 : wg_index_drop (vlib_main_t *vm, uint32_t key)
     130             : {
     131         214 :   wg_main_t *wmp = &wg_main;
     132         214 :   wg_index_table_del (vm, &wmp->index_table, key);
     133         214 : }
     134             : 
     135             : static clib_error_t *
     136         164 : wg_if_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
     137             : {
     138             :   vnet_hw_interface_t *hi;
     139             :   index_t wgii;
     140             :   u32 hw_flags;
     141             : 
     142         164 :   hi = vnet_get_hw_interface (vnm, hw_if_index);
     143         164 :   hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ?
     144             :               VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
     145         164 :   vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
     146             : 
     147         164 :   wgii = wg_if_find_by_sw_if_index (hi->sw_if_index);
     148             : 
     149         164 :   wg_if_peer_walk (wg_if_get (wgii), wg_peer_if_admin_state_change, NULL);
     150             : 
     151         164 :   return (NULL);
     152             : }
     153             : 
     154             : void
     155         116 : wg_if_update_adj (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai)
     156             : {
     157             :   index_t wgii;
     158             : 
     159             :   /* Convert any neighbour adjacency that has a next-hop reachable through
     160             :    * the wg interface into a midchain. This is to avoid sending ARP/ND to
     161             :    * resolve the next-hop address via the wg interface. Then, if one of the
     162             :    * peers has matching prefix among allowed prefixes, the midchain will be
     163             :    * updated to the corresponding one.
     164             :    */
     165         116 :   adj_nbr_midchain_update_rewrite (ai, NULL, NULL, ADJ_FLAG_NONE, NULL);
     166             : 
     167         116 :   wgii = wg_if_find_by_sw_if_index (sw_if_index);
     168         116 :   wg_if_peer_walk (wg_if_get (wgii), wg_peer_if_adj_change, &ai);
     169         116 : }
     170             : 
     171             : 
     172             : /* *INDENT-OFF* */
     173        1119 : VNET_DEVICE_CLASS (wg_if_device_class) = {
     174             :   .name = "Wireguard Tunnel",
     175             :   .format_device_name = format_wg_if_name,
     176             :   .admin_up_down_function = wg_if_admin_up_down,
     177             : };
     178             : 
     179        1119 : VNET_HW_INTERFACE_CLASS(wg_hw_interface_class) = {
     180             :   .name = "Wireguard",
     181             :   .update_adjacency = wg_if_update_adj,
     182             :   .flags = VNET_HW_INTERFACE_CLASS_FLAG_NBMA,
     183             : };
     184             : /* *INDENT-ON* */
     185             : 
     186             : /*
     187             :  * Maintain a bitmap of allocated wg_if instance numbers.
     188             :  */
     189             : #define WG_ITF_MAX_INSTANCE             (16 * 1024)
     190             : 
     191             : static u32
     192          82 : wg_if_instance_alloc (u32 want)
     193             : {
     194             :   /*
     195             :    * Check for dynamically allocated instance number.
     196             :    */
     197          82 :   if (~0 == want)
     198             :     {
     199             :       u32 bit;
     200             : 
     201          82 :       bit = clib_bitmap_first_clear (wg_if_instances);
     202          82 :       if (bit >= WG_ITF_MAX_INSTANCE)
     203             :         {
     204           0 :           return ~0;
     205             :         }
     206          82 :       wg_if_instances = clib_bitmap_set (wg_if_instances, bit, 1);
     207          82 :       return bit;
     208             :     }
     209             : 
     210             :   /*
     211             :    * In range?
     212             :    */
     213           0 :   if (want >= WG_ITF_MAX_INSTANCE)
     214             :     {
     215           0 :       return ~0;
     216             :     }
     217             : 
     218             :   /*
     219             :    * Already in use?
     220             :    */
     221           0 :   if (clib_bitmap_get (wg_if_instances, want))
     222             :     {
     223           0 :       return ~0;
     224             :     }
     225             : 
     226             :   /*
     227             :    * Grant allocation request.
     228             :    */
     229           0 :   wg_if_instances = clib_bitmap_set (wg_if_instances, want, 1);
     230             : 
     231           0 :   return want;
     232             : }
     233             : 
     234             : static int
     235          82 : wg_if_instance_free (u32 instance)
     236             : {
     237          82 :   if (instance >= WG_ITF_MAX_INSTANCE)
     238             :     {
     239           0 :       return -1;
     240             :     }
     241             : 
     242          82 :   if (clib_bitmap_get (wg_if_instances, instance) == 0)
     243             :     {
     244           0 :       return -1;
     245             :     }
     246             : 
     247          82 :   wg_if_instances = clib_bitmap_set (wg_if_instances, instance, 0);
     248          82 :   return 0;
     249             : }
     250             : 
     251             : 
     252             : int
     253          82 : wg_if_create (u32 user_instance,
     254             :               const u8 private_key[NOISE_PUBLIC_KEY_LEN],
     255             :               u16 port, const ip_address_t * src_ip, u32 * sw_if_indexp)
     256             : {
     257          82 :   vnet_main_t *vnm = vnet_get_main ();
     258             :   u32 instance, hw_if_index;
     259             :   vnet_hw_interface_t *hi;
     260             :   wg_if_t *wg_if;
     261             :   noise_local_t *local;
     262             : 
     263          82 :   ASSERT (sw_if_indexp);
     264             : 
     265          82 :   *sw_if_indexp = (u32) ~ 0;
     266             : 
     267             :   /*
     268             :    * Allocate a wg_if instance. Either select on dynamically
     269             :    * or try to use the desired user_instance number.
     270             :    */
     271          82 :   instance = wg_if_instance_alloc (user_instance);
     272          82 :   if (instance == ~0)
     273           0 :     return VNET_API_ERROR_INVALID_REGISTRATION;
     274             : 
     275             :   /* *INDENT-OFF* */
     276          82 :   struct noise_upcall upcall =  {
     277             :     .u_remote_get = wg_remote_get,
     278             :     .u_index_set = wg_index_set,
     279             :     .u_index_drop = wg_index_drop,
     280             :   };
     281             :   /* *INDENT-ON* */
     282             : 
     283          82 :   pool_get (noise_local_pool, local);
     284             : 
     285          82 :   noise_local_init (local, &upcall);
     286          82 :   if (!noise_local_set_private (local, private_key))
     287             :     {
     288           0 :       pool_put (noise_local_pool, local);
     289           0 :       wg_if_instance_free (instance);
     290           0 :       return VNET_API_ERROR_INVALID_REGISTRATION;
     291             :     }
     292             : 
     293          82 :   pool_get_zero (wg_if_pool, wg_if);
     294             : 
     295             :   /* tunnel index (or instance) */
     296          82 :   u32 t_idx = wg_if - wg_if_pool;
     297             : 
     298          82 :   wg_if->user_instance = instance;
     299          82 :   if (~0 == wg_if->user_instance)
     300           0 :     wg_if->user_instance = t_idx;
     301             : 
     302       37610 :   vec_validate_init_empty (wg_if_indexes_by_port, port, NULL);
     303          82 :   if (vec_len (wg_if_indexes_by_port[port]) == 0)
     304             :     {
     305          79 :       udp_register_dst_port (vlib_get_main (), port, wg4_input_node.index,
     306             :                              UDP_IP4);
     307          79 :       udp_register_dst_port (vlib_get_main (), port, wg6_input_node.index,
     308             :                              UDP_IP6);
     309             :     }
     310             : 
     311          82 :   vec_add1 (wg_if_indexes_by_port[port], t_idx);
     312             : 
     313          82 :   wg_if->port = port;
     314          82 :   wg_if->local_idx = local - noise_local_pool;
     315          82 :   cookie_checker_init (&wg_if->cookie_checker, wg_ratelimit_pool);
     316          82 :   cookie_checker_update (&wg_if->cookie_checker, local->l_public);
     317             : 
     318          82 :   hw_if_index = vnet_register_interface (vnm,
     319             :                                          wg_if_device_class.index,
     320             :                                          t_idx,
     321             :                                          wg_hw_interface_class.index, t_idx);
     322             : 
     323          82 :   hi = vnet_get_hw_interface (vnm, hw_if_index);
     324             : 
     325         100 :   vec_validate_init_empty (wg_if_index_by_sw_if_index, hi->sw_if_index,
     326             :                            INDEX_INVALID);
     327          82 :   wg_if_index_by_sw_if_index[hi->sw_if_index] = t_idx;
     328             : 
     329          82 :   ip_address_copy (&wg_if->src_ip, src_ip);
     330          82 :   wg_if->sw_if_index = *sw_if_indexp = hi->sw_if_index;
     331          82 :   vnet_set_interface_l3_output_node (vnm->vlib_main, hi->sw_if_index,
     332             :                                      (u8 *) "tunnel-output");
     333             : 
     334          82 :   return 0;
     335             : }
     336             : 
     337             : int
     338          82 : wg_if_delete (u32 sw_if_index)
     339             : {
     340          82 :   vnet_main_t *vnm = vnet_get_main ();
     341             : 
     342          82 :   if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
     343           0 :     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
     344             : 
     345          82 :   vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
     346          82 :   if (hw == 0 || hw->dev_class_index != wg_if_device_class.index)
     347           0 :     return VNET_API_ERROR_INVALID_VALUE;
     348             : 
     349             :   wg_if_t *wg_if;
     350          82 :   index_t wgii = wg_if_find_by_sw_if_index (sw_if_index);
     351          82 :   wg_if = wg_if_get (wgii);
     352          82 :   if (NULL == wg_if)
     353           0 :     return VNET_API_ERROR_INVALID_SW_IF_INDEX_2;
     354             : 
     355          82 :   if (wg_if_instance_free (wg_if->user_instance) < 0)
     356           0 :     return VNET_API_ERROR_INVALID_VALUE_2;
     357             : 
     358             :   // Remove peers before interface deletion
     359          82 :   wg_if_peer_walk (wg_if, wg_peer_if_delete, NULL);
     360             : 
     361          82 :   hash_free (wg_if->peers);
     362             : 
     363             :   index_t *ii;
     364          82 :   index_t *ifs = wg_if_indexes_get_by_port (wg_if->port);
     365          85 :   vec_foreach (ii, ifs)
     366             :     {
     367          84 :       if (*ii == wgii)
     368             :         {
     369          81 :           vec_del1 (ifs, ifs - ii);
     370          81 :           break;
     371             :         }
     372             :     }
     373          82 :   if (vec_len (ifs) == 0)
     374             :     {
     375          78 :       udp_unregister_dst_port (vlib_get_main (), wg_if->port, 1);
     376          78 :       udp_unregister_dst_port (vlib_get_main (), wg_if->port, 0);
     377             :     }
     378             : 
     379          82 :   cookie_checker_deinit (&wg_if->cookie_checker);
     380             : 
     381          82 :   vnet_reset_interface_l3_output_node (vnm->vlib_main, sw_if_index);
     382          82 :   vnet_delete_hw_interface (vnm, hw->hw_if_index);
     383          82 :   pool_put_index (noise_local_pool, wg_if->local_idx);
     384          82 :   pool_put (wg_if_pool, wg_if);
     385             : 
     386          82 :   return 0;
     387             : }
     388             : 
     389             : void
     390         146 : wg_if_peer_add (wg_if_t * wgi, index_t peeri)
     391             : {
     392         146 :   hash_set (wgi->peers, peeri, peeri);
     393             : 
     394         146 :   if (1 == hash_elts (wgi->peers))
     395             :     {
     396          80 :       vnet_feature_enable_disable ("ip4-output", "wg4-output-tun",
     397             :                                    wgi->sw_if_index, 1, 0, 0);
     398          80 :       vnet_feature_enable_disable ("ip6-output", "wg6-output-tun",
     399             :                                    wgi->sw_if_index, 1, 0, 0);
     400             :     }
     401         146 : }
     402             : 
     403             : void
     404         146 : wg_if_peer_remove (wg_if_t * wgi, index_t peeri)
     405             : {
     406         146 :   hash_unset (wgi->peers, peeri);
     407             : 
     408         146 :   if (0 == hash_elts (wgi->peers))
     409             :     {
     410          80 :       vnet_feature_enable_disable ("ip4-output", "wg4-output-tun",
     411             :                                    wgi->sw_if_index, 0, 0, 0);
     412          80 :       vnet_feature_enable_disable ("ip6-output", "wg6-output-tun",
     413             :                                    wgi->sw_if_index, 0, 0, 0);
     414             :     }
     415         146 : }
     416             : 
     417             : void
     418          84 : wg_if_walk (wg_if_walk_cb_t fn, void *data)
     419             : {
     420             :   index_t wgii;
     421             : 
     422             :   /* *INDENT-OFF* */
     423          88 :   pool_foreach_index (wgii, wg_if_pool)
     424             :   {
     425           4 :     if (WALK_STOP == fn(wgii, data))
     426           0 :       break;
     427             :   }
     428             :   /* *INDENT-ON* */
     429          84 : }
     430             : 
     431             : index_t
     432         362 : wg_if_peer_walk (wg_if_t * wgi, wg_if_peer_walk_cb_t fn, void *data)
     433             : {
     434             :   index_t peeri, val;
     435             : 
     436             :   /* *INDENT-OFF* */
     437       12732 :   hash_foreach (peeri, val, wgi->peers, {
     438             :     if (WALK_STOP == fn (peeri, data))
     439             :       return peeri;
     440             :   });
     441             :   /* *INDENT-ON* */
     442             : 
     443         322 :   return INDEX_INVALID;
     444             : }
     445             : 
     446             : /*
     447             :  * fd.io coding-style-patch-verification: ON
     448             :  *
     449             :  * Local Variables:
     450             :  * eval: (c-set-style "gnu")
     451             :  * End:
     452             :  */

Generated by: LCOV version 1.14