From 71b3391dee3720e60bd3d8be9aaed13da7583207 Mon Sep 17 00:00:00 2001 From: Lev Stipakov Date: Wed, 29 Mar 2023 14:08:33 +0300 Subject: [PATCH] dco-win: add support for peer stats Make DCOTransportSource aware of tun stats. Implemenent DCOTransportSource interface. Withing stats delta callback, fetch peer stats and return delta between last and current stats (same as in DCO Linux). Fixes OVPN3-947. Signed-off-by: Lev Stipakov --- openvpn/dco/ovpndcowincli.hpp | 58 ++++++++++++++++++++++++++++++++--- openvpn/log/sessionstats.hpp | 39 ++++++++++++++--------- 2 files changed, 78 insertions(+), 19 deletions(-) diff --git a/openvpn/dco/ovpndcowincli.hpp b/openvpn/dco/ovpndcowincli.hpp index 7cf25cd0..fb9b580a 100644 --- a/openvpn/dco/ovpndcowincli.hpp +++ b/openvpn/dco/ovpndcowincli.hpp @@ -21,7 +21,9 @@ #pragma once -class OvpnDcoWinClient : public Client, public KoRekey::Receiver +class OvpnDcoWinClient : public Client, + public KoRekey::Receiver, + public SessionStats::DCOTransportSource { friend class ClientConfig; typedef RCPtr Ptr; @@ -285,6 +287,8 @@ class OvpnDcoWinClient : public Client, public KoRekey::Receiver if (!self->halt) self->queue_read_(); } }); + + config->transport.stats->dco_configure(this); } void queue_read_() @@ -332,6 +336,8 @@ class OvpnDcoWinClient : public Client, public KoRekey::Receiver { if (!halt) { + get_stats_(); + halt = true; async_resolve_cancel(); @@ -399,7 +405,7 @@ class OvpnDcoWinClient : public Client, public KoRekey::Receiver } }}; - const DWORD ec = dco_ioctl_(OVPN_IOCTL_NEW_PEER, &peer, sizeof(peer), &ov); + const DWORD ec = dco_ioctl_(OVPN_IOCTL_NEW_PEER, &peer, sizeof(peer), NULL, 0, &ov); if (ec == ERROR_SUCCESS) complete(); else if (ec != ERROR_IO_PENDING) @@ -463,9 +469,34 @@ class OvpnDcoWinClient : public Client, public KoRekey::Receiver dco_ioctl_(OVPN_IOCTL_SWAP_KEYS); } + void get_stats_() + { + const SessionStats::DCOTransportSource::Data old_stats = last_stats; + + try + { + OVPN_STATS stats{0}; + DWORD res = dco_ioctl_(OVPN_IOCTL_GET_STATS, 0, 0, &stats, sizeof(stats)); + if (res == ERROR_SUCCESS) + { + last_stats = SessionStats::DCOTransportSource::Data(stats.TransportBytesReceived, stats.TransportBytesSent, stats.TunBytesReceived, stats.TunBytesSent); + } + } + catch (const ErrorCode &e) + { + // no device handle - ignore + if (e.code() != Error::TUN_SETUP_FAILED) + throw e; + } + + last_delta = last_stats - old_stats; + } + DWORD dco_ioctl_(DWORD code, - LPVOID data = NULL, - DWORD size = 0, + 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) { static const std::map code_str{ @@ -483,7 +514,7 @@ class OvpnDcoWinClient : public Client, public KoRekey::Receiver HANDLE th(handle->native_handle()); LPOVERLAPPED ov_ = (ov ? ov->get() : NULL); - if (!DeviceIoControl(th, code, data, size, NULL, 0, NULL, ov_)) + if (!DeviceIoControl(th, code, in_buf, in_buf_size, out_buf, out_buf_size, NULL, ov_)) { const DWORD error_code = GetLastError(); if (ov) @@ -515,10 +546,27 @@ class OvpnDcoWinClient : public Client, public KoRekey::Receiver return nullptr; } + virtual SessionStats::DCOTransportSource::Data dco_transport_stats_delta() override + { + if (halt) + { + /* retrieve the last stats update and erase it to avoid race conditions with other queries */ + SessionStats::DCOTransportSource::Data delta = last_delta; + last_delta = SessionStats::DCOTransportSource::Data(); + return delta; + } + + get_stats_(); + return last_delta; + } + TunWin::SetupBase::Ptr tun_setup_; BufferAllocated buf_; Protocol proto_; openvpn_io::ip::udp::endpoint endpoint_; TunWin::DcoTunPersist::Ptr tun_persist; + + SessionStats::DCOTransportSource::Data last_stats; + SessionStats::DCOTransportSource::Data last_delta; }; diff --git a/openvpn/log/sessionstats.hpp b/openvpn/log/sessionstats.hpp index 03d69fff..fb1c1fe7 100644 --- a/openvpn/log/sessionstats.hpp +++ b/openvpn/log/sessionstats.hpp @@ -128,28 +128,37 @@ class SessionStats : public RC struct Data { - count_t bytes_in; - count_t bytes_out; + count_t transport_bytes_in = 0; + count_t transport_bytes_out = 0; + count_t tun_bytes_in = 0; + count_t tun_bytes_out = 0; - Data() - : bytes_in(0), - bytes_out(0) + Data() = default; + + Data(count_t transport_bytes_in_arg, count_t transport_bytes_out_arg) + : transport_bytes_in(transport_bytes_in_arg), + transport_bytes_out(transport_bytes_out_arg) { } - Data(count_t bytes_in_arg, count_t bytes_out_arg) - : bytes_in(bytes_in_arg), - bytes_out(bytes_out_arg) + Data(count_t transport_bytes_in_arg, count_t transport_bytes_out_arg, count_t tun_bytes_in_arg, count_t tun_bytes_out_arg) + : transport_bytes_in(transport_bytes_in_arg), + transport_bytes_out(transport_bytes_out_arg), + tun_bytes_in(tun_bytes_in_arg), tun_bytes_out(tun_bytes_out_arg) { } Data operator-(const Data &rhs) const { Data data; - if (bytes_in > rhs.bytes_in) - data.bytes_in = bytes_in - rhs.bytes_in; - if (bytes_out > rhs.bytes_out) - data.bytes_out = bytes_out - rhs.bytes_out; + if (transport_bytes_in > rhs.transport_bytes_in) + data.transport_bytes_in = transport_bytes_in - rhs.transport_bytes_in; + if (transport_bytes_out > rhs.transport_bytes_out) + data.transport_bytes_out = transport_bytes_out - rhs.transport_bytes_out; + if (tun_bytes_in > rhs.tun_bytes_in) + data.tun_bytes_in = tun_bytes_in - rhs.tun_bytes_in; + if (tun_bytes_out > rhs.tun_bytes_out) + data.tun_bytes_out = tun_bytes_out - rhs.tun_bytes_out; return data; } }; @@ -167,8 +176,10 @@ class SessionStats : public RC if (dco_) { const DCOTransportSource::Data data = dco_->dco_transport_stats_delta(); - stats_[BYTES_IN] += data.bytes_in; - stats_[BYTES_OUT] += data.bytes_out; + stats_[BYTES_IN] += data.transport_bytes_in; + stats_[BYTES_OUT] += data.transport_bytes_out; + stats_[TUN_BYTES_IN] += data.tun_bytes_in; + stats_[TUN_BYTES_OUT] += data.tun_bytes_out; } }