]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet/ip_output.c
xnu-201.14.tar.gz
[apple/xnu.git] / bsd / netinet / ip_output.c
index 7f74f5998964012f9623bdd34d78b0e68baf54ec..ff9b2da853fbddad59a28bd070a4f1fdad6c8df1 100644 (file)
@@ -146,14 +146,10 @@ static int        ip_pcbopts __P((int, struct mbuf **, struct mbuf *));
 static int     ip_setmoptions
        __P((struct sockopt *, struct ip_moptions **));
 static u_long  lo_dl_tag = 0;
-
-#if IPFILTER_LKM || IPFILTER
-int    ip_optcopy __P((struct ip *, struct ip *));
-extern int (*fr_checkp) __P((struct ip *, int, struct ifnet *, int, struct mbuf **));
-#else
 static int     ip_optcopy __P((struct ip *, struct ip *));
-#endif
 
+void in_delayed_cksum(struct mbuf *m);
+extern int apple_hwcksum_tx;
 
 extern struct protosw inetsw[];
 
@@ -179,7 +175,7 @@ ip_output(m0, opt, ro, flags, imo)
        int len, off, error = 0;
        struct sockaddr_in *dst;
        struct in_ifaddr *ia;
-       int isbroadcast;
+       int isbroadcast, sw_csum;
 #if IPSEC
        struct route iproute;
        struct socket *so;
@@ -500,16 +496,6 @@ sendit:
         * - Wrap: fake packet's addr/port <unimpl.>
         * - Encapsulate: put it in another IP and send out. <unimp.>
         */ 
-#if IPFILTER || IPFILTER_LKM
-       if (fr_checkp) {
-               struct  mbuf    *m1 = m;
-
-               if ((error = (*fr_checkp)(ip, hlen, ifp, 1, &m1)) || !m1)
-                       goto done;
-               ip = mtod(m = m1, struct ip *);
-       }
-#endif
-
 #if COMPAT_IPFW
         if (ip_nat_ptr && !(*ip_nat_ptr)(&ip, &m, ifp, IP_NAT_OUT)) {
                error = EACCES; 
@@ -560,6 +546,22 @@ sendit:
 #endif   
 #if IPDIVERT
                 if (off > 0 && off < 0x10000) {         /* Divert packet */
+
+                        /*
+                         * delayed checksums are not currently compatible
+                         * with divert sockets.
+                         */
+                        if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
+                                in_delayed_cksum(m);
+                               if (m == NULL)
+                                       return(ENOMEM);
+                                m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
+                        }
+
+                        /* Restore packet header fields to original values */
+                       ip->ip_len = htons((u_short)ip->ip_len);
+                       ip->ip_off = htons((u_short)ip->ip_off);
+
                        ip_divert_port = off & 0xffff ;
                        (*ip_protox[IPPROTO_DIVERT]->pr_input)(m, 0);
                        goto done;
@@ -616,11 +618,17 @@ sendit:
                                ip_fw_fwd_addr = dst;
                                if (m->m_pkthdr.rcvif == NULL)
                                        m->m_pkthdr.rcvif = ifunit("lo0");
+
+                                if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { 
+                                        m->m_pkthdr.csum_flags |=
+                                            CSUM_DATA_VALID | CSUM_PSEUDO_HDR; 
+                                        m0->m_pkthdr.csum_data = 0xffff;    
+                                }
+                                m->m_pkthdr.csum_flags |=
+                                    CSUM_IP_CHECKED | CSUM_IP_VALID; 
                                ip->ip_len = htons((u_short)ip->ip_len);
                                ip->ip_off = htons((u_short)ip->ip_off);
-                               ip->ip_sum = 0;
-                               
-                               ip->ip_sum = in_cksum(m, hlen);
+
 
                                ip_input(m);
                                goto done;
@@ -732,9 +740,6 @@ pass:
                printf("ip_output: Invalid policy found. %d\n", sp->policy);
        }
 
-       ip->ip_len = htons((u_short)ip->ip_len);
-       ip->ip_off = htons((u_short)ip->ip_off);
-       ip->ip_sum = 0;
 
     {
        struct ipsec_output_state state;
@@ -747,6 +752,21 @@ pass:
                state.ro = ro;
        state.dst = (struct sockaddr *)dst;
 
+        ip->ip_sum = 0;
+            
+        /*
+         * delayed checksums are not currently compatible with IPsec
+         */
+        if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
+                in_delayed_cksum(m);
+               if (m == NULL)
+                       return(ENOMEM);
+                m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
+        }
+
+       ip->ip_len = htons((u_short)ip->ip_len);
+       ip->ip_off = htons((u_short)ip->ip_off);
+
        error = ipsec4_output(&state, sp, flags);
 
        m = state.m;
