? sys/net/old
? sys/net/old2
? sys/netinet/tcpsign
Index: sys/net/route.c
===================================================================
RCS file: /cvsroot/src/sys/net/route.c,v
retrieving revision 1.98
diff -u -p -r1.98 route.c
--- sys/net/route.c	10 Oct 2007 22:14:38 -0000	1.98
+++ sys/net/route.c	12 Nov 2007 17:14:39 -0000
@@ -147,6 +147,7 @@ struct callout rt_timer_ch; /* callout f
 static int _rtcache_debug = 0;
 #endif /* RTFLUSH_DEBUG */
 
+struct rtentry *rtgethead(const struct sockaddr *, const struct sockaddr *);
 static int rtdeletemsg(struct rtentry *);
 static int rtflushclone1(struct rtentry *, void *);
 static void rtflushclone(sa_family_t family, struct rtentry *);
@@ -304,6 +305,34 @@ rtalloc(struct route *ro)
 	rtcache(ro);
 }
 
+/*
+ * Returns rtentry in a RR fashion
+ * rt should be the first path
+ */
+struct rtentry *
+rtchoosepath_rr(struct rtentry *rt)
+{
+	rt->rt_last = rtnext(rt->rt_last);
+	return rt->rt_last;
+}
+
+/*
+ * Next rtentry that it's UP (in case there is such thing)
+ * If none is found return the feeded rtentry
+ */
+struct rtentry *
+rtnext(struct rtentry *rt)
+{
+	struct rtentry *retrt, *sentinel;
+
+	KASSERT(rt != NULL);
+	CLIST_FOREACH(retrt, CLIST_NEXT(rt, rt_list), sentinel, rt_list)
+	    if (retrt->rt_flags & RTF_UP)
+		return retrt;
+
+	return rt;
+}
+
 struct rtentry *
 rtalloc1(const struct sockaddr *dst, int report)
 {
@@ -355,28 +384,81 @@ rtalloc1(const struct sockaddr *dst, int
 	return newrt;
 }
 
+/*
+ * returns head of the list
+ * just a rnh_lookup wrapper
+ */
+struct rtentry *
+rtgethead(const struct sockaddr *dst, const struct sockaddr *netmask)
+{
+	struct radix_node_head *rnh = rt_tables[dst->sa_family];
+	struct rtentry *rt = NULL;
+	struct radix_node *rn;
+	int  s = splsoftnet();
+
+	if (rnh && (rn = rnh->rnh_lookup(dst, netmask, rnh)) &&
+	    ((rn->rn_flags & RNF_ROOT) == 0))
+		rt = (struct rtentry *)rn;
+		else
+		rtstat.rts_unreach++;
+
+	splx(s);
+	return rt;
+}
+
 void
 rtfree(struct rtentry *rt)
 {
-	struct ifaddr *ifa;
+	struct rtentry *rthead;
 
 	if (rt == NULL)
 		panic("rtfree");
 	rt->rt_refcnt--;
 	if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) {
-		if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT))
-			panic ("rtfree 2");
 		rttrash--;
 		if (rt->rt_refcnt < 0) {
 			printf("rtfree: %p not freed (neg refs)\n", rt);
 			return;
 		}
+		rthead = RTFIRST(rt);
+		rthead->rt_total--;
+		if (rthead->rt_total == 0 &&
+		    (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)))
+			panic("rtfree 2");
 		rt_timer_remove_all(rt, 0);
-		ifa = rt->rt_ifa;
-		rt->rt_ifa = NULL;
-		IFAFREE(ifa);
-		rt->rt_ifp = NULL;
-		rt_destroy(rt);
+		IFAFREE(rt->rt_ifa);
+		if (rthead->rt_total == 0) {
+			/* No other paths */
+			rt_destroy(rt);
+		} else if (rthead == rt) {
+			/* First GW to delete from more */
+			struct radix_node_head *rnh;
+			struct rtentry *srt = CLIST_NEXT(rthead, rt_list),
+				*sen, *rtin;
+			KASSERT(rt != srt);
+			srt->rt_total = rt->rt_total;
+			srt->rt_last = srt;
+			CLIST_REMOVE(rt, rt_list);
+			if ((rnh = rt_tables[rt_getkey(rt)->sa_family]) == NULL)
+				panic("rtfree: rt_tables");
+			if (rnh->rnh_deladdr(rt_getkey(rt), rt_mask(rt), rnh) == NULL)
+				panic("rtfree: deladdr");
+			if (rnh->rnh_addaddr(rt_getkey(srt), rt_mask(srt), rnh,
+			    srt->rt_nodes) == NULL)
+				panic("rtfree: addaddr");
+			CLIST_FOREACH(rtin, srt, sen, rt_list)
+				RTFIRST(rtin) = srt;
+		} else {
+			/* Delete a non-first path */
+			CLIST_REMOVE(rt, rt_list);
+			if (rthead->rt_last == rt)
+				rthead->rt_last = rthead;
+		}
+
+		if (rt->rt_gateway != NULL)
+			sockaddr_free(rt->rt_gateway);
+		/* do I really need this ? I also Bzero at pool_get */
+		Bzero(rt, sizeof(*rt));
 		pool_put(&rtentry_pool, rt);
 	}
 }
