From 4ba469e429c4d0e75f187661c7314e368ee56cc9 Mon Sep 17 00:00:00 2001 From: Apostolis Hardalias Date: Wed, 16 Sep 2026 20:11:51 +0300 Subject: [PATCH] fix(nm): support VLAN interfaces in NetworkManager backend NetworkManager VLAN interfaces (NM.DeviceType.VLAN / NM.SETTING_VLAN_SETTING_NAME) were previously excluded from physical device discovery and parent connection resolution. When the primary network interface is a VLAN trunk (where the parent ethernet device has no default gateway and the active default route lives on a VLAN sub-interface like enp8s0.10), WireGuard connection setup failed with GatewayNotFoundError and timed out due to the killswitch dummy interface blocking reachability checks. This adds NM.DeviceType.VLAN and NM.SETTING_VLAN_SETTING_NAME to the NetworkManager backend device filters and physical connection checks. --- proton/vpn/backend/networkmanager/core/nmclient.py | 3 ++- .../networkmanager/killswitch/wireguard/nmclient.py | 12 +++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/proton/vpn/backend/networkmanager/core/nmclient.py b/proton/vpn/backend/networkmanager/core/nmclient.py index 6fd4bfa..b41c990 100644 --- a/proton/vpn/backend/networkmanager/core/nmclient.py +++ b/proton/vpn/backend/networkmanager/core/nmclient.py @@ -303,7 +303,8 @@ def is_physical_connection(active_connection): NM.SETTING_WIRELESS_SETTING_NAME, # Wifi NM.SETTING_GSM_SETTING_NAME, # USB+Dongle NM.SETTING_CDMA_SETTING_NAME, # USB+Dongle - NM.SETTING_BRIDGE_SETTING_NAME # Bridge + NM.SETTING_BRIDGE_SETTING_NAME, # Bridge + NM.SETTING_VLAN_SETTING_NAME # VLAN ) for active_connection in self._nm_client.get_active_connections(): diff --git a/proton/vpn/backend/networkmanager/killswitch/wireguard/nmclient.py b/proton/vpn/backend/networkmanager/killswitch/wireguard/nmclient.py index 24ed342..85663c9 100644 --- a/proton/vpn/backend/networkmanager/killswitch/wireguard/nmclient.py +++ b/proton/vpn/backend/networkmanager/killswitch/wireguard/nmclient.py @@ -215,10 +215,14 @@ def _add_connection_async(): return future_conn_activated def get_physical_devices(self) -> List[NM.Device]: - """Returns all the active ethernet/wifi devices.""" + """Returns all the active ethernet/wifi/vlan devices.""" return [ device for device in self._nm_client.get_devices() if ( - device.get_device_type() in (NM.DeviceType.ETHERNET, NM.DeviceType.WIFI) + device.get_device_type() in ( + NM.DeviceType.ETHERNET, + NM.DeviceType.WIFI, + NM.DeviceType.VLAN, + ) and device.get_state() is NM.DeviceState.ACTIVATED and device.get_active_connection() # Maybe this is redundant. ) @@ -226,7 +230,9 @@ def get_physical_devices(self) -> List[NM.Device]: def _is_ethernet_or_wifi_connection(self, active_connection: NM.ActiveConnection): return active_connection.props.type in ( - NM.SETTING_WIRED_SETTING_NAME, NM.SETTING_WIRELESS_SETTING_NAME + NM.SETTING_WIRED_SETTING_NAME, + NM.SETTING_WIRELESS_SETTING_NAME, + NM.SETTING_VLAN_SETTING_NAME, ) def get_ethernet_and_wifi_connections(self) -> List[NM.ActiveConnection]: