All of lore.kernel.org
 help / color / mirror / Atom feed
From: Junnan Zhang <zhangjn_dev@163.com>
To: Willem de Bruijn <willemdebruijn.kernel@gmail.com>,
	"David S . Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>
Cc: Simon Horman <horms@kernel.org>,
	"Michael S . Tsirkin" <mst@redhat.com>,
	Hangbin Liu <liuhangbin@gmail.com>,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	zhangjn_dev@163.com, Junnan Zhang <zhangjn11@chinatelecom.cn>,
	Shouxin Sun <sunshx@chinatelecom.cn>
Subject: [PATCH net v3] net/packet: fix network header offset for non-VLAN raw packets on VLAN subinterfaces
Date: Tue,  1 Sep 2026 15:42:14 +0800	[thread overview]
Message-ID: <20260901074214.57717-1-zhangjn_dev@163.com> (raw)

AF_PACKET SOCK_RAW sets skb network_header to dev->hard_header_len in
packet_snd(). On VLAN subinterfaces with software tag insertion,
hard_header_len includes space for the VLAN tag (ETH_HLEN + VLAN_HLEN =
18) while min_header_len is the on-wire Ethernet header length
(ETH_HLEN = 14). A non-VLAN SOCK_RAW frame carries a standard 14-byte
Ethernet header, so its L3 header sits at min_header_len, not
hard_header_len.

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 VLAN_HLEN bytes too late and
inet_gso_segment() fails with -EINVAL.

Observed on a virtio_net NIC (KVM guest) that advertises
NETIF_F_HW_VLAN_CTAG_FILTER but not NETIF_F_HW_VLAN_CTAG_TX, so VLAN
subinterfaces use software tag insertion (hard_header_len = 18). An
AF_PACKET SOCK_RAW socket bound to the VLAN subinterface with
PACKET_VNET_HDR enabled sends a large IPv4/TCP frame exceeding the path
MTU, with gso_type set in the virtio-net header. The user frame is a
plain [ethhdr][IP...] layout without a VLAN tag;
vlan_dev_hard_start_xmit() only attaches the tag metadata via
__vlan_hwaccel_put_tag(), and validate_xmit_vlan() on the physical
device later inserts the actual tag bytes via
__vlan_hwaccel_push_inside(). With network_header stuck at 18 while the
real IP header is at ETH_HLEN (14), inet_gso_segment() reads a
misaligned ip_hdr(skb) and returns -EINVAL.

For non-VLAN SOCK_RAW frames on VLAN subinterfaces, set network_header
to min_header_len so that 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 <zhangjn11@chinatelecom.cn>
Signed-off-by: Shouxin Sun <sunshx@chinatelecom.cn>
Signed-off-by: Junnan Zhang <zhangjn_dev@163.com>
---
v3:
- Use is_vlan_dev() instead of the min_header_len < hard_header_len
  heuristic to detect the affected device; the heuristic may have false
  positives for other protocols, and the ARPHRD_ETHER check excluded
  the other hard_header_len cases anyway, so scope the fix back to
  VLAN subinterfaces.
- Rename is_vlan to is_vlan_packet to disambiguate from is_vlan_dev().
- Correct where the tag is inserted: vlan_dev_hard_start_xmit() only
  attaches the tag metadata via __vlan_hwaccel_put_tag(); the tag
  bytes are inserted later by validate_xmit_vlan() on the physical
  device.
- Reword the subject and commit message back to the VLAN-subinterface
  scope.

v2: https://lore.kernel.org/all/20260831072034.40044-1-zhangjn_dev@163.com/
v1: https://lore.kernel.org/all/20260821085722.24036-1-zhangjn_dev@163.com/#t
---
 net/packet/af_packet.c | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 1168bd6b09cd..3bada38b3556 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 is_vlan_packet = false;
 
 	/* On TX skb->data is the L2 header; anchor it for all socket types. */
 	skb_reset_mac_header(skb);
@@ -1943,11 +1944,28 @@ static void packet_parse_headers(struct sk_buff *skb, struct socket *sock)
 	    sock->type == SOCK_RAW)
 		skb->protocol = dev_parse_header_protocol(skb);
 
+	if (likely(skb->dev->type == ARPHRD_ETHER)) {
+		is_vlan_packet = eth_type_vlan(skb->protocol);
+
+		/* For non-VLAN SOCK_RAW frames on VLAN subinterfaces with
+		 * software tag insertion, hard_header_len includes space
+		 * for the VLAN tag while min_header_len is the on-wire
+		 * Ethernet header length. The user frame carries a
+		 * standard Ethernet header, so its L3 sits at
+		 * min_header_len, not hard_header_len. Move
+		 * network_header to the actual L2/L3 boundary so the
+		 * transport header probe below and subsequent GSO see
+		 * the right L3.
+		 */
+		if (sock->type == SOCK_RAW && !is_vlan_packet &&
+		    is_vlan_dev(skb->dev))
+			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 (is_vlan_packet &&
 	    vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0)
 		skb_set_network_header(skb, depth);
 }
-- 
2.43.0


             reply	other threads:[~2026-09-01  7:42 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-01  7:42 Junnan Zhang [this message]
2026-09-01 14:06 ` [PATCH net v3] net/packet: fix network header offset for non-VLAN raw packets on VLAN subinterfaces Willem de Bruijn
2026-09-03  7:45 ` [net,v3] " netdev-bot+sashiko
2026-09-04  7:47   ` [PATCH net v3] " Junnan Zhang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260901074214.57717-1-zhangjn_dev@163.com \
    --to=zhangjn_dev@163.com \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=horms@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=liuhangbin@gmail.com \
    --cc=mst@redhat.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=sunshx@chinatelecom.cn \
    --cc=willemdebruijn.kernel@gmail.com \
    --cc=zhangjn11@chinatelecom.cn \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.