@@ -427,20 +509,33 @@ rtredirect(const struct sockaddr *dst, c
 		error = ENETUNREACH;
 		goto out;
 	}
-	rt = rtalloc1(dst, 0);
 	/*
-	 * If the redirect isn't from our current router for this dst,
-	 * it's either old or wrong.  If it redirects us to ourselves,
-	 * we have a routing loop, perhaps as a result of an interface
-	 * going down recently.
+	 * If it redirects us to ourselves we have a routing loop,
+	 * perhaps as a result of an interface going down recently.
 	 */
-	if (!(flags & RTF_DONE) && rt &&
-	     (!equal(src, rt->rt_gateway) || rt->rt_ifa != ifa))
-		error = EINVAL;
-	else if (ifa_ifwithaddr(gateway))
+	if (ifa_ifwithaddr(gateway)) {
 		error = EHOSTUNREACH;
-	if (error)
-		goto done;
+		goto out;
+	}
+	rt = rtalloc1(dst, 0);
+	if (rt && !(flags & RTF_DONE)) {
+		/*
+	 	 * If the redirect isn't from our current router for this dst,
+	 	 * it's either old or wrong. Also calibrate rt.
+	 	 */
+		struct rtentry *sentinel, *nrt;
+		CLIST_FOREACH(nrt, rt, sentinel, rt_list)
+		    if(equal(src, nrt->rt_gateway) && (nrt->rt_ifa == ifa))
+			break;
+		if(nrt == NULL) {
+			error = EINVAL;
+			goto done;
+		}
+		rt->rt_refcnt--;
+		nrt->rt_refcnt++;
+		rt = nrt;
+	}
+
 	/*
 	 * Create a new entry if we just got back a wildcard entry
 	 * or the lookup failed.  This is necessary for hosts
@@ -485,6 +580,7 @@ rtredirect(const struct sockaddr *dst, c
 		}
 	} else
 		error = EHOSTUNREACH;
+
 done:
 	if (rt) {
 		if (rtp != NULL && !error)
@@ -674,7 +770,7 @@ rtrequest1(int req, struct rt_addrinfo *
 {
 	int s = splsoftnet();
 	int error = 0;
-	struct rtentry *rt, *crt;
+	struct rtentry *rt, *crt = NULL, *sentinel, *nrt;
 	struct radix_node *rn;
 	struct radix_node_head *rnh;
 	struct ifaddr *ifa;
@@ -698,16 +794,45 @@ rtrequest1(int req, struct rt_addrinfo *
 		}
 		if ((rn = rnh->rnh_lookup(dst, netmask, rnh)) == NULL)
 			senderr(ESRCH);
-		rt = (struct rtentry *)rn;
+		crt = rt = (struct rtentry *)rn;
+		/* Calibrate */
+		if (gateway != NULL && !(crt->rt_flags & RTF_CLONING)) {
+			/*
+			 * XXX: we can have a gateway on cloning route
+			 */
+			CLIST_FOREACH(rt, crt, sentinel, rt_list)
+			    if (sockaddr_cmp(gateway, rt->rt_gateway) == 0)
+				break;
+			if (rt == NULL)
+				senderr(ESRCH);
+		} else
+			if (! CLIST_SINGULAR(crt, rt_list)) {
+				/*
+				 * If gateway is not provided when
+				 * multiple paths exist check if it's a cloning
+				 * route and try to match ifp
+				 */
+				if ( (crt->rt_flags & RTF_CLONING) == 0 ||
+				    !(info->rti_ifa)) 
+				    senderr(EINVAL);
+				CLIST_FOREACH(rt, crt, sentinel, rt_list)
+				    if (rt->rt_ifp == info->rti_ifa->ifa_ifp)
+					break;
+				if (rt == NULL)
+				    senderr(EINVAL);
+			}
+		if (CLIST_SINGULAR(rt, rt_list)) {
+			if ((rn = rnh->rnh_deladdr(dst, netmask, rnh)) == NULL)
+				senderr(ESRCH);
+			if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT))
+				panic("rtrequest delete");
+		}
 		if ((rt->rt_flags & RTF_CLONING) != 0) {
 			/* clean up any cloned children */
 			rtflushclone(dst->sa_family, rt);
 		}
