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