Do not calculate IP, TCP, UDP checksums on loopback interface.
authorbluhm <bluhm@openbsd.org>
Mon, 5 Jun 2023 11:35:46 +0000 (11:35 +0000)
committerbluhm <bluhm@openbsd.org>
Mon, 5 Jun 2023 11:35:46 +0000 (11:35 +0000)
Packets sent over loopback got their checksums calculated twice.
In the output path they were filled in and during TCP/IP input all
checksums were calculated again to be compared with the previous
result.

Avoid this 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.
Keep the flag that it should be calculated for the case that it may
be bridged or forwarded later.

A drawback is that "tcpdump -ni lo0 -v" reports invalid checksum.
But that is the same with physical interfaces and hardware offloading.

OK dlg@

sys/net/if.c
sys/net/if_loop.c

index 36c5233..208dd44 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if.c,v 1.698 2023/05/30 23:55:42 dlg Exp $    */
+/*     $OpenBSD: if.c,v 1.699 2023/06/05 11:35:46 bluhm Exp $  */
 /*     $NetBSD: if.c,v 1.35 1996/05/07 05:26:04 thorpej Exp $  */
 
 /*
@@ -778,7 +778,7 @@ if_input(struct ifnet *ifp, struct mbuf_list *ml)
 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 mbuf *m, sa_family_t af)
        }
 #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 mbuf *m, sa_family_t af)
 
        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 4077198..d96f5ea 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_loop.c,v 1.93 2022/10/21 14:20:03 kn Exp $ */
+/*     $OpenBSD: if_loop.c,v 1.94 2023/06/05 11:35:46 bluhm Exp $      */
 /*     $NetBSD: if_loop.c,v 1.15 1996/05/07 02:40:33 thorpej Exp $     */
 
 /*
@@ -173,6 +173,9 @@ loop_clone_create(struct if_clone *ifc, int unit)
        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;