+ /* Deliver packet to divert input routine */
+ divert_packet(m, 0, off & 0xffff, args.fwa_divert_rule);
+
+ /* If 'tee', continue with original packet */
+ if (clone != NULL) {
+ m0 = m = clone;
+ ip = mtod(m, struct ip *);
+ goto pass;
+ }
+ goto done;
+ }
+#endif
+
+#if IPFIREWALL_FORWARD
+ /* Here we check dst to make sure it's directly reachable on the
+ * interface we previously thought it was.
+ * If it isn't (which may be likely in some situations) we have
+ * to re-route it (ie, find a route for the next-hop and the
+ * associated interface) and set them here. This is nested
+ * forwarding which in most cases is undesirable, except where
+ * such control is nigh impossible. So we do it here.
+ * And I'm babbling.
+ */
+ if (off == 0 && old != dst) {
+ struct in_ifaddr *ia_fw;
+
+ /* It's changed... */
+ /* There must be a better way to do this next line... */
+ static struct route sro_fwd, *ro_fwd = &sro_fwd;
+#if IPFIREWALL_FORWARD_DEBUG
+ printf("IPFIREWALL_FORWARD: New dst ip: ");
+ print_ip(dst->sin_addr);
+ printf("\n");
+#endif
+ /*
+ * We need to figure out if we have been forwarded
+ * to a local socket. If so then we should somehow
+ * "loop back" to ip_input, and get directed to the
+ * PCB as if we had received this packet. This is
+ * because it may be dificult to identify the packets
+ * you want to forward until they are being output
+ * and have selected an interface. (e.g. locally
+ * initiated packets) If we used the loopback inteface,
+ * we would not be able to control what happens
+ * as the packet runs through ip_input() as
+ * it is done through a ISR.
+ */
+ lck_rw_lock_shared(in_ifaddr_rwlock);
+ TAILQ_FOREACH(ia_fw, &in_ifaddrhead, ia_link) {
+ /*
+ * If the addr to forward to is one
+ * of ours, we pretend to
+ * be the destination for this packet.
+ */
+ IFA_LOCK_SPIN(&ia_fw->ia_ifa);
+ if (IA_SIN(ia_fw)->sin_addr.s_addr ==
+ dst->sin_addr.s_addr) {
+ IFA_UNLOCK(&ia_fw->ia_ifa);
+ break;
+ }
+ IFA_UNLOCK(&ia_fw->ia_ifa);
+ }
+ lck_rw_done(in_ifaddr_rwlock);
+ if (ia_fw) {
+ /* tell ip_input "dont filter" */
+ struct m_tag *fwd_tag;
+ struct ip_fwd_tag *ipfwd_tag;
+
+ fwd_tag = m_tag_create(KERNEL_MODULE_TAG_ID,
+ KERNEL_TAG_TYPE_IPFORWARD,
+ sizeof (*ipfwd_tag), M_NOWAIT, m);
+ if (fwd_tag == NULL) {
+ error = ENOBUFS;
+ goto bad;
+ }
+
+ ipfwd_tag = (struct ip_fwd_tag *)(fwd_tag+1);
+ ipfwd_tag->next_hop = args.fwa_next_hop;
+
+ m_tag_prepend(m, fwd_tag);
+
+ if (m->m_pkthdr.rcvif == NULL)
+ m->m_pkthdr.rcvif = lo_ifp;
+ if ((~IF_HWASSIST_CSUM_FLAGS(m->m_pkthdr.rcvif->if_hwassist) &
+ m->m_pkthdr.csum_flags) == 0) {
+ if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
+ m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
+ m->m_pkthdr.csum_flags |=
+ CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
+ m->m_pkthdr.csum_flags |=
+ CSUM_IP_CHECKED | CSUM_IP_VALID;
+ }
+ else if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
+ in_delayed_cksum(m);
+ m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
+ ip->ip_sum = in_cksum(m, hlen);
+ }
+
+#if BYTE_ORDER != BIG_ENDIAN
+ HTONS(ip->ip_len);
+ HTONS(ip->ip_off);
+#endif
+
+ /* we need to call dlil_output to run filters
+ * and resync to avoid recursion loops.
+ */
+ if (lo_ifp) {
+ dlil_output(lo_ifp, PF_INET, m, 0,
+ (struct sockaddr *)dst, 0, adv);
+ }
+ else {
+ printf("ip_output: no loopback ifp for forwarding!!!\n");
+ }
+ goto done;
+ }
+ /* Some of the logic for this was
+ * nicked from above.
+ *
+ * This rewrites the cached route in a local PCB.
+ * Is this what we want to do?
+ */
+ bcopy(dst, &ro_fwd->ro_dst, sizeof(*dst));
+
+ ro_fwd->ro_rt = NULL;
+ rtalloc_ign(ro_fwd, RTF_PRCLONING);
+
+ if (ro_fwd->ro_rt == NULL) {
+ OSAddAtomic(1, &ipstat.ips_noroute);
+ error = EHOSTUNREACH;
+ goto bad;
+ }
+
+ RT_LOCK_SPIN(ro_fwd->ro_rt);
+ ia_fw = ifatoia(ro_fwd->ro_rt->rt_ifa);
+ if (ia_fw != NULL) {
+ /* Become a regular mutex */
+ RT_CONVERT_LOCK(ro_fwd->ro_rt);
+ IFA_ADDREF(&ia_fw->ia_ifa);
+ }
+ ifp = ro_fwd->ro_rt->rt_ifp;
+ ro_fwd->ro_rt->rt_use++;
+ if (ro_fwd->ro_rt->rt_flags & RTF_GATEWAY)
+ dst = (struct sockaddr_in *)(void *)ro_fwd->ro_rt->rt_gateway;
+ if (ro_fwd->ro_rt->rt_flags & RTF_HOST) {
+ isbroadcast =
+ (ro_fwd->ro_rt->rt_flags & RTF_BROADCAST);
+ } else {
+ /* Become a regular mutex */
+ RT_CONVERT_LOCK(ro_fwd->ro_rt);
+ isbroadcast = in_broadcast(dst->sin_addr, ifp);
+ }
+ RT_UNLOCK(ro_fwd->ro_rt);
+ rtfree(ro->ro_rt);
+ ro->ro_rt = ro_fwd->ro_rt;
+ dst = (struct sockaddr_in *)(void *)&ro_fwd->ro_dst;
+
+ /*
+ * If we added a default src ip earlier,
+ * which would have been gotten from the-then
+ * interface, do it again, from the new one.
+ */
+ if (ia_fw != NULL) {
+ if (fwd_rewrite_src) {
+ IFA_LOCK_SPIN(&ia_fw->ia_ifa);
+ ip->ip_src = IA_SIN(ia_fw)->sin_addr;
+ IFA_UNLOCK(&ia_fw->ia_ifa);
+ }
+ IFA_REMREF(&ia_fw->ia_ifa);
+ }
+ goto pass ;
+ }
+#endif /* IPFIREWALL_FORWARD */
+ /*
+ * if we get here, none of the above matches, and
+ * we have to drop the pkt
+ */
+ m_freem(m);
+ error = EACCES; /* not sure this is the right error msg */
+ goto done;
+ }
+
+pass:
+#endif /* IPFIREWALL */
+#if __APPLE__
+ /* Do not allow loopback address to wind up on a wire */
+ if ((ifp->if_flags & IFF_LOOPBACK) == 0 &&
+ ((ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||
+ (ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)) {
+ OSAddAtomic(1, &ipstat.ips_badaddr);
+ m_freem(m);
+ /*
+ * Do not simply drop the packet just like a firewall -- we want the
+ * the application to feel the pain.
+ * Return ENETUNREACH like ip6_output does in some similar cases.
+ * This can startle the otherwise clueless process that specifies
+ * loopback as the source address.
+ */
+ error = ENETUNREACH;
+ goto done;
+ }
+#endif
+ m->m_pkthdr.csum_flags |= CSUM_IP;
+ tso = (ifp->if_hwassist & IFNET_TSO_IPV4) && (m->m_pkthdr.csum_flags & CSUM_TSO_IPV4);
+
+ sw_csum = m->m_pkthdr.csum_flags
+ & ~IF_HWASSIST_CSUM_FLAGS(ifp->if_hwassist);
+
+ if ((ifp->if_hwassist & CSUM_TCP_SUM16) != 0) {
+ /*
+ * Special case code for GMACE
+ * frames that can be checksumed by GMACE SUM16 HW:
+ * frame >64, no fragments, no UDP
+ */
+ if (apple_hwcksum_tx && (m->m_pkthdr.csum_flags & CSUM_TCP)
+ && (ip->ip_len > 50) && (ip->ip_len <= ifp->if_mtu)) {
+ /* 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 {
+ /* let the software handle any UDP or TCP checksums */
+ sw_csum |= (CSUM_DELAY_DATA & m->m_pkthdr.csum_flags);
+ }
+ } else if (apple_hwcksum_tx == 0) {
+ sw_csum |= (CSUM_DELAY_DATA | CSUM_DELAY_IP) &
+ m->m_pkthdr.csum_flags;
+ }
+
+ if (sw_csum & CSUM_DELAY_DATA) {
+ in_delayed_cksum(m);
+ sw_csum &= ~CSUM_DELAY_DATA;
+ m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
+ }
+
+ if (apple_hwcksum_tx != 0) {
+ m->m_pkthdr.csum_flags &=
+ IF_HWASSIST_CSUM_FLAGS(ifp->if_hwassist);
+ } else {
+ m->m_pkthdr.csum_flags = 0;
+ }
+
+ /*
+ * 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 || tso ||
+ ifp->if_hwassist & CSUM_FRAGMENT) {
+ if (tso)
+ m->m_pkthdr.csum_flags |= CSUM_TSO_IPV4;
+
+
+#if BYTE_ORDER != BIG_ENDIAN
+ HTONS(ip->ip_len);
+ HTONS(ip->ip_off);
+#endif
+
+ ip->ip_sum = 0;
+ if (sw_csum & CSUM_DELAY_IP) {
+ ip->ip_sum = in_cksum(m, hlen);
+ }
+
+#ifndef __APPLE__
+ /* Record statistics for this interface address. */
+ if (!(flags & IP_FORWARDING) && ia != NULL) {
+ ia->ia_ifa.if_opackets++;
+ ia->ia_ifa.if_obytes += m->m_pkthdr.len;
+ }
+#endif