From cb5e3ceea74a3c3e78ac68f1d235333ae195a84b Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 25 Jul 2024 18:32:24 +0200 Subject: [PATCH] device: support reapplying bridge-port VLANs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For now, always reapply the VLANs unconditionally, even if they didn't change in kernel. To set again the VLANs on the port we need to clear all the existing one before. However, this deletes also the VLAN for the default-pvid on the bridge. Therefore, we need some additional logic to inject the default-pvid in the list of VLANs. Co-authored-by: Íñigo Huguet (cherry picked from commit c5d1e35f993e8d3aca3aac23035663cf95df840b) --- NEWS | 1 + src/core/devices/nm-device-bridge.c | 87 +++++++++++++++++++++++++++++ src/core/devices/nm-device-bridge.h | 2 + src/core/devices/nm-device.c | 2 + 4 files changed, 92 insertions(+) diff --git a/NEWS b/NEWS index c1c87b6fe7..7c567ca763 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ Overview of changes since NetworkManager-1.48.2 addresses configured on interfaces, NetworkManager now takes into account the content of /etc/hosts. * keyfile: Stop writing offensive terms into keyfiles +* Support reapplying the VLANs on bridge ports. =============================================== NetworkManager-1.48.2 diff --git a/src/core/devices/nm-device-bridge.c b/src/core/devices/nm-device-bridge.c index cde94a4264..7628e4600f 100644 --- a/src/core/devices/nm-device-bridge.c +++ b/src/core/devices/nm-device-bridge.c @@ -728,6 +728,93 @@ bridge_set_vlan_options(NMDevice *device, NMSettingBridge *s_bridge, gboolean is return TRUE; } +static NMPlatformBridgeVlan * +merge_bridge_vlan_default_pvid(NMPlatformBridgeVlan *vlans, guint *num_vlans, guint default_pvid) +{ + NMPlatformBridgeVlan *vlan; + gboolean has_pvid = FALSE; + guint i; + + for (i = 0; i < *num_vlans; i++) { + if (vlans[i].pvid) { + has_pvid = TRUE; + break; + } + } + + /* search if the list of VLANs already contains the default PVID */ + vlan = NULL; + for (i = 0; i < *num_vlans; i++) { + if (default_pvid >= vlans[i].vid_start && default_pvid <= vlans[i].vid_end) { + vlan = &vlans[i]; + break; + } + } + + if (!vlan) { + /* VLAN id not found, append the default PVID at the end. + * Set the PVID flag only if the port didn't have one. */ + vlans = g_realloc_n(vlans, *num_vlans + 1, sizeof(NMPlatformBridgeVlan)); + (*num_vlans)++; + vlans[*num_vlans - 1] = (NMPlatformBridgeVlan){ + .vid_start = default_pvid, + .vid_end = default_pvid, + .untagged = TRUE, + .pvid = !has_pvid, + }; + } + + return vlans; +} + +void +nm_device_reapply_bridge_port_vlans(NMDevice *device) +{ + NMSettingBridgePort *s_bridge_port; + NMDevice *controller; + NMSettingBridge *s_bridge; + gs_unref_ptrarray GPtrArray *tmp_vlans = NULL; + gs_free NMPlatformBridgeVlan *setting_vlans = NULL; + guint num_setting_vlans = 0; + NMPlatform *plat; + int ifindex; + + s_bridge_port = nm_device_get_applied_setting(device, NM_TYPE_SETTING_BRIDGE_PORT); + if (!s_bridge_port) + return; + + controller = nm_device_get_controller(device); + if (!controller) + return; + + s_bridge = nm_device_get_applied_setting(controller, NM_TYPE_SETTING_BRIDGE); + if (!s_bridge) + return; + + if (nm_setting_bridge_get_vlan_filtering(s_bridge)) { + g_object_get(s_bridge_port, NM_SETTING_BRIDGE_PORT_VLANS, &tmp_vlans, NULL); + setting_vlans = setting_vlans_to_platform(tmp_vlans, &num_setting_vlans); + + /* During a regular activation, we first set the default_pvid on the bridge + * (which creates the PVID VLAN on the port) and then add the VLANs on the port. + * This ensures that the PVID VLAN is inherited from the bridge, but it's + * overridden if the port specifies one. + * During a reapply on the port, we are not going to touch the bridge and + * so we need to merge manually the PVID from the bridge with the port VLANs. */ + setting_vlans = + merge_bridge_vlan_default_pvid(setting_vlans, + &num_setting_vlans, + nm_setting_bridge_get_vlan_default_pvid(s_bridge)); + } + + plat = nm_device_get_platform(device); + ifindex = nm_device_get_ifindex(device); + + nm_platform_link_set_bridge_vlans(plat, ifindex, TRUE, NULL, 0); + if (num_setting_vlans > 0) + nm_platform_link_set_bridge_vlans(plat, ifindex, TRUE, setting_vlans, num_setting_vlans); +} + static void _platform_lnk_bridge_init_from_setting(NMSettingBridge *s_bridge, NMPlatformLnkBridge *props) { diff --git a/src/core/devices/nm-device-bridge.h b/src/core/devices/nm-device-bridge.h index 6d9f1614c8..f9be7580bb 100644 --- a/src/core/devices/nm-device-bridge.h +++ b/src/core/devices/nm-device-bridge.h @@ -27,4 +27,6 @@ extern const NMBtVTableNetworkServer *nm_bt_vtable_network_server; void _nm_device_bridge_notify_unregister_bt_nap(NMDevice *device, const char *reason); +void nm_device_reapply_bridge_port_vlans(NMDevice *device); + #endif /* __NETWORKMANAGER_DEVICE_BRIDGE_H__ */ diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 725241d5b6..799aca047c 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -13881,6 +13881,8 @@ check_and_reapply_connection(NMDevice *self, if (priv->state >= NM_DEVICE_STATE_ACTIVATED) nm_device_update_metered(self); + nm_device_reapply_bridge_port_vlans(self); + sett_conn = nm_device_get_settings_connection(self); if (sett_conn) { nm_settings_connection_autoconnect_blocked_reason_set(