scope6.c revision 243029
1139823Simp/*-
21541Srgrimes * Copyright (C) 2000 WIDE Project.
31541Srgrimes * All rights reserved.
41541Srgrimes *
51541Srgrimes * Redistribution and use in source and binary forms, with or without
61541Srgrimes * modification, are permitted provided that the following conditions
71541Srgrimes * are met:
81541Srgrimes * 1. Redistributions of source code must retain the above copyright
91541Srgrimes *    notice, this list of conditions and the following disclaimer.
101541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111541Srgrimes *    notice, this list of conditions and the following disclaimer in the
121541Srgrimes *    documentation and/or other materials provided with the distribution.
131541Srgrimes * 3. Neither the name of the project nor the names of its contributors
141541Srgrimes *    may be used to endorse or promote products derived from this software
151541Srgrimes *    without specific prior written permission.
161541Srgrimes *
171541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
181541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
211541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271541Srgrimes * SUCH DAMAGE.
281541Srgrimes *
291541Srgrimes *	$KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $
301541Srgrimes */
311541Srgrimes
3236503Speter#include <sys/cdefs.h>
331541Srgrimes__FBSDID("$FreeBSD: head/sys/netinet6/scope6.c 243029 2012-11-14 17:23:48Z ae $");
341541Srgrimes
3583651Speter#include <sys/param.h>
3683651Speter#include <sys/malloc.h>
3783651Speter#include <sys/mbuf.h>
381541Srgrimes#include <sys/socket.h>
391541Srgrimes#include <sys/systm.h>
401541Srgrimes#include <sys/queue.h>
411541Srgrimes#include <sys/syslog.h>
421541Srgrimes
4383651Speter#include <net/if.h>
44100134Salfred#include <net/vnet.h>
45100134Salfred
461541Srgrimes#include <netinet/in.h>
4748274Speter
4848274Speter#include <netinet/ip6.h>
4960041Sphk#include <netinet6/in6_var.h>
5031886Sbde#include <netinet6/ip6_var.h>
511541Srgrimes#include <netinet6/scope6_var.h>
521541Srgrimes
531541Srgrimes#ifdef ENABLE_DEFAULT_SCOPE
541541SrgrimesVNET_DEFINE(int, ip6_use_defzone) = 1;
551541Srgrimes#else
56150634SjhbVNET_DEFINE(int, ip6_use_defzone) = 0;
571541Srgrimes#endif
581541Srgrimes
599336Sdfr/*
6083700Speter * The scope6_lock protects the global sid default stored in
612997Swollman * sid_default below.
622997Swollman */
6383651Speterstatic struct mtx scope6_lock;
641541Srgrimes#define	SCOPE6_LOCK_INIT()	mtx_init(&scope6_lock, "scope6_lock", NULL, MTX_DEF)
653305Sphk#define	SCOPE6_LOCK()		mtx_lock(&scope6_lock)
6612662Sdg#define	SCOPE6_UNLOCK()		mtx_unlock(&scope6_lock)
6712662Sdg#define	SCOPE6_LOCK_ASSERT()	mtx_assert(&scope6_lock, MA_OWNED)
6892783Sjeff
693305Sphkstatic VNET_DEFINE(struct scope6_id, sid_default);
701541Srgrimes#define	V_sid_default			VNET(sid_default)
719336Sdfr
7283651Speter#define SID(ifp) \
731541Srgrimes	(((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->scope6_id)
7483651Speter
751541Srgrimesvoid
761541Srgrimesscope6_init(void)
771541Srgrimes{
781541Srgrimes
791541Srgrimes	bzero(&V_sid_default, sizeof(V_sid_default));
801541Srgrimes
811541Srgrimes	if (!IS_DEFAULT_VNET(curvnet))
8289094Smsmith		return;
8389094Smsmith
8489094Smsmith	SCOPE6_LOCK_INIT();
8589094Smsmith}
8689094Smsmith
871541Srgrimesstruct scope6_id *
881541Srgrimesscope6_ifattach(struct ifnet *ifp)
89129639Srwatson{
90129639Srwatson	struct scope6_id *sid;
9183651Speter
9283651Speter	sid = (struct scope6_id *)malloc(sizeof(*sid), M_IFADDR, M_WAITOK);
9312911Sphk	bzero(sid, sizeof(*sid));
9489094Smsmith
959336Sdfr	/*
96184588Sdfr	 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
979759Sbde	 * Should we rather hardcode here?
989759Sbde	 */
999759Sbde	sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index;
1009759Sbde	sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
101184588Sdfr#ifdef MULTI_SCOPE
1029759Sbde	/* by default, we don't care about scope boundary for these scopes. */
103168268Sjhb	sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
104168268Sjhb	sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
105168268Sjhb#endif
10638894Sbde
107129639Srwatson	return sid;
108129639Srwatson}
1099336Sdfr
1109336Sdfrvoid
1119336Sdfrscope6_ifdetach(struct scope6_id *sid)
112129639Srwatson{
1139336Sdfr
1149336Sdfr	free(sid, M_IFADDR);
1159336Sdfr}
1169336Sdfr
1179336Sdfrint
1189336Sdfrscope6_set(struct ifnet *ifp, struct scope6_id *idlist)
1199336Sdfr{
1209336Sdfr	int i;
1219336Sdfr	int error = 0;
1229336Sdfr	struct scope6_id *sid = NULL;
1239336Sdfr
1249336Sdfr	IF_AFDATA_LOCK(ifp);
1259336Sdfr	sid = SID(ifp);
1269336Sdfr
1279336Sdfr	if (!sid) {	/* paranoid? */
1289336Sdfr		IF_AFDATA_UNLOCK(ifp);
1299336Sdfr		return (EINVAL);
1309336Sdfr	}
1319336Sdfr
1329336Sdfr	/*
1339336Sdfr	 * XXX: We need more consistency checks of the relationship among
1349336Sdfr	 * scopes (e.g. an organization should be larger than a site).
1359336Sdfr	 */
1369336Sdfr
1379336Sdfr	/*
1389336Sdfr	 * TODO(XXX): after setting, we should reflect the changes to
1399336Sdfr	 * interface addresses, routing table entries, PCB entries...
1409336Sdfr	 */
141129639Srwatson
1429336Sdfr	for (i = 0; i < 16; i++) {
1439336Sdfr		if (idlist->s6id_list[i] &&
1449336Sdfr		    idlist->s6id_list[i] != sid->s6id_list[i]) {
1459336Sdfr			/*
1469336Sdfr			 * An interface zone ID must be the corresponding
1479336Sdfr			 * interface index by definition.
1489336Sdfr			 */
1499336Sdfr			if (i == IPV6_ADDR_SCOPE_INTFACELOCAL &&
1509336Sdfr			    idlist->s6id_list[i] != ifp->if_index) {
1519336Sdfr				IF_AFDATA_UNLOCK(ifp);
1529336Sdfr				return (EINVAL);
1539336Sdfr			}
1549336Sdfr
1559336Sdfr			if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
1569336Sdfr			    idlist->s6id_list[i] > V_if_index) {
1579336Sdfr				/*
1589336Sdfr				 * XXX: theoretically, there should be no
1599336Sdfr				 * relationship between link IDs and interface
1609336Sdfr				 * IDs, but we check the consistency for
1619336Sdfr				 * safety in later use.
1629336Sdfr				 */
1639336Sdfr				IF_AFDATA_UNLOCK(ifp);
1649336Sdfr				return (EINVAL);
1659336Sdfr			}
1669336Sdfr
1679336Sdfr			/*
1689336Sdfr			 * XXX: we must need lots of work in this case,
169102236Sphk			 * but we simply set the new value in this initial
170102236Sphk			 * implementation.
1719336Sdfr			 */
172129639Srwatson			sid->s6id_list[i] = idlist->s6id_list[i];
173102236Sphk		}
174102236Sphk	}
175102236Sphk	IF_AFDATA_UNLOCK(ifp);
176102236Sphk
177102236Sphk	return (error);
178102236Sphk}
179102236Sphk
180102236Sphkint
181102236Sphkscope6_get(struct ifnet *ifp, struct scope6_id *idlist)
182102236Sphk{
183102236Sphk	/* We only need to lock the interface's afdata for SID() to work. */
184102236Sphk	IF_AFDATA_LOCK(ifp);
185102236Sphk	struct scope6_id *sid = SID(ifp);
186102236Sphk
187102236Sphk	if (sid == NULL) {	/* paranoid? */
1889336Sdfr		IF_AFDATA_UNLOCK(ifp);
1899336Sdfr		return (EINVAL);
1909336Sdfr	}
1919336Sdfr
1929336Sdfr	*idlist = *sid;
1939336Sdfr
1949336Sdfr	IF_AFDATA_UNLOCK(ifp);
1959336Sdfr	return (0);
1969336Sdfr}
1979336Sdfr
1989336Sdfr
1999336Sdfr/*
2009336Sdfr * Get a scope of the address. Node-local, link-local, site-local or global.
201129639Srwatson */
2029336Sdfrint
2039336Sdfrin6_addrscope(struct in6_addr *addr)
2049336Sdfr{
2059336Sdfr	int scope;
206129639Srwatson
2079336Sdfr	if (addr->s6_addr[0] == 0xfe) {
2089336Sdfr		scope = addr->s6_addr[1] & 0xc0;
2099336Sdfr
2109336Sdfr		switch (scope) {
2119336Sdfr		case 0x80:
2129336Sdfr			return IPV6_ADDR_SCOPE_LINKLOCAL;
2139336Sdfr			break;
2149336Sdfr		case 0xc0:
215129639Srwatson			return IPV6_ADDR_SCOPE_SITELOCAL;
2169336Sdfr			break;
2179336Sdfr		default:
2189336Sdfr			return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
2199336Sdfr			break;
2209336Sdfr		}
2219336Sdfr	}
2229336Sdfr
2239336Sdfr
2249336Sdfr	if (addr->s6_addr[0] == 0xff) {
2259336Sdfr		scope = addr->s6_addr[1] & 0x0f;
2269336Sdfr
2279336Sdfr		/*
2289336Sdfr		 * due to other scope such as reserved,
2299336Sdfr		 * return scope doesn't work.
2309336Sdfr		 */
231129639Srwatson		switch (scope) {
2329336Sdfr		case IPV6_ADDR_SCOPE_INTFACELOCAL:
2339336Sdfr			return IPV6_ADDR_SCOPE_INTFACELOCAL;
2349336Sdfr			break;
2359336Sdfr		case IPV6_ADDR_SCOPE_LINKLOCAL:
2369336Sdfr			return IPV6_ADDR_SCOPE_LINKLOCAL;
2379336Sdfr			break;
2389336Sdfr		case IPV6_ADDR_SCOPE_SITELOCAL:
2399336Sdfr			return IPV6_ADDR_SCOPE_SITELOCAL;
2409336Sdfr			break;
2419336Sdfr		default:
2429336Sdfr			return IPV6_ADDR_SCOPE_GLOBAL;
2439336Sdfr			break;
244129639Srwatson		}
2459336Sdfr	}
2469336Sdfr
2479336Sdfr	/*
2489336Sdfr	 * Regard loopback and unspecified addresses as global, since
2499336Sdfr	 * they have no ambiguity.
2509336Sdfr	 */
2519336Sdfr	if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
2529336Sdfr		if (addr->s6_addr[15] == 1) /* loopback */
253129639Srwatson			return IPV6_ADDR_SCOPE_LINKLOCAL;
2549336Sdfr		if (addr->s6_addr[15] == 0) /* unspecified */
2559336Sdfr			return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */
2569336Sdfr	}
2579336Sdfr
2589336Sdfr	return IPV6_ADDR_SCOPE_GLOBAL;
2599336Sdfr}
2609336Sdfr
2619336Sdfr/*
2629336Sdfr * ifp - note that this might be NULL
2639336Sdfr */
2649336Sdfr
265129639Srwatsonvoid
2669336Sdfrscope6_setdefault(struct ifnet *ifp)
2679336Sdfr{
2689336Sdfr
2699336Sdfr	/*
2709336Sdfr	 * Currently, this function just sets the default "interfaces"
2719336Sdfr	 * and "links" according to the given interface.
2729336Sdfr	 * We might eventually have to separate the notion of "link" from
2739336Sdfr	 * "interface" and provide a user interface to set the default.
2749336Sdfr	 */
2759336Sdfr	SCOPE6_LOCK();
2769336Sdfr	if (ifp) {
277129639Srwatson		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] =
2789336Sdfr			ifp->if_index;
2799336Sdfr		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
2809336Sdfr			ifp->if_index;
2819336Sdfr	} else {
2829336Sdfr		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0;
2839336Sdfr		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
2849336Sdfr	}
2859336Sdfr	SCOPE6_UNLOCK();
2869336Sdfr}
2879336Sdfr
2889336Sdfrint
2899336Sdfrscope6_get_default(struct scope6_id *idlist)
2909336Sdfr{
2919336Sdfr
292129639Srwatson	SCOPE6_LOCK();
2939336Sdfr	*idlist = V_sid_default;
2949336Sdfr	SCOPE6_UNLOCK();
2959336Sdfr
2969336Sdfr	return (0);
2979336Sdfr}
2989336Sdfr
2999336Sdfru_int32_t
3009336Sdfrscope6_addr2default(struct in6_addr *addr)
3019336Sdfr{
3029336Sdfr	u_int32_t id;
3039336Sdfr
3049336Sdfr	/*
3059336Sdfr	 * special case: The loopback address should be considered as
3069336Sdfr	 * link-local, but there's no ambiguity in the syntax.
3079336Sdfr	 */
3089336Sdfr	if (IN6_IS_ADDR_LOOPBACK(addr))
309129639Srwatson		return (0);
3109336Sdfr
3119336Sdfr	/*
3129336Sdfr	 * XXX: 32-bit read is atomic on all our platforms, is it OK
3139336Sdfr	 * not to lock here?
3149336Sdfr	 */
3159336Sdfr	SCOPE6_LOCK();
3169336Sdfr	id = V_sid_default.s6id_list[in6_addrscope(addr)];
3179336Sdfr	SCOPE6_UNLOCK();
3189336Sdfr	return (id);
3199336Sdfr}
3209336Sdfr
3219336Sdfr/*
3229336Sdfr * Validate the specified scope zone ID in the sin6_scope_id field.  If the ID
3239336Sdfr * is unspecified (=0), needs to be specified, and the default zone ID can be
3249336Sdfr * used, the default value will be used.
3259336Sdfr * This routine then generates the kernel-internal form: if the address scope
326129639Srwatson * of is interface-local or link-local, embed the interface index in the
3279336Sdfr * address.
3289336Sdfr */
3299336Sdfrint
3309336Sdfrsa6_embedscope(struct sockaddr_in6 *sin6, int defaultok)
3319336Sdfr{
3329336Sdfr	struct ifnet *ifp;
3339336Sdfr	u_int32_t zoneid;
3349336Sdfr
3359336Sdfr	if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok)
3369336Sdfr		zoneid = scope6_addr2default(&sin6->sin6_addr);
3379336Sdfr
3389336Sdfr	if (zoneid != 0 &&
3399336Sdfr	    (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
3409336Sdfr	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) {
3419336Sdfr		/*
3429336Sdfr		 * At this moment, we only check interface-local and
343129639Srwatson		 * link-local scope IDs, and use interface indices as the
3449336Sdfr		 * zone IDs assuming a one-to-one mapping between interfaces
3459336Sdfr		 * and links.
3469336Sdfr		 */
3479336Sdfr		if (V_if_index < zoneid)
3489336Sdfr			return (ENXIO);
3499336Sdfr		ifp = ifnet_byindex(zoneid);
3509336Sdfr		if (ifp == NULL) /* XXX: this can happen for some OS */
3519336Sdfr			return (ENXIO);
3529336Sdfr
3539336Sdfr		/* XXX assignment to 16bit from 32bit variable */
3549336Sdfr		sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);
3559336Sdfr
3569336Sdfr		sin6->sin6_scope_id = 0;
3579336Sdfr	}
3589336Sdfr
3599336Sdfr	return 0;
3609336Sdfr}
361129639Srwatson
3629336Sdfr/*
3639336Sdfr * generate standard sockaddr_in6 from embedded form.
3649336Sdfr */
3659336Sdfrint
3669336Sdfrsa6_recoverscope(struct sockaddr_in6 *sin6)
3679336Sdfr{
3689336Sdfr	char ip6buf[INET6_ADDRSTRLEN];
3699336Sdfr	u_int32_t zoneid;
3709336Sdfr
3719336Sdfr	if (sin6->sin6_scope_id != 0) {
3729336Sdfr		log(LOG_NOTICE,
3739336Sdfr		    "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
3749336Sdfr		    ip6_sprintf(ip6buf, &sin6->sin6_addr), sin6->sin6_scope_id);
375129639Srwatson		/* XXX: proceed anyway... */
3769336Sdfr	}
3779336Sdfr	if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
3789336Sdfr	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
3799336Sdfr		/*
3809336Sdfr		 * KAME assumption: link id == interface id
3819336Sdfr		 */
3829336Sdfr		zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
3839336Sdfr		if (zoneid) {
3849336Sdfr			/* sanity check */
3859336Sdfr			if (V_if_index < zoneid)
3869336Sdfr				return (ENXIO);
3879336Sdfr			if (!ifnet_byindex(zoneid))
3889336Sdfr				return (ENXIO);
3899336Sdfr			sin6->sin6_addr.s6_addr16[1] = 0;
3909336Sdfr			sin6->sin6_scope_id = zoneid;
3919336Sdfr		}
3929336Sdfr	}
393129639Srwatson
3949336Sdfr	return 0;
3959336Sdfr}
3969336Sdfr
3979336Sdfr/*
3989336Sdfr * Determine the appropriate scope zone ID for in6 and ifp.  If ret_id is
3999336Sdfr * non NULL, it is set to the zone ID.  If the zone ID needs to be embedded
4009336Sdfr * in the in6_addr structure, in6 will be modified.
4019336Sdfr *
4029336Sdfr * ret_id - unnecessary?
4039336Sdfr */
4049336Sdfrint
4059336Sdfrin6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id)
4069336Sdfr{
4079336Sdfr	int scope;
4089336Sdfr	u_int32_t zoneid = 0;
4099336Sdfr	struct scope6_id *sid;
4109336Sdfr
4119336Sdfr	IF_AFDATA_LOCK(ifp);
4129336Sdfr
4139336Sdfr	sid = SID(ifp);
4149336Sdfr
4159336Sdfr#ifdef DIAGNOSTIC
416129639Srwatson	if (sid == NULL) { /* should not happen */
4179336Sdfr		panic("in6_setscope: scope array is NULL");
4189336Sdfr		/* NOTREACHED */
4199336Sdfr	}
4209336Sdfr#endif
4219336Sdfr
4229336Sdfr	/*
4239336Sdfr	 * special case: the loopback address can only belong to a loopback
4249336Sdfr	 * interface.
4259336Sdfr	 */
4269336Sdfr	if (IN6_IS_ADDR_LOOPBACK(in6)) {
4279336Sdfr		if (!(ifp->if_flags & IFF_LOOPBACK)) {
4289336Sdfr			IF_AFDATA_UNLOCK(ifp);
4299336Sdfr			return (EINVAL);
4309336Sdfr		} else {
4319336Sdfr			if (ret_id != NULL)
4329336Sdfr				*ret_id = 0; /* there's no ambiguity */
4339336Sdfr			IF_AFDATA_UNLOCK(ifp);
4349336Sdfr			return (0);
4359336Sdfr		}
436129639Srwatson	}
4379336Sdfr
4389336Sdfr	scope = in6_addrscope(in6);
4399336Sdfr	switch (scope) {
4409336Sdfr	case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
4419336Sdfr		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
4429336Sdfr		break;
4439336Sdfr
4449336Sdfr	case IPV6_ADDR_SCOPE_LINKLOCAL:
4459336Sdfr		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
4469336Sdfr		break;
4479336Sdfr
4489336Sdfr	case IPV6_ADDR_SCOPE_SITELOCAL:
449129639Srwatson		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
4509336Sdfr		break;
4519336Sdfr
4529336Sdfr	case IPV6_ADDR_SCOPE_ORGLOCAL:
4539336Sdfr		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
4549336Sdfr		break;
4559336Sdfr
4569336Sdfr	default:
4579336Sdfr		zoneid = 0;	/* XXX: treat as global. */
4589336Sdfr		break;
4599336Sdfr	}
4609336Sdfr	IF_AFDATA_UNLOCK(ifp);
4619336Sdfr
4629336Sdfr	if (ret_id != NULL)
463129639Srwatson		*ret_id = zoneid;
4649336Sdfr
4659336Sdfr	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
4669336Sdfr		in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */
4679336Sdfr
4689336Sdfr	return (0);
4699336Sdfr}
4709336Sdfr
4719336Sdfr/*
472129639Srwatson * Just clear the embedded scope identifier.  Return 0 if the original address
4739336Sdfr * is intact; return non 0 if the address is modified.
4749336Sdfr */
4759336Sdfrint
4769336Sdfrin6_clearscope(struct in6_addr *in6)
4779336Sdfr{
4789336Sdfr	int modified = 0;
4799336Sdfr
480129639Srwatson	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
4819336Sdfr		if (in6->s6_addr16[1] != 0)
4829336Sdfr			modified = 1;
4839336Sdfr		in6->s6_addr16[1] = 0;
4849336Sdfr	}
4859336Sdfr
4869336Sdfr	return (modified);
4879336Sdfr}
488129639Srwatson
4899336Sdfr/*
4909336Sdfr * Return the scope identifier or zero.
4919336Sdfr */
4929336Sdfruint16_t
4939336Sdfrin6_getscope(struct in6_addr *in6)
4949336Sdfr{
4959336Sdfr
4969336Sdfr	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
497129639Srwatson		return (in6->s6_addr16[1]);
4989336Sdfr
4999336Sdfr	return (0);
5009336Sdfr}
5019336Sdfr