[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20210622161533.1214662-1-dwmw2@infradead.org>
Date: Tue, 22 Jun 2021 17:15:30 +0100
From: David Woodhouse <dwmw2@...radead.org>
To: netdev@...r.kernel.org
Cc: Jason Wang <jasowang@...hat.com>,
Eugenio PĂ©rez <eperezma@...hat.com>
Subject: [PATCH v2 1/4] net: tun: fix tun_xdp_one() for IFF_TUN mode
From: David Woodhouse <dwmw@...zon.co.uk>
In tun_get_user(), skb->protocol is either taken from the tun_pi header
or inferred from the first byte of the packet in IFF_TUN mode, while
eth_type_trans() is called only in the IFF_TAP mode where the payload
is expected to be an Ethernet frame.
The alternative path in tun_xdp_one() was unconditionally using
eth_type_trans(), which corrupts packets in IFF_TUN mode. Fix it to
do the correct thing for IFF_TUN mode, as tun_get_user() does.
Fixes: 043d222f93ab ("tuntap: accept an array of XDP buffs through sendmsg()")
Signed-off-by: David Woodhouse <dwmw@...zon.co.uk>
---
drivers/net/tun.c | 44 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 43 insertions(+), 1 deletion(-)
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 4cf38be26dc9..f812dcdc640e 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -2394,8 +2394,50 @@ static int tun_xdp_one(struct tun_struct *tun,
err = -EINVAL;
goto out;
}
+ switch (tun->flags & TUN_TYPE_MASK) {
+ case IFF_TUN:
+ if (tun->flags & IFF_NO_PI) {
+ u8 ip_version = skb->len ? (skb->data[0] >> 4) : 0;
+
+ switch (ip_version) {
+ case 4:
+ skb->protocol = htons(ETH_P_IP);
+ break;
+ case 6:
+ skb->protocol = htons(ETH_P_IPV6);
+ break;
+ default:
+ atomic_long_inc(&tun->dev->rx_dropped);
+ kfree_skb(skb);
+ err = -EINVAL;
+ goto out;
+ }
+ } else {
+ struct tun_pi *pi = (struct tun_pi *)skb->data;
+ if (!pskb_may_pull(skb, sizeof(*pi))) {
+ atomic_long_inc(&tun->dev->rx_dropped);
+ kfree_skb(skb);
+ err = -ENOMEM;
+ goto out;
+ }
+ skb_pull_inline(skb, sizeof(*pi));
+ skb->protocol = pi->proto;
+ }
+
+ skb_reset_mac_header(skb);
+ skb->dev = tun->dev;
+ break;
+ case IFF_TAP:
+ if (!pskb_may_pull(skb, ETH_HLEN)) {
+ atomic_long_inc(&tun->dev->rx_dropped);
+ kfree_skb(skb);
+ err = -ENOMEM;
+ goto out;
+ }
+ skb->protocol = eth_type_trans(skb, tun->dev);
+ break;
+ }
- skb->protocol = eth_type_trans(skb, tun->dev);
skb_reset_network_header(skb);
skb_probe_transport_header(skb);
skb_record_rx_queue(skb, tfile->queue_index);
--
2.31.1
Powered by blists - more mailing lists