Simplex interface sends packet back without hardware checksum
authorbluhm <bluhm@openbsd.org>
Sat, 6 Feb 2021 13:15:37 +0000 (13:15 +0000)
committerbluhm <bluhm@openbsd.org>
Sat, 6 Feb 2021 13:15:37 +0000 (13:15 +0000)
offloading.  The checksum must be calculated in software.  Use the
same condition in ether_resolve() to send the broadcast packet back
to the stack and in in_ifcap_cksum() to force software checksumming.
This fixes regress/sys/kern/sosplice/loop.
OK procter@

sys/net/if_ethersubr.c
sys/netinet/ip_output.c

index 6a8c678..47d7495 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_ethersubr.c,v 1.269 2021/02/05 16:47:48 bluhm Exp $        */
+/*     $OpenBSD: if_ethersubr.c,v 1.270 2021/02/06 13:15:37 bluhm Exp $        */
 /*     $NetBSD: if_ethersubr.c,v 1.19 1996/05/07 02:40:30 thorpej Exp $        */
 
 /*
@@ -227,7 +227,11 @@ ether_resolve(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
                        return (error);
                eh->ether_type = htons(ETHERTYPE_IP);
 
-               /* If broadcasting on a simplex interface, loopback a copy */
+               /*
+                * If broadcasting on a simplex interface, loopback a copy.
+                * The checksum must be calculated in software.  Keep the
+                * condition in sync with in_ifcap_cksum().
+                */
                if (ISSET(m->m_flags, M_BCAST) &&
                    ISSET(ifp->if_flags, IFF_SIMPLEX) &&
                    !m->m_pkthdr.pf.routed) {
index cd84b3a..b32adb4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ip_output.c,v 1.363 2021/02/02 17:47:42 claudio Exp $ */
+/*     $OpenBSD: ip_output.c,v 1.364 2021/02/06 13:15:37 bluhm Exp $   */
 /*     $NetBSD: ip_output.c,v 1.28 1996/02/13 23:43:07 christos Exp $  */
 
 /*
@@ -79,6 +79,7 @@ void ip_mloopback(struct ifnet *, struct mbuf *, struct sockaddr_in *);
 static __inline u_int16_t __attribute__((__unused__))
     in_cksum_phdr(u_int32_t, u_int32_t, u_int32_t);
 void in_delayed_cksum(struct mbuf *);
+int in_ifcap_cksum(struct mbuf *, struct ifnet *, int);
 
 #ifdef IPSEC
 struct tdb *
@@ -458,8 +459,7 @@ sendit:
         */
        if (ntohs(ip->ip_len) <= mtu) {
                ip->ip_sum = 0;
-               if ((ifp->if_capabilities & IFCAP_CSUM_IPv4) &&
-                   (ifp->if_bridgeidx == 0))
+               if (in_ifcap_cksum(m, ifp, IFCAP_CSUM_IPv4))
                        m->m_pkthdr.csum_flags |= M_IPV4_CSUM_OUT;
                else {
                        ipstat_inc(ips_outswcsum);
@@ -719,9 +719,7 @@ ip_fragment(struct mbuf *m, struct ifnet *ifp, u_long mtu)
                m->m_pkthdr.ph_ifidx = 0;
                mhip->ip_off = htons((u_int16_t)mhip->ip_off);
                mhip->ip_sum = 0;
-               if ((ifp != NULL) &&
-                   (ifp->if_capabilities & IFCAP_CSUM_IPv4) &&
-                   (ifp->if_bridgeidx == 0))
+               if (in_ifcap_cksum(m, ifp, IFCAP_CSUM_IPv4))
                        m->m_pkthdr.csum_flags |= M_IPV4_CSUM_OUT;
                else {
                        ipstat_inc(ips_outswcsum);
@@ -740,9 +738,7 @@ ip_fragment(struct mbuf *m, struct ifnet *ifp, u_long mtu)
        ip->ip_len = htons((u_int16_t)m->m_pkthdr.len);
        ip->ip_off |= htons(IP_MF);
        ip->ip_sum = 0;
-       if ((ifp != NULL) &&
-           (ifp->if_capabilities & IFCAP_CSUM_IPv4) &&
-           (ifp->if_bridgeidx == 0))
+       if (in_ifcap_cksum(m, ifp, IFCAP_CSUM_IPv4))
                m->m_pkthdr.csum_flags |= M_IPV4_CSUM_OUT;
        else {
                ipstat_inc(ips_outswcsum);
@@ -1855,15 +1851,15 @@ in_proto_cksum_out(struct mbuf *m, struct ifnet *ifp)
        }
 
        if (m->m_pkthdr.csum_flags & M_TCP_CSUM_OUT) {
-               if (!ifp || !(ifp->if_capabilities & IFCAP_CSUM_TCPv4) ||
-                   ip->ip_hl != 5 || ifp->if_bridgeidx != 0) {
+               if (!in_ifcap_cksum(m, ifp, IFCAP_CSUM_TCPv4) ||
+                   ip->ip_hl != 5) {
                        tcpstat_inc(tcps_outswcsum);
                        in_delayed_cksum(m);
                        m->m_pkthdr.csum_flags &= ~M_TCP_CSUM_OUT; /* Clear */
                }
        } else if (m->m_pkthdr.csum_flags & M_UDP_CSUM_OUT) {
-               if (!ifp || !(ifp->if_capabilities & IFCAP_CSUM_UDPv4) ||
-                   ip->ip_hl != 5 || ifp->if_bridgeidx != 0) {
+               if (!in_ifcap_cksum(m, ifp, IFCAP_CSUM_UDPv4) ||
+                   ip->ip_hl != 5) {
                        udpstat_inc(udps_outswcsum);
                        in_delayed_cksum(m);
                        m->m_pkthdr.csum_flags &= ~M_UDP_CSUM_OUT; /* Clear */
@@ -1873,3 +1869,22 @@ in_proto_cksum_out(struct mbuf *m, struct ifnet *ifp)
                m->m_pkthdr.csum_flags &= ~M_ICMP_CSUM_OUT; /* Clear */
        }
 }
+
+int
+in_ifcap_cksum(struct mbuf *m, struct ifnet *ifp, int ifcap)
+{
+       if ((ifp == NULL) ||
+           !ISSET(ifp->if_capabilities, ifcap) ||
+           (ifp->if_bridgeidx != 0))
+               return (0);
+       /*
+        * Simplex interface sends packet back without hardware cksum.
+        * Keep this check in sync with the condition where ether_resolve()
+        * calls if_input_local().
+        */
+       if (ISSET(m->m_flags, M_BCAST) &&
+           ISSET(ifp->if_flags, IFF_SIMPLEX) &&
+           !m->m_pkthdr.pf.routed)
+               return (0);
+       return (1);
+}