scope6.c revision 190787
1139826Simp/*-
262587Sitojun * Copyright (C) 2000 WIDE Project.
362587Sitojun * All rights reserved.
4120941Sume *
562587Sitojun * Redistribution and use in source and binary forms, with or without
662587Sitojun * modification, are permitted provided that the following conditions
762587Sitojun * are met:
862587Sitojun * 1. Redistributions of source code must retain the above copyright
962587Sitojun *    notice, this list of conditions and the following disclaimer.
1062587Sitojun * 2. Redistributions in binary form must reproduce the above copyright
1162587Sitojun *    notice, this list of conditions and the following disclaimer in the
1262587Sitojun *    documentation and/or other materials provided with the distribution.
1362587Sitojun * 3. Neither the name of the project nor the names of its contributors
1462587Sitojun *    may be used to endorse or promote products derived from this software
1562587Sitojun *    without specific prior written permission.
16120941Sume *
1762587Sitojun * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
1862587Sitojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1962587Sitojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2062587Sitojun * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2162587Sitojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2262587Sitojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2362587Sitojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2462587Sitojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2562587Sitojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2662587Sitojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2762587Sitojun * SUCH DAMAGE.
28174510Sobrien *
29174510Sobrien *	$KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $
3062587Sitojun */
3162587Sitojun
32174510Sobrien#include <sys/cdefs.h>
33174510Sobrien__FBSDID("$FreeBSD: head/sys/netinet6/scope6.c 190787 2009-04-06 22:29:41Z zec $");
34174510Sobrien
35189106Sbz#include "opt_route.h"
36189106Sbz
3762587Sitojun#include <sys/param.h>
3862587Sitojun#include <sys/malloc.h>
3962587Sitojun#include <sys/mbuf.h>
4062587Sitojun#include <sys/socket.h>
4162587Sitojun#include <sys/systm.h>
4278064Sume#include <sys/queue.h>
43148385Sume#include <sys/syslog.h>
44181803Sbz#include <sys/vimage.h>
4562587Sitojun
4662587Sitojun#include <net/route.h>
4762587Sitojun#include <net/if.h>
48185571Sbz#include <net/vnet.h>
4962587Sitojun
5062587Sitojun#include <netinet/in.h>
51185571Sbz
52183550Szec#include <netinet/ip6.h>
5362587Sitojun#include <netinet6/in6_var.h>
5462587Sitojun#include <netinet6/scope6_var.h>
55185571Sbz#include <netinet6/vinet6.h>
5662587Sitojun
57148385Sume
58121343Sume/*
59138184Sgnn * The scope6_lock protects the global sid default stored in
60138184Sgnn * sid_default below.
61121343Sume */
62121343Sumestatic struct mtx scope6_lock;
63121343Sume#define	SCOPE6_LOCK_INIT()	mtx_init(&scope6_lock, "scope6_lock", NULL, MTX_DEF)
64121343Sume#define	SCOPE6_LOCK()		mtx_lock(&scope6_lock)
65121343Sume#define	SCOPE6_UNLOCK()		mtx_unlock(&scope6_lock)
66121343Sume#define	SCOPE6_LOCK_ASSERT()	mtx_assert(&scope6_lock, MA_OWNED)
67121343Sume
68185088Szec#ifdef VIMAGE_GLOBALS
69121161Sumestatic struct scope6_id sid_default;
70185088Szecint ip6_use_defzone;
71185088Szec#endif
72185088Szec
73121161Sume#define SID(ifp) \
74121161Sume	(((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->scope6_id)
7562587Sitojun
7662587Sitojunvoid
77171259Sdelphijscope6_init(void)
78121161Sume{
79183550Szec	INIT_VNET_INET6(curvnet);
80121161Sume
81185088Szec#ifdef ENABLE_DEFAULT_SCOPE
82185088Szec	V_ip6_use_defzone = 1;
83185088Szec#else
84185088Szec	V_ip6_use_defzone = 0;
85185088Szec#endif
86190787Szec	bzero(&V_sid_default, sizeof(V_sid_default));
87190787Szec
88190787Szec	if (!IS_DEFAULT_VNET(curvnet))
89190787Szec		return;
90190787Szec
91121343Sume	SCOPE6_LOCK_INIT();
92121161Sume}
93121161Sume
94121161Sumestruct scope6_id *
95171259Sdelphijscope6_ifattach(struct ifnet *ifp)
9662587Sitojun{
97121161Sume	struct scope6_id *sid;
9862587Sitojun
99121161Sume	sid = (struct scope6_id *)malloc(sizeof(*sid), M_IFADDR, M_WAITOK);
100121161Sume	bzero(sid, sizeof(*sid));
10162587Sitojun
10262587Sitojun	/*
10362587Sitojun	 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
10462587Sitojun	 * Should we rather hardcode here?
10562587Sitojun	 */
106121315Sume	sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index;
107121161Sume	sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
10862587Sitojun#ifdef MULTI_SCOPE
10962587Sitojun	/* by default, we don't care about scope boundary for these scopes. */
110121161Sume	sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
111121161Sume	sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
11262587Sitojun#endif
11362587Sitojun
114121161Sume	return sid;
11562587Sitojun}
11662587Sitojun
117121161Sumevoid
118171259Sdelphijscope6_ifdetach(struct scope6_id *sid)
119121161Sume{
120121161Sume
121121161Sume	free(sid, M_IFADDR);
122121161Sume}
123121161Sume
12462587Sitojunint
125171259Sdelphijscope6_set(struct ifnet *ifp, struct scope6_id *idlist)
12662587Sitojun{
127183550Szec	INIT_VNET_NET(ifp->if_vnet);
128138184Sgnn	int i;
12962587Sitojun	int error = 0;
130138184Sgnn	struct scope6_id *sid = NULL;
13162587Sitojun
132138184Sgnn	IF_AFDATA_LOCK(ifp);
133138184Sgnn	sid = SID(ifp);
134138184Sgnn
135138184Sgnn	if (!sid) {	/* paranoid? */
136138184Sgnn		IF_AFDATA_UNLOCK(ifp);
137120856Sume		return (EINVAL);
138138184Sgnn	}
13962587Sitojun
14062587Sitojun	/*
14162587Sitojun	 * XXX: We need more consistency checks of the relationship among
14262587Sitojun	 * scopes (e.g. an organization should be larger than a site).
14362587Sitojun	 */
14462587Sitojun
14562587Sitojun	/*
14662587Sitojun	 * TODO(XXX): after setting, we should reflect the changes to
147120941Sume	 * interface addresses, routing table entries, PCB entries...
14862587Sitojun	 */
14962587Sitojun
150121343Sume	SCOPE6_LOCK();
15162587Sitojun	for (i = 0; i < 16; i++) {
152121161Sume		if (idlist->s6id_list[i] &&
153121161Sume		    idlist->s6id_list[i] != sid->s6id_list[i]) {
154121315Sume			/*
155121315Sume			 * An interface zone ID must be the corresponding
156121315Sume			 * interface index by definition.
157121315Sume			 */
158121315Sume			if (i == IPV6_ADDR_SCOPE_INTFACELOCAL &&
159121315Sume			    idlist->s6id_list[i] != ifp->if_index) {
160138184Sgnn				IF_AFDATA_UNLOCK(ifp);
161138184Sgnn				SCOPE6_UNLOCK();
162121315Sume				return (EINVAL);
163121315Sume			}
164121315Sume
16562587Sitojun			if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
166181803Sbz			    idlist->s6id_list[i] > V_if_index) {
16762587Sitojun				/*
16862587Sitojun				 * XXX: theoretically, there should be no
16962587Sitojun				 * relationship between link IDs and interface
17062587Sitojun				 * IDs, but we check the consistency for
17162587Sitojun				 * safety in later use.
17262587Sitojun				 */
173138184Sgnn				IF_AFDATA_UNLOCK(ifp);
174138184Sgnn				SCOPE6_UNLOCK();
175120856Sume				return (EINVAL);
17662587Sitojun			}
17762587Sitojun
17862587Sitojun			/*
17962587Sitojun			 * XXX: we must need lots of work in this case,
18062587Sitojun			 * but we simply set the new value in this initial
18162587Sitojun			 * implementation.
18262587Sitojun			 */
183121161Sume			sid->s6id_list[i] = idlist->s6id_list[i];
18462587Sitojun		}
18562587Sitojun	}
186121343Sume	SCOPE6_UNLOCK();
187138184Sgnn	IF_AFDATA_UNLOCK(ifp);
18862587Sitojun
189120856Sume	return (error);
19062587Sitojun}
19162587Sitojun
19262587Sitojunint
193171259Sdelphijscope6_get(struct ifnet *ifp, struct scope6_id *idlist)
19462587Sitojun{
195138184Sgnn	/* We only need to lock the interface's afdata for SID() to work. */
196138184Sgnn	IF_AFDATA_LOCK(ifp);
197121161Sume	struct scope6_id *sid = SID(ifp);
198121161Sume
199138184Sgnn	if (sid == NULL) {	/* paranoid? */
200138184Sgnn		IF_AFDATA_UNLOCK(ifp);
201120856Sume		return (EINVAL);
202138184Sgnn	}
20362587Sitojun
204121343Sume	SCOPE6_LOCK();
205121161Sume	*idlist = *sid;
206121343Sume	SCOPE6_UNLOCK();
20762587Sitojun
208138184Sgnn	IF_AFDATA_UNLOCK(ifp);
209120856Sume	return (0);
21062587Sitojun}
21162587Sitojun
21262587Sitojun
21362587Sitojun/*
21462587Sitojun * Get a scope of the address. Node-local, link-local, site-local or global.
21562587Sitojun */
21662587Sitojunint
217171259Sdelphijin6_addrscope(struct in6_addr *addr)
21862587Sitojun{
21962587Sitojun	int scope;
22062587Sitojun
221121315Sume	if (addr->s6_addr[0] == 0xfe) {
222121315Sume		scope = addr->s6_addr[1] & 0xc0;
22362587Sitojun
22462587Sitojun		switch (scope) {
22562587Sitojun		case 0x80:
22662587Sitojun			return IPV6_ADDR_SCOPE_LINKLOCAL;
22762587Sitojun			break;
22862587Sitojun		case 0xc0:
22962587Sitojun			return IPV6_ADDR_SCOPE_SITELOCAL;
23062587Sitojun			break;
23162587Sitojun		default:
23262587Sitojun			return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
23362587Sitojun			break;
23462587Sitojun		}
23562587Sitojun	}
23662587Sitojun
23762587Sitojun
238121315Sume	if (addr->s6_addr[0] == 0xff) {
239121315Sume		scope = addr->s6_addr[1] & 0x0f;
24062587Sitojun
24162587Sitojun		/*
24262587Sitojun		 * due to other scope such as reserved,
24362587Sitojun		 * return scope doesn't work.
24462587Sitojun		 */
24562587Sitojun		switch (scope) {
246121315Sume		case IPV6_ADDR_SCOPE_INTFACELOCAL:
247121315Sume			return IPV6_ADDR_SCOPE_INTFACELOCAL;
24862587Sitojun			break;
24962587Sitojun		case IPV6_ADDR_SCOPE_LINKLOCAL:
25062587Sitojun			return IPV6_ADDR_SCOPE_LINKLOCAL;
25162587Sitojun			break;
25262587Sitojun		case IPV6_ADDR_SCOPE_SITELOCAL:
25362587Sitojun			return IPV6_ADDR_SCOPE_SITELOCAL;
25462587Sitojun			break;
25562587Sitojun		default:
25662587Sitojun			return IPV6_ADDR_SCOPE_GLOBAL;
25762587Sitojun			break;
25862587Sitojun		}
25962587Sitojun	}
26062587Sitojun
261121315Sume	/*
262121315Sume	 * Regard loopback and unspecified addresses as global, since
263121315Sume	 * they have no ambiguity.
264121315Sume	 */
26593128Sume	if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
266121315Sume		if (addr->s6_addr[15] == 1) /* loopback */
26762587Sitojun			return IPV6_ADDR_SCOPE_LINKLOCAL;
268121315Sume		if (addr->s6_addr[15] == 0) /* unspecified */
269121315Sume			return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */
27062587Sitojun	}
27162587Sitojun
27262587Sitojun	return IPV6_ADDR_SCOPE_GLOBAL;
27362587Sitojun}
27462587Sitojun
275171259Sdelphij/*
276171259Sdelphij * ifp - note that this might be NULL
277171259Sdelphij */
278171259Sdelphij
27962587Sitojunvoid
280171259Sdelphijscope6_setdefault(struct ifnet *ifp)
28162587Sitojun{
282183550Szec	INIT_VNET_INET6(ifp->if_vnet);
283183550Szec
28462587Sitojun	/*
285138184Sgnn	 * Currently, this function just sets the default "interfaces"
286121161Sume	 * and "links" according to the given interface.
28762587Sitojun	 * We might eventually have to separate the notion of "link" from
28862587Sitojun	 * "interface" and provide a user interface to set the default.
28962587Sitojun	 */
290121343Sume	SCOPE6_LOCK();
29162587Sitojun	if (ifp) {
292181803Sbz		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] =
29362587Sitojun			ifp->if_index;
294181803Sbz		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
295121161Sume			ifp->if_index;
296120941Sume	} else {
297181803Sbz		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0;
298181803Sbz		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
29962587Sitojun	}
300121343Sume	SCOPE6_UNLOCK();
30162587Sitojun}
30262587Sitojun
30362587Sitojunint
304171259Sdelphijscope6_get_default(struct scope6_id *idlist)
30562587Sitojun{
306183550Szec	INIT_VNET_INET6(curvnet);
307121343Sume
308121343Sume	SCOPE6_LOCK();
309181803Sbz	*idlist = V_sid_default;
310121343Sume	SCOPE6_UNLOCK();
31162587Sitojun
312120856Sume	return (0);
31362587Sitojun}
31462587Sitojun
31562587Sitojunu_int32_t
316171259Sdelphijscope6_addr2default(struct in6_addr *addr)
31762587Sitojun{
318183550Szec	INIT_VNET_INET6(curvnet);
319121343Sume	u_int32_t id;
320121343Sume
321121161Sume	/*
322121161Sume	 * special case: The loopback address should be considered as
323121161Sume	 * link-local, but there's no ambiguity in the syntax.
324121161Sume	 */
325121161Sume	if (IN6_IS_ADDR_LOOPBACK(addr))
326121161Sume		return (0);
327121161Sume
328121343Sume	/*
329121343Sume	 * XXX: 32-bit read is atomic on all our platforms, is it OK
330121343Sume	 * not to lock here?
331121343Sume	 */
332121343Sume	SCOPE6_LOCK();
333181803Sbz	id = V_sid_default.s6id_list[in6_addrscope(addr)];
334121343Sume	SCOPE6_UNLOCK();
335121343Sume	return (id);
33662587Sitojun}
337148385Sume
338148385Sume/*
339148385Sume * Validate the specified scope zone ID in the sin6_scope_id field.  If the ID
340148385Sume * is unspecified (=0), needs to be specified, and the default zone ID can be
341148385Sume * used, the default value will be used.
342148385Sume * This routine then generates the kernel-internal form: if the address scope
343148385Sume * of is interface-local or link-local, embed the interface index in the
344148385Sume * address.
345148385Sume */
346148385Sumeint
347171259Sdelphijsa6_embedscope(struct sockaddr_in6 *sin6, int defaultok)
348148385Sume{
349183550Szec	INIT_VNET_NET(curvnet);
350148385Sume	struct ifnet *ifp;
351148385Sume	u_int32_t zoneid;
352148385Sume
353148385Sume	if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok)
354148385Sume		zoneid = scope6_addr2default(&sin6->sin6_addr);
355148385Sume
356148385Sume	if (zoneid != 0 &&
357148385Sume	    (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
358148385Sume	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) {
359148385Sume		/*
360148385Sume		 * At this moment, we only check interface-local and
361148385Sume		 * link-local scope IDs, and use interface indices as the
362148385Sume		 * zone IDs assuming a one-to-one mapping between interfaces
363148385Sume		 * and links.
364148385Sume		 */
365181803Sbz		if (V_if_index < zoneid)
366148385Sume			return (ENXIO);
367148385Sume		ifp = ifnet_byindex(zoneid);
368148385Sume		if (ifp == NULL) /* XXX: this can happen for some OS */
369148385Sume			return (ENXIO);
370148385Sume
371148385Sume		/* XXX assignment to 16bit from 32bit variable */
372148385Sume		sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);
373148385Sume
374148385Sume		sin6->sin6_scope_id = 0;
375148385Sume	}
376148385Sume
377148385Sume	return 0;
378148385Sume}
379148385Sume
380148385Sume/*
381148385Sume * generate standard sockaddr_in6 from embedded form.
382148385Sume */
383148385Sumeint
384171259Sdelphijsa6_recoverscope(struct sockaddr_in6 *sin6)
385148385Sume{
386183550Szec	INIT_VNET_NET(curvnet);
387165118Sbz	char ip6buf[INET6_ADDRSTRLEN];
388148385Sume	u_int32_t zoneid;
389148385Sume
390148385Sume	if (sin6->sin6_scope_id != 0) {
391148385Sume		log(LOG_NOTICE,
392148385Sume		    "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
393165118Sbz		    ip6_sprintf(ip6buf, &sin6->sin6_addr), sin6->sin6_scope_id);
394148385Sume		/* XXX: proceed anyway... */
395148385Sume	}
396148385Sume	if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
397148385Sume	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
398148385Sume		/*
399148385Sume		 * KAME assumption: link id == interface id
400148385Sume		 */
401148385Sume		zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
402148385Sume		if (zoneid) {
403148385Sume			/* sanity check */
404181803Sbz			if (zoneid < 0 || V_if_index < zoneid)
405148385Sume				return (ENXIO);
406148385Sume			if (!ifnet_byindex(zoneid))
407148385Sume				return (ENXIO);
408148385Sume			sin6->sin6_addr.s6_addr16[1] = 0;
409148385Sume			sin6->sin6_scope_id = zoneid;
410148385Sume		}
411148385Sume	}
412148385Sume
413148385Sume	return 0;
414148385Sume}
415148385Sume
416148385Sume/*
417148385Sume * Determine the appropriate scope zone ID for in6 and ifp.  If ret_id is
418148385Sume * non NULL, it is set to the zone ID.  If the zone ID needs to be embedded
419148385Sume * in the in6_addr structure, in6 will be modified.
420171259Sdelphij *
421171259Sdelphij * ret_id - unnecessary?
422148385Sume */
423148385Sumeint
424171259Sdelphijin6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id)
425148385Sume{
426148385Sume	int scope;
427148385Sume	u_int32_t zoneid = 0;
428148396Sume	struct scope6_id *sid;
429148385Sume
430148396Sume	IF_AFDATA_LOCK(ifp);
431148396Sume
432148396Sume	sid = SID(ifp);
433148396Sume
434148385Sume#ifdef DIAGNOSTIC
435148385Sume	if (sid == NULL) { /* should not happen */
436148385Sume		panic("in6_setscope: scope array is NULL");
437148385Sume		/* NOTREACHED */
438148385Sume	}
439148385Sume#endif
440148385Sume
441148385Sume	/*
442148385Sume	 * special case: the loopback address can only belong to a loopback
443148385Sume	 * interface.
444148385Sume	 */
445148385Sume	if (IN6_IS_ADDR_LOOPBACK(in6)) {
446148399Sume		if (!(ifp->if_flags & IFF_LOOPBACK)) {
447148396Sume			IF_AFDATA_UNLOCK(ifp);
448148385Sume			return (EINVAL);
449148399Sume		} else {
450148385Sume			if (ret_id != NULL)
451148385Sume				*ret_id = 0; /* there's no ambiguity */
452148396Sume			IF_AFDATA_UNLOCK(ifp);
453148385Sume			return (0);
454148385Sume		}
455148385Sume	}
456148385Sume
457148385Sume	scope = in6_addrscope(in6);
458148385Sume
459148396Sume	SCOPE6_LOCK();
460148385Sume	switch (scope) {
461148385Sume	case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
462148385Sume		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
463148385Sume		break;
464148385Sume
465148385Sume	case IPV6_ADDR_SCOPE_LINKLOCAL:
466148385Sume		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
467148385Sume		break;
468148385Sume
469148385Sume	case IPV6_ADDR_SCOPE_SITELOCAL:
470148385Sume		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
471148385Sume		break;
472148385Sume
473148385Sume	case IPV6_ADDR_SCOPE_ORGLOCAL:
474148385Sume		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
475148385Sume		break;
476148385Sume
477148385Sume	default:
478148385Sume		zoneid = 0;	/* XXX: treat as global. */
479148385Sume		break;
480148385Sume	}
481148396Sume	SCOPE6_UNLOCK();
482148396Sume	IF_AFDATA_UNLOCK(ifp);
483148385Sume
484148385Sume	if (ret_id != NULL)
485148385Sume		*ret_id = zoneid;
486148385Sume
487148385Sume	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
488148385Sume		in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */
489148385Sume
490148385Sume	return (0);
491148385Sume}
492148385Sume
493148385Sume/*
494148385Sume * Just clear the embedded scope identifier.  Return 0 if the original address
495148385Sume * is intact; return non 0 if the address is modified.
496148385Sume */
497148385Sumeint
498171259Sdelphijin6_clearscope(struct in6_addr *in6)
499148385Sume{
500148385Sume	int modified = 0;
501148385Sume
502148385Sume	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
503148385Sume		if (in6->s6_addr16[1] != 0)
504148385Sume			modified = 1;
505148385Sume		in6->s6_addr16[1] = 0;
506148385Sume	}
507148385Sume
508148385Sume	return (modified);
509148385Sume}
510