LCOV - code coverage report
Current view: top level - plugins/wireguard - wireguard_send.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 153 171 89.5 %
Date: 2023-10-26 01:39:38 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2020 Doc.ai and/or its affiliates.
       3             :  * Copyright (c) 2020 Cisco 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/vnet.h>
      18             : #include <vnet/ip/ip6_link.h>
      19             : #include <vppinfra/error.h>
      20             : #include <vlibmemory/api.h>
      21             : #include <wireguard/wireguard.h>
      22             : #include <wireguard/wireguard_send.h>
      23             : 
      24             : static int
      25         272 : ip46_enqueue_packet (vlib_main_t *vm, u32 bi0, int is_ip4)
      26             : {
      27         272 :   vlib_frame_t *f = 0;
      28         272 :   u32 lookup_node_index =
      29         272 :     is_ip4 ? ip4_lookup_node.index : ip6_lookup_node.index;
      30             : 
      31         272 :   f = vlib_get_frame_to_node (vm, lookup_node_index);
      32             :   /* f can not be NULL here - frame allocation failure causes panic */
      33             : 
      34         272 :   u32 *to_next = vlib_frame_vector_args (f);
      35         272 :   f->n_vectors = 1;
      36         272 :   to_next[0] = bi0;
      37             : 
      38         272 :   vlib_put_frame_to_node (vm, lookup_node_index, f);
      39             : 
      40         272 :   return f->n_vectors;
      41             : }
      42             : 
      43             : static void
      44         272 : wg_buffer_prepend_rewrite (vlib_main_t *vm, vlib_buffer_t *b0,
      45             :                            const u8 *rewrite, u8 is_ip4)
      46             : {
      47         272 :   if (is_ip4)
      48             :     {
      49             :       ip4_udp_header_t *hdr4;
      50             : 
      51         194 :       vlib_buffer_advance (b0, -sizeof (*hdr4));
      52             : 
      53         194 :       hdr4 = vlib_buffer_get_current (b0);
      54             : 
      55             :       /* copy only ip4 and udp header; wireguard header not needed */
      56         194 :       clib_memcpy (hdr4, rewrite, sizeof (ip4_udp_header_t));
      57             : 
      58         194 :       hdr4->udp.length =
      59         194 :         clib_host_to_net_u16 (b0->current_length - sizeof (ip4_header_t));
      60         194 :       ip4_header_set_len_w_chksum (&hdr4->ip4,
      61         194 :                                    clib_host_to_net_u16 (b0->current_length));
      62             :     }
      63             :   else
      64             :     {
      65             :       ip6_udp_header_t *hdr6;
      66             : 
      67          78 :       vlib_buffer_advance (b0, -sizeof (*hdr6));
      68             : 
      69          78 :       hdr6 = vlib_buffer_get_current (b0);
      70             : 
      71             :       /* copy only ip6 and udp header; wireguard header not needed */
      72          78 :       clib_memcpy (hdr6, rewrite, sizeof (ip6_udp_header_t));
      73             : 
      74          78 :       hdr6->ip6.payload_length = hdr6->udp.length =
      75          78 :         clib_host_to_net_u16 (b0->current_length - sizeof (ip6_header_t));
      76             : 
      77             :       /* IPv6 UDP checksum is mandatory */
      78          78 :       int bogus = 0;
      79          78 :       ip6_header_t *ip6_0 = &(hdr6->ip6);
      80          78 :       hdr6->udp.checksum =
      81          78 :         ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip6_0, &bogus);
      82          78 :       ASSERT (bogus == 0);
      83             :     }
      84         272 : }
      85             : 
      86             : static bool
      87         272 : wg_create_buffer (vlib_main_t *vm, const u8 *rewrite, const u8 *packet,
      88             :                   u32 packet_len, u32 *bi, u8 is_ip4)
      89             : {
      90         272 :   u32 n_buf0 = 0;
      91             :   vlib_buffer_t *b0;
      92             : 
      93         272 :   n_buf0 = vlib_buffer_alloc (vm, bi, 1);
      94         272 :   if (!n_buf0)
      95           0 :     return false;
      96             : 
      97         272 :   b0 = vlib_get_buffer (vm, *bi);
      98             : 
      99         272 :   u8 *payload = vlib_buffer_get_current (b0);
     100         272 :   clib_memcpy (payload, packet, packet_len);
     101             : 
     102         272 :   b0->current_length = packet_len;
     103             : 
     104         272 :   wg_buffer_prepend_rewrite (vm, b0, rewrite, is_ip4);
     105             : 
     106         272 :   return true;
     107             : }
     108             : 
     109             : u8 *
     110         190 : wg_build_rewrite (ip46_address_t *src_addr, u16 src_port,
     111             :                   ip46_address_t *dst_addr, u16 dst_port, u8 is_ip4)
     112             : {
     113         190 :   if (ip46_address_is_zero (dst_addr) || 0 == dst_port)
     114           4 :     return NULL;
     115             : 
     116         186 :   u8 *rewrite = NULL;
     117         186 :   if (is_ip4)
     118             :     {
     119             :       ip4_udp_header_t *hdr;
     120             : 
     121             :       /* reserve space for ip4, udp and wireguard headers */
     122         140 :       vec_validate (rewrite, sizeof (ip4_udp_wg_header_t) - 1);
     123         140 :       hdr = (ip4_udp_header_t *) rewrite;
     124             : 
     125         140 :       hdr->ip4.ip_version_and_header_length = 0x45;
     126         140 :       hdr->ip4.ttl = 64;
     127         140 :       hdr->ip4.src_address = src_addr->ip4;
     128         140 :       hdr->ip4.dst_address = dst_addr->ip4;
     129         140 :       hdr->ip4.protocol = IP_PROTOCOL_UDP;
     130         140 :       hdr->ip4.checksum = ip4_header_checksum (&hdr->ip4);
     131             : 
     132         140 :       hdr->udp.src_port = clib_host_to_net_u16 (src_port);
     133         140 :       hdr->udp.dst_port = clib_host_to_net_u16 (dst_port);
     134         140 :       hdr->udp.checksum = 0;
     135             :     }
     136             :   else
     137             :     {
     138             :       ip6_udp_header_t *hdr;
     139             : 
     140             :       /* reserve space for ip6, udp and wireguard headers */
     141          46 :       vec_validate (rewrite, sizeof (ip6_udp_wg_header_t) - 1);
     142          46 :       hdr = (ip6_udp_header_t *) rewrite;
     143             : 
     144          46 :       hdr->ip6.ip_version_traffic_class_and_flow_label = 0x60;
     145          46 :       ip6_address_copy (&hdr->ip6.src_address, &src_addr->ip6);
     146          46 :       ip6_address_copy (&hdr->ip6.dst_address, &dst_addr->ip6);
     147          46 :       hdr->ip6.protocol = IP_PROTOCOL_UDP;
     148          46 :       hdr->ip6.hop_limit = 64;
     149             : 
     150          46 :       hdr->udp.src_port = clib_host_to_net_u16 (src_port);
     151          46 :       hdr->udp.dst_port = clib_host_to_net_u16 (dst_port);
     152          46 :       hdr->udp.checksum = 0;
     153             :     }
     154             : 
     155         186 :   return (rewrite);
     156             : }
     157             : 
     158             : bool
     159         168 : wg_send_handshake (vlib_main_t * vm, wg_peer_t * peer, bool is_retry)
     160             : {
     161         168 :   ASSERT (vm->thread_index == 0);
     162             : 
     163         168 :   if (!wg_peer_can_send (peer))
     164           0 :     return false;
     165             : 
     166             :   message_handshake_initiation_t packet;
     167             : 
     168         168 :   if (!is_retry)
     169         156 :     peer->timer_handshake_attempts = 0;
     170             : 
     171         318 :   if (!wg_birthdate_has_expired (peer->last_sent_handshake, REKEY_TIMEOUT) ||
     172         150 :       wg_peer_is_dead (peer))
     173          18 :     return true;
     174             : 
     175         150 :   if (noise_create_initiation (vm,
     176             :                                &peer->remote,
     177             :                                &packet.sender_index,
     178             :                                packet.unencrypted_ephemeral,
     179             :                                packet.encrypted_static,
     180             :                                packet.encrypted_timestamp))
     181             :     {
     182         150 :       packet.header.type = MESSAGE_HANDSHAKE_INITIATION;
     183         150 :       cookie_maker_mac (&peer->cookie_maker, &packet.macs, &packet,
     184             :                         sizeof (packet));
     185         150 :       wg_timers_any_authenticated_packet_sent (peer);
     186         150 :       wg_timers_handshake_initiated (peer);
     187         150 :       wg_timers_any_authenticated_packet_traversal (peer);
     188             : 
     189         150 :       peer->last_sent_handshake = vlib_time_now (vm);
     190             :     }
     191             :   else
     192           0 :     return false;
     193             : 
     194         150 :   u8 is_ip4 = ip46_address_is_ip4 (&peer->dst.addr);
     195         150 :   u32 bi0 = 0;
     196         150 :   if (!wg_create_buffer (vm, peer->rewrite, (u8 *) &packet, sizeof (packet),
     197             :                          &bi0, is_ip4))
     198           0 :     return false;
     199             : 
     200         150 :   ip46_enqueue_packet (vm, bi0, is_ip4);
     201         150 :   return true;
     202             : }
     203             : 
     204             : typedef struct
     205             : {
     206             :   u32 peer_idx;
     207             :   bool is_retry;
     208             : } wg_send_args_t;
     209             : 
     210             : static void *
     211          18 : wg_send_handshake_thread_fn (void *arg)
     212             : {
     213          18 :   wg_send_args_t *a = arg;
     214             : 
     215          18 :   wg_main_t *wmp = &wg_main;
     216          18 :   wg_peer_t *peer = wg_peer_get (a->peer_idx);
     217             :   bool handshake;
     218             : 
     219          18 :   wg_send_handshake (wmp->vlib_main, peer, a->is_retry);
     220          18 :   handshake = false;
     221          18 :   __atomic_store_n (&peer->handshake_is_sent, handshake, __ATOMIC_RELEASE);
     222          18 :   return 0;
     223             : }
     224             : 
     225             : void
     226          18 : wg_send_handshake_from_mt (u32 peer_idx, bool is_retry)
     227             : {
     228          18 :   wg_send_args_t a = {
     229             :     .peer_idx = peer_idx,
     230             :     .is_retry = is_retry,
     231             :   };
     232             : 
     233          18 :   wg_peer_t *peer = wg_peer_get (peer_idx);
     234             : 
     235          18 :   bool handshake =
     236          18 :     __atomic_load_n (&peer->handshake_is_sent, __ATOMIC_ACQUIRE);
     237             : 
     238          18 :   if (handshake == false)
     239             :     {
     240          18 :       handshake = true;
     241          18 :       __atomic_store_n (&peer->handshake_is_sent, handshake, __ATOMIC_RELEASE);
     242          18 :       vl_api_rpc_call_main_thread (wg_send_handshake_thread_fn, (u8 *) &a,
     243             :                                    sizeof (a));
     244             :     }
     245          18 : }
     246             : 
     247             : bool
     248          35 : wg_send_keepalive (vlib_main_t * vm, wg_peer_t * peer)
     249             : {
     250          35 :   ASSERT (vm->thread_index == 0);
     251             : 
     252          35 :   if (!wg_peer_can_send (peer))
     253           0 :     return false;
     254             : 
     255          35 :   u32 size_of_packet = message_data_len (0);
     256          35 :   message_data_t *packet =
     257          35 :     (message_data_t *) wg_main.per_thread_data[vm->thread_index].data;
     258          35 :   u32 bi0 = 0;
     259          35 :   bool ret = true;
     260             :   enum noise_state_crypt state;
     261             : 
     262          35 :   if (!peer->remote.r_current)
     263             :     {
     264           0 :       wg_send_handshake (vm, peer, false);
     265           0 :       goto out;
     266             :     }
     267             : 
     268             :   state =
     269          35 :     noise_remote_encrypt (vm,
     270             :                           &peer->remote,
     271          35 :                           &packet->receiver_index,
     272          35 :                           &packet->counter, NULL, 0, packet->encrypted_data);
     273             : 
     274          35 :   if (PREDICT_FALSE (state == SC_KEEP_KEY_FRESH))
     275             :     {
     276           0 :       wg_send_handshake (vm, peer, false);
     277             :     }
     278          35 :   else if (PREDICT_FALSE (state == SC_FAILED))
     279             :     {
     280           0 :       wg_peer_update_flags (peer - wg_peer_pool, WG_PEER_ESTABLISHED, false);
     281           0 :       ret = false;
     282           0 :       goto out;
     283             :     }
     284             : 
     285          35 :   u8 is_ip4 = ip46_address_is_ip4 (&peer->dst.addr);
     286          35 :   packet->header.type = MESSAGE_DATA;
     287             : 
     288          35 :   if (!wg_create_buffer (vm, peer->rewrite, (u8 *) packet, size_of_packet,
     289             :                          &bi0, is_ip4))
     290             :     {
     291           0 :       ret = false;
     292           0 :       goto out;
     293             :     }
     294             : 
     295          35 :   ip46_enqueue_packet (vm, bi0, is_ip4);
     296             : 
     297          35 :   wg_timers_any_authenticated_packet_sent (peer);
     298          35 :   wg_timers_any_authenticated_packet_traversal (peer);
     299             : 
     300          35 : out:
     301          35 :   return ret;
     302             : }
     303             : 
     304             : bool
     305          59 : wg_send_handshake_response (vlib_main_t * vm, wg_peer_t * peer)
     306             : {
     307             :   message_handshake_response_t packet;
     308             : 
     309          59 :   if (!wg_peer_can_send (peer))
     310           0 :     return false;
     311             : 
     312          59 :   if (noise_create_response (vm,
     313             :                              &peer->remote,
     314             :                              &packet.sender_index,
     315             :                              &packet.receiver_index,
     316             :                              packet.unencrypted_ephemeral,
     317             :                              packet.encrypted_nothing))
     318             :     {
     319          59 :       packet.header.type = MESSAGE_HANDSHAKE_RESPONSE;
     320          59 :       cookie_maker_mac (&peer->cookie_maker, &packet.macs, &packet,
     321             :                         sizeof (packet));
     322             : 
     323          59 :       if (noise_remote_begin_session (vm, &peer->remote))
     324             :         {
     325          59 :           wg_timers_session_derived (peer);
     326          59 :           wg_timers_any_authenticated_packet_sent (peer);
     327          59 :           wg_timers_any_authenticated_packet_traversal (peer);
     328             : 
     329          59 :           u32 bi0 = 0;
     330          59 :           u8 is_ip4 = ip46_address_is_ip4 (&peer->dst.addr);
     331          59 :           if (!wg_create_buffer (vm, peer->rewrite, (u8 *) &packet,
     332             :                                  sizeof (packet), &bi0, is_ip4))
     333           0 :             return false;
     334             : 
     335          59 :           ip46_enqueue_packet (vm, bi0, is_ip4);
     336          59 :           return true;
     337             :         }
     338           0 :       return false;
     339             :     }
     340           0 :   return false;
     341             : }
     342             : 
     343             : bool
     344          28 : wg_send_handshake_cookie (vlib_main_t *vm, u32 sender_index,
     345             :                           cookie_checker_t *cookie_checker,
     346             :                           message_macs_t *macs, ip46_address_t *wg_if_addr,
     347             :                           u16 wg_if_port, ip46_address_t *remote_addr,
     348             :                           u16 remote_port)
     349             : {
     350             :   message_handshake_cookie_t packet;
     351             :   u8 *rewrite;
     352             : 
     353          28 :   packet.header.type = MESSAGE_HANDSHAKE_COOKIE;
     354          28 :   packet.receiver_index = sender_index;
     355             : 
     356          28 :   cookie_checker_create_payload (vm, cookie_checker, macs, packet.nonce,
     357             :                                  packet.encrypted_cookie, remote_addr,
     358             :                                  remote_port);
     359             : 
     360          28 :   u32 bi0 = 0;
     361          28 :   u8 is_ip4 = ip46_address_is_ip4 (remote_addr);
     362             :   bool ret;
     363          28 :   rewrite = wg_build_rewrite (wg_if_addr, wg_if_port, remote_addr, remote_port,
     364             :                               is_ip4);
     365             : 
     366          28 :   ret = wg_create_buffer (vm, rewrite, (u8 *) &packet, sizeof (packet), &bi0,
     367             :                           is_ip4);
     368          28 :   vec_free (rewrite);
     369          28 :   if (!ret)
     370           0 :     return false;
     371             : 
     372          28 :   ip46_enqueue_packet (vm, bi0, is_ip4);
     373             : 
     374          28 :   return true;
     375             : }
     376             : 
     377             : /*
     378             :  * fd.io coding-style-patch-verification: ON
     379             :  *
     380             :  * Local Variables:
     381             :  * eval: (c-set-style "gnu")
     382             :  * End:
     383             :  */

Generated by: LCOV version 1.14