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 #include #include +#include #include #include #include @@ -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 __FBSDID("$FreeBSD$"); +#include "opt_inet.h" + #define IN_HISTORICAL_NETS /* include class masks */ #include 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 +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 +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);