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