mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Gabriel Goller <g.goller@proxmox.com>
To: "David S. Miller" <davem@davemloft.net>,
	 Eric Dumazet <edumazet@kernel.org>,
	Jakub Kicinski <kuba@kernel.org>,
	 Paolo Abeni <pabeni@redhat.com>, Simon Horman <horms@kernel.org>,
	 Boris Sukholitko <boris.sukholitko@broadcom.com>,
	 Aaron Conole <aconole@redhat.com>,
	Eelco Chaudron <echaudro@redhat.com>,
	 Ilya Maximets <i.maximets@ovn.org>,
	Shuah Khan <shuah@kernel.org>
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	 dev@openvswitch.org, linux-kselftest@vger.kernel.org,
	 Gabriel Goller <g.goller@proxmox.com>
Subject: [PATCH RFC net 3/3] net: test stacked VLAN headers before TX offloads
Date: Wed, 11 Nov 2026 18:37:51 +0100	[thread overview]
Message-ID: <20261111-net_restore_the-v1-3-6aaeffa601c9@proxmox.com> (raw)
In-Reply-To: <20261111-net_restore_the-v1-0-6aaeffa601c9@proxmox.com>

Cover the QinQ regression without requiring an igb NIC, so incorrect L3
header offsets are caught before they reach hardware offload processing.

The IPv4 and IPv6 cases fail without the TX header fix and pass with it.
All 22 enabled net_core KUnit cases pass in QEMU. This does not validate
segmentation on physical hardware.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
---
 net/core/net_test.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 145 insertions(+)

diff --git a/net/core/net_test.c b/net/core/net_test.c
index 2b5080c22663..73a795f1443e 100644
--- a/net/core/net_test.c
+++ b/net/core/net_test.c
@@ -1721,7 +1721,152 @@ static void netmem_ref_test_tcp_devmem_recv(struct kunit *test)
 #endif
 #endif
 
