diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2016-10-07 15:10:20 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2017-01-10 09:53:31 +0100 |
commit | c40e45b75eb76d79a05c7fa85c1fa9b5c728a12f (patch) | |
tree | ad4f2519067709f00ab98b3c591186c26dc3a21f /freebsd/sys/netinet6/scope6.c | |
parent | userspace-header-gen.py: Simplify program ports (diff) | |
download | rtems-libbsd-c40e45b75eb76d79a05c7fa85c1fa9b5c728a12f.tar.bz2 |
Update to FreeBSD head 2016-08-23
Git mirror commit 9fe7c416e6abb28b1398fd3e5687099846800cfd.
Diffstat (limited to 'freebsd/sys/netinet6/scope6.c')
-rw-r--r-- | freebsd/sys/netinet6/scope6.c | 278 |
1 files changed, 161 insertions, 117 deletions
diff --git a/freebsd/sys/netinet6/scope6.c b/freebsd/sys/netinet6/scope6.c index 2ccd2f7a..0f8ead2d 100644 --- a/freebsd/sys/netinet6/scope6.c +++ b/freebsd/sys/netinet6/scope6.c @@ -41,9 +41,11 @@ __FBSDID("$FreeBSD$"); #include <sys/sockio.h> #include <sys/systm.h> #include <sys/queue.h> +#include <sys/sysctl.h> #include <sys/syslog.h> #include <net/if.h> +#include <net/if_var.h> #include <net/vnet.h> #include <netinet/in.h> @@ -58,6 +60,11 @@ VNET_DEFINE(int, ip6_use_defzone) = 1; #else VNET_DEFINE(int, ip6_use_defzone) = 0; #endif +VNET_DEFINE(int, deembed_scopeid) = 1; +SYSCTL_DECL(_net_inet6_ip6); +SYSCTL_INT(_net_inet6_ip6, OID_AUTO, deembed_scopeid, CTLFLAG_VNET | CTLFLAG_RW, + &VNET_NAME(deembed_scopeid), 0, + "Extract embedded zone ID and set it to sin6_scope_id in sockaddr_in6."); /* * The scope6_lock protects the global sid default stored in @@ -95,22 +102,14 @@ scope6_ifattach(struct ifnet *ifp) { struct scope6_id *sid; - sid = (struct scope6_id *)malloc(sizeof(*sid), M_IFADDR, M_WAITOK); - bzero(sid, sizeof(*sid)); - + sid = malloc(sizeof(*sid), M_IFADDR, M_WAITOK | M_ZERO); /* * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard. * Should we rather hardcode here? */ sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index; sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index; -#ifdef MULTI_SCOPE - /* by default, we don't care about scope boundary for these scopes. */ - sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1; - sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1; -#endif - - return sid; + return (sid); } void @@ -230,62 +229,24 @@ scope6_get(struct ifnet *ifp, struct scope6_id *idlist) * Get a scope of the address. Node-local, link-local, site-local or global. */ int -in6_addrscope(struct in6_addr *addr) +in6_addrscope(const struct in6_addr *addr) { - int scope; - - if (addr->s6_addr[0] == 0xfe) { - scope = addr->s6_addr[1] & 0xc0; - - switch (scope) { - case 0x80: - return IPV6_ADDR_SCOPE_LINKLOCAL; - break; - case 0xc0: - return IPV6_ADDR_SCOPE_SITELOCAL; - break; - default: - return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */ - break; - } - } - - - if (addr->s6_addr[0] == 0xff) { - scope = addr->s6_addr[1] & 0x0f; + if (IN6_IS_ADDR_MULTICAST(addr)) { /* - * due to other scope such as reserved, - * return scope doesn't work. + * Addresses with reserved value F must be treated as + * global multicast addresses. */ - switch (scope) { - case IPV6_ADDR_SCOPE_INTFACELOCAL: - return IPV6_ADDR_SCOPE_INTFACELOCAL; - break; - case IPV6_ADDR_SCOPE_LINKLOCAL: - return IPV6_ADDR_SCOPE_LINKLOCAL; - break; - case IPV6_ADDR_SCOPE_SITELOCAL: - return IPV6_ADDR_SCOPE_SITELOCAL; - break; - default: - return IPV6_ADDR_SCOPE_GLOBAL; - break; - } + if (IPV6_ADDR_MC_SCOPE(addr) == 0x0f) + return (IPV6_ADDR_SCOPE_GLOBAL); + return (IPV6_ADDR_MC_SCOPE(addr)); } - - /* - * Regard loopback and unspecified addresses as global, since - * they have no ambiguity. - */ - if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) { - if (addr->s6_addr[15] == 1) /* loopback */ - return IPV6_ADDR_SCOPE_LINKLOCAL; - if (addr->s6_addr[15] == 0) /* unspecified */ - return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */ - } - - return IPV6_ADDR_SCOPE_GLOBAL; + if (IN6_IS_ADDR_LINKLOCAL(addr) || + IN6_IS_ADDR_LOOPBACK(addr)) + return (IPV6_ADDR_SCOPE_LINKLOCAL); + if (IN6_IS_ADDR_SITELOCAL(addr)) + return (IPV6_ADDR_SCOPE_SITELOCAL); + return (IPV6_ADDR_SCOPE_GLOBAL); } /* @@ -359,7 +320,6 @@ scope6_addr2default(struct in6_addr *addr) int sa6_embedscope(struct sockaddr_in6 *sin6, int defaultok) { - struct ifnet *ifp; u_int32_t zoneid; if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok) @@ -374,15 +334,11 @@ sa6_embedscope(struct sockaddr_in6 *sin6, int defaultok) * zone IDs assuming a one-to-one mapping between interfaces * and links. */ - if (V_if_index < zoneid) - return (ENXIO); - ifp = ifnet_byindex(zoneid); - if (ifp == NULL) /* XXX: this can happen for some OS */ + if (V_if_index < zoneid || ifnet_byindex(zoneid) == NULL) return (ENXIO); /* XXX assignment to 16bit from 32bit variable */ sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff); - sin6->sin6_scope_id = 0; } @@ -398,12 +354,6 @@ sa6_recoverscope(struct sockaddr_in6 *sin6) char ip6buf[INET6_ADDRSTRLEN]; u_int32_t zoneid; - if (sin6->sin6_scope_id != 0) { - log(LOG_NOTICE, - "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n", - ip6_sprintf(ip6buf, &sin6->sin6_addr), sin6->sin6_scope_id); - /* XXX: proceed anyway... */ - } if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) || IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) { /* @@ -414,8 +364,19 @@ sa6_recoverscope(struct sockaddr_in6 *sin6) /* sanity check */ if (V_if_index < zoneid) return (ENXIO); +#if 0 + /* XXX: Disabled due to possible deadlock. */ if (!ifnet_byindex(zoneid)) return (ENXIO); +#endif + if (sin6->sin6_scope_id != 0 && + zoneid != sin6->sin6_scope_id) { + log(LOG_NOTICE, + "%s: embedded scope mismatch: %s%%%d. " + "sin6_scope_id was overridden\n", __func__, + ip6_sprintf(ip6buf, &sin6->sin6_addr), + sin6->sin6_scope_id); + } sin6->sin6_addr.s6_addr16[1] = 0; sin6->sin6_scope_id = zoneid; } @@ -438,63 +399,35 @@ in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id) u_int32_t zoneid = 0; struct scope6_id *sid; - IF_AFDATA_RLOCK(ifp); - - sid = SID(ifp); - -#ifdef DIAGNOSTIC - if (sid == NULL) { /* should not happen */ - panic("in6_setscope: scope array is NULL"); - /* NOTREACHED */ - } -#endif - /* * special case: the loopback address can only belong to a loopback * interface. */ if (IN6_IS_ADDR_LOOPBACK(in6)) { - if (!(ifp->if_flags & IFF_LOOPBACK)) { - IF_AFDATA_RUNLOCK(ifp); + if (!(ifp->if_flags & IFF_LOOPBACK)) return (EINVAL); - } else { - if (ret_id != NULL) - *ret_id = 0; /* there's no ambiguity */ + } else { + scope = in6_addrscope(in6); + if (scope == IPV6_ADDR_SCOPE_INTFACELOCAL || + scope == IPV6_ADDR_SCOPE_LINKLOCAL) { + /* + * Currently we use interface indeces as the + * zone IDs for interface-local and link-local + * scopes. + */ + zoneid = ifp->if_index; + in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */ + } else if (scope != IPV6_ADDR_SCOPE_GLOBAL) { + IF_AFDATA_RLOCK(ifp); + sid = SID(ifp); + zoneid = sid->s6id_list[scope]; IF_AFDATA_RUNLOCK(ifp); - return (0); } } - scope = in6_addrscope(in6); - switch (scope) { - case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */ - zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL]; - break; - - case IPV6_ADDR_SCOPE_LINKLOCAL: - zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]; - break; - - case IPV6_ADDR_SCOPE_SITELOCAL: - zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]; - break; - - case IPV6_ADDR_SCOPE_ORGLOCAL: - zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]; - break; - - default: - zoneid = 0; /* XXX: treat as global. */ - break; - } - IF_AFDATA_RUNLOCK(ifp); - if (ret_id != NULL) *ret_id = zoneid; - if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) - in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */ - return (0); } @@ -528,3 +461,114 @@ in6_getscope(struct in6_addr *in6) return (0); } + +/* + * Return pointer to ifnet structure, corresponding to the zone id of + * link-local scope. + */ +struct ifnet* +in6_getlinkifnet(uint32_t zoneid) +{ + + return (ifnet_byindex((u_short)zoneid)); +} + +/* + * Return zone id for the specified scope. + */ +uint32_t +in6_getscopezone(const struct ifnet *ifp, int scope) +{ + + if (scope == IPV6_ADDR_SCOPE_INTFACELOCAL || + scope == IPV6_ADDR_SCOPE_LINKLOCAL) + return (ifp->if_index); + if (scope >= 0 && scope < IPV6_ADDR_SCOPES_COUNT) + return (SID(ifp)->s6id_list[scope]); + return (0); +} + +/* + * Extracts scope from adddress @dst, stores cleared address + * inside @dst and zone inside @scopeid + */ +void +in6_splitscope(const struct in6_addr *src, struct in6_addr *dst, + uint32_t *scopeid) +{ + uint32_t zoneid; + + *dst = *src; + zoneid = ntohs(in6_getscope(dst)); + in6_clearscope(dst); + *scopeid = zoneid; +} + +/* + * This function is for checking sockaddr_in6 structure passed + * from the application level (usually). + * + * sin6_scope_id should be set for link-local unicast, link-local and + * interface-local multicast addresses. + * + * If it is zero, then look into default zone ids. If default zone id is + * not set or disabled, then return error. + */ +int +sa6_checkzone(struct sockaddr_in6 *sa6) +{ + int scope; + + scope = in6_addrscope(&sa6->sin6_addr); + if (scope == IPV6_ADDR_SCOPE_GLOBAL) + return (sa6->sin6_scope_id ? EINVAL: 0); + if (IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr) && + scope != IPV6_ADDR_SCOPE_LINKLOCAL && + scope != IPV6_ADDR_SCOPE_INTFACELOCAL) { + if (sa6->sin6_scope_id == 0 && V_ip6_use_defzone != 0) + sa6->sin6_scope_id = V_sid_default.s6id_list[scope]; + return (0); + } + /* + * Since ::1 address always configured on the lo0, we can + * automatically set its zone id, when it is not specified. + * Return error, when specified zone id doesn't match with + * actual value. + */ + if (IN6_IS_ADDR_LOOPBACK(&sa6->sin6_addr)) { + if (sa6->sin6_scope_id == 0) + sa6->sin6_scope_id = in6_getscopezone(V_loif, scope); + else if (sa6->sin6_scope_id != in6_getscopezone(V_loif, scope)) + return (EADDRNOTAVAIL); + } + /* XXX: we can validate sin6_scope_id here */ + if (sa6->sin6_scope_id != 0) + return (0); + if (V_ip6_use_defzone != 0) + sa6->sin6_scope_id = V_sid_default.s6id_list[scope]; + /* Return error if we can't determine zone id */ + return (sa6->sin6_scope_id ? 0: EADDRNOTAVAIL); +} + +/* + * This function is similar to sa6_checkzone, but it uses given ifp + * to initialize sin6_scope_id. + */ +int +sa6_checkzone_ifp(struct ifnet *ifp, struct sockaddr_in6 *sa6) +{ + int scope; + + scope = in6_addrscope(&sa6->sin6_addr); + if (scope == IPV6_ADDR_SCOPE_LINKLOCAL || + scope == IPV6_ADDR_SCOPE_INTFACELOCAL) { + if (sa6->sin6_scope_id == 0) { + sa6->sin6_scope_id = in6_getscopezone(ifp, scope); + return (0); + } else if (sa6->sin6_scope_id != in6_getscopezone(ifp, scope)) + return (EADDRNOTAVAIL); + } + return (sa6_checkzone(sa6)); +} + + |