scope6.c revision 171259
162587Sitojun/*	$FreeBSD: head/sys/netinet6/scope6.c 171259 2007-07-05 16:23:49Z delphij $	*/
278064Sume/*	$KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $	*/
362587Sitojun
4139826Simp/*-
562587Sitojun * Copyright (C) 2000 WIDE Project.
662587Sitojun * All rights reserved.
7120941Sume *
862587Sitojun * Redistribution and use in source and binary forms, with or without
962587Sitojun * modification, are permitted provided that the following conditions
1062587Sitojun * are met:
1162587Sitojun * 1. Redistributions of source code must retain the above copyright
1262587Sitojun *    notice, this list of conditions and the following disclaimer.
1362587Sitojun * 2. Redistributions in binary form must reproduce the above copyright
1462587Sitojun *    notice, this list of conditions and the following disclaimer in the
1562587Sitojun *    documentation and/or other materials provided with the distribution.
1662587Sitojun * 3. Neither the name of the project nor the names of its contributors
1762587Sitojun *    may be used to endorse or promote products derived from this software
1862587Sitojun *    without specific prior written permission.
19120941Sume *
2062587Sitojun * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2162587Sitojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2262587Sitojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2362587Sitojun * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2462587Sitojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2562587Sitojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2662587Sitojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2762587Sitojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2862587Sitojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2962587Sitojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3062587Sitojun * SUCH DAMAGE.
3162587Sitojun */
3262587Sitojun
3362587Sitojun#include <sys/param.h>
3462587Sitojun#include <sys/malloc.h>
3562587Sitojun#include <sys/mbuf.h>
3662587Sitojun#include <sys/socket.h>
3762587Sitojun#include <sys/systm.h>
3878064Sume#include <sys/queue.h>
39148385Sume#include <sys/syslog.h>
4062587Sitojun
4162587Sitojun#include <net/route.h>
4262587Sitojun#include <net/if.h>
4362587Sitojun
4462587Sitojun#include <netinet/in.h>
4562587Sitojun
4662587Sitojun#include <netinet6/in6_var.h>
4762587Sitojun#include <netinet6/scope6_var.h>
4862587Sitojun
49148385Sume#ifdef ENABLE_DEFAULT_SCOPE
50148385Sumeint ip6_use_defzone = 1;
51148385Sume#else
52148385Sumeint ip6_use_defzone = 0;
53148385Sume#endif
54148385Sume
55121343Sume/*
56138184Sgnn * The scope6_lock protects the global sid default stored in
57138184Sgnn * sid_default below.
58121343Sume */
59121343Sumestatic struct mtx scope6_lock;
60121343Sume#define	SCOPE6_LOCK_INIT()	mtx_init(&scope6_lock, "scope6_lock", NULL, MTX_DEF)
61121343Sume#define	SCOPE6_LOCK()		mtx_lock(&scope6_lock)
62121343Sume#define	SCOPE6_UNLOCK()		mtx_unlock(&scope6_lock)
63121343Sume#define	SCOPE6_LOCK_ASSERT()	mtx_assert(&scope6_lock, MA_OWNED)
64121343Sume
65121161Sumestatic struct scope6_id sid_default;
66121161Sume#define SID(ifp) \
67121161Sume	(((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->scope6_id)
6862587Sitojun
6962587Sitojunvoid
70171259Sdelphijscope6_init(void)
71121161Sume{
72121161Sume
73121343Sume	SCOPE6_LOCK_INIT();
74121161Sume	bzero(&sid_default, sizeof(sid_default));
75121161Sume}
76121161Sume
77121161Sumestruct scope6_id *
78171259Sdelphijscope6_ifattach(struct ifnet *ifp)
7962587Sitojun{
80121161Sume	struct scope6_id *sid;
8162587Sitojun
82121161Sume	sid = (struct scope6_id *)malloc(sizeof(*sid), M_IFADDR, M_WAITOK);
83121161Sume	bzero(sid, sizeof(*sid));
8462587Sitojun
8562587Sitojun	/*
8662587Sitojun	 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
8762587Sitojun	 * Should we rather hardcode here?
8862587Sitojun	 */
89121315Sume	sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index;
90121161Sume	sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
9162587Sitojun#ifdef MULTI_SCOPE
9262587Sitojun	/* by default, we don't care about scope boundary for these scopes. */
93121161Sume	sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
94121161Sume	sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
9562587Sitojun#endif
9662587Sitojun
97121161Sume	return sid;
9862587Sitojun}
9962587Sitojun
100121161Sumevoid
101171259Sdelphijscope6_ifdetach(struct scope6_id *sid)
102121161Sume{
103121161Sume
104121161Sume	free(sid, M_IFADDR);
105121161Sume}
106121161Sume
10762587Sitojunint
108171259Sdelphijscope6_set(struct ifnet *ifp, struct scope6_id *idlist)
10962587Sitojun{
110138184Sgnn	int i;
11162587Sitojun	int error = 0;
112138184Sgnn	struct scope6_id *sid = NULL;
11362587Sitojun
114138184Sgnn	IF_AFDATA_LOCK(ifp);
115138184Sgnn	sid = SID(ifp);
116138184Sgnn
117138184Sgnn	if (!sid) {	/* paranoid? */
118138184Sgnn		IF_AFDATA_UNLOCK(ifp);
119120856Sume		return (EINVAL);
120138184Sgnn	}
12162587Sitojun
12262587Sitojun	/*
12362587Sitojun	 * XXX: We need more consistency checks of the relationship among
12462587Sitojun	 * scopes (e.g. an organization should be larger than a site).
12562587Sitojun	 */
12662587Sitojun
12762587Sitojun	/*
12862587Sitojun	 * TODO(XXX): after setting, we should reflect the changes to
129120941Sume	 * interface addresses, routing table entries, PCB entries...
13062587Sitojun	 */
13162587Sitojun
132121343Sume	SCOPE6_LOCK();
13362587Sitojun	for (i = 0; i < 16; i++) {
134121161Sume		if (idlist->s6id_list[i] &&
135121161Sume		    idlist->s6id_list[i] != sid->s6id_list[i]) {
136121315Sume			/*
137121315Sume			 * An interface zone ID must be the corresponding
138121315Sume			 * interface index by definition.
139121315Sume			 */
140121315Sume			if (i == IPV6_ADDR_SCOPE_INTFACELOCAL &&
141121315Sume			    idlist->s6id_list[i] != ifp->if_index) {
142138184Sgnn				IF_AFDATA_UNLOCK(ifp);
143138184Sgnn				SCOPE6_UNLOCK();
144121315Sume				return (EINVAL);
145121315Sume			}
146121315Sume
14762587Sitojun			if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
148121161Sume			    idlist->s6id_list[i] > if_index) {
14962587Sitojun				/*
15062587Sitojun				 * XXX: theoretically, there should be no
15162587Sitojun				 * relationship between link IDs and interface
15262587Sitojun				 * IDs, but we check the consistency for
15362587Sitojun				 * safety in later use.
15462587Sitojun				 */
155138184Sgnn				IF_AFDATA_UNLOCK(ifp);
156138184Sgnn				SCOPE6_UNLOCK();
157120856Sume				return (EINVAL);
15862587Sitojun			}
15962587Sitojun
16062587Sitojun			/*
16162587Sitojun			 * XXX: we must need lots of work in this case,
16262587Sitojun			 * but we simply set the new value in this initial
16362587Sitojun			 * implementation.
16462587Sitojun			 */
165121161Sume			sid->s6id_list[i] = idlist->s6id_list[i];
16662587Sitojun		}
16762587Sitojun	}
168121343Sume	SCOPE6_UNLOCK();
169138184Sgnn	IF_AFDATA_UNLOCK(ifp);
17062587Sitojun
171120856Sume	return (error);
17262587Sitojun}
17362587Sitojun
17462587Sitojunint
175171259Sdelphijscope6_get(struct ifnet *ifp, struct scope6_id *idlist)
17662587Sitojun{
177138184Sgnn	/* We only need to lock the interface's afdata for SID() to work. */
178138184Sgnn	IF_AFDATA_LOCK(ifp);
179121161Sume	struct scope6_id *sid = SID(ifp);
180121161Sume
181138184Sgnn	if (sid == NULL) {	/* paranoid? */
182138184Sgnn		IF_AFDATA_UNLOCK(ifp);
183120856Sume		return (EINVAL);
184138184Sgnn	}
18562587Sitojun
186121343Sume	SCOPE6_LOCK();
187121161Sume	*idlist = *sid;
188121343Sume	SCOPE6_UNLOCK();
18962587Sitojun
190138184Sgnn	IF_AFDATA_UNLOCK(ifp);
191120856Sume	return (0);
19262587Sitojun}
19362587Sitojun
19462587Sitojun
19562587Sitojun/*
19662587Sitojun * Get a scope of the address. Node-local, link-local, site-local or global.
19762587Sitojun */
19862587Sitojunint
199171259Sdelphijin6_addrscope(struct in6_addr *addr)
20062587Sitojun{
20162587Sitojun	int scope;
20262587Sitojun
203121315Sume	if (addr->s6_addr[0] == 0xfe) {
204121315Sume		scope = addr->s6_addr[1] & 0xc0;
20562587Sitojun
20662587Sitojun		switch (scope) {
20762587Sitojun		case 0x80:
20862587Sitojun			return IPV6_ADDR_SCOPE_LINKLOCAL;
20962587Sitojun			break;
21062587Sitojun		case 0xc0:
21162587Sitojun			return IPV6_ADDR_SCOPE_SITELOCAL;
21262587Sitojun			break;
21362587Sitojun		default:
21462587Sitojun			return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
21562587Sitojun			break;
21662587Sitojun		}
21762587Sitojun	}
21862587Sitojun
21962587Sitojun
220121315Sume	if (addr->s6_addr[0] == 0xff) {
221121315Sume		scope = addr->s6_addr[1] & 0x0f;
22262587Sitojun
22362587Sitojun		/*
22462587Sitojun		 * due to other scope such as reserved,
22562587Sitojun		 * return scope doesn't work.
22662587Sitojun		 */
22762587Sitojun		switch (scope) {
228121315Sume		case IPV6_ADDR_SCOPE_INTFACELOCAL:
229121315Sume			return IPV6_ADDR_SCOPE_INTFACELOCAL;
23062587Sitojun			break;
23162587Sitojun		case IPV6_ADDR_SCOPE_LINKLOCAL:
23262587Sitojun			return IPV6_ADDR_SCOPE_LINKLOCAL;
23362587Sitojun			break;
23462587Sitojun		case IPV6_ADDR_SCOPE_SITELOCAL:
23562587Sitojun			return IPV6_ADDR_SCOPE_SITELOCAL;
23662587Sitojun			break;
23762587Sitojun		default:
23862587Sitojun			return IPV6_ADDR_SCOPE_GLOBAL;
23962587Sitojun			break;
24062587Sitojun		}
24162587Sitojun	}
24262587Sitojun
243121315Sume	/*
244121315Sume	 * Regard loopback and unspecified addresses as global, since
245121315Sume	 * they have no ambiguity.
246121315Sume	 */
24793128Sume	if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
248121315Sume		if (addr->s6_addr[15] == 1) /* loopback */
24962587Sitojun			return IPV6_ADDR_SCOPE_LINKLOCAL;
250121315Sume		if (addr->s6_addr[15] == 0) /* unspecified */
251121315Sume			return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */
25262587Sitojun	}
25362587Sitojun
25462587Sitojun	return IPV6_ADDR_SCOPE_GLOBAL;
25562587Sitojun}
25662587Sitojun
257171259Sdelphij/*
258171259Sdelphij * ifp - note that this might be NULL
259171259Sdelphij */
260171259Sdelphij
26162587Sitojunvoid
262171259Sdelphijscope6_setdefault(struct ifnet *ifp)
26362587Sitojun{
26462587Sitojun	/*
265138184Sgnn	 * Currently, this function just sets the default "interfaces"
266121161Sume	 * and "links" according to the given interface.
26762587Sitojun	 * We might eventually have to separate the notion of "link" from
26862587Sitojun	 * "interface" and provide a user interface to set the default.
26962587Sitojun	 */
270121343Sume	SCOPE6_LOCK();
27162587Sitojun	if (ifp) {
272121315Sume		sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] =
27362587Sitojun			ifp->if_index;
274121161Sume		sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
275121161Sume			ifp->if_index;
276120941Sume	} else {
277121315Sume		sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0;
278121161Sume		sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
27962587Sitojun	}
280121343Sume	SCOPE6_UNLOCK();
28162587Sitojun}
28262587Sitojun
28362587Sitojunint
284171259Sdelphijscope6_get_default(struct scope6_id *idlist)
28562587Sitojun{
286121343Sume
287121343Sume	SCOPE6_LOCK();
288121161Sume	*idlist = sid_default;
289121343Sume	SCOPE6_UNLOCK();
29062587Sitojun
291120856Sume	return (0);
29262587Sitojun}
29362587Sitojun
29462587Sitojunu_int32_t
295171259Sdelphijscope6_addr2default(struct in6_addr *addr)
29662587Sitojun{
297121343Sume	u_int32_t id;
298121343Sume
299121161Sume	/*
300121161Sume	 * special case: The loopback address should be considered as
301121161Sume	 * link-local, but there's no ambiguity in the syntax.
302121161Sume	 */
303121161Sume	if (IN6_IS_ADDR_LOOPBACK(addr))
304121161Sume		return (0);
305121161Sume
306121343Sume	/*
307121343Sume	 * XXX: 32-bit read is atomic on all our platforms, is it OK
308121343Sume	 * not to lock here?
309121343Sume	 */
310121343Sume	SCOPE6_LOCK();
311121343Sume	id = sid_default.s6id_list[in6_addrscope(addr)];
312121343Sume	SCOPE6_UNLOCK();
313121343Sume	return (id);
31462587Sitojun}
315148385Sume
316148385Sume/*
317148385Sume * Validate the specified scope zone ID in the sin6_scope_id field.  If the ID
318148385Sume * is unspecified (=0), needs to be specified, and the default zone ID can be
319148385Sume * used, the default value will be used.
320148385Sume * This routine then generates the kernel-internal form: if the address scope
321148385Sume * of is interface-local or link-local, embed the interface index in the
322148385Sume * address.
323148385Sume */
324148385Sumeint
325171259Sdelphijsa6_embedscope(struct sockaddr_in6 *sin6, int defaultok)
326148385Sume{
327148385Sume	struct ifnet *ifp;
328148385Sume	u_int32_t zoneid;
329148385Sume
330148385Sume	if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok)
331148385Sume		zoneid = scope6_addr2default(&sin6->sin6_addr);
332148385Sume
333148385Sume	if (zoneid != 0 &&
334148385Sume	    (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
335148385Sume	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) {
336148385Sume		/*
337148385Sume		 * At this moment, we only check interface-local and
338148385Sume		 * link-local scope IDs, and use interface indices as the
339148385Sume		 * zone IDs assuming a one-to-one mapping between interfaces
340148385Sume		 * and links.
341148385Sume		 */
342148385Sume		if (if_index < zoneid)
343148385Sume			return (ENXIO);
344148385Sume		ifp = ifnet_byindex(zoneid);
345148385Sume		if (ifp == NULL) /* XXX: this can happen for some OS */
346148385Sume			return (ENXIO);
347148385Sume
348148385Sume		/* XXX assignment to 16bit from 32bit variable */
349148385Sume		sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);
350148385Sume
351148385Sume		sin6->sin6_scope_id = 0;
352148385Sume	}
353148385Sume
354148385Sume	return 0;
355148385Sume}
356148385Sume
357148385Sume/*
358148385Sume * generate standard sockaddr_in6 from embedded form.
359148385Sume */
360148385Sumeint
361171259Sdelphijsa6_recoverscope(struct sockaddr_in6 *sin6)
362148385Sume{
363165118Sbz	char ip6buf[INET6_ADDRSTRLEN];
364148385Sume	u_int32_t zoneid;
365148385Sume
366148385Sume	if (sin6->sin6_scope_id != 0) {
367148385Sume		log(LOG_NOTICE,
368148385Sume		    "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
369165118Sbz		    ip6_sprintf(ip6buf, &sin6->sin6_addr), sin6->sin6_scope_id);
370148385Sume		/* XXX: proceed anyway... */
371148385Sume	}
372148385Sume	if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
373148385Sume	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
374148385Sume		/*
375148385Sume		 * KAME assumption: link id == interface id
376148385Sume		 */
377148385Sume		zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
378148385Sume		if (zoneid) {
379148385Sume			/* sanity check */
380148385Sume			if (zoneid < 0 || if_index < zoneid)
381148385Sume				return (ENXIO);
382148385Sume			if (!ifnet_byindex(zoneid))
383148385Sume				return (ENXIO);
384148385Sume			sin6->sin6_addr.s6_addr16[1] = 0;
385148385Sume			sin6->sin6_scope_id = zoneid;
386148385Sume		}
387148385Sume	}
388148385Sume
389148385Sume	return 0;
390148385Sume}
391148385Sume
392148385Sume/*
393148385Sume * Determine the appropriate scope zone ID for in6 and ifp.  If ret_id is
394148385Sume * non NULL, it is set to the zone ID.  If the zone ID needs to be embedded
395148385Sume * in the in6_addr structure, in6 will be modified.
396171259Sdelphij *
397171259Sdelphij * ret_id - unnecessary?
398148385Sume */
399148385Sumeint
400171259Sdelphijin6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id)
401148385Sume{
402148385Sume	int scope;
403148385Sume	u_int32_t zoneid = 0;
404148396Sume	struct scope6_id *sid;
405148385Sume
406148396Sume	IF_AFDATA_LOCK(ifp);
407148396Sume
408148396Sume	sid = SID(ifp);
409148396Sume
410148385Sume#ifdef DIAGNOSTIC
411148385Sume	if (sid == NULL) { /* should not happen */
412148385Sume		panic("in6_setscope: scope array is NULL");
413148385Sume		/* NOTREACHED */
414148385Sume	}
415148385Sume#endif
416148385Sume
417148385Sume	/*
418148385Sume	 * special case: the loopback address can only belong to a loopback
419148385Sume	 * interface.
420148385Sume	 */
421148385Sume	if (IN6_IS_ADDR_LOOPBACK(in6)) {
422148399Sume		if (!(ifp->if_flags & IFF_LOOPBACK)) {
423148396Sume			IF_AFDATA_UNLOCK(ifp);
424148385Sume			return (EINVAL);
425148399Sume		} else {
426148385Sume			if (ret_id != NULL)
427148385Sume				*ret_id = 0; /* there's no ambiguity */
428148396Sume			IF_AFDATA_UNLOCK(ifp);
429148385Sume			return (0);
430148385Sume		}
431148385Sume	}
432148385Sume
433148385Sume	scope = in6_addrscope(in6);
434148385Sume
435148396Sume	SCOPE6_LOCK();
436148385Sume	switch (scope) {
437148385Sume	case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
438148385Sume		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
439148385Sume		break;
440148385Sume
441148385Sume	case IPV6_ADDR_SCOPE_LINKLOCAL:
442148385Sume		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
443148385Sume		break;
444148385Sume
445148385Sume	case IPV6_ADDR_SCOPE_SITELOCAL:
446148385Sume		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
447148385Sume		break;
448148385Sume
449148385Sume	case IPV6_ADDR_SCOPE_ORGLOCAL:
450148385Sume		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
451148385Sume		break;
452148385Sume
453148385Sume	default:
454148385Sume		zoneid = 0;	/* XXX: treat as global. */
455148385Sume		break;
456148385Sume	}
457148396Sume	SCOPE6_UNLOCK();
458148396Sume	IF_AFDATA_UNLOCK(ifp);
459148385Sume
460148385Sume	if (ret_id != NULL)
461148385Sume		*ret_id = zoneid;
462148385Sume
463148385Sume	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
464148385Sume		in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */
465148385Sume
466148385Sume	return (0);
467148385Sume}
468148385Sume
469148385Sume/*
470148385Sume * Just clear the embedded scope identifier.  Return 0 if the original address
471148385Sume * is intact; return non 0 if the address is modified.
472148385Sume */
473148385Sumeint
474171259Sdelphijin6_clearscope(struct in6_addr *in6)
475148385Sume{
476148385Sume	int modified = 0;
477148385Sume
478148385Sume	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
479148385Sume		if (in6->s6_addr16[1] != 0)
480148385Sume			modified = 1;
481148385Sume		in6->s6_addr16[1] = 0;
482148385Sume	}
483148385Sume
484148385Sume	return (modified);
485148385Sume}
486