From 1c8d29812ac766df7a7172da4f02b3513cc990a4 Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Mon, 27 Jul 2026 14:48:55 -0400 Subject: [PATCH 1/2] chore: update CHANGELOG.md --- nmrs/CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nmrs/CHANGELOG.md b/nmrs/CHANGELOG.md index 10221edf..93e3b4b5 100644 --- a/nmrs/CHANGELOG.md +++ b/nmrs/CHANGELOG.md @@ -10,10 +10,14 @@ All notable changes to the `nmrs` crate will be documented in this file. passwords and private keys from debug output, and connection tracing no longer logs raw settings, without changing public field types. ([#508](https://github.com/freedesktop-rs/nmrs/pull/508)) - Non-palindromic IPv4 DNS server addresses are no longer rejected by the - OpenVPN and WireGuard builders. ([#509](https://github.com/freedesktop-rs/nmrs/pull/519)) + OpenVPN and WireGuard builders. ([#519](https://github.com/freedesktop-rs/nmrs/pull/519)) ### Fixed +- Classify an active VPN as `ActiveConnection::Vpn` even when NetworkManager + parents it on the physical device it tunnels over, so OpenVPN-style tunnels + over Ethernet are no longer reported as + `ActiveConnection::Wired`. ([#520](https://github.com/freedesktop-rs/nmrs/pull/520)) - Resolve Bluetooth devices through the BlueZ adapter that owns their address instead of assuming the adapter is `hci0`. ([#501](https://github.com/freedesktop-rs/nmrs/pull/501)) From 1fb8d94bea2436ab84fd6ec56c7159d5906011ee Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Mon, 27 Jul 2026 14:49:29 -0400 Subject: [PATCH 2/2] fix: classify VPNs by connection type before device type NetworkManager parents a VPNs active connection on the physical device it tunnels over, so an active OpenVPN connection on enp42s0 reports device type ETHERNET. active_connection_kind() matched the device type first, classifying such a VPN as ActiveConnection::Wired instead of ActiveConnection::Vpn. Consumers that filter active connections to the Vpn variant never saw the connection, so the UI showed "Connect" for an already-connected VPN, clicking it re-activated a live connection as a no-op, and Disconnect was never offered. WireGuard was unaffected because its device type is not wired and it fell through to the connection-type check. Check for the vpn and wireguard connection types up front and only then fall back to device-type classification. WireGuard behavior is unchanged but now matches by rule rather than by fallthrough. The classifies_by_device_type_first test asserted the buggy contract ((ETHERNET, "vpn") -> Wired) and is replaced by two tests covering the corrected precedence and the unchanged non-VPN device-type paths. --- nmrs/src/core/active_connection.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/nmrs/src/core/active_connection.rs b/nmrs/src/core/active_connection.rs index 25b7143d..45735144 100644 --- a/nmrs/src/core/active_connection.rs +++ b/nmrs/src/core/active_connection.rs @@ -367,10 +367,16 @@ fn active_connection_kind( raw_device_type: Option, connection_type: Option<&str>, ) -> ActiveConnectionKind { + // NetworkManager parents a VPN's active connection on the device it tunnels + // over, so an OpenVPN tunnel over Ethernet reports device type ETHERNET. The + // connection type has to be checked first or such a VPN classifies as Wired. + if matches!(connection_type, Some("vpn" | "wireguard")) { + return ActiveConnectionKind::Vpn; + } + match raw_device_type { Some(raw_type) if device_type_registry::is_wired(raw_type) => ActiveConnectionKind::Wired, Some(device_type::WIFI) => ActiveConnectionKind::Wifi, - _ if matches!(connection_type, Some("vpn" | "wireguard")) => ActiveConnectionKind::Vpn, _ => ActiveConnectionKind::Other, } } @@ -381,9 +387,25 @@ mod tests { use std::collections::HashMap; #[test] - fn classifies_by_device_type_first() { + fn classifies_vpn_carried_over_a_physical_device_as_vpn() { assert_eq!( active_connection_kind(Some(device_type::ETHERNET), Some("vpn")), + ActiveConnectionKind::Vpn + ); + assert_eq!( + active_connection_kind(Some(device_type::WIFI), Some("vpn")), + ActiveConnectionKind::Vpn + ); + assert_eq!( + active_connection_kind(Some(device_type::ETHERNET), Some("wireguard")), + ActiveConnectionKind::Vpn + ); + } + + #[test] + fn classifies_non_vpn_connections_by_device_type() { + assert_eq!( + active_connection_kind(Some(device_type::ETHERNET), Some("802-3-ethernet")), ActiveConnectionKind::Wired ); assert_eq!( @@ -391,7 +413,7 @@ mod tests { ActiveConnectionKind::Wired ); assert_eq!( - active_connection_kind(Some(device_type::WIFI), Some("vpn")), + active_connection_kind(Some(device_type::WIFI), Some("802-11-wireless")), ActiveConnectionKind::Wifi ); }