Index: sys/kern/kern_jail.c
===================================================================
RCS file: /home/dcvs/src/sys/kern/kern_jail.c,v
retrieving revision 1.13
diff -u -r1.13 kern_jail.c
--- sys/kern/kern_jail.c	27 Oct 2006 04:56:31 -0000	1.13
+++ sys/kern/kern_jail.c	9 Nov 2006 22:16:23 -0000
@@ -28,6 +28,7 @@
 #include <sys/kern_syscall.h>
 #include <net/if.h>
 #include <netinet/in.h>
+#include <netinet6/in6_var.h>
 
 static struct prison	*prison_find(int);
 
@@ -44,7 +45,7 @@
 int	jail_socket_unixiproute_only = 1;
 SYSCTL_INT(_jail, OID_AUTO, socket_unixiproute_only, CTLFLAG_RW,
     &jail_socket_unixiproute_only, 0,
-    "Processes in jail are limited to creating UNIX/IPv4/route sockets only");
+    "Processes in jail are limited to creating UNIX/IPv[46]/route sockets only");
 
 int	jail_sysvipc_allowed = 0;
 SYSCTL_INT(_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW,
@@ -96,18 +97,58 @@
 	struct prison *pr, *tpr;
 	struct jail j;
 	struct thread *td = curthread;
-	int error, tryprid;
+	int error, tryprid, x;
+	uint32_t jversion;
 	struct nlookupdata nd;
+	/* multiip */
+	struct sockaddr_storage *uips; /* userland ips */
+	struct sockaddr_in ip4addr;
+	struct jail_ip_storage *jip;
+	/* multiip */
 
 	error = suser(td);
 	if (error)
 		return(error);
-	error = copyin(uap->jail, &j, sizeof j);
+	error = copyin(uap->jail, &jversion, sizeof jversion);
 	if (error)
 		return(error);
-	if (j.version != 0)
-		return(EINVAL);
-	MALLOC(pr, struct prison *, sizeof *pr , M_PRISON, M_WAITOK | M_ZERO);
+	pr = kmalloc(sizeof *pr , M_PRISON, M_WAITOK | M_ZERO);
+	SLIST_INIT(&pr->pr_ips);
+
+	switch (jversion) {
+	case 0:
+		error = copyin(uap->jail, &j, sizeof(struct jail_v0));
+		if (error)
+			goto bail;
+		jip = kmalloc(sizeof(*jip),  M_PRISON, M_WAITOK | M_ZERO);
+		ip4addr.sin_family = AF_INET;
+		ip4addr.sin_addr.s_addr = htonl(j.ip_number);
+		memcpy(&jip->ip, &ip4addr, sizeof(ip4addr));
+		SLIST_INSERT_HEAD(&pr->pr_ips, jip, entries);
+		break;
+	case 1:
+		error = copyin(uap->jail, &j, sizeof(j));
+		if (error)
+			goto bail;
+		uips = kmalloc((sizeof(*uips) * j.n_ips), M_PRISON,
+				M_WAITOK | M_ZERO);
+		error = copyin(j.ips, uips, (sizeof(*uips) * j.n_ips));
+		if (error) {
+			kfree(uips, M_PRISON);
+			goto bail;
+		}
+		for (x = 0, j.n_ips--; x < j.n_ips; x++) {
+			jip = kmalloc(sizeof(*jip),  M_PRISON,
+				      M_WAITOK | M_ZERO);
+			memcpy(&jip->ip, &uips[x], sizeof(*uips));
+			SLIST_INSERT_HEAD(&pr->pr_ips, jip, entries);
+		}
+		kfree(uips, M_PRISON);
+		break;
+	default:
+		error = EINVAL;
+		goto bail;
+	}
 
 	error = copyinstr(j.hostname, &pr->pr_host, sizeof pr->pr_host, 0);
 	if (error)
@@ -120,7 +161,6 @@
 		goto nlookup_init_clean;
 	cache_copy(&nd.nl_nch, &pr->pr_root);
 
-	pr->pr_ip = j.ip_number;
 	varsymset_init(&pr->pr_varsymset, NULL);
 
 	tryprid = lastprid + 1;
@@ -155,6 +195,12 @@
 nlookup_init_clean:
 	nlookup_done(&nd);
 bail:
+	/* delete all ips */
+	while (!SLIST_EMPTY(&pr->pr_ips)) {
+		jip = SLIST_FIRST(&pr->pr_ips);
+		SLIST_REMOVE_HEAD(&pr->pr_ips, entries);
+		FREE(jip, M_PRISON);
+	}
 	FREE(pr, M_PRISON);
 	return(error);
 }
@@ -175,60 +221,203 @@
 	return(kern_jail_attach(uap->jid));
 }
 
+/* always in network byte order */
 int
-prison_ip(struct thread *td, int flag, u_int32_t *ip)
+prison_ip(int af, struct thread *td, struct sockaddr *ip)
 {
-	u_int32_t tmp;
+	struct sockaddr_in *ip4 = (struct sockaddr_in *)ip;
+	struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)ip;
 	struct prison *pr;
 
 	if (td->td_proc == NULL)
 		return (0);
 	if ((pr = td->td_proc->p_ucred->cr_prison) == NULL)
 		return (0);
-	if (flag)
-		tmp = *ip;
-	else
-		tmp = ntohl(*ip);
-	if (tmp == INADDR_ANY) {
-		if (flag)
-			*ip = pr->pr_ip;
-		else
-			*ip = htonl(pr->pr_ip);
-		return (0);
-	}
-	if (tmp == INADDR_LOOPBACK) {
-		if (flag)
-			*ip = pr->pr_ip;
-		else
-			*ip = htonl(pr->pr_ip);
-		return (0);
+
+	switch (ip->sa_family) {
+	case AF_INET:
+		if (ip4->sin_addr.s_addr == htonl(INADDR_ANY))
+			return(0);
+		if (ip4->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
+			if (prison_glip(AF_INET, pr, ip))
+				return(0);
+			if (prison_gnlip(AF_INET, pr, ip))
+				return(0);
+			/* no ipv4 addresses */
+			return (1);
+		}
+		if (jailed_ip(pr, ip))
+			return(0);
+		break;
+	case AF_INET6:
+		if (IN6_IS_ADDR_UNSPECIFIED(&ip6->sin6_addr))
+			return(0);
+		if (IN6_IS_ADDR_LOOPBACK(&ip6->sin6_addr)) {
+			if (prison_glip(AF_INET6, pr, ip))
+				return(0);
+			if (prison_gnlip(AF_INET6, pr, ip))
+				return(0);
+			/* no ipv6 addresses */
+			return (1);
+		}
+		if (jailed_ip(pr, ip))
+			return(0);
+		break;
 	}
-	if (pr->pr_ip != tmp)
-		return (1);
-	return (0);
+	return(1);
 }
 
