scope6.c revision 111119
1/*	$FreeBSD: head/sys/netinet6/scope6.c 111119 2003-02-19 05:47:46Z 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
48struct scope6_id {
49	/*
50	 * 16 is correspondent to 4bit multicast scope field.
51	 * i.e. from node-local to global with some reserved/unassigned types.
52	 */
53	u_int32_t s6id_list[16];
54};
55static size_t if_indexlim = 8;
56struct scope6_id *scope6_ids = NULL;
57
58void
59scope6_ifattach(ifp)
60	struct ifnet *ifp;
61{
62	int s = splnet();
63
64	/*
65	 * We have some arrays that should be indexed by if_index.
66	 * since if_index will grow dynamically, they should grow too.
67	 */
68	if (scope6_ids == NULL || if_index >= if_indexlim) {
69		size_t n;
70		caddr_t q;
71
72		while (if_index >= if_indexlim)
73			if_indexlim <<= 1;
74
75		/* grow scope index array */
76		n = if_indexlim * sizeof(struct scope6_id);
77		/* XXX: need new malloc type? */
78		q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK);
79		bzero(q, n);
80		if (scope6_ids) {
81			bcopy((caddr_t)scope6_ids, q, n/2);
82			free((caddr_t)scope6_ids, M_IFADDR);
83		}
84		scope6_ids = (struct scope6_id *)q;
85	}
86
87#define SID scope6_ids[ifp->if_index]
88
89	/* don't initialize if called twice */
90	if (SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]) {
91		splx(s);
92		return;
93	}
94
95	/*
96	 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
97	 * Should we rather hardcode here?
98	 */
99	SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
100#ifdef MULTI_SCOPE
101	/* by default, we don't care about scope boundary for these scopes. */
102	SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
103	SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
104#endif
105#undef SID
106
107	splx(s);
108}
109
110int
111scope6_set(ifp, idlist)
112	struct ifnet *ifp;
113	u_int32_t *idlist;
114{
115	int i, s;
116	int error = 0;
117
118	if (scope6_ids == NULL)	/* paranoid? */
119		return(EINVAL);
120
121	/*
122	 * XXX: We need more consistency checks of the relationship among
123	 * scopes (e.g. an organization should be larger than a site).
124	 */
125
126	/*
127	 * TODO(XXX): after setting, we should reflect the changes to
128	 * interface addresses, routing table entries, PCB entries...
129	 */
130
131	s = splnet();
132
133	for (i = 0; i < 16; i++) {
134		if (idlist[i] &&
135		    idlist[i] != scope6_ids[ifp->if_index].s6id_list[i]) {
136			if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
137			    idlist[i] > if_index) {
138				/*
139				 * XXX: theoretically, there should be no
140				 * relationship between link IDs and interface
141				 * IDs, but we check the consistency for
142				 * safety in later use.
143				 */
144				splx(s);
145				return(EINVAL);
146			}
147
148			/*
149			 * XXX: we must need lots of work in this case,
150			 * but we simply set the new value in this initial
151			 * implementation.
152			 */
153			scope6_ids[ifp->if_index].s6id_list[i] = idlist[i];
154		}
155	}
156	splx(s);
157
158	return(error);
159}
160
161int
162scope6_get(ifp, idlist)
163	struct ifnet *ifp;
164	u_int32_t *idlist;
165{
166	if (scope6_ids == NULL)	/* paranoid? */
167		return(EINVAL);
168
169	bcopy(scope6_ids[ifp->if_index].s6id_list, idlist,
170	      sizeof(scope6_ids[ifp->if_index].s6id_list));
171
172	return(0);
173}
174
175
176/*
177 * Get a scope of the address. Node-local, link-local, site-local or global.
178 */
179int
180in6_addrscope(addr)
181struct in6_addr *addr;
182{
183	int scope;
184
185	if (addr->s6_addr8[0] == 0xfe) {
186		scope = addr->s6_addr8[1] & 0xc0;
187
188		switch (scope) {
189		case 0x80:
190			return IPV6_ADDR_SCOPE_LINKLOCAL;
191			break;
192		case 0xc0:
193			return IPV6_ADDR_SCOPE_SITELOCAL;
194			break;
195		default:
196			return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
197			break;
198		}
199	}
200
201
202	if (addr->s6_addr8[0] == 0xff) {
203		scope = addr->s6_addr8[1] & 0x0f;
204
205		/*
206		 * due to other scope such as reserved,
207		 * return scope doesn't work.
208		 */
209		switch (scope) {
210		case IPV6_ADDR_SCOPE_NODELOCAL:
211			return IPV6_ADDR_SCOPE_NODELOCAL;
212			break;
213		case IPV6_ADDR_SCOPE_LINKLOCAL:
214			return IPV6_ADDR_SCOPE_LINKLOCAL;
215			break;
216		case IPV6_ADDR_SCOPE_SITELOCAL:
217			return IPV6_ADDR_SCOPE_SITELOCAL;
218			break;
219		default:
220			return IPV6_ADDR_SCOPE_GLOBAL;
221			break;
222		}
223	}
224
225	if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
226		if (addr->s6_addr8[15] == 1) /* loopback */
227			return IPV6_ADDR_SCOPE_NODELOCAL;
228		if (addr->s6_addr8[15] == 0) /* unspecified */
229			return IPV6_ADDR_SCOPE_LINKLOCAL;
230	}
231
232	return IPV6_ADDR_SCOPE_GLOBAL;
233}
234
235int
236in6_addr2scopeid(ifp, addr)
237	struct ifnet *ifp;	/* must not be NULL */
238	struct in6_addr *addr;	/* must not be NULL */
239{
240	int scope = in6_addrscope(addr);
241
242	if (scope6_ids == NULL)	/* paranoid? */
243		return(0);	/* XXX */
244	if (ifp->if_index >= if_indexlim)
245		return(0);	/* XXX */
246
247#define SID scope6_ids[ifp->if_index]
248	switch(scope) {
249	case IPV6_ADDR_SCOPE_NODELOCAL:
250		return(-1);	/* XXX: is this an appropriate value? */
251
252	case IPV6_ADDR_SCOPE_LINKLOCAL:
253		return(SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]);
254
255	case IPV6_ADDR_SCOPE_SITELOCAL:
256		return(SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]);
257
258	case IPV6_ADDR_SCOPE_ORGLOCAL:
259		return(SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]);
260
261	default:
262		return(0);	/* XXX: treat as global. */
263	}
264#undef SID
265}
266
267void
268scope6_setdefault(ifp)
269	struct ifnet *ifp;	/* note that this might be NULL */
270{
271	/*
272	 * Currently, this function just set the default "link" according to
273	 * the given interface.
274	 * We might eventually have to separate the notion of "link" from
275	 * "interface" and provide a user interface to set the default.
276	 */
277	if (ifp) {
278		scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
279			ifp->if_index;
280	}
281	else
282		scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
283}
284
285int
286scope6_get_default(idlist)
287	u_int32_t *idlist;
288{
289	if (scope6_ids == NULL)	/* paranoid? */
290		return(EINVAL);
291
292	bcopy(scope6_ids[0].s6id_list, idlist,
293	      sizeof(scope6_ids[0].s6id_list));
294
295	return(0);
296}
297
298u_int32_t
299scope6_addr2default(addr)
300	struct in6_addr *addr;
301{
302	return(scope6_ids[0].s6id_list[in6_addrscope(addr)]);
303}
304