10
9
Fork
You've already forked linux-app
14

Wrong Downloaded/Uploaded statistics with OpenVPN #646

Closed
opened 2025年06月06日 15:00:17 +02:00 by mhau · 3 comments

Installation

Debian 12: Using the official repostory https://app.eduvpn.org/linux/v4/deb/
Arch Linux: Using the (user-maintained) AUR packages

Version

Debian 12:

eduvpn-client 4.5.1-1+debian+12+1
eduvpn-client-data 4.5.1-1+debian+12+1
libeduvpn-common 3.0.0-1+debian+12+1
python3-eduvpn-client 4.5.1-1+debian+12+1
python3-eduvpn-common 3.0.0-1+debian+12+1
libnm0 1.42.4-1
network-manager 1.42.4-1
network-manager-openvpn 1.10.2-2

Arch Linux:

python-eduvpn-client 4.5.1-1
python-eduvpn_common 3.0.0-1
libnm 1.52.0-1
networkmanager 1.52.0-1
networkmanager-openvpn 1.12.0-1

Check if your issue is in the known issues list

It is not.

OS/Distribution

Debian 12.8
Arch Linux (Rolling Release)

Logs

Do you have a problem while adding before connecting?

No.

Do you have a problem during or after connecting?

When connected using the OpenVPN protocol, the shown Downloaded/Uploaded statistics are wrong. The shown statistics are from the "primary interface" through which the OpenVPN connection was established, and not the tunnel interface. This is not noticeable with a full-tunnel profile where all the traffic goes though the tunnel, but very noticeable with a split-tunnel profile where only certain traffic (ip prefixes) is routed through the tunnel. I could confirm/reproduce this on multiple different systems.

Attached you find a screenshot from a fresh vanilla Debian 12.8 system. You can see that the shown statistics in the eduVPN client are (roughly) those of the primary ethernet interface enp3s0 (1231306048 B are ~1.147 GiB, and 881471384 B are ~840.6 MiB), and not of the tunnel interface tun0. So those statistics currently don't show the amount of data that actually was sent through the tunnel.

Disclaimer: I have not tested how this behaves with Wireguard.


The eduVPN-Client uses the first device associated with the "eduVPN" connection in NetworkManager that it creates/activates/monitors. The only device there is the primary interface, and not the tunnel interface. I also found no other attribute which directly links the NetworkManager vpn connection to the associated tunnel interface.

Looking at the DBus messages from the official NetworkManager OpenVPN plugin to the NetworkManager daemon, you can see this information is provided in the SetConfig call using the tundev attribute. And while I could verify that this info is correctly stored internally, I found no place where it is exposed; via an API or otherwise.

‣ Type=method_call Endian=l Flags=0 Version=1 Cookie=7 Timestamp="Thu 2025年06月05日 15:26:30.349343 UTC"
 Sender=:1.4258 Destination=:1.4257 Path=/org/freedesktop/NetworkManager/VPN/Plugin Interface=org.freedesktop.NetworkManager.VPN.Plugin Member=SetConfig
 UniqueName=:1.4258
 MESSAGE "a{sv}" {
 ARRAY "{sv}" {
 DICT_ENTRY "sv" {
 STRING "gateway";
 VARIANT "u" {
 UINT32 2130706433;
 };
 };
 DICT_ENTRY "sv" {
 STRING "can-persist";
 VARIANT "b" {
 BOOLEAN true;
 };
 };
 DICT_ENTRY "sv" {
 STRING "tundev";
 VARIANT "s" {
 STRING "tun0";
 };
 };
 DICT_ENTRY "sv" {
 STRING "mtu";
 VARIANT "u" {
 UINT32 1380;
 };
 };
 DICT_ENTRY "sv" {
 STRING "has-ip4";
 VARIANT "b" {
 BOOLEAN true;
 };
 };
 };
 };

The only way I found right now to get the right tunnel interface is to match using the assigned IP address. Below a quick-and-dirty Proof-of-Concept.

diff --git a/eduvpn/nm.py b/eduvpn/nm.py
index ac443b2..e4c451a 100644
--- a/eduvpn/nm.py
+++ b/eduvpn/nm.py
@@ -248,6 +248,25 @@ class NMManager:
 if not active_con:
 return None
 
