Index: conf/files
===================================================================
RCS file: /home/ncvs/src/sys/conf/files,v
retrieving revision 1.850
diff -u -p -r1.850 files
--- conf/files	12 Nov 2003 04:22:36 -0000	1.850
+++ conf/files	12 Nov 2003 20:27:47 -0000
@@ -1448,6 +1448,7 @@ netinet/ip_ecn.c	optional inet
 netinet/ip_ecn.c	optional inet6
 netinet/ip_encap.c	optional inet
 netinet/ip_encap.c	optional inet6
+netinet/ip_fastforward.c	optional inet
 netinet/ip_flow.c	optional inet
 netinet/ip_fw2.c	optional ipfirewall
 netinet/ip_icmp.c	optional inet
Index: net/if_arcsubr.c
===================================================================
RCS file: /home/ncvs/src/sys/net/if_arcsubr.c,v
retrieving revision 1.14
diff -u -p -r1.14 if_arcsubr.c
--- net/if_arcsubr.c	31 Oct 2003 18:32:08 -0000	1.14
+++ net/if_arcsubr.c	12 Nov 2003 20:27:52 -0000
@@ -543,14 +543,14 @@ arc_input(ifp, m)
 #ifdef INET
 	case ARCTYPE_IP:
 		m_adj(m, ARC_HDRNEWLEN);
-		if (ipflow_fastforward(m))
+		if (ip_fastforward(m))
 			return;
 		isr = NETISR_IP;
 		break;
 
 	case ARCTYPE_IP_OLD:
 		m_adj(m, ARC_HDRLEN);
-		if (ipflow_fastforward(m))
+		if (ip_fastforward(m))
 			return;
 		isr = NETISR_IP;
 		break;
