| From bc74bffd31f28d8edbbfe791caf4fc3222481f7d Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Sat, 19 Feb 2022 17:45:19 +0200 |
| Subject: ipv4: Invalidate neighbour for broadcast address upon address |
| addition |
| |
| From: Ido Schimmel <idosch@nvidia.com> |
| |
| [ Upstream commit 0c51e12e218f20b7d976158fdc18019627326f7a ] |
| |
| In case user space sends a packet destined to a broadcast address when a |
| matching broadcast route is not configured, the kernel will create a |
| unicast neighbour entry that will never be resolved [1]. |
| |
| When the broadcast route is configured, the unicast neighbour entry will |
| not be invalidated and continue to linger, resulting in packets being |
| dropped. |
| |
| Solve this by invalidating unresolved neighbour entries for broadcast |
| addresses after routes for these addresses are internally configured by |
| the kernel. This allows the kernel to create a broadcast neighbour entry |
| following the next route lookup. |
| |
| Another possible solution that is more generic but also more complex is |
| to have the ARP code register a listener to the FIB notification chain |
| and invalidate matching neighbour entries upon the addition of broadcast |
| routes. |
| |
| It is also possible to wave off the issue as a user space problem, but |
| it seems a bit excessive to expect user space to be that intimately |
| familiar with the inner workings of the FIB/neighbour kernel code. |
| |
| [1] https://lore.kernel.org/netdev/55a04a8f-56f3-f73c-2aea-2195923f09d1@huawei.com/ |
| |
| Reported-by: Wang Hai <wanghai38@huawei.com> |
| Signed-off-by: Ido Schimmel <idosch@nvidia.com> |
| Tested-by: Wang Hai <wanghai38@huawei.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| include/net/arp.h | 1 + |
| net/ipv4/arp.c | 9 +++++++-- |
| net/ipv4/fib_frontend.c | 5 ++++- |
| 3 files changed, 12 insertions(+), 3 deletions(-) |
| |
| diff --git a/include/net/arp.h b/include/net/arp.h |
| index 4950191f6b2b..4a23a97195f3 100644 |
| --- a/include/net/arp.h |
| +++ b/include/net/arp.h |
| @@ -71,6 +71,7 @@ void arp_send(int type, int ptype, __be32 dest_ip, |
| const unsigned char *src_hw, const unsigned char *th); |
| int arp_mc_map(__be32 addr, u8 *haddr, struct net_device *dev, int dir); |
| void arp_ifdown(struct net_device *dev); |
| +int arp_invalidate(struct net_device *dev, __be32 ip, bool force); |
| |
| struct sk_buff *arp_create(int type, int ptype, __be32 dest_ip, |
| struct net_device *dev, __be32 src_ip, |
| diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c |
| index 857a144b1ea9..5ee382309a9d 100644 |
| --- a/net/ipv4/arp.c |
| +++ b/net/ipv4/arp.c |
| @@ -1116,13 +1116,18 @@ static int arp_req_get(struct arpreq *r, struct net_device *dev) |
| return err; |
| } |
| |
| -static int arp_invalidate(struct net_device *dev, __be32 ip) |
| +int arp_invalidate(struct net_device *dev, __be32 ip, bool force) |
| { |
| struct neighbour *neigh = neigh_lookup(&arp_tbl, &ip, dev); |
| int err = -ENXIO; |
| struct neigh_table *tbl = &arp_tbl; |
| |
| if (neigh) { |
| + if ((neigh->nud_state & NUD_VALID) && !force) { |
| + neigh_release(neigh); |
| + return 0; |
| + } |
| + |
| if (neigh->nud_state & ~NUD_NOARP) |
| err = neigh_update(neigh, NULL, NUD_FAILED, |
| NEIGH_UPDATE_F_OVERRIDE| |
| @@ -1169,7 +1174,7 @@ static int arp_req_delete(struct net *net, struct arpreq *r, |
| if (!dev) |
| return -EINVAL; |
| } |
| - return arp_invalidate(dev, ip); |
| + return arp_invalidate(dev, ip, true); |
| } |
| |
| /* |
| diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c |
| index 4d61ddd8a0ec..1eb7795edb9d 100644 |
| --- a/net/ipv4/fib_frontend.c |
| +++ b/net/ipv4/fib_frontend.c |
| @@ -1112,9 +1112,11 @@ void fib_add_ifaddr(struct in_ifaddr *ifa) |
| return; |
| |
| /* Add broadcast address, if it is explicitly assigned. */ |
| - if (ifa->ifa_broadcast && ifa->ifa_broadcast != htonl(0xFFFFFFFF)) |
| + if (ifa->ifa_broadcast && ifa->ifa_broadcast != htonl(0xFFFFFFFF)) { |
| fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, |
| prim, 0); |
| + arp_invalidate(dev, ifa->ifa_broadcast, false); |
| + } |
| |
| if (!ipv4_is_zeronet(prefix) && !(ifa->ifa_flags & IFA_F_SECONDARY) && |
| (prefix != addr || ifa->ifa_prefixlen < 32)) { |
| @@ -1128,6 +1130,7 @@ void fib_add_ifaddr(struct in_ifaddr *ifa) |
| if (ifa->ifa_prefixlen < 31) { |
| fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix | ~mask, |
| 32, prim, 0); |
| + arp_invalidate(dev, prefix | ~mask, false); |
| } |
| } |
| } |
| -- |
| 2.35.1 |
| |