+ if active_con.get_connection_type() == 'vpn':
+ needle_ipv4 = self.ipv4
+ if not needle_ipv4:
+ return None
+ for haystack_con in self.client.get_active_connections():
+ haystack_ipv4 = haystack_con.get_ip4_config()
+ if not haystack_ipv4:
+ continue
+ haystack_ipv4 = haystack_ipv4.get_addresses()
+ if not haystack_ipv4:
+ continue
+ haystack_ipv4 = con4[0].get_address()
+
+ if haystack_con.get_uuid() != active_con.get_uuid() and haystack_ipv4 == needle_ipv4:
+ active_con = haystack_con
+ break
+ else:
+ return None
+
 devices = active_con.get_devices()
 if not devices:
 return None

As a side note, while debugging I found that the (superfluous?) iface check in NMManager.get_stats_bytes() triggers resolution of the iface property twice per second, which makes the caching in NetworkStats a bit pointless.

# Installation Debian 12: Using the official repostory `https://app.eduvpn.org/linux/v4/deb/` Arch Linux: Using the (user-maintained) AUR packages # Version Debian 12: ``` eduvpn-client 4.5.1-1+debian+12+1 eduvpn-client-data 4.5.1-1+debian+12+1 libeduvpn-common 3.0.0-1+debian+12+1 python3-eduvpn-client 4.5.1-1+debian+12+1 python3-eduvpn-common 3.0.0-1+debian+12+1 libnm0 1.42.4-1 network-manager 1.42.4-1 network-manager-openvpn 1.10.2-2 ``` Arch Linux: ``` python-eduvpn-client 4.5.1-1 python-eduvpn_common 3.0.0-1 libnm 1.52.0-1 networkmanager 1.52.0-1 networkmanager-openvpn 1.12.0-1 ``` # Check if your issue is in the known issues list It is not. # OS/Distribution Debian 12.8 Arch Linux (Rolling Release) # Logs ## Do you have a problem while adding before connecting? No. ## Do you have a problem during or after connecting? When connected using the OpenVPN protocol, the shown Downloaded/Uploaded statistics are wrong. The shown statistics are from the "primary interface" through which the OpenVPN connection was established, and not the tunnel interface. This is not noticeable with a full-tunnel profile where all the traffic goes though the tunnel, but very noticeable with a split-tunnel profile where only certain traffic (ip prefixes) is routed through the tunnel. I could confirm/reproduce this on multiple different systems. Attached you find a screenshot from a fresh vanilla Debian 12.8 system. You can see that the shown statistics in the eduVPN client are (roughly) those of the primary ethernet interface `enp3s0` (1231306048 B are ~1.147 GiB, and 881471384 B are ~840.6 MiB), and not of the tunnel interface `tun0`. So those statistics currently don't show the amount of data that actually was sent through the tunnel. Disclaimer: I have not tested how this behaves with Wireguard. <hr /> The eduVPN-Client uses the first device associated with the "eduVPN" connection in NetworkManager that it creates/activates/monitors. The only device there is the primary interface, and not the tunnel interface. I also found no other attribute which directly links the NetworkManager vpn connection to the associated tunnel interface. Looking at the DBus messages from the official NetworkManager OpenVPN plugin to the NetworkManager daemon, you can see this information is provided in [the `SetConfig` call](https://gitlab.gnome.org/GNOME/NetworkManager-openvpn/-/blob/1.12.0/src/nm-openvpn-service-openvpn-helper.c?ref_type=tags#L564) using the [`tundev`](https://github.com/NetworkManager/NetworkManager/blob/1.52.0/src/libnm-core-public/nm-vpn-dbus-interface.h#L180) attribute. And while I could verify that this info is [correctly stored internally](https://github.com/NetworkManager/NetworkManager/blob/1.52.0/src/core/vpn/nm-vpn-connection.c#L1803), I found no place where it is exposed; via an API or otherwise. ``` ‣ Type=method_call Endian=l Flags=0 Version=1 Cookie=7 Timestamp="Thu 2025年06月05日 15:26:30.349343 UTC" Sender=:1.4258 Destination=:1.4257 Path=/org/freedesktop/NetworkManager/VPN/Plugin Interface=org.freedesktop.NetworkManager.VPN.Plugin Member=SetConfig UniqueName=:1.4258 MESSAGE "a{sv}" { ARRAY "{sv}" { DICT_ENTRY "sv" { STRING "gateway"; VARIANT "u" { UINT32 2130706433; }; }; DICT_ENTRY "sv" { STRING "can-persist"; VARIANT "b" { BOOLEAN true; }; }; DICT_ENTRY "sv" { STRING "tundev"; VARIANT "s" { STRING "tun0"; }; }; DICT_ENTRY "sv" { STRING "mtu"; VARIANT "u" { UINT32 1380; }; }; DICT_ENTRY "sv" { STRING "has-ip4"; VARIANT "b" { BOOLEAN true; }; }; }; }; ``` The only way I found right now to get the right tunnel interface is to match using the assigned IP address. Below a quick-and-dirty Proof-of-Concept. ```diff diff --git a/eduvpn/nm.py b/eduvpn/nm.py index ac443b2..e4c451a 100644 --- a/eduvpn/nm.py +++ b/eduvpn/nm.py @@ -248,6 +248,25 @@ class NMManager: if not active_con: return None + if active_con.get_connection_type() == 'vpn': + needle_ipv4 = self.ipv4 + if not needle_ipv4: + return None + for haystack_con in self.client.get_active_connections(): + haystack_ipv4 = haystack_con.get_ip4_config() + if not haystack_ipv4: + continue + haystack_ipv4 = haystack_ipv4.get_addresses() + if not haystack_ipv4: + continue + haystack_ipv4 = con4[0].get_address() + + if haystack_con.get_uuid() != active_con.get_uuid() and haystack_ipv4 == needle_ipv4: + active_con = haystack_con + break + else: + return None + devices = active_con.get_devices() if not devices: return None ``` <hr /> As a side note, while debugging I found that the (superfluous?) [`iface` check in `NMManager.get_stats_bytes()`](https://codeberg.org/eduVPN/linux-app/src/tag/4.5.1/eduvpn/nm.py#L145) triggers resolution of the `iface` property twice per second, which makes the caching in [`NetworkStats`](https://codeberg.org/eduVPN/linux-app/src/tag/4.5.1/eduvpn/ui/stats.py#L59) a bit pointless.

