Download raw body.
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
sys/net: force software checksums for injected VLAN tags