jail: network epoch protection for IP address lists

Now struct prison has two pointers (IPv4 and IPv6) of struct
prison_ip type.  Each points into epoch context, address count
and variable size array of addresses.  These structures are
freed with network epoch deferred free and are not edited in
place, instead a new structure is allocated and set.

While here, the change also generalizes a lot (but not enough)
of IPv4 and IPv6 processing. E.g. address family agnostic helpers
for kern_jail_set() are provided, that reduce v4-v6 copy-paste.

The fast-path prison_check_ip[46]_locked() is also generalized
into prison_ip_check() that can be executed with network epoch
protection only.

Reviewed by:		jamie
Differential revision:	https://reviews.freebsd.org/D33339
This commit is contained in:
Gleb Smirnoff 2021-12-26 10:45:50 -08:00
parent 9df53d07e6
commit eb8dcdeac2
5 changed files with 570 additions and 495 deletions

View File

@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$");
#include <sys/osd.h>
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/epoch.h>
#include <sys/taskqueue.h>
#include <sys/fcntl.h>
#include <sys/jail.h>
@ -531,15 +532,407 @@ sys_jail_set(struct thread *td, struct jail_set_args *uap)
return (error);
}
#if defined(INET) || defined(INET6)
typedef int prison_addr_cmp_t(const void *, const void *);
typedef bool prison_addr_valid_t(const void *);
static const struct pr_family {
size_t size;
prison_addr_cmp_t *cmp;
prison_addr_valid_t *valid;
int ip_flag;
} pr_families[PR_FAMILY_MAX] = {
#ifdef INET
[PR_INET] = {
.size = sizeof(struct in_addr),
.cmp = prison_qcmp_v4,
.valid = prison_valid_v4,
.ip_flag = PR_IP4_USER,
},
#endif
#ifdef INET6
[PR_INET6] = {
.size = sizeof(struct in6_addr),
.cmp = prison_qcmp_v6,
.valid = prison_valid_v6,
.ip_flag = PR_IP6_USER,
},
#endif
};
/*
* Network address lists (pr_addrs) allocation for jails. The addresses
* are accessed locklessly by the network stack, thus need to be protected by
* the network epoch.
*/
struct prison_ip {
struct epoch_context ctx;
uint32_t ips;
#ifdef FUTURE_C
union {
struct in_addr pr_ip4[];
struct in6_addr pr_ip6[];
};
#else /* No future C :( */
#define PR_IP(pip, i) ((const char *)((pip) + 1) + pr_families[af].size * (i))
#define PR_IPD(pip, i) ((char *)((pip) + 1) + pr_families[af].size * (i))
#endif
};
static struct prison_ip *
prison_ip_alloc(const pr_family_t af, uint32_t cnt, int flags)
{
struct prison_ip *pip;
pip = malloc(sizeof(struct prison_ip) + cnt * pr_families[af].size,
M_PRISON, flags);
if (pip != NULL)
pip->ips = cnt;
return (pip);
}
/*
* Allocate and copyin user supplied address list, sorting and validating.
* kern_jail_set() helper.
*/
static struct prison_ip *
prison_ip_copyin(const pr_family_t af, void *op, uint32_t cnt)
{
prison_addr_cmp_t *const cmp = pr_families[af].cmp;
const size_t size = pr_families[af].size;
struct prison_ip *pip;
pip = prison_ip_alloc(af, cnt, M_WAITOK);
bcopy(op, pip + 1, cnt * size);
/*
* IP addresses are all sorted but ip[0] to preserve
* the primary IP address as given from userland.
* This special IP is used for unbound outgoing
* connections as well for "loopback" traffic in case
* source address selection cannot find any more fitting
* address to connect from.
*/
if (cnt > 1)
qsort((char *)(pip + 1) + size, cnt - 1, size,
pr_families[af].cmp);
/*
* Check for duplicate addresses and do some simple
* zero and broadcast checks. If users give other bogus
* addresses it is their problem.
*/
for (int i = 0; i < cnt; i++) {
if (!pr_families[af].valid(PR_IP(pip, i))) {
free(pip, M_PRISON);
return (NULL);
}
if (i + 1 < cnt &&
(cmp(PR_IP(pip, 0), PR_IP(pip, i + 1)) == 0 ||
cmp(PR_IP(pip, i), PR_IP(pip, i + 1)) == 0)) {
free(pip, M_PRISON);
return (NULL);
}
}
return (pip);
}
/*
* Allocate and dup parent prison address list.
* kern_jail_set() helper.
*/
static void
prison_ip_dup(struct prison *ppr, struct prison *pr, const pr_family_t af)
{
if (ppr->pr_addrs[af] != NULL) {
pr->pr_addrs[af] = prison_ip_alloc(af,
ppr->pr_addrs[af]->ips, M_WAITOK);
bcopy(ppr->pr_addrs[af], pr->pr_addrs[af],
pr->pr_addrs[af]->ips * pr_families[af].size);
}
}
/*
* Make sure the new set of IP addresses is a subset of the parent's list.
* Don't worry about the parent being unlocked, as any setting is done with
* allprison_lock held.
* kern_jail_set() helper.
*/
static bool
prison_ip_parent_match(const struct prison_ip *ppip,
const struct prison_ip *pip, const pr_family_t af)
{
prison_addr_cmp_t *const cmp = pr_families[af].cmp;
int i, j;
if (ppip == NULL)
return (false);
for (i = 0; i < ppip->ips; i++)
if (cmp(PR_IP(pip, 0), PR_IP(ppip, i)) == 0)
break;
if (i == ppip->ips)
/* Main address not present in parent. */
return (false);
if (pip->ips > 1) {
for (i = j = 1; i < pip->ips; i++) {
if (cmp(PR_IP(pip, i), PR_IP(ppip, 0)) == 0)
/* Equals to parent primary address. */
continue;
for (; j < ppip->ips; j++)
if (cmp(PR_IP(pip, i), PR_IP(ppip, j)) == 0)
break;
if (j == ppip->ips)
break;
}
if (j == ppip->ips)
/* Address not present in parent. */
return (false);
}
return (true);
}
/*
* Check for conflicting IP addresses. We permit them if there is no more
* than one IP on each jail. If there is a duplicate on a jail with more
* than one IP stop checking and return error.
* kern_jail_set() helper.
*/
static bool
prison_ip_conflict_check(const struct prison *ppr, const struct prison *pr,
const struct prison_ip *pip, pr_family_t af)
{
const struct prison *tppr, *tpr;
int descend;
#ifdef VIMAGE
for (tppr = ppr; tppr != &prison0; tppr = tppr->pr_parent)
if (tppr->pr_flags & PR_VNET)
break;
#else
tppr = &prison0;
#endif
FOREACH_PRISON_DESCENDANT(tppr, tpr, descend) {
if (tpr == pr ||
#ifdef VIMAGE
(tpr != tppr && (tpr->pr_flags & PR_VNET)) ||
#endif
!prison_isalive(tpr)) {
descend = 0;
continue;
}
if (!(tpr->pr_flags & pr_families[af].ip_flag))
continue;
descend = 0;
if (tpr->pr_addrs[af] == NULL ||
(pip->ips == 1 && tpr->pr_addrs[af]->ips == 1))
continue;
for (int i = 0; i < pip->ips; i++)
if (prison_ip_check(tpr, af, PR_IP(pip, i)) == 0)
return (false);
}
return (true);
}
_Static_assert(offsetof(struct prison_ip, ctx) == 0,
"prison must start with epoch context");
static void
prison_ip_free_deferred(epoch_context_t ctx)
{
free(ctx, M_PRISON);
}
static void
prison_ip_free(struct prison_ip *pip)
{
if (pip != NULL)
NET_EPOCH_CALL(prison_ip_free_deferred, &pip->ctx);
}
static void
prison_ip_set(struct prison *pr, const pr_family_t af, struct prison_ip *new)
{
struct prison_ip **mem, *old;
mtx_assert(&pr->pr_mtx, MA_OWNED);
mem = &pr->pr_addrs[af];
old = *mem;
ck_pr_store_ptr(mem, new);
prison_ip_free(old);
}
/*
* Restrict a prison's IP address list with its parent's, possibly replacing
* it. Return true if the replacement buffer was used (or would have been).
* kern_jail_set() helper.
*/
static bool
prison_ip_restrict(struct prison *pr, const pr_family_t af,
struct prison_ip *new)
{
const struct prison_ip *ppip = pr->pr_parent->pr_addrs[af];
const struct prison_ip *pip = pr->pr_addrs[af];
int (*const cmp)(const void *, const void *) = pr_families[af].cmp;
const size_t size = pr_families[af].size;
uint32_t ips;
bool alloced;
mtx_assert(&pr->pr_mtx, MA_OWNED);
/*
* Due to epoch-synchronized access to the IP address lists we always
* allocate a new list even if the old one has enough space. We could
* atomically update an IPv4 address inside a list, but that would
* screw up sorting, and in case of IPv6 we can't even atomically write
* one.
*/
ips = (pr->pr_flags & pr_families[af].ip_flag) ? pip->ips : ppip->ips;
if (ips == 0) {
prison_ip_set(pr, af, NULL);
return (false);
}
if (new == NULL) {
new = prison_ip_alloc(af, ips, M_NOWAIT);
if (new == NULL)
return (true);
alloced = true;
} else
alloced = false;
if (!(pr->pr_flags & pr_families[af].ip_flag)) {
/* This has no user settings, so just copy the parent's list. */
bcopy(ppip, new, ips * size);
} else {
/* Remove addresses that aren't in the parent. */
int i;
i = 0; /* index in pip */
ips = 0; /* index in new */
for (int pi = 0; pi < ppip->ips; pi++)
if (cmp(PR_IP(pip, 0), PR_IP(ppip, pi)) == 0) {
/* Found our primary address in parent. */
bcopy(PR_IP(pip, i), PR_IPD(new, ips), size);
i++;
ips++;
break;
}
for (int pi = 1; i < pip->ips; ) {
/* Check against primary, which is unsorted. */
if (cmp(PR_IP(pip, i), PR_IP(ppip, 0)) == 0) {
/* Matches parent's primary address. */
bcopy(PR_IP(pip, i), PR_IPD(new, ips), size);
i++;
ips++;
continue;
}
/* The rest are sorted. */
switch (pi >= ppip->ips ? -1 :
cmp(PR_IP(pip, i), PR_IP(ppip, pi))) {
case -1:
i++;
break;
case 0:
bcopy(PR_IP(pr, i), PR_IPD(new, ips), size);
i++;
pi++;
ips++;
break;
case 1:
pi++;
break;
}
}
if (ips == 0) {
if (alloced)
prison_ip_free(new);
new = NULL;
}
}
prison_ip_set(pr, af, new);
return (new != NULL ? true : false);
}
/*
* Fast-path check if an address belongs to a prison.
*/
int
prison_ip_check(const struct prison *pr, const pr_family_t af,
const void *addr)
{
int (*const cmp)(const void *, const void *) = pr_families[af].cmp;
const struct prison_ip *pip;
int i, a, z, d;
MPASS(mtx_owned(&pr->pr_mtx) ||
in_epoch(net_epoch_preempt) ||
sx_xlocked(&allprison_lock));
pip = ck_pr_load_ptr(&pr->pr_addrs[af]);
if (__predict_false(pip == NULL))
return (EAFNOSUPPORT);
/* Check the primary IP. */
if (cmp(PR_IP(pip, 0), addr) == 0)
return (0);
/*
* All the other IPs are sorted so we can do a binary search.
*/
a = 0;
z = pip->ips - 2;
while (a <= z) {
i = (a + z) / 2;
d = cmp(PR_IP(pip, i + 1), addr);
if (d > 0)
z = i - 1;
else if (d < 0)
a = i + 1;
else
return (0);
}
return (EADDRNOTAVAIL);
}
/*
* Grab primary IP. Historically required mutex, but nothing prevents
* us to support epoch-protected access. Is it used in fast path?
* in{6}_jail.c helper
*/
const void *
prison_ip_get0(const struct prison *pr, const pr_family_t af)
{
const struct prison_ip *pip = pr->pr_addrs[af];
mtx_assert(&pr->pr_mtx, MA_OWNED);
MPASS(pip);
return (pip + 1);
}
u_int
prison_ip_cnt(const struct prison *pr, const pr_family_t af)
{
return (pr->pr_addrs[af]->ips);
}
#endif /* defined(INET) || defined(INET6) */
int
kern_jail_set(struct thread *td, struct uio *optuio, int flags)
{
struct nameidata nd;
#ifdef INET
struct in_addr *ip4;
struct prison_ip *ip4;
#endif
#ifdef INET6
struct in6_addr *ip6;
struct prison_ip *ip6;
#endif
struct vfsopt *opt;
struct vfsoptlist *opts;
@ -550,7 +943,6 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
struct bool_flags *bf;
struct jailsys_flags *jsf;
#if defined(INET) || defined(INET6)
struct prison *tppr;
void *op;
#endif
unsigned long hid;
@ -560,9 +952,6 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
int gotchildmax, gotenforce, gothid, gotrsnum, gotslevel;
int jid, jsys, len, level;
int childmax, osreldt, rsnum, slevel;
#if defined(INET) || defined(INET6)
int ii, ij;
#endif
#ifdef INET
int ip4s, redo_ip4;
#endif
@ -823,52 +1212,23 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
ip4s = 0;
else if (error != 0)
goto done_free;
else if (ip4s & (sizeof(*ip4) - 1)) {
else if (ip4s & (sizeof(struct in_addr) - 1)) {
error = EINVAL;
goto done_free;
} else {
ch_flags |= PR_IP4_USER;
pr_flags |= PR_IP4_USER;
if (ip4s > 0) {
ip4s /= sizeof(*ip4);
ip4s /= sizeof(struct in_addr);
if (ip4s > jail_max_af_ips) {
error = EINVAL;
vfs_opterror(opts, "too many IPv4 addresses");
goto done_errmsg;
}
ip4 = malloc(ip4s * sizeof(*ip4), M_PRISON, M_WAITOK);
bcopy(op, ip4, ip4s * sizeof(*ip4));
/*
* IP addresses are all sorted but ip[0] to preserve
* the primary IP address as given from userland.
* This special IP is used for unbound outgoing
* connections as well for "loopback" traffic in case
* source address selection cannot find any more fitting
* address to connect from.
*/
if (ip4s > 1)
qsort(ip4 + 1, ip4s - 1, sizeof(*ip4),
prison_qcmp_v4);
/*
* Check for duplicate addresses and do some simple
* zero and broadcast checks. If users give other bogus
* addresses it is their problem.
*
* We do not have to care about byte order for these
* checks so we will do them in NBO.
*/
for (ii = 0; ii < ip4s; ii++) {
if (ip4[ii].s_addr == INADDR_ANY ||
ip4[ii].s_addr == INADDR_BROADCAST) {
error = EINVAL;
goto done_free;
}
if ((ii+1) < ip4s &&
(ip4[0].s_addr == ip4[ii+1].s_addr ||
ip4[ii].s_addr == ip4[ii+1].s_addr)) {
error = EINVAL;
goto done_free;
}
ip4 = prison_ip_copyin(PR_INET, op, ip4s);
if (ip4 == NULL) {
error = EINVAL;
goto done_free;
}
}
}
@ -880,36 +1240,23 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
ip6s = 0;
else if (error != 0)
goto done_free;
else if (ip6s & (sizeof(*ip6) - 1)) {
else if (ip6s & (sizeof(struct in6_addr) - 1)) {
error = EINVAL;
goto done_free;
} else {
ch_flags |= PR_IP6_USER;
pr_flags |= PR_IP6_USER;
if (ip6s > 0) {
ip6s /= sizeof(*ip6);
ip6s /= sizeof(struct in6_addr);
if (ip6s > jail_max_af_ips) {
error = EINVAL;
vfs_opterror(opts, "too many IPv6 addresses");
goto done_errmsg;
}
ip6 = malloc(ip6s * sizeof(*ip6), M_PRISON, M_WAITOK);
bcopy(op, ip6, ip6s * sizeof(*ip6));
if (ip6s > 1)
qsort(ip6 + 1, ip6s - 1, sizeof(*ip6),
prison_qcmp_v6);
for (ii = 0; ii < ip6s; ii++) {
if (IN6_IS_ADDR_UNSPECIFIED(&ip6[ii])) {
error = EINVAL;
goto done_free;
}
if ((ii+1) < ip6s &&
(IN6_ARE_ADDR_EQUAL(&ip6[0], &ip6[ii+1]) ||
IN6_ARE_ADDR_EQUAL(&ip6[ii], &ip6[ii+1])))
{
error = EINVAL;
goto done_free;
}
ip6 = prison_ip_copyin(PR_INET6, op, ip6s);
if (ip6 == NULL) {
error = EINVAL;
goto done_free;
}
}
}
@ -1276,14 +1623,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
pr->pr_flags |= PR_IP4 | PR_IP4_USER;
else if (!(pr_flags & PR_IP4_USER)) {
pr->pr_flags |= ppr->pr_flags & PR_IP4;
if (ppr->pr_ip4 != NULL) {
pr->pr_ip4s = ppr->pr_ip4s;
pr->pr_ip4 = malloc(pr->pr_ip4s *
sizeof(struct in_addr), M_PRISON,
M_WAITOK);
bcopy(ppr->pr_ip4, pr->pr_ip4,
pr->pr_ip4s * sizeof(*pr->pr_ip4));
}
prison_ip_dup(ppr, pr, PR_INET);
}
#endif
#ifdef INET6
@ -1291,14 +1631,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
pr->pr_flags |= PR_IP6 | PR_IP6_USER;
else if (!(pr_flags & PR_IP6_USER)) {
pr->pr_flags |= ppr->pr_flags & PR_IP6;
if (ppr->pr_ip6 != NULL) {
pr->pr_ip6s = ppr->pr_ip6s;
pr->pr_ip6 = malloc(pr->pr_ip6s *
sizeof(struct in6_addr), M_PRISON,
M_WAITOK);
bcopy(ppr->pr_ip6, pr->pr_ip6,
pr->pr_ip6s * sizeof(*pr->pr_ip6));
}
prison_ip_dup(ppr, pr, PR_INET6);
}
#endif
}
@ -1408,143 +1741,31 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
}
#ifdef INET
if (ip4s > 0) {
if (ppr->pr_flags & PR_IP4) {
/*
* Make sure the new set of IP addresses is a
* subset of the parent's list. Don't worry
* about the parent being unlocked, as any
* setting is done with allprison_lock held.
*/
for (ij = 0; ij < ppr->pr_ip4s; ij++)
if (ip4[0].s_addr == ppr->pr_ip4[ij].s_addr)
break;
if (ij == ppr->pr_ip4s) {
error = EPERM;
goto done_deref;
}
if (ip4s > 1) {
for (ii = ij = 1; ii < ip4s; ii++) {
if (ip4[ii].s_addr ==
ppr->pr_ip4[0].s_addr)
continue;
for (; ij < ppr->pr_ip4s; ij++)
if (ip4[ii].s_addr ==
ppr->pr_ip4[ij].s_addr)
break;
if (ij == ppr->pr_ip4s)
break;
}
if (ij == ppr->pr_ip4s) {
error = EPERM;
goto done_deref;
}
}
if ((ppr->pr_flags & PR_IP4) &&
!prison_ip_parent_match(ppr->pr_addrs[PR_INET], ip4,
PR_INET)) {
error = EPERM;
goto done_deref;
}
/*
* Check for conflicting IP addresses. We permit them
* if there is no more than one IP on each jail. If
* there is a duplicate on a jail with more than one
* IP stop checking and return error.
*/
#ifdef VIMAGE
for (tppr = ppr; tppr != &prison0; tppr = tppr->pr_parent)
if (tppr->pr_flags & PR_VNET)
break;
#else
tppr = &prison0;
#endif
FOREACH_PRISON_DESCENDANT(tppr, tpr, descend) {
if (tpr == pr ||
#ifdef VIMAGE
(tpr != tppr && (tpr->pr_flags & PR_VNET)) ||
#endif
!prison_isalive(tpr)) {
descend = 0;
continue;
}
if (!(tpr->pr_flags & PR_IP4_USER))
continue;
descend = 0;
if (tpr->pr_ip4 == NULL ||
(ip4s == 1 && tpr->pr_ip4s == 1))
continue;
for (ii = 0; ii < ip4s; ii++) {
if (prison_check_ip4_locked(tpr, &ip4[ii]) ==
0) {
error = EADDRINUSE;
vfs_opterror(opts,
"IPv4 addresses clash");
goto done_deref;
}
}
if (!prison_ip_conflict_check(ppr, pr, ip4, PR_INET)) {
error = EADDRINUSE;
vfs_opterror(opts, "IPv4 addresses clash");
goto done_deref;
}
}
#endif
#ifdef INET6
if (ip6s > 0) {
if (ppr->pr_flags & PR_IP6) {
/*
* Make sure the new set of IP addresses is a
* subset of the parent's list.
*/
for (ij = 0; ij < ppr->pr_ip6s; ij++)
if (IN6_ARE_ADDR_EQUAL(&ip6[0],
&ppr->pr_ip6[ij]))
break;
if (ij == ppr->pr_ip6s) {
error = EPERM;
goto done_deref;
}
if (ip6s > 1) {
for (ii = ij = 1; ii < ip6s; ii++) {
if (IN6_ARE_ADDR_EQUAL(&ip6[ii],
&ppr->pr_ip6[0]))
continue;
for (; ij < ppr->pr_ip6s; ij++)
if (IN6_ARE_ADDR_EQUAL(
&ip6[ii], &ppr->pr_ip6[ij]))
break;
if (ij == ppr->pr_ip6s)
break;
}
if (ij == ppr->pr_ip6s) {
error = EPERM;
goto done_deref;
}
}
if ((ppr->pr_flags & PR_IP6) &&
!prison_ip_parent_match(ppr->pr_addrs[PR_INET6], ip6,
PR_INET6)) {
error = EPERM;
goto done_deref;
}
/* Check for conflicting IP addresses. */
#ifdef VIMAGE
for (tppr = ppr; tppr != &prison0; tppr = tppr->pr_parent)
if (tppr->pr_flags & PR_VNET)
break;
#else
tppr = &prison0;
#endif
FOREACH_PRISON_DESCENDANT(tppr, tpr, descend) {
if (tpr == pr ||
#ifdef VIMAGE
(tpr != tppr && (tpr->pr_flags & PR_VNET)) ||
#endif
!prison_isalive(tpr)) {
descend = 0;
continue;
}
if (!(tpr->pr_flags & PR_IP6_USER))
continue;
descend = 0;
if (tpr->pr_ip6 == NULL ||
(ip6s == 1 && tpr->pr_ip6s == 1))
continue;
for (ii = 0; ii < ip6s; ii++) {
if (prison_check_ip6_locked(tpr, &ip6[ii]) ==
0) {
error = EADDRINUSE;
vfs_opterror(opts,
"IPv6 addresses clash");
goto done_deref;
}
}
if (!prison_ip_conflict_check(ppr, pr, ip6, PR_INET6)) {
error = EADDRINUSE;
vfs_opterror(opts, "IPv6 addresses clash");
goto done_deref;
}
}
#endif
@ -1615,9 +1836,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
redo_ip4 = 0;
if (pr_flags & PR_IP4_USER) {
pr->pr_flags |= PR_IP4;
free(pr->pr_ip4, M_PRISON);
pr->pr_ip4s = ip4s;
pr->pr_ip4 = ip4;
prison_ip_set(pr, PR_INET, ip4);
ip4 = NULL;
FOREACH_PRISON_DESCENDANT_LOCKED(pr, tpr, descend) {
#ifdef VIMAGE
@ -1626,7 +1845,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
continue;
}
#endif
if (prison_restrict_ip4(tpr, NULL)) {
if (prison_ip_restrict(tpr, PR_INET, NULL)) {
redo_ip4 = 1;
descend = 0;
}
@ -1637,9 +1856,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
redo_ip6 = 0;
if (pr_flags & PR_IP6_USER) {
pr->pr_flags |= PR_IP6;
free(pr->pr_ip6, M_PRISON);
pr->pr_ip6s = ip6s;
pr->pr_ip6 = ip6;
prison_ip_set(pr, PR_INET6, ip6);
ip6 = NULL;
FOREACH_PRISON_DESCENDANT_LOCKED(pr, tpr, descend) {
#ifdef VIMAGE
@ -1648,7 +1865,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
continue;
}
#endif
if (prison_restrict_ip6(tpr, NULL)) {
if (prison_ip_restrict(tpr, PR_INET6, NULL)) {
redo_ip6 = 1;
descend = 0;
}
@ -1792,8 +2009,8 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
*/
#ifdef INET
while (redo_ip4) {
ip4s = pr->pr_ip4s;
ip4 = malloc(ip4s * sizeof(*ip4), M_PRISON, M_WAITOK);
ip4s = pr->pr_addrs[PR_INET]->ips;
ip4 = prison_ip_alloc(PR_INET, ip4s, M_WAITOK);
mtx_lock(&pr->pr_mtx);
redo_ip4 = 0;
FOREACH_PRISON_DESCENDANT_LOCKED(pr, tpr, descend) {
@ -1803,7 +2020,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
continue;
}
#endif
if (prison_restrict_ip4(tpr, ip4)) {
if (prison_ip_restrict(tpr, PR_INET, ip4)) {
if (ip4 != NULL)
ip4 = NULL;
else
@ -1815,8 +2032,8 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
#endif
#ifdef INET6
while (redo_ip6) {
ip6s = pr->pr_ip6s;
ip6 = malloc(ip6s * sizeof(*ip6), M_PRISON, M_WAITOK);
ip6s = pr->pr_addrs[PR_INET6]->ips;
ip6 = prison_ip_alloc(PR_INET6, ip6s, M_WAITOK);
mtx_lock(&pr->pr_mtx);
redo_ip6 = 0;
FOREACH_PRISON_DESCENDANT_LOCKED(pr, tpr, descend) {
@ -1826,7 +2043,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
continue;
}
#endif
if (prison_restrict_ip6(tpr, ip6)) {
if (prison_ip_restrict(tpr, PR_INET6, ip6)) {
if (ip6 != NULL)
ip6 = NULL;
else
@ -1912,10 +2129,10 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
}
done_free:
#ifdef INET
free(ip4, M_PRISON);
prison_ip_free(ip4);
#endif
#ifdef INET6
free(ip6, M_PRISON);
prison_ip_free(ip6);
#endif
if (g_path != NULL)
free(g_path, M_TEMP);
@ -2128,14 +2345,16 @@ kern_jail_get(struct thread *td, struct uio *optuio, int flags)
if (error != 0 && error != ENOENT)
goto done;
#ifdef INET
error = vfs_setopt_part(opts, "ip4.addr", pr->pr_ip4,
pr->pr_ip4s * sizeof(*pr->pr_ip4));
error = vfs_setopt_part(opts, "ip4.addr", pr->pr_addrs[PR_INET] + 1,
pr->pr_addrs[PR_INET] ? pr->pr_addrs[PR_INET]->ips *
pr_families[PR_INET].size : 0 );
if (error != 0 && error != ENOENT)
goto done;
#endif
#ifdef INET6
error = vfs_setopt_part(opts, "ip6.addr", pr->pr_ip6,
pr->pr_ip6s * sizeof(*pr->pr_ip6));
error = vfs_setopt_part(opts, "ip6.addr", pr->pr_addrs[PR_INET6] + 1,
pr->pr_addrs[PR_INET6] ? pr->pr_addrs[PR_INET6]->ips *
pr_families[PR_INET6].size : 0 );
if (error != 0 && error != ENOENT)
goto done;
#endif
@ -2875,10 +3094,10 @@ prison_deref(struct prison *pr, int flags)
vrele(rpr->pr_root);
mtx_destroy(&rpr->pr_mtx);
#ifdef INET
free(rpr->pr_ip4, M_PRISON);
prison_ip_free(rpr->pr_addrs[PR_INET]);
#endif
#ifdef INET6
free(rpr->pr_ip6, M_PRISON);
prison_ip_free(rpr->pr_addrs[PR_INET6]);
#endif
if (rpr->pr_cpuset != NULL)
cpuset_rel(rpr->pr_cpuset);
@ -3074,7 +3293,8 @@ prison_check_af(struct ucred *cred, int af)
if (pr->pr_flags & PR_IP4)
{
mtx_lock(&pr->pr_mtx);
if ((pr->pr_flags & PR_IP4) && pr->pr_ip4 == NULL)
if ((pr->pr_flags & PR_IP4) &&
pr->pr_addrs[PR_INET] == NULL)
error = EAFNOSUPPORT;
mtx_unlock(&pr->pr_mtx);
}
@ -3085,7 +3305,8 @@ prison_check_af(struct ucred *cred, int af)
if (pr->pr_flags & PR_IP6)
{
mtx_lock(&pr->pr_mtx);
if ((pr->pr_flags & PR_IP6) && pr->pr_ip6 == NULL)
if ((pr->pr_flags & PR_IP6) &&
pr->pr_addrs[PR_INET6] == NULL)
error = EAFNOSUPPORT;
mtx_unlock(&pr->pr_mtx);
}
@ -3179,7 +3400,7 @@ prison_ischild(struct prison *pr1, struct prison *pr2)
* holds user references and it isn't being removed.
*/
bool
prison_isalive(struct prison *pr)
prison_isalive(const struct prison *pr)
{
if (__predict_false(pr->pr_state != PRISON_STATE_ALIVE))
@ -3811,6 +4032,31 @@ prison_path(struct prison *pr1, struct prison *pr2)
static SYSCTL_NODE(_security, OID_AUTO, jail, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
"Jails");
#if defined(INET) || defined(INET6)
/*
* Copy address array to memory that would be then SYSCTL_OUT-ed.
* sysctl_jail_list() helper.
*/
static void
prison_ip_copyout(struct prison *pr, const pr_family_t af, void **out, int *len)
{
const size_t size = pr_families[af].size;
again:
mtx_assert(&pr->pr_mtx, MA_OWNED);
if (pr->pr_addrs[af] != NULL) {
if (*len < pr->pr_addrs[af]->ips) {
*len = pr->pr_addrs[af]->ips;
mtx_unlock(&pr->pr_mtx);
*out = realloc(*out, *len * size, M_TEMP, M_WAITOK);
mtx_lock(&pr->pr_mtx);
goto again;
}
bcopy(pr->pr_addrs[af] + 1, *out, pr->pr_addrs[af]->ips * size);
}
}
#endif
static int
sysctl_jail_list(SYSCTL_HANDLER_ARGS)
{
@ -3831,35 +4077,12 @@ sysctl_jail_list(SYSCTL_HANDLER_ARGS)
error = 0;
sx_slock(&allprison_lock);
FOREACH_PRISON_DESCENDANT(pr, cpr, descend) {
#if defined(INET) || defined(INET6)
again:
#endif
mtx_lock(&cpr->pr_mtx);
#ifdef INET
if (cpr->pr_ip4s > 0) {
if (ip4s < cpr->pr_ip4s) {
ip4s = cpr->pr_ip4s;
mtx_unlock(&cpr->pr_mtx);
ip4 = realloc(ip4, ip4s *
sizeof(struct in_addr), M_TEMP, M_WAITOK);
goto again;
}
bcopy(cpr->pr_ip4, ip4,
cpr->pr_ip4s * sizeof(struct in_addr));
}
prison_ip_copyout(cpr, PR_INET, (void **)&ip4, &ip4s);
#endif
#ifdef INET6
if (cpr->pr_ip6s > 0) {
if (ip6s < cpr->pr_ip6s) {
ip6s = cpr->pr_ip6s;
mtx_unlock(&cpr->pr_mtx);
ip6 = realloc(ip6, ip6s *
sizeof(struct in6_addr), M_TEMP, M_WAITOK);
goto again;
}
bcopy(cpr->pr_ip6, ip6,
cpr->pr_ip6s * sizeof(struct in6_addr));
}
prison_ip_copyout(cpr, PR_INET6, (void **)&ip6, &ip6s);
#endif
bzero(xp, sizeof(*xp));
xp->pr_version = XPRISON_VERSION;
@ -3869,10 +4092,10 @@ sysctl_jail_list(SYSCTL_HANDLER_ARGS)
strlcpy(xp->pr_host, cpr->pr_hostname, sizeof(xp->pr_host));
strlcpy(xp->pr_name, prison_name(pr, cpr), sizeof(xp->pr_name));
#ifdef INET
xp->pr_ip4s = cpr->pr_ip4s;
xp->pr_ip4s = ip4s;
#endif
#ifdef INET6
xp->pr_ip6s = cpr->pr_ip6s;
xp->pr_ip6s = ip6s;
#endif
mtx_unlock(&cpr->pr_mtx);
error = SYSCTL_OUT(req, xp, sizeof(*xp));
@ -4559,18 +4782,29 @@ db_show_prison(struct prison *pr)
db_printf(" host.hostuuid = %s\n", pr->pr_hostuuid);
db_printf(" host.hostid = %lu\n", pr->pr_hostid);
#ifdef INET
db_printf(" ip4s = %d\n", pr->pr_ip4s);
for (ii = 0; ii < pr->pr_ip4s; ii++)
db_printf(" %s %s\n",
ii == 0 ? "ip4.addr =" : " ",
inet_ntoa_r(pr->pr_ip4[ii], ip4buf));
if (pr->pr_addrs[PR_INET] != NULL) {
pr_family_t af = PR_INET;
db_printf(" ip4s = %d\n", pr->pr_addrs[af]->ips);
for (ii = 0; ii < pr->pr_addrs[af]->ips; ii++)
db_printf(" %s %s\n",
ii == 0 ? "ip4.addr =" : " ",
inet_ntoa_r(
*(const struct in_addr *)PR_IP(pr, ii),
ip4buf));
}
#endif
#ifdef INET6
db_printf(" ip6s = %d\n", pr->pr_ip6s);
for (ii = 0; ii < pr->pr_ip6s; ii++)
db_printf(" %s %s\n",
ii == 0 ? "ip6.addr =" : " ",
ip6_sprintf(ip6buf, &pr->pr_ip6[ii]));
if (pr->pr_addrs[PR_INET6] != NULL) {
pr_family_t af = PR_INET6;
db_printf(" ip6s = %d\n", pr->pr_addrs[af]->ips);
for (ii = 0; ii < pr->pr_addrs[af]->ips; ii++)
db_printf(" %s %s\n",
ii == 0 ? "ip6.addr =" : " ",
ip6_sprintf(ip6buf,
(const struct in6_addr *)PR_IP(pr, ii)));
}
#endif
}

View File

@ -35,6 +35,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_inet.h"
#define IN_HISTORICAL_NETS /* include class masks */
#include <sys/param.h>

View File

@ -65,6 +65,13 @@ __FBSDID("$FreeBSD$");
#include <netinet/in.h>
static in_addr_t
prison_primary_ip4(const struct prison *pr)
{
return (((const struct in_addr *)prison_ip_get0(pr, PR_INET))->s_addr);
}
int
prison_qcmp_v4(const void *ip1, const void *ip2)
{
@ -90,88 +97,16 @@ prison_qcmp_v4(const void *ip1, const void *ip2)
return (0);
}
/*
* Restrict a prison's IP address list with its parent's, possibly replacing
* it. Return true if the replacement buffer was used (or would have been).
*/
int
prison_restrict_ip4(struct prison *pr, struct in_addr *newip4)
bool
prison_valid_v4(const void *ip)
{
int ii, ij, used;
struct prison *ppr;
in_addr_t ia = ((const struct in_addr *)ip)->s_addr;
ppr = pr->pr_parent;
if (!(pr->pr_flags & PR_IP4_USER)) {
/* This has no user settings, so just copy the parent's list. */
if (pr->pr_ip4s < ppr->pr_ip4s) {
/*
* There's no room for the parent's list. Use the
* new list buffer, which is assumed to be big enough
* (if it was passed). If there's no buffer, try to
* allocate one.
*/
used = 1;
if (newip4 == NULL) {
newip4 = malloc(ppr->pr_ip4s * sizeof(*newip4),
M_PRISON, M_NOWAIT);
if (newip4 != NULL)
used = 0;
}
if (newip4 != NULL) {
bcopy(ppr->pr_ip4, newip4,
ppr->pr_ip4s * sizeof(*newip4));
free(pr->pr_ip4, M_PRISON);
pr->pr_ip4 = newip4;
pr->pr_ip4s = ppr->pr_ip4s;
}
return (used);
}
pr->pr_ip4s = ppr->pr_ip4s;
if (pr->pr_ip4s > 0)
bcopy(ppr->pr_ip4, pr->pr_ip4,
pr->pr_ip4s * sizeof(*newip4));
else if (pr->pr_ip4 != NULL) {
free(pr->pr_ip4, M_PRISON);
pr->pr_ip4 = NULL;
}
} else if (pr->pr_ip4s > 0) {
/* Remove addresses that aren't in the parent. */
for (ij = 0; ij < ppr->pr_ip4s; ij++)
if (pr->pr_ip4[0].s_addr == ppr->pr_ip4[ij].s_addr)
break;
if (ij < ppr->pr_ip4s)
ii = 1;
else {
bcopy(pr->pr_ip4 + 1, pr->pr_ip4,
--pr->pr_ip4s * sizeof(*pr->pr_ip4));
ii = 0;
}
for (ij = 1; ii < pr->pr_ip4s; ) {
if (pr->pr_ip4[ii].s_addr == ppr->pr_ip4[0].s_addr) {
ii++;
continue;
}
switch (ij >= ppr->pr_ip4s ? -1 :
prison_qcmp_v4(&pr->pr_ip4[ii], &ppr->pr_ip4[ij])) {
case -1:
bcopy(pr->pr_ip4 + ii + 1, pr->pr_ip4 + ii,
(--pr->pr_ip4s - ii) * sizeof(*pr->pr_ip4));
break;
case 0:
ii++;
ij++;
break;
case 1:
ij++;
break;
}
}
if (pr->pr_ip4s == 0) {
free(pr->pr_ip4, M_PRISON);
pr->pr_ip4 = NULL;
}
}
return (0);
/*
* We do not have to care about byte order for these
* checks so we will do them in NBO.
*/
return (ia != INADDR_ANY && ia != INADDR_BROADCAST);
}
/*
@ -199,12 +134,12 @@ prison_get_ip4(struct ucred *cred, struct in_addr *ia)
mtx_unlock(&pr->pr_mtx);
return (0);
}
if (pr->pr_ip4 == NULL) {
if (pr->pr_addrs[PR_INET] == NULL) {
mtx_unlock(&pr->pr_mtx);
return (EAFNOSUPPORT);
}
ia->s_addr = pr->pr_ip4[0].s_addr;
ia->s_addr = prison_primary_ip4(pr);
mtx_unlock(&pr->pr_mtx);
return (0);
}
@ -299,7 +234,7 @@ prison_local_ip4(struct ucred *cred, struct in_addr *ia)
mtx_unlock(&pr->pr_mtx);
return (0);
}
if (pr->pr_ip4 == NULL) {
if (pr->pr_addrs[PR_INET] == NULL) {
mtx_unlock(&pr->pr_mtx);
return (EAFNOSUPPORT);
}
@ -310,15 +245,15 @@ prison_local_ip4(struct ucred *cred, struct in_addr *ia)
/*
* In case there is only 1 IPv4 address, bind directly.
*/
if (pr->pr_ip4s == 1)
ia->s_addr = pr->pr_ip4[0].s_addr;
if (prison_ip_cnt(pr, PR_INET) == 1)
ia->s_addr = prison_primary_ip4(pr);
mtx_unlock(&pr->pr_mtx);
return (0);
}
error = prison_check_ip4_locked(pr, ia);
if (error == EADDRNOTAVAIL && ia0.s_addr == INADDR_LOOPBACK) {
ia->s_addr = pr->pr_ip4[0].s_addr;
ia->s_addr = prison_primary_ip4(pr);
error = 0;
}
@ -348,14 +283,14 @@ prison_remote_ip4(struct ucred *cred, struct in_addr *ia)
mtx_unlock(&pr->pr_mtx);
return (0);
}
if (pr->pr_ip4 == NULL) {
if (pr->pr_addrs[PR_INET] == NULL) {
mtx_unlock(&pr->pr_mtx);
return (EAFNOSUPPORT);
}
if (ntohl(ia->s_addr) == INADDR_LOOPBACK &&
prison_check_ip4_locked(pr, ia) == EADDRNOTAVAIL) {
ia->s_addr = pr->pr_ip4[0].s_addr;
ia->s_addr = prison_primary_ip4(pr);
mtx_unlock(&pr->pr_mtx);
return (0);
}
@ -376,31 +311,11 @@ prison_remote_ip4(struct ucred *cred, struct in_addr *ia)
int
prison_check_ip4_locked(const struct prison *pr, const struct in_addr *ia)
{
int i, a, z, d;
/*
* Check the primary IP.
*/
if (pr->pr_ip4[0].s_addr == ia->s_addr)
if (!(pr->pr_flags & PR_IP4))
return (0);
/*
* All the other IPs are sorted so we can do a binary search.
*/
a = 0;
z = pr->pr_ip4s - 2;
while (a <= z) {
i = (a + z) / 2;
d = prison_qcmp_v4(&pr->pr_ip4[i+1], ia);
if (d > 0)
z = i - 1;
else if (d < 0)
a = i + 1;
else
return (0);
}
return (EADDRNOTAVAIL);
return (prison_ip_check(pr, PR_INET, ia));
}
int
@ -420,7 +335,7 @@ prison_check_ip4(const struct ucred *cred, const struct in_addr *ia)
mtx_unlock(&pr->pr_mtx);
return (0);
}
if (pr->pr_ip4 == NULL) {
if (pr->pr_addrs[PR_INET] == NULL) {
mtx_unlock(&pr->pr_mtx);
return (EAFNOSUPPORT);
}

View File

@ -65,6 +65,13 @@ __FBSDID("$FreeBSD$");
#include <netinet/in.h>
static void
prison_bcopy_primary_ip6(const struct prison *pr, struct in6_addr *ia6)
{
bcopy(prison_ip_get0(pr, PR_INET6), ia6, sizeof(struct in6_addr));
}
int
prison_qcmp_v6(const void *ip1, const void *ip2)
{
@ -84,86 +91,12 @@ prison_qcmp_v6(const void *ip1, const void *ip2)
return (rc);
}
int
prison_restrict_ip6(struct prison *pr, struct in6_addr *newip6)
bool
prison_valid_v6(const void *ip)
{
int ii, ij, used;
struct prison *ppr;
const struct in6_addr *ia = ip;
ppr = pr->pr_parent;
if (!(pr->pr_flags & PR_IP6_USER)) {
/* This has no user settings, so just copy the parent's list. */
if (pr->pr_ip6s < ppr->pr_ip6s) {
/*
* There's no room for the parent's list. Use the
* new list buffer, which is assumed to be big enough
* (if it was passed). If there's no buffer, try to
* allocate one.
*/
used = 1;
if (newip6 == NULL) {
newip6 = malloc(ppr->pr_ip6s * sizeof(*newip6),
M_PRISON, M_NOWAIT);
if (newip6 != NULL)
used = 0;
}
if (newip6 != NULL) {
bcopy(ppr->pr_ip6, newip6,
ppr->pr_ip6s * sizeof(*newip6));
free(pr->pr_ip6, M_PRISON);
pr->pr_ip6 = newip6;
pr->pr_ip6s = ppr->pr_ip6s;
}
return (used);
}
pr->pr_ip6s = ppr->pr_ip6s;
if (pr->pr_ip6s > 0)
bcopy(ppr->pr_ip6, pr->pr_ip6,
pr->pr_ip6s * sizeof(*newip6));
else if (pr->pr_ip6 != NULL) {
free(pr->pr_ip6, M_PRISON);
pr->pr_ip6 = NULL;
}
} else if (pr->pr_ip6s > 0) {
/* Remove addresses that aren't in the parent. */
for (ij = 0; ij < ppr->pr_ip6s; ij++)
if (IN6_ARE_ADDR_EQUAL(&pr->pr_ip6[0],
&ppr->pr_ip6[ij]))
break;
if (ij < ppr->pr_ip6s)
ii = 1;
else {
bcopy(pr->pr_ip6 + 1, pr->pr_ip6,
--pr->pr_ip6s * sizeof(*pr->pr_ip6));
ii = 0;
}
for (ij = 1; ii < pr->pr_ip6s; ) {
if (IN6_ARE_ADDR_EQUAL(&pr->pr_ip6[ii],
&ppr->pr_ip6[0])) {
ii++;
continue;
}
switch (ij >= ppr->pr_ip6s ? -1 :
prison_qcmp_v6(&pr->pr_ip6[ii], &ppr->pr_ip6[ij])) {
case -1:
bcopy(pr->pr_ip6 + ii + 1, pr->pr_ip6 + ii,
(--pr->pr_ip6s - ii) * sizeof(*pr->pr_ip6));
break;
case 0:
ii++;
ij++;
break;
case 1:
ij++;
break;
}
}
if (pr->pr_ip6s == 0) {
free(pr->pr_ip6, M_PRISON);
pr->pr_ip6 = NULL;
}
}
return 0;
return (!IN6_IS_ADDR_UNSPECIFIED(ia));
}
/*
@ -190,12 +123,12 @@ prison_get_ip6(struct ucred *cred, struct in6_addr *ia6)
mtx_unlock(&pr->pr_mtx);
return (0);
}
if (pr->pr_ip6 == NULL) {
if (pr->pr_addrs[PR_INET6] == NULL) {
mtx_unlock(&pr->pr_mtx);
return (EAFNOSUPPORT);
}
bcopy(&pr->pr_ip6[0], ia6, sizeof(struct in6_addr));
prison_bcopy_primary_ip6(pr, ia6);
mtx_unlock(&pr->pr_mtx);
return (0);
}
@ -287,7 +220,7 @@ prison_local_ip6(struct ucred *cred, struct in6_addr *ia6, int v6only)
mtx_unlock(&pr->pr_mtx);
return (0);
}
if (pr->pr_ip6 == NULL) {
if (pr->pr_addrs[PR_INET6] == NULL) {
mtx_unlock(&pr->pr_mtx);
return (EAFNOSUPPORT);
}
@ -297,15 +230,15 @@ prison_local_ip6(struct ucred *cred, struct in6_addr *ia6, int v6only)
* In case there is only 1 IPv6 address, and v6only is true,
* then bind directly.
*/
if (v6only != 0 && pr->pr_ip6s == 1)
bcopy(&pr->pr_ip6[0], ia6, sizeof(struct in6_addr));
if (v6only != 0 && prison_ip_cnt(pr, PR_INET6) == 1)
prison_bcopy_primary_ip6(pr, ia6);
mtx_unlock(&pr->pr_mtx);
return (0);
}
error = prison_check_ip6_locked(pr, ia6);
if (error == EADDRNOTAVAIL && IN6_IS_ADDR_LOOPBACK(ia6)) {
bcopy(&pr->pr_ip6[0], ia6, sizeof(struct in6_addr));
prison_bcopy_primary_ip6(pr, ia6);
error = 0;
}
@ -334,14 +267,14 @@ prison_remote_ip6(struct ucred *cred, struct in6_addr *ia6)
mtx_unlock(&pr->pr_mtx);
return (0);
}
if (pr->pr_ip6 == NULL) {
if (pr->pr_addrs[PR_INET6] == NULL) {
mtx_unlock(&pr->pr_mtx);
return (EAFNOSUPPORT);
}
if (IN6_IS_ADDR_LOOPBACK(ia6) &&
prison_check_ip6_locked(pr, ia6) == EADDRNOTAVAIL) {
bcopy(&pr->pr_ip6[0], ia6, sizeof(struct in6_addr));
prison_bcopy_primary_ip6(pr, ia6);
mtx_unlock(&pr->pr_mtx);
return (0);
}
@ -362,31 +295,11 @@ prison_remote_ip6(struct ucred *cred, struct in6_addr *ia6)
int
prison_check_ip6_locked(const struct prison *pr, const struct in6_addr *ia6)
{
int i, a, z, d;
/*
* Check the primary IP.
*/
if (IN6_ARE_ADDR_EQUAL(&pr->pr_ip6[0], ia6))
if (!(pr->pr_flags & PR_IP6))
return (0);
/*
* All the other IPs are sorted so we can do a binary search.
*/
a = 0;
z = pr->pr_ip6s - 2;
while (a <= z) {
i = (a + z) / 2;
d = prison_qcmp_v6(&pr->pr_ip6[i+1], ia6);
if (d > 0)
z = i - 1;
else if (d < 0)
a = i + 1;
else
return (0);
}
return (EADDRNOTAVAIL);
return (prison_ip_check(pr, PR_INET6, ia6));
}
int
@ -406,7 +319,7 @@ prison_check_ip6(const struct ucred *cred, const struct in6_addr *ia6)
mtx_unlock(&pr->pr_mtx);
return (0);
}
if (pr->pr_ip6 == NULL) {
if (pr->pr_addrs[PR_INET6] == NULL) {
mtx_unlock(&pr->pr_mtx);
return (EAFNOSUPPORT);
}

View File

@ -146,6 +146,12 @@ MALLOC_DECLARE(M_PRISON);
struct racct;
struct prison_racct;
typedef enum {
PR_INET = 0,
PR_INET6 = 1,
PR_FAMILY_MAX = 2,
} pr_family_t;
/*
* This structure describes a prison. It is pointed to by all struct
* ucreds's of the inmates. pr_ref keeps track of them and is used to
@ -161,6 +167,7 @@ struct prison_racct;
* (q) locked by both pr_mtx and allprison_lock
* (r) atomic via refcount(9), pr_mtx and allprison_lock required to
* decrement to zero
* (n) read access granted with the network epoch
*/
struct prison {
TAILQ_ENTRY(prison) pr_list; /* (a) all prisons */
@ -177,10 +184,7 @@ struct prison {
struct cpuset *pr_cpuset; /* (p) cpuset */
struct vnet *pr_vnet; /* (c) network stack */
struct vnode *pr_root; /* (c) vnode to rdir */
int pr_ip4s; /* (p) number of v4 IPs */
int pr_ip6s; /* (p) number of v6 IPs */
struct in_addr *pr_ip4; /* (p) v4 IPs of jail */
struct in6_addr *pr_ip6; /* (p) v6 IPs of jail */
struct prison_ip *pr_addrs[PR_FAMILY_MAX]; /* (p,n) IPs of jail */
struct prison_racct *pr_prison_racct; /* (c) racct jail proxy */
void *pr_sparep[3];
int pr_childcount; /* (a) number of child jails */
@ -430,8 +434,14 @@ void prison_proc_hold(struct prison *);
void prison_proc_free(struct prison *);
void prison_set_allow(struct ucred *cred, unsigned flag, int enable);
int prison_ischild(struct prison *, struct prison *);
bool prison_isalive(struct prison *);
bool prison_isalive(const struct prison *);
bool prison_isvalid(struct prison *);
#if defined(INET) || defined(INET6)
int prison_ip_check(const struct prison *, const pr_family_t, const void *);
const void *prison_ip_get0(const struct prison *, const pr_family_t);
u_int prison_ip_cnt(const struct prison *, const pr_family_t);
#endif
#ifdef INET
int prison_equal_ip4(struct prison *, struct prison *);
int prison_get_ip4(struct ucred *cred, struct in_addr *ia);
int prison_local_ip4(struct ucred *cred, struct in_addr *ia);
@ -439,8 +449,9 @@ int prison_remote_ip4(struct ucred *cred, struct in_addr *ia);
int prison_check_ip4(const struct ucred *, const struct in_addr *);
int prison_check_ip4_locked(const struct prison *, const struct in_addr *);
int prison_saddrsel_ip4(struct ucred *, struct in_addr *);
int prison_restrict_ip4(struct prison *, struct in_addr *);
int prison_qcmp_v4(const void *, const void *);
bool prison_valid_v4(const void *);
#endif
#ifdef INET6
int prison_equal_ip6(struct prison *, struct prison *);
int prison_get_ip6(struct ucred *, struct in6_addr *);
@ -449,8 +460,8 @@ int prison_remote_ip6(struct ucred *, struct in6_addr *);
int prison_check_ip6(const struct ucred *, const struct in6_addr *);
int prison_check_ip6_locked(const struct prison *, const struct in6_addr *);
int prison_saddrsel_ip6(struct ucred *, struct in6_addr *);
int prison_restrict_ip6(struct prison *, struct in6_addr *);
int prison_qcmp_v6(const void *, const void *);
bool prison_valid_v6(const void *);
#endif
int prison_check_af(struct ucred *cred, int af);
int prison_if(struct ucred *cred, const struct sockaddr *sa);