@@ -809,14 +829,52 @@ pass:
 skip_ipsec:
 #endif /*IPSEC*/
 
+
+               sw_csum = m->m_pkthdr.csum_flags | CSUM_IP;
+
+
+       /* frames that can be checksumed by GMACE SUM16 HW: frame >64, no fragments, no UDP odd length */ 
+
+       if (apple_hwcksum_tx && (sw_csum & CSUM_DELAY_DATA) && (ifp->if_hwassist & CSUM_TCP_SUM16)
+               && (ip->ip_len > 50) && (ip->ip_len <= ifp->if_mtu) 
+               && !((ip->ip_len & 0x1) && (sw_csum & CSUM_UDP)) ) {
+
+               /* Apple GMAC HW, expects STUFF_OFFSET << 16  | START_OFFSET */
+               u_short offset = (IP_VHL_HL(ip->ip_vhl) << 2) +14 ; /* IP+Enet header length */
+               u_short csumprev= m->m_pkthdr.csum_data & 0xFFFF;
+                       m->m_pkthdr.csum_flags = CSUM_DATA_VALID | CSUM_TCP_SUM16; /* for GMAC */
+               m->m_pkthdr.csum_data = (csumprev + offset)  << 16 ;
+               m->m_pkthdr.csum_data += offset; 
+                       sw_csum = CSUM_DELAY_IP; /* do IP hdr chksum in software */
+       }
+       else {
+               if (ifp->if_hwassist & CSUM_TCP_SUM16) /* force SW checksuming */ 
+                               m->m_pkthdr.csum_flags = 0;
+               else { /* not Apple enet */
+                               m->m_pkthdr.csum_flags = sw_csum & ifp->if_hwassist;
+                               sw_csum &= ~ifp->if_hwassist;
+               }
+
+               if (sw_csum & CSUM_DELAY_DATA) { /* perform TCP/UDP checksuming now */
+                               in_delayed_cksum(m);
+                       if (m == NULL)
+                               return(ENOMEM);
+                               sw_csum &= ~CSUM_DELAY_DATA;
+               }
+        }
+
        /*
-        * If small enough for interface, can just send directly.
+        * If small enough for interface, or the interface will take    
+        * care of the fragmentation for us, can just send directly.
         */
