Hi, Currently packets sent over loopback interface get their checksum calculated twice. In the output path it is set and during TCP/IP input it is calculated again to be compared with the previous value.
This can be avoided by claiming that lo(4) supports hardware checksum offloading. For each packet convert the flag that the checksum should be calculated to the flag that it has been checked successfully. In a simple test on a vmm guest I see between 30% to 60% increase of thoughput over lo0. A drawback is that "tcpdump -ni lo0 -v" reports invalid checksum. But people are used to that with physical interfaces and hardware offloading. The question is, does it break corner cases? Please test with pf route-to, IPsec, bridging, IPv6 and other setups where loopback might be involved. bluhm Index: net/if.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/net/if.c,v retrieving revision 1.697 diff -u -p -r1.697 if.c --- net/if.c 16 May 2023 14:32:54 -0000 1.697 +++ net/if.c 30 May 2023 12:33:42 -0000 @@ -778,7 +778,7 @@ if_input(struct ifnet *ifp, struct mbuf_ int if_input_local(struct ifnet *ifp, struct mbuf *m, sa_family_t af) { - int keepflags; + int keepflags, keepcksum; #if NBPFILTER > 0 /* @@ -796,11 +796,26 @@ if_input_local(struct ifnet *ifp, struct } #endif keepflags = m->m_flags & (M_BCAST|M_MCAST); + /* + * Preserve outgoing checksum flags, in case the packet is + * forwarded to another interface. Then the checksum, which + * is now incorrect, will be calculated before sending. + */ + keepcksum = m->m_pkthdr.csum_flags & (M_IPV4_CSUM_OUT | + M_TCP_CSUM_OUT | M_UDP_CSUM_OUT | M_ICMP_CSUM_OUT); m_resethdr(m); m->m_flags |= M_LOOP | keepflags; + m->m_pkthdr.csum_flags = keepcksum; m->m_pkthdr.ph_ifidx = ifp->if_index; m->m_pkthdr.ph_rtableid = ifp->if_rdomain; + if (ISSET(keepcksum, M_TCP_CSUM_OUT)) + m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_OK; + if (ISSET(keepcksum, M_UDP_CSUM_OUT)) + m->m_pkthdr.csum_flags |= M_UDP_CSUM_IN_OK; + if (ISSET(keepcksum, M_ICMP_CSUM_OUT)) + m->m_pkthdr.csum_flags |= M_ICMP_CSUM_IN_OK; + ifp->if_opackets++; ifp->if_obytes += m->m_pkthdr.len; @@ -809,6 +824,8 @@ if_input_local(struct ifnet *ifp, struct switch (af) { case AF_INET: + if (ISSET(keepcksum, M_IPV4_CSUM_OUT)) + m->m_pkthdr.csum_flags |= M_IPV4_CSUM_IN_OK; ipv4_input(ifp, m); break; #ifdef INET6 Index: net/if_loop.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/net/if_loop.c,v retrieving revision 1.93 diff -u -p -r1.93 if_loop.c --- net/if_loop.c 21 Oct 2022 14:20:03 -0000 1.93 +++ net/if_loop.c 30 May 2023 11:48:55 -0000 @@ -173,6 +173,9 @@ loop_clone_create(struct if_clone *ifc, ifp->if_mtu = LOMTU; ifp->if_flags = IFF_LOOPBACK | IFF_MULTICAST; ifp->if_xflags = IFXF_CLONED; + ifp->if_capabilities = IFCAP_CSUM_IPv4 | + IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4 | + IFCAP_CSUM_TCPv6 | IFCAP_CSUM_UDPv6; ifp->if_rtrequest = lortrequest; ifp->if_ioctl = loioctl; ifp->if_input = loinput;