scope6.c revision 120941
1198090Srdivacky/*	$FreeBSD: head/sys/netinet6/scope6.c 120941 2003-10-09 16:13:47Z ume $	*/
2198090Srdivacky/*	$KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $	*/
3198090Srdivacky
4198090Srdivacky/*
5198090Srdivacky * Copyright (C) 2000 WIDE Project.
6198090Srdivacky * All rights reserved.
7198090Srdivacky *
8198090Srdivacky * Redistribution and use in source and binary forms, with or without
9198090Srdivacky * modification, are permitted provided that the following conditions
10198090Srdivacky * are met:
11218893Sdim * 1. Redistributions of source code must retain the above copyright
12218893Sdim *    notice, this list of conditions and the following disclaimer.
13198090Srdivacky * 2. Redistributions in binary form must reproduce the above copyright
14218893Sdim *    notice, this list of conditions and the following disclaimer in the
15218893Sdim *    documentation and/or other materials provided with the distribution.
16218893Sdim * 3. Neither the name of the project nor the names of its contributors
17198090Srdivacky *    may be used to endorse or promote products derived from this software
18198090Srdivacky *    without specific prior written permission.
19198090Srdivacky *
20198090Srdivacky * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21198090Srdivacky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22198090Srdivacky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23198090Srdivacky * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24198090Srdivacky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25198090Srdivacky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26198090Srdivacky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27218893Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28198090Srdivacky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29198090Srdivacky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30198090Srdivacky * SUCH DAMAGE.
31198090Srdivacky */
32198090Srdivacky
33198090Srdivacky#include <sys/param.h>
34198090Srdivacky#include <sys/malloc.h>
35198090Srdivacky#include <sys/mbuf.h>
36198090Srdivacky#include <sys/socket.h>
37198090Srdivacky#include <sys/systm.h>
38198090Srdivacky#include <sys/queue.h>
39198090Srdivacky
40198090Srdivacky#include <net/route.h>
41198090Srdivacky#include <net/if.h>
42198090Srdivacky
43198090Srdivacky#include <netinet/in.h>
44198090Srdivacky
45198090Srdivacky#include <netinet6/in6_var.h>
46198090Srdivacky#include <netinet6/scope6_var.h>
47198090Srdivacky
48198090Srdivackystruct scope6_id {
49198090Srdivacky	/*
50198090Srdivacky	 * 16 is correspondent to 4bit multicast scope field.
51198090Srdivacky	 * i.e. from node-local to global with some reserved/unassigned types.
52198090Srdivacky	 */
53198090Srdivacky	u_int32_t s6id_list[16];
54198090Srdivacky};
55198090Srdivackystatic size_t if_indexlim = 8;
56198090Srdivackystruct scope6_id *scope6_ids = NULL;
57198090Srdivacky
58198090Srdivackyvoid
59198090Srdivackyscope6_ifattach(ifp)
60198090Srdivacky	struct ifnet *ifp;
61198090Srdivacky{
62198090Srdivacky	int s = splnet();
63198090Srdivacky
64198090Srdivacky	/*
65198090Srdivacky	 * We have some arrays that should be indexed by if_index.
66198090Srdivacky	 * since if_index will grow dynamically, they should grow too.
67198090Srdivacky	 */
68198090Srdivacky	if (scope6_ids == NULL || if_index >= if_indexlim) {
69198090Srdivacky		size_t n;
70198090Srdivacky		caddr_t q;
71198090Srdivacky
72198090Srdivacky		while (if_index >= if_indexlim)
73198090Srdivacky			if_indexlim <<= 1;
74198090Srdivacky
75218893Sdim		/* grow scope index array */
76218893Sdim		n = if_indexlim * sizeof(struct scope6_id);
77218893Sdim		/* XXX: need new malloc type? */
78218893Sdim		q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK);
79218893Sdim		bzero(q, n);
80245431Sdim		if (scope6_ids) {
81218893Sdim			bcopy((caddr_t)scope6_ids, q, n/2);
82218893Sdim			free((caddr_t)scope6_ids, M_IFADDR);
83218893Sdim		}
84218893Sdim		scope6_ids = (struct scope6_id *)q;
85218893Sdim	}
86218893Sdim
87218893Sdim#define SID scope6_ids[ifp->if_index]
88218893Sdim
89218893Sdim	/* don't initialize if called twice */
90218893Sdim	if (SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]) {
91221345Sdim		splx(s);
92218893Sdim		return;
93218893Sdim	}
94218893Sdim
95218893Sdim	/*
96218893Sdim	 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
97198090Srdivacky	 * Should we rather hardcode here?
98198090Srdivacky	 */
99198090Srdivacky	SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
100198090Srdivacky#ifdef MULTI_SCOPE
101218893Sdim	/* by default, we don't care about scope boundary for these scopes. */
102252723Sdim	SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
103218893Sdim	SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
104198090Srdivacky#endif
105198090Srdivacky#undef SID
106198090Srdivacky
107198090Srdivacky	splx(s);
108235633Sdim}
109226890Sdim
110226890Sdimint
111245431Sdimscope6_set(ifp, idlist)
112263509Sdim	struct ifnet *ifp;
113245431Sdim	u_int32_t *idlist;
114245431Sdim{
115263509Sdim	int i, s;
116198090Srdivacky	int error = 0;
117198090Srdivacky
118263509Sdim	if (scope6_ids == NULL)	/* paranoid? */
119198090Srdivacky		return (EINVAL);
120198090Srdivacky
121198090Srdivacky	/*
122198090Srdivacky	 * XXX: We need more consistency checks of the relationship among
123198090Srdivacky	 * scopes (e.g. an organization should be larger than a site).
124198090Srdivacky	 */
125198090Srdivacky
126218893Sdim	/*
127212904Sdim	 * TODO(XXX): after setting, we should reflect the changes to
128212904Sdim	 * interface addresses, routing table entries, PCB entries...
129263509Sdim	 */
130263509Sdim
131263509Sdim	s = splnet();
132263509Sdim
133263509Sdim	for (i = 0; i < 16; i++) {
134263509Sdim		if (idlist[i] &&
135263509Sdim		    idlist[i] != scope6_ids[ifp->if_index].s6id_list[i]) {
136245431Sdim			if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
137245431Sdim			    idlist[i] > if_index) {
138245431Sdim				/*
139245431Sdim				 * XXX: theoretically, there should be no
140245431Sdim				 * relationship between link IDs and interface
141245431Sdim				 * IDs, but we check the consistency for
142245431Sdim				 * safety in later use.
143245431Sdim				 */
144198090Srdivacky				splx(s);
145198090Srdivacky				return (EINVAL);
146198090Srdivacky			}
147198090Srdivacky
148198090Srdivacky			/*
149198090Srdivacky			 * XXX: we must need lots of work in this case,
150198090Srdivacky			 * but we simply set the new value in this initial
151198090Srdivacky			 * implementation.
152198090Srdivacky			 */
153198090Srdivacky			scope6_ids[ifp->if_index].s6id_list[i] = idlist[i];
154198090Srdivacky		}
155198090Srdivacky	}
156198090Srdivacky	splx(s);
157198090Srdivacky
158198090Srdivacky	return (error);
159198090Srdivacky}
160198090Srdivacky
161198090Srdivackyint
162198090Srdivackyscope6_get(ifp, idlist)
163198090Srdivacky	struct ifnet *ifp;
164198090Srdivacky	u_int32_t *idlist;
165198090Srdivacky{
166198090Srdivacky	if (scope6_ids == NULL)	/* paranoid? */
167198090Srdivacky		return (EINVAL);
168198090Srdivacky
169198090Srdivacky	bcopy(scope6_ids[ifp->if_index].s6id_list, idlist,
170198090Srdivacky	      sizeof(scope6_ids[ifp->if_index].s6id_list));
171198090Srdivacky
172198090Srdivacky	return (0);
173198090Srdivacky}
174198090Srdivacky
175198090Srdivacky
176198090Srdivacky/*
177198090Srdivacky * Get a scope of the address. Node-local, link-local, site-local or global.
178198090Srdivacky */
179198090Srdivackyint
180198090Srdivackyin6_addrscope(addr)
181198090Srdivacky	struct in6_addr *addr;
182198090Srdivacky{
183198090Srdivacky	int scope;
184198090Srdivacky
185198090Srdivacky	if (addr->s6_addr8[0] == 0xfe) {
186198090Srdivacky		scope = addr->s6_addr8[1] & 0xc0;
187198090Srdivacky
188198090Srdivacky		switch (scope) {
189198090Srdivacky		case 0x80:
190198090Srdivacky			return IPV6_ADDR_SCOPE_LINKLOCAL;
191218893Sdim			break;
192218893Sdim		case 0xc0:
193218893Sdim			return IPV6_ADDR_SCOPE_SITELOCAL;
194218893Sdim			break;
195198090Srdivacky		default:
196263509Sdim			return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
197198090Srdivacky			break;
198245431Sdim		}
199245431Sdim	}
200198090Srdivacky
201198090Srdivacky
202198090Srdivacky	if (addr->s6_addr8[0] == 0xff) {
203198090Srdivacky		scope = addr->s6_addr8[1] & 0x0f;
204198090Srdivacky
205198090Srdivacky		/*
206198090Srdivacky		 * due to other scope such as reserved,
207198090Srdivacky		 * return scope doesn't work.
208198090Srdivacky		 */
209198090Srdivacky		switch (scope) {
210198090Srdivacky		case IPV6_ADDR_SCOPE_NODELOCAL:
211245431Sdim			return IPV6_ADDR_SCOPE_NODELOCAL;
212198090Srdivacky			break;
213198090Srdivacky		case IPV6_ADDR_SCOPE_LINKLOCAL:
214198090Srdivacky			return IPV6_ADDR_SCOPE_LINKLOCAL;
215198090Srdivacky			break;
216198090Srdivacky		case IPV6_ADDR_SCOPE_SITELOCAL:
217198090Srdivacky			return IPV6_ADDR_SCOPE_SITELOCAL;
218198090Srdivacky			break;
219198090Srdivacky		default:
220198090Srdivacky			return IPV6_ADDR_SCOPE_GLOBAL;
221198090Srdivacky			break;
222198090Srdivacky		}
223198090Srdivacky	}
224263509Sdim
225263509Sdim	if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
226218893Sdim		if (addr->s6_addr8[15] == 1) /* loopback */
227198090Srdivacky			return IPV6_ADDR_SCOPE_NODELOCAL;
228263509Sdim		if (addr->s6_addr8[15] == 0) /* unspecified */
229198090Srdivacky			return IPV6_ADDR_SCOPE_LINKLOCAL;
230198090Srdivacky	}
231198090Srdivacky
232198090Srdivacky	return IPV6_ADDR_SCOPE_GLOBAL;
233198090Srdivacky}
234198090Srdivacky
235198090Srdivackyint
236198090Srdivackyin6_addr2scopeid(ifp, addr)
237198090Srdivacky	struct ifnet *ifp;	/* must not be NULL */
238198090Srdivacky	struct in6_addr *addr;	/* must not be NULL */
239198090Srdivacky{
240198090Srdivacky	int scope = in6_addrscope(addr);
241198090Srdivacky
242198090Srdivacky	if (scope6_ids == NULL)	/* paranoid? */
243198090Srdivacky		return (0);	/* XXX */
244198090Srdivacky	if (ifp->if_index >= if_indexlim)
245198090Srdivacky		return (0);	/* XXX */
246218893Sdim
247198090Srdivacky#define SID scope6_ids[ifp->if_index]
248198090Srdivacky	switch(scope) {
249198090Srdivacky	case IPV6_ADDR_SCOPE_NODELOCAL:
250245431Sdim		return (-1);	/* XXX: is this an appropriate value? */
251198090Srdivacky
252198090Srdivacky	case IPV6_ADDR_SCOPE_LINKLOCAL:
253198090Srdivacky		return (SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]);
254198090Srdivacky
255198090Srdivacky	case IPV6_ADDR_SCOPE_SITELOCAL:
256198090Srdivacky		return (SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]);
257198090Srdivacky
258198090Srdivacky	case IPV6_ADDR_SCOPE_ORGLOCAL:
259198090Srdivacky		return (SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]);
260198090Srdivacky
261198090Srdivacky	default:
262198090Srdivacky		return (0);	/* XXX: treat as global. */
263198090Srdivacky	}
264198090Srdivacky#undef SID
265198090Srdivacky}
266198090Srdivacky
267208599Srdivackyvoid
268208599Srdivackyscope6_setdefault(ifp)
269208599Srdivacky	struct ifnet *ifp;	/* note that this might be NULL */
270198090Srdivacky{
271198090Srdivacky	/*
272198090Srdivacky	 * Currently, this function just set the default "link" according to
273198090Srdivacky	 * the given interface.
274198090Srdivacky	 * We might eventually have to separate the notion of "link" from
275198090Srdivacky	 * "interface" and provide a user interface to set the default.
276235633Sdim	 */
277198090Srdivacky	if (ifp) {
278198090Srdivacky		scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
279221345Sdim			ifp->if_index;
280208599Srdivacky	} else {
281210299Sed		scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
282208599Srdivacky	}
283210299Sed}
284208599Srdivacky
285208599Srdivackyint
286208599Srdivackyscope6_get_default(idlist)
287198090Srdivacky	u_int32_t *idlist;
288198090Srdivacky{
289198090Srdivacky	if (scope6_ids == NULL)	/* paranoid? */
290198090Srdivacky		return (EINVAL);
291245431Sdim
292245431Sdim	bcopy(scope6_ids[0].s6id_list, idlist,
293245431Sdim	      sizeof(scope6_ids[0].s6id_list));
294245431Sdim
295245431Sdim	return (0);
296245431Sdim}
297245431Sdim
298245431Sdimu_int32_t
299245431Sdimscope6_addr2default(addr)
300218893Sdim	struct in6_addr *addr;
301218893Sdim{
302218893Sdim	return (scope6_ids[0].s6id_list[in6_addrscope(addr)]);
303218893Sdim}
304218893Sdim