Index: conf/files
===================================================================
RCS file: /home/ncvs/src/sys/conf/files,v
retrieving revision 1.846
diff -u -p -r1.846 files
--- conf/files	10 Nov 2003 10:39:14 -0000	1.846
+++ conf/files	11 Nov 2003 18:18:06 -0000
@@ -1447,7 +1447,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_flow.c	optional inet
+netinet/ip_fastforward.c	optional inet
 netinet/ip_fw2.c	optional ipfirewall
 netinet/ip_icmp.c	optional inet
 netinet/ip_input.c	optional inet
@@ -1455,6 +1455,7 @@ netinet/ip_mroute.c	optional mrouting
 netinet/ip_output.c	optional inet
 netinet/raw_ip.c	optional inet
 netinet/tcp_debug.c	optional tcpdebug
+netinet/tcp_hostcache.c	optional inet
 netinet/tcp_input.c	optional inet
 netinet/tcp_output.c	optional inet
 netinet/tcp_subr.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	11 Nov 2003 18:18:14 -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	11 Nov 2003 18:18:14 -0000
@@ -253,8 +253,8 @@ ef_inputEII(struct mbuf *m, struct ether
 #endif
 #ifdef INET
 	case ETHERTYPE_IP:
-		if (ipflow_fastforward(m))
-			return (0);
+		if (ip_fastforward(m))
+			return;
 		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	11 Nov 2003 18:18:14 -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_faith.c
===================================================================
RCS file: /home/ncvs/src/sys/net/if_faith.c,v
retrieving revision 1.23
diff -u -p -r1.23 if_faith.c
--- net/if_faith.c	31 Oct 2003 18:32:08 -0000	1.23
+++ net/if_faith.c	11 Nov 2003 18:18:14 -0000
@@ -270,17 +270,8 @@ faithrtrequest(cmd, rt, info)
 	struct rt_addrinfo *info;
 {
 	RT_LOCK_ASSERT(rt);
-
-	if (rt) {
-		rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu; /* for ISO */
-		/*
-		 * For optimal performance, the send and receive buffers
-		 * should be at least twice the MTU plus a little more for
-		 * overhead.
-		 */
-		rt->rt_rmx.rmx_recvpipe =
-			rt->rt_rmx.rmx_sendpipe = 3 * FAITHMTU;
-	}
+	if (rt)
+		rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu;
 }
 
 /*
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	11 Nov 2003 18:18:15 -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	11 Nov 2003 18:18:15 -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_loop.c
===================================================================
RCS file: /home/ncvs/src/sys/net/if_loop.c,v
retrieving revision 1.90
diff -u -p -r1.90 if_loop.c
--- net/if_loop.c	31 Oct 2003 18:32:08 -0000	1.90
+++ net/if_loop.c	11 Nov 2003 18:18:15 -0000
@@ -356,17 +356,8 @@ lortrequest(cmd, rt, info)
 	struct rt_addrinfo *info;
 {
 	RT_LOCK_ASSERT(rt);
-
-	if (rt) {
-		rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu; /* for ISO */
-		/*
-		 * For optimal performance, the send and receive buffers
-		 * should be at least twice the MTU plus a little more for
-		 * overhead.
-		 */
-		rt->rt_rmx.rmx_recvpipe =
-			rt->rt_rmx.rmx_sendpipe = 3 * LOMTU;
-	}
+	if (rt)
+		rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu;
 }
 
 /*
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	11 Nov 2003 18:18:16 -0000
@@ -1537,8 +1537,8 @@ ppp_inproc(sc, m)
 	m->m_pkthdr.len -= PPP_HDRLEN;
 	m->m_data += PPP_HDRLEN;
 	m->m_len -= PPP_HDRLEN;
-	if (ipflow_fastforward(m))
-	    return;
+	if (ip_fastforward(m))
+		return;
 	isr = NETISR_IP;
 	break;
 #endif
Index: net/route.c
===================================================================
RCS file: /home/ncvs/src/sys/net/route.c,v
retrieving revision 1.90
diff -u -p -r1.90 route.c
--- net/route.c	8 Nov 2003 23:36:29 -0000	1.90
+++ net/route.c	11 Nov 2003 18:18:16 -0000
@@ -139,7 +139,7 @@ rtalloc1(struct sockaddr *dst, int repor
 		 */
 		newrt = rt = (struct rtentry *)rn;
 		nflags = rt->rt_flags & ~ignflags;
