OpenVPN 3 Core Library
Loading...
Searching...
No Matches
ovpndcowincli.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12#pragma once
13
14class OvpnDcoWinClient : public Client,
15 public KoRekey::Receiver,
16 public SessionStats::DCOTransportSource
17{
18 friend class ClientConfig;
19 typedef RCPtr<OvpnDcoWinClient> Ptr;
20
21 public:
22 static bool available()
23 {
24 std::string path;
25 TunWin::Util::TapNameGuidPair tap;
26 TunWin::Type tun_type(TunWin::OvpnDco);
27 TunWin::Util::TapNameGuidPairList guids(tun_type);
28 Win::ScopedHANDLE hnd(TunWin::Util::tap_open(tun_type, guids, path, tap));
29 return hnd.defined();
30 }
31
32 void transport_start() override
33 {
34 if (tun_persist)
35 return;
36
37 halt = false;
38 RemoteList &rl = *config->transport.remote_list;
39 if (rl.endpoint_available(&server_host, &server_port, &proto_))
40 {
41 // defer to let pending IO finish
42 openvpn_io::post(io_context, [self = Ptr(this)]()
43 {
45 self->start_impl_(); });
46 }
47 else
48 {
49 transport_parent->transport_pre_resolve();
50 async_resolve_name(server_host, server_port);
51 }
52 }
53
54 bool transport_send_const(const Buffer &buf) override
55 {
56 if (halt)
57 return false;
58 return send_(buf);
59 }
60
61 bool transport_send(BufferAllocated &buf) override
62 {
63 return send_(buf);
64 }
65
66 void setup_tun(const OptionList &opt, TransportClient &transcli, bool is_tun_start = true)
67 {
68 const IP::Addr server_addr = transcli.server_endpoint_addr();
69
70 // Check if persisted tun session matches properties of to-be-created session
71 if (tun_persist->use_persisted_tun(server_addr, config->tun.tun_prop, opt))
72 {
73 state = tun_persist->state().state;
74
75 OPENVPN_LOG("TunPersist: reused tun context");
76 }
77 else
78 {
79 // notify parent
80 tun_parent->tun_pre_tun_config();
81
82 OPENVPN_LOG("TunPersist: clear tun settings");
83 std::ostringstream os;
84 tun_persist->close_destructor();
85
87
88 // parse pushed options
89 TunBuilderCapture::Ptr po(new TunBuilderCapture());
90 TunProp::configure_builder(po.get(),
91 state.get(),
92 nullptr,
93 transcli.server_endpoint_addr(),
94 config->tun.tun_prop,
95 opt,
96 nullptr,
97 false);
98 OPENVPN_LOG("CAPTURED OPTIONS:" << std::endl
99 << po->to_string());
100
101 tun_setup_->establish(*po, Win::module_name(), NULL, os, NULL);
102 OPENVPN_LOG_STRING(os.str());
103
104 // persist tun settings state
105 if (tun_persist->persist_tun_state(handle_(),
106 {state, tun_setup_->get_adapter_state()},
107 is_tun_start))
108 OPENVPN_LOG("TunPersist: saving tun context:" << std::endl
109 << tun_persist->options());
110
111 state->vpn_interface_index = tun_setup_->vpn_interface_index();
112
113 handle_.release();
114
115 // enable tun_setup destructor
116 tun_persist->add_destructor(tun_setup_);
117
118 // arm fail handler which is invoked when service process exits
120 }
121 }
122
123 void apply_push_update(const OptionList &opt, TransportClient &transcli) override
124 {
125 auto adapter_state = tun_setup_->get_adapter_state();
126
127 // create new tun setup object
128 tun_setup_ = config->tun.new_setup_obj(io_context, config->tun.allow_local_dns_resolvers);
129 tun_setup_->set_adapter_state(adapter_state);
130
131 setup_tun(opt, transcli, false);
132
133 tun_parent->tun_connected();
134 }
135
136 void tun_start(const OptionList &opt,
137 TransportClient &transcli,
138 CryptoDCSettings &dc_settings) override
139 {
140 halt = false;
141
142 setup_tun(opt, transcli);
143
145
146 // Add a hook so ProtoContext will call back to rekey() on rekey ops.
147 dc_settings.set_factory(CryptoDCFactory::Ptr(new KoRekey::Factory(
148 dc_settings.factory(), this, config->transport.frame)));
149
150 tun_parent->tun_connected();
151 }
152
153 std::string tun_name() const override
154 {
155 return "ovpn-dco-win";
156 }
157
158 std::uint32_t vpn_interface_index() const override
159 {
160 if (tun_setup_ && tun_setup_->vpn_interface_index() != INVALID_ADAPTER_INDEX)
161 return tun_setup_->vpn_interface_index();
162
163 if (tun_persist && tun_persist->state().state)
164 return tun_persist->state().state->vpn_interface_index;
165
166 return INVALID_ADAPTER_INDEX;
167 }
168
170 {
171 tun_setup_->set_service_fail_handler([self = Ptr(this)]()
172 {
173 if (!self->halt)
174 self->tun_parent->tun_error(Error::TUN_IFACE_DISABLED, "service failure"); });
175 }
176
177 void adjust_mss(int mss) override
178 {
179 OVPN_SET_PEER peer{-1, -1, mss};
180 dco_ioctl_(OVPN_IOCTL_SET_PEER, &peer, sizeof(peer));
181 }
182
183 IP::Addr server_endpoint_addr() const override
184 {
185 return IP::Addr::from_asio(endpoint_.address());
186 }
187
188 Protocol transport_protocol() const override
189 {
190 return proto_;
191 }
192
193 void rekey(const CryptoDCInstance::RekeyType rktype,
194 const KoRekey::Info &rkinfo) override
195 {
196 if (halt)
197 return;
198
199 KoRekey::OvpnDcoKey key(rktype, rkinfo);
200
201 switch (rktype)
202 {
203 case CryptoDCInstance::ACTIVATE_PRIMARY:
204 add_crypto_(rktype, key());
205 break;
206
207 case CryptoDCInstance::NEW_SECONDARY:
208 add_crypto_(rktype, key());
209 break;
210
211 case CryptoDCInstance::PRIMARY_SECONDARY_SWAP:
212 swap_keys_();
213 break;
214
215 case CryptoDCInstance::DEACTIVATE_SECONDARY:
216 break;
217
218 case CryptoDCInstance::DEACTIVATE_ALL:
219 break;
220
221 default:
222 OPENVPN_LOG("ovpndcocli: unknown rekey type: " << rktype);
223 break;
224 }
225 }
226
227 protected:
228 OvpnDcoWinClient(openvpn_io::io_context &io_context,
230 TransportClientParent *parent)
231 : Client(io_context, config, parent)
232 {
233 }
234
235 void resolve_callback(const openvpn_io::error_code &error,
236 results_type results) override
237 {
238 if (halt)
239 return;
240
241 if (error)
242 {
243 std::ostringstream os;
244 os << "DNS resolve error on '" << server_host << "' for "
245 << proto_.str() << " session: " << error.message();
246
247 stop();
248 config->transport.stats->error(Error::RESOLVE_ERROR);
249 transport_parent->transport_error(Error::UNDEF, os.str());
250 }
251 else
252 {
253 config->transport.remote_list->set_endpoint_range(results);
254 start_impl_();
255 }
256 }
257
258 TunWin::ScopedTAPStream handle_;
259
261 {
262 if (halt)
263 return;
264
265 // create new tun setup object
266 tun_setup_ = config->tun.new_setup_obj(io_context, config->tun.allow_local_dns_resolvers);
267
268 if (config->tun.tun_persist)
269 tun_persist = config->tun.tun_persist; // long-term persistent
270 else
271 tun_persist.reset(new TunWin::DcoTunPersist(false, TunWrapObjRetain::NO_RETAIN, nullptr)); // short-term
272
273 if (!tun_persist->obj_defined())
274 {
275 std::ostringstream os;
276 HANDLE th = tun_setup_->get_handle(os);
277 OPENVPN_LOG_STRING(os.str());
278 if (th == INVALID_HANDLE_VALUE)
279 return;
280
281 handle_.reset(new TunWin::TAPStream(io_context, th));
282 }
283 else
284 {
285 tun_setup_->set_adapter_state(tun_persist->state().adapter_state);
286 }
287
288 tun_setup_->confirm();
289
290 config->transport.remote_list->get_endpoint(endpoint_);
291
292 if (config->transport.socket_protect)
293 {
294 /* socket descriptor is not used on dco-win */
295 if (!config->transport.socket_protect->socket_protect(-1, server_endpoint_addr()))
296 {
297 config->transport.stats->error(Error::SOCKET_PROTECT_ERROR);
298 stop();
299 transport_parent->transport_error(Error::UNDEF, "socket_protect error (dco-win)");
300 return;
301 }
302 }
303
304 add_peer_([self = Ptr(this)]()
305 {
306 if (!self->halt) {
307 self->transport_parent->transport_connecting();
308 /* above line might set halt to true in case of TCP reconnect */
309 if (!self->halt)
310 self->queue_read_();
311 } });
312
313 config->transport.stats->dco_configure(this);
314 }
315
317 {
318 buf_.reset(0, 2048, BufAllocFlags::NO_FLAGS);
319
320 get_handle()->async_read_some(
321 buf_.mutable_buffer_clamp(),
322 [self = Ptr(this)](const openvpn_io::error_code &error,
323 const size_t bytes_recvd)
324 {
325 if (self->halt)
326 return;
327 if (!error)
328 {
329 self->buf_.set_size(bytes_recvd);
330 self->transport_parent->transport_recv(self->buf_);
331 if (!self->halt)
332 self->queue_read_();
333 }
334 else if (!self->halt)
335 {
336 self->stop_();
337 self->transport_parent->transport_error(Error::TRANSPORT_ERROR,
338 error.message());
339 }
340 });
341 }
342
343 bool send_(const Buffer &buf)
344 {
345 openvpn_io::error_code error;
346 get_handle()->write_some(buf.const_buffer(), error);
347 if (error)
348 {
349 transport_parent->transport_error(Error::TRANSPORT_ERROR,
350 error.message());
351 stop_();
352 return false;
353 }
354 return true;
355 }
356
357 void stop_() override
358 {
359 if (!halt)
360 {
361 get_stats_();
362
363 halt = true;
364 async_resolve_cancel();
365
366 try
367 {
369 }
370 catch (const ErrorCode &e)
371 {
372 // this is fine - stopped before we got driver handle
373 if (e.code() != Error::TUN_SETUP_FAILED)
374 throw e;
375 }
376
377 handle_.close();
378
379 tun_persist.reset();
380 }
381 }
382
383 template <typename CB>
384 void add_peer_(CB complete)
385 {
386 OVPN_NEW_PEER peer = {};
387
388 peer.Proto = proto_.is_tcp() ? OVPN_PROTO_TCP : OVPN_PROTO_UDP;
389
390 openvpn_io::ip::address addr = endpoint_.address();
391 if (addr.is_v4())
392 {
393 peer.Remote.Addr4.sin_family = AF_INET;
394 peer.Remote.Addr4.sin_port = ::htons(endpoint_.port());
395 std::memcpy(&peer.Remote.Addr4.sin_addr,
396 addr.to_v4().to_bytes().data(),
397 sizeof(peer.Remote.Addr4.sin_addr));
398 peer.Local.Addr4.sin_family = peer.Remote.Addr4.sin_family;
399 peer.Local.Addr4.sin_port = 0;
400 }
401 else
402 {
403 peer.Remote.Addr6.sin6_family = AF_INET6;
404 peer.Remote.Addr6.sin6_port = ::htons(endpoint_.port());
405 std::memcpy(&peer.Remote.Addr6.sin6_addr,
406 addr.to_v6().to_bytes().data(),
407 sizeof(peer.Remote.Addr6.sin6_addr));
408 peer.Local.Addr6.sin6_family = peer.Remote.Addr6.sin6_family;
409 peer.Local.Addr6.sin6_port = 0;
410 }
411
412 openvpn_io::windows::overlapped_ptr ov{io_context,
413 [self = Ptr(this), complete](const openvpn_io::error_code &ec,
414 std::size_t len)
415 {
416 if (self->halt)
417 return;
418 if (!ec)
419 complete();
420 else
421 {
422 std::ostringstream errmsg;
423 errmsg << "TCP connection error: " << ec.message();
424 self->config->transport.stats->error(Error::TCP_CONNECT_ERROR);
425 self->transport_parent->transport_error(Error::UNDEF, errmsg.str());
426 self->stop_();
427 }
428 }};
429
430 const DWORD ec = dco_ioctl_(OVPN_IOCTL_NEW_PEER, &peer, sizeof(peer), NULL, 0, &ov);
431 if (ec == ERROR_SUCCESS)
432 complete();
433 else if (ec != ERROR_IO_PENDING)
434 {
435 std::ostringstream errmsg;
436 errmsg << "failed to connect '" << server_host << "' " << endpoint_;
437 config->transport.stats->error(Error::TCP_CONNECT_ERROR);
438 transport_parent->transport_error(Error::UNDEF, errmsg.str());
439 stop_();
440 }
441 }
442
444 {
445 if (!transport_parent->is_keepalive_enabled())
446 return;
447
448 unsigned int keepalive_interval = 0;
449 unsigned int keepalive_timeout = 0;
450
451 // since userspace doesn't know anything about presense or
452 // absense of data channel traffic, ping should be handled in kernel
453 transport_parent->disable_keepalive(keepalive_interval, keepalive_timeout);
454
455 if (config->ping_restart_override)
456 keepalive_timeout = config->ping_restart_override;
457
458 // enable keepalive in kernel
459 OVPN_SET_PEER peer{static_cast<LONG>(keepalive_interval), static_cast<LONG>(keepalive_timeout), -1};
460 dco_ioctl_(OVPN_IOCTL_SET_PEER, &peer, sizeof(peer));
461 }
462
463 void add_crypto_(const CryptoDCInstance::RekeyType type,
464 const KoRekey::KeyConfig *kc)
465 {
466 OVPN_CRYPTO_DATA data;
467 ZeroMemory(&data, sizeof(data));
468
469 const size_t nonce_tail_len = sizeof(kc->encrypt.nonce_tail);
470
471 data.Encrypt.KeyLen = kc->encrypt.cipher_key_size;
472 std::memcpy(data.Encrypt.Key, kc->encrypt.cipher_key, data.Encrypt.KeyLen);
473 std::memcpy(data.Encrypt.NonceTail, kc->encrypt.nonce_tail, nonce_tail_len);
474
475 data.Decrypt.KeyLen = kc->decrypt.cipher_key_size;
476 std::memcpy(data.Decrypt.Key, kc->decrypt.cipher_key, data.Decrypt.KeyLen);
477 std::memcpy(data.Decrypt.NonceTail, kc->decrypt.nonce_tail, nonce_tail_len);
478
479 data.KeyId = kc->key_id;
480 data.PeerId = kc->remote_peer_id;
481 data.CipherAlg = (OVPN_CIPHER_ALG)kc->cipher_alg;
482 data.KeySlot = (type == CryptoDCInstance::ACTIVATE_PRIMARY
483 ? OVPN_KEY_SLOT::OVPN_KEY_SLOT_PRIMARY
484 : OVPN_KEY_SLOT::OVPN_KEY_SLOT_SECONDARY);
485
486 dco_ioctl_(OVPN_IOCTL_NEW_KEY, &data, sizeof(data));
487 }
488
493
495 {
496 const SessionStats::DCOTransportSource::Data old_stats = last_stats;
497
498 try
499 {
500 OVPN_STATS stats{0};
501 DWORD res = dco_ioctl_(OVPN_IOCTL_GET_STATS, 0, 0, &stats, sizeof(stats));
502 if (res == ERROR_SUCCESS)
503 {
504 last_stats = SessionStats::DCOTransportSource::Data(stats.TransportBytesReceived, stats.TransportBytesSent, stats.TunBytesReceived, stats.TunBytesSent);
505 }
506 }
507 catch (const ErrorCode &e)
508 {
509 // no device handle - ignore
510 if (e.code() != Error::TUN_SETUP_FAILED)
511 throw e;
512 }
513
514 last_delta = last_stats - old_stats;
515 }
516
517 DWORD dco_ioctl_(DWORD code,
518 LPVOID in_buf = NULL,
519 DWORD in_buf_size = 0,
520 LPVOID out_buf = NULL,
521 DWORD out_buf_size = 0,
522 openvpn_io::windows::overlapped_ptr *ov = nullptr)
523 {
524 static const std::map<const DWORD, const char *> code_str{
525 {OVPN_IOCTL_NEW_PEER, "OVPN_IOCTL_NEW_PEER"},
526 {OVPN_IOCTL_GET_STATS, "OVPN_IOCTL_GET_STATS"},
527 {OVPN_IOCTL_NEW_KEY, "OVPN_IOCTL_NEW_KEY"},
528 {OVPN_IOCTL_SWAP_KEYS, "OVPN_IOCTL_SWAP_KEYS"},
529 {OVPN_IOCTL_SET_PEER, "OVPN_IOCTL_SET_PEER"},
530 {OVPN_IOCTL_START_VPN, "OVPN_IOCTL_START_VPN"},
531 {OVPN_IOCTL_DEL_PEER, "OVPN_IOCTL_DEL_PEER"},
532 };
533
534 auto handle = get_handle();
535 if (handle == nullptr)
536 throw ErrorCode(Error::TUN_SETUP_FAILED, false, "no device handle");
537
538 HANDLE th(handle->native_handle());
539 LPOVERLAPPED ov_ = (ov ? ov->get() : NULL);
540 if (!DeviceIoControl(th, code, in_buf, in_buf_size, out_buf, out_buf_size, NULL, ov_))
541 {
542 const DWORD error_code = GetLastError();
543 if (ov)
544 {
545 if (error_code == ERROR_IO_PENDING)
546 {
547 ov->release();
548 return error_code;
549 }
550 openvpn_io::error_code error(error_code, openvpn_io::system_category());
551 ov->complete(error, 0);
552 }
553
554 std::ostringstream os;
555 os << "DeviceIoControl(" << code_str.at(code) << ")"
556 << " failed with code " << error_code;
557 throw ErrorCode(Error::TUN_SETUP_FAILED, true, os.str());
558 }
559 return ERROR_SUCCESS;
560 }
561
562 TunWin::TAPStream *get_handle()
563 {
564 if (tun_persist && tun_persist->obj_defined())
565 return tun_persist->obj();
566 else if (handle_.defined())
567 return handle_();
568
569 return nullptr;
570 }
571
572 virtual SessionStats::DCOTransportSource::Data dco_transport_stats_delta() override
573 {
574 if (halt)
575 {
576 /* retrieve the last stats update and erase it to avoid race conditions with other queries */
577 SessionStats::DCOTransportSource::Data delta = last_delta;
578 last_delta = SessionStats::DCOTransportSource::Data();
579 return delta;
580 }
581
582 get_stats_();
583 return last_delta;
584 }
585
586 TunWin::SetupBase::Ptr tun_setup_;
587 BufferAllocated buf_;
588 Protocol proto_;
589 openvpn_io::ip::udp::endpoint endpoint_;
590
591 TunWin::DcoTunPersist::Ptr tun_persist;
592
593 SessionStats::DCOTransportSource::Data last_stats;
594 SessionStats::DCOTransportSource::Data last_delta;
595};
#define OPENVPN_ASYNC_HANDLER
Definition bigmutex.hpp:36
void stop()
Definition Client.java:68
OMI * parent
Definition openvpn.cpp:78
SessionStats::DCOTransportSource::Data last_delta
Protocol transport_protocol() const override
void resolve_callback(const openvpn_io::error_code &error, results_type results) override
TunWin::ScopedTAPStream handle_
void transport_start() override
void tun_start(const OptionList &opt, TransportClient &transcli, CryptoDCSettings &dc_settings) override
void setup_tun(const OptionList &opt, TransportClient &transcli, bool is_tun_start=true)
void apply_push_update(const OptionList &opt, TransportClient &transcli) override
void rekey(const CryptoDCInstance::RekeyType rktype, const KoRekey::Info &rkinfo) override
std::uint32_t vpn_interface_index() const override
OvpnDcoWinClient(openvpn_io::io_context &io_context, ClientConfig *config, TransportClientParent *parent)
bool send_(const Buffer &buf)
void add_peer_(CB complete)
static bool available()
IP::Addr server_endpoint_addr() const override
SessionStats::DCOTransportSource::Data last_stats
TunWin::SetupBase::Ptr tun_setup_
virtual SessionStats::DCOTransportSource::Data dco_transport_stats_delta() override
std::string tun_name() const override
TunWin::DcoTunPersist::Ptr tun_persist
void add_crypto_(const CryptoDCInstance::RekeyType type, const KoRekey::KeyConfig *kc)
DWORD dco_ioctl_(DWORD code, LPVOID in_buf=NULL, DWORD in_buf_size=0, LPVOID out_buf=NULL, DWORD out_buf_size=0, openvpn_io::windows::overlapped_ptr *ov=nullptr)
bool transport_send_const(const Buffer &buf) override
void adjust_mss(int mss) override
void stop_() override
friend class ClientConfig
RCPtr< OvpnDcoWinClient > Ptr
openvpn_io::ip::udp::endpoint endpoint_
void set_service_fail_handler()
bool transport_send(BufferAllocated &buf) override
BufferAllocated buf_
TunWin::TAPStream * get_handle()
Private::ClientState * state
Definition ovpncli.hpp:760
#define OPENVPN_LOG(args)
#define OPENVPN_LOG_STRING(str)
#define OVPN_IOCTL_GET_STATS
Definition ovpn-dco.h:132
@ OVPN_PROTO_UDP
Definition ovpn-dco.h:54
@ OVPN_PROTO_TCP
Definition ovpn-dco.h:55
#define OVPN_IOCTL_SWAP_KEYS
Definition ovpn-dco.h:134
#define OVPN_IOCTL_NEW_KEY
Definition ovpn-dco.h:133
enum _OVPN_CIPHER_ALG OVPN_CIPHER_ALG
#define OVPN_IOCTL_SET_PEER
Definition ovpn-dco.h:135
#define OVPN_IOCTL_NEW_PEER
Definition ovpn-dco.h:131
#define OVPN_IOCTL_START_VPN
Definition ovpn-dco.h:136
#define OVPN_IOCTL_DEL_PEER
Definition ovpn-dco.h:137
union _OVPN_NEW_PEER::@41 Local
SOCKADDR_IN6 Addr6
Definition ovpn-dco.h:62
SOCKADDR_IN Addr4
Definition ovpn-dco.h:61
union _OVPN_NEW_PEER::@42 Remote
OVPN_PROTO Proto
Definition ovpn-dco.h:70
std::ostringstream os
static const char config[]