+/* VLAN TX offloads need the L3 header, not the inner VLAN header. */
+
+#include <linux/if_vlan.h>
+#include <linux/etherdevice.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
+#include <net/flow_dissector.h>
+
+static void vlan_test_free_skb(void *ptr)
+{
+	kfree_skb(ptr);
+}
+
+static void vlan_test_free_netdev(void *ptr)
+{
+	free_netdev(ptr);
+}
+
+static netdev_features_t vlan_test_features_check(struct sk_buff *skb,
+						  struct net_device *dev,
+						  netdev_features_t features)
+{
+	struct kunit *test = *(struct kunit **)netdev_priv(dev);
+	unsigned int ip_len = vlan_get_protocol(skb) == htons(ETH_P_IP) ?
+			      sizeof(struct iphdr) : sizeof(struct ipv6hdr);
+
+	KUNIT_EXPECT_EQ(test, skb_network_offset(skb),
+			skb_transport_offset(skb) - ip_len);
+
+	/* Like igb, allow hardware TSO with multiple VLAN tags. */
+	return features;
+}
+
+static const struct net_device_ops vlan_test_netdev_ops = {
+	.ndo_features_check = vlan_test_features_check,
+};
+
+static void vlan_xmit_test(struct kunit *test)
+{
+	const __be16 *proto = test->param_value;
+	unsigned int ip_len = *proto == htons(ETH_P_IP) ?
+			      sizeof(struct iphdr) : sizeof(struct ipv6hdr);
+	unsigned int len = ETH_HLEN + ip_len + sizeof(struct tcphdr) + 2880;
+	struct net_device *dev;
+	struct flow_keys keys;
+	struct sk_buff *skb, *xmit;
+	unsigned int nhoff;
+	bool again = false;
+	int i;
+
+	dev = alloc_netdev(sizeof(test), "vlan_test%d", NET_NAME_UNKNOWN,
+			   ether_setup);
+	KUNIT_ASSERT_NOT_NULL(test, dev);
+	KUNIT_ASSERT_EQ(test,
+			kunit_add_action_or_reset(test, vlan_test_free_netdev, dev),
+			0);
+	dev->netdev_ops = &vlan_test_netdev_ops;
+	*(struct kunit **)netdev_priv(dev) = test;
+	dev->features = NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_TSO |
+			NETIF_F_TSO6 | NETIF_F_HW_VLAN_CTAG_TX;
+	dev->vlan_features = dev->features;
+
+	skb = alloc_skb(len, GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, skb);
+	KUNIT_ASSERT_EQ(test,
+			kunit_add_action_or_reset(test, vlan_test_free_skb, skb),
+			0);
+	skb_put_zero(skb, len);
+	skb_reset_mac_header(skb);
+	skb_set_network_header(skb, ETH_HLEN);
+	skb_set_transport_header(skb, ETH_HLEN + ip_len);
+	skb_reset_mac_len(skb);
+	skb->protocol = *proto;
+	skb->dev = dev;
+	eth_hdr(skb)->h_proto = *proto;
+	if (*proto == htons(ETH_P_IP)) {
+		ip_hdr(skb)->version = 4;
+		ip_hdr(skb)->ihl = 5;
+		ip_hdr(skb)->protocol = IPPROTO_TCP;
+		ip_hdr(skb)->tot_len = htons(len - ETH_HLEN);
+	} else {
+		ipv6_hdr(skb)->version = 6;
+		ipv6_hdr(skb)->nexthdr = IPPROTO_TCP;
+		ipv6_hdr(skb)->payload_len = htons(len - ETH_HLEN - ip_len);
+	}
+	tcp_hdr(skb)->source = htons(1234);
+	tcp_hdr(skb)->dest = htons(4321);
+	tcp_hdr(skb)->doff = sizeof(struct tcphdr) / 4;
+	skb->ip_summed = CHECKSUM_PARTIAL;
+	skb->csum_start = skb->transport_header;
+	skb->csum_offset = offsetof(struct tcphdr, check);
+	skb_shinfo(skb)->gso_size = 1440;
+	skb_shinfo(skb)->gso_segs = 2;
+	skb_shinfo(skb)->gso_type = *proto == htons(ETH_P_IP) ?
+				  SKB_GSO_TCPV4 : SKB_GSO_TCPV6;
+
+	for (i = 0; i < 3; i++) {
+		KUNIT_ASSERT_EQ(test,
+				skb_vlan_push(skb, htons(ETH_P_8021Q), 200 + i),
+				0);
+		skb_reset_mac_len(skb);
+		/* Classification after a push must still see all VLAN tags. */
+		memset(&keys, 0, sizeof(keys));
+		KUNIT_EXPECT_TRUE(test,
+				  __skb_flow_dissect(&init_net, skb,
+						     &flow_keys_dissector, &keys,
+						     NULL, 0, 0, 0, 0));
+		KUNIT_EXPECT_EQ(test, keys.basic.n_proto, *proto);
+		KUNIT_EXPECT_EQ(test, keys.basic.ip_proto, IPPROTO_TCP);
+		KUNIT_EXPECT_EQ(test, keys.ports.src, htons(1234));
+		KUNIT_EXPECT_EQ(test, keys.ports.dst, htons(4321));
+		KUNIT_EXPECT_EQ(test, (u16)keys.vlan.vlan_id, 200 + i);
+
+		xmit = skb_clone(skb, GFP_KERNEL);
+		KUNIT_ASSERT_NOT_NULL(test, xmit);
+		xmit = validate_xmit_skb_list(xmit, dev, &again);
+		KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xmit);
+		nhoff = ETH_HLEN + i * VLAN_HLEN;
+		KUNIT_EXPECT_EQ(test, skb_network_offset(xmit), nhoff);
+		KUNIT_EXPECT_EQ(test, skb_transport_offset(xmit), nhoff + ip_len);
+		KUNIT_EXPECT_EQ(test, skb_checksum_start_offset(xmit),
+				nhoff + ip_len);
+		KUNIT_EXPECT_EQ(test, (u8)ip_hdr(xmit)->version,
+				*proto == htons(ETH_P_IP) ? 4 : 6);
+		KUNIT_EXPECT_TRUE(test, skb_is_gso(xmit));
+		KUNIT_EXPECT_PTR_EQ(test, xmit->next, NULL);
+		kfree_skb_list(xmit);
+	}
+}
+
+static const __be16 vlan_test_protos[] = {
+	htons(ETH_P_IP),
+	htons(ETH_P_IPV6),
+};
+
+static void vlan_test_desc(const __be16 *proto, char *desc)
+{
+	snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s",
+		 *proto == htons(ETH_P_IP) ? "IPv4" : "IPv6");
+}
+
+KUNIT_ARRAY_PARAM(vlan, vlan_test_protos, vlan_test_desc);
+
 static struct kunit_case net_test_cases[] = {
+	KUNIT_CASE_PARAM(vlan_xmit_test, vlan_gen_params),
 	KUNIT_CASE_PARAM(gso_test_func, gso_test_gen_params),
 	KUNIT_CASE_PARAM(ip_tunnel_flags_test_run,
 			 ip_tunnel_flags_test_gen_params),

-- 
2.47.3



  parent reply	other threads:[~2026-11-11 17:38 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-11 17:37 [PATCH RFC net 0/3] net: fix stacked VLAN header corruption in TX offloads and OVS Gabriel Goller
2026-11-11 17:37 ` [PATCH RFC net 1/3] net: restore the L3 header before VLAN TX offload checks Gabriel Goller
2026-11-11 17:37 ` [PATCH RFC net 2/3] openvswitch: preserve the L3 header after stacked VLAN pushes Gabriel Goller
2026-11-11 17:37 ` Gabriel Goller [this message]
2026-11-11 18:33 ` [PATCH RFC net 0/3] net: fix stacked VLAN header corruption in TX offloads and OVS Ilya Maximets

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=20261111-net_restore_the-v1-3-6aaeffa601c9@proxmox.com \
    --to=g.goller@proxmox.com \
    --cc=aconole@redhat.com \
    --cc=boris.sukholitko@broadcom.com \
    --cc=davem@davemloft.net \
    --cc=dev@openvswitch.org \
    --cc=echaudro@redhat.com \
    --cc=edumazet@kernel.org \
    --cc=horms@kernel.org \
    --cc=i.maximets@ovn.org \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=shuah@kernel.org \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®