From patchwork Tue Sep 2 12:25:36 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gert Doering X-Patchwork-Id: 4385 Return-Path: Delivered-To: patchwork@openvpn.net Received: by 2002:a05:7000:2a1c:b0:671:5a2c:6455 with SMTP id k28csp1851810maz; Tue, 2 Sep 2025 07:35:58 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCXlixo1pzzMnleqyKcWtmdsuatsF7bDO5IxBvmYx/PzGos+2/4vLBiYLbbs1b6p4bRfl+5gJL9iKw4=@openvpn.net X-Google-Smtp-Source: AGHT+IE8KN1aqlzo/BOuiRbLOM6bXJj5696OIGWuKXtMEGEGj1usIeet41JCnbnG/YllQ8nycNPS X-Received: by 2002:a05:6871:5423:b0:319:c30d:10ba with SMTP id 586e51a60fabf-319c30d17b6mr751886fac.23.1756823758335; Tue, 02 Sep 2025 07:35:58 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1756823758; cv=none; d=google.com; s=arc-20240605; b=DqJc9LUIzvxVdRNvCz4lcfsci0rwdF5vNllJ07Ax2P9R7eGiZYResFN/aRQ4WNTq6F aDy88lQu+egkWGqwvNmhWmQ+jqVpCGWzQ05CNmCXNVoT/Ph3DyC+oCnYoyLtvFmrWHNg ffr5/O/FinQ6tJtUiJpi/TRVb0nGFcZEL8G5djnDp8Nzn1nwElhIzKfWK9YnYAd5nkR/ XWp4fsqt7ZXl1z67q6qmFlksxk3VUYCJc1XocPIya9KcgBoS8mYxChm13CKNk1VPHnFj CTxZZj/jwJRT2RA6IiFRHGcAAH39qvwIBPAZ4lx/0Wwe2oDVqCozwWdiuqx7F/vxI+pd 1AOw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=errors-to:content-transfer-encoding:list-subscribe:list-help :list-post:list-archive:list-unsubscribe:list-id:precedence:subject :mime-version:references:in-reply-to:message-id:date:to:from :dkim-signature:dkim-signature:dkim-signature; bh=eIaLESRLQPZx/ja5Q83HCE6FPqlHQZUhYXutLwp+JUE=; fh=4NbAC/LsuMLI0S0hprUlLSLCiHwg6SCAifhH718Jh0Q=; b=L5NMfjKCcH8gNCUzQiRZVOyoXKI+1C14ePIjyF7ksi2tSd/F2IIkRFCHfw+Cm0R1BH 2xvoTlO0ba/vCfO6SQnMPx6wPgxftRViZMep1D66Pkoprs3iYcHr8FaP9yC82vYZasKx cEuCfqiY0Bo6Jn3aOgI0wbbCwp/IRQPDQ8dr8Bxjr+m9oLM8PDWouUtlcCjjhzP6hvEF Xply1dV+cBWCARP0egadkhPaBf92HRXupq4nPTdvvDrMoUs/T/QbCa8iidFWAWSUeg28 OVJjIHVaytbRPNFPHqytKZgBLfg7Kl2HFo9qT59B/60wHHzRTXGYBaT6KBUAtynh2GuQ 1b8w==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@lists.sourceforge.net header.s=beta header.b=juvdtBIK; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=XNdCIS95; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=Y61a4b5J; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=muc.de Received: from lists.sourceforge.net (lists.sourceforge.net. [216.105.38.7]) by mx.google.com with ESMTPS id 586e51a60fabf-319b5896530si435740fac.74.2025.09.02.07.35.56 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Tue, 02 Sep 2025 07:35:57 -0700 (PDT) Received-SPF: pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) client-ip=216.105.38.7; Authentication-Results: mx.google.com; dkim=pass header.i=@lists.sourceforge.net header.s=beta header.b=juvdtBIK; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=XNdCIS95; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=Y61a4b5J; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=muc.de DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.sourceforge.net; s=beta; h=Content-Transfer-Encoding:Content-Type: List-Subscribe:List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id: Subject:MIME-Version:References:In-Reply-To:Message-ID:Date:To:From:Sender: Reply-To:Cc:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=eIaLESRLQPZx/ja5Q83HCE6FPqlHQZUhYXutLwp+JUE=; b=juvdtBIKja3evGeSZ+MNlfoF55 IIORPAzCvKXLe5ahcbrzhwRXN4nAlrMHKA1/umK3ajsRXyQKIDOH6YwgFDwwc33IWHRM9IrqoAqcb zunEmLXSI5UaKjWkGWdzb155noiuXWcfDo6uggAu0htS2Eb27x1F1eBWzAZqlOkzYlnY=; Received: from [127.0.0.1] (helo=sfs-ml-3.v29.lw.sourceforge.com) by sfs-ml-3.v29.lw.sourceforge.com with esmtp (Exim 4.95) (envelope-from ) id 1utS6x-0003sV-7a; Tue, 02 Sep 2025 14:35:51 +0000 Received: from [172.30.29.66] (helo=mx.sourceforge.net) by sfs-ml-3.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.95) (envelope-from ) id 1utS6v-0003s7-G2 for openvpn-devel@lists.sourceforge.net; Tue, 02 Sep 2025 14:35:49 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Content-Transfer-Encoding:MIME-Version:References: In-Reply-To:Message-ID:Date:Subject:To:From:Sender:Reply-To:Cc:Content-Type: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=YHBuhaykUB95n2Q6bi/evc3Cg0TniWR7eqr9IBjSyL0=; b=XNdCIS95xK1ALkFnwcoZUonRhI cAbY75vF6bvwy64EGnRCF5ky8q9yUFdLoXfcyysZMAmNtGzVkP3ZFCHuc9ot6/knGT7NwCBKXdaXu sS44EfuE/eyADTnuBXn1u9yEMEC6LDdZRdIahba7IcVSzdjRogC4pXXjjiuig24vgNPo=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-ID: Date:Subject:To:From:Sender:Reply-To:Cc:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=YHBuhaykUB95n2Q6bi/evc3Cg0TniWR7eqr9IBjSyL0=; b=Y61a4b5JzhNwtpn+KzyUc8nvRf cECe45CzlOcV4MGEhfz/Z9pX37/KVfpEhFIREdFcJ6e8upZThnd2/aJplZavPk3oKcpAiIcUfnv4E b7voDx9anW5uQsbelJ0Oaz1TmmYgDpA0ARO2oH1tvoNTcNX3SdZAecEE4xr1BZ3ug20U=; Received: from [193.149.48.143] (helo=blue.greenie.muc.de) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.95) id 1utS6t-00085W-W2 for openvpn-devel@lists.sourceforge.net; Tue, 02 Sep 2025 14:35:49 +0000 Received: from blue.greenie.muc.de (localhost [127.0.0.1]) by blue.greenie.muc.de (8.18.1/8.18.1) with ESMTP id 582CPhJi031041 for ; Tue, 2 Sep 2025 14:25:43 +0200 Received: (from gert@localhost) by blue.greenie.muc.de (8.18.1/8.18.1/Submit) id 582CPhlx031040 for openvpn-devel@lists.sourceforge.net; Tue, 2 Sep 2025 14:25:43 +0200 From: Gert Doering To: openvpn-devel@lists.sourceforge.net Date: Tue, 2 Sep 2025 14:25:36 +0200 Message-ID: <20250902122542.31023-1-gert@greenie.muc.de> X-Mailer: git-send-email 2.49.1 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Score: 1.3 (+) X-Spam-Report: Spam detection software, running on the system "sfi-spamd-1.hosts.colo.sdot.me", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: From: Lev Stipakov Use the new driver API to fetch per-peer link and VPN byte counters in both client and server modes. Two usage modes are supported: Content analysis details: (1.3 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 1.3 RDNS_NONE Delivered to internal network by a host with no rDNS X-Headers-End: 1utS6t-00085W-W2 Subject: [Openvpn-devel] [PATCH v2] dco-win: add support for multipeer stats X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox X-GMAIL-THRID: =?utf-8?q?1842163228347717942?= X-GMAIL-MSGID: =?utf-8?q?1842163228347717942?= From: Lev Stipakov Use the new driver API to fetch per-peer link and VPN byte counters in both client and server modes. Two usage modes are supported: - Single peer: pass the peer ID and a fixed-size output buffer. If the IOCTL is not supported (old driver), fall back to the legacy API. - All peers: first call the IOCTL with a small output buffer to get the required size, then allocate a buffer and call again to fetch stats for all peers. Change-Id: I525d7300e49f9a5a18e7146ee35ccc2af8184b8a Signed-off-by: Lev Stipakov Acked-by: Gert Doering --- This change was reviewed on Gerrit and approved by at least one developer. I request to merge it to master. Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1143 This mail reflects revision 2 of this Change. Acked-by according to Gerrit (reflected above): Gert Doering diff --git a/src/openvpn/dco_win.c b/src/openvpn/dco_win.c index 5317ac1..bf3f151 100644 --- a/src/openvpn/dco_win.c +++ b/src/openvpn/dco_win.c @@ -30,6 +30,7 @@ #include "forward.h" #include "tun.h" #include "crypto.h" +#include "multi.h" #include "ssl_common.h" #include "openvpn.h" @@ -190,6 +191,8 @@ { dco_context_t *dco = &c->c1.tuntap->dco; + dco->c = c; + switch (c->mode) { case MODE_POINT_TO_POINT: @@ -714,12 +717,132 @@ int dco_get_peer_stats_multi(dco_context_t *dco, const bool raise_sigusr1_on_err) { - /* Not implemented. */ - return 0; + struct gc_arena gc = gc_new(); + + int ret = 0; + struct tuntap *tt = dco->tt; + + if (!tuntap_defined(tt)) + { + ret = -1; + goto done; + } + + OVPN_GET_PEER_STATS ps = { + .PeerId = -1 + }; + + DWORD required_size = 0, bytes_returned = 0; + /* first, figure out buffer size */ + if (!DeviceIoControl(tt->hand, OVPN_IOCTL_GET_PEER_STATS, &ps, sizeof(ps), &required_size, sizeof(DWORD), &bytes_returned, NULL)) + { + if (GetLastError() == ERROR_MORE_DATA) + { + if (bytes_returned != sizeof(DWORD)) + { + msg(M_WARN, "%s: invalid bytes returned for size query (%lu, expected %zu)", __func__, bytes_returned, sizeof(DWORD)); + ret = -1; + goto done; + } + /* required_size now contains the size written by the driver */ + if (required_size == 0) + { + ret = 0; /* no peers to process */ + goto done; + } + if (required_size < sizeof(OVPN_PEER_STATS)) + { + msg(M_WARN, "%s: invalid required size %lu (minimum %zu)", __func__, required_size, sizeof(OVPN_PEER_STATS)); + ret = -1; + goto done; + } + } + else + { + msg(M_WARN | M_ERRNO, "%s: failed to fetch required buffer size", __func__); + ret = -1; + goto done; + } + } + else + { + /* unexpected success? */ + if (bytes_returned == 0) + { + ret = 0; /* no peers to process */ + goto done; + } + + msg(M_WARN, "%s: first DeviceIoControl call succeeded unexpectedly (%lu bytes returned)", __func__, bytes_returned); + ret = -1; + goto done; + } + + + /* allocate the buffer and fetch stats */ + OVPN_PEER_STATS *peer_stats = gc_malloc(required_size, true, &gc); + if (!peer_stats) + { + msg(M_WARN, "%s: failed to allocate buffer of size %lu", __func__, required_size); + ret = -1; + goto done; + } + + if (!DeviceIoControl(tt->hand, OVPN_IOCTL_GET_PEER_STATS, &ps, sizeof(ps), peer_stats, required_size, &bytes_returned, NULL)) + { + /* unlikely case when a peer has been added since fetching buffer size, not an error! */ + if (GetLastError() == ERROR_MORE_DATA) + { + msg(M_WARN, "%s: peer has been added, skip fetching stats", __func__); + ret = 0; + goto done; + } + + msg(M_WARN | M_ERRNO, "%s: failed to fetch multipeer stats", __func__); + ret = -1; + goto done; + } + + /* iterate over stats and update peers */ + for (int i = 0; i < bytes_returned / sizeof(OVPN_PEER_STATS); ++i) + { + OVPN_PEER_STATS *stat = &peer_stats[i]; + + if (stat->PeerId >= dco->c->multi->max_clients) + { + msg(M_WARN, "%s: received out of bound peer_id %u (max=%u)", __func__, stat->PeerId, + dco->c->multi->max_clients); + continue; + } + + struct multi_instance *mi = dco->c->multi->instances[stat->PeerId]; + if (!mi) + { + msg(M_WARN, "%s: received data for a non-existing peer %u", __func__, stat->PeerId); + continue; + } + + /* update peer stats */ + struct context_2 *c2 = &mi->context.c2; + c2->dco_read_bytes = stat->LinkRxBytes; + c2->dco_write_bytes = stat->LinkTxBytes; + c2->tun_read_bytes = stat->VpnRxBytes; + c2->tun_write_bytes = stat->VpnTxBytes; + } + +done: + gc_free(&gc); + + if (raise_sigusr1_on_err && ret < 0) + { + register_signal(dco->c->sig, SIGUSR1, "dco peer stats error"); + } + + return ret; } int -dco_get_peer_stats(struct context *c, const bool raise_sigusr1_on_err) +dco_get_peer_stats_fallback(struct context *c, const bool raise_sigusr1_on_err) { struct tuntap *tt = c->c1.tuntap; @@ -747,6 +870,48 @@ return 0; } +int +dco_get_peer_stats(struct context *c, const bool raise_sigusr1_on_err) +{ + struct tuntap *tt = c->c1.tuntap; + + if (!tuntap_defined(tt)) + { + return -1; + } + + /* first, try a new ioctl */ + OVPN_GET_PEER_STATS ps = { .PeerId = c->c2.tls_multi->dco_peer_id }; + + OVPN_PEER_STATS peer_stats = { 0 }; + DWORD bytes_returned = 0; + if (!DeviceIoControl(tt->hand, OVPN_IOCTL_GET_PEER_STATS, &ps, sizeof(ps), &peer_stats, sizeof(peer_stats), + &bytes_returned, NULL)) + { + if (GetLastError() == ERROR_INVALID_FUNCTION) + { + /* are we using the old driver? */ + return dco_get_peer_stats_fallback(c, raise_sigusr1_on_err); + } + + msg(M_WARN | M_ERRNO, "%s: DeviceIoControl(OVPN_IOCTL_GET_PEER_STATS) failed", __func__); + return -1; + } + + if (bytes_returned != sizeof(OVPN_PEER_STATS)) + { + msg(M_WARN | M_ERRNO, "%s: DeviceIoControl(OVPN_IOCTL_GET_PEER_STATS) returnted invalid size", __func__); + return -1; + } + + c->c2.dco_read_bytes = peer_stats.LinkRxBytes; + c->c2.dco_write_bytes = peer_stats.LinkTxBytes; + c->c2.tun_read_bytes = peer_stats.VpnRxBytes; + c->c2.tun_write_bytes = peer_stats.VpnTxBytes; + + return 0; +} + void dco_event_set(dco_context_t *dco, struct event_set *es, void *arg) { diff --git a/src/openvpn/dco_win.h b/src/openvpn/dco_win.h index a7f4865..4f3f028 100644 --- a/src/openvpn/dco_win.h +++ b/src/openvpn/dco_win.h @@ -57,6 +57,8 @@ uint64_t dco_read_bytes; uint64_t dco_write_bytes; + + struct context *c; }; typedef struct dco_context dco_context_t; diff --git a/src/openvpn/ovpn_dco_win.h b/src/openvpn/ovpn_dco_win.h index baf7214..9e1378a 100644 --- a/src/openvpn/ovpn_dco_win.h +++ b/src/openvpn/ovpn_dco_win.h @@ -83,6 +83,14 @@ LONG64 TunBytesReceived; } OVPN_STATS, * POVPN_STATS; +typedef struct _OVPN_PEER_STATS { + int PeerId; + LONG64 LinkRxBytes; + LONG64 LinkTxBytes; + LONG64 VpnRxBytes; + LONG64 VpnTxBytes; +} OVPN_PEER_STATS, * POVPN_PEER_STATS; + typedef enum _OVPN_KEY_SLOT { OVPN_KEY_SLOT_PRIMARY, OVPN_KEY_SLOT_SECONDARY @@ -185,6 +193,10 @@ int IPv6; } OVPN_MP_IROUTE, * POVPN_MP_IROUTE; +typedef struct _OVPN_GET_PEER_STATS { + int PeerId; // -1 for all peers stats +} OVPN_GET_PEER_STATS, * POVPN_GET_PEER_STATS; + #define OVPN_IOCTL_NEW_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_ANY_ACCESS) #define OVPN_IOCTL_GET_STATS CTL_CODE(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_ANY_ACCESS) #define OVPN_IOCTL_NEW_KEY CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS) @@ -207,3 +219,5 @@ #define OVPN_IOCTL_MP_ADD_IROUTE CTL_CODE(FILE_DEVICE_UNKNOWN, 17, METHOD_BUFFERED, FILE_ANY_ACCESS) #define OVPN_IOCTL_MP_DEL_IROUTE CTL_CODE(FILE_DEVICE_UNKNOWN, 18, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define OVPN_IOCTL_GET_PEER_STATS CTL_CODE(FILE_DEVICE_UNKNOWN, 19, METHOD_BUFFERED, FILE_ANY_ACCESS)