-#if IPFIREWALL
- /*
- * Check with the firewall...
- * but not if we are already being fwd'd from a firewall.
- */
- if (fw_enable && IPFW_LOADED && !args.fwa_next_hop) {
- struct sockaddr_in *old = dst;
-
- args.fwa_m = m;
- args.fwa_next_hop = dst;
- args.fwa_oif = ifp;
- ipfwoff = ip_fw_chk_ptr(&args);
- m = args.fwa_m;
- dst = args.fwa_next_hop;
-
- /*
- * On return we must do the following:
- * IP_FW_PORT_DENY_FLAG -> drop the pkt (XXX new)
- * 1<=off<= 0xffff -> DIVERT
- * (off & IP_FW_PORT_DYNT_FLAG) -> send to a DUMMYNET pipe
- * (off & IP_FW_PORT_TEE_FLAG) -> TEE the packet
- * dst != old -> IPFIREWALL_FORWARD
- * off==0, dst==old -> accept
- * If some of the above modules is not compiled in, then
- * we should't have to check the corresponding condition
- * (because the ipfw control socket should not accept
- * unsupported rules), but better play safe and drop
- * packets in case of doubt.
- */
- m0 = m;
- if ((ipfwoff & IP_FW_PORT_DENY_FLAG) || m == NULL) {
- if (m)
- m_freem(m);
- error = EACCES;
- goto done;
- }
- ip = mtod(m, struct ip *);
-
- if (ipfwoff == 0 && dst == old) { /* common case */
- goto pass;
- }
-#if DUMMYNET
- if (DUMMYNET_LOADED && (ipfwoff & IP_FW_PORT_DYNT_FLAG) != 0) {
- /*
- * pass the pkt to dummynet. Need to include
- * pipe number, m, ifp, ro, dst because these are
- * not recomputed in the next pass.
- * All other parameters have been already used and
- * so they are not needed anymore.
- * XXX note: if the ifp or ro entry are deleted
- * while a pkt is in dummynet, we are in trouble!
- */
- args.fwa_ro = ro;
- args.fwa_dst = dst;
- args.fwa_oflags = flags;
- if (flags & IP_OUTARGS)
- args.fwa_ipoa = ipoa;
-
- error = ip_dn_io_ptr(m, ipfwoff & 0xffff, DN_TO_IP_OUT,
- &args, DN_CLIENT_IPFW);
- goto done;
- }
-#endif /* DUMMYNET */
-#if IPDIVERT
- if (ipfwoff != 0 && (ipfwoff & IP_FW_PORT_DYNT_FLAG) == 0) {
- struct mbuf *clone = NULL;
-
- /* Clone packet if we're doing a 'tee' */
- if ((ipfwoff & IP_FW_PORT_TEE_FLAG) != 0)
- clone = m_dup(m, M_DONTWAIT);
- /*
- * XXX
- * delayed checksums are not currently compatible
- * with divert sockets.
- */
- if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA)
- in_delayed_cksum(m);
-
- /* Restore packet header fields to original values */
-
-#if BYTE_ORDER != BIG_ENDIAN
- HTONS(ip->ip_len);
- HTONS(ip->ip_off);
-#endif
-
- /* Deliver packet to divert input routine */
- divert_packet(m, 0, ipfwoff & 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 /* IPDIVERT */
-#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 (ipfwoff == 0 && old != dst) {
- struct in_ifaddr *ia_fw;
- struct route *ro_fwd = &sro_fwd;
-
-#if IPFIREWALL_FORWARD_DEBUG
- printf("IPFIREWALL_FORWARD: New dst ip: ");
- print_ip(dst->sin_addr);
- printf("\n");
-#endif /* IPFIREWALL_FORWARD_DEBUG */
- /*
- * 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 BYTE_ORDER != BIG_ENDIAN
- HTONS(ip->ip_len);
- HTONS(ip->ip_off);
-#endif
- mbuf_outbound_finalize(m, PF_INET, 0);
-
- /*
- * 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, NULL,
- SA(dst), 0, adv);
- } else {
- printf("%s: no loopback ifp for "
- "forwarding!!!\n", __func__);
- }
- 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?
- */
- ROUTE_RELEASE(ro_fwd);
- bcopy(dst, &ro_fwd->ro_dst, sizeof (*dst));
-
- rtalloc_ign(ro_fwd, RTF_PRCLONING, false);
-
- 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 = SIN(ro_fwd->ro_rt->rt_gateway);
- if (ro_fwd->ro_rt->rt_flags & RTF_HOST) {
- /* double negation needed for bool bit field */
- ipobf.isbroadcast =
- !!(ro_fwd->ro_rt->rt_flags & RTF_BROADCAST);
- } else {
- /* Become a regular mutex */
- RT_CONVERT_LOCK(ro_fwd->ro_rt);
- ipobf.isbroadcast =
- in_broadcast(dst->sin_addr, ifp);
- }
- RT_UNLOCK(ro_fwd->ro_rt);
- ROUTE_RELEASE(ro);
- ro->ro_rt = ro_fwd->ro_rt;
- ro_fwd->ro_rt = NULL;
- dst = SIN(&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 (ipobf.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 */