ipv4: handle devconf post-set actions on netlink updates

When IPv4 device configuration parameters are updated via netlink, the
kernel currently only updates the value. This bypasses several
post-modification actions that occur when these same parameters are
updated via sysctl, such as flushing the routing cache or emitting
RTM_NEWNETCONF notifications.

This patch addresses the inconsistency by calling the
devinet_conf_post_set() helper inside inet_set_link_af(). If a flush is
required, we defer it until the netlink attribute parsing loop
completes.

This ensures consistent behavior and side-effects for devconf changes,
regardless of whether they are initiated via sysctl or netlink.

Reviewed-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
Link: https://patch.msgid.link/20260609204520.4670-2-fmancera@suse.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Fernando Fernandez Mancera
2026-06-09 22:45:19 +02:00
committed by Jakub Kicinski
parent 3a29b55505
commit 489730ec2a

View File

@@ -2161,6 +2161,20 @@ static bool devinet_conf_post_set(struct net *net, struct ipv4_devconf *cnf,
NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
ifindex, cnf);
break;
case IPV4_DEVCONF_FORWARDING:
if (new == 1) {
/* it is safe to use container_of() because forwarding case
* is only used by the netlink path
*/
struct in_device *idev = container_of(cnf, struct in_device, cnf);
netif_disable_lro(idev->dev);
}
inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
NETCONFA_FORWARDING,
ifindex, cnf);
return true;
default:
break;
}
@@ -2173,6 +2187,8 @@ static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla,
{
struct in_device *in_dev = __in_dev_get_rtnl(dev);
struct nlattr *a, *tb[IFLA_INET_MAX+1];
struct net *net = dev_net(dev);
bool flush_cache = false;
int rem;
if (!in_dev)
@@ -2182,8 +2198,17 @@ static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla,
return -EINVAL;
if (tb[IFLA_INET_CONF]) {
nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
int old_value = ipv4_devconf_get(in_dev, nla_type(a));
int new_value = nla_get_u32(a);
ipv4_devconf_set(in_dev, nla_type(a), new_value);
if (devinet_conf_post_set(net, &in_dev->cnf, nla_type(a), new_value,
old_value, dev->ifindex))
flush_cache = true;
}
if (flush_cache)
rt_cache_flush(net);
}
return 0;