[PATCH] net/packet: fix network header offset for non-VLAN raw packets on VLAN subinterfaces

Junnan Zhang <[email protected]>
Newsgroups org.kernel.vger.netdev,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
From: Junnan Zhang <[email protected]>

AF_PACKET SOCK_RAW reserves dev->hard_header_len bytes of headroom. For
VLAN subinterfaces, hard_header_len includes VLAN tag space (18 bytes)
while min_header_len is the real Ethernet header length (14 bytes). When
userspace sends a standard untagged Ethernet frame through a VLAN
subinterface, packet_parse_headers() only corrects network_header for
VLAN-tagged frames. For non-VLAN frames it leaves network_header at
hard_header_len, so the IP header is found 4 bytes too late and
inet_gso_segment() fails with -EINVAL.

Set network_header to min_header_len for non-VLAN SOCK_RAW frames on
Ethernet devices whose hard_header_len exceeds min_header_len, so the
L3/L4 header positions match the actual on-the-wire frame.

This fix is placed before skb_probe_transport_header() so that both the
transport header probe (which uses skb_network_offset() as nhoff) and
subsequent GSO see the right L3/L4 offsets. It complements
commit 01fdecc0480d ("net: packet: fix wrong transport_header when sending VLAN-tagged frame")
which only covers VLAN-tagged frames.

Fixes: dfed913e8b55 ("net/af_packet: add VLAN support for AF_PACKET SOCK_RAW GSO")
Signed-off-by: Junnan Zhang <[email protected]>
Signed-off-by: Shouxin Sun <[email protected]>
Signed-off-by: Junnan Zhang <[email protected]>
---
 net/packet/af_packet.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 1168bd6b09cd..4669320f551b 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1935,6 +1935,7 @@ static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev,
 static void packet_parse_headers(struct sk_buff *skb, struct socket *sock)
 {
 	int depth;
+	bool has_vlan;
 
 	/* On TX skb->data is the L2 header; anchor it for all socket types. */
 	skb_reset_mac_header(skb);
@@ -1943,11 +1944,23 @@ static void packet_parse_headers(struct sk_buff *skb, struct socket *sock)
 	    sock->type == SOCK_RAW)
 		skb->protocol = dev_parse_header_protocol(skb);
 
+	has_vlan = likely(skb->dev->type == ARPHRD_ETHER) &&
+		   eth_type_vlan(skb->protocol);
+
+	/* For non-VLAN raw frames on devices whose hard_header_len includes
+	 * VLAN tag space (e.g. VLAN subinterfaces), the network header must be
+	 * at the actual L2/L3 boundary, not hard_header_len, so that both the
+	 * transport header probe below and subsequent GSO see the right L3.
+	 */
+	if (!has_vlan && sock->type == SOCK_RAW &&
+	    likely(skb->dev->type == ARPHRD_ETHER) &&
+	    skb->dev->min_header_len < skb->dev->hard_header_len)
+		skb_set_network_header(skb, skb->dev->min_header_len);
+
 	skb_probe_transport_header(skb);
 
 	/* Move network header to the right position for VLAN tagged packets */
-	if (likely(skb->dev->type == ARPHRD_ETHER) &&
-	    eth_type_vlan(skb->protocol) &&
+	if (has_vlan &&
 	    vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0)
 		skb_set_network_header(skb, depth);
 }
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.