-void
-prison_remote_ip(struct thread *td, int flag, u_int32_t *ip)
+int
+prison_remote_ip(int af, struct thread *td, struct sockaddr *ip)
 {
-	u_int32_t tmp;
+	struct sockaddr_in *ip4 = (struct sockaddr_in *)ip;
+	struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)ip;
 	struct prison *pr;
 
 	if (td == NULL || td->td_proc == NULL)
-		return;
+		return(1);
 	if ((pr = td->td_proc->p_ucred->cr_prison) == NULL)
-		return;
-	if (flag)
-		tmp = *ip;
-	else
-		tmp = ntohl(*ip);
-	if (tmp == INADDR_LOOPBACK) {
-		if (flag)
-			*ip = pr->pr_ip;
-		else
-			*ip = htonl(pr->pr_ip);
+		return(1);
+	switch (af) {
+	case AF_INET:
+		if (ip4->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
+			/*
+			 * if we have a loopback address between
+			 * ours, use that
+			 */
+			if (!prison_glip(AF_INET, pr, ip));
+				if (!prison_gnlip(AF_INET, pr, ip))
+					return(0); /* IPv6 only jail */
+		}
+		break;
+	case AF_INET6:
+		if (IN6_IS_ADDR_LOOPBACK(&ip6->sin6_addr)) {
+			/*
+			 * if we have a loopback address between
+			 * ours, use that
+			 */
+			if (!prison_glip(AF_INET6, pr, ip))
+				if (!prison_gnlip(AF_INET6, pr, ip))
+					return(0); /* IPv4 only jail */
+		}
+		break;
 	}
-	return;
+	return(1);
+}
+
+/*
+ * prison get non loopback ip:
+ * put on *ip the first IP address that is not a loopback address
+ * af is the address family of the ip we want (AF_INET|AF_INET6)
+ * ip is in network by order and we don't touch it unless we find a valid ip
+ * return 1 if we've found a non loopback ip, else return 0
+ */
+
+int
+prison_gnlip(int af, struct prison *pr, struct sockaddr *ip)
+{
+	struct jail_ip_storage *jis;
+	struct sockaddr_in *jip4, *ip4;
+	struct sockaddr_in6 *jip6, *ip6;
+
+	ip4 = (struct sockaddr_in *)ip;
+	ip6 = (struct sockaddr_in6 *)ip;
+	SLIST_FOREACH(jis, &pr->pr_ips, entries) {
+		switch (ip->sa_family) {
+		case AF_INET:
+			jip4 = (struct sockaddr_in *) &jis->ip;
+			if (jip4->sin_family == AF_INET &&
+			    !((ntohl(jip4->sin_addr.s_addr) >>
+			       IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)) {
+				ip4->sin_addr.s_addr = jip4->sin_addr.s_addr;
+				return(1);
+			}
+			break;
+		case AF_INET6:
+			jip6 = (struct sockaddr_in6 *) &jis->ip;
+			if ( jip6->sin6_family == AF_INET6 &&
+			     !IN6_IS_ADDR_LOOPBACK(&jip6->sin6_addr)) {
+				ip6->sin6_addr = jip6->sin6_addr;
+				return(1);
+			}
+			break;
+		}
+	}
+	return(0);
+}
+
+/*
+ * prison get loopback ip
+ * put on *ip the first loopback IP address.
+ * af is the address family of the ip we want (AF_INET|PF_INET)
+ * *ip is in network by order and we don't touch it unless we find a valid ip
+ * return 1 if we've found a loopback ip, else return 0
+ */
+
+int
+prison_glip(int af, struct prison *pr, struct sockaddr *ip)
+{
+	struct jail_ip_storage *jis;
+	struct sockaddr_in *jip4, *ip4;
+	struct sockaddr_in6 *jip6, *ip6;
+
+	ip4 = (struct sockaddr_in *)ip;
+	ip6 = (struct sockaddr_in6 *)ip;
+	SLIST_FOREACH(jis, &pr->pr_ips, entries) {
+		switch(ip->sa_family) {
+		case AF_INET:
+			jip4 = (struct sockaddr_in *) &jis->ip;
+			if (jip4->sin_family == AF_INET &&
+			    ((ntohl(jip4->sin_addr.s_addr) >> IN_CLASSA_NSHIFT)
+			    == IN_LOOPBACKNET)) {
+				ip4->sin_addr.s_addr = jip4->sin_addr.s_addr;
+				return(1);
+			}
+			break;
+		case AF_INET6:
+			jip6 = (struct sockaddr_in6 *) &jis->ip;
+			if (jip6->sin6_family == AF_INET6 &&
+			     IN6_IS_ADDR_LOOPBACK(&jip6->sin6_addr)) {
+				ip6->sin6_addr = jip6->sin6_addr;
+				return(1);
+			}
+			break;
+		}
+	}
+	return(0);
+}
+
+/* check if the IP is between ours, if it is return 1, else 0 */
+int
+jailed_ip(struct prison *pr, struct sockaddr *ip)
+{
+	struct jail_ip_storage *jis;
+	struct sockaddr_in *jip4, *ip4;
+	struct sockaddr_in6 *jip6, *ip6;
+
+	if (pr == NULL)
+		return(0);
+	ip4 = (struct sockaddr_in *)ip;
+	ip6 = (struct sockaddr_in6 *)ip;
+	SLIST_FOREACH(jis, &pr->pr_ips, entries) {
+		switch (ip->sa_family) {
+		case AF_INET:
+			jip4 = (struct sockaddr_in *) &jis->ip;
+			if (jip4->sin_family == AF_INET &&
+			    ip4->sin_addr.s_addr == jip4->sin_addr.s_addr)
+				return(1);
+			break;
+		case AF_INET6:
+			jip6 = (struct sockaddr_in6 *) &jis->ip;
+			if (jip6->sin6_family == AF_INET6 &&
+			    IN6_ARE_ADDR_EQUAL(&ip6->sin6_addr,
+					       &jip6->sin6_addr))
+				return(1);
+			break;
+		}
+	}
+	/* ip not in list */
+	return(0);
 }
 
 int
@@ -236,19 +425,17 @@
 {
 	struct prison *pr;
 	struct sockaddr_in *sai = (struct sockaddr_in*) sa;
-	int ok;
 
 	pr = cred->cr_prison;
 
-	if ((sai->sin_family != AF_INET) && jail_socket_unixiproute_only)
-		ok = 1;
-	else if (sai->sin_family != AF_INET)
-		ok = 0;
-	else if (pr->pr_ip != ntohl(sai->sin_addr.s_addr))
-		ok = 1;
-	else
-		ok = 0;
-	return (ok);
+	if (!((sai->sin_family == AF_INET) || (sai->sin_family == AF_INET6))
+	    && jail_socket_unixiproute_only)
+		return(1);
+	else if ((sai->sin_family != AF_INET) && (sai->sin_family != AF_INET6))
+		return(0);
+	else if (jailed_ip(pr,sa))
+		return(0);
+	return(1);
 }
 
 /*
@@ -303,7 +490,7 @@
 			bzero(xp->pr_path, sizeof(xp->pr_path));
 		}
 		strlcpy(xp->pr_host, pr->pr_host, sizeof(xp->pr_host));
-		xp->pr_ip = pr->pr_ip;
+/*		xp->pr_ip = pr->pr_ip; */
 		xp++;
 	}
 
