scope6.c revision 139826
1/*	$FreeBSD: head/sys/netinet6/scope6.c 139826 2005-01-07 02:30:35Z imp $	*/
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
40#include <net/route.h>
41#include <net/if.h>
42
43#include <netinet/in.h>
44
45#include <netinet6/in6_var.h>
46#include <netinet6/scope6_var.h>
47
48/*
49 * The scope6_lock protects the global sid default stored in
50 * sid_default below.
51 */
52static struct mtx scope6_lock;
53#define	SCOPE6_LOCK_INIT()	mtx_init(&scope6_lock, "scope6_lock", NULL, MTX_DEF)
54#define	SCOPE6_LOCK()		mtx_lock(&scope6_lock)
55#define	SCOPE6_UNLOCK()		mtx_unlock(&scope6_lock)
56#define	SCOPE6_LOCK_ASSERT()	mtx_assert(&scope6_lock, MA_OWNED)
57
58static struct scope6_id sid_default;
59#define SID(ifp) \
60	(((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->scope6_id)
61
62void
63scope6_init()
64{
65
66	SCOPE6_LOCK_INIT();
67	bzero(&sid_default, sizeof(sid_default));
68}
69
70struct scope6_id *
71scope6_ifattach(ifp)
72	struct ifnet *ifp;
73{
74	struct scope6_id *sid;
75
76	sid = (struct scope6_id *)malloc(sizeof(*sid), M_IFADDR, M_WAITOK);
77	bzero(sid, sizeof(*sid));
78
79	/*
80	 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
81	 * Should we rather hardcode here?
82	 */
83	sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index;
84	sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
85#ifdef MULTI_SCOPE
86	/* by default, we don't care about scope boundary for these scopes. */
87	sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
88	sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
89#endif
90
91	return sid;
92}
93
94void
95scope6_ifdetach(sid)
96	struct scope6_id *sid;
97{
98
99	free(sid, M_IFADDR);
100}
101
102int
103scope6_set(ifp, idlist)
104	struct ifnet *ifp;
105	struct scope6_id *idlist;
106{
107	int i;
108	int error = 0;
109	struct scope6_id *sid = NULL;
110
111	IF_AFDATA_LOCK(ifp);
112	sid = SID(ifp);
113
114	if (!sid) {	/* paranoid? */
115		IF_AFDATA_UNLOCK(ifp);
116		return (EINVAL);
117	}
118
119	/*
120	 * XXX: We need more consistency checks of the relationship among
121	 * scopes (e.g. an organization should be larger than a site).
122	 */
123
124	/*
125	 * TODO(XXX): after setting, we should reflect the changes to
126	 * interface addresses, routing table entries, PCB entries...
127	 */
128
129	SCOPE6_LOCK();
130	for (i = 0; i < 16; i++) {
131		if (idlist->s6id_list[i] &&
132		    idlist->s6id_list[i] != sid->s6id_list[i]) {
133			/*
134			 * An interface zone ID must be the corresponding
135			 * interface index by definition.
136			 */
137			if (i == IPV6_ADDR_SCOPE_INTFACELOCAL &&
138			    idlist->s6id_list[i] != ifp->if_index) {
139				IF_AFDATA_UNLOCK(ifp);
140				SCOPE6_UNLOCK();
141				return (EINVAL);
142			}
143
144			if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
145			    idlist->s6id_list[i] > if_index) {
146				/*
147				 * XXX: theoretically, there should be no
148				 * relationship between link IDs and interface
149				 * IDs, but we check the consistency for
150				 * safety in later use.
151				 */
152				IF_AFDATA_UNLOCK(ifp);
153				SCOPE6_UNLOCK();
154				return (EINVAL);
155			}
156
157			/*
158			 * XXX: we must need lots of work in this case,
159			 * but we simply set the new value in this initial
160			 * implementation.
161			 */
162			sid->s6id_list[i] = idlist->s6id_list[i];
163		}
164	}
165	SCOPE6_UNLOCK();
166	IF_AFDATA_UNLOCK(ifp);
167
168	return (error);
169}
170
171int
172scope6_get(ifp, idlist)
173	struct ifnet *ifp;
174	struct scope6_id *idlist;
175{
176	/* We only need to lock the interface's afdata for SID() to work. */
177	IF_AFDATA_LOCK(ifp);
178	struct scope6_id *sid = SID(ifp);
179
180	if (sid == NULL) {	/* paranoid? */
181		IF_AFDATA_UNLOCK(ifp);
182		return (EINVAL);
183	}
184
185	SCOPE6_LOCK();
186	*idlist = *sid;
187	SCOPE6_UNLOCK();
188
189	IF_AFDATA_UNLOCK(ifp);
190	return (0);
191}
192
193
194/*
195 * Get a scope of the address. Node-local, link-local, site-local or global.
196 */
197int
198in6_addrscope(addr)
199	struct in6_addr *addr;
200{
201	int scope;
202
203	if (addr->s6_addr[0] == 0xfe) {
204		scope = addr->s6_addr[1] & 0xc0;
205
206		switch (scope) {
207		case 0x80:
208			return IPV6_ADDR_SCOPE_LINKLOCAL;
209			break;
210		case 0xc0:
211			return IPV6_ADDR_SCOPE_SITELOCAL;
212			break;
213		default:
214			return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
215			break;
216		}
217	}
218
219
220	if (addr->s6_addr[0] == 0xff) {
221		scope = addr->s6_addr[1] & 0x0f;
222
223		/*
224		 * due to other scope such as reserved,
225		 * return scope doesn't work.
226		 */
227		switch (scope) {
228		case IPV6_ADDR_SCOPE_INTFACELOCAL:
229			return IPV6_ADDR_SCOPE_INTFACELOCAL;
230			break;
231		case IPV6_ADDR_SCOPE_LINKLOCAL:
232			return IPV6_ADDR_SCOPE_LINKLOCAL;
233			break;
234		case IPV6_ADDR_SCOPE_SITELOCAL:
235			return IPV6_ADDR_SCOPE_SITELOCAL;
236			break;
237		default:
238			return IPV6_ADDR_SCOPE_GLOBAL;
239			break;
240		}
241	}
242
243	/*
244	 * Regard loopback and unspecified addresses as global, since
245	 * they have no ambiguity.
246	 */
247	if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
248		if (addr->s6_addr[15] == 1) /* loopback */
249			return IPV6_ADDR_SCOPE_LINKLOCAL;
250		if (addr->s6_addr[15] == 0) /* unspecified */
251			return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */
252	}
253
254	return IPV6_ADDR_SCOPE_GLOBAL;
255}
256
257/*
258 * When we introduce the "4+28" split semantics in sin6_scope_id,
259 * a 32bit integer is not enough to tell a large ID from an error (-1).
260 * So, we intentionally use a large type as the return value.
261 */
262int
263in6_addr2zoneid(ifp, addr, ret_id)
264	struct ifnet *ifp;	/* must not be NULL */
265	struct in6_addr *addr;	/* must not be NULL */
266	u_int32_t *ret_id;	/* must not be NULL */
267{
268	int scope;
269	u_int32_t zoneid = 0;
270	struct scope6_id *sid = NULL;
271
272	IF_AFDATA_LOCK(ifp);
273
274	sid = SID(ifp);
275
276#ifdef DIAGNOSTIC
277	if (sid == NULL) { /* should not happen */
278		panic("in6_addr2zoneid: scope array is NULL");
279		/* NOTREACHED */
280	}
281	if (ret_id == NULL) {
282		panic("in6_addr2zoneid: return ID is null");
283		/* NOTREACHED */
284	}
285#endif
286
287	/*
288	 * special case: the loopback address can only belong to a loopback
289	 * interface.
290	 */
291	if (IN6_IS_ADDR_LOOPBACK(addr)) {
292		if (!(ifp->if_flags & IFF_LOOPBACK)) {
293			IF_AFDATA_UNLOCK(ifp);
294			return (-1);
295		} else {
296			*ret_id = 0; /* there's no ambiguity */
297			IF_AFDATA_UNLOCK(ifp);
298			return (0);
299		}
300	}
301
302	scope = in6_addrscope(addr);
303
304	/*
305	 * XXX: These are all u_int32_t reads, so may not require locking.
306	 */
307	SCOPE6_LOCK();
308	switch (scope) {
309	case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
310		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
311		break;
312
313	case IPV6_ADDR_SCOPE_LINKLOCAL:
314		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
315		break;
316
317	case IPV6_ADDR_SCOPE_SITELOCAL:
318		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
319		break;
320
321	case IPV6_ADDR_SCOPE_ORGLOCAL:
322		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
323		break;
324
325	default:
326		zoneid = 0;	/* XXX: treat as global. */
327		break;
328	}
329	SCOPE6_UNLOCK();
330
331	*ret_id = zoneid;
332
333	IF_AFDATA_UNLOCK(ifp);
334
335	return (0);
336}
337
338void
339scope6_setdefault(ifp)
340	struct ifnet *ifp;	/* note that this might be NULL */
341{
342	/*
343	 * Currently, this function just sets the default "interfaces"
344	 * and "links" according to the given interface.
345	 * We might eventually have to separate the notion of "link" from
346	 * "interface" and provide a user interface to set the default.
347	 */
348	SCOPE6_LOCK();
349	if (ifp) {
350		sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] =
351			ifp->if_index;
352		sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
353			ifp->if_index;
354	} else {
355		sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0;
356		sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
357	}
358	SCOPE6_UNLOCK();
359}
360
361int
362scope6_get_default(idlist)
363	struct scope6_id *idlist;
364{
365
366	SCOPE6_LOCK();
367	*idlist = sid_default;
368	SCOPE6_UNLOCK();
369
370	return (0);
371}
372
373u_int32_t
374scope6_addr2default(addr)
375	struct in6_addr *addr;
376{
377	u_int32_t id;
378
379	/*
380	 * special case: The loopback address should be considered as
381	 * link-local, but there's no ambiguity in the syntax.
382	 */
383	if (IN6_IS_ADDR_LOOPBACK(addr))
384		return (0);
385
386	/*
387	 * XXX: 32-bit read is atomic on all our platforms, is it OK
388	 * not to lock here?
389	 */
390	SCOPE6_LOCK();
391	id = sid_default.s6id_list[in6_addrscope(addr)];
392	SCOPE6_UNLOCK();
393	return (id);
394}
395