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[];
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;
* - 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;
#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;
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;
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;
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;
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;
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,
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;
}
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).
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:
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,
* 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;
}
#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