]> git.saurik.com Git - apple/xnu.git/blame - bsd/netinet6/scope6.c
xnu-7195.60.75.tar.gz
[apple/xnu.git] / bsd / netinet6 / scope6.c
CommitLineData
6d2010ae 1/*
39236c6e 2 * Copyright (c) 2009-2013 Apple Inc. All rights reserved.
6d2010ae
A
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
39236c6e 5 *
6d2010ae
A
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.
39236c6e 14 *
6d2010ae
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
39236c6e 17 *
6d2010ae
A
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.
39236c6e 25 *
6d2010ae
A
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
9bccf70c
A
29/*
30 * Copyright (C) 2000 WIDE Project.
31 * All rights reserved.
39236c6e 32 *
9bccf70c
A
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
41 * 3. Neither the name of the project nor the names of its contributors
42 * may be used to endorse or promote products derived from this software
43 * without specific prior written permission.
39236c6e 44 *
9bccf70c
A
45 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * SUCH DAMAGE.
56 */
57
58#include <sys/param.h>
59#include <sys/malloc.h>
60#include <sys/mbuf.h>
61#include <sys/socket.h>
62#include <sys/systm.h>
63#include <sys/queue.h>
6d2010ae
A
64#include <sys/syslog.h>
65#include <sys/mcache.h>
9bccf70c
A
66
67#include <net/route.h>
68#include <net/if.h>
69
70#include <netinet/in.h>
71
72#include <netinet6/in6_var.h>
73#include <netinet6/scope6_var.h>
74
6d2010ae
A
75#ifdef ENABLE_DEFAULT_SCOPE
76int ip6_use_defzone = 1;
77#else
78int ip6_use_defzone = 0;
79#endif
80
39236c6e
A
81decl_lck_mtx_data(static, scope6_lock);
82static struct scope6_id sid_default;
9bccf70c 83
39236c6e 84#define SID(ifp) &IN6_IFEXTRA(ifp)->scope6_id
9bccf70c 85
39236c6e
A
86void
87scope6_init(lck_grp_t *grp, lck_attr_t *attr)
88{
89 bzero(&sid_default, sizeof(sid_default));
90 lck_mtx_init(&scope6_lock, grp, attr);
91}
9bccf70c 92
39236c6e
A
93void
94scope6_ifattach(struct ifnet *ifp)
95{
96 struct scope6_id *sid;
9bccf70c 97
39236c6e
A
98 VERIFY(IN6_IFEXTRA(ifp) != NULL);
99 if_inet6data_lock_exclusive(ifp);
100 sid = SID(ifp);
101 /* N.B.: the structure is already zero'ed */
9bccf70c
A
102 /*
103 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
104 * Should we rather hardcode here?
105 */
39236c6e
A
106 sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index;
107 sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
9bccf70c
A
108#if MULTI_SCOPE
109 /* by default, we don't care about scope boundary for these scopes. */
39236c6e
A
110 sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
111 sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
9bccf70c 112#endif
39236c6e 113 if_inet6data_lock_done(ifp);
9bccf70c
A
114}
115
9bccf70c
A
116/*
117 * Get a scope of the address. Node-local, link-local, site-local or global.
118 */
119int
39236c6e 120in6_addrscope(struct in6_addr *addr)
9bccf70c
A
121{
122 int scope;
123
124 if (addr->s6_addr8[0] == 0xfe) {
125 scope = addr->s6_addr8[1] & 0xc0;
126
127 switch (scope) {
128 case 0x80:
0a7de745 129 return IPV6_ADDR_SCOPE_LINKLOCAL;
9bccf70c 130 case 0xc0:
0a7de745 131 return IPV6_ADDR_SCOPE_SITELOCAL;
9bccf70c 132 default:
0a7de745 133 return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
9bccf70c
A
134 }
135 }
136
9bccf70c
A
137 if (addr->s6_addr8[0] == 0xff) {
138 scope = addr->s6_addr8[1] & 0x0f;
139
140 /*
141 * due to other scope such as reserved,
142 * return scope doesn't work.
143 */
144 switch (scope) {
6d2010ae 145 case IPV6_ADDR_SCOPE_INTFACELOCAL:
0a7de745 146 return IPV6_ADDR_SCOPE_INTFACELOCAL;
9bccf70c 147 case IPV6_ADDR_SCOPE_LINKLOCAL:
0a7de745 148 return IPV6_ADDR_SCOPE_LINKLOCAL;
9bccf70c 149 case IPV6_ADDR_SCOPE_SITELOCAL:
0a7de745 150 return IPV6_ADDR_SCOPE_SITELOCAL;
9bccf70c 151 default:
0a7de745 152 return IPV6_ADDR_SCOPE_GLOBAL;
9bccf70c
A
153 }
154 }
155
6d2010ae
A
156 /*
157 * Regard loopback and unspecified addresses as global, since
158 * they have no ambiguity.
159 */
0a7de745
A
160 if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
161 if (addr->s6_addr8[15] == 1) { /* loopback */
162 return IPV6_ADDR_SCOPE_LINKLOCAL;
163 }
164 if (addr->s6_addr8[15] == 0) { /* unspecified */
165 return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */
166 }
9bccf70c
A
167 }
168
0a7de745 169 return IPV6_ADDR_SCOPE_GLOBAL;
9bccf70c
A
170}
171
172int
39236c6e 173in6_addr2scopeid(struct ifnet *ifp, struct in6_addr *addr)
9bccf70c
A
174{
175 int scope = in6_addrscope(addr);
b0d623f7 176 int retid = 0;
39236c6e 177 struct scope6_id *sid;
9bccf70c 178
39236c6e 179 if_inet6data_lock_shared(ifp);
0a7de745 180 if (IN6_IFEXTRA(ifp) == NULL) {
39236c6e 181 goto err;
0a7de745 182 }
39236c6e
A
183 sid = SID(ifp);
184 switch (scope) {
9bccf70c 185 case IPV6_ADDR_SCOPE_NODELOCAL:
0a7de745 186 retid = -1; /* XXX: is this an appropriate value? */
b0d623f7 187 break;
9bccf70c 188 case IPV6_ADDR_SCOPE_LINKLOCAL:
39236c6e 189 retid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
b0d623f7 190 break;
9bccf70c 191 case IPV6_ADDR_SCOPE_SITELOCAL:
39236c6e 192 retid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
b0d623f7 193 break;
9bccf70c 194 case IPV6_ADDR_SCOPE_ORGLOCAL:
39236c6e 195 retid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
b0d623f7 196 break;
9bccf70c 197 default:
0a7de745 198 break; /* XXX: value 0, treat as global. */
9bccf70c 199 }
39236c6e
A
200err:
201 if_inet6data_lock_done(ifp);
b0d623f7 202
0a7de745 203 return retid;
9bccf70c
A
204}
205
6d2010ae
A
206/*
207 * Validate the specified scope zone ID in the sin6_scope_id field. If the ID
208 * is unspecified (=0), needs to be specified, and the default zone ID can be
209 * used, the default value will be used.
210 * This routine then generates the kernel-internal form: if the address scope
211 * of is interface-local or link-local, embed the interface index in the
212 * address.
213 */
214int
215sa6_embedscope(struct sockaddr_in6 *sin6, int defaultok)
216{
217 struct ifnet *ifp;
218 u_int32_t zoneid;
219
0a7de745 220 if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok) {
6d2010ae 221 zoneid = scope6_addr2default(&sin6->sin6_addr);
0a7de745 222 }
6d2010ae
A
223
224 if (zoneid != 0 &&
225 (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
226 IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) {
227 /*
228 * At this moment, we only check interface-local and
229 * link-local scope IDs, and use interface indices as the
230 * zone IDs assuming a one-to-one mapping between interfaces
231 * and links.
232 */
0a7de745
A
233 if (if_index < zoneid) {
234 return ENXIO;
235 }
6d2010ae
A
236 ifnet_head_lock_shared();
237 ifp = ifindex2ifnet[zoneid];
0a7de745 238 if (ifp == NULL) { /* XXX: this can happen for some OS */
6d2010ae 239 ifnet_head_done();
0a7de745 240 return ENXIO;
6d2010ae
A
241 }
242 ifnet_head_done();
243 /* XXX assignment to 16bit from 32bit variable */
244 sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);
245
246 sin6->sin6_scope_id = 0;
247 }
248
0a7de745 249 return 0;
6d2010ae
A
250}
251
252void
253rtkey_to_sa6(struct rtentry *rt, struct sockaddr_in6 *sin6)
254{
255 VERIFY(rt_key(rt)->sa_family == AF_INET6);
256
316670eb 257 *sin6 = *((struct sockaddr_in6 *)(void *)rt_key(rt));
6d2010ae
A
258 sin6->sin6_scope_id = 0;
259}
260
261void
262rtgw_to_sa6(struct rtentry *rt, struct sockaddr_in6 *sin6)
263{
264 VERIFY(rt->rt_flags & RTF_GATEWAY);
265
316670eb 266 *sin6 = *((struct sockaddr_in6 *)(void *)rt->rt_gateway);
6d2010ae
A
267 sin6->sin6_scope_id = 0;
268}
269
270/*
271 * generate standard sockaddr_in6 from embedded form.
272 */
273int
316670eb 274sa6_recoverscope(struct sockaddr_in6 *sin6, boolean_t attachcheck)
6d2010ae
A
275{
276 u_int32_t zoneid;
277
278 if (sin6->sin6_scope_id != 0) {
279 log(LOG_NOTICE,
280 "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
281 ip6_sprintf(&sin6->sin6_addr), sin6->sin6_scope_id);
282 /* XXX: proceed anyway... */
283 }
284 if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
285 IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
286 /*
287 * KAME assumption: link id == interface id
288 */
289 zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
290 if (zoneid) {
291 /* sanity check */
0a7de745
A
292 if (if_index < zoneid) {
293 return ENXIO;
294 }
316670eb
A
295 /*
296 * We use the attachcheck parameter to skip the
297 * interface attachment check.
298 * Some callers might hold the ifnet_head lock in
299 * exclusive mode. This means that:
300 * 1) the interface can't go away -- hence we don't
301 * need to perform this check
302 * 2) we can't perform this check because the lock is
303 * in exclusive mode and trying to lock it in shared
304 * mode would cause a deadlock.
305 */
306 if (attachcheck) {
307 ifnet_head_lock_shared();
308 if (ifindex2ifnet[zoneid] == NULL) {
309 ifnet_head_done();
0a7de745 310 return ENXIO;
316670eb 311 }
6d2010ae 312 ifnet_head_done();
6d2010ae 313 }
6d2010ae
A
314 sin6->sin6_addr.s6_addr16[1] = 0;
315 sin6->sin6_scope_id = zoneid;
316 }
317 }
318
0a7de745 319 return 0;
6d2010ae
A
320}
321
9bccf70c 322void
39236c6e 323scope6_setdefault(struct ifnet *ifp)
9bccf70c
A
324{
325 /*
326 * Currently, this function just set the default "link" according to
327 * 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 */
39236c6e
A
331 lck_mtx_lock(&scope6_lock);
332 if (ifp != NULL) {
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;
6d2010ae 337 } else {
39236c6e
A
338 sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0;
339 sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
6d2010ae 340 }
39236c6e 341 lck_mtx_unlock(&scope6_lock);
9bccf70c
A
342}
343
9bccf70c
A
344
345u_int32_t
39236c6e 346scope6_addr2default(struct in6_addr *addr)
9bccf70c 347{
b0d623f7
A
348 u_int32_t id = 0;
349 int index = in6_addrscope(addr);
39236c6e
A
350
351 /*
352 * special case: The loopback address should be considered as
353 * link-local, but there's no ambiguity in the syntax.
354 */
0a7de745
A
355 if (IN6_IS_ADDR_LOOPBACK(addr)) {
356 return 0;
357 }
39236c6e
A
358
359 lck_mtx_lock(&scope6_lock);
360 id = sid_default.s6id_list[index];
361 lck_mtx_unlock(&scope6_lock);
362
0a7de745 363 return id;
9bccf70c 364}
6d2010ae
A
365
366/*
367 * Determine the appropriate scope zone ID for in6 and ifp. If ret_id is
368 * non NULL, it is set to the zone ID. If the zone ID needs to be embedded
369 * in the in6_addr structure, in6 will be modified.
370 *
371 * ret_id - unnecessary?
372 */
373int
374in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id)
375{
376 int scope;
377 u_int32_t zoneid = 0;
39236c6e 378 struct scope6_id *sid;
6d2010ae
A
379
380 /*
381 * special case: the loopback address can only belong to a loopback
382 * interface.
383 */
384 if (IN6_IS_ADDR_LOOPBACK(in6)) {
385 if (!(ifp->if_flags & IFF_LOOPBACK)) {
0a7de745 386 return EINVAL;
6d2010ae 387 } else {
0a7de745 388 if (ret_id != NULL) {
6d2010ae 389 *ret_id = 0; /* there's no ambiguity */
0a7de745
A
390 }
391 return 0;
6d2010ae
A
392 }
393 }
394
395 scope = in6_addrscope(in6);
396
39236c6e
A
397 if_inet6data_lock_shared(ifp);
398 if (IN6_IFEXTRA(ifp) == NULL) {
399 if_inet6data_lock_done(ifp);
0a7de745 400 if (ret_id) {
316670eb 401 *ret_id = 0;
0a7de745
A
402 }
403 return EINVAL;
316670eb 404 }
39236c6e 405 sid = SID(ifp);
6d2010ae
A
406 switch (scope) {
407 case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
39236c6e 408 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
6d2010ae
A
409 break;
410
411 case IPV6_ADDR_SCOPE_LINKLOCAL:
39236c6e 412 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
6d2010ae
A
413 break;
414
415 case IPV6_ADDR_SCOPE_SITELOCAL:
39236c6e 416 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
6d2010ae
A
417 break;
418
419 case IPV6_ADDR_SCOPE_ORGLOCAL:
39236c6e 420 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
6d2010ae 421 break;
6d2010ae 422 default:
0a7de745 423 zoneid = 0; /* XXX: treat as global. */
6d2010ae
A
424 break;
425 }
39236c6e 426 if_inet6data_lock_done(ifp);
6d2010ae 427
0a7de745 428 if (ret_id != NULL) {
6d2010ae 429 *ret_id = zoneid;
0a7de745 430 }
6d2010ae 431
0a7de745 432 if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
6d2010ae 433 in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */
0a7de745
A
434 }
435 return 0;
6d2010ae
A
436}
437
438/*
439 * Just clear the embedded scope identifier. Return 0 if the original address
440 * is intact; return non 0 if the address is modified.
441 */
442int
443in6_clearscope(struct in6_addr *in6)
444{
445 int modified = 0;
446
447 if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
0a7de745 448 if (in6->s6_addr16[1] != 0) {
6d2010ae 449 modified = 1;
0a7de745 450 }
6d2010ae
A
451 in6->s6_addr16[1] = 0;
452 }
453
0a7de745 454 return modified;
6d2010ae 455}