[PATCH] netifd: Ensure custom MTU is respected for bridges

Kristian Evensen kristian.evensen at gmail.com
Thu Jul 9 08:04:39 EDT 2020


When an interface is added or removed from a bridge, the kernel updates
the MTU to ETH_DATA_LEN or the minimum MTU of the ports (see
br_min_mtu() in net/bridge/br_if.c). netifd already works around this
behavior by updating the MTU when an interface is added to a bridge.
However, remove is not handled.

This commit introduces a new callback in the device-struct named
update_mtu. If the callback is set, it is called from cb_rtnl_event for
devices with a custom MTU.

The bridge code has been extended to make use of this callback. If the
MTU received from the kernel differs from the custom MTU, the MTU of the
bridge device is update. The callback covers both the case when a device
is added and removed (NEWLINK events are received in both cases), so the
old work-around is removed.

Signed-off-by: Kristian Evensen <kristian.evensen at gmail.com>
---
 bridge.c       | 22 +++++++++++++---------
 device.h       |  2 ++
 system-linux.c |  3 +++
 3 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/bridge.c b/bridge.c
index c1f4ffa..8421366 100644
--- a/bridge.c
+++ b/bridge.c
@@ -304,15 +304,9 @@ bridge_member_cb(struct device_user *dev, enum device_event ev)
 
 		if (bst->n_present == 1)
 			device_set_present(&bst->dev, true);
-		if (bst->dev.active && !bridge_enable_member(bm)) {
-			/*
-			 * Adding a bridge member can overwrite the bridge mtu
-			 * in the kernel, apply the bridge settings in case the
-			 * bridge mtu is set
-			 */
-			system_if_apply_settings(&bst->dev, &bst->dev.settings,
-						 DEV_OPT_MTU | DEV_OPT_MTU6);
-		}
+
+		if (bst->dev.active)
+			bridge_enable_member(bm);
 
 		break;
 	case DEV_EVENT_REMOVE:
@@ -710,6 +704,14 @@ bridge_retry_members(struct uloop_timeout *timeout)
 	}
 }
 
+static void
+bridge_update_mtu(struct device *dev, uint32_t mtu)
+{
+	if (dev->settings.mtu != mtu || dev->settings.mtu6 != mtu)
+		system_if_apply_settings(dev, &dev->settings, DEV_OPT_MTU |
+							      DEV_OPT_MTU6);
+}
+
 static struct device *
 bridge_create(const char *name, struct device_type *devtype,
 	struct blob_attr *attr)
@@ -735,6 +737,8 @@ bridge_create(const char *name, struct device_type *devtype,
 	bst->set_state = dev->set_state;
 	dev->set_state = bridge_set_state;
 
+	dev->update_mtu = bridge_update_mtu;
+
 	dev->hotplug_ops = &bridge_ops;
 
 	vlist_init(&bst->members, avl_strcmp, bridge_member_update);
diff --git a/device.h b/device.h
index 5f3fae2..382af81 100644
--- a/device.h
+++ b/device.h
@@ -25,6 +25,7 @@ struct device_hotplug_ops;
 struct interface;
 
 typedef int (*device_state_cb)(struct device *, bool up);
+typedef void (*device_mtu_cb)(struct device *, uint32_t mtu);
 
 enum {
 	DEV_ATTR_TYPE,
@@ -213,6 +214,7 @@ struct device {
 
 	/* set interface up or down */
 	device_state_cb set_state;
+	device_mtu_cb update_mtu;
 
 	const struct device_hotplug_ops *hotplug_ops;
 
diff --git a/system-linux.c b/system-linux.c
index 3b09bbb..54a35d0 100644
--- a/system-linux.c
+++ b/system-linux.c
@@ -597,6 +597,9 @@ static int cb_rtnl_event(struct nl_msg *msg, void *arg)
 
 	device_set_link(dev, link_state ? true : false);
 
+	if (nla[IFLA_MTU] && (dev->settings.flags & DEV_OPT_MTU) &&
+	    dev->update_mtu)
+		dev->update_mtu(dev, nla_get_u32(nla[IFLA_MTU]));
 out:
 	return 0;
 }
-- 
2.20.1




More information about the openwrt-devel mailing list