When modifying IPv4 devconf values using netlink for some relevant fields, the rt_cache_flush() call was missing. In addition, disable LRO if forwarding is enabled on the interface. The flushing and the disabling of LRO only happens if the value changed. As the value is converted to int when passing it to ipv4_devconf_set(), it is safe to cast it earlier to compare it against the current value. This is needed to avoid possible connectivity issues and ease the responsibilities of user space tools. Note that this follows the behavior for the sysctl function ipv4_doint_and_flush() and also partially devinet_conf_proc() as the missing notification would be handled in a follow-up patch. Fixes: 9f0f7272ac95 ("ipv4: AF_INET link address family") Signed-off-by: Fernando Fernandez Mancera --- v2: use netif_disable_lro() as we already hold netdev_need_ops_lock() and use net_device struct passed as argument instead of using in_dev->dev v3: separate it from the series and sent it to net tree instead v4: handle BC_FORWARDING and ACCEPT_LOCAL too, modify logic to skip useless calls to ipv4_devconf_set() due to value not modified. Finally, ammend the commit message. --- net/ipv4/devinet.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 537bb6c315d2..6dd8fd0a1323 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -2098,6 +2098,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) @@ -2107,8 +2109,33 @@ 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 new_value = (int)nla_get_u32(a); + + if (ipv4_devconf_get(in_dev, nla_type(a)) == new_value) + continue; + + ipv4_devconf_set(in_dev, nla_type(a), new_value); + switch (nla_type(a)) { + case IPV4_DEVCONF_FORWARDING: + if (new_value) + netif_disable_lro(dev); + fallthrough; + case IPV4_DEVCONF_NOXFRM: + case IPV4_DEVCONF_NOPOLICY: + case IPV4_DEVCONF_PROMOTE_SECONDARIES: + case IPV4_DEVCONF_ROUTE_LOCALNET: + case IPV4_DEVCONF_DROP_UNICAST_IN_L2_MULTICAST: + case IPV4_DEVCONF_BC_FORWARDING: + case IPV4_DEVCONF_ACCEPT_LOCAL: + flush_cache = true; + break; + default: + break; + } + } + if (flush_cache) + rt_cache_flush(net); } return 0; -- 2.53.0