-		if ((rn = rnh->rnh_deladdr(dst, netmask, rnh)) == NULL)
-			senderr(ESRCH);
-		if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT))
-			panic ("rtrequest delete");
-		rt = (struct rtentry *)rn;
+		if (rt->rt_nodes->rn_flags & RNF_ROOT)
+			panic("rtrequest delete 2");
 		if (rt->rt_gwroute) {
 			RTFREE(rt->rt_gwroute);
 			rt->rt_gwroute = NULL;
@@ -781,26 +906,60 @@ rtrequest1(int req, struct rt_addrinfo *
 			rt->rt_parent = *ret_nrt;
 			rt->rt_parent->rt_refcnt++;
 		}
+		rt->rt_total = 1;
+		rt->rt_first = rt;
+		rt->rt_last = rt;
+		CLIST_INIT(rt, rt_list);
 		RT_DPRINTF("%s l.%d: rt->_rt_key = %p\n", __func__,
 		    __LINE__, (void *)rt->_rt_key);
 		rn = rnh->rnh_addaddr(rt_getkey(rt), netmask, rnh,
 		    rt->rt_nodes);
 		RT_DPRINTF("%s l.%d: rt->_rt_key = %p\n", __func__,
 		    __LINE__, (void *)rt->_rt_key);
-		if (rn == NULL && (crt = rtalloc1(rt_getkey(rt), 0)) != NULL) {
+		if (rn == NULL &&
+		    ((crt = rtgethead(rt_getkey(rt), NULL)) != NULL) &&
 			/* overwrite cloned route */
-			if ((crt->rt_flags & RTF_CLONED) != 0) {
-				rtdeletemsg(crt);
-				rn = rnh->rnh_addaddr(rt_getkey(rt),
-				    netmask, rnh, rt->rt_nodes);
+		    ((crt->rt_flags & RTF_CLONED) != 0)) {
+			rtdeletemsg(crt);
+			rn = rnh->rnh_addaddr(rt_getkey(rt),
+			    netmask, rnh, rt->rt_nodes);
+			crt = NULL;
+			if (rn == NULL) {
+				error = ENOMEM;
+				goto eexist;
 			}
-			RTFREE(crt);
 			RT_DPRINTF("%s l.%d: rt->_rt_key = %p\n", __func__,
-			    __LINE__, (void *)rt->_rt_key);
+		    	    __LINE__, (void *)rt->_rt_key);
 		}
-		RT_DPRINTF("%s l.%d: rt->_rt_key = %p\n", __func__,
-		    __LINE__, (void *)rt->_rt_key);
-		if (rn == NULL) {
+		else if (req == RTM_ADD && rn == NULL &&
+		    ((crt = rtgethead(rt_getkey(rt), netmask)) != NULL)) {
+			/* New route for the same destination */
+			if (crt->rt_total >= MAX_PATHS) {
+				error = E2BIG;
+				goto eexist;
+			}
+			if (gateway) {
+			    CLIST_FOREACH(nrt, crt, sentinel, rt_list)
+				if (sockaddr_cmp(nrt->rt_gateway, gateway) == 0)
+				    goto eexist;
+			} else if((rt->rt_flags & RTF_CLONING) &&
+				    (info->rti_ifa)) {
+			    CLIST_FOREACH(nrt, crt, sentinel, rt_list)
+				if (nrt->rt_ifp == info->rti_ifa->ifa_ifp)
+				    goto eexist;
+			} else if(rt->rt_flags & RTF_CLONING)
+			    CLIST_FOREACH(nrt, crt, sentinel, rt_list)
+				if (nrt->rt_ifp == rt->rt_ifp)
+				    goto eexist;
+			sockaddr_free(rt->_rt_key);
+			rt->rt_nodes->rn_mask = crt->rt_nodes->rn_mask;
+			rt->_rt_key = crt->_rt_key;
+			rt->rt_first = crt;
+			CLIST_INSERT_AFTER(crt, rt, rt_list);
+			crt->rt_total++;
+			crt = NULL;
+		} else if (rn == NULL) {
+eexist:
 			IFAFREE(ifa);
 			if ((rt->rt_flags & RTF_CLONED) != 0 && rt->rt_parent)
 				rtfree(rt->rt_parent);
@@ -808,7 +967,10 @@ rtrequest1(int req, struct rt_addrinfo *
 				rtfree(rt->rt_gwroute);
 			rt_destroy(rt);
 			pool_put(&rtentry_pool, rt);
-			senderr(EEXIST);
+			if (error)
+				senderr(error)
+			else
+				senderr(EEXIST);
 		}
 		RT_DPRINTF("%s l.%d: rt->_rt_key = %p\n", __func__,
 		    __LINE__, (void *)rt->_rt_key);
@@ -824,7 +986,8 @@ rtrequest1(int req, struct rt_addrinfo *
 			/* clean up any cloned children */
 			rtflushclone(dst->sa_family, rt);
 		}
-		rtflushall(dst->sa_family);
+		if (crt == NULL)
+			rtflushall(dst->sa_family);
 		break;
 	case RTM_GET:
 		if (netmask != NULL) {
@@ -837,6 +1000,7 @@ rtrequest1(int req, struct rt_addrinfo *
 			senderr(ESRCH);
 		if (ret_nrt != NULL) {
 			rt = (struct rtentry *)rn;
+			rt = rtchoosepath_rr(rt);
 			*ret_nrt = rt;
 			rt->rt_refcnt++;
 		}
@@ -944,8 +1108,12 @@ rtinit(struct ifaddr *ifa, int cmd, int 
 			rt_maskedcopy(odst, dst, ifa->ifa_netmask);
 		}
 		if ((rt = rtalloc1(dst, 0)) != NULL) {
+			struct rtentry *sentinel;
 			rt->rt_refcnt--;
-			if (rt->rt_ifa != ifa)
+			CLIST_FOREACH(rt, rt, sentinel, rt_list)
+				if (rt->rt_ifa->ifa_ifp == ifa->ifa_ifp)
+					break;
+			if (rt == NULL)
 				return (flags & RTF_HOST) ? EHOSTUNREACH
 							: ENETUNREACH;
 		}
Index: sys/net/route.h
===================================================================
RCS file: /cvsroot/src/sys/net/route.h,v
retrieving revision 1.58
diff -u -p -r1.58 route.h
--- sys/net/route.h	27 Aug 2007 00:34:01 -0000	1.58
+++ sys/net/route.h	12 Nov 2007 17:14:39 -0000
@@ -93,6 +93,10 @@ struct rt_metrics {
 #ifndef RNF_NORMAL
 #include <net/radix.h>
 #endif
+
+/* XXX: sysctl maybe ? */
+#define MAX_PATHS 64
+
 struct rtentry {
 	struct	radix_node rt_nodes[2];	/* tree glue, and other values */
 #define	rt_mask(r)	((const struct sockaddr *)((r)->rt_nodes->rn_mask))
@@ -108,7 +112,13 @@ struct rtentry {
 	struct	rtentry *rt_gwroute;	/* implied entry for gatewayed routes */
 	LIST_HEAD(, rttimer) rt_timer;  /* queue of timeouts for misc funcs */
 	struct	rtentry *rt_parent;	/* parent of cloned route */
-	struct sockaddr *_rt_key;
+	struct	sockaddr *_rt_key;
+	/* load-sharing */
+	CLIST_ENTRY(rtentry) rt_list;
+	struct	rtentry *rt_first;	/* First entry in list */
+#define	RTFIRST(r)	((r)->rt_first)
+	struct	rtentry *rt_last;	/* For round robin */
+	uint8_t	rt_total;		/* Number of paths */
 };
 
 static inline const struct sockaddr *
@@ -366,6 +376,7 @@ out:
 }
 
 struct rtentry *rtfindparent(struct radix_node_head *, struct route *);
+struct rtentry *rtnext(struct rtentry *);
 
 #ifdef RTCACHE_DEBUG
 #define	rtcache_init(ro)		rtcache_init_debug(__func__, ro)
@@ -386,6 +397,7 @@ void	rtcache_clear(struct route *);
 void	rtcache_update(struct route *, int);
 void	rtcache_free(struct route *);
 int	rtcache_setdst(struct route *, const struct sockaddr *);
+struct rtentry* rtchoosepath_rr(struct rtentry *);
 
 static inline struct rtentry *
 rtcache_lookup1(struct route *ro, const struct sockaddr *dst, int clone)
Index: sys/net/rtsock.c
===================================================================
RCS file: /cvsroot/src/sys/net/rtsock.c,v
retrieving revision 1.95
diff -u -p -r1.95 rtsock.c
--- sys/net/rtsock.c	19 Jul 2007 20:48:53 -0000	1.95
+++ sys/net/rtsock.c	12 Nov 2007 17:14:39 -0000
@@ -306,7 +306,7 @@ route_output(struct mbuf *m, ...)
 		if (rtm->rtm_type != RTM_GET) {/* XXX: too grotty */
 			struct radix_node *rn;
 
-			if (memcmp(dst, rt_getkey(rt), dst->sa_len) != 0)
+			if (sockaddr_cmp(dst, rt_getkey(rt)) != 0)
 				senderr(ESRCH);
 			netmask = intern_netmask(netmask);
 			for (rn = rt->rt_nodes; rn; rn = rn->rn_dupedkey)
@@ -923,6 +923,8 @@ sysctl_dumpentry(struct rtentry *rt, voi
 	int error = 0, size;
 	struct rt_addrinfo info;
 
+	if (CLIST_NEXT(rt, rt_list) != RTFIRST(rt))
+		sysctl_dumpentry(CLIST_NEXT(rt, rt_list), v);
 	if (w->w_op == NET_RT_FLAGS && !(rt->rt_flags & w->w_arg))
 		return 0;
 	memset(&info, 0, sizeof(info));
Index: sys/netinet/if_arp.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/if_arp.c,v
retrieving revision 1.128
diff -u -p -r1.128 if_arp.c
--- sys/netinet/if_arp.c	2 Sep 2007 19:42:22 -0000	1.128
+++ sys/netinet/if_arp.c	12 Nov 2007 17:14:39 -0000
@@ -768,16 +768,18 @@ arpresolve(struct ifnet *ifp, struct rte
 	if (rt->rt_expire) {
 		rt->rt_flags &= ~RTF_REJECT;
 		if (la->la_asked == 0 || rt->rt_expire != time_second) {
+			struct rtentry *nrt, *sentinel;
 			rt->rt_expire = time_second;
 			if (la->la_asked++ < arp_maxtries)
-				arprequest(ifp,
-				    &satocsin(rt->rt_ifa->ifa_addr)->sin_addr,
+			    CLIST_FOREACH(nrt, rt->rt_parent, sentinel, rt_list)
+				arprequest(nrt->rt_ifp,
+				    &satocsin(nrt->rt_ifa->ifa_addr)->sin_addr,
 				    &satocsin(dst)->sin_addr,
 #if NCARP > 0
 				    (rt->rt_ifp->if_type == IFT_CARP) ?
 				    CLLADDR(rt->rt_ifp->if_sadl):
 #endif
-				    CLLADDR(ifp->if_sadl));
+				    CLLADDR(nrt->rt_ifp->if_sadl));
 			else {
 				rt->rt_flags |= RTF_REJECT;
 				rt->rt_expire += arpt_down;
@@ -1097,6 +1099,19 @@ in_arpinput(struct mbuf *m)
 		if (rt->rt_expire)
 			rt->rt_expire = time_second + arpt_keep;
 		rt->rt_flags &= ~RTF_REJECT;
+		if(rt->rt_ifp != ifp) {
+			/*
+			 * Reply came on different interface. Check
+			 * if we have a rt_parent with this ifp
+			 */
+			struct rtentry *nrt, *sentinel;
+			CLIST_FOREACH(nrt, rt->rt_parent, sentinel, rt_list)
+				if(nrt->rt_ifp == ifp) {
+					rt_replace_ifa(rt, nrt->rt_ifa);
+					rt->rt_ifp = nrt->rt_ifp;
+					break;
+				}
+		}
 		la->la_asked = 0;
 
 		s = splnet();
Index: sys/netinet/in.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/in.c,v
retrieving revision 1.118
diff -u -p -r1.118 in.c
--- sys/netinet/in.c	1 Sep 2007 04:32:51 -0000	1.118
+++ sys/netinet/in.c	12 Nov 2007 17:14:39 -0000
@@ -987,7 +987,7 @@ bad:
 
 /*
  * add a route to prefix ("connected route" in cisco terminology).
- * does nothing if there's some interface address with the same prefix already.
+ * does nothing if there's same prefix already assigned to the same interface.
  */
 static int
 in_addprefix(struct in_ifaddr *target, int flags)
@@ -1012,14 +1012,11 @@ in_addprefix(struct in_ifaddr *target, i
 			p.s_addr &= ia->ia_sockmask.sin_addr.s_addr;
 		}
 
-		if (prefix.s_addr != p.s_addr)
+		if (prefix.s_addr != p.s_addr || target->ia_ifp != ia->ia_ifp)
 			continue;
-
 		/*
-		 * if we got a matching prefix route inserted by other
-		 * interface address, we don't need to bother
-		 *
-		 * XXX RADIX_MPATH implications here? -dyoung
+		 * if we got a matching prefix route inserted on the same
+		 * interface, we don't need to bother
 		 */
 		if (ia->ia_flags & IFA_ROUTE)
 			return 0;
Index: sys/netinet/in.h
===================================================================
RCS file: /cvsroot/src/sys/netinet/in.h,v
retrieving revision 1.81
diff -u -p -r1.81 in.h
--- sys/netinet/in.h	19 Sep 2007 04:33:43 -0000	1.81
+++ sys/netinet/in.h	12 Nov 2007 17:14:39 -0000
@@ -450,8 +450,9 @@ struct ip_mreq {
 #define	IPCTL_IFQ	       21	/* ipintrq node */
 #define	IPCTL_RANDOMID	       22	/* use random IP ids (if configured) */
 #define	IPCTL_LOOPBACKCKSUM    23	/* do IP checksum on loopback */
-#define	IPCTL_STATS		24	/* IP statistics */
-#define	IPCTL_MAXID	       25
+#define	IPCTL_STATS	       24	/* IP statistics */
+#define IPCTL_LOAD_SHARING     25	/* Load sharing */
+#define	IPCTL_MAXID	       26
 
 #define	IPCTL_NAMES { \
 	{ 0, 0 }, \
@@ -479,7 +480,13 @@ struct ip_mreq {
 	{ "random_id", CTLTYPE_INT }, \
 	{ "do_loopback_cksum", CTLTYPE_INT }, \
 	{ "stats", CTLTYPE_STRUCT }, \
+	{ "load-sharing", CTLTYPE_NODE }, \
 }
+
+/* Load sharing */
+#define	IPCTL_LS_SELECTED	1
+#define	IPCTL_LS_AVAILABLE	2
+
 #endif /* _NETBSD_SOURCE */
 
 /* INET6 stuff */
Index: sys/netinet/ip_input.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/ip_input.c,v
retrieving revision 1.254
diff -u -p -r1.254 ip_input.c
--- sys/netinet/ip_input.c	2 Oct 2007 20:35:04 -0000	1.254
+++ sys/netinet/ip_input.c	12 Nov 2007 17:14:39 -0000
@@ -218,6 +218,13 @@ int	ip_do_randomid = 0;
  */
 int	ip_checkinterface = 0;
 
+#define INITIAL_LS 2
+#define MAX_LS_STRING 20
+
+/* See also defines in ip_output.c if you want to change these */
+const char* load_sharing_strings[] = { "first-only", "round-robin",
+		"simple-sum", NULL };
+int	load_sharing_index = INITIAL_LS;
 
 struct rttimer_queue *ip_mtudisc_timeout_q = NULL;
 
@@ -2163,6 +2170,45 @@ sysctl_net_inet_ip_hashsize(SYSCTLFN_ARG
 }
 #endif /* GATEWAY */
 
+static int
+sysctl_load_sharing(SYSCTLFN_ARGS)
+{
+	int error, i;
+	struct sysctlnode node = *rnode;
+	char lsc[MAX_LS_STRING];
+
+	strlcpy(lsc, load_sharing_strings[load_sharing_index], MAX_LS_STRING);
+	node.sysctl_data = lsc;
+	error = sysctl_lookup(SYSCTLFN_CALL(&node));
+	if (error || newp == NULL)
+		return error;
+	for (i=0; load_sharing_strings[i] != NULL; i++)
+		if (strncmp(load_sharing_strings[i], lsc, MAX_LS_STRING) == 0)
+			break;
+
+	if (load_sharing_strings[i] == NULL)
+		return EINVAL;
+	load_sharing_index = i;
+	return 0;
+}
+
+static int
+sysctl_ls_types(SYSCTLFN_ARGS)
+{
+	struct sysctlnode node = *rnode;
+	int i;
+	char rt[255];
+
+	rt[0]=0;
+	/* XXX: slow and ugly */
+	for (i=0; load_sharing_strings[i] != NULL; i++) {
+		strlcat(rt, load_sharing_strings[i], 255);
+		if (load_sharing_strings[i+1] != NULL)
+			strlcat(rt, " ", 255);
+		}
+	node.sysctl_data = rt;
+	return sysctl_lookup(SYSCTLFN_CALL(&node));
+}
 
 SYSCTL_SETUP(sysctl_net_inet_ip_setup, "sysctl net.inet.ip subtree setup")
 {
@@ -2370,4 +2416,24 @@ SYSCTL_SETUP(sysctl_net_inet_ip_setup, "
 		       NULL, 0, &ipstat, sizeof(ipstat),
 		       CTL_NET, PF_INET, IPPROTO_IP, IPCTL_STATS,
 		       CTL_EOL);
+	sysctl_createv(clog, 0, NULL, NULL,
+		       CTLFLAG_PERMANENT, CTLTYPE_NODE, "load-sharing",
+		       SYSCTL_DESCR("IP load sharing"),
+		       NULL, 0, NULL, 0, CTL_NET, PF_INET, IPPROTO_IP,
+		       IPCTL_LOAD_SHARING, CTL_EOL);
+	sysctl_createv(clog, 0, NULL, NULL,
+		       CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
+		       CTLTYPE_STRING, "selected",
+		       SYSCTL_DESCR("IP load sharing algorithm"),
+		       sysctl_load_sharing, 0,
+		       &load_sharing_strings[INITIAL_LS],
+		       MAX_LS_STRING - 1,
+		       CTL_NET, PF_INET, IPPROTO_IP,
+		       IPCTL_LOAD_SHARING, IPCTL_LS_SELECTED, CTL_EOL);
+	sysctl_createv(clog, 0, NULL, NULL,
+		       CTLFLAG_PERMANENT, CTLTYPE_STRING, "available",
+		       SYSCTL_DESCR("IP load sharing supported algorithms"),
+		       sysctl_ls_types, 0, NULL, 255, CTL_NET,
+		       PF_INET, IPPROTO_IP, IPCTL_LOAD_SHARING, IPCTL_LS_AVAILABLE,
+		       CTL_EOL);
 }
Index: sys/netinet/ip_output.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/ip_output.c,v
retrieving revision 1.184
diff -u -p -r1.184 ip_output.c
--- sys/netinet/ip_output.c	19 Sep 2007 04:33:43 -0000	1.184
+++ sys/netinet/ip_output.c	12 Nov 2007 17:14:39 -0000
@@ -171,6 +171,16 @@ int	ip_do_loopback_cksum = 0;
 	(((csum_flags) & M_CSUM_TCPv4) != 0 && tcp_do_loopback_cksum) || \
 	(((csum_flags) & M_CSUM_IPv4) != 0 && ip_do_loopback_cksum)))
 
+/* See also string associations in ip_input.c if you want to change these */
+#define LS_NONE 0
+#define LS_RR   1
+#define LS_SS   2
+
+extern int load_sharing_index;
+
+#define tiny_sum(ip4a) ((ip4a >> 24) + (ip4a << 8 >> 24) + \
+			(ip4a << 16 >> 24) + (ip4a << 24 >> 24))
+
 /*
  * IP output.  The packet in mbuf chain m contains a skeletal IP
  * header (with len, off, ttl, proto, tos, src, dst).
@@ -338,13 +348,43 @@ ip_output(struct mbuf *m0, ...)
 		mtu = ifp->if_mtu;
 		IFP_TO_IA(ifp, ia);
 	} else {
-		if (ro->ro_rt == NULL)
+		int ro_cached = 1;
+		if (ro->ro_rt == NULL) {
 			rtcache_init(ro);
+			ro_cached = 0;
+		}
 		if (ro->ro_rt == NULL) {
 			ipstat.ips_noroute++;
 			error = EHOSTUNREACH;
 			goto bad;
 		}
+		/* Load-sharing */
+		if (ro->ro_rt->rt_total > 1 &&
+		    load_sharing_index != LS_NONE &&
+		    !(load_sharing_index == LS_SS && ro_cached)) {
+			ro->ro_rt->rt_refcnt--;
+			switch(load_sharing_index) {
+			    case LS_RR:
+				ro->ro_rt = rtchoosepath_rr(ro->ro_rt);
+				break;
+			    case LS_SS:
+				{
+				uint8_t i, hsh;
+				/* I'm not that happy with this "sum" */
+				hsh = ( tiny_sum(ip->ip_src.s_addr) +
+					tiny_sum(ip->ip_dst.s_addr) +
+					ip->ip_p + ip->ip_tos) %
+						ro->ro_rt->rt_total;
+				/* XXX: Normally it should be up... */
+				if (hsh == 0 && !(ro->ro_rt->rt_flags & RTF_UP))
+					ro->ro_rt = rtnext(ro->ro_rt);
+				else for (i = 0; i < hsh; i++)
+					ro->ro_rt = rtnext(ro->ro_rt);
+				}
+				break;
+			}
+			ro->ro_rt->rt_refcnt++;
+		}
 		ia = ifatoia(ro->ro_rt->rt_ifa);
 		ifp = ro->ro_rt->rt_ifp;
 		if ((mtu = ro->ro_rt->rt_rmx.rmx_mtu) == 0)
Index: sys/sys/queue.h
===================================================================
RCS file: /cvsroot/src/sys/sys/queue.h,v
retrieving revision 1.47
diff -u -p -r1.47 queue.h
--- sys/sys/queue.h	18 Jul 2007 12:07:35 -0000	1.47
+++ sys/sys/queue.h	12 Nov 2007 17:14:39 -0000
@@ -674,4 +674,57 @@ struct {								\
 	    ? ((head)->cqh_last)					\
 	    : (elm->field.cqe_prev))
 
+/*
+ * Circular lists definitions
+ */
+#define	CLIST_ENTRY(__type)		\
+	struct {			\
+		struct __type *cl_next;	\
+		struct __type *cl_prev;	\
+	}
+
+/*
+ * Circular lists functions
+ */
+#define	CLIST_FOREACH1(__elm, __first, __sentinel, __field)	\
+	for ((__elm) = (__sentinel) = (__first); (__elm) != NULL;\
+	     (__elm) = ((__elm)->__field == (__sentinel))	\
+	        ?  NULL						\
+		: (__elm)->__field)
+
+#define	CLIST_FOREACH(__elm, __first, __sentinel, __field)		\
+	CLIST_FOREACH1((__elm), (__first), __sentinel, __field.cl_next)
+
+#define	CLIST_FOREACH_REVERSE(__elm, __first, __sentinel, __field)	\
+	CLIST_FOREACH1((__elm), (__first), __sentinel, __field.cl_prev)
+
+#define	CLIST_INIT(__elm, __field)					\
+	do {								\
+		(__elm)->__field.cl_prev = (__elm)->__field.cl_next =	\
+		    (__elm);		\
+	} while (/*CONSTCOND*/0)
+
+#define	CLIST_SINGULAR(__elm, __field)	((__elm)->__field.cl_prev == (__elm))
+
+#define	CLIST_REMOVE(__elm, __field)				\
+	do {							\
+		(__elm)->__field.cl_prev->__field.cl_next =	\
+		    (__elm)->__field.cl_next;			\
+		(__elm)->__field.cl_next->__field.cl_prev =	\
+		    (__elm)->__field.cl_prev;			\
+		CLIST_INIT((__elm), __field);			\
+	} while (/*CONSTCOND*/0)
+
+#define	CLIST_INSERT_AFTER(__listelm, __elm, __field)			\
+	do {								\
+		assert(__listelm != __elm);				\
+		(__elm)->__field.cl_prev = (__listelm);			\
+		(__elm)->__field.cl_next = (__listelm)->__field.cl_next;\
+		(__listelm)->__field.cl_next = (__elm);			\
+		(__elm)->__field.cl_next->__field.cl_prev = (__elm);	\
+	} while (/*CONSTCOND*/0)
+
+#define	CLIST_NEXT(__elm, __field)	((__elm)->__field.cl_next)
+#define	CLIST_PREV(__elm, __field)	((__elm)->__field.cl_prev)
+
 #endif	/* !_SYS_QUEUE_H_ */
Index: usr.bin/netstat/route.c
===================================================================
RCS file: /cvsroot/src/usr.bin/netstat/route.c,v
retrieving revision 1.69
diff -u -p -r1.69 route.c
--- usr.bin/netstat/route.c	19 Jul 2007 20:51:04 -0000	1.69
+++ usr.bin/netstat/route.c	12 Nov 2007 17:14:39 -0000
@@ -171,6 +171,11 @@ again:
 		} else if (do_rtent) {
 			kget(rn, rtentry);
 			p_krtentry(&rtentry);
+			while ( CLIST_NEXT(&rtentry, rt_list) !=
+			    (struct rtentry*)rn ) {
+				kget(CLIST_NEXT(&rtentry, rt_list), rtentry);
+				p_krtentry(&rtentry);
+			}
 			if (Aflag)
 				p_rtnode();
 		} else {
