Use struct sockaddr_in6 to keep IPv6 addresses. --- sys/kern/kern_jail.c (svn+ssh://svn.freebsd.org/base/head) (revision 261548) +++ sys/kern/kern_jail.c (working copy) @@ -66,12 +66,12 @@ __FBSDID("$FreeBSD$"); #include #include - -#ifdef DDB -#include #ifdef INET6 #include +#include #endif /* INET6 */ +#ifdef DDB +#include #endif /* DDB */ #include @@ -139,8 +139,12 @@ static int _prison_check_ip4(const struct prison * static int prison_restrict_ip4(struct prison *pr, struct in_addr *newip4); #endif #ifdef INET6 -static int _prison_check_ip6(struct prison *pr, struct in6_addr *ia6); -static int prison_restrict_ip6(struct prison *pr, struct in6_addr *newip6); +static int copyin_inet6(struct jail *j, void *kaddr); +static int _prison_check_ip6(struct prison *, const struct sockaddr_in6 *); +static int prison_restrict_ip6(struct prison *, struct sockaddr_in6 *); +#define SA6_ARE_ADDR_EQUAL(a, b) \ + (IN6_ARE_ADDR_EQUAL(&(a)->sin6_addr, &(b)->sin6_addr) && \ + (a)->sin6_scope_id == (b)->sin6_scope_id) #endif /* Flags for prison_deref */ @@ -268,19 +272,27 @@ qcmp_v4(const void *ip1, const void *ip2) static int qcmp_v6(const void *ip1, const void *ip2) { - const struct in6_addr *ia6a, *ia6b; + const struct sockaddr_in6 *ip6a, *ip6b; int i, rc; - ia6a = (const struct in6_addr *)ip1; - ia6b = (const struct in6_addr *)ip2; + ip6a = (const struct sockaddr_in6 *)ip1; + ip6b = (const struct sockaddr_in6 *)ip2; rc = 0; for (i = 0; rc == 0 && i < sizeof(struct in6_addr); i++) { - if (ia6a->s6_addr[i] > ia6b->s6_addr[i]) + if (ip6a->sin6_addr.s6_addr[i] > + ip6b->sin6_addr.s6_addr[i]) rc = 1; - else if (ia6a->s6_addr[i] < ia6b->s6_addr[i]) + else if (ip6a->sin6_addr.s6_addr[i] < + ip6b->sin6_addr.s6_addr[i]) rc = -1; } + if (rc == 0) { + if (ip6a->sin6_scope_id > ip6b->sin6_scope_id) + rc = 1; + else if (ip6a->sin6_scope_id < ip6b->sin6_scope_id) + rc = -1; + } return (rc); } #endif @@ -327,6 +339,7 @@ sys_jail(struct thread *td, struct jail_args *uap) case 2: /* JAIL_API_VERSION */ /* FreeBSD multi-IPv4/IPv6,noIP jails. */ + case 3: /* in6_addr -> sockaddr_in6 */ error = copyin(uap->jail, &j, sizeof(struct jail)); if (error) return (error); @@ -358,7 +371,7 @@ kern_jail(struct thread *td, struct jail *j) struct in_addr *u_ip4; #endif #ifdef INET6 - struct in6_addr *u_ip6; + struct sockaddr_in6 *u_ip6; #endif size_t tmplen; int error, enforce_statfs, fi; @@ -405,7 +418,7 @@ kern_jail(struct thread *td, struct jail *j) #ifdef INET6 if (j->ip6s > jail_max_af_ips) return (EINVAL); - tmplen += j->ip6s * sizeof(struct in6_addr); + tmplen += j->ip6s * sizeof(struct sockaddr_in6); #else if (j->ip6s > 0) return (EINVAL); @@ -418,9 +431,9 @@ kern_jail(struct thread *td, struct jail *j) #endif #ifdef INET6 #ifdef INET - u_ip6 = (struct in6_addr *)(u_ip4 + ip4s); + u_ip6 = (struct sockaddr_in6 *)(u_ip4 + ip4s); #else - u_ip6 = (struct in6_addr *)(u_name + MAXHOSTNAMELEN); + u_ip6 = (struct sockaddr_in6 *)(u_name + MAXHOSTNAMELEN); #endif #endif optiov[opt.uio_iovcnt].iov_base = "path"; @@ -480,8 +493,8 @@ kern_jail(struct thread *td, struct jail *j) optiov[opt.uio_iovcnt].iov_len = sizeof("ip6.addr"); opt.uio_iovcnt++; optiov[opt.uio_iovcnt].iov_base = u_ip6; - optiov[opt.uio_iovcnt].iov_len = j->ip6s * sizeof(struct in6_addr); - error = copyin(j->ip6, u_ip6, optiov[opt.uio_iovcnt].iov_len); + optiov[opt.uio_iovcnt].iov_len = j->ip6s * sizeof(struct sockaddr_in6); + error = copyin_inet6(j, u_ip6); if (error) { free(u_path, M_TEMP); return (error); @@ -529,7 +542,7 @@ kern_jail_set(struct thread *td, struct uio *optui struct in_addr *ip4; #endif #ifdef INET6 - struct in6_addr *ip6; + struct sockaddr_in6 *ip6; #endif struct vfsopt *opt; struct vfsoptlist *opts; @@ -881,17 +894,24 @@ kern_jail_set(struct thread *td, struct uio *optui if (ip6s > 1) qsort(ip6 + 1, ip6s - 1, sizeof(*ip6), qcmp_v6); for (ii = 0; ii < ip6s; ii++) { - if (IN6_IS_ADDR_UNSPECIFIED(&ip6[ii])) { + if (IN6_IS_ADDR_UNSPECIFIED(&ip6[ii].sin6_addr)) { 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]))) - { + if (ip6[ii].sin6_family != AF_INET6 || + ip6[ii].sin6_len != sizeof(*ip6) || + sa6_checkzone(&ip6[ii]) != 0) { error = EINVAL; goto done_free; } + if ((ii + 1) < ip6s && ( + SA6_ARE_ADDR_EQUAL(&ip6[0], + &ip6[ii + 1]) || + SA6_ARE_ADDR_EQUAL(&ip6[ii], + &ip6[ii + 1]))) { + error = EINVAL; + goto done_free; + } } } } @@ -1272,8 +1292,8 @@ kern_jail_set(struct thread *td, struct uio *optui 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); + sizeof(struct sockaddr_in6), + M_PRISON, M_WAITOK); bcopy(ppr->pr_ip6, pr->pr_ip6, pr->pr_ip6s * sizeof(*pr->pr_ip6)); } @@ -1465,7 +1485,7 @@ kern_jail_set(struct thread *td, struct uio *optui * subset of the parent's list. */ for (ij = 0; ij < ppr->pr_ip6s; ij++) - if (IN6_ARE_ADDR_EQUAL(&ip6[0], + if (SA6_ARE_ADDR_EQUAL(&ip6[0], &ppr->pr_ip6[ij])) break; if (ij == ppr->pr_ip6s) { @@ -1474,11 +1494,11 @@ kern_jail_set(struct thread *td, struct uio *optui } if (ip6s > 1) { for (ii = ij = 1; ii < ip6s; ii++) { - if (IN6_ARE_ADDR_EQUAL(&ip6[ii], - &ppr->pr_ip6[0])) + if (SA6_ARE_ADDR_EQUAL(&ip6[ii], + &ppr->pr_ip6[0])) continue; for (; ij < ppr->pr_ip6s; ij++) - if (IN6_ARE_ADDR_EQUAL( + if (SA6_ARE_ADDR_EQUAL( &ip6[ii], &ppr->pr_ip6[ij])) break; if (ij == ppr->pr_ip6s) @@ -2989,8 +3009,41 @@ prison_check_ip4(const struct ucred *cred, const s #ifdef INET6 static int -prison_restrict_ip6(struct prison *pr, struct in6_addr *newip6) +copyin_inet6(struct jail *j, void *kaddr) { + struct sockaddr_in6 *ip6; + struct in6_addr *in6; + size_t len; + int error, i; + char *buf; + + if (j->version == 3) + return (copyin(j->ip6, kaddr, + j->ip6s * sizeof(struct sockaddr_in6))); + if (j->version != 2) + return (EINVAL); + len = j->ip6s * sizeof(struct in6_addr); + buf = malloc(len, M_TEMP, M_WAITOK); + error = copyin(j->ip6, buf, len); + if (error != 0) { + free(buf, M_TEMP); + return (error); + } + bzero(kaddr, j->ip6s * sizeof(struct sockaddr_in6)); + ip6 = (struct sockaddr_in6 *)kaddr; + in6 = (struct in6_addr *)buf; + for (i = 0; i < j->ip6s; i++) { + ip6[i].sin6_family = AF_INET6; + ip6[i].sin6_len = sizeof(*ip6); + ip6[i].sin6_addr = in6[i]; + } + free(buf, M_TEMP); + return (0); +} + +static int +prison_restrict_ip6(struct prison *pr, struct sockaddr_in6 *newip6) +{ int ii, ij, used; struct prison *ppr; @@ -3031,7 +3084,7 @@ static int } 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], + if (SA6_ARE_ADDR_EQUAL(&pr->pr_ip6[0], &ppr->pr_ip6[ij])) break; if (ij < ppr->pr_ip6s) @@ -3042,7 +3095,7 @@ static int ii = 0; } for (ij = 1; ii < pr->pr_ip6s; ) { - if (IN6_ARE_ADDR_EQUAL(&pr->pr_ip6[ii], + if (SA6_ARE_ADDR_EQUAL(&pr->pr_ip6[ii], &ppr->pr_ip6[0])) { ii++; continue; @@ -3072,6 +3125,16 @@ static int } /* + * Copy only significant fields of struct sockaddr_in6. + */ +static void +prison_copy_ip6(const struct sockaddr_in6 *src, struct sockaddr_in6 *dst) +{ + + dst->sin6_addr = src->sin6_addr; + dst->sin6_scope_id = src->sin6_scope_id; +} +/* * Pass back primary IPv6 address for this jail. * * If not restricted return success but do not alter the address. Caller has @@ -3080,7 +3143,7 @@ static int * Returns 0 on success, EAFNOSUPPORT if the jail doesn't allow IPv6. */ int -prison_get_ip6(struct ucred *cred, struct in6_addr *ia6) +prison_get_ip6(struct ucred *cred, struct sockaddr_in6 *ia6) { struct prison *pr; @@ -3100,7 +3163,7 @@ int return (EAFNOSUPPORT); } - bcopy(&pr->pr_ip6[0], ia6, sizeof(struct in6_addr)); + prison_copy_ip6(&pr->pr_ip6[0], ia6); mtx_unlock(&pr->pr_mtx); return (0); } @@ -3113,10 +3176,10 @@ int * Return EAFNOSUPPORT, in case this jail does not allow IPv6. */ int -prison_saddrsel_ip6(struct ucred *cred, struct in6_addr *ia6) +prison_saddrsel_ip6(struct ucred *cred, struct sockaddr_in6 *ia6) { + struct sockaddr_in6 addr; struct prison *pr; - struct in6_addr lia6; int error; KASSERT(cred != NULL, ("%s: cred is NULL", __func__)); @@ -3129,14 +3192,14 @@ int if (pr->pr_flags & PR_IP6_SADDRSEL) return (1); - lia6 = in6addr_any; - error = prison_get_ip6(cred, &lia6); + addr = sa6_any; + error = prison_get_ip6(cred, &addr); if (error) return (error); - if (IN6_IS_ADDR_UNSPECIFIED(&lia6)) + if (IN6_IS_ADDR_UNSPECIFIED(&addr.sin6_addr)) return (1); - bcopy(&lia6, ia6, sizeof(struct in6_addr)); + prison_copy_ip6(&addr, ia6); return (0); } @@ -3176,7 +3239,7 @@ prison_equal_ip6(struct prison *pr1, struct prison * doesn't allow IPv6. */ int -prison_local_ip6(struct ucred *cred, struct in6_addr *ia6, int v6only) +prison_local_ip6(struct ucred *cred, struct sockaddr_in6 *ia6, int v6only) { struct prison *pr; int error; @@ -3197,19 +3260,19 @@ int return (EAFNOSUPPORT); } - if (IN6_IS_ADDR_LOOPBACK(ia6)) { - bcopy(&pr->pr_ip6[0], ia6, sizeof(struct in6_addr)); + if (IN6_IS_ADDR_LOOPBACK(&ia6->sin6_addr)) { + prison_copy_ip6(&pr->pr_ip6[0], ia6); mtx_unlock(&pr->pr_mtx); return (0); } - if (IN6_IS_ADDR_UNSPECIFIED(ia6)) { + if (IN6_IS_ADDR_UNSPECIFIED(&ia6->sin6_addr)) { /* * 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)); + prison_copy_ip6(&pr->pr_ip6[0], ia6); mtx_unlock(&pr->pr_mtx); return (0); } @@ -3225,7 +3288,7 @@ int * Returns 0 on success, EAFNOSUPPORT if the jail doesn't allow IPv6. */ int -prison_remote_ip6(struct ucred *cred, struct in6_addr *ia6) +prison_remote_ip6(struct ucred *cred, struct sockaddr_in6 *ia6) { struct prison *pr; @@ -3245,8 +3308,8 @@ int return (EAFNOSUPPORT); } - if (IN6_IS_ADDR_LOOPBACK(ia6)) { - bcopy(&pr->pr_ip6[0], ia6, sizeof(struct in6_addr)); + if (IN6_IS_ADDR_LOOPBACK(&ia6->sin6_addr)) { + prison_copy_ip6(&pr->pr_ip6[0], ia6); mtx_unlock(&pr->pr_mtx); return (0); } @@ -3266,7 +3329,7 @@ int * doesn't allow IPv6. */ static int -_prison_check_ip6(struct prison *pr, struct in6_addr *ia6) +_prison_check_ip6(struct prison *pr, const struct sockaddr_in6 *ia6) { int i, a, z, d; @@ -3273,7 +3336,7 @@ static int /* * Check the primary IP. */ - if (IN6_ARE_ADDR_EQUAL(&pr->pr_ip6[0], ia6)) + if (SA6_ARE_ADDR_EQUAL(&pr->pr_ip6[0], ia6)) return (0); /* @@ -3296,7 +3359,7 @@ static int } int -prison_check_ip6(struct ucred *cred, struct in6_addr *ia6) +prison_check_ip6(struct ucred *cred, const struct sockaddr_in6 *ia6) { struct prison *pr; int error; @@ -3321,6 +3384,22 @@ int mtx_unlock(&pr->pr_mtx); return (error); } + +int +prison_check_in6(struct ucred *cred, const struct in6_addr *ia6, + uint32_t zoneid) +{ + struct sockaddr_in6 addr; + + /* XXX: do we need better initialization? */ + addr.sin6_addr = *ia6; + if (IN6_IS_ADDR_LINKLOCAL(ia6)) + addr.sin6_scope_id = zoneid; + else + addr.sin6_scope_id = 0; + return (prison_check_ip6(cred, &addr)); +} + #endif /* @@ -3388,14 +3467,11 @@ prison_check_af(struct ucred *cred, int af) * the jail doesn't allow the address family. IPv4 Address passed in in NBO. */ int -prison_if(struct ucred *cred, struct sockaddr *sa) +prison_if(struct ucred *cred, const struct sockaddr *sa) { #ifdef INET - struct sockaddr_in *sai; + const struct sockaddr_in *sai; #endif -#ifdef INET6 - struct sockaddr_in6 *sai6; -#endif int error; KASSERT(cred != NULL, ("%s: cred is NULL", __func__)); @@ -3411,14 +3487,14 @@ int { #ifdef INET case AF_INET: - sai = (struct sockaddr_in *)sa; + sai = (const struct sockaddr_in *)sa; error = prison_check_ip4(cred, &sai->sin_addr); break; #endif #ifdef INET6 case AF_INET6: - sai6 = (struct sockaddr_in6 *)sa; - error = prison_check_ip6(cred, &sai6->sin6_addr); + error = prison_check_ip6(cred, + (const struct sockaddr_in6 *)sa); break; #endif default: @@ -4033,7 +4109,7 @@ sysctl_jail_list(SYSCTL_HANDLER_ARGS) int ip4s = 0; #endif #ifdef INET6 - struct in6_addr *ip6 = NULL; + struct sockaddr_in6 *ip6 = NULL; int ip6s = 0; #endif int descend, error; @@ -4065,12 +4141,12 @@ sysctl_jail_list(SYSCTL_HANDLER_ARGS) 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); + ip6 = realloc(ip6, ip6s * sizeof(*ip6), + M_TEMP, M_WAITOK); goto again; } bcopy(cpr->pr_ip6, ip6, - cpr->pr_ip6s * sizeof(struct in6_addr)); + cpr->pr_ip6s * sizeof(*ip6)); } #endif if (cpr->pr_ref == 0) { @@ -4106,7 +4182,7 @@ sysctl_jail_list(SYSCTL_HANDLER_ARGS) #ifdef INET6 if (xp->pr_ip6s > 0) { error = SYSCTL_OUT(req, ip6, - xp->pr_ip6s * sizeof(struct in6_addr)); + xp->pr_ip6s * sizeof(*ip6)); if (error) break; } @@ -4364,8 +4440,8 @@ SYSCTL_JAIL_PARAM(_ip4, saddrsel, CTLTYPE_INT | CT #ifdef INET6 SYSCTL_JAIL_PARAM_SYS_NODE(ip6, CTLFLAG_RDTUN, "Jail IPv6 address virtualization"); -SYSCTL_JAIL_PARAM_STRUCT(_ip6, addr, CTLFLAG_RW, sizeof(struct in6_addr), - "S,in6_addr,a", "Jail IPv6 addresses"); +SYSCTL_JAIL_PARAM_STRUCT(_ip6, addr, CTLFLAG_RW, sizeof(struct sockaddr_in6), + "S,sockaddr_in6,a", "Jail IPv6 addresses"); SYSCTL_JAIL_PARAM(_ip6, saddrsel, CTLTYPE_INT | CTLFLAG_RW, "B", "Do (not) use IPv6 source address selection rather than the " "primary jail IPv6 address."); @@ -4630,9 +4706,10 @@ db_show_prison(struct prison *pr) #ifdef INET6 db_printf(" ip6s = %d\n", pr->pr_ip6s); for (ii = 0; ii < pr->pr_ip6s; ii++) - db_printf(" %s %s\n", + db_printf(" %s %s%%%d\n", ii == 0 ? "ip6.addr =" : " ", - ip6_sprintf(ip6buf, &pr->pr_ip6[ii])); + ip6_sprintf(ip6buf, &pr->pr_ip6[ii].sin6_addr), + pr->pr_ip6[ii].sin6_scope_id); #endif }