@@ -324,11 +511,18 @@
 void
 prison_free(struct prison *pr)
 {
+	struct jail_ip_storage *jls;
 	KKASSERT(pr->pr_ref >= 1);
 
 	if (--pr->pr_ref > 0)
 		return;
 
+	/* delete all ips */
+	while (!SLIST_EMPTY(&pr->pr_ips)) {
+		jls = SLIST_FIRST(&pr->pr_ips);
+		SLIST_REMOVE_HEAD(&pr->pr_ips, entries);
+		FREE(jls, M_PRISON);
+	}
 	LIST_REMOVE(pr, pr_list);
 	prisoncount--;
 
Index: sys/kern/uipc_socket.c
===================================================================
RCS file: /home/dcvs/src/sys/kern/uipc_socket.c,v
retrieving revision 1.38
diff -u -r1.38 uipc_socket.c
--- sys/kern/uipc_socket.c	13 Jun 2006 08:12:03 -0000	1.38
+++ sys/kern/uipc_socket.c	5 Nov 2006 10:32:05 -0000
@@ -195,6 +195,7 @@
 	if (p->p_ucred->cr_prison && jail_socket_unixiproute_only &&
 	    prp->pr_domain->dom_family != PF_LOCAL &&
 	    prp->pr_domain->dom_family != PF_INET &&
+	    prp->pr_domain->dom_family != PF_INET6 &&
 	    prp->pr_domain->dom_family != PF_ROUTE) {
 		return (EPROTONOSUPPORT);
 	}
Index: sys/netinet/in.h
===================================================================
RCS file: /home/dcvs/src/sys/netinet/in.h,v
retrieving revision 1.13
diff -u -r1.13 in.h
--- sys/netinet/in.h	15 Jul 2005 17:54:47 -0000	1.13
+++ sys/netinet/in.h	5 Nov 2006 10:32:54 -0000
@@ -516,8 +516,8 @@
 int	 in_localaddr (struct in_addr);
 char	*inet_ntoa (struct in_addr); /* in libkern */
 
-int	prison_ip (struct thread *td, int flag, u_int32_t *ip);
-void	prison_remote_ip (struct thread *td, int flag, u_int32_t *ip);
+int	prison_ip (int af, struct thread *td, struct sockaddr *ip);
+int	prison_remote_ip (int af, struct thread *td, struct sockaddr *ip);
 
 #define in_hosteq(s, t)	((s).s_addr == (t).s_addr)
 #define in_nullhost(x)	((x).s_addr == INADDR_ANY)
Index: sys/netinet/in_pcb.c
===================================================================
RCS file: /home/dcvs/src/sys/netinet/in_pcb.c,v
retrieving revision 1.39
diff -u -r1.39 in_pcb.c
--- sys/netinet/in_pcb.c	5 Sep 2006 00:55:48 -0000	1.39
+++ sys/netinet/in_pcb.c	9 Nov 2006 22:16:35 -0000
@@ -254,10 +254,12 @@
 	struct proc *p = td->td_proc;
 	unsigned short *lastport;
 	struct sockaddr_in *sin;
+	struct sockaddr_in jsin;
 	struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
+	struct ucred *cred = NULL;
 	u_short lport = 0;
 	int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
-	int error, prison = 0;
+	int error;
 
 	KKASSERT(p);
 
@@ -267,6 +269,8 @@
 		return (EINVAL);	/* already bound */
 	if (!(so->so_options & (SO_REUSEADDR|SO_REUSEPORT)))
 		wild = 1;    /* neither SO_REUSEADDR nor SO_REUSEPORT is set */
+	if (p && p->p_ucred)
+		cred = p->p_ucred;
 	if (nam != NULL) {
 		sin = (struct sockaddr_in *)nam;
 		if (nam->sa_len != sizeof *sin)
@@ -279,8 +283,7 @@
 		if (sin->sin_family != AF_INET)
 			return (EAFNOSUPPORT);
 #endif
-		if (sin->sin_addr.s_addr != INADDR_ANY &&
-		    prison_ip(td, 0, &sin->sin_addr.s_addr))
+		if (prison_ip(AF_INET, td, nam))
 				return (EINVAL);
 		lport = sin->sin_port;
 		if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
@@ -304,15 +307,13 @@
 
 			/* GROSS */
 			if (ntohs(lport) < IPPORT_RESERVED &&
-			    p && suser_cred(p->p_ucred, PRISON_ROOT))
+			    cred && suser_cred(cred, PRISON_ROOT))
 				return (EACCES);
-			if (p && p->p_ucred->cr_prison)
-				prison = 1;
 			if (so->so_cred->cr_uid != 0 &&
 			    !IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
-				t = in_pcblookup_local(inp->inp_pcbinfo,
+				t = in_pcblookup_local(cred, inp->inp_pcbinfo,
 				    sin->sin_addr, lport,
-				    prison ? 0 : INPLOOKUP_WILDCARD);
+				    INPLOOKUP_WILDCARD);
 				if (t &&
 				    (!in_nullhost(sin->sin_addr) ||
 				     !in_nullhost(t->inp_laddr) ||
@@ -329,10 +330,10 @@
 					return (EADDRINUSE);
 				}
 			}
-			if (prison && prison_ip(td, 0, &sin->sin_addr.s_addr))
+			if (cred && prison_ip(AF_INET, td, nam))
 				return (EADDRNOTAVAIL);
-			t = in_pcblookup_local(pcbinfo, sin->sin_addr,
-			    lport, prison ? 0 : wild);
+			t = in_pcblookup_local(cred, pcbinfo, sin->sin_addr,
+					       lport, wild);
 			if (t && !(reuseport & t->inp_socket->so_options)) {
 #ifdef INET6
 				if (!in_nullhost(sin->sin_addr) ||
@@ -347,11 +348,14 @@
 	if (lport == 0) {
 		ushort first, last;
 		int count;
-
-		if (inp->inp_laddr.s_addr != INADDR_ANY &&
-		    prison_ip(td, 0, &inp->inp_laddr.s_addr )) {
-			inp->inp_laddr.s_addr = INADDR_ANY;
-			return (EINVAL);
+		{ /* jail stuff */
+			jsin.sin_family = AF_INET;
+			jsin.sin_addr.s_addr = inp->inp_laddr.s_addr;
+			if (prison_ip(AF_INET, td, (struct sockaddr *)&jsin)) {
+				inp->inp_laddr.s_addr = INADDR_ANY;
+				return (EINVAL);
+			}
+			inp->inp_laddr.s_addr = jsin.sin_addr.s_addr;
 		}
 		inp->inp_flags |= INP_ANONPORT;
 
@@ -360,8 +364,8 @@
 			last  = ipport_hilastauto;
 			lastport = &pcbinfo->lasthi;
 		} else if (inp->inp_flags & INP_LOWPORT) {
-			if (p &&
-			    (error = suser_cred(p->p_ucred, PRISON_ROOT))) {
+			if (cred &&
+			    (error = suser_cred(cred, PRISON_ROOT))) {
 				inp->inp_laddr.s_addr = INADDR_ANY;
 				return (error);
 			}
@@ -395,7 +399,7 @@
 				if (*lastport > first || *lastport < last)
 					*lastport = first;
 				lport = htons(*lastport);
-			} while (in_pcblookup_local(pcbinfo,
+			} while (in_pcblookup_local(cred, pcbinfo,
 				 inp->inp_laddr, lport, wild));
 		} else {
 			/*
@@ -412,15 +416,20 @@
 				if (*lastport < first || *lastport > last)
 					*lastport = first;
 				lport = htons(*lastport);
-			} while (in_pcblookup_local(pcbinfo,
+			} while (in_pcblookup_local(cred, pcbinfo,
 				 inp->inp_laddr, lport, wild));
 		}
 	}
 	inp->inp_lport = lport;
-	if (prison_ip(td, 0, &inp->inp_laddr.s_addr)) {
-		inp->inp_laddr.s_addr = INADDR_ANY;
-		inp->inp_lport = 0;
-		return (EINVAL);
+	{ /* jail stuff */
+		jsin.sin_family = AF_INET;
+		jsin.sin_addr.s_addr = inp->inp_laddr.s_addr;
+		if (prison_ip(AF_INET, td, (struct sockaddr*)&jsin)) {
+			inp->inp_laddr.s_addr = INADDR_ANY;
+			inp->inp_lport = 0;
+			return (EINVAL);
+		}
+		inp->inp_laddr.s_addr = jsin.sin_addr.s_addr;
 	}
 	if (in_pcbinsporthash(inp) != 0) {
 		inp->inp_laddr.s_addr = INADDR_ANY;
@@ -483,7 +492,7 @@
 		    (!(ro->ro_rt->rt_flags & RTF_UP) ||
 		     ro->ro_dst.sa_family != AF_INET ||
 		     satosin(&ro->ro_dst)->sin_addr.s_addr !=
-				      sin->sin_addr.s_addr ||
+				      sin->sin_addr.s_addr ||		    
 		     inp->inp_socket->so_options & SO_DONTROUTE)) {
 			RTFREE(ro->ro_rt);
 			ro->ro_rt = (struct rtentry *)NULL;
@@ -566,16 +575,6 @@
 	struct ucred *cr = td->td_proc ? td->td_proc->p_ucred : NULL;
 	int error;
 
-	if (cr && cr->cr_prison != NULL && in_nullhost(inp->inp_laddr)) {
-		bzero(&sa, sizeof sa);
-		sa.sin_addr.s_addr = htonl(cr->cr_prison->pr_ip);
-		sa.sin_len = sizeof sa;
-		sa.sin_family = AF_INET;
-		error = in_pcbbind(inp, (struct sockaddr *)&sa, td);
-		if (error)
-			return (error);
-	}
-
 	/* Call inner routine to assign local interface address. */
 	if ((error = in_pcbladdr(inp, nam, &if_sin)) != 0)
 		return (error);
@@ -814,8 +813,8 @@
  * Lookup a PCB based on the local address and port.
  */
 struct inpcb *
-in_pcblookup_local(struct inpcbinfo *pcbinfo, struct in_addr laddr, u_int lport_arg,
-		   int wild_okay)
+in_pcblookup_local(struct ucred *cred, struct inpcbinfo *pcbinfo, struct in_addr laddr,
+		    u_int lport_arg, int wild_okay)
 {
 	struct inpcb *inp;
 	int matchwild = 3, wildcard;
@@ -862,7 +861,14 @@
 			if (wildcard && !wild_okay)
 				continue;
 			if (wildcard < matchwild) {
-				match = inp;
+				if (inp->inp_socket == NULL || cred == NULL) {
+					match = inp;
+				} else if (inp->inp_socket->so_cred->cr_prison\
+					   == cred->cr_prison) {
+					match = inp;
+				} else {
+					continue;
+				}
 				matchwild = wildcard;
 				if (matchwild == 0) {
 					break;
@@ -882,7 +888,7 @@
 		  struct ifnet *ifp)
 {
 	struct inpcbhead *head;
-	struct inpcb *inp;
+	struct inpcb *inp, *jinp=NULL;
 	u_short fport = fport_arg, lport = lport_arg;
 
 	/*
@@ -899,32 +905,67 @@
 		    in_hosteq(inp->inp_laddr, laddr) &&
 		    inp->inp_fport == fport && inp->inp_lport == lport) {
 			/* found */
-			return (inp);
+			if (inp->inp_socket == NULL ||
+			inp->inp_socket->so_cred->cr_prison == NULL) {
+				return (inp);
+			} else {
+				if  (jinp == NULL)
+					jinp = inp;
+			}
 		}
 	}
-
+	if (jinp != NULL)
+		return (jinp);
 	if (wildcard) {
 		struct inpcb *local_wild = NULL;
+		struct inpcb *jinp_wild = NULL;
 #ifdef INET6
 		struct inpcb *local_wild_mapped = NULL;
 #endif
 		struct inpcontainer *ic;
 		struct inpcontainerhead *chead;
+		struct sockaddr_in jsin;
+		struct ucred *cred;
 
+		/*
+		 * Order of socket selection:
+		 * 1. non-jailed, non-wild.
+		 * 2. non-jailed, wild.
+		 * 3. jailed, non-wild.
+		 * 4. jailed, wild.
+		 */
+		jsin.sin_family = AF_INET;
 		chead = &pcbinfo->wildcardhashbase[
 		    INP_PCBWILDCARDHASH(lport, pcbinfo->wildcardhashmask)];
 		LIST_FOREACH(ic, chead, ic_list) {
 			inp = ic->ic_inp;
+			jsin.sin_addr.s_addr = laddr.s_addr;
 #ifdef INET6
 			if (!(inp->inp_vflag & INP_IPV4))
 				continue;
 #endif
+			if (inp->inp_socket != NULL)
+				cred = inp->inp_socket->so_cred;
+			else
+				cred = NULL;
+			if (cred != NULL && jailed(cred)) {
+				if (jinp != NULL)
+					continue;
+				else
+					if (!jailed_ip(cred->cr_prison,
+					    (struct sockaddr *)&jsin))
+						continue;
+			}
 			if (inp->inp_lport == lport) {
 				if (ifp && ifp->if_type == IFT_FAITH &&
 				    !(inp->inp_flags & INP_FAITH))
 					continue;
-				if (inp->inp_laddr.s_addr == laddr.s_addr)
-					return (inp);
+				if (inp->inp_laddr.s_addr == laddr.s_addr) {
+					if (cred != NULL && jailed(cred))
+						jinp = inp;
+					else
+						return (inp);
+				}
 				if (inp->inp_laddr.s_addr == INADDR_ANY) {
 #ifdef INET6
 					if (INP_CHECK_SOCKAF(inp->inp_socket,
@@ -932,15 +973,23 @@
 						local_wild_mapped = inp;
 					else
 #endif
-						local_wild = inp;
+						if (cred != NULL &&
+						    jailed(cred))
+							jinp_wild = inp;
+						else
+							local_wild = inp;
 				}
 			}
 		}
+		if (local_wild != NULL)
+			return (local_wild);
 #ifdef INET6
-		if (local_wild == NULL)
+		if (local_wild_mapped != NULL)
 			return (local_wild_mapped);
 #endif
-		return (local_wild);
+		if (jinp != NULL)
+			return (jinp);
+		return (jinp_wild);
 	}
 
 	/*
@@ -1127,7 +1176,9 @@
 	cr = td->td_proc->p_ucred;
 	if (cr->cr_prison == NULL)
 		return (0);
-	if (ntohl(inp->inp_laddr.s_addr) == cr->cr_prison->pr_ip)
+	if (inp->inp_socket && inp->inp_socket->so_cred &&
+	    inp->inp_socket->so_cred->cr_prison &&
+	    cr->cr_prison == inp->inp_socket->so_cred->cr_prison)
 		return (0);
 	return (1);
 }
Index: sys/netinet/in_pcb.h
===================================================================
RCS file: /home/dcvs/src/sys/netinet/in_pcb.h,v
retrieving revision 1.20
diff -u -r1.20 in_pcb.h
--- sys/netinet/in_pcb.h	20 May 2006 02:42:12 -0000	1.20
+++ sys/netinet/in_pcb.h	6 Nov 2006 00:09:38 -0000
@@ -397,7 +397,7 @@
 int	in_pcbladdr (struct inpcb *, struct sockaddr *,
 	    struct sockaddr_in **);
 struct inpcb *
-	in_pcblookup_local (struct inpcbinfo *,
+	in_pcblookup_local (struct ucred *, struct inpcbinfo *,
 	    struct in_addr, u_int, int);
 struct inpcb *
 	in_pcblookup_hash (struct inpcbinfo *,
Index: sys/netinet/tcp_usrreq.c
===================================================================
RCS file: /home/dcvs/src/sys/netinet/tcp_usrreq.c,v
retrieving revision 1.38
diff -u -r1.38 tcp_usrreq.c
--- sys/netinet/tcp_usrreq.c	5 Sep 2006 00:55:48 -0000	1.38
+++ sys/netinet/tcp_usrreq.c	9 Nov 2006 22:16:40 -0000
@@ -473,7 +473,10 @@
 		goto out;
 	}
 
-	prison_remote_ip(td, 0, &sinp->sin_addr.s_addr);
+	if (!prison_remote_ip(AF_INET, td, (struct sockaddr*)sinp)) {
+		error = EAFNOSUPPORT; /* IPv6 only jail */
+		goto out;
+	}
 
 	if ((error = tcp_connect(tp, nam, td)) != 0)
 		goto out;
@@ -502,6 +505,11 @@
 		goto out;
 	}
 
+	if (!prison_remote_ip(AF_INET6, td, nam)) {
+		error = EAFNOSUPPORT; /* IPv4 only jail */
+		goto out;
+	}
+
 	if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) {
 		struct sockaddr_in sin;
 
Index: sys/netinet/udp_usrreq.c
===================================================================
RCS file: /home/dcvs/src/sys/netinet/udp_usrreq.c,v
retrieving revision 1.36
diff -u -r1.36 udp_usrreq.c
--- sys/netinet/udp_usrreq.c	14 Jan 2006 11:33:50 -0000	1.36
+++ sys/netinet/udp_usrreq.c	9 Nov 2006 22:16:45 -0000
@@ -704,7 +704,10 @@
 			goto release;
 		}
 		sin = (struct sockaddr_in *)dstaddr;
-		prison_remote_ip(td, 0, &sin->sin_addr.s_addr);
+		if (!prison_remote_ip(AF_INET, td, (struct sockaddr *)&sin)) {
+			error = EAFNOSUPPORT; /* IPv6 only jail */
+			goto release;
+		}
 	} else {
 		if (inp->inp_faddr.s_addr == INADDR_ANY) {
 			/* no destination specified and not already connected */
@@ -879,7 +882,6 @@
 {
 	struct inpcb *inp;
 	int error;
-	struct sockaddr_in *sin;
 
 	inp = so->so_pcb;
 	if (inp == NULL)
@@ -893,8 +895,8 @@
 		error = in_pcbbind(inp, NULL, td);
 	}
 	if (error == 0) {
-		sin = (struct sockaddr_in *)nam;
-		prison_remote_ip(td, 0, &sin->sin_addr.s_addr);
+		if (!prison_remote_ip(AF_INET, td, nam))
+			return(EAFNOSUPPORT); /* IPv6 only jail */
 		if (inp->inp_flags & INP_WILDCARD)
 			in_pcbremwildcardhash(inp);
 		error = in_pcbconnect(inp, nam, td);
Index: sys/netinet6/in6_pcb.c
===================================================================
RCS file: /home/dcvs/src/sys/netinet6/in6_pcb.c,v
retrieving revision 1.31
diff -u -r1.31 in6_pcb.c
--- sys/netinet6/in6_pcb.c	24 Oct 2006 06:18:42 -0000	1.31
+++ sys/netinet6/in6_pcb.c	9 Nov 2006 22:16:54 -0000
@@ -128,7 +128,10 @@
 {
 	struct socket *so = inp->inp_socket;
 	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)NULL;
+	struct sockaddr_in6 jsin6;
 	struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
+	struct proc *p = td->td_proc;
+	struct ucred *cred = NULL;
 	u_short	lport = 0;
 	int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
 
@@ -138,6 +141,8 @@
 		return (EINVAL);
 	if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0)
 		wild = 1;
+	if (p && p->p_ucred)
+		cred = p->p_ucred;
 	if (nam) {
 		sin6 = (struct sockaddr_in6 *)nam;
 		if (nam->sa_len != sizeof(*sin6))
@@ -148,6 +153,9 @@
 		if (nam->sa_family != AF_INET6)
 			return (EAFNOSUPPORT);
 
+		if (prison_ip(AF_INET6, td, nam))
+			return (EINVAL);
+
 		/* KAME hack: embed scopeid */
 		if (in6_embedscope(&sin6->sin6_addr, sin6, inp, NULL) != 0)
 			return EINVAL;
@@ -169,6 +177,10 @@
 			struct ifaddr *ia = NULL;
 
 			sin6->sin6_port = 0;		/* yech... */
+			if (prison_ip(AF_INET6, td, (struct sockaddr *)sin6)) {
+				sin6->sin6_addr = kin6addr_any;
+				return(EINVAL);
+			}
 			if ((ia = ifa_ifwithaddr((struct sockaddr *)sin6)) == 0)
 				return (EADDRNOTAVAIL);
 
@@ -186,15 +198,14 @@
 		}
 		if (lport) {
 			struct inpcb *t;
-			struct proc *p = td->td_proc; /* may be NULL */
 
 			/* GROSS */
-			if (ntohs(lport) < IPV6PORT_RESERVED && p &&
-			    suser_cred(p->p_ucred, PRISON_ROOT))
+			if (ntohs(lport) < IPV6PORT_RESERVED && cred &&
+			    suser_cred(cred, PRISON_ROOT))
 				return (EACCES);
 			if (so->so_cred->cr_uid != 0 &&
 			    !IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
-				t = in6_pcblookup_local(pcbinfo,
+				t = in6_pcblookup_local(cred, pcbinfo,
 				    &sin6->sin6_addr, lport,
 				    INPLOOKUP_WILDCARD);
 				if (t &&
@@ -210,7 +221,7 @@
 					struct sockaddr_in sin;
 
 					in6_sin6_2_sin(&sin, sin6);
-					t = in_pcblookup_local(pcbinfo,
+					t = in_pcblookup_local(cred, pcbinfo,
 						sin.sin_addr, lport,
 						INPLOOKUP_WILDCARD);
 					if (t &&
@@ -223,7 +234,10 @@
 						return (EADDRINUSE);
 				}
 			}
-			t = in6_pcblookup_local(pcbinfo, &sin6->sin6_addr,
+			if (cred && cred->cr_prison &&
+			    prison_ip(AF_INET6, td, nam))
+				return(EADDRNOTAVAIL);
+			t = in6_pcblookup_local(cred, pcbinfo, &sin6->sin6_addr,
 						lport, wild);
 			if (t && (reuseport & t->inp_socket->so_options) == 0)
 				return (EADDRINUSE);
@@ -232,8 +246,9 @@
 				struct sockaddr_in sin;
 
 				in6_sin6_2_sin(&sin, sin6);
-				t = in_pcblookup_local(pcbinfo, sin.sin_addr,
-						       lport, wild);
+				t = in_pcblookup_local(cred, pcbinfo,
+						       sin.sin_addr, lport,
+						       wild);
 				if (t &&
 				    (reuseport & t->inp_socket->so_options)
 				    == 0 &&
@@ -248,6 +263,18 @@
 	}
 	if (lport == 0) {
 		int e;
+
+		{ /* jail stuff */
+			jsin6.sin6_addr = inp->in6p_laddr;
+			jsin6.sin6_family = AF_INET6;
+			if (prison_ip(AF_INET6, td,
+			    (struct sockaddr*)&jsin6)) {
+				inp->in6p_laddr = kin6addr_any;
+				inp->inp_lport = 0;
+				return (EINVAL);
+			}
+		}/* jail stuff */
+
 		if ((e = in6_pcbsetport(&inp->in6p_laddr, inp, td)) != 0)
 			return (e);
 	}
@@ -341,6 +368,8 @@
 {
 	struct in6_addr *addr6;
 	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam;
+	struct sockaddr_in6 jsin6;
+	struct ucred *cr = td->td_proc ? td->td_proc->p_ucred : NULL;
 	int error;
 
 	/*
@@ -831,8 +860,8 @@
  * Lookup a PCB based on the local address and port.
  */
 struct inpcb *
-in6_pcblookup_local(struct inpcbinfo *pcbinfo, struct in6_addr *laddr,
-		    u_int lport_arg, int wild_okay)
+in6_pcblookup_local(struct ucred *cred, struct inpcbinfo *pcbinfo,
+		    struct in6_addr *laddr, u_int lport_arg, int wild_okay)
 {
 	struct inpcb *inp;
 	int matchwild = 3, wildcard;
@@ -877,7 +906,14 @@
 			if (wildcard && !wild_okay)
 				continue;
 			if (wildcard < matchwild) {
-				match = inp;
+				if (inp->inp_socket == NULL || cred == NULL) {
+					match = inp;
+				} else if (inp->inp_socket->so_cred->cr_prison\
+					   == cred->cr_prison) {
+					match = inp;
+				} else {
+					continue;
+				}
 				if (wildcard == 0)
 					break;
 				else
@@ -984,6 +1020,7 @@
 {
 	struct inpcbhead *head;
 	struct inpcb *inp;
+	struct inpcb *jinp = NULL;
 	u_short fport = fport_arg, lport = lport_arg;
 	int faith;
 
@@ -1010,14 +1047,33 @@
 			/*
 			 * Found.
 			 */
-			return (inp);
+			if (inp->inp_socket == NULL ||
+			inp->inp_socket->so_cred->cr_prison == NULL) {
+				return (inp);
+			} else {
+				if  (jinp == NULL)
+					jinp = inp;
+			}
 		}
 	}
+	if (jinp != NULL)
+		return(jinp);
 	if (wildcard) {
 		struct inpcontainerhead *chead;
 		struct inpcontainer *ic;
 		struct inpcb *local_wild = NULL;
+		struct inpcb *jinp_wild = NULL;
+		struct sockaddr_in6 jsin6;
+		struct ucred *cred;
 
+		/*
+		 * Order of socket selection:
+		 * 1. non-jailed, non-wild.
+		 * 2. non-jailed, wild.
+		 * 3. jailed, non-wild.
+		 * 4. jailed, wild.
+		 */
+		jsin6.sin6_family = AF_INET6;
 		chead = &pcbinfo->wildcardhashbase[INP_PCBWILDCARDHASH(lport,
 		    pcbinfo->wildcardhashmask)];
 		LIST_FOREACH(ic, chead, ic_list) {
@@ -1025,18 +1081,44 @@
 
 			if (!(inp->inp_vflag & INP_IPV6))
 				continue;
+			if (inp->inp_socket != NULL)
+				cred = inp->inp_socket->so_cred;
+			else
+				cred = NULL;
+
+			if (cred != NULL && jailed(cred)) {
+				if (jinp != NULL) {
+					continue;
+				} else {
+		                        jsin6.sin6_addr = *laddr;
+					if (!jailed_ip(cred->cr_prison,
+					    (struct sockaddr *)&jsin6))
+						continue;
+				}
+			}
 			if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) &&
 			    inp->inp_lport == lport) {
 				if (faith && (inp->inp_flags & INP_FAITH) == 0)
 					continue;
 				if (IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr,
-						       laddr))
-					return (inp);
-				else if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr))
-					local_wild = inp;
+						       laddr)) {
+					if (cred != NULL && jailed(cred))
+						jinp = inp;
+					else
+						return (inp);
+				} else if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) {
+					if (cred != NULL && jailed(cred))
+						jinp_wild = inp;
+					else
+						local_wild = inp;
+				}
 			}
 		}
-		return (local_wild);
+		if (local_wild != NULL)
+			return (local_wild);
+		if (jinp != NULL)
+			return (jinp);
+		return (jinp_wild);
 	}
 
 	/*
Index: sys/netinet6/in6_pcb.h
===================================================================
RCS file: /home/dcvs/src/sys/netinet6/in6_pcb.h,v
retrieving revision 1.8
diff -u -r1.8 in6_pcb.h
--- sys/netinet6/in6_pcb.h	20 May 2006 02:42:12 -0000	1.8
+++ sys/netinet6/in6_pcb.h	5 Nov 2006 23:23:42 -0000
@@ -105,7 +105,7 @@
 int	in6_pcbladdr (struct inpcb *, struct sockaddr *,
 			  struct in6_addr **);
 struct	inpcb *
-	in6_pcblookup_local (struct inpcbinfo *,
+	in6_pcblookup_local (struct ucred *cred, struct inpcbinfo *,
 				 struct in6_addr *, u_int, int);
 struct	inpcb *
 	in6_pcblookup_hash (struct inpcbinfo *,
Index: sys/netinet6/in6_src.c
===================================================================
RCS file: /home/dcvs/src/sys/netinet6/in6_src.c,v
retrieving revision 1.12
diff -u -r1.12 in6_src.c
--- sys/netinet6/in6_src.c	24 Oct 2006 06:18:42 -0000	1.12
+++ sys/netinet6/in6_src.c	5 Nov 2006 23:26:12 -0000
@@ -332,10 +332,13 @@
 	u_int16_t lport = 0, first, last, *lastport;
 	int count, error = 0, wild = 0;
 	struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
+	struct ucred *cred;
 
 	/* XXX: this is redundant when called from in6_pcbbind */
 	if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0)
 		wild = INPLOOKUP_WILDCARD;
+	if (td->td_proc && td->td_proc->p_ucred)
+		cred = td->td_proc->p_ucred;
 
 	inp->inp_flags |= INP_ANONPORT;
 
@@ -380,7 +383,7 @@
 			if (*lastport > first || *lastport < last)
 				*lastport = first;
 			lport = htons(*lastport);
-		} while (in6_pcblookup_local(pcbinfo,
+		} while (in6_pcblookup_local(cred, pcbinfo,
 					     &inp->in6p_laddr, lport, wild));
 	} else {
 		/*
@@ -401,7 +404,7 @@
 			if (*lastport < first || *lastport > last)
 				*lastport = first;
 			lport = htons(*lastport);
-		} while (in6_pcblookup_local(pcbinfo,
+		} while (in6_pcblookup_local(cred, pcbinfo,
 					     &inp->in6p_laddr, lport, wild));
 	}
 
Index: sys/netinet6/udp6_output.c
===================================================================
RCS file: /home/dcvs/src/sys/netinet6/udp6_output.c,v
retrieving revision 1.7
diff -u -r1.7 udp6_output.c
--- sys/netinet6/udp6_output.c	24 Oct 2006 06:18:42 -0000	1.7
+++ sys/netinet6/udp6_output.c	5 Nov 2006 10:33:01 -0000
@@ -164,6 +164,10 @@
 			error = EISCONN;
 			goto release;
 		}
+		if (!prison_remote_ip(AF_INET6, td, (struct sockaddr *)addr6)) {
+			error = EAFNOSUPPORT; /* IPv4 only jail */
+			goto release;
+		}
 
 		/* protect *sin6 from overwrites */
 		tmp = *sin6;
Index: sys/netinet6/udp6_usrreq.c
===================================================================
RCS file: /home/dcvs/src/sys/netinet6/udp6_usrreq.c,v
retrieving revision 1.22
diff -u -r1.22 udp6_usrreq.c
--- sys/netinet6/udp6_usrreq.c	24 Oct 2006 06:18:42 -0000	1.22
+++ sys/netinet6/udp6_usrreq.c	5 Nov 2006 10:33:01 -0000
@@ -649,6 +649,8 @@
 		return EISCONN;
 	if (inp->inp_flags & INP_WILDCARD)
 		in_pcbremwildcardhash(inp);
+	if (!prison_remote_ip(AF_INET6, td, nam))
+		return(EAFNOSUPPORT); /* IPv4 only jail */
 	crit_enter();
 	error = in6_pcbconnect(inp, nam, td);
 	crit_exit();
Index: sys/sys/jail.h
===================================================================
RCS file: /home/dcvs/src/sys/sys/jail.h,v
retrieving revision 1.8
diff -u -r1.8 jail.h
--- sys/sys/jail.h	27 Oct 2006 04:56:33 -0000	1.8
+++ sys/sys/jail.h	5 Nov 2006 10:33:31 -0000
@@ -20,14 +20,29 @@
 #ifndef _SYS_PARAM_H_
 #include <sys/param.h>
 #endif
+#ifndef _SYS_QUEUE_H_
+#include <sys/queue.h>
+#endif
 #ifndef _SYS_UCRED_H_
 #include <sys/ucred.h>
 #endif
+#ifndef _SYS_IF_H_
+#include <net/if.h>
+#endif
 
 struct jail {
 	uint32_t	version;
 	char		*path;
 	char		*hostname;
+	uint32_t	ip_number; /* not used on version == 1 */
+	uint32_t	n_ips;     /* number of ips */
+	struct sockaddr_storage *ips;
+};
+
+struct jail_v0 {
+	uint32_t	version;
+	char		*path;
+	char		*hostname;
 	uint32_t	ip_number;
 };
 
@@ -51,6 +66,13 @@
 
 #define	JAIL_MAX	999999
 
+/* used to store the IPs of the jail */
+
+struct jail_ip_storage {
+	SLIST_ENTRY(jail_ip_storage) entries;
+	struct sockaddr_storage ip;
+};
+
 /*
  * This structure describes a prison.  It is pointed to by all struct
  * proc's of the inmates.  pr_ref keeps track of them and is used to
@@ -63,7 +85,7 @@
 	int		pr_ref;				/* reference count */
 	struct nchandle pr_root;			/* namecache entry of root */
 	char 		pr_host[MAXHOSTNAMELEN];	/* host name */
-	uint32_t	pr_ip;				/* IP address */
+	SLIST_HEAD(iplist, jail_ip_storage) pr_ips;	/* list of IP addresses */
 	void		*pr_linux;			/* Linux ABI emulation */
 	int		 pr_securelevel;		/* jail securelevel */
 	struct varsymset pr_varsymset;			/* jail varsyms */
@@ -79,6 +101,9 @@
 
 void	prison_hold(struct prison *);
 void	prison_free(struct prison *);
+int	jailed_ip(struct prison *, struct sockaddr *);
+int	prison_glip(int af, struct prison *pr, struct sockaddr *);
+int	prison_gnlip(int af, struct prison *pr, struct sockaddr *);
 
 /*
  * Return 1 if the passed credential is in a jail, otherwise 0.
Index: usr.sbin/jail/jail.8
===================================================================
RCS file: /home/dcvs/src/usr.sbin/jail/jail.8,v
retrieving revision 1.5
diff -u -r1.5 jail.8
--- usr.sbin/jail/jail.8	9 Sep 2006 16:22:52 -0000	1.5
+++ usr.sbin/jail/jail.8	9 Nov 2006 16:32:58 -0000
@@ -43,7 +43,7 @@
 .Sh SYNOPSIS
 .Nm
 .Op Fl u Ar username
-.Ar path hostname ip-number command ...
+.Ar path hostname ip-list command ...
 .Sh DESCRIPTION
 The
 .Nm
@@ -59,8 +59,8 @@
 Directory which is to be the root of the prison.
 .It Ar hostname
 Hostname of the prison.
-.It Ar ip-number
-IP number assigned to the prison.
+.It Ar ip-list
+Comma separated IP list assigned to the prison.
 .It Ar command
 Pathname of the program which is to be executed.
 .El
@@ -105,9 +105,9 @@
 For the sake of this example, we will
 assume you built it in
 .Pa /data/jail/192.168.11.100 ,
-named for the jailed IP address.
+named for the external jail IP address.
 Substitute below as needed with your
-own directory, IP address, and hostname.
+own directory, IP addresses, and hostname.
 .Pp
 First, you will want to set up your real system's environment to be
 .Dq jail-friendly .
@@ -184,7 +184,7 @@
 .Pp
 Now start the jail:
 .Pp
-.Dl "jail /data/jail/192.168.11.100 testhostname 192.168.11.100 /bin/sh"
+.Dl "jail /data/jail/192.168.11.100 testhostname 127.0.0.1,192.168.11.100 /bin/sh"
 .Pp
 You will end up with a shell prompt, assuming no errors, within the jail.
 You can now do the post-install configuration to set various configuration
@@ -250,7 +250,7 @@
 .Bd -literal -offset indent
 ifconfig ed0 inet alias 192.168.11.100/32
 mount -t procfs proc /data/jail/192.168.11.100/proc
-jail /data/jail/192.168.11.100 testhostname 192.168.11.100 \\
+jail /data/jail/192.168.11.100 testhostname 127.0.0.1,192.168.11.100 \\
 	/bin/sh /etc/rc
 .Ed
 .Pp
@@ -354,20 +354,20 @@
 As such, this should be disabled in environments where privileged access to
 jails is given out to untrusted parties.
 .It Va jail.socket_unixiproute_only
-The jail functionality binds an IPv4 address to each jail, and limits
-access to other network addresses in the IPv4 space that may be available
+The jail functionality binds IPv4 and IPv6 addresses to each jail, and limits
+access to other network addresses in the IPv4 and IPv6 space that may be available
 in the host environment.
 However, jail is not currently able to limit access to other network
 protocol stacks that have not had jail functionality added to them.
 As such, by default, processes within jails may only access protocols
 in the following domains:
-.Dv PF_LOCAL , PF_INET ,
+.Dv PF_LOCAL , PF_INET , PF_INET6 ,
 and
 .Dv PF_ROUTE ,
 permitting them access to
 .Ux
 domain sockets,
-IPv4 addresses, and routing sockets.
+IPv4 and IPv6 addresses, and routing sockets.
 To enable access to other domains, this MIB variable may be set to
 0.
 .It Va jail.sysvipc_allowed
@@ -404,6 +404,10 @@
 .Nm
 command appeared in
 .Fx 4.0 .
+.Pp
+Support for multiple IPs and IPv6 appeared in
+.Dx
+1.7.
 .Sh AUTHORS
 .An -nosplit
 The jail feature was written by
@@ -416,6 +420,11 @@
 .An Robert Watson
 wrote the extended documentation, found a few bugs, added
 a few new features, and cleaned up the userland jail environment.
+.Pp
+.An Victor Balada Diaz
+wrote the support for multiple IPs and IPv6. Multiple IPs support
+is based on work done by
+.An Pawel Jakub Dawidek .
 .Sh BUGS
 Jail currently lacks strong management functionality, such as the ability
 to deliver signals to all processes in a jail, and to allow access to
Index: usr.sbin/jail/jail.c
===================================================================
RCS file: /home/dcvs/src/usr.sbin/jail/jail.c,v
retrieving revision 1.4
diff -u -r1.4 jail.c
--- usr.sbin/jail/jail.c	18 Dec 2004 22:48:03 -0000	1.4
+++ usr.sbin/jail/jail.c	5 Nov 2006 10:38:46 -0000
@@ -26,6 +26,12 @@
 #include <string.h>
 #include <unistd.h>
 
+/*
+ * It's safe to raise this number. Kernel structures doesn't impose
+ * any limits on jail numbers.
+ */
+#define JAILIPS	20
+
 static void	usage(void);
 
 int
@@ -33,13 +39,16 @@
 {
 	login_cap_t *lcap = NULL;
 	struct jail j;
+	struct sockaddr_storage sockstor[JAILIPS];
+	struct sockaddr_in *sin4;
+	struct sockaddr_in6 *sin6;
 	struct passwd *pwd = NULL;
-	struct in_addr in;
 	gid_t groups[NGROUPS];
-	int ch, ngroups;
-	char *username;
+	int ch, ngroups, x;
+	char *username, *curpos;
 
 	username = NULL;
+	bzero(sockstor, sizeof(sockstor));
 
 	while ((ch = getopt(argc, argv, "u:")) != -1)
 		switch (ch) {
@@ -69,12 +78,34 @@
 	if (chdir(argv[0]) != 0)
 		err(1, "chdir: %s", argv[0]);
 	memset(&j, 0, sizeof(j));
-	j.version = 0;
+	j.version = 1;
 	j.path = argv[0];
 	j.hostname = argv[1];
-	if (inet_aton(argv[2], &in) == 0)
-		errx(1, "Could not make sense of ip-number: %s", argv[2]);
-	j.ip_number = ntohl(in.s_addr);
+	curpos = strtok(argv[2], ",");
+	for (x=0; curpos != NULL; x++) {
+		if (x == JAILIPS) {
+			printf("Too many IPS, MAX ips per jail: %d\n", JAILIPS);
+			exit(EXIT_FAILURE);
+		}
+
+		sin4 = (struct sockaddr_in *)&sockstor[x];
+		sin6 = (struct sockaddr_in6 *)&sockstor[x];
+
+		if (inet_pton(AF_INET, curpos, &sin4->sin_addr) == 1) {
+			sin4->sin_family = AF_INET;
+		} else {
+			if (inet_pton(AF_INET6, curpos, &sin6->sin6_addr) == 1) {
+				sin6->sin6_family = AF_INET6;
+			} else {
+				printf("Invalid value %s\n", curpos);
+				exit(1);
+			}
+		}
+		curpos = strtok(NULL, ",");
+	}
+
+	j.n_ips = x+1;
+	j.ips = sockstor;
 	if (jail(&j) != 0)
 		err(1, "jail");
 	if (username != NULL) {
