]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ipv4: handle devconf post-set actions on netlink updates
authorFernando Fernandez Mancera <fmancera@suse.de>
Tue, 9 Jun 2026 20:45:19 +0000 (22:45 +0200)
committerJakub Kicinski <kuba@kernel.org>
Sat, 13 Jun 2026 21:15:53 +0000 (14:15 -0700)
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>
net/ipv4/devinet.c

index 8300516fb38f43e10fd5c1d8973b8685a0a1c2f2..a35b72662e431661da1672f428cae6bb3110480b 100644 (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;