OpenVPN
dco_win.c
Go to the documentation of this file.
1 /*
2  * Interface to ovpn-win-dco networking code
3  *
4  * Copyright (C) 2020-2022 Arne Schwabe <arne@rfc2549.org>
5  * Copyright (C) 2020-2022 OpenVPN Inc <sales@openvpn.net>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2
9  * as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program (see the file COPYING included with this
18  * distribution); if not, write to the Free Software Foundation, Inc.,
19  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #elif defined(_MSC_VER)
25 #include "config-msvc.h"
26 #endif
27 
28 #if defined(_WIN32)
29 
30 #include "syshead.h"
31 
32 #include "dco.h"
33 #include "tun.h"
34 #include "crypto.h"
35 #include "ssl_common.h"
36 
37 #include <bcrypt.h>
38 #include <winsock2.h>
39 #include <ws2tcpip.h>
40 
41 #if defined(__MINGW32__)
42 const IN_ADDR in4addr_any = { 0 };
43 #endif
44 
45 struct tuntap
46 create_dco_handle(const char *devname, struct gc_arena *gc)
47 {
48  struct tuntap tt = { .windows_driver = WINDOWS_DRIVER_DCO };
49  const char *device_guid;
50 
51  tun_open_device(&tt, devname, &device_guid, gc);
52 
53  return tt;
54 }
55 
56 bool
58 {
59  return true;
60 }
61 
62 int
63 open_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev)
64 {
65  ASSERT(0);
66  return 0;
67 }
68 
69 static void
70 dco_wait_ready(DWORD idx)
71 {
72  for (int i = 0; i < 20; ++i)
73  {
74  MIB_IPINTERFACE_ROW row = {.InterfaceIndex = idx, .Family = AF_INET};
75  if (GetIpInterfaceEntry(&row) != ERROR_NOT_FOUND)
76  {
77  break;
78  }
79  msg(D_DCO_DEBUG, "interface %ld not yet ready, retrying", idx);
80  Sleep(50);
81  }
82 }
83 
84 void
85 dco_start_tun(struct tuntap *tt)
86 {
87  msg(D_DCO_DEBUG, "%s", __func__);
88 
89  /* reference the tt object inside the DCO context, because the latter will
90  * be passed around
91  */
92  tt->dco.tt = tt;
93 
94  DWORD bytes_returned = 0;
95  if (!DeviceIoControl(tt->hand, OVPN_IOCTL_START_VPN, NULL, 0, NULL, 0,
96  &bytes_returned, NULL))
97  {
98  msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_START_VPN) failed");
99  }
100 
101  /* Sometimes IP Helper API, which we use for setting IP address etc,
102  * complains that interface is not found. Give it some time to settle
103  */
105 }
106 
107 static void
108 dco_connect_wait(HANDLE handle, OVERLAPPED *ov, int timeout, volatile int *signal_received)
109 {
110  /* GetOverlappedResultEx is available starting from Windows 8 */
111  typedef BOOL (*get_overlapped_result_ex_t) (HANDLE, LPOVERLAPPED, LPDWORD, DWORD, BOOL);
112  get_overlapped_result_ex_t get_overlapped_result_ex =
113  (get_overlapped_result_ex_t)GetProcAddress(GetModuleHandle("Kernel32.dll"),
114  "GetOverlappedResultEx");
115 
116  if (get_overlapped_result_ex == NULL)
117  {
118  msg(M_ERR, "Failed to load GetOverlappedResult()");
119  }
120 
121  DWORD timeout_msec = timeout * 1000;
122  const int poll_interval_ms = 50;
123 
124  while (timeout_msec > 0)
125  {
126  timeout_msec -= poll_interval_ms;
127 
128  DWORD transferred;
129  if (get_overlapped_result_ex(handle, ov, &transferred, poll_interval_ms, FALSE) != 0)
130  {
131  /* TCP connection established by dco */
132  return;
133  }
134 
135  DWORD err = GetLastError();
136  if ((err != WAIT_TIMEOUT) && (err != ERROR_IO_INCOMPLETE))
137  {
138  /* dco reported connection error */
139  msg(M_NONFATAL | M_ERRNO, "dco connect error");
140  *signal_received = SIGUSR1;
141  return;
142  }
143 
144  get_signal(signal_received);
145  if (*signal_received)
146  {
147  return;
148  }
149 
150  management_sleep(0);
151  }
152 
153  /* we end up here when timeout occurs in userspace */
154  msg(M_NONFATAL, "dco connect timeout");
155  *signal_received = SIGUSR1;
156 }
157 
158 void
159 dco_create_socket(HANDLE handle, struct addrinfo *remoteaddr, bool bind_local,
160  struct addrinfo *bind, int timeout,
161  volatile int *signal_received)
162 {
163  msg(D_DCO_DEBUG, "%s", __func__);
164 
165  OVPN_NEW_PEER peer = { 0 };
166 
167  struct sockaddr *local = NULL;
168  struct sockaddr *remote = remoteaddr->ai_addr;
169 
170  if (remoteaddr->ai_protocol == IPPROTO_TCP
171  || remoteaddr->ai_socktype == SOCK_STREAM)
172  {
173  peer.Proto = OVPN_PROTO_TCP;
174  }
175  else
176  {
177  peer.Proto = OVPN_PROTO_UDP;
178  }
179 
180  if (bind_local)
181  {
182  /* Use first local address with correct address family */
183  while (bind && !local)
184  {
185  if (bind->ai_family == remote->sa_family)
186  {
187  local = bind->ai_addr;
188  }
189  bind = bind->ai_next;
190  }
191  }
192 
193  if (bind_local && !local)
194  {
195  msg(M_FATAL, "DCO: Socket bind failed: Address to bind lacks %s record",
196  addr_family_name(remote->sa_family));
197  }
198 
199  if (remote->sa_family == AF_INET6)
200  {
201  peer.Remote.Addr6 = *((SOCKADDR_IN6 *)(remoteaddr->ai_addr));
202  if (local)
203  {
204  peer.Local.Addr6 = *((SOCKADDR_IN6 *)local);
205  }
206  else
207  {
208  peer.Local.Addr6.sin6_addr = in6addr_any;
209  peer.Local.Addr6.sin6_port = 0;
210  peer.Local.Addr6.sin6_family = AF_INET6;
211  }
212  }
213  else if (remote->sa_family == AF_INET)
214  {
215  peer.Remote.Addr4 = *((SOCKADDR_IN *)(remoteaddr->ai_addr));
216  if (local)
217  {
218  peer.Local.Addr4 = *((SOCKADDR_IN *)local);
219  }
220  else
221  {
222  peer.Local.Addr4.sin_addr = in4addr_any;
223  peer.Local.Addr4.sin_port = 0;
224  peer.Local.Addr4.sin_family = AF_INET;
225  }
226  }
227  else
228  {
229  ASSERT(0);
230  }
231 
232  OVERLAPPED ov = { 0 };
233  if (!DeviceIoControl(handle, OVPN_IOCTL_NEW_PEER, &peer, sizeof(peer), NULL, 0, NULL, &ov))
234  {
235  DWORD err = GetLastError();
236  if (err != ERROR_IO_PENDING)
237  {
238  msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_NEW_PEER) failed");
239  }
240  else
241  {
242  dco_connect_wait(handle, &ov, timeout, signal_received);
243  }
244  }
245 }
246 
247 int
248 dco_new_peer(dco_context_t *dco, unsigned int peerid, int sd,
249  struct sockaddr *localaddr, struct sockaddr *remoteaddr,
250  struct in_addr *remote_in4, struct in6_addr *remote_in6)
251 {
252  msg(D_DCO_DEBUG, "%s: peer-id %d, fd %d", __func__, peerid, sd);
253  return 0;
254 }
255 
256 int
257 dco_del_peer(dco_context_t *dco, unsigned int peerid)
258 {
259  msg(D_DCO_DEBUG, "%s: peer-id %d", __func__, peerid);
260 
261  DWORD bytes_returned = 0;
262  if (!DeviceIoControl(dco->tt->hand, OVPN_IOCTL_DEL_PEER, NULL,
263  0, NULL, 0, &bytes_returned, NULL))
264  {
265  msg(M_WARN | M_ERRNO, "DeviceIoControl(OVPN_IOCTL_DEL_PEER) failed");
266  return -1;
267  }
268  return 0;
269 }
270 
271 int
272 dco_set_peer(dco_context_t *dco, unsigned int peerid,
273  int keepalive_interval, int keepalive_timeout, int mss)
274 {
275  msg(D_DCO_DEBUG, "%s: peer-id %d, keepalive %d/%d, mss %d", __func__,
276  peerid, keepalive_interval, keepalive_timeout, mss);
277 
278  OVPN_SET_PEER peer;
279 
280  peer.KeepaliveInterval = keepalive_interval;
281  peer.KeepaliveTimeout = keepalive_timeout;
282  peer.MSS = mss;
283 
284  DWORD bytes_returned = 0;
285  if (!DeviceIoControl(dco->tt->hand, OVPN_IOCTL_SET_PEER, &peer,
286  sizeof(peer), NULL, 0, &bytes_returned, NULL))
287  {
288  msg(M_WARN | M_ERRNO, "DeviceIoControl(OVPN_IOCTL_SET_PEER) failed");
289  return -1;
290  }
291  return 0;
292 }
293 
294 int
295 dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid,
296  dco_key_slot_t slot,
297  const uint8_t *encrypt_key, const uint8_t *encrypt_iv,
298  const uint8_t *decrypt_key, const uint8_t *decrypt_iv,
299  const char *ciphername)
300 {
301  msg(D_DCO_DEBUG, "%s: slot %d, key-id %d, peer-id %d, cipher %s",
302  __func__, slot, keyid, peerid, ciphername);
303 
304  const int nonce_len = 8;
305  size_t key_len = cipher_kt_key_size(ciphername);
306 
307  OVPN_CRYPTO_DATA crypto_data;
308  ZeroMemory(&crypto_data, sizeof(crypto_data));
309 
310  crypto_data.CipherAlg = dco_get_cipher(ciphername);
311  crypto_data.KeyId = keyid;
312  crypto_data.PeerId = peerid;
313  crypto_data.KeySlot = slot;
314 
315  CopyMemory(crypto_data.Encrypt.Key, encrypt_key, key_len);
316  crypto_data.Encrypt.KeyLen = (char)key_len;
317  CopyMemory(crypto_data.Encrypt.NonceTail, encrypt_iv, nonce_len);
318 
319  CopyMemory(crypto_data.Decrypt.Key, decrypt_key, key_len);
320  crypto_data.Decrypt.KeyLen = (char)key_len;
321  CopyMemory(crypto_data.Decrypt.NonceTail, decrypt_iv, nonce_len);
322 
323  ASSERT(crypto_data.CipherAlg > 0);
324 
325  DWORD bytes_returned = 0;
326 
327  if (!DeviceIoControl(dco->tt->hand, OVPN_IOCTL_NEW_KEY, &crypto_data,
328  sizeof(crypto_data), NULL, 0, &bytes_returned, NULL))
329  {
330  msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_NEW_KEY) failed");
331  return -1;
332  }
333  return 0;
334 }
335 int
336 dco_del_key(dco_context_t *dco, unsigned int peerid, dco_key_slot_t slot)
337 {
338  msg(D_DCO, "%s: peer-id %d, slot %d called but ignored", __func__, peerid,
339  slot);
340  /* FIXME: Implement in driver first */
341  return 0;
342 }
343 
344 int
345 dco_swap_keys(dco_context_t *dco, unsigned int peer_id)
346 {
347  msg(D_DCO_DEBUG, "%s: peer-id %d", __func__, peer_id);
348 
349  DWORD bytes_returned = 0;
350  if (!DeviceIoControl(dco->tt->hand, OVPN_IOCTL_SWAP_KEYS, NULL, 0, NULL, 0,
351  &bytes_returned, NULL))
352  {
353  msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_SWAP_KEYS) failed");
354  return -1;
355  }
356  return 0;
357 }
358 
359 bool
360 dco_available(int msglevel)
361 {
362  /* try to open device by symbolic name */
363  HANDLE h = CreateFile("\\\\.\\ovpn-dco", GENERIC_READ | GENERIC_WRITE,
364  0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, NULL);
365 
366  if (h != INVALID_HANDLE_VALUE)
367  {
368  CloseHandle(h);
369  return true;
370  }
371 
372  DWORD err = GetLastError();
373  if (err == ERROR_ACCESS_DENIED)
374  {
375  /* this likely means that device exists but is already in use,
376  * don't bail out since later we try to open all existing dco
377  * devices and then bail out if all devices are in use
378  */
379  return true;
380  }
381 
382  msg(msglevel, "Note: ovpn-dco-win driver is missing, disabling data channel offload.");
383  return false;
384 }
385 
386 int
388 {
389  /* no-op on windows */
390  ASSERT(0);
391  return 0;
392 }
393 
394 int
395 dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf)
396 {
397  /* no-op on windows */
398  ASSERT(0);
399  return 0;
400 }
401 
402 void
403 dco_event_set(dco_context_t *dco, struct event_set *es, void *arg)
404 {
405  /* no-op on windows */
406  ASSERT(0);
407 }
408 
409 const char *
411 {
412  /*
413  * this API can be called either from user mode or kernel mode,
414  * which enables us to probe driver's chachapoly support
415  * (available starting from Windows 11)
416  */
417 
418  BCRYPT_ALG_HANDLE h;
419  NTSTATUS status = BCryptOpenAlgorithmProvider(&h, L"CHACHA20_POLY1305", NULL, 0);
420  if (BCRYPT_SUCCESS(status))
421  {
422  BCryptCloseAlgorithmProvider(h, 0);
423  return "AES-128-GCM:AES-256-GCM:AES-192-GCM:CHACHA20-POLY1305";
424  }
425  else
426  {
427  return "AES-128-GCM:AES-256-GCM:AES-192-GCM";
428  }
429 }
430 
431 #endif /* defined(_WIN32) */
_OVPN_NEW_PEER::Proto
OVPN_PROTO Proto
Definition: ovpn_dco_win.h:47
D_DCO_DEBUG
#define D_DCO_DEBUG
Definition: errlevel.h:118
dco_new_key
int dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid, dco_key_slot_t slot, const uint8_t *encrypt_key, const uint8_t *encrypt_iv, const uint8_t *decrypt_key, const uint8_t *decrypt_iv, const char *ciphername)
Definition: dco_win.c:295
M_ERRNO
#define M_ERRNO
Definition: error.h:100
dco_get_supported_ciphers
const char * dco_get_supported_ciphers()
Definition: dco_win.c:410
M_FATAL
#define M_FATAL
Definition: error.h:95
tuntap::windows_driver
enum windows_driver_type windows_driver
Definition: tun.h:209
M_NONFATAL
#define M_NONFATAL
Definition: error.h:96
management_sleep
void management_sleep(const int n)
A sleep function that services the management layer for n seconds rather than doing nothing.
Definition: manage.c:4024
_OVPN_SET_PEER::MSS
LONG MSS
Definition: ovpn_dco_win.h:100
config-msvc.h
create_dco_handle
struct tuntap create_dco_handle(const char *devname, struct gc_arena *gc)
Definition: dco_win.c:46
open_tun_dco
int open_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev)
Definition: dco_win.c:63
bind_local
static void bind_local(struct link_socket *sock, const sa_family_t ai_family)
Definition: socket.c:1086
dco_start_tun
void dco_start_tun(struct tuntap *tt)
Definition: dco_win.c:85
dco_create_socket
void dco_create_socket(HANDLE handle, struct addrinfo *remoteaddr, bool bind_local, struct addrinfo *bind, int timeout, volatile int *signal_received)
Definition: dco_win.c:159
addr_family_name
const char * addr_family_name(int af)
Definition: socket.c:3160
openvpn_net_ctx_t
void * openvpn_net_ctx_t
Definition: networking.h:28
_OVPN_NEW_PEER::Remote
union _OVPN_NEW_PEER::@8 Remote
dco_new_peer
int dco_new_peer(dco_context_t *dco, unsigned int peerid, int sd, struct sockaddr *localaddr, struct sockaddr *remoteaddr, struct in_addr *remote_in4, struct in6_addr *remote_in6)
Definition: dco_win.c:248
dco_do_read
int dco_do_read(dco_context_t *dco)
Definition: dco_win.c:387
_OVPN_SET_PEER
Definition: ovpn_dco_win.h:97
OVPN_PROTO_UDP
@ OVPN_PROTO_UDP
Definition: ovpn_dco_win.h:32
OVPN_IOCTL_DEL_PEER
#define OVPN_IOCTL_DEL_PEER
Definition: ovpn_dco_win.h:109
OVPN_IOCTL_START_VPN
#define OVPN_IOCTL_START_VPN
Definition: ovpn_dco_win.h:108
tuntap::hand
HANDLE hand
Definition: tun.h:193
OVPN_IOCTL_NEW_PEER
#define OVPN_IOCTL_NEW_PEER
Definition: ovpn_dco_win.h:103
ASSERT
#define ASSERT(x)
Definition: error.h:201
D_DCO
#define D_DCO
Definition: errlevel.h:94
tun.h
WINDOWS_DRIVER_DCO
@ WINDOWS_DRIVER_DCO
Definition: tun.h:53
_OVPN_CRYPTO_DATA::PeerId
int PeerId
Definition: ovpn_dco_win.h:94
_OVPN_SET_PEER::KeepaliveTimeout
LONG KeepaliveTimeout
Definition: ovpn_dco_win.h:99
_OVPN_CRYPTO_DATA::KeyId
unsigned char KeyId
Definition: ovpn_dco_win.h:93
ovpn_dco_init
bool ovpn_dco_init(int mode, dco_context_t *dco)
Definition: dco_win.c:57
M_WARN
#define M_WARN
Definition: error.h:97
tuntap::adapter_index
DWORD adapter_index
Definition: tun.h:207
OVPN_IOCTL_NEW_KEY
#define OVPN_IOCTL_NEW_KEY
Definition: ovpn_dco_win.h:105
dco_event_set
void dco_event_set(dco_context_t *dco, struct event_set *es, void *arg)
Definition: dco_win.c:403
crypto.h
dco_connect_wait
static void dco_connect_wait(HANDLE handle, OVERLAPPED *ov, int timeout, volatile int *signal_received)
Definition: dco_win.c:108
M_ERR
#define M_ERR
Definition: error.h:111
_OVPN_NEW_PEER
Definition: ovpn_dco_win.h:36
dco_context_t
void * dco_context_t
Definition: dco.h:237
dco_available
bool dco_available(int msglevel)
Definition: dco_win.c:360
buffer
Wrapper structure for dynamically allocated memory.
Definition: buffer.h:60
dco_set_peer
int dco_set_peer(dco_context_t *dco, unsigned int peerid, int keepalive_interval, int keepalive_timeout, int mss)
Definition: dco_win.c:272
_OVPN_NEW_PEER::Addr4
SOCKADDR_IN Addr4
Definition: ovpn_dco_win.h:38
_OVPN_CRYPTO_DATA::KeySlot
OVPN_KEY_SLOT KeySlot
Definition: ovpn_dco_win.h:91
_OVPN_NEW_PEER::Local
union _OVPN_NEW_PEER::@7 Local
syshead.h
_OVPN_CRYPTO_DATA
Definition: ovpn_dco_win.h:88
_OVPN_SET_PEER::KeepaliveInterval
LONG KeepaliveInterval
Definition: ovpn_dco_win.h:98
gc_arena
Garbage collection arena used to keep track of dynamically allocated memory.
Definition: buffer.h:116
SIGUSR1
#define SIGUSR1
Definition: config-msvc.h:80
dco_del_peer
int dco_del_peer(dco_context_t *dco, unsigned int peerid)
Definition: dco_win.c:257
_OVPN_CRYPTO_DATA::Decrypt
OVPN_KEY_DIRECTION Decrypt
Definition: ovpn_dco_win.h:90
_OVPN_CRYPTO_DATA::Encrypt
OVPN_KEY_DIRECTION Encrypt
Definition: ovpn_dco_win.h:89
_OVPN_KEY_DIRECTION::NonceTail
unsigned char NonceTail[8]
Definition: ovpn_dco_win.h:85
dco_do_write
int dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf)
Definition: dco_win.c:395
cipher_kt_key_size
int cipher_kt_key_size(const char *ciphername)
Returns the size of keys used by the cipher, in bytes.
Definition: crypto_openssl.c:643
dco.h
event_set
Definition: event.h:124
dco_del_key
int dco_del_key(dco_context_t *dco, unsigned int peerid, dco_key_slot_t slot)
Definition: dco_win.c:336
dco_swap_keys
int dco_swap_keys(dco_context_t *dco, unsigned int peer_id)
Definition: dco_win.c:345
OVPN_PROTO_TCP
@ OVPN_PROTO_TCP
Definition: ovpn_dco_win.h:33
tuntap::dco
dco_context_t dco
Definition: tun.h:230
status
static SERVICE_STATUS status
Definition: interactive.c:56
tuntap
Definition: tun.h:167
_OVPN_KEY_DIRECTION::KeyLen
unsigned char KeyLen
Definition: ovpn_dco_win.h:84
_OVPN_KEY_DIRECTION::Key
unsigned char Key[32]
Definition: ovpn_dco_win.h:83
dco_wait_ready
static void dco_wait_ready(DWORD idx)
Definition: dco_win.c:70
config.h
ssl_common.h
get_signal
static void get_signal(volatile int *sig)
Definition: sig.h:89
_OVPN_NEW_PEER::Addr6
SOCKADDR_IN6 Addr6
Definition: ovpn_dco_win.h:39
OVPN_IOCTL_SWAP_KEYS
#define OVPN_IOCTL_SWAP_KEYS
Definition: ovpn_dco_win.h:106
_OVPN_CRYPTO_DATA::CipherAlg
OVPN_CIPHER_ALG CipherAlg
Definition: ovpn_dco_win.h:92
tun_open_device
void tun_open_device(struct tuntap *tt, const char *dev_node, const char **device_guid, struct gc_arena *gc)
Definition: tun.c:6551
msg
#define msg(flags,...)
Definition: error.h:150
OVPN_IOCTL_SET_PEER
#define OVPN_IOCTL_SET_PEER
Definition: ovpn_dco_win.h:107