scope6.c revision 190787
1272343Sngie/*-
2272343Sngie * Copyright (C) 2000 WIDE Project.
3272343Sngie * All rights reserved.
4272343Sngie *
5272343Sngie * Redistribution and use in source and binary forms, with or without
6272343Sngie * modification, are permitted provided that the following conditions
7272343Sngie * are met:
8272343Sngie * 1. Redistributions of source code must retain the above copyright
9272343Sngie *    notice, this list of conditions and the following disclaimer.
10272343Sngie * 2. Redistributions in binary form must reproduce the above copyright
11272343Sngie *    notice, this list of conditions and the following disclaimer in the
12272343Sngie *    documentation and/or other materials provided with the distribution.
13272343Sngie * 3. Neither the name of the project nor the names of its contributors
14272343Sngie *    may be used to endorse or promote products derived from this software
15272343Sngie *    without specific prior written permission.
16272343Sngie *
17272343Sngie * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18272343Sngie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19272343Sngie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20272343Sngie * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21272343Sngie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22272343Sngie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23272343Sngie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24272343Sngie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25272343Sngie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26272343Sngie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27272343Sngie * SUCH DAMAGE.
28272343Sngie *
29272343Sngie *	$KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $
30272343Sngie */
31272343Sngie
32272343Sngie#include <sys/cdefs.h>
33272343Sngie__FBSDID("$FreeBSD: head/sys/netinet6/scope6.c 190787 2009-04-06 22:29:41Z zec $");
34272343Sngie
35272343Sngie#include "opt_route.h"
36272343Sngie
37272343Sngie#include <sys/param.h>
38272343Sngie#include <sys/malloc.h>
39272343Sngie#include <sys/mbuf.h>
40272343Sngie#include <sys/socket.h>
41272343Sngie#include <sys/systm.h>
42272343Sngie#include <sys/queue.h>
43272343Sngie#include <sys/syslog.h>
44272343Sngie#include <sys/vimage.h>
45272343Sngie
46272343Sngie#include <net/route.h>
47272343Sngie#include <net/if.h>
48272343Sngie#include <net/vnet.h>
49272343Sngie
50272343Sngie#include <netinet/in.h>
51272343Sngie
52272343Sngie#include <netinet/ip6.h>
53272343Sngie#include <netinet6/in6_var.h>
54272343Sngie#include <netinet6/scope6_var.h>
55272343Sngie#include <netinet6/vinet6.h>
56272343Sngie
57272343Sngie
58272343Sngie/*
59272343Sngie * The scope6_lock protects the global sid default stored in
60272343Sngie * sid_default below.
61272343Sngie */
62272343Sngiestatic struct mtx scope6_lock;
63272343Sngie#define	SCOPE6_LOCK_INIT()	mtx_init(&scope6_lock, "scope6_lock", NULL, MTX_DEF)
64272343Sngie#define	SCOPE6_LOCK()		mtx_lock(&scope6_lock)
65272343Sngie#define	SCOPE6_UNLOCK()		mtx_unlock(&scope6_lock)
66272343Sngie#define	SCOPE6_LOCK_ASSERT()	mtx_assert(&scope6_lock, MA_OWNED)
67272343Sngie
68272343Sngie#ifdef VIMAGE_GLOBALS
69272343Sngiestatic struct scope6_id sid_default;
70272343Sngieint ip6_use_defzone;
71272343Sngie#endif
72272343Sngie
73272343Sngie#define SID(ifp) \
74272343Sngie	(((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->scope6_id)
75272343Sngie
76272343Sngievoid
77272343Sngiescope6_init(void)
78272343Sngie{
79272343Sngie	INIT_VNET_INET6(curvnet);
80272343Sngie
81272343Sngie#ifdef ENABLE_DEFAULT_SCOPE
82272343Sngie	V_ip6_use_defzone = 1;
83272343Sngie#else
84272343Sngie	V_ip6_use_defzone = 0;
85272343Sngie#endif
86272343Sngie	bzero(&V_sid_default, sizeof(V_sid_default));
87272343Sngie
88272343Sngie	if (!IS_DEFAULT_VNET(curvnet))
89272343Sngie		return;
90272343Sngie
91272343Sngie	SCOPE6_LOCK_INIT();
92272343Sngie}
93272343Sngie
94272343Sngiestruct scope6_id *
95272343Sngiescope6_ifattach(struct ifnet *ifp)
96272343Sngie{
97272343Sngie	struct scope6_id *sid;
98272343Sngie
99272343Sngie	sid = (struct scope6_id *)malloc(sizeof(*sid), M_IFADDR, M_WAITOK);
100272343Sngie	bzero(sid, sizeof(*sid));
101272343Sngie
102272343Sngie	/*
103272343Sngie	 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
104272343Sngie	 * Should we rather hardcode here?
105272343Sngie	 */
106272343Sngie	sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index;
107272343Sngie	sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
108272343Sngie#ifdef MULTI_SCOPE
109272343Sngie	/* by default, we don't care about scope boundary for these scopes. */
110272343Sngie	sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
111272343Sngie	sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
112272343Sngie#endif
113272343Sngie
114272343Sngie	return sid;
115272343Sngie}
116272343Sngie
117272343Sngievoid
118272343Sngiescope6_ifdetach(struct scope6_id *sid)
119272343Sngie{
120272343Sngie
121272343Sngie	free(sid, M_IFADDR);
122272343Sngie}
123272343Sngie
124272343Sngieint
125272343Sngiescope6_set(struct ifnet *ifp, struct scope6_id *idlist)
126272343Sngie{
127272343Sngie	INIT_VNET_NET(ifp->if_vnet);
128272343Sngie	int i;
129272343Sngie	int error = 0;
130272343Sngie	struct scope6_id *sid = NULL;
131272343Sngie
132272343Sngie	IF_AFDATA_LOCK(ifp);
133272343Sngie	sid = SID(ifp);
134272343Sngie
135272343Sngie	if (!sid) {	/* paranoid? */
136272343Sngie		IF_AFDATA_UNLOCK(ifp);
137272343Sngie		return (EINVAL);
138272343Sngie	}
139272343Sngie
140272343Sngie	/*
141272343Sngie	 * XXX: We need more consistency checks of the relationship among
142272343Sngie	 * scopes (e.g. an organization should be larger than a site).
143272343Sngie	 */
144272343Sngie
145272343Sngie	/*
146272343Sngie	 * TODO(XXX): after setting, we should reflect the changes to
147272343Sngie	 * interface addresses, routing table entries, PCB entries...
148272343Sngie	 */
149272343Sngie
150272343Sngie	SCOPE6_LOCK();
151272343Sngie	for (i = 0; i < 16; i++) {
152272343Sngie		if (idlist->s6id_list[i] &&
153272343Sngie		    idlist->s6id_list[i] != sid->s6id_list[i]) {
154272343Sngie			/*
155272343Sngie			 * An interface zone ID must be the corresponding
156272343Sngie			 * interface index by definition.
157272343Sngie			 */
158272343Sngie			if (i == IPV6_ADDR_SCOPE_INTFACELOCAL &&
159272343Sngie			    idlist->s6id_list[i] != ifp->if_index) {
160272343Sngie				IF_AFDATA_UNLOCK(ifp);
161272343Sngie				SCOPE6_UNLOCK();
162272343Sngie				return (EINVAL);
163272343Sngie			}
164272343Sngie
165272343Sngie			if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
166272343Sngie			    idlist->s6id_list[i] > V_if_index) {
167272343Sngie				/*
168272343Sngie				 * XXX: theoretically, there should be no
169272343Sngie				 * relationship between link IDs and interface
170272343Sngie				 * IDs, but we check the consistency for
171272343Sngie				 * safety in later use.
172272343Sngie				 */
173272343Sngie				IF_AFDATA_UNLOCK(ifp);
174272343Sngie				SCOPE6_UNLOCK();
175272343Sngie				return (EINVAL);
176272343Sngie			}
177272343Sngie
178272343Sngie			/*
179272343Sngie			 * XXX: we must need lots of work in this case,
180272343Sngie			 * but we simply set the new value in this initial
181272343Sngie			 * implementation.
182272343Sngie			 */
183			sid->s6id_list[i] = idlist->s6id_list[i];
184		}
185	}
186	SCOPE6_UNLOCK();
187	IF_AFDATA_UNLOCK(ifp);
188
189	return (error);
190}
191
192int
193scope6_get(struct ifnet *ifp, struct scope6_id *idlist)
194{
195	/* We only need to lock the interface's afdata for SID() to work. */
196	IF_AFDATA_LOCK(ifp);
197	struct scope6_id *sid = SID(ifp);
198
199	if (sid == NULL) {	/* paranoid? */
200		IF_AFDATA_UNLOCK(ifp);
201		return (EINVAL);
202	}
203
204	SCOPE6_LOCK();
205	*idlist = *sid;
206	SCOPE6_UNLOCK();
207
208	IF_AFDATA_UNLOCK(ifp);
209	return (0);
210}
211
212
213/*
214 * Get a scope of the address. Node-local, link-local, site-local or global.
215 */
216int
217in6_addrscope(struct in6_addr *addr)
218{
219	int scope;
220
221	if (addr->s6_addr[0] == 0xfe) {
222		scope = addr->s6_addr[1] & 0xc0;
223
224		switch (scope) {
225		case 0x80:
226			return IPV6_ADDR_SCOPE_LINKLOCAL;
227			break;
228		case 0xc0:
229			return IPV6_ADDR_SCOPE_SITELOCAL;
230			break;
231		default:
232			return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
233			break;
234		}
235	}
236
237
238	if (addr->s6_addr[0] == 0xff) {
239		scope = addr->s6_addr[1] & 0x0f;
240
241		/*
242		 * due to other scope such as reserved,
243		 * return scope doesn't work.
244		 */
245		switch (scope) {
246		case IPV6_ADDR_SCOPE_INTFACELOCAL:
247			return IPV6_ADDR_SCOPE_INTFACELOCAL;
248			break;
249		case IPV6_ADDR_SCOPE_LINKLOCAL:
250			return IPV6_ADDR_SCOPE_LINKLOCAL;
251			break;
252		case IPV6_ADDR_SCOPE_SITELOCAL:
253			return IPV6_ADDR_SCOPE_SITELOCAL;
254			break;
255		default:
256			return IPV6_ADDR_SCOPE_GLOBAL;
257			break;
258		}
259	}
260
261	/*
262	 * Regard loopback and unspecified addresses as global, since
263	 * they have no ambiguity.
264	 */
265	if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
266		if (addr->s6_addr[15] == 1) /* loopback */
267			return IPV6_ADDR_SCOPE_LINKLOCAL;
268		if (addr->s6_addr[15] == 0) /* unspecified */
269			return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */
270	}
271
272	return IPV6_ADDR_SCOPE_GLOBAL;
273}
274
275/*
276 * ifp - note that this might be NULL
277 */
278
279void
280scope6_setdefault(struct ifnet *ifp)
281{
282	INIT_VNET_INET6(ifp->if_vnet);
283
284	/*
285	 * Currently, this function just sets the default "interfaces"
286	 * and "links" according to the given interface.
287	 * We might eventually have to separate the notion of "link" from
288	 * "interface" and provide a user interface to set the default.
289	 */
290	SCOPE6_LOCK();
291	if (ifp) {
292		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] =
293			ifp->if_index;
294		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
295			ifp->if_index;
296	} else {
297		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0;
298		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
299	}
300	SCOPE6_UNLOCK();
301}
302
303int
304scope6_get_default(struct scope6_id *idlist)
305{
306	INIT_VNET_INET6(curvnet);
307
308	SCOPE6_LOCK();
309	*idlist = V_sid_default;
310	SCOPE6_UNLOCK();
311
312	return (0);
313}
314
315u_int32_t
316scope6_addr2default(struct in6_addr *addr)
317{
318	INIT_VNET_INET6(curvnet);
319	u_int32_t id;
320
321	/*
322	 * special case: The loopback address should be considered as
323	 * link-local, but there's no ambiguity in the syntax.
324	 */
325	if (IN6_IS_ADDR_LOOPBACK(addr))
326		return (0);
327
328	/*
329	 * XXX: 32-bit read is atomic on all our platforms, is it OK
330	 * not to lock here?
331	 */
332	SCOPE6_LOCK();
333	id = V_sid_default.s6id_list[in6_addrscope(addr)];
334	SCOPE6_UNLOCK();
335	return (id);
336}
337
338/*
339 * Validate the specified scope zone ID in the sin6_scope_id field.  If the ID
340 * is unspecified (=0), needs to be specified, and the default zone ID can be
341 * used, the default value will be used.
342 * This routine then generates the kernel-internal form: if the address scope
343 * of is interface-local or link-local, embed the interface index in the
344 * address.
345 */
346int
347sa6_embedscope(struct sockaddr_in6 *sin6, int defaultok)
348{
349	INIT_VNET_NET(curvnet);
350	struct ifnet *ifp;
351	u_int32_t zoneid;
352
353	if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok)
354		zoneid = scope6_addr2default(&sin6->sin6_addr);
355
356	if (zoneid != 0 &&
357	    (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
358	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) {
359		/*
360		 * At this moment, we only check interface-local and
361		 * link-local scope IDs, and use interface indices as the
362		 * zone IDs assuming a one-to-one mapping between interfaces
363		 * and links.
364		 */
365		if (V_if_index < zoneid)
366			return (ENXIO);
367		ifp = ifnet_byindex(zoneid);
368		if (ifp == NULL) /* XXX: this can happen for some OS */
369			return (ENXIO);
370
371		/* XXX assignment to 16bit from 32bit variable */
372		sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);
373
374		sin6->sin6_scope_id = 0;
375	}
376
377	return 0;
378}
379
380/*
381 * generate standard sockaddr_in6 from embedded form.
382 */
383int
384sa6_recoverscope(struct sockaddr_in6 *sin6)
385{
386	INIT_VNET_NET(curvnet);
387	char ip6buf[INET6_ADDRSTRLEN];
388	u_int32_t zoneid;
389
390	if (sin6->sin6_scope_id != 0) {
391		log(LOG_NOTICE,
392		    "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
393		    ip6_sprintf(ip6buf, &sin6->sin6_addr), sin6->sin6_scope_id);
394		/* XXX: proceed anyway... */
395	}
396	if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
397	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
398		/*
399		 * KAME assumption: link id == interface id
400		 */
401		zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
402		if (zoneid) {
403			/* sanity check */
404			if (zoneid < 0 || V_if_index < zoneid)
405				return (ENXIO);
406			if (!ifnet_byindex(zoneid))
407				return (ENXIO);
408			sin6->sin6_addr.s6_addr16[1] = 0;
409			sin6->sin6_scope_id = zoneid;
410		}
411	}
412
413	return 0;
414}
415
416/*
417 * Determine the appropriate scope zone ID for in6 and ifp.  If ret_id is
418 * non NULL, it is set to the zone ID.  If the zone ID needs to be embedded
419 * in the in6_addr structure, in6 will be modified.
420 *
421 * ret_id - unnecessary?
422 */
423int
424in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id)
425{
426	int scope;
427	u_int32_t zoneid = 0;
428	struct scope6_id *sid;
429
430	IF_AFDATA_LOCK(ifp);
431
432	sid = SID(ifp);
433
434#ifdef DIAGNOSTIC
435	if (sid == NULL) { /* should not happen */
436		panic("in6_setscope: scope array is NULL");
437		/* NOTREACHED */
438	}
439#endif
440
441	/*
442	 * special case: the loopback address can only belong to a loopback
443	 * interface.
444	 */
445	if (IN6_IS_ADDR_LOOPBACK(in6)) {
446		if (!(ifp->if_flags & IFF_LOOPBACK)) {
447			IF_AFDATA_UNLOCK(ifp);
448			return (EINVAL);
449		} else {
450			if (ret_id != NULL)
451				*ret_id = 0; /* there's no ambiguity */
452			IF_AFDATA_UNLOCK(ifp);
453			return (0);
454		}
455	}
456
457	scope = in6_addrscope(in6);
458
459	SCOPE6_LOCK();
460	switch (scope) {
461	case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
462		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
463		break;
464
465	case IPV6_ADDR_SCOPE_LINKLOCAL:
466		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
467		break;
468
469	case IPV6_ADDR_SCOPE_SITELOCAL:
470		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
471		break;
472
473	case IPV6_ADDR_SCOPE_ORGLOCAL:
474		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
475		break;
476
477	default:
478		zoneid = 0;	/* XXX: treat as global. */
479		break;
480	}
481	SCOPE6_UNLOCK();
482	IF_AFDATA_UNLOCK(ifp);
483
484	if (ret_id != NULL)
485		*ret_id = zoneid;
486
487	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
488		in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */
489
490	return (0);
491}
492
493/*
494 * Just clear the embedded scope identifier.  Return 0 if the original address
495 * is intact; return non 0 if the address is modified.
496 */
497int
498in6_clearscope(struct in6_addr *in6)
499{
500	int modified = 0;
501
502	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
503		if (in6->s6_addr16[1] != 0)
504			modified = 1;
505		in6->s6_addr16[1] = 0;
506	}
507
508	return (modified);
509}
510