Index: net/if_ef.c
===================================================================
RCS file: /home/ncvs/src/sys/net/if_ef.c,v
retrieving revision 1.26
diff -u -p -r1.26 if_ef.c
--- net/if_ef.c	31 Oct 2003 18:32:08 -0000	1.26
+++ net/if_ef.c	12 Nov 2003 20:27:52 -0000
@@ -253,7 +253,7 @@ ef_inputEII(struct mbuf *m, struct ether
 #endif
 #ifdef INET
 	case ETHERTYPE_IP:
-		if (ipflow_fastforward(m))
+		if (ip_fastforward(m))
 			return (0);
 		isr = NETISR_IP;
 		break;
Index: net/if_ethersubr.c
===================================================================
RCS file: /home/ncvs/src/sys/net/if_ethersubr.c,v
retrieving revision 1.154
diff -u -p -r1.154 if_ethersubr.c
--- net/if_ethersubr.c	31 Oct 2003 18:32:08 -0000	1.154
+++ net/if_ethersubr.c	12 Nov 2003 20:27:52 -0000
@@ -714,7 +714,7 @@ post_stats:
 	switch (ether_type) {
 #ifdef INET
 	case ETHERTYPE_IP:
-		if (ipflow_fastforward(m))
+		if (ip_fastforward(m))
 			return;
 		isr = NETISR_IP;
 		break;
Index: net/if_fddisubr.c
===================================================================
RCS file: /home/ncvs/src/sys/net/if_fddisubr.c,v
retrieving revision 1.86
diff -u -p -r1.86 if_fddisubr.c
--- net/if_fddisubr.c	23 Oct 2003 17:47:55 -0000	1.86
+++ net/if_fddisubr.c	12 Nov 2003 20:27:52 -0000
@@ -471,7 +471,7 @@ fddi_input(ifp, m)
 		switch (type) {
 #ifdef INET
 		case ETHERTYPE_IP:
-			if (ipflow_fastforward(m))
+			if (ip_fastforward(m))
 				return;
 			isr = NETISR_IP;
 			break;
Index: net/if_iso88025subr.c
===================================================================
RCS file: /home/ncvs/src/sys/net/if_iso88025subr.c,v
retrieving revision 1.57
diff -u -p -r1.57 if_iso88025subr.c
--- net/if_iso88025subr.c	14 Sep 2003 02:32:31 -0000	1.57
+++ net/if_iso88025subr.c	12 Nov 2003 20:27:52 -0000
@@ -556,7 +556,7 @@ iso88025_input(ifp, m)
 #ifdef INET
 		case ETHERTYPE_IP:
 			th->iso88025_shost[0] &= ~(TR_RII); 
-			if (ipflow_fastforward(m))
+			if (ip_fastforward(m))
 				return;
 			isr = NETISR_IP;
 			break;
Index: net/if_ppp.c
===================================================================
RCS file: /home/ncvs/src/sys/net/if_ppp.c,v
retrieving revision 1.93
diff -u -p -r1.93 if_ppp.c
--- net/if_ppp.c	8 Nov 2003 22:28:39 -0000	1.93
+++ net/if_ppp.c	12 Nov 2003 20:27:52 -0000
@@ -1537,7 +1537,7 @@ ppp_inproc(sc, m)
 	m->m_pkthdr.len -= PPP_HDRLEN;
 	m->m_data += PPP_HDRLEN;
 	m->m_len -= PPP_HDRLEN;
-	if (ipflow_fastforward(m))
+	if (ip_fastforward(m))
 	    return;
 	isr = NETISR_IP;
 	break;
Index: netinet/in_var.h
===================================================================
RCS file: /home/ncvs/src/sys/netinet/in_var.h,v
retrieving revision 1.47
diff -u -p -r1.47 in_var.h
--- netinet/in_var.h	20 Aug 2003 17:09:01 -0000	1.47
+++ netinet/in_var.h	12 Nov 2003 20:27:53 -0000
@@ -233,6 +233,7 @@ void	in_ifscrub(struct ifnet *, struct i
 int	ipflow_fastforward(struct mbuf *);
 void	ipflow_create(const struct route *, struct mbuf *);
 void	ipflow_slowtimo(void);
+int	ip_fastforward(struct mbuf *);
 
 #endif /* _KERNEL */
 
Index: sys/mbuf.h
===================================================================
RCS file: /home/ncvs/src/sys/sys/mbuf.h,v
retrieving revision 1.125
diff -u -p -r1.125 mbuf.h
--- sys/mbuf.h	29 Oct 2003 05:40:07 -0000	1.125
+++ sys/mbuf.h	12 Nov 2003 20:27:54 -0000
@@ -558,6 +558,7 @@ struct	mbuf	*m_split(struct mbuf *, int,
 #define	PACKET_TAG_DIVERT			17 /* divert info */
 #define	PACKET_TAG_IPFORWARD			18 /* ipforward info */
 #define	PACKET_TAG_MACLABEL	(19 | MTAG_PERSISTENT) /* MAC label */
+#define	PACKET_TAG_IPFASTFWD_OURS		20 /* IP fastforward dropback */
 
 /* Packet tag routines */
 struct	m_tag 	*m_tag_alloc(u_int32_t, int, int, int);
--- netinet/ip_fastforward.c	Wed Nov 12 21:28:23 2003
+++ netinet/ip_fastforward.c	Wed Nov 12 21:05:38 2003
@@ -0,0 +1,837 @@
+/*
+ * Copyright (c) 2003 Andre Oppermann, Internet Business Solutions AG
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/netinet/ip_fastforward.c,v 1.1 2003/10/14 20:01:36 andre Exp $
+ */
+
+/*
+ * ip_fastforward gets its speed from processing the forwarded packet to
+ * completion (if_output on the other side) without any queues or netisr's.
+ * The receiving interface DMAs the packet into memory, the upper half of
+ * driver calls ip_fastforward, we do our routing table lookup and directly
+ * send it off to the outgoing interface which DMAs the packet to the
+ * network card. The only part of the packet we touch with the CPU is the
+ * IP header. We are essentially limited by bus bandwidth and how fast the
+ * network card/driver can set up receives and transmits.
+ *
+ * We handle basic errors, ip header errors, checksum errors,
+ * destination unreachable, fragmentation and fragmentation needed and
+ * report them via icmp to the sender.
+ *
+ * Else if something is not pure IPv4 unicast forwarding we fall back to
+ * the normal ip_input processing path. We should only be called from
+ * interfaces connected to the outside world.
+ *
+ * Firewalling is fully supported including divert, ipfw fwd and ipfilter
+ * ipnat and address rewrite.
+ *
+ * IPSEC is not supported if this host is a tunnel broker. IPSEC is
+ * supported for connections to/from local host.
+ *
+ * We try to do the least expensive (in CPU ops) checks and operations
+ * first to catch junk with as little overhead as possible.
+ * 
+ * We take full advantage of hardware support for ip checksum and
+ * fragmentation offloading.
+ *
+ * We don't do ICMP redirect in the fast forwarding path. I have had my own
+ * cases where two core routers with Zebra routing suite would send millions
+ * ICMP redirects to connected hosts if the router to dest was not the default
+ * gateway. In one case it was filling the routing table of a host with close
+ * 300'000 cloned redirect entries until it ran out of kernel memory. However
+ * the networking code proved very robust and it didn't crash or went ill
+ * otherwise.
+ */
+
+/*
+ * Many thanks to Matt Thomas of NetBSD for basic structure of ip_flow.c which
+ * is being followed here.
+ */
+
+#include "opt_ipfw.h"
+#include "opt_ipdn.h"
+#include "opt_ipdivert.h"
+#include "opt_ipfilter.h"
+#include "opt_ipstealth.h"
+#include "opt_mac.h"
+#include "opt_pfil_hooks.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mac.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/pfil.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_var.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_icmp.h>
+
+#include <machine/in_cksum.h>
+
+#include <netinet/ip_fw.h>
+#include <netinet/ip_dummynet.h>
+
+static int ipfastforward_active = 0;
+SYSCTL_INT(_net_inet_ip, IPCTL_FASTFORWARDING, fastforwarding, CTLFLAG_RW,
+    &ipfastforward_active, 0, "Enable fast IP forwarding");
+
+/*
+ * return 0, not successful, ip_input has to take over (ie. local addresses)
+ * return 1, successful, job is done and we have got rid of mbuf(s)
+ */
+
+int
+ip_fastforward(struct mbuf *m)
+{
+	struct ip *ip;
+	struct mbuf *m0 = NULL;
+#ifdef IPDIVERT
+	struct ip *tip;
+	struct mbuf *teem = NULL;
+#endif
+	struct mbuf *tag = NULL;
+	struct route ro;
+	struct sockaddr_in *dst = NULL;
+	struct in_ifaddr *ia = NULL;
+	struct ifaddr *ifa = NULL;
+	struct ifnet *ifp = NULL;
+	struct ip_fw_args args;
+	in_addr_t odest, dest;
+	u_short sum;
+	int hlen;
+	int error = 0;
+	int ipfw;
+
+	/*
+	 * Are we active and forwarding packets?
+	 */
+	if (!ipfastforward_active || !ipforwarding)
+		return 0;
+
+	/*
+	 * If there is any MT_TAG we fall back to ip_input because we can't
+	 * handle TAGs here.
+	 */
+	if (m && m->m_type == MT_TAG)
+		return 0;
+
+	M_ASSERTVALID(m);
+	M_ASSERTPKTHDR(m);
+
+	/**
+	 ** First: check for packet drop conditions (and sanity checks)
+	 **/
+
+	/*
+	 * Is entire packet big enough?
+	 */
+	if (m->m_pkthdr.len < sizeof(struct ip)) {
+		ipstat.ips_tooshort++;
+		m_freem(m);
+		return 1;
+	}
+
+	/*
+	 * Is first mbuf large enough for ip header and is header present?
+	 */
+	if (m->m_len < sizeof (struct ip) &&
+	   (m = m_pullup(m, sizeof (struct ip))) == 0) {
+		ipstat.ips_toosmall++;
+		m_freem(m);
+		return 1;
+	}
+
+	ip = mtod(m, struct ip *);
+
+	/*
+	 * Is it IPv4?
+	 */
+	if (ip->ip_v != IPVERSION) {
+		ipstat.ips_badvers++;
+		m_freem(m);
+		return 1;
+	}
+
+	/*
+	 * Is IP header length correct and is it in first mbuf?
+	 */
+	hlen = ip->ip_hl << 2;
+	if (hlen < sizeof(struct ip)) {	/* minimum header length */
+		ipstat.ips_badlen++;
+		m_freem(m);
+		return 1;
+	}
+	if (hlen > m->m_len) {
+		if ((m = m_pullup(m, hlen)) == 0) {
+			ipstat.ips_badhlen++;
+			m_freem(m);
+			return 1;
+		}
+		ip = mtod(m, struct ip *);
+	}
+
+	/*
+	 * Checksum correct?
+	 */
+	if (m->m_pkthdr.csum_flags & CSUM_IP_CHECKED)
+		sum = !(m->m_pkthdr.csum_flags & CSUM_IP_VALID);
+	else {
+		if (hlen == sizeof(struct ip))
+			sum = in_cksum_hdr(ip);
+		else
+			sum = in_cksum(m, hlen);
+	}
+	if (sum) {
+		ipstat.ips_badsum++;
+		m_freem(m);
+		return 1;
+	}
+	m->m_pkthdr.csum_flags |= (CSUM_IP_CHECKED | CSUM_IP_VALID);
+
+	/*
+	 * Convert to host representation
+	 */
+	ip->ip_len = ntohs(ip->ip_len);
+	ip->ip_off = ntohs(ip->ip_off);
+
+	/*
+	 * Is IP length longer than packet we have got?
+	 */
+	if (m->m_pkthdr.len < ip->ip_len) {
+		ipstat.ips_tooshort++;
+		m_freem(m);
+		return 1;
+	}
+
+	/*
+	 * Is packet longer than IP header tells us? If yes, truncate packet.
+	 */
+	if (m->m_pkthdr.len > ip->ip_len) {
+		if (m->m_len == m->m_pkthdr.len) {
+			m->m_len = ip->ip_len;
+			m->m_pkthdr.len = ip->ip_len;
+		} else
+			m_adj(m, ip->ip_len - m->m_pkthdr.len);
+	}
+
+	/*
+	 * Is packet from or to 127/8?
+	 */
+	if ((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||
+	    (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) {
+		ipstat.ips_badaddr++;
+		m_freem(m);
+		return 1;
+	}
+
+	M_ASSERTVALID(m);
+	M_ASSERTPKTHDR(m);
+
+	/**
+	 ** Second: fallback conditions to normal ip_input path processing
+	 **/
+
+	/*
+	 * Only IP packets without options
+	 */
+	if (ip->ip_hl != (sizeof(struct ip) >> 2))
+		goto fallback;
+
+	/*
+	 * Only unicast IP, not from loopback, no L2 or IP broadcast,
+	 * no multicast, no INADDR_ANY
+	 */
+	if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) ||
+	    (ntohl(ip->ip_src.s_addr) == (u_long)INADDR_BROADCAST) ||
+	    (ntohl(ip->ip_dst.s_addr) == (u_long)INADDR_BROADCAST) ||
+	    (IN_MULTICAST(ntohl(ip->ip_src.s_addr))) ||
+	    (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) ||
+	    (ip->ip_dst.s_addr == INADDR_ANY) )
+		goto fallback;
+
+	/*
+	 * Is it for a local address on this host?
+	 */
+	LIST_FOREACH(ia, INADDR_HASH(ip->ip_dst.s_addr), ia_hash) {
+		if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr) {
+			goto fallback;
+		}
+	}
+
+	/*
+	 * Or is it for a local IP broadcast address on this host?
+	 */
+	if (m->m_pkthdr.rcvif->if_flags & IFF_BROADCAST) {
+	        TAILQ_FOREACH(ifa, &m->m_pkthdr.rcvif->if_addrhead, ifa_link) {
+			if (ifa->ifa_addr->sa_family != AF_INET)
+				continue;
+			ia = ifatoia(ifa);
+			if (ia->ia_netbroadcast.s_addr == ip->ip_dst.s_addr)
+				goto fallback;
+			if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr ==
+			    ip->ip_dst.s_addr)
+				goto fallback;
+			continue;
+fallback:
+			/* drop the packet back to netisr */
+			ip->ip_len = htons(ip->ip_len);
+			ip->ip_off = htons(ip->ip_off);
+			return 0;
+		}
+	}
+	ipstat.ips_total++;
+
+
+	/**
+	 ** Third: incoming packet firewall processing
+	 **/
+
+	odest = dest = ip->ip_dst.s_addr;
+
+#ifdef PFIL_HOOKS
+	/*
+	 * Run through list of ipfilter hooks for input packets
+	 */
+	if (pfil_run_hooks(&inet_pfil_hook, &m, m->m_pkthdr.rcvif, PFIL_IN) ||
+	    m == NULL)
+		return 1;
+
+	M_ASSERTVALID(m);
+	M_ASSERTPKTHDR(m);
+
+	ip = mtod(m, struct ip *);	/* if m changed during fw processing */
+	dest = ip->ip_dst.s_addr;
+#endif
+
+	/*
+	 * Run through ipfw for input packets
+	 */
+	if (fw_enable && IPFW_LOADED) {
+		bzero(&args, sizeof(args));
+		args.m = m;
+		ipfw = 0;
+
+		ipfw = ip_fw_chk_ptr(&args);
+		m = args.m;
+
+		M_ASSERTVALID(m);
+		M_ASSERTPKTHDR(m);
+
+		/*
+		 * Packet denied, drop it
+		 */
+		if ( (ipfw & IP_FW_PORT_DENY_FLAG) || m == NULL) {
+			if (m)
+				m_freem(m);
+			return 1;
+		}
+		/*
+		 * Send packet to the appropriate pipe
+		 */
+                if (DUMMYNET_LOADED && (ipfw & IP_FW_PORT_DYNT_FLAG) != 0) {
+			ip_dn_io_ptr(m, ipfw & 0xffff, DN_TO_IP_IN, &args);
+			return 1;
+		}
+#ifdef IPDIVERT
+		/*
+		 * Divert packet
+		 */
+		if (ipfw != 0 && (ipfw & IP_FW_PORT_DYNT_FLAG) == 0) {
+			/*
+			 * See if this is a fragment
+			 */
+			if (ip->ip_off & (IP_MF | IP_OFFMASK)) {
+				MGETHDR(tag, M_DONTWAIT, MT_TAG);
+				if (tag == NULL) {
+					m_freem(m);
+					return 1;
+				}
+				tag->m_flags = PACKET_TAG_DIVERT;
+				tag->m_data = (caddr_t)(u_long)args.divert_rule;
+				tag->m_next = m;
+				m = tag;
+				tag = NULL;
+
+                                goto droptoours;
+			}
+			/*
+			 * Tee packet
+			 */
+			if ((ipfw & IP_FW_PORT_TEE_FLAG) != 0)
+				teem = m_dup(m, M_DONTWAIT);
+			else
+				teem = m;
+			if (teem == NULL)
+				goto passin;
+
+			M_ASSERTVALID(teem);
+			M_ASSERTPKTHDR(teem);
+
+			/*
+			 * Delayed checksums are not compatible
+			 */
+			if (teem->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
+				in_delayed_cksum(teem);
+				teem->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
+			}
+			/*
+			 * Restore packet header fields to original values
+			 */
+			tip = mtod(teem, struct ip *);
+			tip->ip_len = htons(tip->ip_len);
+			tip->ip_off = htons(tip->ip_off);
+			/*
+			 * Deliver packet to divert input routine
+			 */
+			divert_packet(teem, 0, ipfw & 0xffff, args.divert_rule);
+			/*
+			 * If this was not tee, we are done
+			 */
+			if ((ipfw & IP_FW_PORT_TEE_FLAG) == 0)
+				return 1;
+			/* Continue if it was tee */
+			goto passin;
+		}
+#endif
+		if (ipfw == 0 && args.next_hop != NULL) {
+			dest = args.next_hop->sin_addr.s_addr;
+			goto passin;
+		}
+		/*
+		 * Let through or not?
+		 */
+		if (ipfw != 0) {
+			m_freem(m);
+			return 1;
+		}
+	}
+passin:
+	ip = mtod(m, struct ip *);	/* if m changed during fw processing */
+
+	/*
+	 * Destination address changed?
+	 */
+	if (odest != dest) {
+		/*
+		 * Is it now for a local address on this host?
+		 */
+		LIST_FOREACH(ia, INADDR_HASH(ip->ip_dst.s_addr), ia_hash) {
+			if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr)
+				goto forwardlocal;
+		}
+		/*
+		 * Go on with new destination address
+		 */
+	}
+
+	M_ASSERTVALID(m);
+	M_ASSERTPKTHDR(m);
+
+	/**
+	 ** Forth: decrement TTL and look up route
+	 **/
+
+	/*
+	 * Check TTL
+	 */
+#ifdef IPSTEALTH
+	if (!ipstealth) {
+#endif
+	if (ip->ip_ttl <= IPTTLDEC) {
+		icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, NULL, NULL);
+		return 1;
+	}
+
+	/*
+	 * Decrement the TTL and incrementally change the checksum.
+	 * Don't bother doing this with hw checksum offloading.
+	 */
+	ip->ip_ttl -= IPTTLDEC;
+	if (ip->ip_sum >= (u_int16_t) ~htons(IPTTLDEC << 8))
+		ip->ip_sum -= ~htons(IPTTLDEC << 8);
+	else
+		ip->ip_sum += htons(IPTTLDEC << 8);
+#ifdef IPSTEALTH
+	}
+#endif
+
+	/*
+	 * Find route to destination.
+	 */
+	bzero(&ro, sizeof(ro));
+	dst = (struct sockaddr_in *)&ro.ro_dst;
+	dst->sin_family = AF_INET;
+	dst->sin_len = sizeof(*dst);
+	dst->sin_addr.s_addr = dest;
+	rtalloc(&ro);
+
+	/*
+	 * Route there and interface still up?
+	 */
+	if ((ro.ro_rt) &&
+	    (ro.ro_rt->rt_flags & RTF_UP) &&
+	    (ro.ro_rt->rt_ifp->if_flags & IFF_UP)) {
+		ia = ifatoia(ro.ro_rt->rt_ifa);
+		ifp = ro.ro_rt->rt_ifp;
+		if (ro.ro_rt->rt_flags & RTF_GATEWAY)
+			dst = (struct sockaddr_in *)ro.ro_rt->rt_gateway;
+	} else {
+		ipstat.ips_noroute++;
+		ipstat.ips_cantforward++;
+		icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, NULL, NULL);
+		if (ro.ro_rt)
+			RTFREE(ro.ro_rt);
+		return 1;
+	}
+
+
+	/**
+	 ** Fifth: outgoing firewall packet processing
+	 **/
+
+#ifdef PFIL_HOOKS
+	/*
+	 * Run through list of hooks for output packets.
+	 */
+	if (pfil_run_hooks(&inet_pfil_hook, &m, ifp, PFIL_OUT) ||
+	    m == NULL) {
+		RTFREE(ro.ro_rt);
+		return 1;
+	}
+
+	M_ASSERTVALID(m);
+	M_ASSERTPKTHDR(m);
+
+	ip = mtod(m, struct ip *);
+	dest = ip->ip_dst.s_addr;
+#endif
+	if (fw_enable && IPFW_LOADED && !args.next_hop) {
+                bzero(&args, sizeof(args));
+		args.m = m;
+		args.oif = ifp;
+                ipfw = 0;
+
+		ipfw = ip_fw_chk_ptr(&args);
+		m = args.m;
+
+		M_ASSERTVALID(m);
+		M_ASSERTPKTHDR(m);
+
+		if ( (ipfw & IP_FW_PORT_DENY_FLAG) || m == NULL) {
+			if (m)
+				m_freem(m);
+			RTFREE(ro.ro_rt);
+			return 1;
+		}
+                if (DUMMYNET_LOADED && (ipfw & IP_FW_PORT_DYNT_FLAG) != 0) {
+			/*
+			 * XXX note: if the ifp or rt entry are deleted
+			 * while a pkt is in dummynet, we are in trouble!
+			 */
+			args.ro = &ro;		/* dummynet does not save it */
+			args.dst = dst;
+
+			ip_dn_io_ptr(m, ipfw & 0xffff, DN_TO_IP_OUT, &args);
+			RTFREE(ro.ro_rt);
+			return 1;
+		}
+#ifdef IPDIVERT
+		if (ipfw != 0 && (ipfw & IP_FW_PORT_DYNT_FLAG) == 0) {
+			/*
+			 * See if this is a fragment
+			 */
+			if (ip->ip_off & (IP_MF | IP_OFFMASK)) {
+				MGETHDR(tag, M_DONTWAIT, MT_TAG);
+				if (tag == NULL) {
+					m_freem(m);
+					RTFREE(ro.ro_rt);
+					return 1;
+				}
+				tag->m_flags = PACKET_TAG_DIVERT;
+				tag->m_data = (caddr_t)(u_int32_t)args.divert_rule;
+				tag->m_next = m;
+				m = tag;
+				tag = NULL;
+
+				goto droptoours;
+			}
+			/*
+			 * Tee packet
+			 */
+			if ((ipfw & IP_FW_PORT_TEE_FLAG) != 0)
+				teem = m_dup(m, M_DONTWAIT);
+			else
+				teem = m;
+			if (teem == NULL)
+				goto passout;
+
+			M_ASSERTVALID(m);
+			M_ASSERTPKTHDR(m);
+
+			/*
+			 * delayed checksums are not compatible with divert
+			 */
+			if (teem->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
+				in_delayed_cksum(teem);
+				teem->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
+			}
+			/*
+			 * Restore packet header fields to original values
+			 */
+			tip = mtod(teem, struct ip *);
+			tip->ip_len = htons(tip->ip_len);
+			tip->ip_off = htons(tip->ip_off);
+			/*
+			 * Deliver packet to divert input routine
+			 */
+			divert_packet(teem, 0, ipfw & 0xffff, args.divert_rule);
+			/*
+			 * If this was not tee, we are done
+			 */
+			if ((ipfw & IP_FW_PORT_TEE_FLAG) == 0) {
+				RTFREE(ro.ro_rt);
+				return 1;
+			}
+			/* Continue if it was tee */
+			goto passout;
+		}
+#endif
+		if (ipfw == 0 && args.next_hop != NULL) {
+			dest = args.next_hop->sin_addr.s_addr;
+			goto passout;
+		}
+		/*
+		 * Let through or not?
+		 */
+		if (ipfw != 0) {
+			m_freem(m);
+			return 1;
+		}
+	}
+passout:
+	ip = mtod(m, struct ip *);
+
+	/*
+	 * Destination address changed?
+	 */
+	if (odest != dest) {
+		/*
+		 * Is it now for a local address on this host?
+		 */
+		LIST_FOREACH(ia, INADDR_HASH(ip->ip_dst.s_addr), ia_hash) {
+			if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr) {
+forwardlocal:
+				if(args.next_hop) {
+					MGETHDR(tag, M_DONTWAIT, MT_TAG);
+					if (tag == NULL) {
+						m_freem(m);
+						if(ro.ro_rt)
+							RTFREE(ro.ro_rt);
+						return 1;
+					}
+					tag->m_flags = PACKET_TAG_IPFORWARD;
+					tag->m_data = (caddr_t)args.next_hop;
+					tag->m_next = m;
+					m = tag;
+					tag = NULL;
+				}
+#ifdef IPDIVERT
+droptoours:	/* Used for DIVERT */
+#endif
+				MGETHDR(tag, M_DONTWAIT, MT_TAG);
+				if (tag == NULL) {
+					m_freem(m);
+					if(ro.ro_rt)
+						RTFREE(ro.ro_rt);
+					return 1;
+				}
+                                tag->m_flags = PACKET_TAG_IPFASTFWD_OURS;
+                                tag->m_data = NULL;
+                                tag->m_next = m;
+				m = tag;
+				tag = NULL;
+
+#if 0
+				/*
+				 * Do some checks, we never know what fw has
+				 * done to it.
+				 */
+				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;
+					m->m_pkthdr.csum_data = 0xffff;
+				}
+				m->m_pkthdr.csum_flags |=
+				    CSUM_IP_CHECKED | CSUM_IP_VALID;
+#endif
+
+				/* ip still points to the real packet */
+				ip->ip_len = htons(ip->ip_len);
+				ip->ip_off = htons(ip->ip_off);
+
+				M_ASSERTVALID(m);
+
+				/*
+				 * Drop packet to ip_input
+				 */
+				if (ro.ro_rt)
+					RTFREE(ro.ro_rt);
+				return 0;
+			}
+		}
+		/*
+		 * Redo route lookup with new destination address
+		 */
+		RTFREE(ro.ro_rt);
+		bzero(&ro, sizeof(ro));
+		dst = (struct sockaddr_in *)&ro.ro_dst;
+		dst->sin_family = AF_INET;
+		dst->sin_len = sizeof(*dst);
+		dst->sin_addr.s_addr = dest;
+		rtalloc(&ro);
+
+		/*
+		 * Route there and interface still up?
+		 */
+		if ((ro.ro_rt) &&
+		    (ro.ro_rt->rt_flags & RTF_UP) &&
+		    (ro.ro_rt->rt_ifp->if_flags & IFF_UP)) {
+			ia = ifatoia(ro.ro_rt->rt_ifa);
+			ifp = ro.ro_rt->rt_ifp;
+			if (ro.ro_rt->rt_flags & RTF_GATEWAY)
+				dst = (struct sockaddr_in *)ro.ro_rt->rt_gateway;
+		} else {
+			ipstat.ips_noroute++;
+			ipstat.ips_cantforward++;
+			icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, NULL, NULL);
+			if (ro.ro_rt)
+				RTFREE(ro.ro_rt);
+			return 1;
+		}
+	}
+
+	M_ASSERTVALID(m);
+	M_ASSERTPKTHDR(m);
+
+	/**
+	 ** Sixth: send off the packet
+	 **/
+
+	/*
+	 * Check if packet fits MTU or if hardware will fragement for us
+	 */
+	if (ip->ip_len <= ifp->if_mtu || (ifp->if_hwassist & CSUM_FRAGMENT &&
+	   ((ip->ip_off & IP_DF) == 0))) {
+		/*
+		 * Restore packet header fields to original values
+		 */
+		ip->ip_len = htons(ip->ip_len);
+		ip->ip_off = htons(ip->ip_off);
+		/*
+		 * Send off the packet via outgoing interface
+		 */
+		error = (ifp->if_output)(ifp, m, (struct sockaddr *)dst, ro.ro_rt);
+		if (ia) {
+			ia->ia_ifa.if_opackets++;
+			ia->ia_ifa.if_obytes += m->m_pkthdr.len;
+		}
+	} else {
+		/*
+		 * Handle EMSGSIZE with icmp reply needfrag for TCP MTU discovery
+		 */
+		if (ip->ip_off & IP_DF) {
+			icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG, NULL, ifp);
+			ipstat.ips_cantfrag++;
+			RTFREE(ro.ro_rt);
+			return 1;
+		} else {
+			/*
+			 * We have to fragement the packet
+			 */
+			m->m_pkthdr.csum_flags |= CSUM_IP;
+			if (ip_fragment(ip, &m, ifp->if_mtu, ifp->if_hwassist,
+					(~ifp->if_hwassist & CSUM_DELAY_IP))) {
+				m_freem(m);
+				RTFREE(ro.ro_rt);
+				return 1;
+			}
+			/*
+			 * Send off the fragments via outgoing interface
+			 */
+			for (; m; m = m0) {
+				m0 = m->m_nextpkt;
+				m->m_nextpkt = 0;
+
+				M_ASSERTVALID(m);
+				M_ASSERTPKTHDR(m);
+
+				if (error == 0) {
+					error = (*ifp->if_output)(ifp, m,
+						(struct sockaddr *)dst, ro.ro_rt);
+					if (ia) {
+						ia->ia_ifa.if_opackets++;
+						ia->ia_ifa.if_obytes += m->m_pkthdr.len;
+					}
+				} else {
+					m_freem(m);
+				}
+			}
+			if (error == 0)
+				ipstat.ips_fragmented++;
+		}
+	}
+
+	if (error == ENOBUFS)
+		ipstat.ips_odropped++;
+	else if (error != 0)
+		ipstat.ips_odropped++;
+	else {
+		ro.ro_rt->rt_rmx.rmx_pksent++;
+		ipstat.ips_forward++;
+		ipstat.ips_fastforward++;
+	}
+	RTFREE(ro.ro_rt);
+	return 1;
+}
+
Index: netinet/ip_input.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/ip_input.c,v
retrieving revision 1.252
diff -u -p -r1.252 ip_input.c
--- netinet/ip_input.c	8 Nov 2003 23:36:31 -0000	1.252
+++ netinet/ip_input.c	12 Nov 2003 20:48:45 -0000
@@ -360,11 +360,12 @@ ip_forward_cacheinval(void)
 void
 ip_input(struct mbuf *m)
 {
-	struct ip *ip;
+	struct ip *ip = NULL;
 	struct ipq *fp;
 	struct in_ifaddr *ia = NULL;
 	struct ifaddr *ifa;
-	int    i, hlen, checkif;
+	int    i, checkif, hlen = 0;
+	int    ours = 0;
 	u_short sum;
 	struct in_addr pkt_dst;
 	u_int32_t divert_info = 0;		/* packet divert/tee info */
@@ -406,10 +407,17 @@ ip_input(struct mbuf *m)
 		case PACKET_TAG_IPFORWARD:
 			args.next_hop = (struct sockaddr_in *)m->m_hdr.mh_data;
 			break;
+
+		case PACKET_TAG_IPFASTFWD_OURS:
+			ours = 1;
+			break;
 		}
 	}
 
 	M_ASSERTPKTHDR(m);
+
+	if (ours)		/* ip_fastforward firewall changed dest to local */
+		goto ours;
 
 	if (args.rule) {	/* dummynet already filtered us */
 		ip = mtod(m, struct ip *);