-		if (report && (nflags & (RTF_CLONING | RTF_PRCLONING))) {
+		if (report && (nflags & RTF_CLONING)) {
 			/*
 			 * We are apparently adding (report = 0 in delete).
 			 * If it requires that it be cloned, do so.
@@ -584,7 +584,7 @@ rtexpunge(struct rtentry *rt)
 	 * Now search what's left of the subtree for any cloned
 	 * routes which might have been formed from this node.
 	 */
-	if ((rt->rt_flags & (RTF_CLONING | RTF_PRCLONING)) && rt_mask(rt))
+	if ((rt->rt_flags & RTF_CLONING) && rt_mask(rt))
 		rnh->rnh_walktree_from(rnh, rt_key(rt), rt_mask(rt),
 				       rt_fixdelete, rt);
 
@@ -647,7 +647,7 @@ rtrequest1(int req, struct rt_addrinfo *
 	 */
 	if (flags & RTF_HOST) {
 		netmask = 0;
-		flags &= ~(RTF_CLONING | RTF_PRCLONING);
+		flags &= ~RTF_CLONING;
 	}
 	switch (req) {
 	case RTM_DELETE:
@@ -669,7 +669,7 @@ rtrequest1(int req, struct rt_addrinfo *
 		 * Now search what's left of the subtree for any cloned
 		 * routes which might have been formed from this node.
 		 */
-		if ((rt->rt_flags & (RTF_CLONING | RTF_PRCLONING)) &&
+		if ((rt->rt_flags & RTF_CLONING) &&
 		    rt_mask(rt)) {
 			rnh->rnh_walktree_from(rnh, dst, rt_mask(rt),
 					       rt_fixdelete, rt);
@@ -716,7 +716,7 @@ rtrequest1(int req, struct rt_addrinfo *
 		ifa = rt->rt_ifa;
 		/* XXX locking? */
 		flags = rt->rt_flags &
-		    ~(RTF_CLONING | RTF_PRCLONING | RTF_STATIC);
+		    ~(RTF_CLONING | RTF_STATIC);
 		flags |= RTF_WASCLONED;
 		gateway = rt->rt_gateway;
 		if ((netmask = rt->rt_genmask) == 0)
@@ -777,11 +777,11 @@ rtrequest1(int req, struct rt_addrinfo *
 			/*
 			 * Uh-oh, we already have one of these in the tree.
 			 * We do a special hack: if the route that's already
-			 * there was generated by the protocol-cloning
-			 * mechanism, then we just blow it away and retry
-			 * the insertion of the new one.
+			 * there was generated by the cloning mechanism
+			 * then we just blow it away and retry the insertion
+			 * of the new one.
 			 */
-			rt2 = rtalloc1(dst, 0, RTF_PRCLONING);
+			rt2 = rtalloc1(dst, 0, 0);
 			if (rt2 && rt2->rt_parent) {
 				rtexpunge(rt2);
 				RT_UNLOCK(rt2);
@@ -820,7 +820,7 @@ rtrequest1(int req, struct rt_addrinfo *
 				("no route to clone from"));
 			rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */
 			rt->rt_rmx.rmx_pksent = 0; /* reset packet counter */
-			if ((*ret_nrt)->rt_flags & (RTF_CLONING | RTF_PRCLONING)) {
+			if ((*ret_nrt)->rt_flags & RTF_CLONING) {
 				/*
 				 * NB: We do not bump the refcnt on the parent
 				 * entry under the assumption that it will
@@ -896,7 +896,7 @@ rt_fixdelete(struct radix_node *rn, void
 	struct rtentry *rt0 = vp;
 
 	if (rt->rt_parent == rt0 &&
-	    !(rt->rt_flags & (RTF_PINNED | RTF_CLONING | RTF_PRCLONING))) {
+	    !(rt->rt_flags & (RTF_PINNED | RTF_CLONING))) {
 		return rtrequest(RTM_DELETE, rt_key(rt),
 				 (struct sockaddr *)0, rt_mask(rt),
 				 rt->rt_flags, (struct rtentry **)0);
@@ -937,7 +937,7 @@ rt_fixchange(struct radix_node *rn, void
 #endif
 
 	if (!rt->rt_parent ||
-	    (rt->rt_flags & (RTF_PINNED | RTF_CLONING | RTF_PRCLONING))) {
+	    (rt->rt_flags & (RTF_PINNED | RTF_CLONING))) {
 #ifdef DEBUG
 		if(rtfcdebug) printf("no parent, pinned or cloning\n");
 #endif
@@ -1087,10 +1087,11 @@ rt_setgate(struct rtentry *rt, struct so
 	 * correct choice anyway), and avoid the resulting reference loops
 	 * by disallowing any route to run through itself as a gateway.
 	 * This is obviously mandatory when we get rt->rt_output().
+	 * XXX: After removal of PRCLONING this probably not needed anymore.
 	 */
 	if (rt->rt_flags & RTF_GATEWAY) {
 		/* XXX LOR here */
-		rt->rt_gwroute = rtalloc1(gate, 1, RTF_PRCLONING);
+		rt->rt_gwroute = rtalloc1(gate, 1, 0);
 		if (rt->rt_gwroute == rt) {
 			RTFREE_LOCKED(rt->rt_gwroute);
 			rt->rt_gwroute = 0;
Index: net/route.h
===================================================================
RCS file: /home/ncvs/src/sys/net/route.h,v
retrieving revision 1.52
diff -u -p -r1.52 route.h
--- net/route.h	8 Nov 2003 23:36:30 -0000	1.52
+++ net/route.h	11 Nov 2003 18:18:16 -0000
@@ -58,6 +58,12 @@ struct route {
  * These numbers are used by reliable protocols for determining
  * retransmission behavior and are included in the routing structure.
  */
+struct rt_metrics_lite {
+	u_long	rmx_mtu;	/* MTU for this path */
+	u_long	rmx_expire;	/* lifetime for route, e.g. redirect */
+	u_long	rmx_pksent;	/* packets sent using this route */
+};
+
 struct rt_metrics {
 	u_long	rmx_locks;	/* Kernel must leave these values alone */
 	u_long	rmx_mtu;	/* MTU for this path */
@@ -104,10 +110,10 @@ struct rtentry {
 	long	rt_refcnt;		/* # held references */
 	u_long	rt_flags;		/* up/down?, host/net */
 	struct	ifnet *rt_ifp;		/* the answer: interface to use */
-	struct	ifaddr *rt_ifa;		/* the answer: interface to use */
+	struct	ifaddr *rt_ifa;		/* the answer: interface address to use */
 	struct	sockaddr *rt_genmask;	/* for generation of cloned routes */
 	caddr_t	rt_llinfo;		/* pointer to link level info cache */
-	struct	rt_metrics rt_rmx;	/* metrics used by rx'ing protocols */
+	struct	rt_metrics_lite rt_rmx;	/* metrics used by rx'ing protocols */
 	struct	rtentry *rt_gwroute;	/* implied entry for gatewayed routes */
 	int	(*rt_output)(struct ifnet *, struct mbuf *, struct sockaddr *,
 		    struct rtentry *);
@@ -151,7 +157,7 @@ struct ortentry {
 #define RTF_PROTO2	0x4000		/* protocol specific routing flag */
 #define RTF_PROTO1	0x8000		/* protocol specific routing flag */
 
-#define RTF_PRCLONING	0x10000		/* protocol requires cloning */
+/*			0x10000		   unused */
 #define RTF_WASCLONED	0x20000		/* route generated through cloning */
 #define RTF_PROTO3	0x40000		/* protocol specific routing flag */
 /*			0x80000		   unused */
Index: net/rtsock.c
===================================================================
RCS file: /home/ncvs/src/sys/net/rtsock.c,v
retrieving revision 1.94
diff -u -p -r1.94 rtsock.c
--- net/rtsock.c	8 Nov 2003 23:36:30 -0000	1.94
+++ net/rtsock.c	11 Nov 2003 18:18:17 -0000
@@ -86,7 +86,8 @@ static int	rt_xaddrs(caddr_t, caddr_t, s
 static int	sysctl_dumpentry(struct radix_node *rn, void *vw);
 static int	sysctl_iflist(int af, struct walkarg *w);
 static int	route_output(struct mbuf *, struct socket *);
-static void	rt_setmetrics(u_long, struct rt_metrics *, struct rt_metrics *);
+static void	rt_setmetrics(u_long, struct rt_metrics *, struct rt_metrics_lite *);
+static void	rt_getmetrics(struct rt_metrics_lite *, struct rt_metrics *);
 static void	rt_dispatch(struct mbuf *, struct sockaddr *);
 
 /*
@@ -354,9 +355,6 @@ route_output(m, so)
 			RT_LOCK(saved_nrt);
 			rt_setmetrics(rtm->rtm_inits,
 				&rtm->rtm_rmx, &saved_nrt->rt_rmx);
-			saved_nrt->rt_rmx.rmx_locks &= ~(rtm->rtm_inits);
-			saved_nrt->rt_rmx.rmx_locks |=
-				(rtm->rtm_inits & rtm->rtm_rmx.rmx_locks);
 			RT_REMREF(saved_nrt);
 			saved_nrt->rt_genmask = info.rti_info[RTAX_GENMASK];
 			RT_UNLOCK(saved_nrt);
@@ -427,7 +425,7 @@ route_output(m, so)
 			(void)rt_msg2(rtm->rtm_type, &info, (caddr_t)rtm,
 				(struct walkarg *)0);
 			rtm->rtm_flags = rt->rt_flags;
-			rtm->rtm_rmx = rt->rt_rmx;
+			rt_getmetrics(&rt->rt_rmx, &rtm->rtm_rmx);
 			rtm->rtm_addrs = info.rti_addrs;
 			break;
 
@@ -477,9 +475,7 @@ route_output(m, so)
 				rt->rt_genmask = info.rti_info[RTAX_GENMASK];
 			/* FALLTHROUGH */
 		case RTM_LOCK:
-			rt->rt_rmx.rmx_locks &= ~(rtm->rtm_inits);
-			rt->rt_rmx.rmx_locks |=
-				(rtm->rtm_inits & rtm->rtm_rmx.rmx_locks);
+			/* We don't support locks anymore */
 			break;
 		}
 		RT_UNLOCK(rt);
@@ -541,20 +537,28 @@ flush:
 }
 
 static void
-rt_setmetrics(u_long which, struct rt_metrics *in, struct rt_metrics *out)
+rt_setmetrics(u_long which, struct rt_metrics *in, struct rt_metrics_lite *out)
 {
 #define metric(f, e) if (which & (f)) out->e = in->e;
-	metric(RTV_RPIPE, rmx_recvpipe);
-	metric(RTV_SPIPE, rmx_sendpipe);
-	metric(RTV_SSTHRESH, rmx_ssthresh);
-	metric(RTV_RTT, rmx_rtt);
-	metric(RTV_RTTVAR, rmx_rttvar);
-	metric(RTV_HOPCOUNT, rmx_hopcount);
+	/*
+	 * Only these are stored in the routing entry since introduction
+	 * of tcp hostcache. The rest is ignored.
+	 */
 	metric(RTV_MTU, rmx_mtu);
 	metric(RTV_EXPIRE, rmx_expire);
 #undef metric
 }
 
+static void
+rt_getmetrics(struct rt_metrics_lite *in, struct rt_metrics *out)
+{
+#define metric(e) out->e = in->e;
+	bzero(out, sizeof(*out));
+	metric(rmx_mtu);
+	metric(rmx_expire);
+#undef metric
+}
+
 #define ROUNDUP(a) \
 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
 
@@ -943,8 +947,8 @@ sysctl_dumpentry(struct radix_node *rn, 
 		struct rt_msghdr *rtm = (struct rt_msghdr *)w->w_tmem;
 
 		rtm->rtm_flags = rt->rt_flags;
-		rtm->rtm_use = rt->rt_use;
-		rtm->rtm_rmx = rt->rt_rmx;
+		rtm->rtm_use = rt->rt_rmx.rmx_pksent;
+		rt_getmetrics(&rt->rt_rmx, &rtm->rtm_rmx);
 		rtm->rtm_index = rt->rt_ifp->if_index;
 		rtm->rtm_errno = rtm->rtm_pid = rtm->rtm_seq = 0;
 		rtm->rtm_addrs = info.rti_addrs;
Index: netatalk/ddp_output.c
===================================================================
RCS file: /home/ncvs/src/sys/netatalk/ddp_output.c,v
retrieving revision 1.20
diff -u -p -r1.20 ddp_output.c
--- netatalk/ddp_output.c	31 Oct 2003 18:32:10 -0000	1.20
+++ netatalk/ddp_output.c	11 Nov 2003 18:18:17 -0000
@@ -217,7 +217,7 @@ ddp_route( struct mbuf *m, struct route 
 	elh->el_type = ELAP_DDPEXTEND;
 	elh->el_dnode = gate.sat_addr.s_node;
     }
-    ro->ro_rt->rt_use++;
+    ro->ro_rt->rt_rmx.rmx_pksent++;
 
 #ifdef NETATALK_DEBUG
     printf ("ddp_route: from %d.%d to %d.%d, via %d.%d (%s)\n",
Index: netinet/icmp_var.h
===================================================================
RCS file: /home/ncvs/src/sys/netinet/icmp_var.h,v
retrieving revision 1.21
diff -u -p -r1.21 icmp_var.h
--- netinet/icmp_var.h	19 Mar 2002 21:25:46 -0000	1.21
+++ netinet/icmp_var.h	11 Nov 2003 18:18:17 -0000
@@ -81,11 +81,12 @@ SYSCTL_DECL(_net_inet_icmp);
 extern int badport_bandlim(int);
 #define BANDLIM_UNLIMITED -1
 #define BANDLIM_ICMP_UNREACH 0
-#define BANDLIM_ICMP_ECHO 1
-#define BANDLIM_ICMP_TSTAMP 2
-#define BANDLIM_RST_CLOSEDPORT 3 /* No connection, and no listeners */
-#define BANDLIM_RST_OPENPORT 4   /* No connection, listener */
-#define BANDLIM_MAX 4
+#define BANDLIM_ICMP_UNREACH_HOST 1
+#define BANDLIM_ICMP_ECHO 2
+#define BANDLIM_ICMP_TSTAMP 3
+#define BANDLIM_RST_CLOSEDPORT 4	/* No connection, and no listeners */
+#define BANDLIM_RST_OPENPORT 5		/* No connection, listener */
+#define BANDLIM_MAX 5
 #endif
 
 #endif
Index: netinet/in_pcb.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/in_pcb.c,v
retrieving revision 1.128
diff -u -p -r1.128 in_pcb.c
--- netinet/in_pcb.c	10 Nov 2003 22:45:37 -0000	1.128
+++ netinet/in_pcb.c	11 Nov 2003 18:18:18 -0000
@@ -550,7 +550,6 @@ in_pcbconnect_setup(inp, nam, laddrp, lp
 		if (error)
 			return (error);
 	}
-
 	if (!TAILQ_EMPTY(&in_ifaddrhead)) {
 		/*
 		 * If the destination address is INADDR_ANY,
@@ -568,7 +567,8 @@ in_pcbconnect_setup(inp, nam, laddrp, lp
 			    &in_ifaddrhead)->ia_broadaddr)->sin_addr;
 	}
 	if (laddr.s_addr == INADDR_ANY) {
-		register struct route *ro;
+		struct route *ro;
+		struct route sro;
 
 		ia = (struct in_ifaddr *)0;
 		/*
@@ -577,19 +577,10 @@ in_pcbconnect_setup(inp, nam, laddrp, lp
 		 * Note that we should check the address family of the cached
 		 * destination, in case of sharing the cache with IPv6.
 		 */
-		ro = &inp->inp_route;
-		if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
-		    ro->ro_dst.sa_family != AF_INET ||
-		    satosin(&ro->ro_dst)->sin_addr.s_addr != faddr.s_addr ||
-		    inp->inp_socket->so_options & SO_DONTROUTE)) {
-			RTFREE(ro->ro_rt);
-			ro->ro_rt = (struct rtentry *)0;
-		}
-		if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
-		    (ro->ro_rt == (struct rtentry *)0 ||
-		    ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
-			/* No route yet, so try to acquire one */
-			bzero(&ro->ro_dst, sizeof(struct sockaddr_in));
+		ro = &sro;
+		bzero(ro, sizeof(*ro));
+		if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0) {
+			/* Find out route to destination */
 			ro->ro_dst.sa_family = AF_INET;
 			ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
 			((struct sockaddr_in *)&ro->ro_dst)->sin_addr = faddr;
@@ -603,6 +594,8 @@ in_pcbconnect_setup(inp, nam, laddrp, lp
 		 */
 		if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))
 			ia = ifatoia(ro->ro_rt->rt_ifa);
+		if (ro->ro_rt)
+			RTFREE(ro->ro_rt);
 		if (ia == 0) {
 			bzero(&sa, sizeof(sa));
 			sa.sin_addr = faddr;
@@ -695,8 +688,6 @@ in_pcbdetach(inp)
 	}
 	if (inp->inp_options)
 		(void)m_free(inp->inp_options);
-	if (inp->inp_route.ro_rt)
-		RTFREE(inp->inp_route.ro_rt);
 	ip_freemoptions(inp->inp_moptions);
 	inp->inp_vflag = 0;
 	INP_LOCK_DESTROY(inp);
@@ -867,62 +858,6 @@ in_pcbpurgeif0(pcbinfo, ifp)
 		INP_UNLOCK(inp);
 	}
 	INP_INFO_RUNLOCK(pcbinfo);
-}
-
-/*
- * Check for alternatives when higher level complains
- * about service problems.  For now, invalidate cached
- * routing information.  If the route was created dynamically
- * (by a redirect), time to try a default gateway again.
- */
-void
-in_losing(inp)
-	struct inpcb *inp;
-{
-	register struct rtentry *rt;
-	struct rt_addrinfo info;
-
-	INP_LOCK_ASSERT(inp);
-
-	if ((rt = inp->inp_route.ro_rt)) {
-		RT_LOCK(rt);
-		inp->inp_route.ro_rt = NULL;
-		bzero((caddr_t)&info, sizeof(info));
-		info.rti_flags = rt->rt_flags;
-		info.rti_info[RTAX_DST] = rt_key(rt);
-		info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
-		info.rti_info[RTAX_NETMASK] = rt_mask(rt);
-		rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0);
-		if (rt->rt_flags & RTF_DYNAMIC)
-			rtexpunge(rt);
-		RTFREE_LOCKED(rt);
-		/*
-		 * A new route can be allocated
-		 * the next time output is attempted.
-		 */
-	}
-}
-
-/*
- * After a routing change, flush old routing
- * and allocate a (hopefully) better one.
- */
-struct inpcb *
-in_rtchange(inp, errno)
-	register struct inpcb *inp;
-	int errno;
-{
-	INP_LOCK_ASSERT(inp);
-
-	if (inp->inp_route.ro_rt) {
-		RTFREE(inp->inp_route.ro_rt);
-		inp->inp_route.ro_rt = 0;
-		/*
-		 * A new route can be allocated the next time
-		 * output is attempted.
-		 */
-	}
-	return inp;
 }
 
 /*
Index: netinet/in_pcb.h
===================================================================
RCS file: /home/ncvs/src/sys/netinet/in_pcb.h,v
retrieving revision 1.65
diff -u -p -r1.65 in_pcb.h
--- netinet/in_pcb.h	8 Nov 2003 22:48:27 -0000	1.65
+++ netinet/in_pcb.h	11 Nov 2003 18:18:18 -0000
@@ -94,31 +94,22 @@ struct in_endpoints {
 
 /*
  * XXX
- * At some point struct route should possibly change to:
- *   struct rtentry *rt
- *   struct in_endpoints *ie; 
+ * the defines for inc_* are hacks and should be changed to direct references
  */
 struct in_conninfo {
 	u_int8_t	inc_flags;
 	u_int8_t	inc_len;
 	u_int16_t	inc_pad;	/* XXX alignment for in_endpoints */
-	/* protocol dependent part; cached route */
+	/* protocol dependent part */
 	struct	in_endpoints inc_ie;
-	union {
-		/* placeholder for routing entry */
-		struct	route inc4_route;
-		struct	route_in6 inc6_route;
-	} inc_dependroute;
 };
 #define inc_isipv6	inc_flags	/* temp compatability */
 #define	inc_fport	inc_ie.ie_fport
 #define	inc_lport	inc_ie.ie_lport
 #define	inc_faddr	inc_ie.ie_faddr
 #define	inc_laddr	inc_ie.ie_laddr
-#define	inc_route	inc_dependroute.inc4_route
 #define	inc6_faddr	inc_ie.ie6_faddr
 #define	inc6_laddr	inc_ie.ie6_laddr
-#define	inc6_route	inc_dependroute.inc6_route
 
 struct	icmp6_filter;
 
@@ -156,7 +147,6 @@ struct inpcb {
 #define inp_lport	inp_inc.inc_lport
 #define	inp_faddr	inp_inc.inc_faddr
 #define	inp_laddr	inp_inc.inc_laddr
-#define	inp_route	inp_inc.inc_route
 #define	inp_ip_tos	inp_depend4.inp4_ip_tos
 #define	inp_options	inp_depend4.inp4_options
 #define	inp_moptions	inp_depend4.inp4_moptions
@@ -181,7 +171,7 @@ struct inpcb {
 
 #define	in6p_faddr	inp_inc.inc6_faddr
 #define	in6p_laddr	inp_inc.inc6_laddr
-#define	in6p_route	inp_inc.inc6_route
+#define	in6p_ip6_hlim	inp_depend6.inp6_hlim
 #define	in6p_hops	inp_depend6.inp6_hops	/* default hop limit */
 #define	in6p_ip6_nxt	inp_ip_p
 #define	in6p_flowinfo	inp_flow
@@ -346,9 +336,6 @@ extern int	ipport_hifirstauto;
 extern int	ipport_hilastauto;
 
 void	in_pcbpurgeif0(struct inpcbinfo *, struct ifnet *);
-void	in_losing(struct inpcb *);
-struct inpcb *
-	in_rtchange(struct inpcb *, int);
 int	in_pcballoc(struct socket *, struct inpcbinfo *, struct thread *);
 int	in_pcbbind(struct inpcb *, struct sockaddr *, struct thread *);
 int	in_pcbbind_setup(struct inpcb *, struct sockaddr *, in_addr_t *,
Index: netinet/in_rmx.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/in_rmx.c,v
retrieving revision 1.48
diff -u -p -r1.48 in_rmx.c
--- netinet/in_rmx.c	3 Nov 2003 06:11:44 -0000	1.48
+++ netinet/in_rmx.c	11 Nov 2003 18:18:18 -0000
@@ -73,15 +73,6 @@ in_addroute(void *v_arg, void *n_arg, st
 	struct radix_node *ret;
 
 	/*
-	 * For IP, all unicast non-host routes are automatically cloning.
-	 */
-	if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
-		rt->rt_flags |= RTF_MULTICAST;
-
-	if (!(rt->rt_flags & (RTF_HOST | RTF_CLONING | RTF_MULTICAST)))
-		rt->rt_flags |= RTF_PRCLONING;
-
-	/*
 	 * A little bit of help for both IP output and input:
 	 *   For host routes, we make sure that RTF_BROADCAST
 	 *   is set for anything that looks like a broadcast address.
@@ -94,8 +85,7 @@ in_addroute(void *v_arg, void *n_arg, st
 	 *
 	 * We also mark routes to multicast addresses as such, because
 	 * it's easy to do and might be useful (but this is much more
-	 * dubious since it's so easy to inspect the address).  (This
-	 * is done above.)
+	 * dubious since it's so easy to inspect the address).
 	 */
 	if (rt->rt_flags & RTF_HOST) {
 		if (in_broadcast(sin->sin_addr, rt->rt_ifp)) {
@@ -105,9 +95,10 @@ in_addroute(void *v_arg, void *n_arg, st
 			rt->rt_flags |= RTF_LOCAL;
 		}
 	}
+	if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
+		rt->rt_flags |= RTF_MULTICAST;
 
-	if (!rt->rt_rmx.rmx_mtu && !(rt->rt_rmx.rmx_locks & RTV_MTU) &&
-	    rt->rt_ifp)
+	if (!rt->rt_rmx.rmx_mtu && rt->rt_ifp)
 		rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu;
 
 	ret = rn_addroute(v_arg, n_arg, head, treenodes);
@@ -118,8 +109,7 @@ in_addroute(void *v_arg, void *n_arg, st
 		 * Find out if it is because of an
 		 * ARP entry and delete it if so.
 		 */
-		rt2 = rtalloc1((struct sockaddr *)sin, 0,
-				RTF_CLONING | RTF_PRCLONING);
+		rt2 = rtalloc1((struct sockaddr *)sin, 0, RTF_CLONING);
 		if (rt2) {
 			if (rt2->rt_flags & RTF_LLINFO &&
 			    rt2->rt_flags & RTF_HOST &&
@@ -133,14 +123,6 @@ in_addroute(void *v_arg, void *n_arg, st
 				RTFREE_LOCKED(rt2);
 		}
 	}
-
-	/*
-	 * If the new route created successfully, and we are forwarding,
-	 * flush any cached routes to avoid using a stale value.
-	 */
-	if (ret != NULL && ipforwarding)
-		ip_forward_cacheinval();
-
 	return ret;
 }
 
@@ -386,7 +368,7 @@ in_ifadownkill(struct radix_node *rn, vo
 		 * the routes that rtrequest() would have in any case,
 		 * so that behavior is not needed there.
 		 */
-		rt->rt_flags &= ~(RTF_CLONING | RTF_PRCLONING);
+		rt->rt_flags &= ~RTF_CLONING;
 		rtexpunge(rt);
 	}
 	RT_UNLOCK(rt);
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	11 Nov 2003 18:18:18 -0000
@@ -230,9 +230,7 @@ void	in_rtqdrain(void);
 void	ip_input(struct mbuf *);
 int	in_ifadown(struct ifaddr *ifa, int);
 void	in_ifscrub(struct ifnet *, struct in_ifaddr *);
-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: netinet/ip_divert.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/ip_divert.c,v
retrieving revision 1.77
diff -u -p -r1.77 ip_divert.c
--- netinet/ip_divert.c	8 Nov 2003 23:09:42 -0000	1.77
+++ netinet/ip_divert.c	11 Nov 2003 18:18:18 -0000
@@ -335,7 +335,7 @@ div_output(struct socket *so, struct mbu
 			ipstat.ips_rawout++;			/* XXX */
 
 			error = ip_output((struct mbuf *)&divert_tag,
-				    inp->inp_options, &inp->inp_route,
+				    inp->inp_options, NULL,
 				    (so->so_options & SO_DONTROUTE) |
 				    IP_ALLOWBROADCAST | IP_RAWOUTPUT,
 				    inp->inp_moptions, NULL);
@@ -526,11 +526,8 @@ div_ctlinput(int cmd, struct sockaddr *s
 	faddr = ((struct sockaddr_in *)sa)->sin_addr;
 	if (sa->sa_family != AF_INET || faddr.s_addr == INADDR_ANY)
         	return;
-	if (PRC_IS_REDIRECT(cmd)) {
-		/* flush held routes */
-		in_pcbnotifyall(&divcbinfo, faddr,
-			inetctlerrmap[cmd], in_rtchange);
-	}
+	if (PRC_IS_REDIRECT(cmd))
+		return;
 }
 
 static int
Index: netinet/ip_flow.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/ip_flow.c,v
retrieving revision 1.16
diff -u -p -r1.16 ip_flow.c
--- netinet/ip_flow.c	8 Nov 2003 23:36:31 -0000	1.16
+++ netinet/ip_flow.c	11 Nov 2003 18:18:18 -0000
@@ -1,377 +1 @@
-/*-
- * Copyright (c) 1998 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by the 3am Software Foundry ("3am").  It was developed by Matt Thomas.
- *
- * 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. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *	This product includes software developed by the NetBSD
- *	Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- *    contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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_flow.c,v 1.16 2003/11/08 23:36:31 sam Exp $
- */
 
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/lock.h>
-#include <sys/malloc.h>
-#include <sys/mbuf.h>
-#include <sys/mutex.h>
-#include <sys/protosw.h>
-#include <sys/socket.h>
-#include <sys/kernel.h>
-
-#include <sys/sysctl.h>
-
-#include <net/if.h>
-#include <net/route.h>
-
-#include <netinet/in.h>
-#include <netinet/in_systm.h>
-#include <netinet/ip.h>
-#include <netinet/in_var.h>
-#include <netinet/ip_var.h>
-#include <netinet/ip_flow.h>
-
-#define	IPFLOW_TIMER		(5 * PR_SLOWHZ)
-#define IPFLOW_HASHBITS		6	/* should not be a multiple of 8 */
-#define	IPFLOW_HASHSIZE		(1 << IPFLOW_HASHBITS)
-#if IPFLOW_HASHSIZE > 255
-#error "make ipf_hash larger"
-#endif
-static struct ipflow_head ipflows[IPFLOW_HASHSIZE];
-static int ipflow_inuse;
-#define	IPFLOW_MAX		256
-
-/*
- * Each flow list has a lock that guards updates to the list and to
- * all entries on the list.  Flow entries hold the hash index for
- * finding the head of the list so the lock can be found quickly.
- *
- * ipflow_inuse holds a count of the number of flow entries present.
- * This is used to bound the size of the table.  When IPFLOW_MAX entries
- * are present and an additional entry is needed one is chosen for
- * replacement.  We could use atomic ops for this counter but having it
- * inconsistent doesn't appear to be a problem.
- */
-#define	IPFLOW_HEAD_LOCK(_ipfh)		mtx_lock(&(_ipfh)->ipfh_mtx)
-#define	IPFLOW_HEAD_UNLOCK(_ipfh)	mtx_unlock(&(_ipfh)->ipfh_mtx)
-#define	IPFLOW_LOCK(_ipf) \
-	IPFLOW_HEAD_LOCK(&ipflows[(_ipf)->ipf_hash])
-#define	IPFLOW_UNLOCK(_ipf) \
-	IPFLOW_HEAD_UNLOCK(&ipflows[(_ipf)->ipf_hash])
-
-static int ipflow_active = 0;
-SYSCTL_INT(_net_inet_ip, IPCTL_FASTFORWARDING, fastforwarding, CTLFLAG_RW,
-    &ipflow_active, 0, "Enable flow-based IP forwarding");
-
-static MALLOC_DEFINE(M_IPFLOW, "ip_flow", "IP flow");
-
-static unsigned
-ipflow_hash(struct in_addr dst, struct in_addr src, unsigned tos)
-{
-	unsigned hash = tos;
-	int idx;
-	for (idx = 0; idx < 32; idx += IPFLOW_HASHBITS)
-		hash += (dst.s_addr >> (32 - idx)) + (src.s_addr >> idx);
-	return hash & (IPFLOW_HASHSIZE-1);
-}
-
-static struct ipflow *
-ipflow_lookup(const struct ip *ip)
-{
-	unsigned hash;
-	struct ipflow_head *head;
-	struct ipflow *ipf;
-
-	hash = ipflow_hash(ip->ip_dst, ip->ip_src, ip->ip_tos);
-	head = &ipflows[hash];
-
-	IPFLOW_HEAD_LOCK(head);
-	LIST_FOREACH(ipf, &head->ipfh_head, ipf_next) {
-		if (ip->ip_dst.s_addr == ipf->ipf_dst.s_addr
-		    && ip->ip_src.s_addr == ipf->ipf_src.s_addr
-		    && ip->ip_tos == ipf->ipf_tos) {
-			/* NB: return head locked */
-			return ipf;
-		}
-	}
-	IPFLOW_HEAD_UNLOCK(head);
-	return NULL;
-}
-
-int
-ipflow_fastforward(struct mbuf *m)
-{
-	struct ip *ip;
-	struct ipflow *ipf;
-	struct rtentry *rt;
-	struct sockaddr *dst;
-	int error;
-
-	/*
-	 * Are we forwarding packets?  Big enough for an IP packet?
-	 */
-	if (!ipforwarding || !ipflow_active || m->m_len < sizeof(struct ip))
-		return 0;
-	/*
-	 * IP header with no option and valid version and length
-	 */
-	ip = mtod(m, struct ip *);
-	if (ip->ip_v != IPVERSION || ip->ip_hl != (sizeof(struct ip) >> 2)
-	    || ntohs(ip->ip_len) > m->m_pkthdr.len)
-		return 0;
-	/*
-	 * Find a flow.
-	 */
-	if ((ipf = ipflow_lookup(ip)) == NULL)
-		return 0;
-
-	/*
-	 * Route and interface still up?
-	 */
-	rt = ipf->ipf_ro.ro_rt;
-	if ((rt->rt_flags & RTF_UP) == 0 || (rt->rt_ifp->if_flags & IFF_UP) == 0) {
-		IPFLOW_UNLOCK(ipf);
-		return 0;
-	}
-
-	/*
-	 * Packet size OK?  TTL?
-	 */
-	if (m->m_pkthdr.len > rt->rt_ifp->if_mtu || ip->ip_ttl <= IPTTLDEC) {
-		IPFLOW_UNLOCK(ipf);
-		return 0;
-	}
-
-	/*
-	 * Everything checks out and so we can forward this packet.
-	 * Modify the TTL and incrementally change the checksum.
-	 */
-	ip->ip_ttl -= IPTTLDEC;
-	if (ip->ip_sum >= htons(0xffff - (IPTTLDEC << 8))) {
-		ip->ip_sum += htons(IPTTLDEC << 8) + 1;
-	} else {
-		ip->ip_sum += htons(IPTTLDEC << 8);
-	}
-
-	/*
-	 * Send the packet on its way.  All we can get back is ENOBUFS
-	 */
-	ipf->ipf_uses++;
-	ipf->ipf_timer = IPFLOW_TIMER;
-
-	if (rt->rt_flags & RTF_GATEWAY)
-		dst = rt->rt_gateway;
-	else
-		dst = &ipf->ipf_ro.ro_dst;
-	if ((error = (*rt->rt_ifp->if_output)(rt->rt_ifp, m, dst, rt)) != 0) {
-		if (error == ENOBUFS)
-			ipf->ipf_dropped++;
-		else
-			ipf->ipf_errors++;
-	}
-	IPFLOW_UNLOCK(ipf);
-	return 1;
-}
-
-static void
-ipflow_addstats(struct ipflow *ipf)
-{
-	ipf->ipf_ro.ro_rt->rt_use += ipf->ipf_uses;
-	ipstat.ips_cantforward += ipf->ipf_errors + ipf->ipf_dropped;
-	ipstat.ips_forward += ipf->ipf_uses;
-	ipstat.ips_fastforward += ipf->ipf_uses;
-}
-
-/*
- * XXX the locking here makes reaping an entry very expensive...
- */
-static struct ipflow *
-ipflow_reap(void)
-{
-	struct ipflow *victim = NULL;
-	struct ipflow *ipf;
-	int idx;
-
-	for (idx = 0; idx < IPFLOW_HASHSIZE; idx++) {
-		struct ipflow_head *head = &ipflows[idx];
-
-		IPFLOW_HEAD_LOCK(head);
-		LIST_FOREACH(ipf, &head->ipfh_head, ipf_next) {
-			/*
-			 * If this no longer points to a valid route
-			 * reclaim it.
-			 */
-			if ((ipf->ipf_ro.ro_rt->rt_flags & RTF_UP) == 0)
-				goto done;
-			/*
-			 * choose the one that's been least recently used
-			 * or has had the least uses in the last 1.5 
-			 * intervals.
-			 */
-			if (victim == NULL)
-				victim = ipf;
-			else if (ipf->ipf_timer < victim->ipf_timer
-			    || (ipf->ipf_timer == victim->ipf_timer
-				&& ipf->ipf_last_uses + ipf->ipf_uses <
-				    victim->ipf_last_uses + victim->ipf_uses)) {
-				if (victim->ipf_hash != ipf->ipf_hash)
-					IPFLOW_UNLOCK(victim);
-				victim = ipf;
-			}
-		}
-		if (victim && victim->ipf_hash != idx)
-			IPFLOW_HEAD_UNLOCK(head);
-	}
-	ipf = victim;
-    done:
-	/*
-	 * Remove the entry from the flow table.
-	 */
-	LIST_REMOVE(ipf, ipf_next);
-	IPFLOW_UNLOCK(ipf);
-
-	ipflow_addstats(ipf);
-	RTFREE(ipf->ipf_ro.ro_rt);
-	return ipf;
-}
-
-static void
-ipflow_free(struct ipflow *ipf)
-{
-	/*
-	 * Remove the flow from the hash table.
-	 */
-	LIST_REMOVE(ipf, ipf_next);
-
-	ipflow_addstats(ipf);
-	RTFREE(ipf->ipf_ro.ro_rt);
-	ipflow_inuse--;
-	free(ipf, M_IPFLOW);
-}
-
-void
-ipflow_slowtimo(void)
-{
-	struct ipflow *ipf;
-	int idx;
-
-	for (idx = 0; idx < IPFLOW_HASHSIZE; idx++) {
-		struct ipflow_head *head = &ipflows[idx];
-
-		IPFLOW_HEAD_LOCK(head);
-		ipf = LIST_FIRST(&head->ipfh_head);
-		while (ipf != NULL) {
-			struct ipflow *next_ipf = LIST_NEXT(ipf, ipf_next);
-			if (--ipf->ipf_timer == 0) {
-				ipflow_free(ipf);
-			} else {
-				ipf->ipf_last_uses = ipf->ipf_uses;
-				ipf->ipf_ro.ro_rt->rt_use += ipf->ipf_uses;
-				ipstat.ips_forward += ipf->ipf_uses;
-				ipstat.ips_fastforward += ipf->ipf_uses;
-				ipf->ipf_uses = 0;
-			}
-			ipf = next_ipf;
-		}
-		IPFLOW_HEAD_UNLOCK(head);
-	}
-}
-
-void
-ipflow_create(const struct route *ro, struct mbuf *m)
-{
-	const struct ip *const ip = mtod(m, struct ip *);
-	struct ipflow *ipf;
-
-	/*
-	 * Don't create cache entries for ICMP messages.
-	 */
-	if (!ipflow_active || ip->ip_p == IPPROTO_ICMP)
-		return;
-	/*
-	 * See if an existing flow struct exists.  If so remove it from it's
-	 * list and free the old route.  If not, try to malloc a new one
-	 * (if we aren't at our limit).
-	 */
-	ipf = ipflow_lookup(ip);
-	if (ipf == NULL) {
-		if (ipflow_inuse == IPFLOW_MAX) {
-			ipf = ipflow_reap();
-		} else {
-			ipf = (struct ipflow *) malloc(sizeof(*ipf), M_IPFLOW,
-						       M_NOWAIT);
-			if (ipf == NULL)
-				return;
-			ipflow_inuse++;
-		}
-		bzero((caddr_t) ipf, sizeof(*ipf));
-
-		ipf->ipf_hash = ipflow_hash(ip->ip_dst, ip->ip_src, ip->ip_tos);
-		ipf->ipf_dst = ip->ip_dst;
-		ipf->ipf_src = ip->ip_src;
-		ipf->ipf_tos = ip->ip_tos;
-
-		IPFLOW_LOCK(ipf);
-	} else {
-		LIST_REMOVE(ipf, ipf_next);
-
-		ipflow_addstats(ipf);		/* add stats to old route */
-		RTFREE(ipf->ipf_ro.ro_rt);	/* clear reference */
-		ipf->ipf_uses = ipf->ipf_last_uses = 0;
-		ipf->ipf_errors = ipf->ipf_dropped = 0;
-	}
-
-	/*
-	 * Fill in the updated information.
-	 */
-	ipf->ipf_ro = *ro;
-	RT_LOCK(ro->ro_rt);
-	RT_ADDREF(ro->ro_rt);
-	RT_UNLOCK(ro->ro_rt);
-	ipf->ipf_timer = IPFLOW_TIMER;
-	/*
-	 * Insert into the approriate bucket of the flow table.
-	 */
-	LIST_INSERT_HEAD(&ipflows[ipf->ipf_hash].ipfh_head, ipf, ipf_next);
-	IPFLOW_UNLOCK(ipf);
-}
-
-static void
-ipflow_init(void)
-{
-	int idx;
-
-	for (idx = 0; idx < IPFLOW_HASHSIZE; idx++) {
-		struct ipflow_head *head = &ipflows[idx];
-		LIST_INIT(&head->ipfh_head);
-		mtx_init(&head->ipfh_mtx, "ipflow list head", NULL, MTX_DEF);
-	}
-}
-SYSINIT(ipflow, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY, ipflow_init, 0);
Index: netinet/ip_flow.h
===================================================================
RCS file: /home/ncvs/src/sys/netinet/ip_flow.h,v
retrieving revision 1.5
diff -u -p -r1.5 ip_flow.h
--- netinet/ip_flow.h	1 Sep 2003 05:12:36 -0000	1.5
+++ netinet/ip_flow.h	11 Nov 2003 18:18:18 -0000
@@ -1,64 +1,4 @@
-/*-
- * Copyright (c) 1998 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by the 3am Software Foundry ("3am").  It was developed by Matt Thomas.
- *
- * 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. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *	This product includes software developed by the NetBSD
- *	Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- *    contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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_flow.h,v 1.5 2003/09/01 05:12:36 sam Exp $
+/*
+ * Gone...
  */
 
-#ifndef _NETINET_IP_FLOW_H
-#define _NETINET_IP_FLOW_H
-
-struct ipflow {
-	LIST_ENTRY(ipflow) ipf_next;	/* next ipflow in bucket */
-	struct in_addr ipf_dst;		/* destination address */
-	struct in_addr ipf_src;		/* source address */
-
-	/* NB: this assumes the size of the list head hash table is <=256 */
-	u_int8_t ipf_hash;		/* index in list head table */
-	u_int8_t ipf_tos;		/* type-of-service */
-	struct route ipf_ro;		/* associated route entry */
-	u_long ipf_uses;		/* number of uses in this period */
-
-	int ipf_timer;			/* remaining lifetime of this entry */
-	u_long ipf_dropped;		/* ENOBUFS returned by if_output */
-	u_long ipf_errors;		/* other errors returned by if_output */
-	u_long ipf_last_uses;		/* number of uses in last period */
-};
-
-struct ipflow_head {
-	LIST_HEAD(ipflowhead, ipflow) ipfh_head;
-	struct mtx ipfh_mtx;
-};
-
-#endif
Index: netinet/ip_fw.h
===================================================================
RCS file: /home/ncvs/src/sys/netinet/ip_fw.h,v
retrieving revision 1.80
diff -u -p -r1.80 ip_fw.h
--- netinet/ip_fw.h	31 Oct 2003 18:32:11 -0000	1.80
+++ netinet/ip_fw.h	11 Nov 2003 18:18:18 -0000
@@ -28,6 +28,7 @@
 #ifndef _IPFW2_H
 #define _IPFW2_H
 #define IPFW2  1
+
 /*
  * The kernel representation of ipfw rules is made of a list of
  * 'instructions' (for all practical purposes equivalent to BPF
Index: netinet/ip_fw2.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/ip_fw2.c,v
retrieving revision 1.43
diff -u -p -r1.43 ip_fw2.c
--- netinet/ip_fw2.c	7 Nov 2003 23:26:57 -0000	1.43
+++ netinet/ip_fw2.c	11 Nov 2003 18:18:18 -0000
@@ -463,13 +463,16 @@ verify_rev_path(struct in_addr src, stru
 		dst->sin_len = sizeof(*dst);
 		dst->sin_addr = src;
 
-		rtalloc_ign(&ro, RTF_CLONING|RTF_PRCLONING);
+		rtalloc_ign(&ro, RTF_CLONING);
 	}
 
-	if ((ro.ro_rt == NULL) || (ifp == NULL) ||
-	    (ro.ro_rt->rt_ifp->if_index != ifp->if_index))
+	if (ro.ro_rt == NULL)
 		return 0;
-
+	if ((ifp == NULL) || (ro.ro_rt->rt_ifp->if_index != ifp->if_index)) {
+		RTFREE(ro.ro_rt);
+		return 0;
+	}
+	RTFREE(ro.ro_rt);
 	return 1;
 }
 
@@ -1160,7 +1163,6 @@ send_pkt(struct ipfw_flow_id *id, u_int3
 	struct mbuf *m;
 	struct ip *ip;
 	struct tcphdr *tcp;
-	struct route sro;	/* fake route */
 
 	MGETHDR(m, M_DONTWAIT, MT_HEADER);
 	if (m == 0)
@@ -1226,12 +1228,8 @@ send_pkt(struct ipfw_flow_id *id, u_int3
 	 */
 	ip->ip_ttl = ip_defttl;
 	ip->ip_len = m->m_pkthdr.len;
-	bzero (&sro, sizeof (sro));
-	ip_rtaddr(ip->ip_dst, &sro);
 	m->m_flags |= M_SKIP_FIREWALL;
-	ip_output(m, NULL, &sro, 0, NULL, NULL);
-	if (sro.ro_rt)
-		RTFREE(sro.ro_rt);
+	ip_output(m, NULL, NULL, 0, NULL, NULL);
 }
 
 /*
Index: netinet/ip_icmp.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/ip_icmp.c,v
retrieving revision 1.81
diff -u -p -r1.81 ip_icmp.c
--- netinet/ip_icmp.c	29 Oct 2003 05:40:07 -0000	1.81
+++ netinet/ip_icmp.c	11 Nov 2003 18:18:19 -0000
@@ -52,11 +52,15 @@
 #include <net/route.h>
 
 #include <netinet/in.h>
+#include <netinet/in_pcb.h>
 #include <netinet/in_systm.h>
 #include <netinet/in_var.h>
 #include <netinet/ip.h>
 #include <netinet/ip_icmp.h>
 #include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
 #include <netinet/icmp_var.h>
 
 #ifdef IPSEC
@@ -120,7 +124,7 @@ int	icmpprintfs = 0;
 #endif
 
 static void	icmp_reflect(struct mbuf *);
-static void	icmp_send(struct mbuf *, struct mbuf *, struct route *);
+static void	icmp_send(struct mbuf *, struct mbuf *);
 static int	ip_next_mtu(int, int);
 
 extern	struct protosw inetsw[];
@@ -165,6 +169,18 @@ icmp_error(n, type, code, dest, destifp)
 	if (n->m_flags & (M_BCAST|M_MCAST))
 		goto freeit;
 	/*
+	 * Limit sending of ICMP host unreachable messages.
+	 * If we are acting as a router and someone is doing a sweep
+	 * scan (eg. nmap and/or numerous windows worms) for destinations
+	 * we are the gateway for but are not reachable (ie. a /24 on a
+	 * interface and only a couple of hosts on the ethernet) we would
+	 * generate a storm of ICMP host unreachable messages.
+	 */
+	if (type == ICMP_UNREACH && code == ICMP_UNREACH_HOST) {
+		if (badport_bandlim(BANDLIM_ICMP_UNREACH_HOST) < 0)
+			goto freeit;
+	}
+	/*
 	 * First, formulate icmp message
 	 */
 	m = m_gethdr(M_DONTWAIT, MT_HEADER);
@@ -233,27 +249,34 @@ freeit:
 	m_freem(n);
 }
 
-static struct sockaddr_in icmpsrc = { sizeof (struct sockaddr_in), AF_INET };
-static struct sockaddr_in icmpdst = { sizeof (struct sockaddr_in), AF_INET };
-static struct sockaddr_in icmpgw = { sizeof (struct sockaddr_in), AF_INET };
-
 /*
  * Process a received ICMP message.
  */
 void
 icmp_input(m, off)
-	register struct mbuf *m;
+	struct mbuf *m;
 	int off;
 {
 	int hlen = off;
-	register struct icmp *icp;
-	register struct ip *ip = mtod(m, struct ip *);
-	int icmplen = ip->ip_len;
-	register int i;
+	struct icmp *icp;
 	struct in_ifaddr *ia;
+	struct ip *ip = mtod(m, struct ip *);
+	int icmplen = ip->ip_len;
+	int i, code;
 	void (*ctlfunc)(int, struct sockaddr *, void *);
-	int code;
+	struct sockaddr_in icmpsrc, icmpdst, icmpgw;
 
+	/* Initialize */
+	bzero(&icmpsrc, sizeof(icmpsrc));
+	icmpsrc.sin_len = sizeof(struct sockaddr_in);
+	icmpsrc.sin_family = AF_INET;
+	bzero(&icmpdst, sizeof(icmpdst));
+	icmpdst.sin_len = sizeof(struct sockaddr_in);
+	icmpdst.sin_family = AF_INET;
+	bzero(&icmpgw, sizeof(icmpgw));
+	icmpgw.sin_len = sizeof(struct sockaddr_in);
+	icmpgw.sin_family = AF_INET;
+	
 	/*
 	 * Locate icmp structure in mbuf, and check
 	 * that not corrupted and of at least minimum length.
@@ -387,7 +410,7 @@ icmp_input(m, off)
 			printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
 #endif
 		icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
-#if 1
+
 		/*
 		 * MTU discovery:
 		 * If we got a needfrag and there is a host route to the
@@ -397,40 +420,38 @@ icmp_input(m, off)
 		 * notice that the MTU has changed and adapt accordingly.
 		 * If no new MTU was suggested, then we guess a new one
 		 * less than the current value.  If the new MTU is 
-		 * unreasonably small (arbitrarily set at 296), then
-		 * we reset the MTU to the interface value and enable the
-		 * lock bit, indicating that we are no longer doing MTU
-		 * discovery.
+		 * unreasonably small (defined by sysctl tcp_minmss), then
+		 * we don't update the MTU value.
+		 *
+		 * XXX: All this should be done in tcp_mtudisc() because
+		 * the way we do it now, everyone can send us bogus ICMP
+		 * MSGSIZE packets for any destination. By doing this far
+		 * higher in the chain we have a matching tcp connection.
+		 * Thus spoofing is much harder. However there is no easy
+		 * non-hackish way to pass the new MTU up to tcp_mtudisc().
+		 * Also see next XXX regarding IPv4 AH TCP.
 		 */
 		if (code == PRC_MSGSIZE) {
-			struct rtentry *rt;
 			int mtu;
+			struct in_conninfo inc;
+
+			bzero(&inc, sizeof(inc));
+			inc.inc_flags = 0; /* IPv4 */
+			inc.inc_faddr = icmpsrc.sin_addr;
+
+			mtu = ntohs(icp->icmp_nextmtu);
+			if (!mtu)
+				mtu = ip_next_mtu(mtu, 1);
+
+			if (mtu >= max(296, (tcp_minmss + sizeof(struct tcpiphdr))))
+				tcp_hc_updatemtu(&inc, mtu);
 
-			rt = rtalloc1((struct sockaddr *)&icmpsrc, 0,
-				      RTF_CLONING | RTF_PRCLONING);
-			if (rt && (rt->rt_flags & RTF_HOST)
-			    && !(rt->rt_rmx.rmx_locks & RTV_MTU)) {
-				mtu = ntohs(icp->icmp_nextmtu);
-				if (!mtu)
-					mtu = ip_next_mtu(rt->rt_rmx.rmx_mtu,
-							  1);
 #ifdef DEBUG_MTUDISC
-				printf("MTU for %s reduced to %d\n",
-					inet_ntoa(icmpsrc.sin_addr), mtu);
+			printf("MTU for %s reduced to %d\n",
+				inet_ntoa(icmpsrc.sin_addr), mtu);
 #endif
-				if (mtu < 296) {
-					/* rt->rt_rmx.rmx_mtu =
-						rt->rt_ifp->if_mtu; */
-					rt->rt_rmx.rmx_locks |= RTV_MTU;
-				} else if (rt->rt_rmx.rmx_mtu > mtu) {
-					rt->rt_rmx.rmx_mtu = mtu;
-				}
-			}
-			if (rt)
-				rtfree(rt);
 		}
 
-#endif
 		/*
 		 * XXX if the packet contains [IPv4 AH TCP], we can't make a
 		 * notification to TCP layer.
@@ -607,7 +628,6 @@ icmp_reflect(m)
 	struct in_addr t;
 	struct mbuf *opts = 0;
 	int optlen = (ip->ip_hl << 2) - sizeof(struct ip);
-	struct route *ro = NULL, rt;
 
 	if (!in_canforward(ip->ip_src) &&
 	    ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) !=
@@ -618,8 +638,6 @@ icmp_reflect(m)
 	}
 	t = ip->ip_dst;
 	ip->ip_dst = ip->ip_src;
-	ro = &rt;
-	bzero(ro, sizeof(*ro));
 	/*
 	 * If the incoming packet was addressed directly to us,
 	 * use dst as the src for the reply.  Otherwise (broadcast
@@ -640,7 +658,7 @@ icmp_reflect(m)
 				goto match;
 		}
 	}
-	ia = ip_rtaddr(ip->ip_dst, ro);
+	ia = ip_rtaddr(ip->ip_dst);
 	/* We need a route to do anything useful. */
 	if (ia == NULL) {
 		m_freem(m);
@@ -730,12 +748,10 @@ match:
 	}
 	m_tag_delete_nonpersistent(m);
 	m->m_flags &= ~(M_BCAST|M_MCAST);
-	icmp_send(m, opts, ro);
+	icmp_send(m, opts);
 done:
 	if (opts)
 		(void)m_free(opts);
-	if (ro && ro->ro_rt)
-		RTFREE(ro->ro_rt);
 }
 
 /*
@@ -743,10 +759,9 @@ done:
  * after supplying a checksum.
  */
 static void
-icmp_send(m, opts, rt)
+icmp_send(m, opts)
 	register struct mbuf *m;
 	struct mbuf *opts;
-	struct route *rt;
 {
 	register struct ip *ip = mtod(m, struct ip *);
 	register int hlen;
@@ -769,7 +784,7 @@ icmp_send(m, opts, rt)
 		       buf, inet_ntoa(ip->ip_src));
 	}
 #endif
-	(void) ip_output(m, opts, rt, 0, NULL, NULL);
+	(void) ip_output(m, opts, NULL, 0, NULL, NULL);
 }
 
 n_time
@@ -783,7 +798,6 @@ iptime()
 	return (htonl(t));
 }
 
-#if 1
 /*
  * Return the next larger or smaller MTU plateau (table from RFC 1191)
  * given current value MTU.  If DIR is less than zero, a larger plateau
@@ -821,7 +835,6 @@ ip_next_mtu(mtu, dir)
 		}
 	}
 }
-#endif
 
 
 /*
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	11 Nov 2003 18:18:19 -0000
@@ -213,52 +213,6 @@ int fw_one_pass = 1;
 ip_dn_io_t *ip_dn_io_ptr;
 
 /*
- * One deep route cache for ip forwarding.  This is done
- * very inefficiently.  We don't care as it's about to be
- * replaced by something better.
- */
-static struct rtcache {
-	struct route	rc_ro;		/* most recently used route */
-	struct mtx	rc_mtx;		/* update lock for cache */
-} ip_fwdcache;
-
-#define	RTCACHE_LOCK()		mtx_lock(&ip_fwdcache.rc_mtx)
-#define	RTCACHE_UNLOCK()	mtx_unlock(&ip_fwdcache.rc_mtx)
-#define	RTCACHE_LOCK_INIT() \
-	mtx_init(&ip_fwdcache.rc_mtx, "route cache", NULL, MTX_DEF)
-#define	RTCACHE_LOCK_ASSERT()	mtx_assert(&ip_fwdcache.rc_mtx, MA_OWNED)
-
-/*
- * Get a copy of the current route cache contents.
- */
-#define	RTCACHE_GET(_ro) do {					\
-	struct rtentry *rt;					\
-	RTCACHE_LOCK();						\
-	*(_ro) = ip_fwdcache.rc_ro;				\
-	if ((rt = (_ro)->ro_rt) != NULL) {			\
-		RT_LOCK(rt);					\
-		RT_ADDREF(rt);					\
-		RT_UNLOCK(rt);					\
-	}							\
-	RTCACHE_UNLOCK();					\
-} while (0)
-
-/*
- * Update the cache contents.
- */
-#define	RTCACHE_UPDATE(_ro) do {				\
-	struct rtentry *rt;					\
-	RTCACHE_LOCK();						\
-	rt = ip_fwdcache.rc_ro.ro_rt;				\
-	if ((_ro)->ro_rt != rt) {				\
-		ip_fwdcache.rc_ro = *(_ro);			\
-		if (rt)						\
-			RTFREE(rt);				\
-	}							\
-	RTCACHE_UNLOCK();					\
-} while (0)
-
-/*
  * XXX this is ugly -- the following two global variables are
  * used to store packet state while it travels through the stack.
  * Note that the code even makes assumptions on the size and
@@ -282,7 +236,7 @@ static	struct ip_srcrt {
 static void	save_rte(u_char *, struct in_addr);
 static int	ip_dooptions(struct mbuf *m, int,
 			struct sockaddr_in *next_hop);
-static void	ip_forward(struct mbuf *m, struct route *, int srcrt,
+static void	ip_forward(struct mbuf *m, int srcrt,
 			struct sockaddr_in *next_hop);
 static void	ip_freef(struct ipqhead *, struct ipq *);
 static struct	mbuf *ip_reass(struct mbuf *, struct ipqhead *,
@@ -323,9 +277,6 @@ ip_init()
 	for (i = 0; i < IPREASS_NHASH; i++)
 	    TAILQ_INIT(&ipq[i]);
 
-	bzero(&ip_fwdcache, sizeof(ip_fwdcache));
-	RTCACHE_LOCK_INIT();
-
 	maxnipq = nmbclusters / 32;
 	maxfragsperpacket = 16;
 
@@ -338,39 +289,23 @@ ip_init()
 }
 
 /*
- * Invalidate any cached route used for forwarding.
- */
-void
-ip_forward_cacheinval(void)
-{
-	struct rtentry *rt;
-
-	RTCACHE_LOCK();
-	rt = ip_fwdcache.rc_ro.ro_rt;
-	ip_fwdcache.rc_ro.ro_rt = 0;
-	if (rt != NULL)
-		RTFREE(rt);
-	RTCACHE_UNLOCK();
-}
-
-/*
  * Ip input routine.  Checksum and byte swap header.  If fragmented
  * try to reassemble.  Process options.  Pass to next level.
  */
 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 */
 	struct ip_fw_args args;
-	struct route cro;			/* copy of cached route */
-	int srcrt = 0;				/* forward by ``src routing'' */
+	int dchg = 0;				/* dest changed after fw */
 #ifdef PFIL_HOOKS
 	struct in_addr odst;			/* original dst address */
 #endif
@@ -406,11 +341,18 @@ 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 *);
 		hlen = ip->ip_hl << 2;
@@ -542,7 +484,7 @@ iphack:
 	if (m == NULL)			/* consumed by filter */
 		return;
 	ip = mtod(m, struct ip *);
-	srcrt = (odst.s_addr != ip->ip_dst.s_addr);
+	dchg = (odst.s_addr != ip->ip_dst.s_addr);
 #endif /* PFIL_HOOKS */
 
 	if (fw_enable && IPFW_LOADED) {
@@ -778,8 +720,7 @@ pass:
 			goto bad;
 		}
 #endif /* FAST_IPSEC */
-		RTCACHE_GET(&cro);
-		ip_forward(m, &cro, srcrt, args.next_hop);
+		ip_forward(m, dchg, args.next_hop);
 	}
 	return;
 
@@ -1350,7 +1291,6 @@ ip_slowtimo()
 		}
 	}
 	IPQ_UNLOCK();
-	ipflow_slowtimo();
 	splx(s);
 }
 
@@ -1396,14 +1336,6 @@ ip_dooptions(struct mbuf *m, int pass, s
 	struct in_addr *sin, dst;
 	n_time ntime;
 	struct	sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET };
-	struct route cro;			/* copy of cached route */
-
-	/*
-	 * Grab a copy of the route cache in case we need
-	 * to update to reflect source routing or the like.
-	 * Could optimize this to do it later...
-	 */
-	RTCACHE_GET(&cro);
 
 	dst = ip->ip_dst;
 	cp = (u_char *)(ip + 1);
@@ -1523,7 +1455,7 @@ dropit:
 			    if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
 				ia = (INA)ifa_ifwithnet((SA)&ipaddr);
 			} else
-				ia = ip_rtaddr(ipaddr.sin_addr, &cro);
+				ia = ip_rtaddr(ipaddr.sin_addr);
 			if (ia == 0) {
 				type = ICMP_UNREACH;
 				code = ICMP_UNREACH_SRCFAIL;
@@ -1565,7 +1497,7 @@ dropit:
 			 * use the incoming interface (should be same).
 			 */
 			if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
-			    (ia = ip_rtaddr(ipaddr.sin_addr, &cro)) == 0) {
+			    (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
 				type = ICMP_UNREACH;
 				code = ICMP_UNREACH_HOST;
 				goto bad;
@@ -1645,7 +1577,7 @@ dropit:
 		}
 	}
 	if (forward && ipforwarding) {
-		ip_forward(m, &cro, 1, next_hop);
+		ip_forward(m, 1, next_hop);
 		return (1);
 	}
 	return (0);
@@ -1660,30 +1592,26 @@ bad:
  * return internet address info of interface to be used to get there.
  */
 struct in_ifaddr *
-ip_rtaddr(dst, rt)
+ip_rtaddr(dst)
 	struct in_addr dst;
-	struct route *rt;
 {
-	register struct sockaddr_in *sin;
+	struct sockaddr_in *sin;
+	struct in_ifaddr *ifa;
+	struct route ro;
+
+	bzero(&ro, sizeof(ro));
+	sin = (struct sockaddr_in *)&ro.ro_dst;
+	sin->sin_family = AF_INET;
+	sin->sin_len = sizeof(*sin);
+	sin->sin_addr = dst;
+	rtalloc(&ro);
 
-	sin = (struct sockaddr_in *)&rt->ro_dst;
-
-	if (rt->ro_rt == 0 ||
-	    !(rt->ro_rt->rt_flags & RTF_UP) ||
-	    dst.s_addr != sin->sin_addr.s_addr) {
-		if (rt->ro_rt) {
-			RTFREE(rt->ro_rt);
-			rt->ro_rt = 0;
-		}
-		sin->sin_family = AF_INET;
-		sin->sin_len = sizeof(*sin);
-		sin->sin_addr = dst;
-
-		rtalloc_ign(rt, RTF_PRCLONING);
-	}
-	if (rt->ro_rt == 0)
+	if (ro.ro_rt == 0)
 		return ((struct in_ifaddr *)0);
-	return (ifatoia(rt->ro_rt->rt_ifa));
+
+	ifa = ifatoia(ro.ro_rt->rt_ifa);
+	RTFREE(ro.ro_rt);
+	return ifa;
 }
 
 /*
@@ -1830,11 +1758,10 @@ u_char inetctlerrmap[PRC_NCMDS] = {
  * via a source route.
  */
 static void
-ip_forward(struct mbuf *m, struct route *ro,
-	int srcrt, struct sockaddr_in *next_hop)
+ip_forward(struct mbuf *m, int srcrt, struct sockaddr_in *next_hop)
 {
 	struct ip *ip = mtod(m, struct ip *);
-	struct rtentry *rt;
+	struct in_ifaddr *ia;
 	int error, type = 0, code = 0;
 	struct mbuf *mcopy;
 	n_long dest;
@@ -1844,7 +1771,6 @@ ip_forward(struct mbuf *m, struct route 
 	struct ifnet dummyifp;
 #endif
 
-	dest = 0;
 	/*
 	 * Cache the destination address of the packet; this may be
 	 * changed by use of 'ipfw fwd'.
@@ -1869,18 +1795,17 @@ ip_forward(struct mbuf *m, struct route 
 #endif
 		if (ip->ip_ttl <= IPTTLDEC) {
 			icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS,
-			    dest, 0);
+			    0, 0);
 			return;
 		}
 #ifdef IPSTEALTH
 	}
 #endif
 
-	if (ip_rtaddr(pkt_dst, ro) == 0) {
-		icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest, 0);
+	if ((ia = ip_rtaddr(pkt_dst)) == 0) {
+		icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0);
 		return;
-	} else
-		rt = ro->ro_rt;
+	}
 
 	/*
 	 * Save the IP header and at most 8 bytes of the payload,
@@ -1931,27 +1856,44 @@ ip_forward(struct mbuf *m, struct route 
 	 * Also, don't send redirect if forwarding using a default route
 	 * or a route modified by a redirect.
 	 */
-	if (rt->rt_ifp == m->m_pkthdr.rcvif &&
-	    (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 &&
-	    satosin(rt_key(rt))->sin_addr.s_addr != 0 &&
-	    ipsendredirects && !srcrt && !next_hop) {
+	dest = 0;
+	if (ia->ia_ifp == m->m_pkthdr.rcvif) {
+		struct sockaddr_in *sin;
+		struct route ro;
+		struct rtentry *rt;
+
+		bzero(&ro, sizeof(ro));
+		sin = (struct sockaddr_in *)&ro.ro_dst;
+		sin->sin_family = AF_INET;
+		sin->sin_len = sizeof(*sin);
+		sin->sin_addr = pkt_dst;
+		rtalloc(&ro);
+
+		rt = ro.ro_rt;
+
+		if (rt && (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 &&
+		    satosin(rt_key(rt))->sin_addr.s_addr != 0 &&
+		    ipsendredirects && !srcrt && !next_hop) {
 #define	RTA(rt)	((struct in_ifaddr *)(rt->rt_ifa))
-		u_long src = ntohl(ip->ip_src.s_addr);
+			u_long src = ntohl(ip->ip_src.s_addr);
 
-		if (RTA(rt) &&
-		    (src & RTA(rt)->ia_subnetmask) == RTA(rt)->ia_subnet) {
-		    if (rt->rt_flags & RTF_GATEWAY)
-			dest = satosin(rt->rt_gateway)->sin_addr.s_addr;
-		    else
-			dest = pkt_dst.s_addr;
-		    /* Router requirements says to only send host redirects */
-		    type = ICMP_REDIRECT;
-		    code = ICMP_REDIRECT_HOST;
+			if (RTA(rt) &&
+			    (src & RTA(rt)->ia_subnetmask) == RTA(rt)->ia_subnet) {
+				if (rt->rt_flags & RTF_GATEWAY)
+					dest = satosin(rt->rt_gateway)->sin_addr.s_addr;
+				else
+					dest = pkt_dst.s_addr;
+				/* Router requirements says to only send host redirects */
+				type = ICMP_REDIRECT;
+				code = ICMP_REDIRECT_HOST;
 #ifdef DIAGNOSTIC
-		    if (ipprintfs)
-		        printf("redirect (%d) to %lx\n", code, (u_long)dest);
+				if (ipprintfs)
+					printf("redirect (%d) to %lx\n", code, (u_long)dest);
 #endif
+			}
 		}
+		if (rt)
+			RTFREE(rt);
 	}
 
     {
@@ -1966,13 +1908,8 @@ ip_forward(struct mbuf *m, struct route 
 		tag.mh_next = m;
 		m = (struct mbuf *)&tag;
 	}
-	error = ip_output(m, (struct mbuf *)0, ro, IP_FORWARDING, 0, NULL);
+	error = ip_output(m, (struct mbuf *)0, NULL, IP_FORWARDING, 0, NULL);
     }
-	/*
-	 * Update the ip forwarding cache with the route we used.
-	 * We may want to do this more selectively; not sure.
-	 */
-	RTCACHE_UPDATE(ro);
 	if (error)
 		ipstat.ips_cantforward++;
 	else {
@@ -1980,10 +1917,8 @@ ip_forward(struct mbuf *m, struct route 
 		if (type)
 			ipstat.ips_redirectsent++;
 		else {
-			if (mcopy) {
-				ipflow_create(ro, mcopy);
+			if (mcopy)
 				m_freem(mcopy);
-			}
 			return;
 		}
 	}
@@ -2009,77 +1944,31 @@ ip_forward(struct mbuf *m, struct route 
 	case EMSGSIZE:
 		type = ICMP_UNREACH;
 		code = ICMP_UNREACH_NEEDFRAG;
-#ifdef IPSEC
+#if defined(IPSEC) || defined(FAST_IPSEC)
 		/*
 		 * If the packet is routed over IPsec tunnel, tell the
 		 * originator the tunnel MTU.
 		 *	tunnel MTU = if MTU - sizeof(IP) - ESP/AH hdrsiz
 		 * XXX quickhack!!!
 		 */
-		if (ro->ro_rt) {
+		{
 			struct secpolicy *sp = NULL;
 			int ipsecerror;
 			int ipsechdr;
+			struct route *ro;
 
+#ifdef IPSEC
 			sp = ipsec4_getpolicybyaddr(mcopy,
-						    IPSEC_DIR_OUTBOUND,
-			                            IP_FORWARDING,
-			                            &ipsecerror);
-
-			if (sp == NULL)
-				destifp = ro->ro_rt->rt_ifp;
-			else {
-				/* count IPsec header size */
-				ipsechdr = ipsec4_hdrsiz(mcopy,
-							 IPSEC_DIR_OUTBOUND,
-							 NULL);
-
-				/*
-				 * find the correct route for outer IPv4
-				 * header, compute tunnel MTU.
-				 *
-				 * XXX BUG ALERT
-				 * The "dummyifp" code relies upon the fact
-				 * that icmp_error() touches only ifp->if_mtu.
-				 */
-				/*XXX*/
-				destifp = NULL;
-				if (sp->req != NULL
-				 && sp->req->sav != NULL
-				 && sp->req->sav->sah != NULL) {
-					struct route *saro;
-					saro = &sp->req->sav->sah->sa_route;
-					if (saro->ro_rt && saro->ro_rt->rt_ifp) {
-						dummyifp.if_mtu =
-						    saro->ro_rt->rt_ifp->if_mtu;
-						dummyifp.if_mtu -= ipsechdr;
-						destifp = &dummyifp;
-					}
-				}
-
-				key_freesp(sp);
-			}
-		}
-#elif FAST_IPSEC
-		/*
-		 * If the packet is routed over IPsec tunnel, tell the
-		 * originator the tunnel MTU.
-		 *	tunnel MTU = if MTU - sizeof(IP) - ESP/AH hdrsiz
-		 * XXX quickhack!!!
-		 */
-		if (ro->ro_rt) {
-			struct secpolicy *sp = NULL;
-			int ipsecerror;
-			int ipsechdr;
-
-			sp = ipsec_getpolicybyaddr(mcopy,
 						   IPSEC_DIR_OUTBOUND,
 			                           IP_FORWARDING,
 			                           &ipsecerror);
-
-			if (sp == NULL)
-				destifp = ro->ro_rt->rt_ifp;
-			else {
+#else /* FAST_IPSEC */
+			sp = ipsec_getpolicybyaddr(mcopy,
+						   IPSEC_DIR_OUTBOUND,
+						   IP_FORWARDING,
+						   &ipsecerror);
+#endif
+			if (sp != NULL) {
 				/* count IPsec header size */
 				ipsechdr = ipsec4_hdrsiz(mcopy,
 							 IPSEC_DIR_OUTBOUND,
@@ -2098,23 +1987,28 @@ ip_forward(struct mbuf *m, struct route 
 				if (sp->req != NULL
 				 && sp->req->sav != NULL
 				 && sp->req->sav->sah != NULL) {
-					struct route *saro;
-					saro = &sp->req->sav->sah->sa_route;
-					if (saro->ro_rt && saro->ro_rt->rt_ifp) {
+					ro = &sp->req->sav->sah->sa_route;
+					if (ro->ro_rt && ro->ro_rt->rt_ifp) {
 						dummyifp.if_mtu =
-						    saro->ro_rt->rt_ifp->if_mtu;
+						    ro->ro_rt->rt_ifp->if_mtu;
 						dummyifp.if_mtu -= ipsechdr;
 						destifp = &dummyifp;
 					}
 				}
 
+#ifdef IPSEC
+				key_freesp(sp);
+#else /* FAST_IPSEC */
 				KEY_FREESP(&sp);
-			}
+#endif
+				ipstat.ips_cantfrag++;
+				break;
+			} else 
+#endif /*IPSEC || FAST_IPSEC*/
+		destifp = ia->ia_ifp;
+#if defined(IPSEC) || defined(FAST_IPSEC)
 		}
-#else /* !IPSEC && !FAST_IPSEC */
-		if (ro->ro_rt)
-			destifp = ro->ro_rt->rt_ifp;
-#endif /*IPSEC*/
+#endif /*IPSEC || FAST_IPSEC*/
 		ipstat.ips_cantfrag++;
 		break;
 
Index: netinet/ip_output.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/ip_output.c,v
retrieving revision 1.198
diff -u -p -r1.198 ip_output.c
--- netinet/ip_output.c	8 Nov 2003 23:03:29 -0000	1.198
+++ netinet/ip_output.c	11 Nov 2003 18:18:19 -0000
@@ -142,13 +142,12 @@ ip_output(struct mbuf *m0, struct mbuf *
 	struct in_ifaddr *ia = NULL;
 	int isbroadcast, sw_csum;
 	struct in_addr pkt_dst;
-#ifdef IPSEC
 	struct route iproute;
+#ifdef IPSEC
 	struct socket *so;
 	struct secpolicy *sp = NULL;
 #endif
 #ifdef FAST_IPSEC
-	struct route iproute;
 	struct m_tag *mtag;
 	struct secpolicy *sp = NULL;
 	struct tdb_ident *tdbi;
@@ -202,10 +201,12 @@ ip_output(struct mbuf *m0, struct mbuf *
 #endif /*IPSEC*/
 
 	M_ASSERTPKTHDR(m);
-#ifndef FAST_IPSEC
-	KASSERT(ro != NULL, ("ip_output: no route, proto %d",
-	    mtod(m, struct ip *)->ip_p));
-#endif
+
+	if (ro == NULL) {
+		ro = &iproute;
+		bzero(ro, sizeof (*ro));
+	}
+
 	if (inp != NULL)
 		INP_LOCK_ASSERT(inp);
 
@@ -256,12 +257,6 @@ ip_output(struct mbuf *m0, struct mbuf *
 		hlen = ip->ip_hl << 2;
 	}
 
-#ifdef FAST_IPSEC
-	if (ro == NULL) {
-		ro = &iproute;
-		bzero(ro, sizeof (*ro));
-	}
-#endif /* FAST_IPSEC */
 	dst = (struct sockaddr_in *)&ro->ro_dst;
 	/*
 	 * If there is a cached route,
@@ -316,7 +311,7 @@ ip_output(struct mbuf *m0, struct mbuf *
 		 * for correct operation (as it is for ARP).
 		 */
 		if (ro->ro_rt == 0)
-			rtalloc_ign(ro, RTF_PRCLONING);
+			rtalloc(ro);
 		if (ro->ro_rt == 0) {
 			ipstat.ips_noroute++;
 			error = EHOSTUNREACH;
@@ -324,7 +319,7 @@ ip_output(struct mbuf *m0, struct mbuf *
 		}
 		ia = ifatoia(ro->ro_rt->rt_ifa);
 		ifp = ro->ro_rt->rt_ifp;
-		ro->ro_rt->rt_use++;
+		ro->ro_rt->rt_rmx.rmx_pksent++;
 		if (ro->ro_rt->rt_flags & RTF_GATEWAY)
 			dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway;
 		if (ro->ro_rt->rt_flags & RTF_HOST)
@@ -944,7 +939,7 @@ spd_done:
 			bcopy(dst, &ro_fwd->ro_dst, sizeof(*dst));
 
 			ro_fwd->ro_rt = 0;
-			rtalloc_ign(ro_fwd, RTF_PRCLONING);
+			rtalloc(ro_fwd);
 
 			if (ro_fwd->ro_rt == 0) {
 				ipstat.ips_noroute++;
@@ -954,7 +949,7 @@ spd_done:
 
 			ia = ifatoia(ro_fwd->ro_rt->rt_ifa);
 			ifp = ro_fwd->ro_rt->rt_ifp;
-			ro_fwd->ro_rt->rt_use++;
+			ro_fwd->ro_rt->rt_rmx.rmx_pksent++;
 			if (ro_fwd->ro_rt->rt_flags & RTF_GATEWAY)
 				dst = (struct sockaddr_in *)
 					ro_fwd->ro_rt->rt_gateway;
@@ -1011,7 +1006,8 @@ pass:
 	 * If small enough for interface, or the interface will take
 	 * care of the fragmentation for us, can just send directly.
 	 */
-	if (ip->ip_len <= ifp->if_mtu || ifp->if_hwassist & CSUM_FRAGMENT) {
+	if (ip->ip_len <= ifp->if_mtu || (ifp->if_hwassist & CSUM_FRAGMENT &&
+	    ((ip->ip_off & IP_DF) == 0))) {
 		ip->ip_len = htons(ip->ip_len);
 		ip->ip_off = htons(ip->ip_off);
 		ip->ip_sum = 0;
@@ -1048,7 +1044,6 @@ pass:
 		 * routes when the MTU is changed.
 		 */
 		if ((ro->ro_rt->rt_flags & (RTF_UP | RTF_HOST)) &&
-		    !(ro->ro_rt->rt_rmx.rmx_locks & RTV_MTU) &&
 		    (ro->ro_rt->rt_rmx.rmx_mtu > ifp->if_mtu)) {
 			ro->ro_rt->rt_rmx.rmx_mtu = ifp->if_mtu;
 		}
@@ -1087,11 +1082,11 @@ pass:
 		ipstat.ips_fragmented++;
 
 done:
-#ifdef IPSEC
 	if (ro == &iproute && ro->ro_rt) {
 		RTFREE(ro->ro_rt);
 		ro->ro_rt = NULL;
 	}
+#ifdef IPSEC
 	if (sp != NULL) {
 		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
 			printf("DP ip_output call free SP:%p\n", sp));
@@ -1099,10 +1094,6 @@ done:
 	}
 #endif
 #ifdef FAST_IPSEC
-	if (ro == &iproute && ro->ro_rt) {
-		RTFREE(ro->ro_rt);
-		ro->ro_rt = NULL;
-	}
 	if (sp != NULL)
 		KEY_FREESP(&sp);
 #endif
Index: netinet/ip_var.h
===================================================================
RCS file: /home/ncvs/src/sys/netinet/ip_var.h,v
retrieving revision 1.78
diff -u -p -r1.78 ip_var.h
--- netinet/ip_var.h	8 Nov 2003 23:09:42 -0000	1.78
+++ netinet/ip_var.h	11 Nov 2003 18:18:19 -0000
@@ -166,7 +166,6 @@ int	 ip_ctloutput(struct socket *, struc
 void	 ip_drain(void);
 int	 ip_fragment(struct ip *ip, struct mbuf **m_frag, int mtu,
 	    u_long if_hwassist_flags, int sw_csum);
-void	 ip_forward_cacheinval(void);
 void	 ip_freemoptions(struct ip_moptions *);
 void	 ip_init(void);
 extern int	 (*ip_mforward)(struct ip *, struct ifnet *, struct mbuf *,
@@ -175,7 +174,7 @@ int	 ip_output(struct mbuf *,
 	    struct mbuf *, struct route *, int, struct ip_moptions *,
 	    struct inpcb *);
 struct in_ifaddr *
-	 ip_rtaddr(struct in_addr, struct route *);
+	 ip_rtaddr(struct in_addr);
 void	 ip_savecontrol(struct inpcb *, struct mbuf **, struct ip *,
 		struct mbuf *);
 void	 ip_slowtimo(void);
Index: netinet/raw_ip.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/raw_ip.c,v
retrieving revision 1.119
diff -u -p -r1.119 raw_ip.c
--- netinet/raw_ip.c	8 Nov 2003 22:53:41 -0000	1.119
+++ netinet/raw_ip.c	11 Nov 2003 18:18:19 -0000
@@ -302,7 +302,7 @@ rip_output(struct mbuf *m, struct socket
 	if (inp->inp_flags & INP_ONESBCAST)
 		flags |= IP_SENDONES;
 
-	return (ip_output(m, inp->inp_options, &inp->inp_route, flags,
+	return (ip_output(m, inp->inp_options, NULL, flags,
 			  inp->inp_moptions, inp));
 }
 
Index: netinet/tcp.h
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp.h,v
retrieving revision 1.18
diff -u -p -r1.18 tcp.h
--- netinet/tcp.h	2 Oct 2002 04:22:34 -0000	1.18
+++ netinet/tcp.h	11 Nov 2003 18:18:19 -0000
@@ -105,11 +105,32 @@ struct tcphdr {
 
 /*
  * Default maximum segment size for TCP.
- * With an IP MSS of 576, this is 536,
+ * With an IP MTU of 576, this is 536,
  * but 512 is probably more convenient.
  * This should be defined as MIN(512, IP_MSS - sizeof (struct tcpiphdr)).
  */
 #define	TCP_MSS	512
+/*
+ * TCP_MINMSS is defined to 256 which is fine for the smallest
+ * link MTU (296 bytes, SLIP interface) in the Internet.
+ * However it is very unlikely to come across such low MTU interfaces
+ * these days (anno dato 2003).
+ * Probably it can be set to 512 without ill effects. But we play safe.
+ * See tcp_subr.c tcp_minmss SYSCTL declaration for more comments.
+ * Setting this to "0" disables the minmss check.
+ */
+#define	TCP_MINMSS 256
+/*
+ * TCP_MINMSSOVERLOAD is defined to 1000 which should cover any type
+ * of interactive TCP session.
+ * See tcp_subr.c tcp_minmssoverload SYSCTL declaration and tcp_input.c
+ * for more comments.
+ * Setting this to "0" disables the minmssoverload check.
+ */
+#define TCP_MINMSSOVERLOAD 1000
+
+/* TCP MSS debugging */
+#define TCPDEBUG_MSS
 
 /*
  * Default maximum segment size for TCP6.
Index: netinet/tcp_input.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_input.c,v
retrieving revision 1.213
diff -u -p -r1.213 tcp_input.c
--- netinet/tcp_input.c	8 Nov 2003 22:59:22 -0000	1.213
+++ netinet/tcp_input.c	11 Nov 2003 18:18:20 -0000
@@ -134,6 +134,12 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, rfc3
     &tcp_do_rfc3390, 0,
     "Enable RFC 3390 (Increasing TCP's Initial Congestion Window)");
 
+#ifdef TCPDEBUG_MSS	/* set arbitrary MSS for outgoing SYN and SYN/ACK */
+int tcp_sendmss = 0;
+SYSCTL_INT(_net_inet_tcp, OID_AUTO, sendmss, CTLFLAG_RW,
+    &tcp_sendmss, 0, "MSS on outgoing connections");
+#endif
+
 struct inpcbhead tcb;
 #define	tcb6	tcb  /* for KAME src sync over BSD*'s */
 struct inpcbinfo tcbinfo;
@@ -154,9 +160,8 @@ static int	 tcp_timewait(struct tcptw *,
 #define ND6_HINT(tp) \
 do { \
 	if ((tp) && (tp)->t_inpcb && \
-	    ((tp)->t_inpcb->inp_vflag & INP_IPV6) != 0 && \
-	    (tp)->t_inpcb->in6p_route.ro_rt) \
-		nd6_nud_hint((tp)->t_inpcb->in6p_route.ro_rt, NULL, 0); \
+	    ((tp)->t_inpcb->inp_vflag & INP_IPV6) != 0) \
+		nd6_nud_hint(NULL, NULL, 0); \
 } while (0)
 #else
 #define ND6_HINT(tp)
@@ -358,8 +363,7 @@ tcp_input(m, off0)
 	int todrop, acked, ourfinisacked, needoutput = 0;
 	u_long tiwin;
 	struct tcpopt to;		/* options in this segment */
-	struct rmxp_tao *taop;		/* pointer to our TAO cache entry */
-	struct rmxp_tao	tao_noncached;	/* in case there's no cached entry */
+	struct rmxp_tao taop;		/* our TAO cache entry */
 	int headlocked = 0;
 	struct sockaddr_in *next_hop = NULL;
 	int rstreason; /* For badport_bandlim accounting purposes */
@@ -389,6 +393,7 @@ tcp_input(m, off0)
 #ifdef INET6
 	isipv6 = (mtod(m, struct ip *)->ip_v == 6) ? 1 : 0;
 #endif
+	bzero(&taop, sizeof(taop));
 	bzero((char *)&to, sizeof(to));
 
 	tcpstat.tcps_rcvtotal++;
@@ -707,11 +712,9 @@ findpcb:
 		if (isipv6) {
 			inc.inc6_faddr = ip6->ip6_src;
 			inc.inc6_laddr = ip6->ip6_dst;
-			inc.inc6_route.ro_rt = NULL;		/* XXX */
 		} else {
 			inc.inc_faddr = ip->ip_src;
 			inc.inc_laddr = ip->ip_dst;
-			inc.inc_route.ro_rt = NULL;		/* XXX */
 		}
 		inc.inc_fport = th->th_sport;
 		inc.inc_lport = th->th_dport;
@@ -916,11 +919,72 @@ findpcb:
 	}
 after_listen:
 
-/* XXX temp debugging */
+	/* XXX temp debugging */
 	/* should not happen - syncache should pick up these connections */
 	if (tp->t_state == TCPS_LISTEN)
 		panic("tcp_input: TCPS_LISTEN");
 
+#ifdef TCP_MINMSSOVERLOAD
+	/*
+	 * This is second part of the MSS DoS prevention code (after minmss
+	 * on the sending side) and it deals with too many too small tcp
+	 * packets in a too short timeframe (1 second).
+	 *
+	 * For every full second we count the number of received packets
+	 * and bytes. If we get a lot of packets per second for this connection
+	 * (tcp_minmssoverload) we take a closer look at it and compute the
+	 * average packet size for the past second. If that is less than
+	 * tcp_minmss we get too many packets with very small payload which
+	 * is not good and burdens our system (and every packet generates
+	 * a wakeup to the process connected to our socket). We can reasonable
+	 * expect this to be small packet DoS attack to exhaust our CPU
+	 * cycles.
+	 *
+	 * Care has to be taken for the minimum packet overload value. This
+	 * value defines the minimum number of packets per second before we
+	 * start to worry. This must not be too low to avoid killing for
+	 * example interactive connections with many small packets like
+	 * telnet or SSH.
+	 *
+	 * Setting either tcp_minmssoverload or tcp_minmss to "0" disables
+	 * this check.
+	 *
+	 * Account for packet if payload packet, skip over ACK, etc.
+	 */
+	if (tcp_minmss && tcp_minmssoverload &&
+	    tp->t_state == TCPS_ESTABLISHED && tlen > 0) {
+		if ((tp->t_rcvtime / hz) == (ticks / hz)) {
+			tp->rcv_pps++;
+			tp->rcv_byps += tlen;
+			if (tp->rcv_pps > tcp_minmssoverload) {
+				if ((tp->rcv_byps / tp->rcv_pps) < tcp_minmss) {
+					printf("too many small tcp packets from "
+					       "%s, %lupps, av. %lubyte/packet, "
+					       "dropping connection\n",
+#ifdef INET6
+						isipv6 ?
+						ip6_sprintf(&inp->inp_inc.inc6_faddr) :
+#endif
+						inet_ntoa(inp->inp_inc.inc_faddr),
+						tp->rcv_pps,
+						(tp->rcv_byps / tp->rcv_pps));
+					tp = tcp_drop(tp, ECONNRESET);
+					goto drop;
+				}
+			}
+		} else {
+#if 0
+			printf("tcp_input: previous interval %lu pps and %lu "
+			       "bytes, av. %lubyte/packet\n",
+				tp->rcv_pps, tp->rcv_byps,
+				(tp->rcv_byps / tp->rcv_pps));
+#endif
+			tp->rcv_pps = 1;
+			tp->rcv_byps = tlen;
+		}
+	}
+#endif
+
 	/*
 	 * Segment received on connection.
 	 * Reset idle time and keep-alive timer.
@@ -930,8 +994,9 @@ after_listen:
 		callout_reset(tp->tt_keep, tcp_keepidle, tcp_timer_keep, tp);
 
 	/*
-	 * Process options.
-	 * XXX this is tradtitional behavior, may need to be cleaned up.
+	 * Process options only when we get SYN/ACK back. The SYN case
+	 * for incoming connections is handled in tcp_syncache.
+	 * XXX this is traditional behavior, may need to be cleaned up.
 	 */
 	tcp_dooptions(&to, optp, optlen, thflags & TH_SYN);
 	if (thflags & TH_SYN) {
@@ -1179,10 +1244,8 @@ after_listen:
 	 *	continue processing rest of data/controls, beginning with URG
 	 */
 	case TCPS_SYN_SENT:
-		if ((taop = tcp_gettaocache(&inp->inp_inc)) == NULL) {
-			taop = &tao_noncached;
-			bzero(taop, sizeof(*taop));
-		}
+		if (tcp_do_rfc1644)
+			tcp_hc_gettao(&inp->inp_inc, &taop);
 
 		if ((thflags & TH_ACK) &&
 		    (SEQ_LEQ(th->th_ack, tp->iss) ||
@@ -1195,7 +1258,7 @@ after_listen:
 			 * Our new SYN, when it arrives, will serve as the
 			 * needed ACK.
 			 */
-			if (taop->tao_ccsent != 0)
+			if (taop.tao_ccsent != 0)
 				goto drop;
 			else {
 				rstreason = BANDLIM_UNLIMITED;
@@ -1225,7 +1288,7 @@ after_listen:
 			 */
 			if (to.to_flags & TOF_CCECHO) {
 				if (tp->cc_send != to.to_ccecho) {
-					if (taop->tao_ccsent != 0)
+					if (taop.tao_ccsent != 0)
 						goto drop;
 					else {
 						rstreason = BANDLIM_UNLIMITED;
@@ -1246,8 +1309,8 @@ after_listen:
 				tp->rcv_scale = tp->request_r_scale;
 			}
 			/* Segment is acceptable, update cache if undefined. */
-			if (taop->tao_ccsent == 0)
-				taop->tao_ccsent = to.to_ccecho;
+			if (taop.tao_ccsent == 0 && (tcp_do_rfc1644))
+				tcp_hc_updatetao(&inp->inp_inc, TCP_HC_TAO_CCSENT, to.to_ccecho, 0);
 
 			tp->rcv_adv += tp->rcv_wnd;
 			tp->snd_una++;		/* SYN is acked */
@@ -1290,14 +1353,15 @@ after_listen:
 			tp->t_flags |= TF_ACKNOW;
 			callout_stop(tp->tt_rexmt);
 			if (to.to_flags & TOF_CC) {
-				if (taop->tao_cc != 0 &&
-				    CC_GT(to.to_cc, taop->tao_cc)) {
+				if (taop.tao_cc != 0 &&
+				    CC_GT(to.to_cc, taop.tao_cc)) {
 					/*
 					 * update cache and make transition:
 					 *        SYN-SENT -> ESTABLISHED*
 					 *        SYN-SENT* -> FIN-WAIT-1*
 					 */
-					taop->tao_cc = to.to_cc;
+					taop.tao_cc = to.to_cc;
+					tcp_hc_updatetao(&inp->inp_inc, TCP_HC_TAO_CC, to.to_cc, 0);
 					tp->t_starttime = ticks;
 					if (tp->t_flags & TF_NEEDFIN) {
 						tp->t_state = TCPS_FIN_WAIT_1;
@@ -1313,8 +1377,11 @@ after_listen:
 				} else
 					tp->t_state = TCPS_SYN_RECEIVED;
 			} else {
-				/* CC.NEW or no option => invalidate cache */
-				taop->tao_cc = 0;
+				if (tcp_do_rfc1644) {
+					/* CC.NEW or no option => invalidate cache */
+					taop.tao_cc = 0;
+					tcp_hc_updatetao(&inp->inp_inc, TCP_HC_TAO_CC, to.to_cc, 0);
+				}
 				tp->t_state = TCPS_SYN_RECEIVED;
 			}
 		}
@@ -1644,7 +1711,7 @@ trimthenstep6:
 	if (thflags & TH_SYN) {
 		tp = tcp_drop(tp, ECONNRESET);
 		rstreason = BANDLIM_UNLIMITED;
-		goto dropwithreset;
+		goto drop;
 	}
 
 	/*
@@ -1682,13 +1749,13 @@ trimthenstep6:
 		}
 		/*
 		 * Upon successful completion of 3-way handshake,
-		 * update cache.CC if it was undefined, pass any queued
-		 * data to the user, and advance state appropriately.
+		 * update cache.CC, pass any queued data to the user,
+		 * and advance state appropriately.
 		 */
-		if ((taop = tcp_gettaocache(&inp->inp_inc)) != NULL &&
-		    taop->tao_cc == 0)
-			taop->tao_cc = tp->cc_recv;
-
+		if (tcp_do_rfc1644) {
+			taop.tao_cc = tp->cc_recv;
+			tcp_hc_updatetao(&inp->inp_inc, TCP_HC_TAO_CC, tp->cc_recv, 0);
+		}
 		/*
 		 * Make transitions:
 		 *      SYN-RECEIVED  -> ESTABLISHED
@@ -2611,25 +2678,26 @@ tcp_xmit_timer(tp, rtt)
  * are present.  Store the upper limit of the length of options plus
  * data in maxopd.
  *
- * NOTE that this routine is only called when we process an incoming
- * segment, for outgoing segments only tcp_mssopt is called.
  *
  * In case of T/TCP, we call this routine during implicit connection
  * setup as well (offer = -1), to initialize maxseg from the cached
  * MSS of our peer.
+ *
+ * NOTE that this routine is only called when we process an incoming
+ * segment. Outgoing SYN/ACK MSS settings are handled in tcp_mssopt().
  */
 void
 tcp_mss(tp, offer)
 	struct tcpcb *tp;
 	int offer;
 {
-	register struct rtentry *rt;
-	struct ifnet *ifp;
-	register int rtt, mss;
+	int rtt, mss;
 	u_long bufsize;
+	u_long maxmtu;
 	struct inpcb *inp = tp->t_inpcb;
 	struct socket *so;
-	struct rmxp_tao *taop;
+	struct hc_metrics_lite metrics;
+	struct rmxp_tao taop;
 	int origoffer = offer;
 #ifdef INET6
 	int isipv6 = ((inp->inp_vflag & INP_IPV6) != 0) ? 1 : 0;
@@ -2637,96 +2705,107 @@ tcp_mss(tp, offer)
 			    sizeof (struct ip6_hdr) + sizeof (struct tcphdr) :
 			    sizeof (struct tcpiphdr);
 #else
-	const int isipv6 = 0;
-	const size_t min_protoh = sizeof (struct tcpiphdr);
+	const size_t min_protoh = sizeof(struct tcpiphdr);
 #endif
+	bzero(&metrics, sizeof(metrics));
+	bzero(&taop, sizeof(taop));
 
-	if (isipv6)
-		rt = tcp_rtlookup6(&inp->inp_inc);
-	else
-		rt = tcp_rtlookup(&inp->inp_inc);
-	if (rt == NULL) {
-		tp->t_maxopd = tp->t_maxseg =
-				isipv6 ? tcp_v6mssdflt : tcp_mssdflt;
-		return;
+	/* initialize */
+#ifdef INET6
+	if (isipv6) {
+		maxmtu = tcp_maxmtu6(&inp->inp_inc);
+		tp->t_maxopd = tp->t_maxseg = tcp_v6mssdflt;
+	} else
+#endif
+	{
+		maxmtu = tcp_maxmtu(&inp->inp_inc);
+		tp->t_maxopd = tp->t_maxseg = tcp_mssdflt;
 	}
-	ifp = rt->rt_ifp;
 	so = inp->inp_socket;
 
-	taop = rmx_taop(rt->rt_rmx);
-	/*
-	 * Offer == -1 means that we didn't receive SYN yet,
-	 * use cached value in that case;
-	 */
-	if (offer == -1)
-		offer = taop->tao_mssopt;
 	/*
-	 * Offer == 0 means that there was no MSS on the SYN segment,
-	 * in this case we use tcp_mssdflt.
+	 * no route to sender, take default mss and return
 	 */
-	if (offer == 0)
-		offer = isipv6 ? tcp_v6mssdflt : tcp_mssdflt;
-	else
-		/*
-		 * Sanity check: make sure that maxopd will be large
-		 * enough to allow some data on segments even is the
-		 * all the option space is used (40bytes).  Otherwise
-		 * funny things may happen in tcp_output.
-		 */
-		offer = max(offer, 64);
-	taop->tao_mssopt = offer;
+	if (maxmtu == 0)
+		return;
+
+	/* what have we got? */
+	switch (offer) {
+		case 0:
+			/*
+			 * Offer == 0 means that there was no MSS on the SYN
+			 * segment, in this case we use tcp_mssdflt.
+			 */
+			offer =
+#ifdef INET6
+				isipv6 ? tcp_v6mssdflt :
+#endif
+				tcp_mssdflt;
+			break;
+
+		case -1:
+			/*
+			 * Offer == -1 means that we didn't receive SYN yet,
+			 * use cached value in that case;
+			 */
+			if (tcp_do_rfc1644)
+				tcp_hc_gettao(&inp->inp_inc, &taop);
+			if (taop.tao_mssopt != 0)
+				offer = taop.tao_mssopt;
+			/* FALLTHROUGH */
+
+		default:
+			/*
+			 * Prevent DoS attack with too small MSS. Round up
+			 * to at least minmss.
+			 */
+			offer = max(offer, tcp_minmss);
+			/*
+			 * Sanity check: make sure that maxopd will be large
+			 * enough to allow some data on segments even if the
+			 * all the option space is used (40bytes).  Otherwise
+			 * funny things may happen in tcp_output.
+			 */
+			offer = max(offer, 64);
+			if (tcp_do_rfc1644)
+				tcp_hc_updatetao(&inp->inp_inc, TCP_HC_TAO_MSSOPT,
+						  0, offer);
+	}
+
+#ifdef TCPDEBUG_MSS
+	if (tcp_sendmss)
+		offer = tcp_sendmss;
+#endif
 
 	/*
-	 * While we're here, check if there's an initial rtt
-	 * or rttvar.  Convert from the route-table units
-	 * to scaled multiples of the slow timeout timer.
+	 * rmx information is now retrieved from tcp_hostcache
 	 */
-	if (tp->t_srtt == 0 && (rtt = rt->rt_rmx.rmx_rtt)) {
-		/*
-		 * XXX the lock bit for RTT indicates that the value
-		 * is also a minimum value; this is subject to time.
-		 */
-		if (rt->rt_rmx.rmx_locks & RTV_RTT)
-			tp->t_rttmin = rtt / (RTM_RTTUNIT / hz);
-		tp->t_srtt = rtt / (RTM_RTTUNIT / (hz * TCP_RTT_SCALE));
-		tp->t_rttbest = tp->t_srtt + TCP_RTT_SCALE;
-		tcpstat.tcps_usedrtt++;
-		if (rt->rt_rmx.rmx_rttvar) {
-			tp->t_rttvar = rt->rt_rmx.rmx_rttvar /
-			    (RTM_RTTUNIT / (hz * TCP_RTTVAR_SCALE));
-			tcpstat.tcps_usedrttvar++;
-		} else {
-			/* default variation is +- 1 rtt */
-			tp->t_rttvar =
-			    tp->t_srtt * TCP_RTTVAR_SCALE / TCP_RTT_SCALE;
-		}
-		TCPT_RANGESET(tp->t_rxtcur,
-			      ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1,
-			      tp->t_rttmin, TCPTV_REXMTMAX);
-	}
+	tcp_hc_get(&inp->inp_inc, &metrics);
+
 	/*
-	 * if there's an mtu associated with the route, use it
+	 * if there's a discovered mtu int tcp hostcache, use it
 	 * else, use the link mtu.
 	 */
-	if (rt->rt_rmx.rmx_mtu)
-		mss = rt->rt_rmx.rmx_mtu - min_protoh;
+	if (metrics.rmx_mtu)
+		mss = metrics.rmx_mtu - min_protoh;
 	else {
 #ifdef INET6
-		mss = (isipv6 ? IN6_LINKMTU(rt->rt_ifp) : ifp->if_mtu)
-			- min_protoh;
-#else
-		mss = ifp->if_mtu - min_protoh;
-#endif
-#ifdef INET6
 		if (isipv6) {
-			if (!in6_localaddr(&inp->in6p_faddr))
+			mss = maxmtu - min_protoh;
+			if (!path_mtu_discovery &&
+			    !in6_localaddr(&inp->in6p_faddr))
 				mss = min(mss, tcp_v6mssdflt);
 		} else
 #endif
-			if (!in_localaddr(inp->inp_faddr))
+		{
+			mss = maxmtu - min_protoh;
+			if (!path_mtu_discovery &&
+			    !in_localaddr(inp->inp_faddr))
 				mss = min(mss, tcp_mssdflt);
+		}
 	}
 	mss = min(mss, offer);
+
 	/*
 	 * maxopd stores the maximum length of data AND options
 	 * in a segment; maxseg is the amount of data in a normal
@@ -2749,6 +2828,7 @@ tcp_mss(tp, offer)
 	    (origoffer == -1 ||
 	     (tp->t_flags & TF_RCVD_CC) == TF_RCVD_CC))
 		mss -= TCPOLEN_CC_APPA;
+	tp->t_maxseg = mss;
 
 #if	(MCLBYTES & (MCLBYTES - 1)) == 0
 		if (mss > MCLBYTES)
@@ -2757,15 +2837,18 @@ tcp_mss(tp, offer)
 		if (mss > MCLBYTES)
 			mss = mss / MCLBYTES * MCLBYTES;
 #endif
+	tp->t_maxseg = mss;
+
 	/*
-	 * If there's a pipesize, change the socket buffer
-	 * to that size.  Make the socket buffers an integral
-	 * number of mss units; if the mss is larger than
-	 * the socket buffer, decrease the mss.
+	 * If there's a pipesize, change the socket buffer to that size,
+	 * don't change if sb_hiwat is different than default (then it
+	 * has been changed on purpose with setsockopt).
+	 * Make the socket buffers an integral number of mss units;
+	 * if the mss is larger than the socket buffer, decrease the mss.
 	 */
-#ifdef RTV_SPIPE
-	if ((bufsize = rt->rt_rmx.rmx_sendpipe) == 0)
-#endif
+	if ((so->so_snd.sb_hiwat == tcp_sendspace) && metrics.rmx_sendpipe)
+		bufsize = metrics.rmx_sendpipe;
+	else
 		bufsize = so->so_snd.sb_hiwat;
 	if (bufsize < mss)
 		mss = bufsize;
@@ -2778,9 +2861,9 @@ tcp_mss(tp, offer)
 	}
 	tp->t_maxseg = mss;
 
-#ifdef RTV_RPIPE
-	if ((bufsize = rt->rt_rmx.rmx_recvpipe) == 0)
-#endif
+	if ((so->so_rcv.sb_hiwat == tcp_recvspace) && metrics.rmx_recvpipe)
+		bufsize = metrics.rmx_recvpipe;
+	else
 		bufsize = so->so_rcv.sb_hiwat;
 	if (bufsize > mss) {
 		bufsize = roundup(bufsize, mss);
@@ -2789,62 +2872,115 @@ tcp_mss(tp, offer)
 		if (bufsize > so->so_rcv.sb_hiwat)
 			(void)sbreserve(&so->so_rcv, bufsize, so, NULL);
 	}
+	/*
+	 * While we're here, check the others too
+	 */
+	if (tp->t_srtt == 0 && (rtt = metrics.rmx_rtt)) {
+		tp->t_srtt = rtt;
+		tp->t_rttbest = tp->t_srtt + TCP_RTT_SCALE;
+		tcpstat.tcps_usedrtt++;
+		if (metrics.rmx_rttvar) {
+			tp->t_rttvar = metrics.rmx_rttvar;
+			tcpstat.tcps_usedrttvar++;
+		} else {
+			/* default variation is +- 1 rtt */
+			tp->t_rttvar =
+			    tp->t_srtt * TCP_RTTVAR_SCALE / TCP_RTT_SCALE;
+		}
+		TCPT_RANGESET(tp->t_rxtcur,
+			      ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1,
+			      tp->t_rttmin, TCPTV_REXMTMAX);
+	}
+	if (metrics.rmx_ssthresh) {
+		/*
+		 * There's some sort of gateway or interface
+		 * buffer limit on the path.  Use this to set
+		 * the slow start threshhold, but set the
+		 * threshold to no less than 2*mss.
+		 */
+		tp->snd_ssthresh = max(2 * mss, metrics.rmx_ssthresh);
+		tcpstat.tcps_usedssthresh++;
+	}
+	if (metrics.rmx_bandwidth)
+		tp->snd_bandwidth = metrics.rmx_bandwidth;
 
 	/*
 	 * Set the slow-start flight size depending on whether this
 	 * is a local network or not.
+	 *
+	 * Extend this so we cache the cwnd too and retrieve it here.
+	 * Make cwnd even bigger than RFC3390 suggests but only if we
+	 * have previous experience with the remote host. Be careful
+	 * not make cwnd bigger than remote receive window or our own
+	 * send socket buffer. Maybe put some additional upper bound
+	 * on the retrieved cwnd. Should do incremental updates to
+	 * hostcache when cwnd collapses so next connection doesn't
+	 * overloads the path again.
+	 *
+	 * RFC3390 says only do this if SYN or SYN/ACK didn't got lost.
+	 * We currently check only in syncache_socket for that.
 	 */
+#define TCP_METRICS_CWND
+#ifdef TCP_METRICS_CWND
+	if (metrics.rmx_cwnd)
+		tp->snd_cwnd = max(mss,
+				min(metrics.rmx_cwnd / 2,
+				 min(tp->snd_wnd, so->so_snd.sb_hiwat)));
+	else
+#endif
 	if (tcp_do_rfc3390)
 		tp->snd_cwnd = min(4 * mss, max(2 * mss, 4380));
+#ifdef INET6
 	else if ((isipv6 && in6_localaddr(&inp->in6p_faddr)) ||
 	    (!isipv6 && in_localaddr(inp->inp_faddr)))
 		tp->snd_cwnd = mss * ss_fltsz_local;
+#endif
 	else
 		tp->snd_cwnd = mss * ss_fltsz;
-
-	if (rt->rt_rmx.rmx_ssthresh) {
-		/*
-		 * There's some sort of gateway or interface
-		 * buffer limit on the path.  Use this to set
-		 * the slow start threshhold, but set the
-		 * threshold to no less than 2*mss.
-		 */
-		tp->snd_ssthresh = max(2 * mss, rt->rt_rmx.rmx_ssthresh);
-		tcpstat.tcps_usedssthresh++;
-	}
 }
 
 /*
  * Determine the MSS option to send on an outgoing SYN.
  */
 int
-tcp_mssopt(tp)
-	struct tcpcb *tp;
+tcp_mssopt(inc)
+	struct in_conninfo *inc;
 {
-	struct rtentry *rt;
+	int mss = 0;
+	u_long maxmtu = 0;
+	u_long thcmtu = 0;
+	size_t min_protoh;
 #ifdef INET6
-	int isipv6 = ((tp->t_inpcb->inp_vflag & INP_IPV6) != 0) ? 1 : 0;
-	size_t min_protoh = isipv6 ?
-			    sizeof (struct ip6_hdr) + sizeof (struct tcphdr) :
-			    sizeof (struct tcpiphdr);
-#else
-	const int isipv6 = 0;
-	const size_t min_protoh = sizeof (struct tcpiphdr);
+	int isipv6 = inc->inc_isipv6 ? 1 : 0;
 #endif
 
-	if (isipv6)
-		rt = tcp_rtlookup6(&tp->t_inpcb->inp_inc);
-	else
-		rt = tcp_rtlookup(&tp->t_inpcb->inp_inc);
-	if (rt == NULL)
-		return (isipv6 ? tcp_v6mssdflt : tcp_mssdflt);
+	KASSERT(inc != NULL, ("tcp_mssopt with NULL in_conninfo pointer"));
 
 #ifdef INET6
-	return (isipv6 ? IN6_LINKMTU(rt->rt_ifp) :
-		rt->rt_ifp->if_mtu - min_protoh);
-#else
-	return (rt->rt_ifp->if_mtu - min_protoh);
+	if (isipv6) {
+		mss = tcp_v6mssdflt;
+		maxmtu = tcp_maxmtu6(inc);
+		thcmtu = tcp_hc_getmtu(inc); /* IPv4 and IPv6 */
+		min_protoh = sizeof(struct ip6_hdr) + sizeof(struct tcphdr);
+	} else
 #endif
+	{
+		mss = tcp_mssdflt;
+		maxmtu = tcp_maxmtu(inc);
+		thcmtu = tcp_hc_getmtu(inc); /* IPv4 and IPv6 */
+		min_protoh = sizeof(struct tcpiphdr);
+	}
+	if (maxmtu && thcmtu)
+		mss = min(maxmtu, thcmtu) - min_protoh;
+	else if (maxmtu || thcmtu)
+		mss = max(maxmtu, thcmtu) - min_protoh;
+
+#ifdef TCPDEBUG_MSS
+	if (tcp_sendmss)
+		mss = tcp_sendmss;
+#endif
+
+	return (mss);
 }
 
 
Index: netinet/tcp_output.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_output.c,v
retrieving revision 1.82
diff -u -p -r1.82 tcp_output.c
--- netinet/tcp_output.c	8 Nov 2003 22:55:52 -0000	1.82
+++ netinet/tcp_output.c	11 Nov 2003 18:18:20 -0000
@@ -125,11 +125,12 @@ tcp_output(struct tcpcb *tp)
 #if 0
 	int maxburst = TCP_MAXBURST;
 #endif
-	struct rmxp_tao *taop;
+	struct rmxp_tao taop;
 #ifdef INET6
 	struct ip6_hdr *ip6 = NULL;
 	int isipv6;
 
+	bzero(&taop, sizeof(taop));
 	isipv6 = (tp->t_inpcb->inp_vflag & INP_IPV6) != 0;
 #endif
 
@@ -232,7 +233,6 @@ again:
 	 */
 	len = (long)ulmin(so->so_snd.sb_cc, win) - off;
 
-	taop = tcp_gettaocache(&tp->t_inpcb->inp_inc);
 
 	/*
 	 * Lop off SYN bit if it has already been sent.  However, if this
@@ -242,8 +242,10 @@ again:
 	if ((flags & TH_SYN) && SEQ_GT(tp->snd_nxt, tp->snd_una)) {
 		flags &= ~TH_SYN;
 		off--, len++;
+		if (tcp_do_rfc1644)
+			tcp_hc_gettao(&tp->t_inpcb->inp_inc, &taop);
 		if (len > 0 && tp->t_state == TCPS_SYN_SENT &&
-		    (taop == NULL || taop->tao_ccsent == 0))
+		     taop.tao_ccsent == 0)
 			return 0;
 	}
 
@@ -429,7 +431,7 @@ send:
 
 			opt[0] = TCPOPT_MAXSEG;
 			opt[1] = TCPOLEN_MAXSEG;
-			mss = htons((u_short) tcp_mssopt(tp));
+			mss = htons((u_short) tcp_mssopt(&tp->t_inpcb->inp_inc));
 			(void)memcpy(opt + 2, &mss, sizeof(mss));
 			optlen = TCPOLEN_MAXSEG;
 
@@ -872,10 +874,7 @@ send:
 		 * Also, desired default hop limit might be changed via
 		 * Neighbor Discovery.
 		 */
-		ip6->ip6_hlim = in6_selecthlim(tp->t_inpcb,
-					       tp->t_inpcb->in6p_route.ro_rt ?
-					       tp->t_inpcb->in6p_route.ro_rt->rt_ifp
-					       : NULL);
+		ip6->ip6_hlim = in6_selecthlim(tp->t_inpcb, NULL);
 
 		/* TODO: IPv6 IP6TOS_ECT bit on */
 #if defined(IPSEC) && !defined(FAST_IPSEC)
@@ -886,36 +885,27 @@ send:
 		}
 #endif /*IPSEC*/
 		error = ip6_output(m,
-			    tp->t_inpcb->in6p_outputopts,
-			    &tp->t_inpcb->in6p_route,
+			    tp->t_inpcb->in6p_outputopts, NULL,
 			    (so->so_options & SO_DONTROUTE), NULL, NULL,
 			    tp->t_inpcb);
 	} else
 #endif /* INET6 */
     {
-	struct rtentry *rt;
 	ip->ip_len = m->m_pkthdr.len;
 #ifdef INET6
  	if (INP_CHECK_SOCKAF(so, AF_INET6))
- 		ip->ip_ttl = in6_selecthlim(tp->t_inpcb,
- 					    tp->t_inpcb->in6p_route.ro_rt ?
- 					    tp->t_inpcb->in6p_route.ro_rt->rt_ifp
- 					    : NULL);
+ 		ip->ip_ttl = in6_selecthlim(tp->t_inpcb, NULL);
 #endif /* INET6 */
 	/*
-	 * See if we should do MTU discovery.  We do it only if the following
-	 * are true:
-	 *	1) we have a valid route to the destination
-	 *	2) the MTU is not locked (if it is, then discovery has been
-	 *	   disabled)
-	 */
-	if (path_mtu_discovery
-	    && (rt = tp->t_inpcb->inp_route.ro_rt)
-	    && rt->rt_flags & RTF_UP
-	    && !(rt->rt_rmx.rmx_locks & RTV_MTU)) {
+	 * If we do path MTU discovery, then we set DF on every packet.
+	 * This might not be the best thing to do according to RFC3390
+	 * Section 2. However the tcp hostcache migitates the problem
+	 * so it affects only the first tcp connection with a host.
+	 */
+	if (path_mtu_discovery)
 		ip->ip_off |= IP_DF;
-	}
-	error = ip_output(m, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
+
+	error = ip_output(m, tp->t_inpcb->inp_options, NULL,
 	    (so->so_options & SO_DONTROUTE), 0, tp->t_inpcb);
     }
 	if (error) {
Index: netinet/tcp_subr.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_subr.c,v
retrieving revision 1.167
diff -u -p -r1.167 tcp_subr.c
--- netinet/tcp_subr.c	8 Nov 2003 22:59:22 -0000	1.167
+++ netinet/tcp_subr.c	11 Nov 2003 18:18:20 -0000
@@ -76,6 +76,7 @@
 #include <netinet/ip_var.h>
 #ifdef INET6
 #include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
 #endif
 #include <netinet/tcp.h>
 #include <netinet/tcp_fsm.h>
@@ -120,6 +121,29 @@ SYSCTL_INT(_net_inet_tcp, TCPCTL_V6MSSDF
 	"Default TCP Maximum Segment Size for IPv6");
 #endif
 
+/*
+ * Minimum MSS we accept and use. This prevents DoS attacks where
+ * we are forced to a ridiculous low MSS like 20 and send hundrets
+ * of packets instead of one. The effect scales with the available
+ * bandwidth and quickly saturates the CPU and network interface
+ * with packet generation and sending. Set to zero to disable MINMSS
+ * checking. This setting prevents us from sending too small packets.
+ */
+int	tcp_minmss = TCP_MINMSS;
+SYSCTL_INT(_net_inet_tcp, TCPCTL_MINMSS, minmss, CTLFLAG_RW,
+    &tcp_minmss , 0, "Minmum TCP Maximum Segment Size");
+/*
+ * Number of TCP segments per second we accept from remote host
+ * before we start to calculate average segement size. If average
+ * segement size drops below the minimum TCP MSS we assume a DoS
+ * attack and reset+drop the connection. Care has to be taken not
+ * set this value too small to not kill interactive type connections
+ * (telnet, SSH) which send many small packets.
+ */
+int     tcp_minmssoverload = TCP_MINMSSOVERLOAD;
+SYSCTL_INT(_net_inet_tcp, TCPCTL_MINMSSOVERLOAD, minmssoverload, CTLFLAG_RW,
+    &tcp_minmssoverload , 0, "Number of TCP Segments per Second allowed to be under the MINMSS Size");
+
 #if 0
 static int 	tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ;
 SYSCTL_INT(_net_inet_tcp, TCPCTL_RTTDFLT, rttdflt, CTLFLAG_RW, 
@@ -177,7 +201,6 @@ static int	tcp_inflight_stab = 20;
 SYSCTL_INT(_net_inet_tcp, OID_AUTO, inflight_stab, CTLFLAG_RW,
     &tcp_inflight_stab, 0, "Inflight Algorithm Stabilization 20 = 2 packets");
 
-static void	tcp_cleartaocache(void);
 static struct inpcb *tcp_notify(struct inpcb *, int);
 static void	tcp_discardcb(struct tcpcb *);
 
@@ -215,7 +238,6 @@ tcp_init()
 	int hashsize = TCBHASHSIZE;
 	
 	tcp_ccgen = 1;
-	tcp_cleartaocache();
 
 	tcp_delacktime = TCPTV_DELACK;
 	tcp_keepinit = TCPTV_KEEP_INIT;
@@ -262,6 +284,7 @@ tcp_init()
 	uma_zone_set_max(tcptw_zone, maxsockets / 5);
 	tcp_timer_init();
 	syncache_init();
+	tcp_hc_init();
 }
 
 /*
@@ -367,18 +390,14 @@ tcp_respond(tp, ipgen, th, m, ack, seq, 
 {
 	register int tlen;
 	int win = 0;
-	struct route *ro = 0;
-	struct route sro;
 	struct ip *ip;
 	struct tcphdr *nth;
 #ifdef INET6
-	struct route_in6 *ro6 = 0;
-	struct route_in6 sro6;
 	struct ip6_hdr *ip6;
 	int isipv6;
 #endif /* INET6 */
 	int ipflags = 0;
-	struct inpcb *inp;
+	struct inpcb *inp = NULL;
 
 	KASSERT(tp != NULL || m != NULL, ("tcp_respond: tp and m both NULL"));
 
@@ -398,24 +417,6 @@ tcp_respond(tp, ipgen, th, m, ack, seq, 
 			if (win > (long)TCP_MAXWIN << tp->rcv_scale)
 				win = (long)TCP_MAXWIN << tp->rcv_scale;
 		}
-#ifdef INET6
-		if (isipv6)
-			ro6 = &inp->in6p_route;
-		else
-#endif /* INET6 */
-		ro = &inp->inp_route;
-	} else {
-		inp = NULL;
-#ifdef INET6
-		if (isipv6) {
-			ro6 = &sro6;
-			bzero(ro6, sizeof *ro6);
-		} else
-#endif /* INET6 */
-	      {
-		ro = &sro;
-		bzero(ro, sizeof *ro);
-	      }
 	}
 	if (m == 0) {
 		m = m_gethdr(M_DONTWAIT, MT_HEADER);
@@ -516,10 +517,7 @@ tcp_respond(tp, ipgen, th, m, ack, seq, 
 		nth->th_sum = in6_cksum(m, IPPROTO_TCP,
 					sizeof(struct ip6_hdr),
 					tlen - sizeof(struct ip6_hdr));
-		ip6->ip6_hlim = in6_selecthlim(inp,
-					       ro6 && ro6->ro_rt ?
-					       ro6->ro_rt->rt_ifp :
-					       NULL);
+		ip6->ip6_hlim = in6_selecthlim(tp ? tp->t_inpcb : NULL, NULL);
 	} else
 #endif /* INET6 */
       {
@@ -533,21 +531,11 @@ tcp_respond(tp, ipgen, th, m, ack, seq, 
 		tcp_trace(TA_OUTPUT, 0, tp, mtod(m, void *), th, 0);
 #endif
 #ifdef INET6
-	if (isipv6) {
-		(void) ip6_output(m, NULL, ro6, ipflags, NULL, NULL, inp);
-		if (ro6 == &sro6 && ro6->ro_rt) {
-			RTFREE(ro6->ro_rt);
-			ro6->ro_rt = NULL;
-		}
-	} else
+	if (isipv6)
+		(void) ip6_output(m, NULL, NULL, ipflags, NULL, NULL, inp);
+	else
 #endif /* INET6 */
-	{
-		(void) ip_output(m, NULL, ro, ipflags, NULL, inp);
-		if (ro == &sro && ro->ro_rt) {
-			RTFREE(ro->ro_rt);
-			ro->ro_rt = NULL;
-		}
-	}
+	(void) ip_output(m, NULL, NULL, ipflags, NULL, inp);
 }
 
 /*
@@ -647,8 +635,6 @@ tcp_discardcb(tp)
 #ifdef INET6
 	int isipv6 = (inp->inp_vflag & INP_IPV6) != 0;
 #endif /* INET6 */
-	struct rtentry *rt;
-	int dosavessthresh;
 
 	/*
 	 * Make sure that all of our timers are stopped before we
@@ -663,89 +649,34 @@ tcp_discardcb(tp)
 	/*
 	 * If we got enough samples through the srtt filter,
 	 * save the rtt and rttvar in the routing entry.
-	 * 'Enough' is arbitrarily defined as the 16 samples.
-	 * 16 samples is enough for the srtt filter to converge
-	 * to within 5% of the correct value; fewer samples and
-	 * we could save a very bogus rtt.
-	 *
-	 * Don't update the default route's characteristics and don't
-	 * update anything that the user "locked".
+	 * 'Enough' is arbitrarily defined as 4 rtt samples.
+	 * 4 samples is enough for the srtt filter to converge
+	 * to within enough % of the correct value; fewer samples
+	 * and we could save a bogus rtt. The danger is not high
+	 * as tcp quickly recovers from everything.
+	 * XXX: Works very well but needs some more statistics!
 	 */
-	if (tp->t_rttupdated >= 16) {
-		register u_long i = 0;
-#ifdef INET6
-		if (isipv6) {
-			struct sockaddr_in6 *sin6;
+	if (tp->t_rttupdated >= 4) {
+		struct hc_metrics_lite metrics;
+		u_long ssthresh;
 
-			if ((rt = inp->in6p_route.ro_rt) == NULL)
-				goto no_valid_rt;
-			sin6 = (struct sockaddr_in6 *)rt_key(rt);
-			if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
-				goto no_valid_rt;
-		}
-		else
-#endif /* INET6 */		
-		if ((rt = inp->inp_route.ro_rt) == NULL ||
-		    ((struct sockaddr_in *)rt_key(rt))->sin_addr.s_addr
-		    == INADDR_ANY)
-			goto no_valid_rt;
-
-		if ((rt->rt_rmx.rmx_locks & RTV_RTT) == 0) {
-			i = tp->t_srtt *
-			    (RTM_RTTUNIT / (hz * TCP_RTT_SCALE));
-			if (rt->rt_rmx.rmx_rtt && i)
-				/*
-				 * filter this update to half the old & half
-				 * the new values, converting scale.
-				 * See route.h and tcp_var.h for a
-				 * description of the scaling constants.
-				 */
-				rt->rt_rmx.rmx_rtt =
-				    (rt->rt_rmx.rmx_rtt + i) / 2;
-			else
-				rt->rt_rmx.rmx_rtt = i;
-			tcpstat.tcps_cachedrtt++;
-		}
-		if ((rt->rt_rmx.rmx_locks & RTV_RTTVAR) == 0) {
-			i = tp->t_rttvar *
-			    (RTM_RTTUNIT / (hz * TCP_RTTVAR_SCALE));
-			if (rt->rt_rmx.rmx_rttvar && i)
-				rt->rt_rmx.rmx_rttvar =
-				    (rt->rt_rmx.rmx_rttvar + i) / 2;
-			else
-				rt->rt_rmx.rmx_rttvar = i;
-			tcpstat.tcps_cachedrttvar++;
-		}
+		bzero(&metrics, sizeof(metrics));
 		/*
-		 * The old comment here said:
-		 * update the pipelimit (ssthresh) if it has been updated
-		 * already or if a pipesize was specified & the threshhold
-		 * got below half the pipesize.  I.e., wait for bad news
-		 * before we start updating, then update on both good
-		 * and bad news.
-		 *
-		 * But we want to save the ssthresh even if no pipesize is
-		 * specified explicitly in the route, because such
-		 * connections still have an implicit pipesize specified
-		 * by the global tcp_sendspace.  In the absence of a reliable
-		 * way to calculate the pipesize, it will have to do.
+		 * Update the ssthresh always when the conditions below
+		 * are satisfied. This gives us better new start value
+		 * for the congestion avoidance for new connections.
+		 * ssthresh is only set if packet loss occured on a session.
 		 */
-		i = tp->snd_ssthresh;
-		if (rt->rt_rmx.rmx_sendpipe != 0)
-			dosavessthresh = (i < rt->rt_rmx.rmx_sendpipe / 2);
-		else
-			dosavessthresh = (i < so->so_snd.sb_hiwat / 2);
-		if (((rt->rt_rmx.rmx_locks & RTV_SSTHRESH) == 0 &&
-		     i != 0 && rt->rt_rmx.rmx_ssthresh != 0)
-		    || dosavessthresh) {
+		ssthresh = tp->snd_ssthresh;
+		if (ssthresh != 0 && ssthresh < so->so_snd.sb_hiwat / 2) {
 			/*
 			 * convert the limit from user data bytes to
 			 * packets then to packet data bytes.
 			 */
-			i = (i + tp->t_maxseg / 2) / tp->t_maxseg;
-			if (i < 2)
-				i = 2;
-			i *= (u_long)(tp->t_maxseg +
+			ssthresh = (ssthresh + tp->t_maxseg / 2) / tp->t_maxseg;
+			if (ssthresh < 2)
+				ssthresh = 2;
+			ssthresh *= (u_long)(tp->t_maxseg +
 #ifdef INET6
 				      (isipv6 ? sizeof (struct ip6_hdr) +
 					       sizeof (struct tcphdr) :
@@ -755,15 +686,21 @@ tcp_discardcb(tp)
 				       )
 #endif
 				      );
-			if (rt->rt_rmx.rmx_ssthresh)
-				rt->rt_rmx.rmx_ssthresh =
-				    (rt->rt_rmx.rmx_ssthresh + i) / 2;
-			else
-				rt->rt_rmx.rmx_ssthresh = i;
-			tcpstat.tcps_cachedssthresh++;
-		}
+		} else
+			ssthresh = 0;
+		metrics.rmx_ssthresh = ssthresh;
+
+		metrics.rmx_rtt = tp->t_srtt;
+		metrics.rmx_rttvar = tp->t_rttvar;
+		/* XXX: This wraps if the pipe is more than 4 Gbit per second */
+		metrics.rmx_bandwidth = tp->snd_bandwidth;
+		metrics.rmx_cwnd = tp->snd_cwnd;
+		metrics.rmx_sendpipe = 0; 
+		metrics.rmx_recvpipe = 0;
+
+		tcp_hc_update(&inp->inp_inc, &metrics);
 	}
-    no_valid_rt:
+
 	/* free the reassembly queue, if any */
 	while ((q = LIST_FIRST(&tp->t_segq)) != NULL) {
 		LIST_REMOVE(q, tqe_q);
@@ -1138,10 +1075,17 @@ tcp_ctlinput(cmd, sa, vip)
 		notify = tcp_drop_syn_sent;
 	else if (cmd == PRC_MSGSIZE)
 		notify = tcp_mtudisc;
-	else if (PRC_IS_REDIRECT(cmd)) {
-		ip = 0;
-		notify = in_rtchange;
-	} else if (cmd == PRC_HOSTDEAD)
+	/*
+	 * Redirects don't need to be handled up here.
+	 */
+	else if (PRC_IS_REDIRECT(cmd))
+		return;
+	/*
+	 * Hostdead is ugly because it goes linearly through all PCBs.
+	 * XXX: We never get this from ICMP, otherwise it makes an
+	 * excellent DoS attack on machines with many connections.
+	 */
+	else if (cmd == PRC_HOSTDEAD)
 		ip = 0;
 	else if ((unsigned)cmd >= PRC_NCMDS || inetctlerrmap[cmd] == 0)
 		return;
@@ -1379,23 +1323,28 @@ tcp_mtudisc(inp, errno)
 	int errno;
 {
 	struct tcpcb *tp = intotcpcb(inp);
-	struct rtentry *rt;
-	struct rmxp_tao *taop;
+	struct rmxp_tao taop;
 	struct socket *so = inp->inp_socket;
-	int offered;
+	u_int maxmtu;
+	u_int romtu;
 	int mss;
 #ifdef INET6
 	int isipv6 = (tp->t_inpcb->inp_vflag & INP_IPV6) != 0;
 #endif /* INET6 */
+	bzero(&taop, sizeof(taop));
 
 	if (tp) {
+		maxmtu = tcp_hc_getmtu(&inp->inp_inc); /* IPv4 and IPv6 */
+		romtu =
 #ifdef INET6
-		if (isipv6)
-			rt = tcp_rtlookup6(&inp->inp_inc);
-		else
+		    isipv6 ? tcp_maxmtu6(&inp->inp_inc) :
 #endif /* INET6 */
-		rt = tcp_rtlookup(&inp->inp_inc);
-		if (!rt || !rt->rt_rmx.rmx_mtu) {
+		    tcp_maxmtu(&inp->inp_inc);
+		if (!maxmtu)
+			maxmtu = romtu;
+		else
+			maxmtu = min(maxmtu, romtu);
+		if (!maxmtu) {
 			tp->t_maxopd = tp->t_maxseg =
 #ifdef INET6
 				isipv6 ? tcp_v6mssdflt :
@@ -1403,9 +1352,7 @@ tcp_mtudisc(inp, errno)
 				tcp_mssdflt;
 			return inp;
 		}
-		taop = rmx_taop(rt->rt_rmx);
-		offered = taop->tao_mssopt;
-		mss = rt->rt_rmx.rmx_mtu -
+		mss = maxmtu -
 #ifdef INET6
 			(isipv6 ?
 			 sizeof(struct ip6_hdr) + sizeof(struct tcphdr) :
@@ -1416,8 +1363,11 @@ tcp_mtudisc(inp, errno)
 #endif /* INET6 */
 			;
 
-		if (offered)
-			mss = min(mss, offered);
+		if (tcp_do_rfc1644) {
+			tcp_hc_gettao(&inp->inp_inc, &taop);
+			if (taop.tao_mssopt)
+				mss = min(mss, taop.tao_mssopt);
+		}
 		/*
 		 * XXX - The above conditional probably violates the TCP
 		 * spec.  The problem is that, since we don't know the
@@ -1471,50 +1421,68 @@ tcp_mtudisc(inp, errno)
  * is called by TCP routines that access the rmx structure and by tcp_mss
  * to get the interface MTU.
  */
-struct rtentry *
-tcp_rtlookup(inc)
+u_long 
+tcp_maxmtu(inc)
 	struct in_conninfo *inc;
 {
-	struct route *ro;
+	struct route ro;
 	struct rtentry *rt;
-
-	ro = &inc->inc_route;
-	rt = ro->ro_rt;
-	if (rt == NULL || !(rt->rt_flags & RTF_UP)) {
-		/* No route yet, so try to acquire one */
-		if (inc->inc_faddr.s_addr != INADDR_ANY) {
-			ro->ro_dst.sa_family = AF_INET;
-			ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
-			((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
-			    inc->inc_faddr;
-			rtalloc(ro);
-			rt = ro->ro_rt;
-		}
+	struct sockaddr_in *dst;
+	struct ifnet *ifp;
+	u_long maxmtu = 0;
+
+	KASSERT(inc != NULL, ("tcp_maxmtu with NULL in_conninfo pointer"));
+
+	bzero(&ro, sizeof(ro));
+	if (inc->inc_faddr.s_addr != INADDR_ANY) {
+	        dst = (struct sockaddr_in *)&ro.ro_dst;
+		dst->sin_family = AF_INET;
+		dst->sin_len = sizeof(*dst);
+		dst->sin_addr = inc->inc_faddr;
+		rtalloc(&ro);
+	}
+	if (ro.ro_rt != NULL) {
+		rt = ro.ro_rt;
+		ifp = rt->rt_ifp;
+		if (rt->rt_rmx.rmx_mtu == 0)
+			maxmtu = ifp->if_mtu;
+		else
+			maxmtu = min(rt->rt_rmx.rmx_mtu, ifp->if_mtu);
+		RTFREE(rt);
 	}
-	return rt;
+	return (maxmtu);
 }
 
 #ifdef INET6
-struct rtentry *
-tcp_rtlookup6(inc)
+u_long
+tcp_maxmtu6(inc)
 	struct in_conninfo *inc;
 {
-	struct route_in6 *ro6;
+	struct route_in6 ro6;
 	struct rtentry *rt;
+	struct ifnet *ifp;
+	u_long maxmtu = 0;
 
-	ro6 = &inc->inc6_route;
-	rt = ro6->ro_rt;
-	if (rt == NULL || !(rt->rt_flags & RTF_UP)) {
-		/* No route yet, so try to acquire one */
-		if (!IN6_IS_ADDR_UNSPECIFIED(&inc->inc6_faddr)) {
-			ro6->ro_dst.sin6_family = AF_INET6;
-			ro6->ro_dst.sin6_len = sizeof(struct sockaddr_in6);
-			ro6->ro_dst.sin6_addr = inc->inc6_faddr;
-			rtalloc((struct route *)ro6);
-			rt = ro6->ro_rt;
-		}
+	KASSERT(inc != NULL, ("tcp_maxmtu6 with NULL in_conninfo pointer"));
+
+	bzero(&ro6, sizeof(ro6));
+	if (!IN6_IS_ADDR_UNSPECIFIED(&inc->inc6_faddr)) {
+		ro6.ro_dst.sin6_family = AF_INET6;
+		ro6.ro_dst.sin6_len = sizeof(struct sockaddr_in6);
+		ro6.ro_dst.sin6_addr = inc->inc6_faddr;
+		rtalloc((struct route *)&ro6);
+	}
+	if (ro6.ro_rt != NULL) {
+		rt = ro6.ro_rt;
+		ifp = rt->rt_ifp;
+		if (rt->rt_rmx.rmx_mtu == 0)
+			maxmtu = IN6_LINKMTU(rt->rt_ifp);
+		else
+			maxmtu = min(rt->rt_rmx.rmx_mtu, IN6_LINKMTU(rt->rt_ifp));
+		RTFREE(rt);
 	}
-	return rt;
+
+	return (maxmtu);
 }
 #endif /* INET6 */
 
@@ -1563,45 +1531,6 @@ ipsec_hdrsiz_tcp(tp)
 #endif /*IPSEC*/
 
 /*
- * Return a pointer to the cached information about the remote host.
- * The cached information is stored in the protocol specific part of
- * the route metrics.
- */
-struct rmxp_tao *
-tcp_gettaocache(inc)
-	struct in_conninfo *inc;
-{
-	struct rtentry *rt;
-
-#ifdef INET6
-	if (inc->inc_isipv6)
-		rt = tcp_rtlookup6(inc);
-	else
-#endif /* INET6 */
-	rt = tcp_rtlookup(inc);
-
-	/* Make sure this is a host route and is up. */
-	if (rt == NULL ||
-	    (rt->rt_flags & (RTF_UP|RTF_HOST)) != (RTF_UP|RTF_HOST))
-		return NULL;
-
-	return rmx_taop(rt->rt_rmx);
-}
-
-/*
- * Clear all the TAO cache entries, called from tcp_init.
- *
- * XXX
- * This routine is just an empty one, because we assume that the routing
- * routing tables are initialized at the same time when TCP, so there is
- * nothing in the cache left over.
- */
-static void
-tcp_cleartaocache()
-{
-}
-
-/*
  * Move a TCP connection into TIME_WAIT state.
  *    tcbinfo is unlocked.
  *    inp is locked, and is unlocked before returning.
@@ -1822,9 +1751,8 @@ tcp_twrespond(struct tcptw *tw, struct s
 	if (isipv6) {
 		th->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(struct ip6_hdr),
 		    sizeof(struct tcphdr) + optlen);
-		ip6->ip6_hlim = in6_selecthlim(inp, inp->in6p_route.ro_rt ?
-		    inp->in6p_route.ro_rt->rt_ifp : NULL);
-		error = ip6_output(m, inp->in6p_outputopts, &inp->in6p_route,
+		ip6->ip6_hlim = in6_selecthlim(inp, NULL);
+		error = ip6_output(m, inp->in6p_outputopts, NULL,
 		    (tw->tw_so_options & SO_DONTROUTE), NULL, NULL, inp);
 	} else
 #endif
@@ -1834,7 +1762,7 @@ tcp_twrespond(struct tcptw *tw, struct s
 		m->m_pkthdr.csum_flags = CSUM_TCP;
 		m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
 		ip->ip_len = m->m_pkthdr.len;
-		error = ip_output(m, inp->inp_options, &inp->inp_route,
+		error = ip_output(m, inp->inp_options, NULL,
 		    (tw->tw_so_options & SO_DONTROUTE), NULL, inp);
 	}
 	if (flags & TH_ACK)
Index: netinet/tcp_syncache.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_syncache.c,v
retrieving revision 1.46
diff -u -p -r1.46 tcp_syncache.c
--- netinet/tcp_syncache.c	10 Nov 2003 23:29:33 -0000	1.46
+++ netinet/tcp_syncache.c	11 Nov 2003 18:18:20 -0000
@@ -1,4 +1,5 @@
-/*-
+
+/*
  * Copyright (c) 2001 Networks Associates Technology, Inc.
  * All rights reserved.
  *
@@ -202,29 +203,9 @@ static MALLOC_DEFINE(M_SYNCACHE, "syncac
 static void
 syncache_free(struct syncache *sc)
 {
-	struct rtentry *rt;
-
 	if (sc->sc_ipopts)
 		(void) m_free(sc->sc_ipopts);
-#ifdef INET6
-	if (sc->sc_inc.inc_isipv6)
-		rt = sc->sc_route6.ro_rt;
-	else
-#endif
-		rt = sc->sc_route.ro_rt;
-	if (rt != NULL) {
-		/*
-		 * If this is the only reference to a protocol cloned 
-		 * route, remove it immediately.
-		 */
-		if (rt->rt_flags & RTF_WASCLONED &&
-		    (sc->sc_flags & SCF_KEEPROUTE) == 0 &&
-		    rt->rt_refcnt == 1)
-			rtrequest(RTM_DELETE, rt_key(rt),
-			    rt->rt_gateway, rt_mask(rt),
-			    rt->rt_flags, NULL);
-		RTFREE(rt);
-	}
+
 	uma_zfree(tcp_syncache.zone, sc);
 }
 
@@ -647,8 +628,6 @@ syncache_socket(sc, lso, m)
 		if (oinp->in6p_outputopts)
 			inp->in6p_outputopts =
 			    ip6_copypktopts(oinp->in6p_outputopts, M_NOWAIT);
-		inp->in6p_route = sc->sc_route6;
-		sc->sc_route6.ro_rt = NULL;
 
 		MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6,
 		    M_SONAME, M_NOWAIT | M_ZERO);
@@ -678,8 +657,6 @@ syncache_socket(sc, lso, m)
 			inp->inp_options = sc->sc_ipopts;
 			sc->sc_ipopts = NULL;
 		}
-		inp->inp_route = sc->sc_route;
-		sc->sc_route.ro_rt = NULL;
 
 		MALLOC(sin, struct sockaddr_in *, sizeof *sin,
 		    M_SONAME, M_NOWAIT | M_ZERO);
@@ -736,6 +713,10 @@ syncache_socket(sc, lso, m)
 		tp->cc_recv = sc->sc_cc_recv;
 	}
 
+	/*
+	 * Set up MSS and get cached values from tcp_hostcache.
+	 * This might overwrite some of the defaults we just set.
+	 */
 	tcp_mss(tp, sc->sc_peer_mss);
 
 	/*
@@ -808,10 +789,9 @@ resetandabort:
 #endif
 		m_freem(m);			/* XXX only needed for above */
 		tcpstat.tcps_sc_aborted++;
-	} else {
-		sc->sc_flags |= SCF_KEEPROUTE;
+	} else
 		tcpstat.tcps_sc_completed++;
-	}
+
 	if (sch == NULL)
 		syncache_free(sc);
 	else
@@ -846,11 +826,12 @@ syncache_add(inc, to, th, sop, m)
 	struct syncache *sc = NULL;
 	struct syncache_head *sch;
 	struct mbuf *ipopts = NULL;
-	struct rmxp_tao *taop;
+	struct rmxp_tao taop;
 	int i, s, win;
 
 	so = *sop;
 	tp = sototcpcb(so);
+	bzero(&taop, sizeof(taop));
 
 	/*
 	 * Remember the IP options, if any.
@@ -946,13 +927,11 @@ syncache_add(inc, to, th, sop, m)
 	if (inc->inc_isipv6) {
 		sc->sc_inc.inc6_faddr = inc->inc6_faddr;
 		sc->sc_inc.inc6_laddr = inc->inc6_laddr;
-		sc->sc_route6.ro_rt = NULL;
 	} else
 #endif
 	{
 		sc->sc_inc.inc_faddr = inc->inc_faddr;
 		sc->sc_inc.inc_laddr = inc->inc_laddr;
-		sc->sc_route.ro_rt = NULL;
 	}
 	sc->sc_irs = th->th_seq;
 	sc->sc_flags = 0;
@@ -1024,17 +1003,18 @@ syncache_add(inc, to, th, sop, m)
 	 *	processing: drop SYN, process data and FIN.
 	 * - otherwise do a normal 3-way handshake.
 	 */
-	taop = tcp_gettaocache(&sc->sc_inc);
+	if (tcp_do_rfc1644)
+		tcp_hc_gettao(&sc->sc_inc, &taop);
+
 	if ((to->to_flags & TOF_CC) != 0) {
 		if (((tp->t_flags & TF_NOPUSH) != 0) &&
-		    sc->sc_flags & SCF_CC && 
-		    taop != NULL && taop->tao_cc != 0 &&
-		    CC_GT(to->to_cc, taop->tao_cc)) {
+		    sc->sc_flags & SCF_CC && taop.tao_cc != 0 &&
+		    CC_GT(to->to_cc, taop.tao_cc)) {
 			sc->sc_rxtslot = 0;
 			so = syncache_socket(sc, *sop, m);
 			if (so != NULL) {
-				sc->sc_flags |= SCF_KEEPROUTE;
-				taop->tao_cc = to->to_cc;
+				taop.tao_cc = to->to_cc;
+				tcp_hc_updatetao(&sc->sc_inc, TCP_HC_TAO_CC, taop.tao_cc, 0);
 				*sop = so;
 			}
 			syncache_free(sc);
@@ -1044,9 +1024,12 @@ syncache_add(inc, to, th, sop, m)
 		/*
 		 * No CC option, but maybe CC.NEW: invalidate cached value.
 		 */
-		if (taop != NULL)
-			taop->tao_cc = 0;
+		if (tcp_do_rfc1644) {
+			taop.tao_cc = 0;
+			tcp_hc_updatetao(&sc->sc_inc, TCP_HC_TAO_CC, taop.tao_cc, 0);
+		}
 	}
+
 	/*
 	 * TAO test failed or there was no CC option,
 	 *    do a standard 3-way handshake.
@@ -1084,32 +1067,21 @@ syncache_respond(sc, m)
 	int optlen, error;
 	u_int16_t tlen, hlen, mssopt;
 	struct ip *ip = NULL;
-	struct rtentry *rt;
 	struct tcphdr *th;
 #ifdef INET6
 	struct ip6_hdr *ip6 = NULL;
 #endif
 
+	hlen =
 #ifdef INET6
-	if (sc->sc_inc.inc_isipv6) {
-		rt = tcp_rtlookup6(&sc->sc_inc);
-		if (rt != NULL)
-			mssopt = rt->rt_ifp->if_mtu -
-			     (sizeof(struct ip6_hdr) + sizeof(struct tcphdr));
-		else 
-			mssopt = tcp_v6mssdflt;
-		hlen = sizeof(struct ip6_hdr);
-	} else
+	       (sc->sc_inc.inc_isipv6) ? sizeof(struct ip6_hdr) : 
 #endif
-	{
-		rt = tcp_rtlookup(&sc->sc_inc);
-		if (rt != NULL)
-			mssopt = rt->rt_ifp->if_mtu -
-			     (sizeof(struct ip) + sizeof(struct tcphdr));
-		else 
-			mssopt = tcp_mssdflt;
-		hlen = sizeof(struct ip);
-	}
+		sizeof(struct ip);
+
+	KASSERT((&sc->sc_inc) != NULL, ("syncache_respond with NULL in_conninfo pointer"));
+
+	/* Determine MSS we advertize to other end of connection */
+	mssopt = tcp_mssopt(&sc->sc_inc);
 
 	/* Compute the size of the TCP options. */
 	if (sc->sc_flags & SCF_NOOPT) {
@@ -1238,13 +1210,10 @@ syncache_respond(sc, m)
 
 #ifdef INET6
 	if (sc->sc_inc.inc_isipv6) {
-		struct route_in6 *ro6 = &sc->sc_route6;
-
 		th->th_sum = 0;
 		th->th_sum = in6_cksum(m, IPPROTO_TCP, hlen, tlen - hlen);
-		ip6->ip6_hlim = in6_selecthlim(NULL,
-		    ro6->ro_rt ? ro6->ro_rt->rt_ifp : NULL);
-		error = ip6_output(m, NULL, ro6, 0, NULL, NULL,
+		ip6->ip6_hlim = in6_selecthlim(NULL, NULL);
+		error = ip6_output(m, NULL, NULL, 0, NULL, NULL,
 				sc->sc_tp->t_inpcb);
 	} else
 #endif
@@ -1263,7 +1232,7 @@ syncache_respond(sc, m)
 			    mtod(m, void *), th, 0);
 		}
 #endif
-		error = ip_output(m, sc->sc_ipopts, &sc->sc_route, 0, NULL,
+		error = ip_output(m, sc->sc_ipopts, NULL, 0, NULL,
 				sc->sc_tp->t_inpcb);
 	}
 	return (error);
@@ -1426,13 +1395,11 @@ syncookie_lookup(inc, th, so)
 	if (inc->inc_isipv6) {
 		sc->sc_inc.inc6_faddr = inc->inc6_faddr;
 		sc->sc_inc.inc6_laddr = inc->inc6_laddr;
-		sc->sc_route6.ro_rt = NULL;
 	} else
 #endif
 	{
 		sc->sc_inc.inc_faddr = inc->inc_faddr;
 		sc->sc_inc.inc_laddr = inc->inc_laddr;
-		sc->sc_route.ro_rt = NULL;
 	}
 	sc->sc_irs = th->th_seq - 1;
 	sc->sc_iss = th->th_ack - 1;
Index: netinet/tcp_timer.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_timer.c,v
retrieving revision 1.62
diff -u -p -r1.62 tcp_timer.c
--- netinet/tcp_timer.c	8 Nov 2003 22:57:13 -0000	1.62
+++ netinet/tcp_timer.c	11 Nov 2003 18:18:20 -0000
@@ -551,10 +551,8 @@ tcp_timer_rexmt(xtp)
 	if ((tp->t_state == TCPS_SYN_SENT) && (tp->t_rxtshift == 3))
 		tp->t_flags &= ~(TF_REQ_SCALE|TF_REQ_TSTMP|TF_REQ_CC);
 	/*
-	 * If losing, let the lower level know and try for
-	 * a better route.  Also, if we backed off this far,
-	 * our srtt estimate is probably bogus.  Clobber it
-	 * so we'll take the next rtt measurement as our srtt;
+	 * If we backed off this far, our srtt estimate is probably bogus.
+	 * Clobber it so we'll take the next rtt measurement as our srtt;
 	 * move the current srtt into rttvar to keep the current
 	 * retransmit times until then.
 	 */
@@ -564,7 +562,6 @@ tcp_timer_rexmt(xtp)
 			in6_losing(tp->t_inpcb);
 		else
 #endif
-		in_losing(tp->t_inpcb);
 		tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT);
 		tp->t_srtt = 0;
 	}
Index: netinet/tcp_usrreq.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_usrreq.c,v
retrieving revision 1.87
diff -u -p -r1.87 tcp_usrreq.c
--- netinet/tcp_usrreq.c	28 Oct 2003 05:47:40 -0000	1.87
+++ netinet/tcp_usrreq.c	11 Nov 2003 18:18:20 -0000
@@ -848,12 +848,13 @@ tcp_connect(tp, nam, td)
 	struct inpcb *inp = tp->t_inpcb, *oinp;
 	struct socket *so = inp->inp_socket;
 	struct tcptw *otw;
-	struct rmxp_tao *taop;
-	struct rmxp_tao tao_noncached;
+	struct rmxp_tao taop;
 	struct in_addr laddr;
 	u_short lport;
 	int error;
 
+	bzero(&taop, sizeof(taop));
+
 	if (inp->inp_lport == 0) {
 		error = in_pcbbind(inp, (struct sockaddr *)0, td);
 		if (error)
@@ -902,20 +903,21 @@ tcp_connect(tp, nam, td)
 	 * Generate a CC value for this connection and
 	 * check whether CC or CCnew should be used.
 	 */
-	if ((taop = tcp_gettaocache(&tp->t_inpcb->inp_inc)) == NULL) {
-		taop = &tao_noncached;
-		bzero(taop, sizeof(*taop));
-	}
+	if (tcp_do_rfc1644)
+		tcp_hc_gettao(&inp->inp_inc, &taop);
 
 	tp->cc_send = CC_INC(tcp_ccgen);
-	if (taop->tao_ccsent != 0 &&
-	    CC_GEQ(tp->cc_send, taop->tao_ccsent)) {
-		taop->tao_ccsent = tp->cc_send;
+	if (taop.tao_ccsent != 0 &&
+	    CC_GEQ(tp->cc_send, taop.tao_ccsent)) {
+		taop.tao_ccsent = tp->cc_send;
 	} else {
-		taop->tao_ccsent = 0;
+		taop.tao_ccsent = 0;
 		tp->t_flags |= TF_SENDCCNEW;
 	}
 
+	if (tcp_do_rfc1644)
+		tcp_hc_updatetao(&inp->inp_inc, TCP_HC_TAO_CCSENT, taop.tao_ccsent, 0);
+
 	return 0;
 }
 
@@ -931,10 +933,11 @@ tcp6_connect(tp, nam, td)
 	struct tcptw *otw;
 	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam;
 	struct in6_addr *addr6;
-	struct rmxp_tao *taop;
-	struct rmxp_tao tao_noncached;
+	struct rmxp_tao taop;
 	int error;
 
+	bzero(&taop, sizeof(taop));
+
 	if (inp->inp_lport == 0) {
 		error = in6_pcbbind(inp, (struct sockaddr *)0, td);
 		if (error)
@@ -991,19 +994,19 @@ tcp6_connect(tp, nam, td)
 	 * Generate a CC value for this connection and
 	 * check whether CC or CCnew should be used.
 	 */
-	if ((taop = tcp_gettaocache(&tp->t_inpcb->inp_inc)) == NULL) {
-		taop = &tao_noncached;
-		bzero(taop, sizeof(*taop));
-	}
+	if (tcp_do_rfc1644)
+		tcp_hc_gettao(&inp->inp_inc, &taop);
 
 	tp->cc_send = CC_INC(tcp_ccgen);
-	if (taop->tao_ccsent != 0 &&
-	    CC_GEQ(tp->cc_send, taop->tao_ccsent)) {
-		taop->tao_ccsent = tp->cc_send;
+	if (taop.tao_ccsent != 0 &&
+	    CC_GEQ(tp->cc_send, taop.tao_ccsent)) {
+		taop.tao_ccsent = tp->cc_send;
 	} else {
-		taop->tao_ccsent = 0;
+		taop.tao_ccsent = 0;
 		tp->t_flags |= TF_SENDCCNEW;
 	}
+	if (tcp_do_rfc1644)
+		tcp_hc_updatetao(&inp->inp_inc, TCP_HC_TAO_CCSENT, taop.tao_ccsent, 0);
 
 	return 0;
 }
@@ -1097,7 +1100,8 @@ tcp_ctloutput(so, sopt)
 			if (error)
 				break;
 
-			if (optval > 0 && optval <= tp->t_maxseg)
+			if (optval > 0 && optval <= tp->t_maxseg &&
+			    optval >= tcp_minmss)
 				tp->t_maxseg = optval;
 			else
 				error = EINVAL;
Index: netinet/tcp_var.h
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_var.h,v
retrieving revision 1.92
diff -u -p -r1.92 tcp_var.h
--- netinet/tcp_var.h	2 Nov 2003 07:47:03 -0000	1.92
+++ netinet/tcp_var.h	11 Nov 2003 18:18:21 -0000
@@ -46,6 +46,10 @@
 extern int	tcp_do_rfc1323;
 extern int	tcp_do_rfc1644;
 
+#ifdef TCPDEBUG_MSS
+extern int	tcp_sendmss;
+#endif
+
 /* TCP segment queue entry */
 struct tseg_qent {
 	LIST_ENTRY(tseg_qent) tqe_q;
@@ -179,6 +183,9 @@ struct tcpcb {
 	tcp_seq	snd_recover_prev;	/* snd_recover prior to retransmit */
 	u_long	t_badrxtwin;		/* window for retransmit recovery */
 	u_char	snd_limited;		/* segments limited transmitted */
+/* anti DoS counters */
+	u_long	rcv_pps;		/* received packets per second */
+	u_long	rcv_byps;		/* received bytes per second */
 };
 
 #define IN_FASTRECOVERY(tp)	(tp->t_flags & TF_FASTRECOVERY)
@@ -213,8 +220,6 @@ struct syncache {
 	struct 		tcpcb *sc_tp;		/* tcb for listening socket */
 	struct		mbuf *sc_ipopts;	/* source route */
 	struct 		in_conninfo sc_inc;	/* addresses */
-#define sc_route	sc_inc.inc_route
-#define sc_route6	sc_inc.inc6_route
 	u_int32_t	sc_tsrecent;
 	tcp_cc		sc_cc_send;		/* holds CC or CCnew */
 	tcp_cc		sc_cc_recv;
@@ -232,7 +237,6 @@ struct syncache {
 #define SCF_TIMESTAMP	0x04			/* negotiated timestamps */
 #define SCF_CC		0x08			/* negotiated CC */
 #define SCF_UNREACH	0x10			/* icmp unreachable received */
-#define SCF_KEEPROUTE	0x20			/* keep cloned route */
 	TAILQ_ENTRY(syncache)	sc_hash;
 	TAILQ_ENTRY(syncache)	sc_timerq;
 };
@@ -242,6 +246,17 @@ struct syncache_head {
 	u_int		sch_length;
 };
 
+struct hc_metrics_lite {	/* must stay in sync with hc_metrics */
+	u_long	rmx_mtu;	/* MTU for this path */
+	u_long	rmx_ssthresh;	/* outbound gateway buffer limit */
+	u_long	rmx_rtt;	/* estimated round trip time */
+	u_long	rmx_rttvar;	/* estimated rtt variance */
+	u_long	rmx_bandwidth;	/* estimated bandwidth */
+	u_long	rmx_cwnd;	/* congestion window */
+	u_long	rmx_sendpipe;   /* outbound delay-bandwidth product */
+	u_long	rmx_recvpipe;   /* inbound delay-bandwidth product */
+};
+
 struct tcptw {
 	struct inpcb	*tw_inpcb;	/* XXX back pointer to internet pcb */
 	tcp_seq		snd_nxt;
@@ -260,8 +275,7 @@ struct tcptw {
 };
  
 /*
- * The TAO cache entry which is stored in the protocol family specific
- * portion of the route metrics.
+ * The TAO cache entry which is stored in the tcp hostcache.
  */
 struct rmxp_tao {
 	tcp_cc	tao_cc;			/* latest CC in valid SYN */
@@ -274,7 +288,6 @@ struct rmxp_tao {
 #define	TAOF_UNDEF	0		/* we don't know yet */
 #endif /* notyet */
 };
-#define rmx_taop(r)	((struct rmxp_tao *)(r).rmx_filler)
 
 #define	intotcpcb(ip)	((struct tcpcb *)(ip)->inp_ppcb)
 #define	intotw(ip)	((struct tcptw *)(ip)->inp_ppcb)
@@ -401,6 +414,9 @@ struct	tcpstat {
 	u_long	tcps_sc_zonefail;	/* zalloc() failed */
 	u_long	tcps_sc_sendcookie;	/* SYN cookie sent */
 	u_long	tcps_sc_recvcookie;	/* SYN cookie received */
+
+	u_long	tcps_hc_added;		/* entry added to hostcache */
+	u_long	tcps_hc_bucketoverflow;	/* hostcache per bucket limit hit */
 };
 
 /*
@@ -435,6 +451,10 @@ struct	xtcpcb {
 #define	TCPCTL_DELACKTIME	12	/* time before sending delayed ACK */
 #define	TCPCTL_V6MSSDFLT	13	/* MSS default for IPv6 */
 #define	TCPCTL_MAXID		14
+#define TCPCTL_MINMSS		15	/* minimum MSS accepted */
+#define TCPCTL_MINMSSOVERLOAD	16	/* pps of lower than MSS packets before
+					 * connection drop
+					 */
 
 #define TCPCTL_NAMES { \
 	{ 0, 0 }, \
@@ -451,6 +471,9 @@ struct	xtcpcb {
 	{ "pcblist", CTLTYPE_STRUCT }, \
 	{ "delacktime", CTLTYPE_INT }, \
 	{ "v6mssdflt", CTLTYPE_INT }, \
+	{ "maxid", CTLTYPE_INT }, \
+	{ "minmss", CTLTYPE_INT }, \
+	{ "minmssoverload", CTLTYPE_INT }, \
 }
 
 
@@ -463,6 +486,8 @@ extern	struct inpcbhead tcb;		/* head of
 extern	struct inpcbinfo tcbinfo;
 extern	struct tcpstat tcpstat;	/* tcp statistics */
 extern	int tcp_mssdflt;	/* XXX */
+extern	int tcp_minmss;
+extern	int tcp_minmssoverload;
 extern	int tcp_delack_enabled;
 extern	int tcp_do_newreno;
 extern	int path_mtu_discovery;
@@ -482,12 +507,12 @@ struct tcpcb *
 	 tcp_drop(struct tcpcb *, int);
 void	 tcp_drain(void);
 void	 tcp_fasttimo(void);
-struct rmxp_tao *
-	 tcp_gettaocache(struct in_conninfo *);
 void	 tcp_init(void);
 void	 tcp_input(struct mbuf *, int);
+u_long	 tcp_maxmtu(struct in_conninfo *);
+u_long	 tcp_maxmtu6(struct in_conninfo *);
 void	 tcp_mss(struct tcpcb *, int);
-int	 tcp_mssopt(struct tcpcb *);
+int	 tcp_mssopt(struct in_conninfo *);
 struct inpcb *	 
 	 tcp_drop_syn_sent(struct inpcb *, int);
 struct inpcb *
@@ -500,8 +525,6 @@ struct inpcb *
 void	 tcp_respond(struct tcpcb *, void *,
 	    struct tcphdr *, struct mbuf *, tcp_seq, tcp_seq, int);
 int	 tcp_twrespond(struct tcptw *, struct socket *, struct mbuf *, int);
-struct rtentry *
-	 tcp_rtlookup(struct in_conninfo *);
 void	 tcp_setpersist(struct tcpcb *);
 void	 tcp_slowtimo(void);
 struct tcptemp *
@@ -519,6 +542,20 @@ int	 syncache_add(struct in_conninfo *, 
 	     struct tcphdr *, struct socket **, struct mbuf *);
 void	 syncache_chkrst(struct in_conninfo *, struct tcphdr *);
 void	 syncache_badack(struct in_conninfo *);
+/*
+ * All tcp_hc_* functions are IPv4 and IPv6 (via in_conninfo)
+ */
+void	 tcp_hc_init(void);
+void	 tcp_hc_get(struct in_conninfo *, struct hc_metrics_lite *);
+u_long	 tcp_hc_getmtu(struct in_conninfo *);
+void	 tcp_hc_gettao(struct in_conninfo *, struct rmxp_tao *);
+void	 tcp_hc_updatemtu(struct in_conninfo *, u_long);
+void	 tcp_hc_update(struct in_conninfo *, struct hc_metrics_lite *);
+void	 tcp_hc_updatetao(struct in_conninfo *, int, tcp_cc, u_short);
+/* update which tao field */
+#define	TCP_HC_TAO_CC		0x1
+#define TCP_HC_TAO_CCSENT	0x2
+#define TCP_HC_TAO_MSSOPT	0x3
 
 extern	struct pr_usrreqs tcp_usrreqs;
 extern	u_long tcp_sendspace;
Index: netinet/udp_usrreq.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/udp_usrreq.c,v
retrieving revision 1.139
diff -u -p -r1.139 udp_usrreq.c
--- netinet/udp_usrreq.c	8 Nov 2003 23:00:48 -0000	1.139
+++ netinet/udp_usrreq.c	11 Nov 2003 18:18:21 -0000
@@ -516,10 +516,17 @@ udp_ctlinput(cmd, sa, vip)
 	if (sa->sa_family != AF_INET || faddr.s_addr == INADDR_ANY)
         	return;
 
-	if (PRC_IS_REDIRECT(cmd)) {
-		ip = 0;
-		notify = in_rtchange;
-	} else if (cmd == PRC_HOSTDEAD)
+	/*
+	 * Redirects don't need to be handled up here.
+	 */
+	if (PRC_IS_REDIRECT(cmd))
+		return;
+	/*
+	 * Hostdead is ugly because it goes linearly through all PCBs.
+	 * XXX: We never get this from ICMP, otherwise it makes an
+	 * excellent DoS attack on machines with many connections.
+	 */
+	if (cmd == PRC_HOSTDEAD)
 		ip = 0;
 	else if ((unsigned)cmd >= PRC_NCMDS || inetctlerrmap[cmd] == 0)
 		return;
@@ -845,7 +852,7 @@ udp_output(inp, m, addr, control, td)
 	((struct ip *)ui)->ip_tos = inp->inp_ip_tos;	/* XXX */
 	udpstat.udps_opackets++;
 
-	error = ip_output(m, inp->inp_options, &inp->inp_route, ipflags,
+	error = ip_output(m, inp->inp_options, NULL, ipflags,
 	    inp->inp_moptions, inp);
 	return (error);
 
Index: netinet6/icmp6.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet6/icmp6.c,v
retrieving revision 1.48
diff -u -p -r1.48 icmp6.c
--- netinet6/icmp6.c	4 Nov 2003 16:02:02 -0000	1.48
+++ netinet6/icmp6.c	11 Nov 2003 18:18:21 -0000
@@ -94,6 +94,7 @@
 #include <netinet/in_var.h>
 #include <netinet/ip6.h>
 #include <netinet/icmp6.h>
+#include <netinet/tcp_var.h>
 #include <netinet6/in6_ifattach.h>
 #include <netinet6/in6_pcb.h>
 #include <netinet6/ip6protosw.h>
@@ -1105,8 +1106,7 @@ icmp6_mtudisc_update(ip6cp, validated)
 	struct icmp6_hdr *icmp6 = ip6cp->ip6c_icmp6;
 	struct mbuf *m = ip6cp->ip6c_m;	/* will be necessary for scope issue */
 	u_int mtu = ntohl(icmp6->icmp6_mtu);
-	struct rtentry *rt = NULL;
-	struct sockaddr_in6 sin6;
+	struct in_conninfo inc;
 
 #if 0
 	/*
@@ -1131,32 +1131,19 @@ icmp6_mtudisc_update(ip6cp, validated)
 	if (!validated)
 		return;
 
-	bzero(&sin6, sizeof(sin6));
-	sin6.sin6_family = PF_INET6;
-	sin6.sin6_len = sizeof(struct sockaddr_in6);
-	sin6.sin6_addr = *dst;
+	bzero(&inc, sizeof(inc));
+	inc.inc_flags = 1; /* IPv6 */
+	inc.inc6_faddr = *dst;
 	/* XXX normally, this won't happen */
 	if (IN6_IS_ADDR_LINKLOCAL(dst)) {
-		sin6.sin6_addr.s6_addr16[1] =
+		inc.inc6_faddr.s6_addr16[1] =
 		    htons(m->m_pkthdr.rcvif->if_index);
 	}
-	/* sin6.sin6_scope_id = XXX: should be set if DST is a scoped addr */
-	rt = rtalloc1((struct sockaddr *)&sin6, 0,
-		      RTF_CLONING | RTF_PRCLONING);
-
-	if (rt && (rt->rt_flags & RTF_HOST) &&
-	    !(rt->rt_rmx.rmx_locks & RTV_MTU)) {
-		if (mtu < IPV6_MMTU) {
-				/* xxx */
-			rt->rt_rmx.rmx_locks |= RTV_MTU;
-		} else if (mtu < rt->rt_ifp->if_mtu &&
-			   rt->rt_rmx.rmx_mtu > mtu) {
-			icmp6stat.icp6s_pmtuchg++;
-			rt->rt_rmx.rmx_mtu = mtu;
-		}
+
+	if (mtu >= IPV6_MMTU) {
+		tcp_hc_updatemtu(&inc, mtu);
+		icmp6stat.icp6s_pmtuchg++;
 	}
-	if (rt)
-		rtfree(rt);
 }
 
 /*
@@ -2135,8 +2122,7 @@ icmp6_reflect(m, off)
 		sin6->sin6_len = sizeof(struct sockaddr_in6);
 		sin6->sin6_addr = ip6->ip6_dst;
 
-		rtalloc_ign((struct route *)&icmp6_reflect_rt.ro_rt,
-			    RTF_PRCLONING);
+		rtalloc((struct route *)&icmp6_reflect_rt.ro_rt);
 	}
 
 	if (icmp6_reflect_rt.ro_rt == 0)
Index: netinet6/in6_pcb.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet6/in6_pcb.c,v
retrieving revision 1.44
diff -u -p -r1.44 in6_pcb.c
--- netinet6/in6_pcb.c	30 Oct 2003 23:02:51 -0000	1.44
+++ netinet6/in6_pcb.c	11 Nov 2003 18:18:22 -0000
@@ -337,8 +337,7 @@ in6_pcbladdr(inp, nam, plocal_addr6)
 		 * Is it the intended behavior?
 		 */
 		*plocal_addr6 = in6_selectsrc(sin6, inp->in6p_outputopts,
-					      inp->in6p_moptions,
-					      &inp->in6p_route,
+					      inp->in6p_moptions, NULL,
 					      &inp->in6p_laddr, &error);
 		if (*plocal_addr6 == 0) {
 			if (error == 0)
@@ -351,10 +350,6 @@ in6_pcbladdr(inp, nam, plocal_addr6)
 		 * and exit to caller, that will do the lookup.
 		 */
 	}
-
-	if (inp->in6p_route.ro_rt)
-		ifp = inp->in6p_route.ro_rt->rt_ifp;
-
 	return (0);
 }
 
@@ -447,8 +442,6 @@ in6_pcbdetach(inp)
 
  	ip6_freepcbopts(inp->in6p_outputopts);
  	ip6_freemoptions(inp->in6p_moptions);
-	if (inp->in6p_route.ro_rt)
-		RTFREE(inp->in6p_route.ro_rt);
 	/* Check and free IPv4 related resources in case of mapped addr */
 	if (inp->inp_options)
 		(void)m_free(inp->inp_options);
@@ -830,26 +823,10 @@ void
 in6_losing(in6p)
 	struct inpcb *in6p;
 {
-	struct rtentry *rt;
-	struct rt_addrinfo info;
-
-	if ((rt = in6p->in6p_route.ro_rt) != NULL) {
-		RT_LOCK(rt);
-		in6p->in6p_route.ro_rt = NULL;
-		bzero((caddr_t)&info, sizeof(info));
-		info.rti_flags = rt->rt_flags;
-		info.rti_info[RTAX_DST] = rt_key(rt);
-		info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
-		info.rti_info[RTAX_NETMASK] = rt_mask(rt);
-		rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0);
-		if (rt->rt_flags & RTF_DYNAMIC)
-			rtexpunge(rt);
-		RTFREE_LOCKED(rt);
-		/*
-		 * A new route can be allocated
-		 * the next time output is attempted.
-		 */
-	}
+	/*
+	 * We don't store route pointers in the routing table anymore
+	 */
+	return;
 }
 
 /*
@@ -861,14 +838,9 @@ in6_rtchange(inp, errno)
 	struct inpcb *inp;
 	int errno;
 {
-	if (inp->in6p_route.ro_rt) {
-		RTFREE(inp->in6p_route.ro_rt);
-		inp->in6p_route.ro_rt = 0;
-		/*
-		 * A new route can be allocated the next time
-		 * output is attempted.
-		 */
-	}
+	/*
+	 * We don't store route pointers in the routing table anymore
+	 */
 	return inp;
 }
 
Index: netinet6/in6_rmx.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet6/in6_rmx.c,v
retrieving revision 1.10
diff -u -p -r1.10 in6_rmx.c
--- netinet6/in6_rmx.c	30 Oct 2003 23:02:51 -0000	1.10
+++ netinet6/in6_rmx.c	11 Nov 2003 18:18:22 -0000
@@ -116,16 +116,9 @@ in6_addroute(void *v_arg, void *n_arg, s
 	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rt_key(rt);
 	struct radix_node *ret;
 
-	/*
-	 * For IPv6, all unicast non-host routes are automatically cloning.
-	 */
 	if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
 		rt->rt_flags |= RTF_MULTICAST;
 
-	if (!(rt->rt_flags & (RTF_HOST | RTF_CLONING | RTF_MULTICAST))) {
-		rt->rt_flags |= RTF_PRCLONING;
-	}
-
 	/*
 	 * A little bit of help for both IPv6 output and input:
 	 *   For local addresses, we make sure that RTF_LOCAL is set,
@@ -148,8 +141,7 @@ in6_addroute(void *v_arg, void *n_arg, s
 		}
 	}
 
-	if (!rt->rt_rmx.rmx_mtu && !(rt->rt_rmx.rmx_locks & RTV_MTU)
-	    && rt->rt_ifp)
+	if (!rt->rt_rmx.rmx_mtu && rt->rt_ifp)
 		rt->rt_rmx.rmx_mtu = IN6_LINKMTU(rt->rt_ifp);
 
 	ret = rn_addroute(v_arg, n_arg, head, treenodes);
@@ -160,8 +152,7 @@ in6_addroute(void *v_arg, void *n_arg, s
 		 * Find out if it is because of an
 		 * ARP entry and delete it if so.
 		 */
-		rt2 = rtalloc1((struct sockaddr *)sin6, 0,
-				RTF_CLONING | RTF_PRCLONING);
+		rt2 = rtalloc1((struct sockaddr *)sin6, 0, RTF_CLONING);
 		if (rt2) {
 			if (rt2->rt_flags & RTF_LLINFO &&
 				rt2->rt_flags & RTF_HOST &&
@@ -188,8 +179,7 @@ in6_addroute(void *v_arg, void *n_arg, s
 		 *	net route entry, 3ffe:0501:: -> if0.
 		 *	This case should not raise an error.
 		 */
-		rt2 = rtalloc1((struct sockaddr *)sin6, 0,
-				RTF_CLONING | RTF_PRCLONING);
+		rt2 = rtalloc1((struct sockaddr *)sin6, 0, RTF_CLONING);
 		if (rt2) {
 			if ((rt2->rt_flags & (RTF_CLONING|RTF_HOST|RTF_GATEWAY))
 					== RTF_CLONING
Index: netinet6/in6_src.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet6/in6_src.c,v
retrieving revision 1.22
diff -u -p -r1.22 in6_src.c
--- netinet6/in6_src.c	5 Nov 2003 16:09:21 -0000	1.22
+++ netinet6/in6_src.c	11 Nov 2003 18:18:22 -0000
@@ -197,7 +197,6 @@ in6_selectsrc(dstsock, opts, mopts, ro, 
 		    != 0) {
 			return (NULL);
 		}
-
 		/*
 		 * determine the appropriate zone id of the source based on
 		 * the zone of the destination and the outgoing interface.
@@ -435,12 +434,19 @@ in6_selectif(dstsock, opts, mopts, ro, r
 	struct route_in6 *ro;
 	struct ifnet **retifp;
 {
-	int error, clone;
+	int error;
+	struct route_in6 sro;
 	struct rtentry *rt = NULL;
 
-	clone = IN6_IS_ADDR_MULTICAST(&dstsock->sin6_addr) ? 0 : 1;
+	if (ro == NULL) {
+		bzero(&sro, sizeof(sro));
+		ro = &sro;
+	}
+
 	if ((error = in6_selectroute(dstsock, opts, mopts, ro, retifp,
-				     &rt, clone)) != 0) {
+				     &rt, 0)) != 0) {
+		if (rt && rt == sro.ro_rt)
+			RTFREE(rt);
 		return (error);
 	}
 
@@ -462,7 +468,11 @@ in6_selectif(dstsock, opts, mopts, ro, r
 	 * We thus reject the case here.
 	 */
 	if (rt && (rt->rt_flags & (RTF_REJECT | RTF_BLACKHOLE))) {
-		return (rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
+		int flags = (rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
+
+		if (rt && rt == sro.ro_rt)
+			RTFREE(rt);
+		return (flags);
 	}
 
 	/*
@@ -475,6 +485,8 @@ in6_selectif(dstsock, opts, mopts, ro, r
 	if (rt && rt->rt_ifa && rt->rt_ifa->ifa_ifp)
 		*retifp = rt->rt_ifa->ifa_ifp;
 
+	if (rt && rt == sro.ro_rt)
+		RTFREE(rt);
 	return (0);
 }
 
@@ -609,6 +621,7 @@ in6_selectroute(dstsock, opts, mopts, ro
 			sa6 = (struct sockaddr_in6 *)&ro->ro_dst;
 			*sa6 = *dstsock;
 			sa6->sin6_scope_id = 0;
+
 			if (clone) {
 				rtalloc((struct route *)ro);
 			} else {
@@ -681,7 +694,7 @@ in6_selectroute(dstsock, opts, mopts, ro
  * 2. (If the outgoing interface is detected) the current
  *     hop limit of the interface specified by router advertisement.
  * 3. The system default hoplimit.
-*/
+ */
 int
 in6_selecthlim(in6p, ifp)
 	struct in6pcb *in6p;
Index: netinet6/ip6_forward.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet6/ip6_forward.c,v
retrieving revision 1.23
diff -u -p -r1.23 ip6_forward.c
--- netinet6/ip6_forward.c	4 Nov 2003 16:02:02 -0000	1.23
+++ netinet6/ip6_forward.c	11 Nov 2003 18:18:22 -0000
@@ -343,8 +343,7 @@ ip6_forward(m, srcrt)
 			}
 
 			/* this probably fails but give it a try again */
-			rtalloc_ign((struct route *)&ip6_forward_rt,
-				    RTF_PRCLONING);
+			rtalloc((struct route *)&ip6_forward_rt);
 		}
 
 		if (ip6_forward_rt.ro_rt == 0) {
@@ -368,7 +367,7 @@ ip6_forward(m, srcrt)
 		dst->sin6_family = AF_INET6;
 		dst->sin6_addr = ip6->ip6_dst;
 
-  		rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING);
+  		rtalloc((struct route *)&ip6_forward_rt);
 		if (ip6_forward_rt.ro_rt == 0) {
 			ip6stat.ip6s_noroute++;
 			in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute);
Index: netinet6/ip6_input.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet6/ip6_input.c,v
retrieving revision 1.66
diff -u -p -r1.66 ip6_input.c
--- netinet6/ip6_input.c	8 Nov 2003 22:28:40 -0000	1.66
+++ netinet6/ip6_input.c	11 Nov 2003 18:18:22 -0000
@@ -499,7 +499,7 @@ ip6_input(m)
 		dst6->sin6_family = AF_INET6;
 		dst6->sin6_addr = ip6->ip6_dst;
 
-		rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING);
+		rtalloc((struct route *)&ip6_forward_rt);
 	}
 
 #define rt6_key(r) ((struct sockaddr_in6 *)((r)->rt_nodes->rn_key))
Index: netinet6/ip6_output.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet6/ip6_output.c,v
retrieving revision 1.68
diff -u -p -r1.68 ip6_output.c
--- netinet6/ip6_output.c	8 Nov 2003 23:36:32 -0000	1.68
+++ netinet6/ip6_output.c	11 Nov 2003 18:18:23 -0000
@@ -96,6 +96,7 @@
 #include <netinet/icmp6.h>
 #include <netinet6/ip6_var.h>
 #include <netinet/in_pcb.h>
+#include <netinet/tcp_var.h>
 #include <netinet6/nd6.h>
 
 #ifdef IPSEC
@@ -661,7 +662,7 @@ skip_ipsec2:;
 		/* XXX rt not locked */
 		ia = ifatoia6(ro->ro_rt->rt_ifa);
 		ifp = ro->ro_rt->rt_ifp;
-		ro->ro_rt->rt_use++;
+		ro->ro_rt->rt_rmx.rmx_pksent++;
 		if (ro->ro_rt->rt_flags & RTF_GATEWAY)
 			dst = (struct sockaddr_in6 *)ro->ro_rt->rt_gateway;
 		m->m_flags &= ~(M_BCAST | M_MCAST);	/* just in case */
@@ -757,7 +758,7 @@ skip_ipsec2:;
 			}
 			ia = ifatoia6(ro->ro_rt->rt_ifa);
 			ifp = ro->ro_rt->rt_ifp;
-			ro->ro_rt->rt_use++;
+			ro->ro_rt->rt_rmx.rmx_pksent++;
 			RT_UNLOCK(ro->ro_rt);
 		}
 
@@ -1387,11 +1388,20 @@ ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup,
 	}
 	if (ro_pmtu->ro_rt) {
 		u_int32_t ifmtu;
+		struct in_conninfo inc;
+
+		bzero(&inc, sizeof(inc));
+		inc.inc_flags = 1; /* IPv6 */
+		inc.inc6_faddr = *dst;
 
 		if (ifp == NULL)
 			ifp = ro_pmtu->ro_rt->rt_ifp;
 		ifmtu = IN6_LINKMTU(ifp);
-		mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu;
+		mtu = tcp_hc_getmtu(&inc);
+		if (mtu)
+			mtu = min(mtu, ro_pmtu->ro_rt->rt_rmx.rmx_mtu);
+		else
+			mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu;
 		if (mtu == 0)
 			mtu = ifmtu;
 		else if (mtu < IPV6_MMTU) {
@@ -1415,8 +1425,7 @@ ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup,
 			 * field isn't locked).
 			 */
 			mtu = ifmtu;
-			if (!(ro_pmtu->ro_rt->rt_rmx.rmx_locks & RTV_MTU))
-				ro_pmtu->ro_rt->rt_rmx.rmx_mtu = mtu;
+			ro_pmtu->ro_rt->rt_rmx.rmx_mtu = mtu;
 		}
 	} else if (ifp) {
 		mtu = IN6_LINKMTU(ifp);
@@ -1993,7 +2002,9 @@ do { \
 			{
 				u_long pmtu = 0;
 				struct ip6_mtuinfo mtuinfo;
-				struct route_in6 *ro = (struct route_in6 *)&in6p->in6p_route;
+				struct route_in6 sro;
+
+				bzero(&sro, sizeof(sro));
 
 				if (!(so->so_state & SS_ISCONNECTED))
 					return (ENOTCONN);
@@ -2002,8 +2013,10 @@ do { \
 				 * routing, or optional information to specify
 				 * the outgoing interface.
 				 */
-				error = ip6_getpmtu(ro, NULL, NULL,
+				error = ip6_getpmtu(&sro, NULL, NULL,
 				    &in6p->in6p_faddr, &pmtu, NULL);
+				if (sro.ro_rt)
+					RTFREE(sro.ro_rt);
 				if (error)
 					break;
 				if (pmtu > IPV6_MAXPACKET)
Index: netinet6/raw_ip6.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet6/raw_ip6.c,v
retrieving revision 1.33
diff -u -p -r1.33 raw_ip6.c
--- netinet6/raw_ip6.c	4 Nov 2003 16:02:03 -0000	1.33
+++ netinet6/raw_ip6.c	11 Nov 2003 18:18:23 -0000
@@ -424,15 +424,12 @@ rip6_output(m, va_alist)
 	 * Source address selection.
 	 */
 	if ((in6a = in6_selectsrc(dstsock, in6p->in6p_outputopts,
-	    in6p->in6p_moptions, &in6p->in6p_route, &in6p->in6p_laddr,
-	    &error)) == 0) {
+	    in6p->in6p_moptions, NULL, &in6p->in6p_laddr, &error)) == 0) {
 		if (error == 0)
 			error = EADDRNOTAVAIL;
 		goto bad;
 	}
 	ip6->ip6_src = *in6a;
-	if (in6p->in6p_route.ro_rt)
-		oifp = ifnet_byindex(in6p->in6p_route.ro_rt->rt_ifp->if_index);
 	ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) |
 		(in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK);
 	ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) |
@@ -477,7 +474,7 @@ rip6_output(m, va_alist)
 	}
 #endif /*IPSEC*/
 
-	error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route, 0,
+	error = ip6_output(m, in6p->in6p_outputopts, NULL, 0,
 			   in6p->in6p_moptions, &oifp, in6p);
 	if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
 		if (oifp)
@@ -691,7 +688,7 @@ rip6_connect(struct socket *so, struct s
 #endif
 	/* Source address selection. XXX: need pcblookup? */
 	in6a = in6_selectsrc(addr, inp->in6p_outputopts,
-			     inp->in6p_moptions, &inp->in6p_route,
+			     inp->in6p_moptions, NULL,
 			     &inp->in6p_laddr, &error);
 	if (in6a == NULL)
 		return (error ? error : EADDRNOTAVAIL);
Index: netinet6/udp6_output.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet6/udp6_output.c,v
retrieving revision 1.14
diff -u -p -r1.14 udp6_output.c
--- netinet6/udp6_output.c	4 Nov 2003 16:02:03 -0000	1.14
+++ netinet6/udp6_output.c	11 Nov 2003 18:18:24 -0000
@@ -203,8 +203,7 @@ udp6_output(in6p, m, addr6, control, td)
 
 		if (!IN6_IS_ADDR_V4MAPPED(faddr)) {
 			laddr = in6_selectsrc(sin6, in6p->in6p_outputopts,
-					      in6p->in6p_moptions,
-					      &in6p->in6p_route,
+					      in6p->in6p_moptions, NULL,
 					      &in6p->in6p_laddr, &error);
 		} else
 			laddr = &in6p->in6p_laddr;	/* XXX */
@@ -277,9 +276,7 @@ udp6_output(in6p, m, addr6, control, td)
 		ip6->ip6_plen	= htons((u_short)plen);
 #endif
 		ip6->ip6_nxt	= IPPROTO_UDP;
-		ip6->ip6_hlim	= in6_selecthlim(in6p,
-						 in6p->in6p_route.ro_rt ?
-						 in6p->in6p_route.ro_rt->rt_ifp : NULL);
+		ip6->ip6_hlim	= in6_selecthlim(in6p, NULL);
 		ip6->ip6_src	= *laddr;
 		ip6->ip6_dst	= *faddr;
 
@@ -297,7 +294,7 @@ udp6_output(in6p, m, addr6, control, td)
 			goto release;
 		}
 #endif /* IPSEC */
-		error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route,
+		error = ip6_output(m, in6p->in6p_outputopts, NULL,
 		    flags, in6p->in6p_moptions, NULL, in6p);
 		break;
 	case AF_INET:
Index: netipx/ipx_input.c
===================================================================
RCS file: /home/ncvs/src/sys/netipx/ipx_input.c,v
retrieving revision 1.34
diff -u -p -r1.34 ipx_input.c
--- netipx/ipx_input.c	8 Nov 2003 22:28:40 -0000	1.34
+++ netipx/ipx_input.c	11 Nov 2003 18:18:24 -0000
@@ -443,7 +443,7 @@ struct route *ro;
 	if (ro->ro_rt == NULL || ro->ro_rt->rt_ifp == NULL) {
 		return (0);
 	}
-	ro->ro_rt->rt_use++;
+	ro->ro_rt->rt_rmx.rmx_pksent++;
 	return (1);
 }
 
Index: netipx/ipx_outputfl.c
===================================================================
RCS file: /home/ncvs/src/sys/netipx/ipx_outputfl.c,v
retrieving revision 1.19
diff -u -p -r1.19 ipx_outputfl.c
--- netipx/ipx_outputfl.c	11 Jun 2003 05:25:14 -0000	1.19
+++ netipx/ipx_outputfl.c	11 Nov 2003 18:18:24 -0000
@@ -105,7 +105,7 @@ ipx_outputfl(m0, ro, flags)
 		error = ENETUNREACH;
 		goto bad;
 	}
-	ro->ro_rt->rt_use++;
+	ro->ro_rt->rt_rmx.rmx_pksent++;
 	if (ro->ro_rt->rt_flags & (RTF_GATEWAY|RTF_HOST))
 		dst = (struct sockaddr_ipx *)ro->ro_rt->rt_gateway;
 gotif:
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	11 Nov 2003 18:18:28 -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/tcp_hostcache.c	Sun Nov  9 16:32:31 2003
+++ netinet/tcp_hostcache.c	Tue Oct 28 16:06:31 2003
@@ -0,0 +1,675 @@
+/*
+ * Copyright (c) 2002 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/tcp_hostcache.c,v 1.1 2002/07/10 19:12:38 andre Exp $
+ */
+
+/*
+ * The tcp_hostcache moves the tcp specific cached metrics from the routing
+ * table into a dedicated structure indexed by the remote IP address. It
+ * keeps information on the measured tcp parameters of past tcp sessions
+ * to have better initial start values for following connections from the
+ * same source. Depending on the network parameters (delay, bandwidth, max
+ * MTU, congestion window) between local and remote site this can lead to
+ * significant speedups for new tcp connections after the first one.
+ *
+ * Due to this new tcp_hostcache all tcp specific metrics information in
+ * the routing table has been removed. The INPCB no longer keeps a pointer
+ * to the routing entry and protocol initiated route cloning has been
+ * removed as well. With these changes the routing table has gone back
+ * to being more lightwight and only carries information related to packet
+ * forwarding.
+ *
+ * Tcp_hostcache is designed for multiple concurrent access in SMP
+ * environments and high contention. All bucket rows have their own
+ * lock and thus multiple lookups and modifies can be done at the same
+ * time as long as they are in different bucket rows. If a request for
+ * insertion of a new record can't be satisfied it simply returns an
+ * empty structure. Nobody and nothing shall ever point directly to
+ * any entry in tcp_hostcache. All communication is done in an object
+ * oriented way and only funtions of tcp_hostcache will manipulate hostcache
+ * entries. Otherwise we are unable to achieve good behaviour in concurrent
+ * access situations. Since tcp_hostcache is only caching information there
+ * are no fatal consequences if we either can't satisfy any particular request
+ * or have to drop/overwrite an existing entry because of bucket limit
+ * memory constrains.
+ */
+
+/*
+ * Many thanks to jlemon for basic structure of tcp_syncache which is being
+ * followed here.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_var.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#endif
+#include <netinet/tcp.h>
+#include <netinet/tcp_var.h>
+#ifdef INET6
+#include <netinet6/tcp6_var.h>
+#endif
+
+#include <vm/uma.h>
+
+
+TAILQ_HEAD(hc_qhead, hc_metrics);
+
+struct hc_head {
+	struct hc_qhead	hch_bucket;
+	u_int		hch_length;
+	struct mtx	hch_mtx;
+};
+
+struct hc_metrics {
+	/* housekeeping */
+	TAILQ_ENTRY(hc_metrics) rmx_q;
+	struct	hc_head *rmx_head; /* head of bucket tail queue */
+	struct	in_addr ip4;	/* IP address */
+	struct	in6_addr ip6;	/* IP6 address */
+	/* endpoint specific values for tcp */
+	u_long	rmx_mtu;	/* MTU for this path */
+	u_long	rmx_ssthresh;	/* outbound gateway buffer limit */
+	u_long	rmx_rtt;	/* estimated round trip time */
+	u_long	rmx_rttvar;	/* estimated rtt variance */
+	u_long	rmx_bandwidth;	/* estimated bandwidth */
+	u_long	rmx_cwnd;	/* congestion window */
+	u_long	rmx_sendpipe;	/* outbound delay-bandwidth product */
+	u_long	rmx_recvpipe;	/* inbound delay-bandwidth product */
+	struct rmxp_tao	rmx_tao;/* TAO cache for T/TCP */
+	/* tcp hostcache internal data */
+	int	rmx_expire;	/* lifetime for object */
+	u_long	rmx_hits;	/* number of hits */
+	u_long	rmx_updates;	/* number of updates */
+};
+
+/* Arbitrary values */
+#define TCP_HOSTCACHE_HASHSIZE		512
+#define TCP_HOSTCACHE_BUCKETLIMIT	30
+#define TCP_HOSTCACHE_EXPIRE		60*60	/* one hour */
+#define TCP_HOSTCACHE_PRUNE		5*60	/* every 5 minutes */
+
+struct tcp_hostcache {
+	struct	hc_head *hashbase;
+	uma_zone_t zone;
+	u_int	hashsize;
+	u_int	hashmask;
+	u_int	bucket_limit;
+	u_int	cache_count;
+	u_int	cache_limit;
+	int	expire;
+	int	purgeall;
+};
+static struct tcp_hostcache tcp_hostcache;
+
+static struct callout tcp_hc_callout;
+
+static struct hc_metrics *tcp_hc_lookup(struct in_conninfo *);
+static struct hc_metrics *tcp_hc_insert(struct in_conninfo *);
+static int sysctl_tcp_hc_list(SYSCTL_HANDLER_ARGS);
+static void tcp_hc_purge(void *);
+
+SYSCTL_NODE(_net_inet_tcp, OID_AUTO, hostcache, CTLFLAG_RW, 0, "TCP Host cache");
+
+SYSCTL_INT(_net_inet_tcp_hostcache, OID_AUTO, cachelimit, CTLFLAG_RDTUN,
+     &tcp_hostcache.cache_limit, 0, "Overall entry limit for hostcache");
+
+SYSCTL_INT(_net_inet_tcp_hostcache, OID_AUTO, hashsize, CTLFLAG_RDTUN,
+     &tcp_hostcache.hashsize, 0, "Size of TCP hostcache hashtable");
+
+SYSCTL_INT(_net_inet_tcp_hostcache, OID_AUTO, bucketlimit, CTLFLAG_RDTUN,
+     &tcp_hostcache.bucket_limit, 0, "Per-bucket hash limit for hostcache");
+
+SYSCTL_INT(_net_inet_tcp_hostcache, OID_AUTO, count, CTLFLAG_RD,
+     &tcp_hostcache.cache_count, 0, "Current number of entries in hostcache");
+
+SYSCTL_INT(_net_inet_tcp_hostcache, OID_AUTO, expire, CTLFLAG_RW,
+     &tcp_hostcache.expire, 0, "Expire time of TCP hostcache entries");
+
+SYSCTL_INT(_net_inet_tcp_hostcache, OID_AUTO, purge, CTLFLAG_RW,
+     &tcp_hostcache.purgeall, 0, "Expire all entires on next purge run");
+
+SYSCTL_PROC(_net_inet_tcp_hostcache, OID_AUTO, list,
+	CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_SKIP, 0, 0,
+	sysctl_tcp_hc_list, "A", "List of all hostcache entries");
+
+
+static MALLOC_DEFINE(M_HOSTCACHE, "hostcache", "TCP hostcache");
+
+#define HOSTCACHE_HASH(ip) \
+	(((ip)->s_addr ^ ((ip)->s_addr >> 7) ^ ((ip)->s_addr >> 17)) & 	\
+	  tcp_hostcache.hashmask)
+
+/* XXX: What is the recommended hash to get good entropy for IPv6 addresses? */
+#define HOSTCACHE_HASH6(ip6)	 					\
+	((inc)->inc6_faddr.s6_addr32[0] ^ 				\
+	 (inc)->inc6_faddr.s6_addr32[3] ^				\
+	  tcp_hostcache.hashmask)
+
+void
+tcp_hc_init(void)
+{
+	int i;
+
+	/*
+	 * Initialize hostcache structures
+	 */
+	tcp_hostcache.cache_count = 0;
+	tcp_hostcache.hashsize = TCP_HOSTCACHE_HASHSIZE;
+	tcp_hostcache.bucket_limit = TCP_HOSTCACHE_BUCKETLIMIT;
+	tcp_hostcache.cache_limit =
+	    tcp_hostcache.hashsize * tcp_hostcache.bucket_limit;
+	tcp_hostcache.expire = TCP_HOSTCACHE_EXPIRE;
+
+        TUNABLE_INT_FETCH("net.inet.tcp.hostcache.hashsize",
+	    &tcp_hostcache.hashsize);
+        TUNABLE_INT_FETCH("net.inet.tcp.hostcache.cachelimit",
+	    &tcp_hostcache.cache_limit);
+        TUNABLE_INT_FETCH("net.inet.tcp.hostcache.bucketlimit",
+	    &tcp_hostcache.bucket_limit);
+	if (!powerof2(tcp_hostcache.hashsize)) {
+                printf("WARNING: hostcache hash size is not a power of 2.\n");
+		tcp_hostcache.hashsize = 512;	/* safe default */
+        }
+	tcp_hostcache.hashmask = tcp_hostcache.hashsize - 1;
+
+	/*
+	 * Allocate the hash table
+	 */
+	MALLOC(tcp_hostcache.hashbase, struct hc_head *,
+	    tcp_hostcache.hashsize * sizeof(struct hc_head),
+	    M_HOSTCACHE, M_WAITOK | M_ZERO);
+
+	/*
+	 * Initialize the hash buckets
+	 */
+	for (i = 0; i < tcp_hostcache.hashsize; i++) {
+		TAILQ_INIT(&tcp_hostcache.hashbase[i].hch_bucket);
+		tcp_hostcache.hashbase[i].hch_length = 0;
+		mtx_init(&tcp_hostcache.hashbase[i].hch_mtx, "tcp_hc_entry",
+			  NULL, MTX_DEF);
+	}
+
+	/*
+	 * Allocate the hostcache entries.
+	 */
+	tcp_hostcache.zone = uma_zcreate("hostcache", sizeof(struct hc_metrics),
+	    NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE);
+	uma_zone_set_max(tcp_hostcache.zone, tcp_hostcache.cache_limit);
+
+	/*
+	 * Set up periodic cache cleanup.
+	 */
+	callout_init(&tcp_hc_callout, CALLOUT_MPSAFE);
+	callout_reset(&tcp_hc_callout, TCP_HOSTCACHE_PRUNE * hz, tcp_hc_purge, 0);
+}
+
+/* internal function */
+static struct hc_metrics *
+tcp_hc_lookup(struct in_conninfo *inc)
+{
+	int hash;
+	struct hc_head *hc_head;
+	struct hc_metrics *hc_entry;
+
+	KASSERT(inc != NULL, ("tcp_hc_lookup with NULL in_conninfo pointer"));
+
+	/*
+	 * Hash the foreign ip address.
+	 */
+	if (inc->inc_isipv6)
+		hash = HOSTCACHE_HASH6(&inc->inc6_faddr);
+	else
+		hash = HOSTCACHE_HASH(&inc->inc_faddr);
+
+	hc_head = &tcp_hostcache.hashbase[hash];
+
+	/*
+	 * aquire lock for this bucket row
+	 * we release the lock if we don't find an entry,
+	 * otherwise the caller has to unlock after he is done
+	 */
+	mtx_lock(&hc_head->hch_mtx);
+
+	/*
+	 * circle through entries in bucket row looking for a match
+	 */
+	TAILQ_FOREACH(hc_entry, &hc_head->hch_bucket, rmx_q) {
+		if (inc->inc_isipv6) {
+			if (memcmp(&inc->inc6_faddr, &hc_entry->ip6,
+			    sizeof(inc->inc6_faddr)) == 0)
+				return hc_entry;
+		} else {
+			if (memcmp(&inc->inc_faddr, &hc_entry->ip4,
+			    sizeof(inc->inc_faddr)) == 0)
+				return hc_entry;
+		}
+	}
+
+	/*
+	 * We were unsuccessful and didn't find anything
+	 */
+	mtx_unlock(&hc_head->hch_mtx);
+	return NULL;
+}
+
+static struct hc_metrics *
+tcp_hc_insert(struct in_conninfo *inc)
+{
+	int hash;
+	struct hc_head *hc_head;
+	struct hc_metrics *hc_entry;
+
+	KASSERT(inc != NULL, ("tcp_hc_insert with NULL in_conninfo pointer"));
+
+	/*
+	 * Hash the foreign ip address
+	 */
+	if (inc->inc_isipv6)
+		hash = HOSTCACHE_HASH6(&inc->inc6_faddr);
+	else
+		hash = HOSTCACHE_HASH(&inc->inc_faddr);
+
+	hc_head = &tcp_hostcache.hashbase[hash];
+
+	/*
+	 * aquire lock for this bucket row
+	 * we release the lock if we don't find an entry,
+	 * otherwise the caller has to unlock after he is done
+	 */
+	mtx_lock(&hc_head->hch_mtx);
+
+	/*
+	 * If the bucket limit is reached reuse the least used element
+	 */
+	if (hc_head->hch_length >= tcp_hostcache.bucket_limit ||
+	    tcp_hostcache.cache_count >= tcp_hostcache.cache_limit) {
+		hc_entry = TAILQ_LAST(&hc_head->hch_bucket, hc_qhead);
+#if 0
+		/*
+		 * At first we were dropping the last element, just to
+		 * reaquire it in the next two lines again which ain't
+		 * very efficient. Instead just reuse the least used element.
+		 * maybe we drop something that is still "in-use" but we can
+		 * be "lossy".
+		 */
+		TAILQ_REMOVE(&hc_head->hch_bucket, hc_entry, rmx_q);
+		uma_zfree(tcp_hostcache.zone, hc_entry);
+		tcp_hostcache.hashbase[hash].hch_length--;
+		tcp_hostcache.cache_count--;
+#endif
+		tcpstat.tcps_hc_bucketoverflow++;
+	} else {
+		/*
+		 * Allocate a new entry, or balk if not possible
+		 */
+		hc_entry = uma_zalloc(tcp_hostcache.zone, M_NOWAIT);
+		if (hc_entry == NULL) {
+			mtx_unlock(&hc_head->hch_mtx);
+			return NULL;
+		}
+	}
+
+	/*
+	 * Initialize basic information of hostcache entry
+	 */
+	bzero(hc_entry, sizeof(*hc_entry));
+	if (inc->inc_isipv6)
+		memcpy(&hc_entry->ip6, &inc->inc6_faddr, sizeof(hc_entry->ip6));
+	else
+		hc_entry->ip4 = inc->inc_faddr;
+	hc_entry->rmx_head = hc_head;
+	hc_entry->rmx_expire = tcp_hostcache.expire;
+
+	/*
+	 * Put it upfront
+	 */
+	TAILQ_INSERT_HEAD(&hc_head->hch_bucket, hc_entry, rmx_q);
+	tcp_hostcache.hashbase[hash].hch_length++;
+	tcp_hostcache.cache_count++;
+	tcpstat.tcps_hc_added++;
+
+	return hc_entry;
+}
+
+/* external functions */
+void
+tcp_hc_get(struct in_conninfo *inc, struct hc_metrics_lite *hc_metrics_lite)
+{
+	struct hc_metrics *hc_entry;
+
+	/*
+	 * Find the right bucket
+	 */
+	hc_entry = tcp_hc_lookup(inc);
+
+	/*
+	 * If we don't have an existing object
+	 */
+	if (hc_entry == NULL) {
+		bzero(hc_metrics_lite, sizeof(*hc_metrics_lite));
+		return;
+	}
+	hc_entry->rmx_hits++;
+	hc_entry->rmx_expire = tcp_hostcache.expire; /* start over again */
+
+	hc_metrics_lite->rmx_mtu = hc_entry->rmx_mtu;
+	hc_metrics_lite->rmx_ssthresh = hc_entry->rmx_ssthresh;
+	hc_metrics_lite->rmx_rtt = hc_entry->rmx_rtt;
+	hc_metrics_lite->rmx_rttvar = hc_entry->rmx_rttvar;
+	hc_metrics_lite->rmx_bandwidth = hc_entry->rmx_bandwidth;
+	hc_metrics_lite->rmx_cwnd = hc_entry->rmx_cwnd;
+	hc_metrics_lite->rmx_sendpipe = hc_entry->rmx_sendpipe;
+	hc_metrics_lite->rmx_recvpipe = hc_entry->rmx_recvpipe;
+
+	/*
+	 * unlock bucket row
+	 */
+	mtx_unlock(&hc_entry->rmx_head->hch_mtx);
+}
+
+u_long
+tcp_hc_getmtu(struct in_conninfo *inc)
+{
+	struct hc_metrics *hc_entry;
+	u_long mtu;
+
+	hc_entry = tcp_hc_lookup(inc);
+	if (hc_entry == NULL) {
+		return 0;
+	}
+	hc_entry->rmx_hits++;
+	hc_entry->rmx_expire = tcp_hostcache.expire; /* start over again */
+
+	mtu = hc_entry->rmx_mtu;
+	mtx_unlock(&hc_entry->rmx_head->hch_mtx);
+	return mtu;
+}
+
+void
+tcp_hc_gettao(struct in_conninfo *inc, struct rmxp_tao *tao)
+{
+	struct hc_metrics *hc_entry;
+
+	hc_entry = tcp_hc_lookup(inc);
+	if (hc_entry == NULL) {
+		bzero(tao, sizeof(*tao));
+		return;
+	}
+	hc_entry->rmx_hits++;
+	hc_entry->rmx_expire = tcp_hostcache.expire; /* start over again */
+
+	memcpy(tao, &hc_entry->rmx_tao, sizeof(*tao));
+	mtx_unlock(&hc_entry->rmx_head->hch_mtx);
+}
+
+void
+tcp_hc_updatemtu(struct in_conninfo *inc, u_long mtu)
+{
+	struct hc_metrics *hc_entry;
+
+	/*
+	 * Find the right bucket
+	 */
+	hc_entry = tcp_hc_lookup(inc);
+
+	/*
+	 * If we don't have an existing object try to insert a new one
+	 */
+	if (hc_entry == NULL) {
+		hc_entry = tcp_hc_insert(inc);
+		if (hc_entry == NULL)
+			return;
+	}
+	hc_entry->rmx_updates++;
+	hc_entry->rmx_expire = tcp_hostcache.expire; /* start over again */
+
+	hc_entry->rmx_mtu = mtu;
+
+	/*
+	 * put it upfront so we find it faster next time
+	 */
+	TAILQ_REMOVE(&hc_entry->rmx_head->hch_bucket, hc_entry, rmx_q);
+	TAILQ_INSERT_HEAD(&hc_entry->rmx_head->hch_bucket, hc_entry, rmx_q);
+
+	/*
+	 * unlock bucket row
+	 */
+	mtx_unlock(&hc_entry->rmx_head->hch_mtx);
+}
+
+void
+tcp_hc_update(struct in_conninfo *inc, struct hc_metrics_lite *hcml)
+{
+	struct hc_metrics *hc_entry;
+
+	hc_entry = tcp_hc_lookup(inc);
+	if (hc_entry == NULL) {
+		hc_entry = tcp_hc_insert(inc);
+		if (hc_entry == NULL)
+			return;
+	}
+	hc_entry->rmx_updates++;
+	hc_entry->rmx_expire = tcp_hostcache.expire; /* start over again */
+
+	if (hcml->rmx_rtt != 0) {
+		if (hc_entry->rmx_rtt == 0)
+			hc_entry->rmx_rtt = hcml->rmx_rtt;
+		else
+			hc_entry->rmx_rtt =
+				(hc_entry->rmx_rtt + hcml->rmx_rtt) / 2;
+		tcpstat.tcps_cachedrtt++;
+	}
+	if (hcml->rmx_rttvar != 0) {
+	        if (hc_entry->rmx_rttvar == 0)
+        	        hc_entry->rmx_rttvar = hcml->rmx_rttvar;
+		else
+			hc_entry->rmx_rttvar =
+				(hc_entry->rmx_rttvar + hcml->rmx_rttvar) / 2;
+		tcpstat.tcps_cachedrttvar++;
+	}
+	if (hcml->rmx_ssthresh != 0) {
+		if (hc_entry->rmx_ssthresh == 0)
+			hc_entry->rmx_ssthresh = hcml->rmx_ssthresh;
+		else
+			hc_entry->rmx_ssthresh =
+				(hc_entry->rmx_ssthresh + hcml->rmx_ssthresh) / 2;
+		tcpstat.tcps_cachedssthresh++;
+	}
+	if (hcml->rmx_bandwidth != 0) {
+		if (hc_entry->rmx_bandwidth == 0)
+			hc_entry->rmx_bandwidth = hcml->rmx_bandwidth;
+		else
+			hc_entry->rmx_bandwidth =
+				(hc_entry->rmx_bandwidth + hcml->rmx_bandwidth) / 2;
+		/* tcpstat.tcps_cachedbandwidth++; */
+	}
+	if (hcml->rmx_cwnd != 0) {
+		if (hc_entry->rmx_cwnd == 0)
+			hc_entry->rmx_cwnd = hcml->rmx_cwnd;
+		else
+			hc_entry->rmx_cwnd =
+				(hc_entry->rmx_cwnd + hcml->rmx_cwnd) / 2;
+		/* tcpstat.tcps_cachedcwnd++; */
+	}
+	if (hcml->rmx_sendpipe != 0) {
+		if (hc_entry->rmx_sendpipe == 0)
+			hc_entry->rmx_sendpipe = hcml->rmx_sendpipe;
+		else
+			hc_entry->rmx_sendpipe =
+				(hc_entry->rmx_sendpipe + hcml->rmx_sendpipe) /2;
+                /* tcpstat.tcps_cachedsendpipe++; */
+        }
+	if (hcml->rmx_recvpipe != 0) {
+		if (hc_entry->rmx_recvpipe == 0)
+			hc_entry->rmx_recvpipe = hcml->rmx_recvpipe;
+		else
+			hc_entry->rmx_recvpipe =
+				(hc_entry->rmx_recvpipe + hcml->rmx_recvpipe) /2;
+		/* tcpstat.tcps_cachedrecvpipe++; */
+	}
+
+	TAILQ_REMOVE(&hc_entry->rmx_head->hch_bucket, hc_entry, rmx_q);
+	TAILQ_INSERT_HEAD(&hc_entry->rmx_head->hch_bucket, hc_entry, rmx_q);
+	mtx_unlock(&hc_entry->rmx_head->hch_mtx);
+}
+
+void
+tcp_hc_updatetao(struct in_conninfo *inc, int field, tcp_cc ccount, u_short mss)
+{
+	struct hc_metrics *hc_entry;
+
+	hc_entry = tcp_hc_lookup(inc);
+	if (hc_entry == NULL) {
+		hc_entry = tcp_hc_insert(inc);
+		if (hc_entry == NULL)
+			return;
+	}
+	hc_entry->rmx_updates++;
+	hc_entry->rmx_expire = tcp_hostcache.expire; /* start over again */
+
+	switch(field) {
+		case TCP_HC_TAO_CC:
+			hc_entry->rmx_tao.tao_cc = ccount;
+			break;
+
+		case TCP_HC_TAO_CCSENT:
+			hc_entry->rmx_tao.tao_ccsent = ccount;
+			break;
+
+		case TCP_HC_TAO_MSSOPT:
+			hc_entry->rmx_tao.tao_mssopt = mss;
+			break;
+	}
+
+	TAILQ_REMOVE(&hc_entry->rmx_head->hch_bucket, hc_entry, rmx_q);
+	TAILQ_INSERT_HEAD(&hc_entry->rmx_head->hch_bucket, hc_entry, rmx_q);
+	mtx_unlock(&hc_entry->rmx_head->hch_mtx);
+}
+
+static int
+sysctl_tcp_hc_list(SYSCTL_HANDLER_ARGS)
+{
+	int bufsize;
+	int linesize = 128;
+	char *p, *buf;
+	int len, i, error;
+	struct hc_metrics *hc_entry;
+
+	bufsize = linesize * (tcp_hostcache.cache_count + 1);
+
+	p = buf = (char *)malloc(bufsize, M_TEMP, M_WAITOK|M_ZERO);
+
+	len = snprintf(p, linesize,
+		"\nIP address        MTU  SSTRESH      RTT   RTTVAR BANDWIDTH "
+		"    CWND SENDPIPE RECVPIPE HITS  UPD  EXP\n");
+	p += len;
+
+#define msec(u) (((u) + 500) / 1000)
+	for (i = 0; i < tcp_hostcache.hashsize; i++) {
+		mtx_lock(&tcp_hostcache.hashbase[i].hch_mtx);
+		TAILQ_FOREACH(hc_entry, &tcp_hostcache.hashbase[i].hch_bucket, rmx_q) {
+			len = snprintf(p, linesize,
+				"%-15s %5lu %8lu %6lums %6lums %9lu %8lu %8lu %8lu %4lu %4lu %4i\n",
+				hc_entry->ip4.s_addr ? inet_ntoa(hc_entry->ip4) :
+#ifdef INET6
+					ip6_sprintf(&hc_entry->ip6),
+#else
+					"IPv6?",
+#endif
+				hc_entry->rmx_mtu,
+				hc_entry->rmx_ssthresh,
+				msec(hc_entry->rmx_rtt * (RTM_RTTUNIT / (hz * TCP_RTT_SCALE))),
+				msec(hc_entry->rmx_rttvar * (RTM_RTTUNIT / (hz * TCP_RTT_SCALE))),
+				hc_entry->rmx_bandwidth * hz * 8,
+				hc_entry->rmx_cwnd,
+				hc_entry->rmx_sendpipe,
+				hc_entry->rmx_recvpipe,
+				hc_entry->rmx_hits,
+				hc_entry->rmx_updates,
+				hc_entry->rmx_expire);
+			p += len;
+		}
+		mtx_unlock(&tcp_hostcache.hashbase[i].hch_mtx);
+	}
+#undef msec
+	error = SYSCTL_OUT(req, buf, p - buf);
+	free(buf, M_TEMP);
+	return(error);
+}
+
+/*
+ * Purge (old|all) entries in the tcp_hostcache
+ */
+static void
+tcp_hc_purge(void *arg)
+{
+	struct hc_metrics *hc_entry;
+	int all = (int)arg;
+	int i;
+
+	if (tcp_hostcache.purgeall) {
+		all = 1;
+		tcp_hostcache.purgeall = 0;
+	}
+
+	for (i = 0; i < tcp_hostcache.hashsize; i++) {
+		mtx_lock(&tcp_hostcache.hashbase[i].hch_mtx);
+		TAILQ_FOREACH(hc_entry, &tcp_hostcache.hashbase[i].hch_bucket, rmx_q) {
+			if (all || hc_entry->rmx_expire <= 0) {
+				TAILQ_REMOVE(&tcp_hostcache.hashbase[i].hch_bucket,
+					      hc_entry, rmx_q);
+				uma_zfree(tcp_hostcache.zone, hc_entry);
+				tcp_hostcache.hashbase[i].hch_length--;
+				tcp_hostcache.cache_count--;
+			} else
+				hc_entry->rmx_expire -= TCP_HOSTCACHE_PRUNE;
+		}
+		mtx_unlock(&tcp_hostcache.hashbase[i].hch_mtx);
+	}
+	callout_reset(&tcp_hc_callout, TCP_HOSTCACHE_PRUNE * hz, tcp_hc_purge, 0);
+}
+
--- netinet/ip_fastforward.c	Sun Nov  9 16:32:31 2003
+++ netinet/ip_fastforward.c	Thu Oct 23 15:30:50 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;
+}
+
