diff options
Diffstat (limited to 'src/core/devices/nm-device-bridge.c')
| -rw-r--r-- | src/core/devices/nm-device-bridge.c | 180 |
1 files changed, 149 insertions, 31 deletions
diff --git a/src/core/devices/nm-device-bridge.c b/src/core/devices/nm-device-bridge.c index b256056d..7a496d96 100644 --- a/src/core/devices/nm-device-bridge.c +++ b/src/core/devices/nm-device-bridge.c @@ -13,6 +13,7 @@ #include "NetworkManagerUtils.h" #include "nm-device-private.h" #include "libnm-platform/nm-platform.h" +#include "libnm-platform/nm-platform-utils.h" #include "nm-device-factory.h" #include "libnm-core-aux-intern/nm-libnm-core-utils.h" #include "libnm-core-intern/nm-core-internal.h" @@ -419,20 +420,18 @@ static const Option controller_options[] = { 0, }}; -static const NMPlatformBridgeVlan ** -setting_vlans_to_platform(GPtrArray *array) +static NMPlatformBridgeVlan * +setting_vlans_to_platform(GPtrArray *array, guint *out_len) { - NMPlatformBridgeVlan **arr; - NMPlatformBridgeVlan *p_data; - guint i; + NMPlatformBridgeVlan *arr; + guint i; - if (!array || !array->len) + if (!array || !array->len) { + *out_len = 0; return NULL; + } - G_STATIC_ASSERT_EXPR(_nm_alignof(NMPlatformBridgeVlan *) >= _nm_alignof(NMPlatformBridgeVlan)); - arr = g_malloc((sizeof(NMPlatformBridgeVlan *) * (array->len + 1)) - + (sizeof(NMPlatformBridgeVlan) * (array->len))); - p_data = (NMPlatformBridgeVlan *) &arr[array->len + 1]; + arr = g_new(NMPlatformBridgeVlan, array->len); for (i = 0; i < array->len; i++) { NMBridgeVlan *vlan = array->pdata[i]; @@ -440,16 +439,16 @@ setting_vlans_to_platform(GPtrArray *array) nm_bridge_vlan_get_vid_range(vlan, &vid_start, &vid_end); - p_data[i] = (NMPlatformBridgeVlan){ + arr[i] = (NMPlatformBridgeVlan){ .vid_start = vid_start, .vid_end = vid_end, .pvid = nm_bridge_vlan_is_pvid(vlan), .untagged = nm_bridge_vlan_is_untagged(vlan), }; - arr[i] = &p_data[i]; } - arr[i] = NULL; - return (const NMPlatformBridgeVlan **) arr; + + *out_len = array->len; + return arr; } static void @@ -639,15 +638,16 @@ is_bridge_pvid_changed(NMDevice *device, NMSettingBridge *s_bridge) static gboolean bridge_set_vlan_options(NMDevice *device, NMSettingBridge *s_bridge, gboolean is_reapply) { - NMDeviceBridge *self = NM_DEVICE_BRIDGE(device); - gconstpointer hwaddr; - size_t length; - gboolean enabled; - guint16 pvid; - NMPlatform *plat; - int ifindex; - gs_unref_ptrarray GPtrArray *vlans = NULL; - gs_free const NMPlatformBridgeVlan **plat_vlans = NULL; + NMDeviceBridge *self = NM_DEVICE_BRIDGE(device); + gconstpointer hwaddr; + size_t length; + gboolean enabled; + guint16 pvid; + NMPlatform *plat; + int ifindex; + gs_unref_ptrarray GPtrArray *vlans = NULL; + gs_free NMPlatformBridgeVlan *plat_vlans = NULL; + guint num_vlans; if (self->vlan_configured) return TRUE; @@ -664,7 +664,7 @@ bridge_set_vlan_options(NMDevice *device, NMSettingBridge *s_bridge, gboolean is .vlan_filtering_val = FALSE, .vlan_default_pvid_has = TRUE, .vlan_default_pvid_val = 1})); - nm_platform_link_set_bridge_vlans(plat, ifindex, FALSE, NULL); + nm_platform_link_set_bridge_vlans(plat, ifindex, FALSE, NULL, 0); return TRUE; } @@ -696,7 +696,7 @@ bridge_set_vlan_options(NMDevice *device, NMSettingBridge *s_bridge, gboolean is .vlan_default_pvid_val = 0})); /* Clear all existing VLANs */ - if (!nm_platform_link_set_bridge_vlans(plat, ifindex, FALSE, NULL)) + if (!nm_platform_link_set_bridge_vlans(plat, ifindex, FALSE, NULL, 0)) return FALSE; /* Now set the default PVID. After this point the kernel creates @@ -714,8 +714,9 @@ bridge_set_vlan_options(NMDevice *device, NMSettingBridge *s_bridge, gboolean is /* Create VLANs only after setting the default PVID, so that * any PVID VLAN overrides the bridge's default PVID. */ g_object_get(s_bridge, NM_SETTING_BRIDGE_VLANS, &vlans, NULL); - plat_vlans = setting_vlans_to_platform(vlans); - if (plat_vlans && !nm_platform_link_set_bridge_vlans(plat, ifindex, FALSE, plat_vlans)) + plat_vlans = setting_vlans_to_platform(vlans, &num_vlans); + if (plat_vlans + && !nm_platform_link_set_bridge_vlans(plat, ifindex, FALSE, plat_vlans, num_vlans)) return FALSE; nm_platform_link_set_bridge_info(plat, @@ -728,6 +729,121 @@ 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) +{ + NMDevice *self = device; /* for logging */ + NMSettingBridgePort *s_bridge_port; + NMDevice *controller; + NMSettingBridge *s_bridge; + gs_unref_ptrarray GPtrArray *tmp_vlans = NULL; + gs_free NMPlatformBridgeVlan *setting_vlans = NULL; + gs_free NMPlatformBridgeVlan *plat_vlans = NULL; + guint num_setting_vlans = 0; + guint num_plat_vlans = 0; + NMPlatform *plat; + int ifindex; + gboolean do_reapply; + + 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); + + if (!nm_platform_link_get_bridge_vlans(plat, ifindex, &plat_vlans, &num_plat_vlans)) { + _LOGD(LOGD_DEVICE, "reapply-bridge-port-vlans: can't get current VLANs from platform"); + do_reapply = TRUE; + } else { + nmp_utils_bridge_vlan_normalize(setting_vlans, &num_setting_vlans); + nmp_utils_bridge_vlan_normalize(plat_vlans, &num_plat_vlans); + if (!nmp_utils_bridge_normalized_vlans_equal(setting_vlans, + num_setting_vlans, + plat_vlans, + num_plat_vlans)) { + _LOGD(LOGD_DEVICE, "reapply-bridge-port-vlans: VLANs in platform need reapply"); + do_reapply = TRUE; + } else { + _LOGD(LOGD_DEVICE, "reapply-bridge-port-vlans: VLANs in platform didn't change"); + do_reapply = FALSE; + } + } + + if (do_reapply) { + 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) { @@ -937,13 +1053,14 @@ attach_port(NMDevice *device, bridge_set_vlan_options(device, s_bridge, FALSE); if (nm_setting_bridge_get_vlan_filtering(s_bridge)) { - gs_free const NMPlatformBridgeVlan **plat_vlans = NULL; - gs_unref_ptrarray GPtrArray *vlans = NULL; + gs_free NMPlatformBridgeVlan *plat_vlans = NULL; + gs_unref_ptrarray GPtrArray *vlans = NULL; + guint num_vlans; if (s_port) g_object_get(s_port, NM_SETTING_BRIDGE_PORT_VLANS, &vlans, NULL); - plat_vlans = setting_vlans_to_platform(vlans); + plat_vlans = setting_vlans_to_platform(vlans, &num_vlans); /* Since the link was just enportd, there are no existing VLANs * (except for the default one) and so there's no need to flush. */ @@ -952,7 +1069,8 @@ attach_port(NMDevice *device, && !nm_platform_link_set_bridge_vlans(nm_device_get_platform(port), nm_device_get_ifindex(port), TRUE, - plat_vlans)) + plat_vlans, + num_vlans)) return FALSE; } |