0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-20 04:02:15 +02:00

tun: add persis-tun support for dco-win

Add DcoTunPersist object to DCO::TunConfig.

DcoTunPersist stores:

 - device handle
 - tun settings
 - adapter index/name
 - pointer to TunSetup object, which itself
 stores commands to undo tun settings

When intializing client options, instantiate DcoTunPersist
object within the scope of ClientConfig, which serves as
transport and tun factory for dco. Indicate that "sock" object
(device handle) should be preserved - not replaced when persisting
tun settings.

When establishing dco-win connection in OvpnDcoWinClient,
either use tun_persist created above (if persistance is enabled)
or instantiate it in-place (no persistance).

If nothing is stored in tun_persist (means this is first
connection or reconnect without persistance), acquire device
handle from tun_setup, wrap it into ASIO's basic_stream_handle
and store it in OvpnDcoWinClient - no need to persist it yet.

When starting tun, check if persisted tun session matches
to-be-created session. If no - clear previous tun settings,
set up tun and persist tun state. If device handle is already
stored in tun_persist, it won't be replaced.

On tun stop, send DEL_PEER command, which deletes peer
from the driver but keeps adapter in connected state. Then
close locally stored ASIO handle and reset tun_persist.
In case of "short term persistance" this will undo tun settings
and close device handle. For long term persistence, tun_persist
is also stored in ClientConfig and handle won't be closed yet.

In case of disconnect, ClientConfig::finalize(disconnect=true)
is called, which resets tun_persist, which in turn
undoes tun settings and closes device handle.

Signed-off-by: Lev Stipakov <lev@openvpn.net>
This commit is contained in:
Lev Stipakov 2022-10-04 11:27:19 +03:00 committed by David Sommerseth
parent 6a9b174f21
commit ae99307219
No known key found for this signature in database
GPG Key ID: 86CF944C9671FDF2
6 changed files with 112 additions and 36 deletions

View File

@ -351,6 +351,10 @@ namespace openvpn {
tunconf.tun_prop.google_dns_fallback = config.google_dns_fallback;
tunconf.tun_prop.remote_list = remote_list;
tunconf.stop = config.stop;
#if defined(OPENVPN_PLATFORM_WIN)
if (config.tun_persist)
tunconf.tun_persist.reset(new TunWin::DcoTunPersist(true, TunWrapObjRetain::NO_RETAIN_NO_REPLACE, nullptr));
#endif
tun_factory = dco->new_tun_factory(tunconf, opt);
}
else

View File

