| From ab606c5963fded70ebf0bb12963a54d7e4170957 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 20 Feb 2025 09:25:59 +0200 |
| Subject: net: loopback: Avoid sending IP packets without an Ethernet header |
| |
| From: Ido Schimmel <idosch@nvidia.com> |
| |
| [ Upstream commit 0e4427f8f587c4b603475468bb3aee9418574893 ] |
| |
| After commit 22600596b675 ("ipv4: give an IPv4 dev to blackhole_netdev") |
| IPv4 neighbors can be constructed on the blackhole net device, but they |
| are constructed with an output function (neigh_direct_output()) that |
| simply calls dev_queue_xmit(). The latter will transmit packets via |
| 'skb->dev' which might not be the blackhole net device if dst_dev_put() |
| switched 'dst->dev' to the blackhole net device while another CPU was |
| using the dst entry in ip_output(), but after it already initialized |
| 'skb->dev' from 'dst->dev'. |
| |
| Specifically, the following can happen: |
| |
| CPU1 CPU2 |
| |
| udp_sendmsg(sk1) udp_sendmsg(sk2) |
| udp_send_skb() [...] |
| ip_output() |
| skb->dev = skb_dst(skb)->dev |
| dst_dev_put() |
| dst->dev = blackhole_netdev |
| ip_finish_output2() |
| resolves neigh on dst->dev |
| neigh_output() |
| neigh_direct_output() |
| dev_queue_xmit() |
| |
| This will result in IPv4 packets being sent without an Ethernet header |
| via a valid net device: |
| |
| tcpdump: verbose output suppressed, use -v[v]... for full protocol decode |
| listening on enp9s0, link-type EN10MB (Ethernet), snapshot length 262144 bytes |
| 22:07:02.329668 20:00:40:11:18:fb > 45:00:00:44:f4:94, ethertype Unknown |
| (0x58c6), length 68: |
| 0x0000: 8dda 74ca f1ae ca6c ca6c 0098 969c 0400 ..t....l.l...... |
| 0x0010: 0000 4730 3f18 6800 0000 0000 0000 9971 ..G0?.h........q |
| 0x0020: c4c9 9055 a157 0a70 9ead bf83 38ca ab38 ...U.W.p....8..8 |
| 0x0030: 8add ab96 e052 .....R |
| |
| Fix by making sure that neighbors are constructed on top of the |
| blackhole net device with an output function that simply consumes the |
| packets, in a similar fashion to dst_discard_out() and |
| blackhole_netdev_xmit(). |
| |
| Fixes: 8d7017fd621d ("blackhole_netdev: use blackhole_netdev to invalidate dst entries") |
| Fixes: 22600596b675 ("ipv4: give an IPv4 dev to blackhole_netdev") |
| Reported-by: Florian Meister <fmei@sfs.com> |
| Closes: https://lore.kernel.org/netdev/20250210084931.23a5c2e4@hermes.local/ |
| Signed-off-by: Ido Schimmel <idosch@nvidia.com> |
| Reviewed-by: Eric Dumazet <edumazet@google.com> |
| Link: https://patch.msgid.link/20250220072559.782296-1-idosch@nvidia.com |
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/net/loopback.c | 14 ++++++++++++++ |
| 1 file changed, 14 insertions(+) |
| |
| diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c |
| index f6eab66c26608..6aa00f62b1f90 100644 |
| --- a/drivers/net/loopback.c |
| +++ b/drivers/net/loopback.c |
| @@ -247,8 +247,22 @@ static netdev_tx_t blackhole_netdev_xmit(struct sk_buff *skb, |
| return NETDEV_TX_OK; |
| } |
| |
| +static int blackhole_neigh_output(struct neighbour *n, struct sk_buff *skb) |
| +{ |
| + kfree_skb(skb); |
| + return 0; |
| +} |
| + |
| +static int blackhole_neigh_construct(struct net_device *dev, |
| + struct neighbour *n) |
| +{ |
| + n->output = blackhole_neigh_output; |
| + return 0; |
| +} |
| + |
| static const struct net_device_ops blackhole_netdev_ops = { |
| .ndo_start_xmit = blackhole_netdev_xmit, |
| + .ndo_neigh_construct = blackhole_neigh_construct, |
| }; |
| |
| /* This is a dst-dummy device used specifically for invalidated |
| -- |
| 2.39.5 |
| |