From: Kirill A. Korinsky Subject: sys/net: force software checksums for injected VLAN tags To: OpenBSD tech Date: Thu, 16 Jul 2026 12:58:18 +0200 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