Thanks for this, I made an issue about this exact topic at NM before: https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1025

I also think there is no other way, so I will think about your patch

Thanks for this, I made an issue about this exact topic at NM before: https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1025 I also think there is no other way, so I will think about your patch
Author
Copy link

I agree that fixing this in NetworkManager is the right way. But even if done now, it would takes years to land in all the major distributions; and even then not in the existing LTS ones. That's why I focused on working with NM how it is right now.

And just for completeness: The VPN.CFG[7]: dev = tun line/attribute in the NM connection config is literally the dev configuration directive from OpenVPN, which means "use the first available tun device using the standard naming scheme (tunX where X is a number)". And I just realized, this may be a way to work around the issue: When creating the connection in NM, change the value from dev = tun to some static interface name like dev = tun-eduvpn (or even just eduvpn). That way the interface name is no longer dynamic and even known beforehand.

I agree that fixing this in NetworkManager is the right way. But even if done now, it would takes years to land in all the major distributions; and even then not in the existing LTS ones. That's why I focused on working with NM how it is right now. And just for completeness: The `VPN.CFG[7]: dev = tun` line/attribute in the NM connection config is literally the [`dev` configuration directive from OpenVPN](https://openvpn.net/community-resources/reference-manual-for-openvpn-2-6/#virtual-network-adapter-vpn-interface), which means "use the first available tun device using the standard naming scheme (`tunX` where X is a number)". And I just realized, this may be a way to work around the issue: When creating the connection in NM, change the value from `dev = tun` to some static interface name like `dev = tun-eduvpn` (or even just `eduvpn`). That way the interface name is no longer dynamic and even known beforehand.

fix has been committed, will be in a new version this week

fix has been committed, will be in a new version this week
Sign in to join this conversation.
No Branch/Tag specified
master
upstream-deb
openvpn-persistent
1.x
4.7.2
4.7.1
4.7.0
4.6.0
4.5.1
4.5.0
4.4.99.0
4.4.0
4.3.1
4.3.0
4.2.99.1
4.2.99.0
4.2.1
4.2.0
4.1.99.0
4.1.3
4.1.2
4.1.1
4.1.0
3.1.1
4.0.1
4.0.0
pr-3.3.1
pr-3.3.0
pr-3.2.0
3.1.0
3.0.0
2.2.1
2.2.0
2.1.0
2.0.0
1.1
1.0.3
1.0.2
1.0.1
1.0rc17
1.0rc16
1.0rc15
1.0rc14
1.0rc13
1.0rc12
1.0rc11
1.0rc10
1.0rc9
1.0rc8
1.0rc7
1.0rc6
1.0rc5
1.0rc4
1.0rc3
1.0rc2
1.0rc1
0.8
0.7.2
0.7.1
0.7
0.6.1
0.6
0.5.1
0.5
0.3
0.2.1
0.2
0.1.1
0.1
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
eduVPN/linux-app#646
Reference in a new issue
eduVPN/linux-app
No description provided.
Delete branch "%!s()"

Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?