From: Jan Klemkow Subject: vport/veb(4): use/fix checksum offload To: tech@openbsd.org Date: Fri, 27 Jun 2025 20:06:42 +0200 Hi, This diff enables checksum offload in vport(4) and fixes checksum offload in veb(4) in some corner cases. If we get packages with M_TCP_CSUM_OUT from an interface. When we bridge two vio(4) interfaces via veb(4), we will lost the VIRTIO_NET_HDR_F_NEEDS_CSUM flags, which is encodes in our mbuf via M_TCP_CSUM_OUT flag. In the case of bridging vio(4) with an physical interface (e.g. em(4), we also have to M_TCP_CSUM_OUT flag. So, the hardware is able to calculate the correct checksum. Or, when we bridge vio(4) with an interface unable to calculate we have to do this here, before sending the packet to the interface. ok? bye, Jan Index: net/if_veb.c =================================================================== RCS file: /cvs/src/sys/net/if_veb.c,v diff -u -p -r1.38 if_veb.c --- net/if_veb.c 27 Jun 2025 09:25:57 -0000 1.38 +++ net/if_veb.c 27 Jun 2025 17:33:10 -0000 @@ -948,6 +948,9 @@ veb_ipsec_out(struct ifnet *ifp0, struct static struct mbuf * veb_offload(struct ifnet *ifp, struct ifnet *ifp0, struct mbuf *m) { + struct ether_extracted ext; + int csum = 0; + #if NVLAN > 0 if (ISSET(m->m_flags, M_VLANTAG) && !ISSET(ifp0->if_capabilities, IFCAP_VLAN_HWTAGGING)) { @@ -956,14 +959,56 @@ veb_offload(struct ifnet *ifp, struct if * support, inject one in software. */ m = vlan_inject(m, ETHERTYPE_VLAN, m->m_pkthdr.ether_vtag); - if (m == NULL) { - counters_inc(ifp->if_counters, ifc_ierrors); - return NULL; - } + if (m == NULL) + goto drop; } #endif + if (ISSET(m->m_pkthdr.csum_flags, M_IPV4_CSUM_OUT) && + !ISSET(ifp0->if_capabilities, IFCAP_CSUM_IPv4)) + csum = 1; + + if (ISSET(m->m_pkthdr.csum_flags, M_TCP_CSUM_OUT) && + (!ISSET(ifp0->if_capabilities, IFCAP_CSUM_TCPv4) || + !ISSET(ifp0->if_capabilities, IFCAP_CSUM_TCPv6))) + csum = 1; + + if (ISSET(m->m_pkthdr.csum_flags, M_UDP_CSUM_OUT) && + (!ISSET(ifp0->if_capabilities, IFCAP_CSUM_UDPv4) || + !ISSET(ifp0->if_capabilities, IFCAP_CSUM_UDPv6))) + csum = 1; + + if (csum) { + int adjlen; + + ether_extract_headers(m, &ext); + if (ext.eh) + adjlen = sizeof *ext.eh; + else if (ext.evh) + adjlen = sizeof *ext.evh; + else + goto drop; + + m_adj(m, adjlen); + + if (ext.ip4) { + in_hdr_cksum_out(m, ifp0); + in_proto_cksum_out(m, ifp0); +#ifdef INET6 + } else if (ext.ip6) { + in6_proto_cksum_out(m, ifp0); +#endif + } + m = m_prepend(m, adjlen, 0); + if (m == NULL) + goto drop; + } + return m; + + drop: + counters_inc(ifp->if_counters, ifc_ierrors); + return NULL; } static void @@ -2380,7 +2425,15 @@ vport_clone_create(struct if_clone *ifc, ifp->if_qstart = vport_start; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_xflags = IFXF_CLONED | IFXF_MPSAFE; - ifp->if_capabilities = IFCAP_VLAN_HWTAGGING; + + ifp->if_capabilities = 0; +#if NVLAN > 0 + ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING; +#endif + ifp->if_capabilities |= IFCAP_CSUM_IPv4; + ifp->if_capabilities |= IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4; + ifp->if_capabilities |= IFCAP_CSUM_TCPv6 | IFCAP_CSUM_UDPv6; + ether_fakeaddr(ifp); if_counters_alloc(ifp);