-       if ((u_short)ip->ip_len <= ifp->if_mtu) {
+        if ((u_short)ip->ip_len <= ifp->if_mtu ||
+            ifp->if_hwassist & CSUM_FRAGMENT) {
+
                ip->ip_len = htons((u_short)ip->ip_len);
                ip->ip_off = htons((u_short)ip->ip_off);
                ip->ip_sum = 0;
-               ip->ip_sum = in_cksum(m, hlen);
+                if (sw_csum & CSUM_DELAY_IP) 
+                       ip->ip_sum = in_cksum(m, hlen);
                error = dlil_output(dl_tag, m, (void *) ro->ro_rt,
                                    (struct sockaddr *)dst, 0);
                goto done;
@@ -848,9 +906,24 @@ skip_ipsec:
                goto bad;
        }
 
+        /*
+         * if the interface will not calculate checksums on
+         * fragmented packets, then do it here.
+         */
+        if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA &&
+            (ifp->if_hwassist & CSUM_IP_FRAGS) == 0) {
+                in_delayed_cksum(m);
+               if (m == NULL)
+                       return(ENOMEM);
+                m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
+        }
+
+
     {
        int mhlen, firstlen = len;
        struct mbuf **mnext = &m->m_nextpkt;
+        int nfrags = 1;
 
        /*
         * Loop through length of segment after first fragment,
@@ -865,7 +938,7 @@ skip_ipsec:
                        ipstat.ips_odropped++;
                        goto sendorfree;
                }
-               m->m_flags |= (m0->m_flags & M_MCAST);
+               m->m_flags |= (m0->m_flags & M_MCAST) | M_FRAG;
                m->m_data += max_linkhdr;
                mhip = mtod(m, struct ip *);
                *mhip = *ip;
@@ -891,13 +964,21 @@ skip_ipsec:
                }
                m->m_pkthdr.len = mhlen + len;
                m->m_pkthdr.rcvif = (struct ifnet *)0;
+                m->m_pkthdr.csum_flags = m0->m_pkthdr.csum_flags;
                mhip->ip_off = htons((u_short)mhip->ip_off);
                mhip->ip_sum = 0;
-               mhip->ip_sum = in_cksum(m, mhlen);
+                if (sw_csum & CSUM_DELAY_IP) 
+                       mhip->ip_sum = in_cksum(m, mhlen);
                *mnext = m;
                mnext = &m->m_nextpkt;
-               ipstat.ips_ofragments++;
+               nfrags++;
        }
+       ipstat.ips_ofragments += nfrags;
+
+       /* set first/last markers for fragment chain */
+       m0->m_flags |= M_FRAG;
+       m0->m_pkthdr.csum_data = nfrags;
+
        /*
         * Update first fragment by trimming what's been copied out
         * and updating header, then send each fragment (in order).
@@ -908,7 +989,8 @@ skip_ipsec:
        ip->ip_len = htons((u_short)m->m_pkthdr.len);
        ip->ip_off = htons((u_short)(ip->ip_off | IP_MF));
        ip->ip_sum = 0;
-       ip->ip_sum = in_cksum(m, hlen);
+        if (sw_csum & CSUM_DELAY_IP) 
+               ip->ip_sum = in_cksum(m, hlen);
 
 sendorfree:
 
@@ -948,6 +1030,43 @@ bad:
        goto done;
 }
 
+extern u_short in_chksum_skip(struct mbuf *, int, int);
+
+void
+in_delayed_cksum(struct mbuf *m)
+{
+        struct ip *ip;
+        u_short csum, csum2, offset;
+
+        ip = mtod(m, struct ip *);
+        offset = IP_VHL_HL(ip->ip_vhl) << 2 ;
+
+        csum = in_cksum_skip(m, ip->ip_len, offset);
+
+       if ((m->m_pkthdr.csum_flags & CSUM_UDP) && csum == 0)
+               csum = 0xffff;
+
+        offset += m->m_pkthdr.csum_data & 0xFFFF;        /* checksum offset */
+
+       if (offset > ip->ip_len) /* bogus offset */
+               return;
+
+        if (offset + sizeof(u_short) > m->m_len) {    
+                printf("delayed m_pullup, m->len: %d  off: %d  p: %d\n",
+                    m->m_len, offset, ip->ip_p);       
+                /*
+                 * XXX
+                 * this shouldn't happen, but if it does, the
+                 * correct behavior may be to insert the checksum
+                 * in the existing chain instead of rearranging it.
+                 */
+                if (m = m_pullup(m, offset + sizeof(u_short)) == 0)
+                       return;
+        }
+
+        *(u_short *)(m->m_data + offset) = csum;
+}
+
 /*
  * Insert IP options into preformed packet.
  * Adjust IP destination as required for IP source routing,
@@ -1001,9 +1120,6 @@ ip_insertoptions(m, opt, phlen)
  * Copy options from ip to jp,
  * omitting those not copied during fragmentation.
  */
-#if !IPFILTER && !IPFILTER_LKM
-static
-#endif
 int
 ip_optcopy(ip, jp)
        struct ip *ip, *jp;
@@ -1844,6 +1960,27 @@ ip_mloopback(ifp, m, dst, hlen)
                }
 #endif
 
+
+                /*
+                * Mark checksum as valid or calculate checksum for loopback.
+                * 
+                * This is done this way because we have to embed the ifp of
+                * the interface we will send the original copy of the packet
+                * out on in the mbuf. ip_input will check if_hwassist of the
+                * embedded ifp and ignore all csum_flags if if_hwassist is 0.
+                * The UDP checksum has not been calculated yet.
+                */
+                if (copym->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
+                        if (ifp->if_hwassist) {
+                                copym->m_pkthdr.csum_flags |=
+                                CSUM_DATA_VALID | CSUM_PSEUDO_HDR |
+                                CSUM_IP_CHECKED | CSUM_IP_VALID;
+                                copym->m_pkthdr.csum_data = 0xffff;
+                        } else
+                                in_delayed_cksum(copym);
+                }
+
+
                /*
                 * TedW: 
                 * We need to send all loopback traffic down to dlil in case