From f6eaaae37aed43535caf0b71858eaa1a5fd70bce Mon Sep 17 00:00:00 2001 From: Gleb Pesin Date: Fri, 7 Aug 2026 17:01:34 +0000 Subject: [PATCH 1/3] rpcd-mod-luci: expose device MTU limits Query RTM_GETLINK once and include the kernel-reported minimum and maximum MTU values in getNetworkDevices. Signed-off-by: Gleb Pesin --- libs/rpcd-mod-luci/Makefile | 2 +- libs/rpcd-mod-luci/src/luci.c | 149 +++++++++++++++++++++++++++++++++- 2 files changed, 148 insertions(+), 3 deletions(-) diff --git a/libs/rpcd-mod-luci/Makefile b/libs/rpcd-mod-luci/Makefile index c5ed10196e40..3b8b58c4616f 100644 --- a/libs/rpcd-mod-luci/Makefile +++ b/libs/rpcd-mod-luci/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=rpcd-mod-luci -PKG_VERSION:=20240305 +PKG_VERSION:=20260807 PKG_RELEASE:=1 PKG_MAINTAINER:=Jo-Philipp Wich diff --git a/libs/rpcd-mod-luci/src/luci.c b/libs/rpcd-mod-luci/src/luci.c index 2abb5a81ff5c..3829d640acef 100644 --- a/libs/rpcd-mod-luci/src/luci.c +++ b/libs/rpcd-mod-luci/src/luci.c @@ -644,10 +644,138 @@ lease_next(void) } +struct network_device_mtu_limit { + struct network_device_mtu_limit *next; + char name[IFNAMSIZ]; + uint32_t min_mtu; + uint32_t max_mtu; +}; + +struct network_device_mtu_context { + struct network_device_mtu_limit *limits; + int pending; +}; + +static int +rpc_luci_parse_network_device_mtu_limits(struct nl_msg *msg, void *arg) +{ + struct network_device_mtu_context *ctx = arg; + struct nlmsghdr *hdr = nlmsg_hdr(msg); + struct ifinfomsg *ifi = NLMSG_DATA(hdr); + struct network_device_mtu_limit *limit; + struct nlattr *tb[IFLA_MAX + 1]; + + if (hdr->nlmsg_type != RTM_NEWLINK) + return NL_SKIP; + + nlmsg_parse(hdr, sizeof(*ifi), tb, IFLA_MAX, NULL); + + if (!tb[IFLA_IFNAME] || (!tb[IFLA_MIN_MTU] && !tb[IFLA_MAX_MTU])) + return NL_SKIP; + + limit = calloc(1, sizeof(*limit)); + if (!limit) + return NL_SKIP; + + snprintf(limit->name, sizeof(limit->name), "%s", + nla_get_string(tb[IFLA_IFNAME])); + + if (tb[IFLA_MIN_MTU]) + limit->min_mtu = nla_get_u32(tb[IFLA_MIN_MTU]); + + if (tb[IFLA_MAX_MTU]) + limit->max_mtu = nla_get_u32(tb[IFLA_MAX_MTU]); + + limit->next = ctx->limits; + ctx->limits = limit; + + return NL_SKIP; +} + +static int +rpc_luci_network_device_mtu_done(struct nl_msg *msg, void *arg) +{ + struct network_device_mtu_context *ctx = arg; + + ctx->pending = 0; + return NL_STOP; +} + +static int +rpc_luci_network_device_mtu_error(struct sockaddr_nl *nla, + struct nlmsgerr *err, void *arg) +{ + struct network_device_mtu_context *ctx = arg; + + ctx->pending = 0; + return NL_STOP; +} + +static void +rpc_luci_get_network_device_mtu_limits(struct network_device_mtu_context *ctx) +{ + struct ifinfomsg ifm = { .ifi_family = AF_UNSPEC }; + struct nl_sock *sock = NULL; + struct nl_msg *msg = NULL; + struct nl_cb *cb = NULL; + + sock = nl_socket_alloc(); + if (!sock || nl_connect(sock, NETLINK_ROUTE)) + goto out; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!cb) + goto out; + + msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP); + if (!msg) + goto out; + + nlmsg_append(msg, &ifm, sizeof(ifm), 0); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, + rpc_luci_parse_network_device_mtu_limits, ctx); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, + rpc_luci_network_device_mtu_done, ctx); + nl_cb_err(cb, NL_CB_CUSTOM, rpc_luci_network_device_mtu_error, ctx); + + ctx->pending = 1; + + if (nl_send_auto_complete(sock, msg) < 0) + ctx->pending = 0; + + while (ctx->pending && nl_recvmsgs(sock, cb) >= 0) + ; + +out: + if (sock) + nl_socket_free(sock); + + if (cb) + nl_cb_put(cb); + + if (msg) + nlmsg_free(msg); +} + +static struct network_device_mtu_limit * +rpc_luci_find_network_device_mtu_limit(struct network_device_mtu_context *ctx, + const char *name) +{ + struct network_device_mtu_limit *limit; + + for (limit = ctx->limits; limit; limit = limit->next) + if (!strcmp(limit->name, name)) + return limit; + + return NULL; +} + static void -rpc_luci_parse_network_device_sys(const char *name, struct ifaddrs *ifaddr) +rpc_luci_parse_network_device_sys(const char *name, struct ifaddrs *ifaddr, + struct network_device_mtu_context *mtu_ctx) { char link[64], buf[512], *p; + struct network_device_mtu_limit *mtu_limit; unsigned int ifa_flags = 0; struct sockaddr_ll *sll; struct ifaddrs *ifa; @@ -715,6 +843,13 @@ rpc_luci_parse_network_device_sys(const char *name, struct ifaddrs *ifaddr) if (n > 0) blobmsg_add_u32(&blob, "mtu", n); + mtu_limit = rpc_luci_find_network_device_mtu_limit(mtu_ctx, name); + if (mtu_limit && mtu_limit->min_mtu > 0) + blobmsg_add_u32(&blob, "min_mtu", mtu_limit->min_mtu); + + if (mtu_limit && mtu_limit->max_mtu > 0) + blobmsg_add_u32(&blob, "max_mtu", mtu_limit->max_mtu); + n = atoi(readstr("/sys/class/net/%s/tx_queue_len", name)); if (n > 0) blobmsg_add_u32(&blob, "qlen", n); @@ -863,11 +998,14 @@ rpc_luci_get_network_devices(struct ubus_context *ctx, const char *method, struct blob_attr *msg) { + struct network_device_mtu_context mtu_ctx = {}; + struct network_device_mtu_limit *limit; struct ifaddrs *ifaddr; struct dirent *e; DIR *d; blob_buf_init(&blob, 0); + rpc_luci_get_network_device_mtu_limits(&mtu_ctx); d = opendir("/sys/class/net"); @@ -882,7 +1020,8 @@ rpc_luci_get_network_devices(struct ubus_context *ctx, break; if (e->d_type != DT_DIR && e->d_type != DT_REG) - rpc_luci_parse_network_device_sys(e->d_name, ifaddr); + rpc_luci_parse_network_device_sys(e->d_name, ifaddr, + &mtu_ctx); } if (ifaddr != NULL) @@ -891,6 +1030,12 @@ rpc_luci_get_network_devices(struct ubus_context *ctx, closedir(d); } + while (mtu_ctx.limits) { + limit = mtu_ctx.limits; + mtu_ctx.limits = limit->next; + free(limit); + } + ubus_send_reply(ctx, req, blob.head); return 0; } From ac41ffcb867a5b9cce8456df04779a61e02c7582 Mon Sep 17 00:00:00 2001 From: Gleb Pesin Date: Fri, 7 Aug 2026 17:01:34 +0000 Subject: [PATCH 2/3] luci-base: expose device MTU limits Add accessors for the minimum and maximum MTU values returned by getNetworkDevices. Signed-off-by: Gleb Pesin --- .../htdocs/luci-static/resources/network.js | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/modules/luci-base/htdocs/luci-static/resources/network.js b/modules/luci-base/htdocs/luci-static/resources/network.js index b5e4c2b13da7..7d2b4b99eb5c 100644 --- a/modules/luci-base/htdocs/luci-static/resources/network.js +++ b/modules/luci-base/htdocs/luci-static/resources/network.js @@ -394,6 +394,8 @@ function initNetworkState(refresh) { type: dev.type, devtype: dev.devtype, mtu: dev.mtu, + min_mtu: dev.min_mtu, + max_mtu: dev.max_mtu, qlen: dev.qlen, wireless: dev.wireless, parent: dev.parent, @@ -2942,6 +2944,26 @@ Device = baseclass.extend(/** @lends LuCI.network.Device.prototype */ { return this._devstate('mtu'); }, + /** + * Get the minimum MTU supported by the device. + * + * @returns {number} + * Returns the minimum MTU of the device. + */ + getMinMTU() { + return this._devstate('min_mtu'); + }, + + /** + * Get the maximum MTU supported by the device. + * + * @returns {number} + * Returns the maximum MTU of the device. + */ + getMaxMTU() { + return this._devstate('max_mtu'); + }, + /** * Get the IPv4 addresses configured on the device. * From 8ed21a4afca32dd8d321666669be19e606860c3b Mon Sep 17 00:00:00 2001 From: Gleb Pesin Date: Fri, 7 Aug 2026 17:01:34 +0000 Subject: [PATCH 3/3] luci-mod-network: use device-specific MTU limits Validate MTU against the kernel-reported range for each network device. Keep 576 to 9200 as the fallback and constrain IPv6 MTU only on devices capable of carrying it. Signed-off-by: Gleb Pesin --- .../luci-static/resources/tools/network.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/modules/luci-mod-network/htdocs/luci-static/resources/tools/network.js b/modules/luci-mod-network/htdocs/luci-static/resources/tools/network.js index 255a62f13b20..685d197ace97 100644 --- a/modules/luci-mod-network/htdocs/luci-static/resources/tools/network.js +++ b/modules/luci-mod-network/htdocs/luci-static/resources/tools/network.js @@ -1104,8 +1104,15 @@ return baseclass.extend({ o.datatype = 'uinteger'; o.depends({ type: 'bridge', multicast_querier: '1' }); + const min_mtu = dev ? dev.getMinMTU() : null; + const max_mtu = dev ? dev.getMaxMTU() : null; + const effective_max_mtu = max_mtu || 9200; + const effective_min_mtu = effective_max_mtu < 576 + ? Math.min(min_mtu || effective_max_mtu, effective_max_mtu) + : Math.min(Math.max(min_mtu || 576, 576), effective_max_mtu); + o = this.replaceOption(s, 'devgeneral', form.Value, 'mtu', _('MTU')); - o.datatype = 'range(576, 9200)'; + o.datatype = 'range(%d, %d)'.format(effective_min_mtu, effective_max_mtu); o.validate = function(section_id, value) { const parent_mtu = (dev && dev.getType() == 'vlan') ? (parent_dev ? parent_dev.getMTU() : null) : null; @@ -1275,9 +1282,11 @@ return baseclass.extend({ o.sysfs = '/proc/sys/net/ipv6/conf/%s/drop_unsolicited_na'.format(devname || 'default'); o.depends('ipv6', /1/); - o = this.replaceOption(s, 'devgeneral', form.Value, 'mtu6', _('IPv6 MTU')); - o.datatype = 'max(9200)'; - o.depends('ipv6', /1/); + if (effective_max_mtu >= 1280) { + o = this.replaceOption(s, 'devgeneral', form.Value, 'mtu6', _('IPv6 MTU')); + o.datatype = 'range(1280, %d)'.format(effective_max_mtu); + o.depends('ipv6', /1/); + } o = this.replaceOption(s, 'devgeneral', form.Value, 'dadtransmits', _('DAD transmits'), _('Amount of Duplicate Address Detection probes to send')); o.placeholder = '1';