bridge: force (hack)-set of the MTU when explicitly set in the profile

Kernel does a auto-mtu adjusting process whenever a port is added/removed from
the bridge, this can cause issues when NM wants to explicitly set an MTU which is
equal to the bridge default one (1500) because if later a port is added with a
different MTU the kernel will assign the bridge that port's MTU resulting in the bridge
runtime configuration differing from the bridge's NM connection profile.

What we can do is to always apply the MTU manually for the bridge (if explicitly
set by the profile), after doing so the kernel won't modify the MTU anymore,
which is what we want, problem is that kernel won't actually apply the MTU
to the netdev if it's not actually changing so we first apply it to
MTU-1 and then to the desired value.

https://bugzilla.redhat.com/show_bug.cgi?id=1778590

Signed-off-by: Antonio Cardace <acardace@redhat.com>
This commit is contained in:
Antonio Cardace 2020-10-21 18:57:18 +02:00
parent 516c623618
commit e23798a5e5
No known key found for this signature in database
GPG key ID: 6BF80ABD43E377D3
3 changed files with 34 additions and 0 deletions

View file

@ -1103,6 +1103,12 @@ create_and_realize(NMDevice * device,
to_sysfs_group_address_sys(nm_setting_bridge_get_group_address(s_bridge), &props.group_addr);
/* If mtu != 0, we set the MTU of the new bridge at creation time. However, kernel will still
* automatically adjust the MTU of the bridge based on the minimum of the slave's MTU.
* We don't want this automatism as the user asked for a fixed MTU.
*
* To workaround this behavior of kernel, we will later toggle the MTU twice. See
* NMDeviceClass.mtu_force_set. */
r = nm_platform_link_bridge_add(nm_device_get_platform(device),
iface,
hwaddr ? mac_address : NULL,
@ -1159,6 +1165,7 @@ nm_device_bridge_class_init(NMDeviceBridgeClass *klass)
device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_BRIDGE);
device_class->is_master = TRUE;
device_class->mtu_force_set = TRUE;
device_class->get_generic_capabilities = get_generic_capabilities;
device_class->check_connection_compatible = check_connection_compatible;
device_class->check_connection_available = check_connection_available;

View file

@ -661,6 +661,8 @@ typedef struct _NMDevicePrivate {
guint64 tx_bytes;
guint64 rx_bytes;
} stats;
bool mtu_force_set_done : 1;
} NMDevicePrivate;
G_DEFINE_ABSTRACT_TYPE(NMDevice, nm_device, NM_TYPE_DBUS_OBJECT)
@ -10419,6 +10421,18 @@ _commit_mtu(NMDevice *self, const NMIP4Config *config)
}
}
if (mtu_desired && NM_DEVICE_GET_CLASS(self)->mtu_force_set && !priv->mtu_force_set_done) {
priv->mtu_force_set_done = TRUE;
if (mtu_desired == mtu_plat) {
mtu_plat--;
if (NM_DEVICE_GET_CLASS(self)->set_platform_mtu(self, mtu_desired - 1)) {
_LOGD(LOGD_DEVICE, "mtu: force-set MTU to %u", mtu_desired - 1);
} else
_LOGW(LOGD_DEVICE, "mtu: failure to force-set MTU to %u", mtu_desired - 1);
}
}
_LOGT(LOGD_DEVICE,
"mtu: device-mtu: %u%s, ipv6-mtu: %u%s, ifindex: %d",
(guint) mtu_desired,
@ -15644,6 +15658,8 @@ _cleanup_generic_post(NMDevice *self, CleanupType cleanup_type)
priv->linklocal6_dad_counter = 0;
priv->mtu_force_set_done = FALSE;
/* Clean up IP configs; this does not actually deconfigure the
* interface; the caller must flush routes and addresses explicitly.
*/

View file

@ -232,6 +232,17 @@ typedef struct _NMDeviceClass {
* type (NMDeviceClass), not the actual device instance. */
bool is_master : 1;
/* Force setting the MTU actually means first setting the MTU
* to (desired_MTU-1) and then setting the desired_MTU
* so that kernel actually applies the MTU, otherwise
* kernel will ignore the request if the link's MTU is the
* same as the desired one.
*
* This is just a workaround made for bridges (ATM) that employ
* a auto-MTU adjust mechanism if no MTU is manually set.
*/
bool mtu_force_set : 1;
void (*state_changed)(NMDevice * device,
NMDeviceState new_state,
NMDeviceState old_state,