device: support reapplying bridge-port VLANs

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 <ihuguet@redhat.com>
This commit is contained in:
Beniamino Galvani
2024-07-25 18:32:24 +02:00
committed by Íñigo Huguet
parent e00c81b153
commit c5d1e35f99
4 changed files with 92 additions and 0 deletions

1
NEWS
View File

@@ -23,6 +23,7 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
* ndisc: Support multiple gateways for a single network
* wifi: Support configuring channel-width in AP mode
* keyfile: Stop writing offensive terms into keyfiles
* Support reapplying the VLANs on bridge ports.
=============================================
NetworkManager-1.48

View File

@@ -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)
{

View File

@@ -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__ */

View File

@@ -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(