]> git.saurik.com Git - apple/xnu.git/blame - bsd/netinet6/scope6.c
xnu-2050.7.9.tar.gz
[apple/xnu.git] / bsd / netinet6 / scope6.c
CommitLineData
6d2010ae 1/*
316670eb 2 * Copyright (c) 2009-2011 Apple Inc. All rights reserved.
6d2010ae
A
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
55e303ae 29/* $FreeBSD: src/sys/netinet6/scope6.c,v 1.3 2002/03/25 10:12:51 ume Exp $ */
9bccf70c
A
30/* $KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $ */
31
32/*
33 * Copyright (C) 2000 WIDE Project.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. Neither the name of the project nor the names of its contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 */
60
61#include <sys/param.h>
62#include <sys/malloc.h>
63#include <sys/mbuf.h>
64#include <sys/socket.h>
65#include <sys/systm.h>
66#include <sys/queue.h>
6d2010ae
A
67#include <sys/syslog.h>
68#include <sys/mcache.h>
9bccf70c
A
69
70#include <net/route.h>
71#include <net/if.h>
72
73#include <netinet/in.h>
74
75#include <netinet6/in6_var.h>
76#include <netinet6/scope6_var.h>
77
b0d623f7
A
78extern lck_mtx_t *scope6_mutex;
79
6d2010ae
A
80#ifdef ENABLE_DEFAULT_SCOPE
81int ip6_use_defzone = 1;
82#else
83int ip6_use_defzone = 0;
84#endif
85
b0d623f7 86static size_t if_scope_indexlim = 8;
9bccf70c
A
87struct scope6_id *scope6_ids = NULL;
88
b0d623f7 89int
91447636
A
90scope6_ifattach(
91 struct ifnet *ifp)
9bccf70c 92{
9bccf70c
A
93 /*
94 * We have some arrays that should be indexed by if_index.
95 * since if_index will grow dynamically, they should grow too.
96 */
b0d623f7
A
97 lck_mtx_lock(scope6_mutex);
98 if (scope6_ids == NULL || if_index >= if_scope_indexlim) {
9bccf70c
A
99 size_t n;
100 caddr_t q;
b0d623f7 101 int newlim = if_scope_indexlim;
9bccf70c 102
b0d623f7
A
103 while (if_index >= newlim)
104 newlim <<= 1;
9bccf70c
A
105
106 /* grow scope index array */
b0d623f7 107 n = newlim * sizeof(struct scope6_id);
9bccf70c
A
108 /* XXX: need new malloc type? */
109 q = (caddr_t)_MALLOC(n, M_IFADDR, M_WAITOK);
b0d623f7
A
110 if (q == NULL) {
111 lck_mtx_unlock(scope6_mutex);
112 return ENOBUFS;
113 }
114 if_scope_indexlim = newlim;
9bccf70c
A
115 bzero(q, n);
116 if (scope6_ids) {
117 bcopy((caddr_t)scope6_ids, q, n/2);
118 FREE((caddr_t)scope6_ids, M_IFADDR);
119 }
316670eb 120 scope6_ids = (struct scope6_id *)(void *)q;
9bccf70c
A
121 }
122
123#define SID scope6_ids[ifp->if_index]
124
125 /* don't initialize if called twice */
126 if (SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]) {
b0d623f7
A
127 lck_mtx_unlock(scope6_mutex);
128 return 0;
9bccf70c
A
129 }
130
131 /*
132 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
133 * Should we rather hardcode here?
134 */
6d2010ae 135 SID.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index;
9bccf70c
A
136 SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
137#if MULTI_SCOPE
138 /* by default, we don't care about scope boundary for these scopes. */
139 SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
140 SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
141#endif
142#undef SID
b0d623f7
A
143 lck_mtx_unlock(scope6_mutex);
144
145 return 0;
9bccf70c
A
146}
147
148int
91447636
A
149scope6_set(
150 struct ifnet *ifp,
151 u_int32_t *idlist)
9bccf70c 152{
2d21ac55 153 int i;
9bccf70c
A
154 int error = 0;
155
156 if (scope6_ids == NULL) /* paranoid? */
157 return(EINVAL);
158
159 /*
160 * XXX: We need more consistency checks of the relationship among
161 * scopes (e.g. an organization should be larger than a site).
162 */
163
164 /*
165 * TODO(XXX): after setting, we should reflect the changes to
6d2010ae 166 * interface addresses, routing table entries, PCB entries...
9bccf70c
A
167 */
168
b0d623f7 169 lck_mtx_lock(scope6_mutex);
9bccf70c
A
170 for (i = 0; i < 16; i++) {
171 if (idlist[i] &&
172 idlist[i] != scope6_ids[ifp->if_index].s6id_list[i]) {
6d2010ae 173 if (i == IPV6_ADDR_SCOPE_INTFACELOCAL &&
9bccf70c
A
174 idlist[i] > if_index) {
175 /*
176 * XXX: theoretically, there should be no
177 * relationship between link IDs and interface
178 * IDs, but we check the consistency for
179 * safety in later use.
180 */
b0d623f7 181 lck_mtx_unlock(scope6_mutex);
9bccf70c
A
182 return(EINVAL);
183 }
184
185 /*
186 * XXX: we must need lots of work in this case,
187 * but we simply set the new value in this initial
188 * implementation.
189 */
190 scope6_ids[ifp->if_index].s6id_list[i] = idlist[i];
191 }
192 }
b0d623f7 193 lck_mtx_unlock(scope6_mutex);
9bccf70c
A
194
195 return(error);
196}
197
198int
91447636
A
199scope6_get(
200 struct ifnet *ifp,
201 u_int32_t *idlist)
9bccf70c
A
202{
203 if (scope6_ids == NULL) /* paranoid? */
204 return(EINVAL);
205
b0d623f7 206 lck_mtx_lock(scope6_mutex);
9bccf70c
A
207 bcopy(scope6_ids[ifp->if_index].s6id_list, idlist,
208 sizeof(scope6_ids[ifp->if_index].s6id_list));
b0d623f7 209 lck_mtx_unlock(scope6_mutex);
9bccf70c
A
210
211 return(0);
212}
213
214
215/*
216 * Get a scope of the address. Node-local, link-local, site-local or global.
217 */
218int
219in6_addrscope(addr)
220struct in6_addr *addr;
221{
222 int scope;
223
224 if (addr->s6_addr8[0] == 0xfe) {
225 scope = addr->s6_addr8[1] & 0xc0;
226
227 switch (scope) {
228 case 0x80:
229 return IPV6_ADDR_SCOPE_LINKLOCAL;
230 break;
231 case 0xc0:
232 return IPV6_ADDR_SCOPE_SITELOCAL;
233 break;
234 default:
235 return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
236 break;
237 }
238 }
239
240
241 if (addr->s6_addr8[0] == 0xff) {
242 scope = addr->s6_addr8[1] & 0x0f;
243
244 /*
245 * due to other scope such as reserved,
246 * return scope doesn't work.
247 */
248 switch (scope) {
6d2010ae
A
249 case IPV6_ADDR_SCOPE_INTFACELOCAL:
250 return IPV6_ADDR_SCOPE_INTFACELOCAL;
9bccf70c
A
251 break;
252 case IPV6_ADDR_SCOPE_LINKLOCAL:
253 return IPV6_ADDR_SCOPE_LINKLOCAL;
254 break;
255 case IPV6_ADDR_SCOPE_SITELOCAL:
256 return IPV6_ADDR_SCOPE_SITELOCAL;
257 break;
258 default:
259 return IPV6_ADDR_SCOPE_GLOBAL;
260 break;
261 }
262 }
263
6d2010ae
A
264 /*
265 * Regard loopback and unspecified addresses as global, since
266 * they have no ambiguity.
267 */
55e303ae 268 if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
9bccf70c 269 if (addr->s6_addr8[15] == 1) /* loopback */
9bccf70c 270 return IPV6_ADDR_SCOPE_LINKLOCAL;
6d2010ae
A
271 if (addr->s6_addr8[15] == 0) /* unspecified */
272 return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */
9bccf70c
A
273 }
274
275 return IPV6_ADDR_SCOPE_GLOBAL;
276}
277
278int
91447636
A
279in6_addr2scopeid(
280 struct ifnet *ifp, /* must not be NULL */
281 struct in6_addr *addr) /* must not be NULL */
9bccf70c
A
282{
283 int scope = in6_addrscope(addr);
91447636 284 int index = ifp->if_index;
b0d623f7 285 int retid = 0;
9bccf70c
A
286
287 if (scope6_ids == NULL) /* paranoid? */
288 return(0); /* XXX */
b0d623f7
A
289
290 lck_mtx_lock(scope6_mutex);
291 if (index >= if_scope_indexlim) {
292 lck_mtx_unlock(scope6_mutex);
9bccf70c 293 return(0); /* XXX */
b0d623f7 294 }
9bccf70c 295
91447636 296#define SID scope6_ids[index]
9bccf70c
A
297 switch(scope) {
298 case IPV6_ADDR_SCOPE_NODELOCAL:
b0d623f7
A
299 retid = -1; /* XXX: is this an appropriate value? */
300 break;
9bccf70c 301 case IPV6_ADDR_SCOPE_LINKLOCAL:
b0d623f7
A
302 retid=SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
303 break;
9bccf70c 304 case IPV6_ADDR_SCOPE_SITELOCAL:
b0d623f7
A
305 retid=SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
306 break;
9bccf70c 307 case IPV6_ADDR_SCOPE_ORGLOCAL:
b0d623f7
A
308 retid=SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
309 break;
9bccf70c 310 default:
b0d623f7 311 break; /* XXX: value 0, treat as global. */
9bccf70c
A
312 }
313#undef SID
b0d623f7
A
314
315 lck_mtx_unlock(scope6_mutex);
316 return retid;
9bccf70c
A
317}
318
6d2010ae
A
319/*
320 * Validate the specified scope zone ID in the sin6_scope_id field. If the ID
321 * is unspecified (=0), needs to be specified, and the default zone ID can be
322 * used, the default value will be used.
323 * This routine then generates the kernel-internal form: if the address scope
324 * of is interface-local or link-local, embed the interface index in the
325 * address.
326 */
327int
328sa6_embedscope(struct sockaddr_in6 *sin6, int defaultok)
329{
330 struct ifnet *ifp;
331 u_int32_t zoneid;
332
333 if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok)
334 zoneid = scope6_addr2default(&sin6->sin6_addr);
335
336 if (zoneid != 0 &&
337 (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
338 IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) {
339 /*
340 * At this moment, we only check interface-local and
341 * link-local scope IDs, and use interface indices as the
342 * zone IDs assuming a one-to-one mapping between interfaces
343 * and links.
344 */
345 if (if_index < zoneid)
346 return (ENXIO);
347 ifnet_head_lock_shared();
348 ifp = ifindex2ifnet[zoneid];
349 if (ifp == NULL) {/* XXX: this can happen for some OS */
350 ifnet_head_done();
351 return (ENXIO);
352 }
353 ifnet_head_done();
354 /* XXX assignment to 16bit from 32bit variable */
355 sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);
356
357 sin6->sin6_scope_id = 0;
358 }
359
360 return 0;
361}
362
363void
364rtkey_to_sa6(struct rtentry *rt, struct sockaddr_in6 *sin6)
365{
366 VERIFY(rt_key(rt)->sa_family == AF_INET6);
367
316670eb 368 *sin6 = *((struct sockaddr_in6 *)(void *)rt_key(rt));
6d2010ae
A
369 sin6->sin6_scope_id = 0;
370}
371
372void
373rtgw_to_sa6(struct rtentry *rt, struct sockaddr_in6 *sin6)
374{
375 VERIFY(rt->rt_flags & RTF_GATEWAY);
376
316670eb 377 *sin6 = *((struct sockaddr_in6 *)(void *)rt->rt_gateway);
6d2010ae
A
378 sin6->sin6_scope_id = 0;
379}
380
381/*
382 * generate standard sockaddr_in6 from embedded form.
383 */
384int
316670eb 385sa6_recoverscope(struct sockaddr_in6 *sin6, boolean_t attachcheck)
6d2010ae
A
386{
387 u_int32_t zoneid;
388
389 if (sin6->sin6_scope_id != 0) {
390 log(LOG_NOTICE,
391 "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
392 ip6_sprintf(&sin6->sin6_addr), sin6->sin6_scope_id);
393 /* XXX: proceed anyway... */
394 }
395 if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
396 IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
397 /*
398 * KAME assumption: link id == interface id
399 */
400 zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
401 if (zoneid) {
402 /* sanity check */
403 if (if_index < zoneid)
404 return (ENXIO);
316670eb
A
405 /*
406 * We use the attachcheck parameter to skip the
407 * interface attachment check.
408 * Some callers might hold the ifnet_head lock in
409 * exclusive mode. This means that:
410 * 1) the interface can't go away -- hence we don't
411 * need to perform this check
412 * 2) we can't perform this check because the lock is
413 * in exclusive mode and trying to lock it in shared
414 * mode would cause a deadlock.
415 */
416 if (attachcheck) {
417 ifnet_head_lock_shared();
418 if (ifindex2ifnet[zoneid] == NULL) {
419 ifnet_head_done();
420 return (ENXIO);
421 }
6d2010ae 422 ifnet_head_done();
6d2010ae 423 }
6d2010ae
A
424 sin6->sin6_addr.s6_addr16[1] = 0;
425 sin6->sin6_scope_id = zoneid;
426 }
427 }
428
429 return 0;
430}
431
9bccf70c 432void
91447636
A
433scope6_setdefault(
434 struct ifnet *ifp) /* note that this might be NULL */
9bccf70c
A
435{
436 /*
437 * Currently, this function just set the default "link" according to
438 * the given interface.
439 * We might eventually have to separate the notion of "link" from
440 * "interface" and provide a user interface to set the default.
441 */
b0d623f7 442 lck_mtx_lock(scope6_mutex);
9bccf70c 443 if (ifp) {
6d2010ae
A
444 scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] =
445 ifp->if_index;
9bccf70c
A
446 scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
447 ifp->if_index;
6d2010ae
A
448 } else {
449 scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0;
9bccf70c 450 scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
6d2010ae 451 }
b0d623f7 452 lck_mtx_unlock(scope6_mutex);
9bccf70c
A
453}
454
455int
91447636
A
456scope6_get_default(
457 u_int32_t *idlist)
9bccf70c
A
458{
459 if (scope6_ids == NULL) /* paranoid? */
460 return(EINVAL);
461
b0d623f7 462 lck_mtx_lock(scope6_mutex);
9bccf70c
A
463 bcopy(scope6_ids[0].s6id_list, idlist,
464 sizeof(scope6_ids[0].s6id_list));
b0d623f7 465 lck_mtx_unlock(scope6_mutex);
9bccf70c
A
466
467 return(0);
468}
469
470u_int32_t
91447636
A
471scope6_addr2default(
472 struct in6_addr *addr)
9bccf70c 473{
b0d623f7
A
474 u_int32_t id = 0;
475 int index = in6_addrscope(addr);
476 lck_mtx_lock(scope6_mutex);
477 id = scope6_ids[0].s6id_list[index];
478 lck_mtx_unlock(scope6_mutex);
479 return (id);
9bccf70c 480}
6d2010ae
A
481
482/*
483 * Determine the appropriate scope zone ID for in6 and ifp. If ret_id is
484 * non NULL, it is set to the zone ID. If the zone ID needs to be embedded
485 * in the in6_addr structure, in6 will be modified.
486 *
487 * ret_id - unnecessary?
488 */
489int
490in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id)
491{
492 int scope;
493 u_int32_t zoneid = 0;
494 int index = ifp->if_index;
495
496#ifdef DIAGNOSTIC
497 if (scope6_ids == NULL) { /* should not happen */
498 panic("in6_setscope: scope array is NULL");
499 /* NOTREACHED */
500 }
501#endif
502
503 /*
504 * special case: the loopback address can only belong to a loopback
505 * interface.
506 */
507 if (IN6_IS_ADDR_LOOPBACK(in6)) {
508 if (!(ifp->if_flags & IFF_LOOPBACK)) {
509 return (EINVAL);
510 } else {
511 if (ret_id != NULL)
512 *ret_id = 0; /* there's no ambiguity */
513 return (0);
514 }
515 }
516
517 scope = in6_addrscope(in6);
518
6d2010ae 519 lck_mtx_lock(scope6_mutex);
316670eb
A
520 if (index >= if_scope_indexlim) {
521 lck_mtx_unlock(scope6_mutex);
522 if (ret_id != NULL)
523 *ret_id = 0;
524 return (EINVAL);
525 }
526#define SID scope6_ids[index]
6d2010ae
A
527 switch (scope) {
528 case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
529 zoneid = SID.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
530 break;
531
532 case IPV6_ADDR_SCOPE_LINKLOCAL:
533 zoneid = SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
534 break;
535
536 case IPV6_ADDR_SCOPE_SITELOCAL:
537 zoneid = SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
538 break;
539
540 case IPV6_ADDR_SCOPE_ORGLOCAL:
541 zoneid = SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
542 break;
543#undef SID
544 default:
545 zoneid = 0; /* XXX: treat as global. */
546 break;
547 }
548 lck_mtx_unlock(scope6_mutex);
549
550 if (ret_id != NULL)
551 *ret_id = zoneid;
552
553 if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
554 in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */
555
556 return (0);
557}
558
559/*
560 * Just clear the embedded scope identifier. Return 0 if the original address
561 * is intact; return non 0 if the address is modified.
562 */
563int
564in6_clearscope(struct in6_addr *in6)
565{
566 int modified = 0;
567
568 if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
569 if (in6->s6_addr16[1] != 0)
570 modified = 1;
571 in6->s6_addr16[1] = 0;
572 }
573
574 return (modified);
575}
576