Date: Wed, 18 Dec 2013 14:36:44 +0000 (UTC) From: "Andrey V. Elsukov" <ae@FreeBSD.org> To: src-committers@freebsd.org, svn-src-user@freebsd.org Subject: svn commit: r259551 - in user/ae/inet6/sys: kern sys Message-ID: <201312181436.rBIEajVG030760@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: ae Date: Wed Dec 18 14:36:44 2013 New Revision: 259551 URL: http://svnweb.freebsd.org/changeset/base/259551 Log: Use struct sockaddr_in6 to keep IPv6 addresses in jail code. Since IPv6 LLAs use zone indexes in addition to the struct in6_addr, we couldn't use these addresses in jails (actually we could use them, but LLAs from different zones were the same for jail). The JAIL_API_VERSION now is 3. And the kernel will reject IPv6 addresses, if they are passed from userland in old format. All prison_xxx_ip6() functions were changed to take struct sockaddr_in6 instead of in6_addr. One new function prison_check_in6() added, it takes struct in6_addr and zone index. In some places the const qualifier was added. Modified: user/ae/inet6/sys/kern/kern_jail.c user/ae/inet6/sys/sys/jail.h Modified: user/ae/inet6/sys/kern/kern_jail.c ============================================================================== --- user/ae/inet6/sys/kern/kern_jail.c Wed Dec 18 12:53:48 2013 (r259550) +++ user/ae/inet6/sys/kern/kern_jail.c Wed Dec 18 14:36:44 2013 (r259551) @@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$"); #include <ddb/ddb.h> #ifdef INET6 #include <netinet6/in6_var.h> +#include <netinet6/scope6_var.h> #endif /* INET6 */ #endif /* DDB */ @@ -139,8 +140,11 @@ static int _prison_check_ip4(const struc 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 _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,17 +272,25 @@ 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); @@ -327,6 +339,7 @@ sys_jail(struct thread *td, struct jail_ 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 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; @@ -403,9 +416,16 @@ kern_jail(struct thread *td, struct jail return (EINVAL); #endif #ifdef INET6 + /* + * We don't support the old way, when the list of IPv6 addresses + * is specified via struct in6_addr. + * XXX: in some case we can (i.e. until LLA isn't used). + */ + if (j->version < 3) + return (EINVAL); 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 +438,9 @@ kern_jail(struct thread *td, struct jail #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,7 +500,7 @@ kern_jail(struct thread *td, struct jail 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); + optiov[opt.uio_iovcnt].iov_len = j->ip6s * sizeof(struct sockaddr_in6); error = copyin(j->ip6, u_ip6, optiov[opt.uio_iovcnt].iov_len); if (error) { free(u_path, M_TEMP); @@ -529,7 +549,7 @@ kern_jail_set(struct thread *td, struct struct in_addr *ip4; #endif #ifdef INET6 - struct in6_addr *ip6; + struct sockaddr_in6 *ip6; #endif struct vfsopt *opt; struct vfsoptlist *opts; @@ -881,14 +901,21 @@ kern_jail_set(struct thread *td, struct 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 +1299,8 @@ kern_jail_set(struct thread *td, struct 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 +1492,7 @@ kern_jail_set(struct thread *td, struct * 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 +1501,11 @@ kern_jail_set(struct thread *td, struct } 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,7 +3016,7 @@ prison_check_ip4(const struct ucred *cre #ifdef INET6 static int -prison_restrict_ip6(struct prison *pr, struct in6_addr *newip6) +prison_restrict_ip6(struct prison *pr, struct sockaddr_in6 *newip6) { int ii, ij, used; struct prison *ppr; @@ -3031,7 +3058,7 @@ prison_restrict_ip6(struct prison *pr, s } 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,12 +3069,12 @@ prison_restrict_ip6(struct prison *pr, s 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; } - switch (ij >= ppr->pr_ip4s ? -1 : + switch (ij >= ppr->pr_ip6s ? -1 : qcmp_v6(&pr->pr_ip6[ii], &ppr->pr_ip6[ij])) { case -1: bcopy(pr->pr_ip6 + ii + 1, pr->pr_ip6 + ii, @@ -3072,6 +3099,16 @@ prison_restrict_ip6(struct prison *pr, s } /* + * 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 +3117,7 @@ prison_restrict_ip6(struct prison *pr, s * 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 +3137,7 @@ prison_get_ip6(struct ucred *cred, struc 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 +3150,10 @@ prison_get_ip6(struct ucred *cred, struc * 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 +3166,14 @@ prison_saddrsel_ip6(struct ucred *cred, 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 +3213,7 @@ prison_equal_ip6(struct prison *pr1, str * 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 +3234,19 @@ prison_local_ip6(struct ucred *cred, str 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 +3262,7 @@ prison_local_ip6(struct ucred *cred, str * 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 +3282,8 @@ prison_remote_ip6(struct ucred *cred, st 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,14 +3303,14 @@ prison_remote_ip6(struct ucred *cred, st * 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; /* * 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 +3333,7 @@ _prison_check_ip6(struct prison *pr, str } 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 +3358,20 @@ prison_check_ip6(struct ucred *cred, str 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; + return (prison_check_ip6(cred, &addr)); +} + #endif /* @@ -3388,13 +3439,10 @@ prison_check_af(struct ucred *cred, int * 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; -#endif -#ifdef INET6 - struct sockaddr_in6 *sai6; + const struct sockaddr_in *sai; #endif int error; @@ -3411,14 +3459,14 @@ prison_if(struct ucred *cred, struct soc { #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 +4081,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 +4113,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 +4154,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 +4412,8 @@ SYSCTL_JAIL_PARAM(_ip4, saddrsel, CTLTYP #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 +4678,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 } Modified: user/ae/inet6/sys/sys/jail.h ============================================================================== --- user/ae/inet6/sys/sys/jail.h Wed Dec 18 12:53:48 2013 (r259550) +++ user/ae/inet6/sys/sys/jail.h Wed Dec 18 14:36:44 2013 (r259551) @@ -47,9 +47,9 @@ struct jail { uint32_t ip4s; uint32_t ip6s; struct in_addr *ip4; - struct in6_addr *ip6; + struct sockaddr_in6 *ip6; }; -#define JAIL_API_VERSION 2 +#define JAIL_API_VERSION 3 /* * For all xprison structs, always keep the pr_version an int and @@ -81,7 +81,7 @@ struct xprison { * IPv4 and IPv6 addesses. Offsets are based numbers of addresses. */ struct in_addr pr_ip4[]; - struct in6_addr pr_ip6[]; + struct sockaddr_in6 pr_ip6[]; #endif }; #define XPRISON_VERSION 3 @@ -168,7 +168,7 @@ struct prison { 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 sockaddr_in6 *pr_ip6; /* (p) v6 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 */ @@ -387,15 +387,18 @@ int prison_remote_ip4(struct ucred *cred int prison_check_ip4(const struct ucred *, const struct in_addr *); int prison_saddrsel_ip4(struct ucred *, struct in_addr *); #ifdef INET6 +struct in6_addr; +struct sockaddr_in6; int prison_equal_ip6(struct prison *, struct prison *); -int prison_get_ip6(struct ucred *, struct in6_addr *); -int prison_local_ip6(struct ucred *, struct in6_addr *, int); -int prison_remote_ip6(struct ucred *, struct in6_addr *); -int prison_check_ip6(struct ucred *, struct in6_addr *); -int prison_saddrsel_ip6(struct ucred *, struct in6_addr *); +int prison_get_ip6(struct ucred *, struct sockaddr_in6 *); +int prison_local_ip6(struct ucred *, struct sockaddr_in6 *, int); +int prison_remote_ip6(struct ucred *, struct sockaddr_in6 *); +int prison_check_ip6(struct ucred *, const struct sockaddr_in6 *); +int prison_check_in6(struct ucred *, const struct in6_addr *, uint32_t); +int prison_saddrsel_ip6(struct ucred *, struct sockaddr_in6 *); #endif int prison_check_af(struct ucred *cred, int af); -int prison_if(struct ucred *cred, struct sockaddr *sa); +int prison_if(struct ucred *cred, const struct sockaddr *sa); char *prison_name(struct prison *, struct prison *); int prison_priv_check(struct ucred *cred, int priv); int sysctl_jail_param(SYSCTL_HANDLER_ARGS);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201312181436.rBIEajVG030760>