mirror of
https://github.com/OpenVPN/openvpn3.git
synced 2024-09-19 19:52: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:
parent
6a9b174f21
commit
ae99307219
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
tun_parent->tun_pre_tun_config();
|
||||
const IP::Addr server_addr = transcli.server_endpoint_addr();
|
||||
|
||||
// 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());
|
||||
// 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
|
||||
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);
|
||||
|
||||
std::ostringstream os;
|
||||
HANDLE th = tun_setup_->get_handle(os);
|
||||
OPENVPN_LOG_STRING(os.str());
|
||||
if (th == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
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
|
||||
|
||||
handle_ = std::make_unique<asio::windows::stream_handle>(io_context, th);
|
||||
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_.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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
||||
|
@ -47,6 +47,7 @@ namespace openvpn {
|
||||
}
|
||||
};
|
||||
typedef TunPersistTemplate<ScopedTAPStream, TunPersistState<RingBuffer::Ptr>> TunPersist;
|
||||
typedef TunPersistTemplate<ScopedTAPStream, TunPersistState<Util::TapNameGuidPair>> DcoTunPersist;
|
||||
|
||||
class ClientConfig : public TunClientFactory
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user