Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
sys/net: force software checksums for injected VLAN tags
To:
OpenBSD tech <tech@openbsd.org>
Date:
Thu, 16 Jul 2026 12:58:18 +0200

Download raw body.

Thread
  • Kirill A. Korinsky:

    sys/net: force software checksums for injected VLAN tags

tech@,

In cnmac VLAN corrupting discussion:
https://marc.info/?t=178398289000003&r=1&w=2

I had suggested a generic approach to prevent that kind of bugs and I think
this should be send as dedicated diff to tech@.

Idea that ether_offload_ifcap() injects a VLAN tag when the output interface
does not provide hardware VLAN tagging, then retains checksum offload based
only on the interface's ordinary checksum capabilities.

Such interfaces without IFCAP_VLAN_HWOFFLOAD consequently receive inline
tagged packets with deferred checksums, violating the capability contract
and corrupting packets in drivers which assume 14 byte Ethernet headers.

So, let use a NULL checksum interface for these packets, forcing the
existing checksum helpers to finish the checksums in software. Preserve
hardware offload for untagged packets and interfaces advertising
IFCAP_VLAN_HWOFFLOAD.

Thoughts?

Index: sys/net/if_ethersubr.c
===================================================================
RCS file: /home/cvs/src/sys/net/if_ethersubr.c,v
diff -u -p -r1.308 if_ethersubr.c
--- sys/net/if_ethersubr.c	19 Dec 2025 02:04:13 -0000	1.308
+++ sys/net/if_ethersubr.c	14 Jul 2026 10:47:15 -0000
@@ -1267,6 +1267,7 @@ struct mbuf *
 ether_offload_ifcap(struct ifnet *ifp, struct mbuf *m)
 {
 	struct ether_extracted ext;
+	struct ifnet *csum_ifp = ifp;
 	int csum = 0;
 
 #if NVLAN > 0
@@ -1274,8 +1275,11 @@ ether_offload_ifcap(struct ifnet *ifp, s
 	    !ISSET(ifp->if_capabilities, IFCAP_VLAN_HWTAGGING)) {
 		/*
 		 * If the underlying interface has no VLAN hardware tagging
-		 * support, inject one in software.
+		 * support, inject one in software. Calculate checksums in
+		 * software unless it supports offloading with an inline tag.
 		 */
+		if (!ISSET(ifp->if_capabilities, IFCAP_VLAN_HWOFFLOAD))
+			csum_ifp = NULL;
 		m = vlan_inject(m, ETHERTYPE_VLAN, m->m_pkthdr.ether_vtag);
 		if (m == NULL)
 			return (NULL);
@@ -1283,16 +1287,19 @@ ether_offload_ifcap(struct ifnet *ifp, s
 #endif
 
 	if (ISSET(m->m_pkthdr.csum_flags, M_IPV4_CSUM_OUT) &&
-	    !ISSET(ifp->if_capabilities, IFCAP_CSUM_IPv4))
+	    (csum_ifp == NULL ||
+	     !ISSET(ifp->if_capabilities, IFCAP_CSUM_IPv4)))
 		csum = 1;
 
 	if (ISSET(m->m_pkthdr.csum_flags, M_TCP_CSUM_OUT) &&
-	    (!ISSET(ifp->if_capabilities, IFCAP_CSUM_TCPv4) ||
+	    (csum_ifp == NULL ||
+	     !ISSET(ifp->if_capabilities, IFCAP_CSUM_TCPv4) ||
 	     !ISSET(ifp->if_capabilities, IFCAP_CSUM_TCPv6)))
 		csum = 1;
 
 	if (ISSET(m->m_pkthdr.csum_flags, M_UDP_CSUM_OUT) &&
-	    (!ISSET(ifp->if_capabilities, IFCAP_CSUM_UDPv4) ||
+	    (csum_ifp == NULL ||
+	     !ISSET(ifp->if_capabilities, IFCAP_CSUM_UDPv4) ||
 	     !ISSET(ifp->if_capabilities, IFCAP_CSUM_UDPv6)))
 		csum = 1;
 
@@ -1320,11 +1327,11 @@ ether_offload_ifcap(struct ifnet *ifp, s
 		m->m_pkthdr.len -= ethlen;
 
 		if (ext.ip4) {
-			in_hdr_cksum_out(m, ifp);
-			in_proto_cksum_out(m, ifp);
+			in_hdr_cksum_out(m, csum_ifp);
+			in_proto_cksum_out(m, csum_ifp);
 #ifdef INET6
 		} else if (ext.ip6) {
-			in6_proto_cksum_out(m, ifp);
+			in6_proto_cksum_out(m, csum_ifp);
 #endif
 		}
 


-- 
wbr, Kirill