wireguard: initial implementation of wireguard protocol
[vpp.git] / src / plugins / wireguard / wireguard_send.c
1 /*
2  * Copyright (c) 2020 Doc.ai and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/vnet.h>
17 #include <vnet/fib/ip6_fib.h>
18 #include <vnet/fib/ip4_fib.h>
19 #include <vnet/fib/fib_entry.h>
20 #include <vnet/ip/ip6_link.h>
21 #include <vnet/pg/pg.h>
22 #include <vnet/udp/udp.h>
23 #include <vppinfra/error.h>
24 #include <wireguard/wireguard.h>
25 #include <wireguard/wireguard_send.h>
26
27 static int
28 ip46_enqueue_packet (vlib_main_t * vm, u32 bi0, int is_ip6)
29 {
30   vlib_frame_t *f = 0;
31   u32 lookup_node_index =
32     is_ip6 ? ip6_lookup_node.index : ip4_lookup_node.index;
33
34   f = vlib_get_frame_to_node (vm, lookup_node_index);
35   /* f can not be NULL here - frame allocation failure causes panic */
36
37   u32 *to_next = vlib_frame_vector_args (f);
38   f->n_vectors = 1;
39   to_next[0] = bi0;
40
41   vlib_put_frame_to_node (vm, lookup_node_index, f);
42
43   return f->n_vectors;
44 }
45
46 static void
47 wg_buffer_prepend_rewrite (vlib_buffer_t * b0, const wg_peer_t * peer)
48 {
49   ip4_udp_header_t *hdr;
50
51   vlib_buffer_advance (b0, -sizeof (*hdr));
52
53   hdr = vlib_buffer_get_current (b0);
54   clib_memcpy (hdr, peer->rewrite, vec_len (peer->rewrite));
55
56   hdr->udp.length =
57     clib_host_to_net_u16 (b0->current_length - sizeof (ip4_header_t));
58   ip4_header_set_len_w_chksum (&hdr->ip4,
59                                clib_host_to_net_u16 (b0->current_length));
60 }
61
62 static bool
63 wg_create_buffer (vlib_main_t * vm,
64                   const wg_peer_t * peer,
65                   const u8 * packet, u32 packet_len, u32 * bi)
66 {
67   u32 n_buf0 = 0;
68   vlib_buffer_t *b0;
69
70   n_buf0 = vlib_buffer_alloc (vm, bi, 1);
71   if (!n_buf0)
72     return false;
73
74   b0 = vlib_get_buffer (vm, *bi);
75
76   u8 *payload = vlib_buffer_get_current (b0);
77   clib_memcpy (payload, packet, packet_len);
78
79   b0->current_length = packet_len;
80
81   wg_buffer_prepend_rewrite (b0, peer);
82
83   return true;
84 }
85
86 bool
87 wg_send_handshake (vlib_main_t * vm, wg_peer_t * peer, bool is_retry)
88 {
89   wg_main_t *wmp = &wg_main;
90   message_handshake_initiation_t packet;
91
92   if (!is_retry)
93     peer->timer_handshake_attempts = 0;
94
95   if (!wg_birthdate_has_expired (peer->last_sent_handshake,
96                                  REKEY_TIMEOUT) || peer->is_dead)
97     {
98       return true;
99     }
100   if (noise_create_initiation (wmp->vlib_main,
101                                &peer->remote,
102                                &packet.sender_index,
103                                packet.unencrypted_ephemeral,
104                                packet.encrypted_static,
105                                packet.encrypted_timestamp))
106     {
107       f64 now = vlib_time_now (vm);
108       packet.header.type = MESSAGE_HANDSHAKE_INITIATION;
109       cookie_maker_mac (&peer->cookie_maker, &packet.macs, &packet,
110                         sizeof (packet));
111       wg_timers_any_authenticated_packet_traversal (peer);
112       wg_timers_any_authenticated_packet_sent (peer);
113       peer->last_sent_handshake = now;
114       wg_timers_handshake_initiated (peer);
115     }
116   else
117     return false;
118   u32 bi0 = 0;
119   if (!wg_create_buffer (vm, peer, (u8 *) & packet, sizeof (packet), &bi0))
120     return false;
121   ip46_enqueue_packet (vm, bi0, false);
122
123   return true;
124 }
125
126 bool
127 wg_send_keepalive (vlib_main_t * vm, wg_peer_t * peer)
128 {
129   wg_main_t *wmp = &wg_main;
130   u32 size_of_packet = message_data_len (0);
131   message_data_t *packet = clib_mem_alloc (size_of_packet);
132   u32 bi0 = 0;
133   bool ret = true;
134   enum noise_state_crypt state;
135
136   if (!peer->remote.r_current)
137     {
138       wg_send_handshake (vm, peer, false);
139       goto out;
140     }
141
142   state =
143     noise_remote_encrypt (wmp->vlib_main,
144                           &peer->remote,
145                           &packet->receiver_index,
146                           &packet->counter, NULL, 0, packet->encrypted_data);
147   switch (state)
148     {
149     case SC_OK:
150       break;
151     case SC_KEEP_KEY_FRESH:
152       wg_send_handshake (vm, peer, false);
153       break;
154     case SC_FAILED:
155       ret = false;
156       goto out;
157     default:
158       break;
159     }
160   packet->header.type = MESSAGE_DATA;
161
162   if (!wg_create_buffer (vm, peer, (u8 *) packet, size_of_packet, &bi0))
163     {
164       ret = false;
165       goto out;
166     }
167
168   ip46_enqueue_packet (vm, bi0, false);
169   wg_timers_any_authenticated_packet_traversal (peer);
170   wg_timers_any_authenticated_packet_sent (peer);
171
172 out:
173   clib_mem_free (packet);
174   return ret;
175 }
176
177 bool
178 wg_send_handshake_response (vlib_main_t * vm, wg_peer_t * peer)
179 {
180   wg_main_t *wmp = &wg_main;
181   message_handshake_response_t packet;
182
183   peer->last_sent_handshake = vlib_time_now (vm);
184
185   if (noise_create_response (vm,
186                              &peer->remote,
187                              &packet.sender_index,
188                              &packet.receiver_index,
189                              packet.unencrypted_ephemeral,
190                              packet.encrypted_nothing))
191     {
192       f64 now = vlib_time_now (vm);
193       packet.header.type = MESSAGE_HANDSHAKE_RESPONSE;
194       cookie_maker_mac (&peer->cookie_maker, &packet.macs, &packet,
195                         sizeof (packet));
196
197       if (noise_remote_begin_session (wmp->vlib_main, &peer->remote))
198         {
199           wg_timers_session_derived (peer);
200           wg_timers_any_authenticated_packet_traversal (peer);
201           wg_timers_any_authenticated_packet_sent (peer);
202           peer->last_sent_handshake = now;
203
204           u32 bi0 = 0;
205           if (!wg_create_buffer (vm, peer, (u8 *) & packet,
206                                  sizeof (packet), &bi0))
207             return false;
208
209           ip46_enqueue_packet (vm, bi0, false);
210         }
211       else
212         return false;
213     }
214   else
215     return false;
216   return true;
217 }
218
219 /*
220  * fd.io coding-style-patch-verification: ON
221  *
222  * Local Variables:
223  * eval: (c-set-style "gnu")
224  * End:
225  */