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