scope6.c revision 148396
1/*	$FreeBSD: head/sys/netinet6/scope6.c 148396 2005-07-25 17:05:37Z ume $	*/
2/*	$KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $	*/
3
4/*-
5 * Copyright (C) 2000 WIDE Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/param.h>
34#include <sys/malloc.h>
35#include <sys/mbuf.h>
36#include <sys/socket.h>
37#include <sys/systm.h>
38#include <sys/queue.h>
39#include <sys/syslog.h>
40
41#include <net/route.h>
42#include <net/if.h>
43
44#include <netinet/in.h>
45
46#include <netinet6/in6_var.h>
47#include <netinet6/scope6_var.h>
48
49#ifdef ENABLE_DEFAULT_SCOPE
50int ip6_use_defzone = 1;
51#else
52int ip6_use_defzone = 0;
53#endif
54
55/*
56 * The scope6_lock protects the global sid default stored in
57 * sid_default below.
58 */
59static struct mtx scope6_lock;
60#define	SCOPE6_LOCK_INIT()	mtx_init(&scope6_lock, "scope6_lock", NULL, MTX_DEF)
61#define	SCOPE6_LOCK()		mtx_lock(&scope6_lock)
62#define	SCOPE6_UNLOCK()		mtx_unlock(&scope6_lock)
63#define	SCOPE6_LOCK_ASSERT()	mtx_assert(&scope6_lock, MA_OWNED)
64
65static struct scope6_id sid_default;
66#define SID(ifp) \
67	(((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->scope6_id)
68
69void
70scope6_init()
71{
72
73	SCOPE6_LOCK_INIT();
74	bzero(&sid_default, sizeof(sid_default));
75}
76
77struct scope6_id *
78scope6_ifattach(ifp)
79	struct ifnet *ifp;
80{
81	struct scope6_id *sid;
82
83	sid = (struct scope6_id *)malloc(sizeof(*sid), M_IFADDR, M_WAITOK);
84	bzero(sid, sizeof(*sid));
85
86	/*
87	 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
88	 * Should we rather hardcode here?
89	 */
90	sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index;
91	sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
92#ifdef MULTI_SCOPE
93	/* by default, we don't care about scope boundary for these scopes. */
94	sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
95	sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
96#endif
97
98	return sid;
99}
100
101void
102scope6_ifdetach(sid)
103	struct scope6_id *sid;
104{
105
106	free(sid, M_IFADDR);
107}
108
109int
110scope6_set(ifp, idlist)
111	struct ifnet *ifp;
112	struct scope6_id *idlist;
113{
114	int i;
115	int error = 0;
116	struct scope6_id *sid = NULL;
117
118	IF_AFDATA_LOCK(ifp);
119	sid = SID(ifp);
120
121	if (!sid) {	/* paranoid? */
122		IF_AFDATA_UNLOCK(ifp);
123		return (EINVAL);
124	}
125
126	/*
127	 * XXX: We need more consistency checks of the relationship among
128	 * scopes (e.g. an organization should be larger than a site).
129	 */
130
131	/*
132	 * TODO(XXX): after setting, we should reflect the changes to
133	 * interface addresses, routing table entries, PCB entries...
134	 */
135
136	SCOPE6_LOCK();
137	for (i = 0; i < 16; i++) {
138		if (idlist->s6id_list[i] &&
139		    idlist->s6id_list[i] != sid->s6id_list[i]) {
140			/*
141			 * An interface zone ID must be the corresponding
142			 * interface index by definition.
143			 */
144			if (i == IPV6_ADDR_SCOPE_INTFACELOCAL &&
145			    idlist->s6id_list[i] != ifp->if_index) {
146				IF_AFDATA_UNLOCK(ifp);
147				SCOPE6_UNLOCK();
148				return (EINVAL);
149			}
150
151			if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
152			    idlist->s6id_list[i] > if_index) {
153				/*
154				 * XXX: theoretically, there should be no
155				 * relationship between link IDs and interface
156				 * IDs, but we check the consistency for
157				 * safety in later use.
158				 */
159				IF_AFDATA_UNLOCK(ifp);
160				SCOPE6_UNLOCK();
161				return (EINVAL);
162			}
163
164			/*
165			 * XXX: we must need lots of work in this case,
166			 * but we simply set the new value in this initial
167			 * implementation.
168			 */
169			sid->s6id_list[i] = idlist->s6id_list[i];
170		}
171	}
172	SCOPE6_UNLOCK();
173	IF_AFDATA_UNLOCK(ifp);
174
175	return (error);
176}
177
178int
179scope6_get(ifp, idlist)
180	struct ifnet *ifp;
181	struct scope6_id *idlist;
182{
183	/* We only need to lock the interface's afdata for SID() to work. */
184	IF_AFDATA_LOCK(ifp);
185	struct scope6_id *sid = SID(ifp);
186
187	if (sid == NULL) {	/* paranoid? */
188		IF_AFDATA_UNLOCK(ifp);
189		return (EINVAL);
190	}
191
192	SCOPE6_LOCK();
193	*idlist = *sid;
194	SCOPE6_UNLOCK();
195
196	IF_AFDATA_UNLOCK(ifp);
197	return (0);
198}
199
200
201/*
202 * Get a scope of the address. Node-local, link-local, site-local or global.
203 */
204int
205in6_addrscope(addr)
206	struct in6_addr *addr;
207{
208	int scope;
209
210	if (addr->s6_addr[0] == 0xfe) {
211		scope = addr->s6_addr[1] & 0xc0;
212
213		switch (scope) {
214		case 0x80:
215			return IPV6_ADDR_SCOPE_LINKLOCAL;
216			break;
217		case 0xc0:
218			return IPV6_ADDR_SCOPE_SITELOCAL;
219			break;
220		default:
221			return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
222			break;
223		}
224	}
225
226
227	if (addr->s6_addr[0] == 0xff) {
228		scope = addr->s6_addr[1] & 0x0f;
229
230		/*
231		 * due to other scope such as reserved,
232		 * return scope doesn't work.
233		 */
234		switch (scope) {
235		case IPV6_ADDR_SCOPE_INTFACELOCAL:
236			return IPV6_ADDR_SCOPE_INTFACELOCAL;
237			break;
238		case IPV6_ADDR_SCOPE_LINKLOCAL:
239			return IPV6_ADDR_SCOPE_LINKLOCAL;
240			break;
241		case IPV6_ADDR_SCOPE_SITELOCAL:
242			return IPV6_ADDR_SCOPE_SITELOCAL;
243			break;
244		default:
245			return IPV6_ADDR_SCOPE_GLOBAL;
246			break;
247		}
248	}
249
250	/*
251	 * Regard loopback and unspecified addresses as global, since
252	 * they have no ambiguity.
253	 */
254	if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
255		if (addr->s6_addr[15] == 1) /* loopback */
256			return IPV6_ADDR_SCOPE_LINKLOCAL;
257		if (addr->s6_addr[15] == 0) /* unspecified */
258			return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */
259	}
260
261	return IPV6_ADDR_SCOPE_GLOBAL;
262}
263
264void
265scope6_setdefault(ifp)
266	struct ifnet *ifp;	/* note that this might be NULL */
267{
268	/*
269	 * Currently, this function just sets the default "interfaces"
270	 * and "links" according to the given interface.
271	 * We might eventually have to separate the notion of "link" from
272	 * "interface" and provide a user interface to set the default.
273	 */
274	SCOPE6_LOCK();
275	if (ifp) {
276		sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] =
277			ifp->if_index;
278		sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
279			ifp->if_index;
280	} else {
281		sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0;
282		sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
283	}
284	SCOPE6_UNLOCK();
285}
286
287int
288scope6_get_default(idlist)
289	struct scope6_id *idlist;
290{
291
292	SCOPE6_LOCK();
293	*idlist = sid_default;
294	SCOPE6_UNLOCK();
295
296	return (0);
297}
298
299u_int32_t
300scope6_addr2default(addr)
301	struct in6_addr *addr;
302{
303	u_int32_t id;
304
305	/*
306	 * special case: The loopback address should be considered as
307	 * link-local, but there's no ambiguity in the syntax.
308	 */
309	if (IN6_IS_ADDR_LOOPBACK(addr))
310		return (0);
311
312	/*
313	 * XXX: 32-bit read is atomic on all our platforms, is it OK
314	 * not to lock here?
315	 */
316	SCOPE6_LOCK();
317	id = sid_default.s6id_list[in6_addrscope(addr)];
318	SCOPE6_UNLOCK();
319	return (id);
320}
321
322/*
323 * Validate the specified scope zone ID in the sin6_scope_id field.  If the ID
324 * is unspecified (=0), needs to be specified, and the default zone ID can be
325 * used, the default value will be used.
326 * This routine then generates the kernel-internal form: if the address scope
327 * of is interface-local or link-local, embed the interface index in the
328 * address.
329 */
330int
331sa6_embedscope(sin6, defaultok)
332	struct sockaddr_in6 *sin6;
333	int defaultok;
334{
335	struct ifnet *ifp;
336	u_int32_t zoneid;
337
338	if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok)
339		zoneid = scope6_addr2default(&sin6->sin6_addr);
340
341	if (zoneid != 0 &&
342	    (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
343	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) {
344		/*
345		 * At this moment, we only check interface-local and
346		 * link-local scope IDs, and use interface indices as the
347		 * zone IDs assuming a one-to-one mapping between interfaces
348		 * and links.
349		 */
350		if (if_index < zoneid)
351			return (ENXIO);
352		ifp = ifnet_byindex(zoneid);
353		if (ifp == NULL) /* XXX: this can happen for some OS */
354			return (ENXIO);
355
356		/* XXX assignment to 16bit from 32bit variable */
357		sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);
358
359		sin6->sin6_scope_id = 0;
360	}
361
362	return 0;
363}
364
365/*
366 * generate standard sockaddr_in6 from embedded form.
367 */
368int
369sa6_recoverscope(sin6)
370	struct sockaddr_in6 *sin6;
371{
372	u_int32_t zoneid;
373
374	if (sin6->sin6_scope_id != 0) {
375		log(LOG_NOTICE,
376		    "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
377		    ip6_sprintf(&sin6->sin6_addr), sin6->sin6_scope_id);
378		/* XXX: proceed anyway... */
379	}
380	if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
381	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
382		/*
383		 * KAME assumption: link id == interface id
384		 */
385		zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
386		if (zoneid) {
387			/* sanity check */
388			if (zoneid < 0 || if_index < zoneid)
389				return (ENXIO);
390			if (!ifnet_byindex(zoneid))
391				return (ENXIO);
392			sin6->sin6_addr.s6_addr16[1] = 0;
393			sin6->sin6_scope_id = zoneid;
394		}
395	}
396
397	return 0;
398}
399
400/*
401 * Determine the appropriate scope zone ID for in6 and ifp.  If ret_id is
402 * non NULL, it is set to the zone ID.  If the zone ID needs to be embedded
403 * in the in6_addr structure, in6 will be modified.
404 */
405int
406in6_setscope(in6, ifp, ret_id)
407	struct in6_addr *in6;
408	struct ifnet *ifp;
409	u_int32_t *ret_id;	/* unnecessary? */
410{
411	int scope;
412	u_int32_t zoneid = 0;
413	struct scope6_id *sid;
414
415	IF_AFDATA_LOCK(ifp);
416
417	sid = SID(ifp);
418
419#ifdef DIAGNOSTIC
420	if (sid == NULL) { /* should not happen */
421		panic("in6_setscope: scope array is NULL");
422		/* NOTREACHED */
423	}
424#endif
425
426	/*
427	 * special case: the loopback address can only belong to a loopback
428	 * interface.
429	 */
430	if (IN6_IS_ADDR_LOOPBACK(in6)) {
431		if (!(ifp->if_flags & IFF_LOOPBACK))
432			IF_AFDATA_UNLOCK(ifp);
433			return (EINVAL);
434		else {
435			if (ret_id != NULL)
436				*ret_id = 0; /* there's no ambiguity */
437			IF_AFDATA_UNLOCK(ifp);
438			return (0);
439		}
440	}
441
442	scope = in6_addrscope(in6);
443
444	SCOPE6_LOCK();
445	switch (scope) {
446	case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
447		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
448		break;
449
450	case IPV6_ADDR_SCOPE_LINKLOCAL:
451		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
452		break;
453
454	case IPV6_ADDR_SCOPE_SITELOCAL:
455		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
456		break;
457
458	case IPV6_ADDR_SCOPE_ORGLOCAL:
459		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
460		break;
461
462	default:
463		zoneid = 0;	/* XXX: treat as global. */
464		break;
465	}
466	SCOPE6_UNLOCK();
467	IF_AFDATA_UNLOCK(ifp);
468
469	if (ret_id != NULL)
470		*ret_id = zoneid;
471
472	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
473		in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */
474
475	return (0);
476}
477
478/*
479 * Just clear the embedded scope identifier.  Return 0 if the original address
480 * is intact; return non 0 if the address is modified.
481 */
482int
483in6_clearscope(in6)
484	struct in6_addr *in6;
485{
486	int modified = 0;
487
488	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
489		if (in6->s6_addr16[1] != 0)
490			modified = 1;
491		in6->s6_addr16[1] = 0;
492	}
493
494	return (modified);
495}
496