FreeBSD/Linux Kernel Cross Reference
sys/netinet6/scope6.c
1 /* $FreeBSD: releng/6.0/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 */
52 static 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
58 static struct scope6_id sid_default;
59 #define SID(ifp) \
60 (((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->scope6_id)
61
62 void
63 scope6_init()
64 {
65
66 SCOPE6_LOCK_INIT();
67 bzero(&sid_default, sizeof(sid_default));
68 }
69
70 struct scope6_id *
71 scope6_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
94 void
95 scope6_ifdetach(sid)
96 struct scope6_id *sid;
97 {
98
99 free(sid, M_IFADDR);
100 }
101
102 int
103 scope6_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
171 int
172 scope6_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 */
197 int
198 in6_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 */
262 int
263 in6_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
338 void
339 scope6_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
361 int
362 scope6_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
373 u_int32_t
374 scope6_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 }
Cache object: 7ff4ad253d9d4555bc2355c33d3addac
|