@ -89,6 +89,14 @@ public:
transport.remote_list->process_push(opt);
}
virtual void finalize(const bool disconnected) override
{
#ifdef ENABLE_OVPNDCOWIN
if (disconnected)
tun.tun_persist.reset();
#endif
}
virtual TunClientFactory::Ptr
new_tun_factory(const DCO::TunConfig &conf, const OptionList &opt) override {
tun = conf;

View File

@ -127,3 +127,4 @@ typedef struct _OVPN_SET_PEER {
#define OVPN_IOCTL_SWAP_KEYS CTL_CODE(FILE_DEVICE_UNKNOWN, 4, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define OVPN_IOCTL_SET_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 5, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define OVPN_IOCTL_START_VPN CTL_CODE(FILE_DEVICE_UNKNOWN, 6, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define OVPN_IOCTL_DEL_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 7, METHOD_BUFFERED, FILE_ANY_ACCESS)

View File

@ -36,7 +36,7 @@ public:
}
void transport_start() override {
if (handle_)
if (tun_persist)
return;
halt = false;
@ -71,22 +71,51 @@ public:
CryptoDCSettings& dc_settings) override {
halt = false;
const IP::Addr server_addr = transcli.server_endpoint_addr();
// Check if persisted tun session matches properties of to-be-created session
if (tun_persist->use_persisted_tun(server_addr, config->tun.tun_prop, opt))
{
state = tun_persist->state().state;
OPENVPN_LOG("TunPersist: reused tun context");
}
else
{
// notify parent
tun_parent->tun_pre_tun_config();
OPENVPN_LOG("TunPersist: clear tun settings");
std::ostringstream os;
tun_persist->close_destructor();
dco_ioctl_(OVPN_IOCTL_START_VPN);
// parse pushed options
po_ = new TunBuilderCapture();
TunProp::configure_builder(po_.get(), state.get(), nullptr,
transcli.server_endpoint_addr(),
config->tun.tun_prop, opt, nullptr, false);
OPENVPN_LOG("CAPTURED OPTIONS:" << std::endl << po_->to_string());
TunBuilderCapture::Ptr po(new TunBuilderCapture());
TunProp::configure_builder(po.get(), state.get(), nullptr,
transcli.server_endpoint_addr(), config->tun.tun_prop, opt, nullptr, false);
OPENVPN_LOG("CAPTURED OPTIONS:" << std::endl << po->to_string());
tun_setup_->establish(*po, Win::module_name(), NULL, os, NULL);
OPENVPN_LOG_STRING(os.str());
// persist tun settings state
if (tun_persist->persist_tun_state(handle_(), { state, tun_setup_->get_adapter_state() }))
OPENVPN_LOG("TunPersist: saving tun context:" << std::endl << tun_persist->options());
handle_.release();
// enable tun_setup destructor
tun_persist->add_destructor(tun_setup_);
}
set_keepalive_();
// Add a hook so ProtoContext will call back to rekey() on rekey ops.
dc_settings.set_factory(CryptoDCFactory::Ptr(new KoRekey::Factory(
dc_settings.factory(), this, config->transport.frame)));
set_keepalive_();
start_vpn_();
tun_parent->tun_connected();
}
@ -160,6 +189,8 @@ protected:
}
}
TunWin::ScopedTAPStream handle_;
void start_impl_() {
if (halt)
return;
@ -167,15 +198,30 @@ protected:
// create new tun setup object
tun_setup_ = config->tun.new_setup_obj(io_context, config->allow_local_dns_resolvers);
if (config->tun.tun_persist)
tun_persist = config->tun.tun_persist; // long-term persistent
else
tun_persist.reset(new TunWin::DcoTunPersist(false, TunWrapObjRetain::NO_RETAIN, nullptr)); // short-term
if (!tun_persist->obj_defined())
{
std::ostringstream os;
HANDLE th = tun_setup_->get_handle(os);
OPENVPN_LOG_STRING(os.str());
if (th == INVALID_HANDLE_VALUE)
return;
handle_ = std::make_unique<asio::windows::stream_handle>(io_context, th);
handle_.reset(new TunWin::TAPStream(io_context, th));
tun_persist->add_destructor(tun_setup_);
}
else
{
tun_setup_->set_adapter_state(tun_persist->state().adapter_state);
}
tun_setup_->confirm();
tun_setup_->set_service_fail_handler([self=Ptr(this)]() {
if (!self->halt)
self->tun_parent->tun_error(Error::TUN_IFACE_DISABLED, "service failure");
@ -194,7 +240,7 @@ protected:
void queue_read_() {
buf_.reset(0, 2048, 0);
handle_->async_read_some(
get_handle()->async_read_some(
buf_.mutable_buffer_clamp(),
[self = Ptr(this)](const openvpn_io::error_code &error,
const size_t bytes_recvd) {
@ -216,7 +262,7 @@ protected:
bool send_(const Buffer &buf) {
openvpn_io::error_code error;
handle_->write_some(buf.const_buffer(), error);
get_handle()->write_some(buf.const_buffer(), error);
if (error) {
transport_parent->transport_error(Error::TRANSPORT_ERROR,
error.message());
@ -228,13 +274,21 @@ protected:
void stop_() override {
if (!halt) {
std::ostringstream os;
halt = true;
async_resolve_cancel();
if (tun_setup_)
tun_setup_->destroy(os);
handle_.reset();
OPENVPN_LOG_STRING(os.str());
try {
dco_ioctl_(OVPN_IOCTL_DEL_PEER);
}
catch (const ErrorCode& e) {
// this is fine - stopped before we got driver handle
if (e.code() != Error::TUN_SETUP_FAILED)
throw e;
}
handle_.close();
tun_persist.reset();
}
}
@ -334,14 +388,6 @@ protected:
dco_ioctl_(OVPN_IOCTL_NEW_KEY, &data, sizeof(data));
}
void start_vpn_() {
dco_ioctl_(OVPN_IOCTL_START_VPN);
std::ostringstream os;
tun_setup_->establish(*po_, Win::module_name(), NULL, os, NULL);
OPENVPN_LOG_STRING(os.str());
}
void swap_keys_() { dco_ioctl_(OVPN_IOCTL_SWAP_KEYS); }
DWORD dco_ioctl_(DWORD code, LPVOID data = NULL, DWORD size = 0,
@ -355,7 +401,11 @@ protected:
{ OVPN_IOCTL_START_VPN, "OVPN_IOCTL_START_VPN" },
};
HANDLE th(handle_->native_handle());
auto handle = get_handle();
if (handle == nullptr)
throw ErrorCode(Error::TUN_SETUP_FAILED, false, "no device handle");
HANDLE th(handle->native_handle());
LPOVERLAPPED ov_ = (ov ? ov->get() : NULL);
if (!DeviceIoControl(th, code, data, size, NULL, 0, NULL, ov_)) {
const DWORD error_code = GetLastError();
@ -375,10 +425,20 @@ protected:
return ERROR_SUCCESS;
}
std::unique_ptr<openvpn_io::windows::stream_handle> handle_;
TunBuilderCapture::Ptr po_;
TunWin::TAPStream* get_handle()
{
if (tun_persist && tun_persist->obj_defined())
return tun_persist->obj();
else if (handle_.defined())
return handle_();
return nullptr;
}
TunWin::SetupBase::Ptr tun_setup_;
BufferAllocated buf_;
Protocol proto_;
openvpn_io::ip::udp::endpoint endpoint_;
TunWin::DcoTunPersist::Ptr tun_persist;
};

View File

@ -38,6 +38,7 @@
#if defined(OPENVPN_PLATFORM_WIN)
#include <openvpn/tun/win/client/tunsetup.hpp>
#include <openvpn/tun/win/client/clientconfig.hpp>
#endif
namespace openvpn {
@ -74,13 +75,14 @@ namespace openvpn {
else
return new TunWin::Setup(io_context, TunWin::OvpnDco, allow_local_dns_resolvers);
}
TunWin::DcoTunPersist::Ptr tun_persist;
#endif
TunProp::Config tun_prop;
Stop* stop = nullptr;
};
virtual TunClientFactory::Ptr new_tun_factory(const TunConfig& conf, const OptionList& opt) = 0;
virtual TransportClientFactory::Ptr new_transport_factory(const TransportConfig& conf) = 0;

View File

@ -47,6 +47,7 @@ namespace openvpn {
}
};
typedef TunPersistTemplate<ScopedTAPStream, TunPersistState<RingBuffer::Ptr>> TunPersist;
typedef TunPersistTemplate<ScopedTAPStream, TunPersistState<Util::TapNameGuidPair>> DcoTunPersist;
class ClientConfig : public TunClientFactory
{