scope6.c revision 194739
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 194739 2009-06-23 17:03:45Z 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/if.h>
45#include <net/vnet.h>
46
47#include <netinet/in.h>
48
49#include <netinet/ip6.h>
50#include <netinet6/in6_var.h>
51#include <netinet6/scope6_var.h>
52#include <netinet6/vinet6.h>
53
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
65#ifdef VIMAGE_GLOBALS
66static struct scope6_id sid_default;
67int ip6_use_defzone;
68#endif
69
70#define SID(ifp) \
71	(((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->scope6_id)
72
73void
74scope6_init(void)
75{
76	INIT_VNET_INET6(curvnet);
77
78#ifdef ENABLE_DEFAULT_SCOPE
79	V_ip6_use_defzone = 1;
80#else
81	V_ip6_use_defzone = 0;
82#endif
83	bzero(&V_sid_default, sizeof(V_sid_default));
84
85	if (!IS_DEFAULT_VNET(curvnet))
86		return;
87
88	SCOPE6_LOCK_INIT();
89}
90
91struct scope6_id *
92scope6_ifattach(struct ifnet *ifp)
93{
94	struct scope6_id *sid;
95
96	sid = (struct scope6_id *)malloc(sizeof(*sid), M_IFADDR, M_WAITOK);
97	bzero(sid, sizeof(*sid));
98
99	/*
100	 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
101	 * Should we rather hardcode here?
102	 */
103	sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index;
104	sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
105#ifdef MULTI_SCOPE
106	/* by default, we don't care about scope boundary for these scopes. */
107	sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
108	sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
109#endif
110
111	return sid;
112}
113
114void
115scope6_ifdetach(struct scope6_id *sid)
116{
117
118	free(sid, M_IFADDR);
119}
120
121int
122scope6_set(struct ifnet *ifp, struct scope6_id *idlist)
123{
124	INIT_VNET_NET(ifp->if_vnet);
125	int i;
126	int error = 0;
127	struct scope6_id *sid = NULL;
128
129	IF_AFDATA_LOCK(ifp);
130	sid = SID(ifp);
131
132	if (!sid) {	/* paranoid? */
133		IF_AFDATA_UNLOCK(ifp);
134		return (EINVAL);
135	}
136
137	/*
138	 * XXX: We need more consistency checks of the relationship among
139	 * scopes (e.g. an organization should be larger than a site).
140	 */
141
142	/*
143	 * TODO(XXX): after setting, we should reflect the changes to
144	 * interface addresses, routing table entries, PCB entries...
145	 */
146
147	SCOPE6_LOCK();
148	for (i = 0; i < 16; i++) {
149		if (idlist->s6id_list[i] &&
150		    idlist->s6id_list[i] != sid->s6id_list[i]) {
151			/*
152			 * An interface zone ID must be the corresponding
153			 * interface index by definition.
154			 */
155			if (i == IPV6_ADDR_SCOPE_INTFACELOCAL &&
156			    idlist->s6id_list[i] != ifp->if_index) {
157				IF_AFDATA_UNLOCK(ifp);
158				SCOPE6_UNLOCK();
159				return (EINVAL);
160			}
161
162			if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
163			    idlist->s6id_list[i] > V_if_index) {
164				/*
165				 * XXX: theoretically, there should be no
166				 * relationship between link IDs and interface
167				 * IDs, but we check the consistency for
168				 * safety in later use.
169				 */
170				IF_AFDATA_UNLOCK(ifp);
171				SCOPE6_UNLOCK();
172				return (EINVAL);
173			}
174
175			/*
176			 * XXX: we must need lots of work in this case,
177			 * but we simply set the new value in this initial
178			 * implementation.
179			 */
180			sid->s6id_list[i] = idlist->s6id_list[i];
181		}
182	}
183	SCOPE6_UNLOCK();
184	IF_AFDATA_UNLOCK(ifp);
185
186	return (error);
187}
188
189int
190scope6_get(struct ifnet *ifp, struct scope6_id *idlist)
191{
192	/* We only need to lock the interface's afdata for SID() to work. */
193	IF_AFDATA_LOCK(ifp);
194	struct scope6_id *sid = SID(ifp);
195
196	if (sid == NULL) {	/* paranoid? */
197		IF_AFDATA_UNLOCK(ifp);
198		return (EINVAL);
199	}
200
201	SCOPE6_LOCK();
202	*idlist = *sid;
203	SCOPE6_UNLOCK();
204
205	IF_AFDATA_UNLOCK(ifp);
206	return (0);
207}
208
209
210/*
211 * Get a scope of the address. Node-local, link-local, site-local or global.
212 */
213int
214in6_addrscope(struct in6_addr *addr)
215{
216	int scope;
217
218	if (addr->s6_addr[0] == 0xfe) {
219		scope = addr->s6_addr[1] & 0xc0;
220
221		switch (scope) {
222		case 0x80:
223			return IPV6_ADDR_SCOPE_LINKLOCAL;
224			break;
225		case 0xc0:
226			return IPV6_ADDR_SCOPE_SITELOCAL;
227			break;
228		default:
229			return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
230			break;
231		}
232	}
233
234
235	if (addr->s6_addr[0] == 0xff) {
236		scope = addr->s6_addr[1] & 0x0f;
237
238		/*
239		 * due to other scope such as reserved,
240		 * return scope doesn't work.
241		 */
242		switch (scope) {
243		case IPV6_ADDR_SCOPE_INTFACELOCAL:
244			return IPV6_ADDR_SCOPE_INTFACELOCAL;
245			break;
246		case IPV6_ADDR_SCOPE_LINKLOCAL:
247			return IPV6_ADDR_SCOPE_LINKLOCAL;
248			break;
249		case IPV6_ADDR_SCOPE_SITELOCAL:
250			return IPV6_ADDR_SCOPE_SITELOCAL;
251			break;
252		default:
253			return IPV6_ADDR_SCOPE_GLOBAL;
254			break;
255		}
256	}
257
258	/*
259	 * Regard loopback and unspecified addresses as global, since
260	 * they have no ambiguity.
261	 */
262	if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
263		if (addr->s6_addr[15] == 1) /* loopback */
264			return IPV6_ADDR_SCOPE_LINKLOCAL;
265		if (addr->s6_addr[15] == 0) /* unspecified */
266			return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */
267	}
268
269	return IPV6_ADDR_SCOPE_GLOBAL;
270}
271
272/*
273 * ifp - note that this might be NULL
274 */
275
276void
277scope6_setdefault(struct ifnet *ifp)
278{
279	INIT_VNET_INET6(ifp->if_vnet);
280
281	/*
282	 * Currently, this function just sets the default "interfaces"
283	 * and "links" according to the given interface.
284	 * We might eventually have to separate the notion of "link" from
285	 * "interface" and provide a user interface to set the default.
286	 */
287	SCOPE6_LOCK();
288	if (ifp) {
289		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] =
290			ifp->if_index;
291		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
292			ifp->if_index;
293	} else {
294		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0;
295		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
296	}
297	SCOPE6_UNLOCK();
298}
299
300int
301scope6_get_default(struct scope6_id *idlist)
302{
303	INIT_VNET_INET6(curvnet);
304
305	SCOPE6_LOCK();
306	*idlist = V_sid_default;
307	SCOPE6_UNLOCK();
308
309	return (0);
310}
311
312u_int32_t
313scope6_addr2default(struct in6_addr *addr)
314{
315	INIT_VNET_INET6(curvnet);
316	u_int32_t id;
317
318	/*
319	 * special case: The loopback address should be considered as
320	 * link-local, but there's no ambiguity in the syntax.
321	 */
322	if (IN6_IS_ADDR_LOOPBACK(addr))
323		return (0);
324
325	/*
326	 * XXX: 32-bit read is atomic on all our platforms, is it OK
327	 * not to lock here?
328	 */
329	SCOPE6_LOCK();
330	id = V_sid_default.s6id_list[in6_addrscope(addr)];
331	SCOPE6_UNLOCK();
332	return (id);
333}
334
335/*
336 * Validate the specified scope zone ID in the sin6_scope_id field.  If the ID
337 * is unspecified (=0), needs to be specified, and the default zone ID can be
338 * used, the default value will be used.
339 * This routine then generates the kernel-internal form: if the address scope
340 * of is interface-local or link-local, embed the interface index in the
341 * address.
342 */
343int
344sa6_embedscope(struct sockaddr_in6 *sin6, int defaultok)
345{
346	INIT_VNET_NET(curvnet);
347	struct ifnet *ifp;
348	u_int32_t zoneid;
349
350	if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok)
351		zoneid = scope6_addr2default(&sin6->sin6_addr);
352
353	if (zoneid != 0 &&
354	    (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
355	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) {
356		/*
357		 * At this moment, we only check interface-local and
358		 * link-local scope IDs, and use interface indices as the
359		 * zone IDs assuming a one-to-one mapping between interfaces
360		 * and links.
361		 */
362		if (V_if_index < zoneid)
363			return (ENXIO);
364		ifp = ifnet_byindex(zoneid);
365		if (ifp == NULL) /* XXX: this can happen for some OS */
366			return (ENXIO);
367
368		/* XXX assignment to 16bit from 32bit variable */
369		sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);
370
371		sin6->sin6_scope_id = 0;
372	}
373
374	return 0;
375}
376
377/*
378 * generate standard sockaddr_in6 from embedded form.
379 */
380int
381sa6_recoverscope(struct sockaddr_in6 *sin6)
382{
383	INIT_VNET_NET(curvnet);
384	char ip6buf[INET6_ADDRSTRLEN];
385	u_int32_t zoneid;
386
387	if (sin6->sin6_scope_id != 0) {
388		log(LOG_NOTICE,
389		    "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
390		    ip6_sprintf(ip6buf, &sin6->sin6_addr), sin6->sin6_scope_id);
391		/* XXX: proceed anyway... */
392	}
393	if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
394	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
395		/*
396		 * KAME assumption: link id == interface id
397		 */
398		zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
399		if (zoneid) {
400			/* sanity check */
401			if (zoneid < 0 || V_if_index < zoneid)
402				return (ENXIO);
403			if (!ifnet_byindex(zoneid))
404				return (ENXIO);
405			sin6->sin6_addr.s6_addr16[1] = 0;
406			sin6->sin6_scope_id = zoneid;
407		}
408	}
409
410	return 0;
411}
412
413/*
414 * Determine the appropriate scope zone ID for in6 and ifp.  If ret_id is
415 * non NULL, it is set to the zone ID.  If the zone ID needs to be embedded
416 * in the in6_addr structure, in6 will be modified.
417 *
418 * ret_id - unnecessary?
419 */
420int
421in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id)
422{
423	int scope;
424	u_int32_t zoneid = 0;
425	struct scope6_id *sid;
426
427	IF_AFDATA_LOCK(ifp);
428
429	sid = SID(ifp);
430
431#ifdef DIAGNOSTIC
432	if (sid == NULL) { /* should not happen */
433		panic("in6_setscope: scope array is NULL");
434		/* NOTREACHED */
435	}
436#endif
437
438	/*
439	 * special case: the loopback address can only belong to a loopback
440	 * interface.
441	 */
442	if (IN6_IS_ADDR_LOOPBACK(in6)) {
443		if (!(ifp->if_flags & IFF_LOOPBACK)) {
444			IF_AFDATA_UNLOCK(ifp);
445			return (EINVAL);
446		} else {
447			if (ret_id != NULL)
448				*ret_id = 0; /* there's no ambiguity */
449			IF_AFDATA_UNLOCK(ifp);
450			return (0);
451		}
452	}
453
454	scope = in6_addrscope(in6);
455
456	SCOPE6_LOCK();
457	switch (scope) {
458	case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
459		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
460		break;
461
462	case IPV6_ADDR_SCOPE_LINKLOCAL:
463		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
464		break;
465
466	case IPV6_ADDR_SCOPE_SITELOCAL:
467		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
468		break;
469
470	case IPV6_ADDR_SCOPE_ORGLOCAL:
471		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
472		break;
473
474	default:
475		zoneid = 0;	/* XXX: treat as global. */
476		break;
477	}
478	SCOPE6_UNLOCK();
479	IF_AFDATA_UNLOCK(ifp);
480
481	if (ret_id != NULL)
482		*ret_id = zoneid;
483
484	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
485		in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */
486
487	return (0);
488}
489
490/*
491 * Just clear the embedded scope identifier.  Return 0 if the original address
492 * is intact; return non 0 if the address is modified.
493 */
494int
495in6_clearscope(struct in6_addr *in6)
496{
497	int modified = 0;
498
499	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
500		if (in6->s6_addr16[1] != 0)
501			modified = 1;
502		in6->s6_addr16[1] = 0;
503	}
504
505	return (modified);
506}
507