Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F102894174
D33339.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
38 KB
Referenced Files
None
Subscribers
None
D33339.diff
View Options
diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c
--- a/sys/kern/kern_jail.c
+++ b/sys/kern/kern_jail.c
@@ -45,6 +45,7 @@
#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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
#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 @@
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 @@
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 @@
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 @@
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 @@
*/
#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 @@
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 @@
#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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
* 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 @@
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 @@
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 @@
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_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
}
diff --git a/sys/netinet/in.c b/sys/netinet/in.c
--- a/sys/netinet/in.c
+++ b/sys/netinet/in.c
@@ -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>
diff --git a/sys/netinet/in_jail.c b/sys/netinet/in_jail.c
--- a/sys/netinet/in_jail.c
+++ b/sys/netinet/in_jail.c
@@ -65,6 +65,13 @@
#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 @@
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;
-
- 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);
+ in_addr_t ia = ((const struct in_addr *)ip)->s_addr;
+
+ /*
+ * 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 @@
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 @@
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 @@
/*
* 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 @@
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 @@
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 @@
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);
}
diff --git a/sys/netinet6/in6_jail.c b/sys/netinet6/in6_jail.c
--- a/sys/netinet6/in6_jail.c
+++ b/sys/netinet6/in6_jail.c
@@ -65,6 +65,13 @@
#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 @@
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;
-
- 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;
+ const struct in6_addr *ia = ip;
+
+ return (!IN6_IS_ADDR_UNSPECIFIED(ia));
}
/*
@@ -190,12 +123,12 @@
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 @@
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 @@
* 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 @@
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 @@
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 @@
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);
}
diff --git a/sys/sys/jail.h b/sys/sys/jail.h
--- a/sys/sys/jail.h
+++ b/sys/sys/jail.h
@@ -146,6 +146,12 @@
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 @@
* (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 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_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_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_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);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Nov 19, 10:22 AM (22 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14714775
Default Alt Text
D33339.diff (38 KB)
Attached To
Mode
D33339: jail: network epoch protection for IP address lists
Attached
Detach File
Event Timeline
Log In to Comment