1/*
2 * Copyright (c) 2009-2013 Apple Inc. All rights reserved.
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
29/*
30 * Copyright (C) 2000 WIDE Project.
31 * All rights reserved.
32 *
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.
44 *
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>
64#include <sys/syslog.h>
65#include <sys/mcache.h>
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
75#ifdef ENABLE_DEFAULT_SCOPE
76int ip6_use_defzone = 1;
77#else
78int ip6_use_defzone = 0;
79#endif
80
81decl_lck_mtx_data(static, scope6_lock);
82static struct scope6_id sid_default;
83
84#define SID(ifp) &IN6_IFEXTRA(ifp)->scope6_id
85
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}
92
93void
94scope6_ifattach(struct ifnet *ifp)
95{
96	struct scope6_id *sid;
97
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 */
102	/*
103	 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
104	 * Should we rather hardcode here?
105	 */
106	sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index;
107	sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
108#if MULTI_SCOPE
109	/* by default, we don't care about scope boundary for these scopes. */
110	sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
111	sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
112#endif
113	if_inet6data_lock_done(ifp);
114}
115
116/*
117 * Get a scope of the address. Node-local, link-local, site-local or global.
118 */
119int
120in6_addrscope(struct in6_addr *addr)
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:
129			return (IPV6_ADDR_SCOPE_LINKLOCAL);
130		case 0xc0:
131			return (IPV6_ADDR_SCOPE_SITELOCAL);
132		default:
133			return (IPV6_ADDR_SCOPE_GLOBAL); /* just in case */
134		}
135	}
136
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) {
145		case IPV6_ADDR_SCOPE_INTFACELOCAL:
146			return (IPV6_ADDR_SCOPE_INTFACELOCAL);
147		case IPV6_ADDR_SCOPE_LINKLOCAL:
148			return (IPV6_ADDR_SCOPE_LINKLOCAL);
149		case IPV6_ADDR_SCOPE_SITELOCAL:
150			return (IPV6_ADDR_SCOPE_SITELOCAL);
151		default:
152			return (IPV6_ADDR_SCOPE_GLOBAL);
153		}
154	}
155
156	/*
157	 * Regard loopback and unspecified addresses as global, since
158	 * they have no ambiguity.
159	 */
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		if (addr->s6_addr8[15] == 0) /* unspecified */
164			return (IPV6_ADDR_SCOPE_GLOBAL); /* XXX: correct? */
165	}
166
167	return (IPV6_ADDR_SCOPE_GLOBAL);
168}
169
170int
171in6_addr2scopeid(struct ifnet *ifp, struct in6_addr *addr)
172{
173	int scope = in6_addrscope(addr);
174	int retid = 0;
175	struct scope6_id *sid;
176
177	if_inet6data_lock_shared(ifp);
178	if (IN6_IFEXTRA(ifp) == NULL)
179		goto err;
180	sid = SID(ifp);
181	switch (scope) {
182	case IPV6_ADDR_SCOPE_NODELOCAL:
183		retid = -1;	/* XXX: is this an appropriate value? */
184		break;
185	case IPV6_ADDR_SCOPE_LINKLOCAL:
186		retid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
187		break;
188	case IPV6_ADDR_SCOPE_SITELOCAL:
189		retid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
190		break;
191	case IPV6_ADDR_SCOPE_ORGLOCAL:
192		retid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
193		break;
194	default:
195		break;	/* XXX: value 0, treat as global. */
196	}
197err:
198	if_inet6data_lock_done(ifp);
199
200	return (retid);
201}
202
203/*
204 * Validate the specified scope zone ID in the sin6_scope_id field.  If the ID
205 * is unspecified (=0), needs to be specified, and the default zone ID can be
206 * used, the default value will be used.
207 * This routine then generates the kernel-internal form: if the address scope
208 * of is interface-local or link-local, embed the interface index in the
209 * address.
210 */
211int
212sa6_embedscope(struct sockaddr_in6 *sin6, int defaultok)
213{
214	struct ifnet *ifp;
215	u_int32_t zoneid;
216
217	if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok)
218		zoneid = scope6_addr2default(&sin6->sin6_addr);
219
220	if (zoneid != 0 &&
221	    (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
222	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) {
223		/*
224		 * At this moment, we only check interface-local and
225		 * link-local scope IDs, and use interface indices as the
226		 * zone IDs assuming a one-to-one mapping between interfaces
227		 * and links.
228		 */
229		if (if_index < zoneid)
230			return (ENXIO);
231		ifnet_head_lock_shared();
232		ifp = ifindex2ifnet[zoneid];
233		if (ifp == NULL) {	/* XXX: this can happen for some OS */
234			ifnet_head_done();
235			return (ENXIO);
236		}
237		ifnet_head_done();
238		/* XXX assignment to 16bit from 32bit variable */
239		sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);
240
241		sin6->sin6_scope_id = 0;
242	}
243
244	return (0);
245}
246
247void
248rtkey_to_sa6(struct rtentry *rt, struct sockaddr_in6 *sin6)
249{
250	VERIFY(rt_key(rt)->sa_family == AF_INET6);
251
252	*sin6 = *((struct sockaddr_in6 *)(void *)rt_key(rt));
253	sin6->sin6_scope_id = 0;
254}
255
256void
257rtgw_to_sa6(struct rtentry *rt, struct sockaddr_in6 *sin6)
258{
259	VERIFY(rt->rt_flags & RTF_GATEWAY);
260
261	*sin6 = *((struct sockaddr_in6 *)(void *)rt->rt_gateway);
262	sin6->sin6_scope_id = 0;
263}
264
265/*
266 * generate standard sockaddr_in6 from embedded form.
267 */
268int
269sa6_recoverscope(struct sockaddr_in6 *sin6, boolean_t attachcheck)
270{
271	u_int32_t zoneid;
272
273	if (sin6->sin6_scope_id != 0) {
274		log(LOG_NOTICE,
275		    "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
276		    ip6_sprintf(&sin6->sin6_addr), sin6->sin6_scope_id);
277		/* XXX: proceed anyway... */
278	}
279	if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
280	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
281		/*
282		 * KAME assumption: link id == interface id
283		 */
284		zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
285		if (zoneid) {
286			/* sanity check */
287			if (if_index < zoneid)
288				return (ENXIO);
289			/*
290			 * We use the attachcheck parameter to skip the
291			 * interface attachment check.
292			 * Some callers might hold the ifnet_head lock in
293			 * exclusive mode. This means that:
294			 * 1) the interface can't go away -- hence we don't
295			 *    need to perform this check
296			 * 2) we can't perform this check because the lock is
297			 *    in exclusive mode and trying to lock it in shared
298			 *    mode would cause a deadlock.
299			 */
300			if (attachcheck) {
301				ifnet_head_lock_shared();
302				if (ifindex2ifnet[zoneid] == NULL) {
303					ifnet_head_done();
304					return (ENXIO);
305				}
306				ifnet_head_done();
307			}
308			sin6->sin6_addr.s6_addr16[1] = 0;
309			sin6->sin6_scope_id = zoneid;
310		}
311	}
312
313	return (0);
314}
315
316void
317scope6_setdefault(struct ifnet *ifp)
318{
319	/*
320	 * Currently, this function just set the default "link" according to
321	 * the given interface.
322	 * We might eventually have to separate the notion of "link" from
323	 * "interface" and provide a user interface to set the default.
324	 */
325	lck_mtx_lock(&scope6_lock);
326	if (ifp != NULL) {
327		sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] =
328		    ifp->if_index;
329		sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
330		    ifp->if_index;
331	} else {
332		sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0;
333		sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
334	}
335	lck_mtx_unlock(&scope6_lock);
336}
337
338
339u_int32_t
340scope6_addr2default(struct in6_addr *addr)
341{
342	u_int32_t id = 0;
343	int index = in6_addrscope(addr);
344
345	/*
346	 * special case: The loopback address should be considered as
347	 * link-local, but there's no ambiguity in the syntax.
348	 */
349	if (IN6_IS_ADDR_LOOPBACK(addr))
350		return (0);
351
352	lck_mtx_lock(&scope6_lock);
353	id = sid_default.s6id_list[index];
354	lck_mtx_unlock(&scope6_lock);
355
356	return (id);
357}
358
359/*
360 * Determine the appropriate scope zone ID for in6 and ifp.  If ret_id is
361 * non NULL, it is set to the zone ID.  If the zone ID needs to be embedded
362 * in the in6_addr structure, in6 will be modified.
363 *
364 * ret_id - unnecessary?
365 */
366int
367in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id)
368{
369	int scope;
370	u_int32_t zoneid = 0;
371	struct scope6_id *sid;
372
373	/*
374	 * special case: the loopback address can only belong to a loopback
375	 * interface.
376	 */
377	if (IN6_IS_ADDR_LOOPBACK(in6)) {
378		if (!(ifp->if_flags & IFF_LOOPBACK)) {
379			return (EINVAL);
380		} else {
381			if (ret_id != NULL)
382				*ret_id = 0; /* there's no ambiguity */
383			return (0);
384		}
385	}
386
387	scope = in6_addrscope(in6);
388
389	if_inet6data_lock_shared(ifp);
390	if (IN6_IFEXTRA(ifp) == NULL) {
391		if_inet6data_lock_done(ifp);
392		if (ret_id)
393			*ret_id = 0;
394		return (EINVAL);
395	}
396	sid = SID(ifp);
397	switch (scope) {
398	case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
399		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
400		break;
401
402	case IPV6_ADDR_SCOPE_LINKLOCAL:
403		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
404		break;
405
406	case IPV6_ADDR_SCOPE_SITELOCAL:
407		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
408		break;
409
410	case IPV6_ADDR_SCOPE_ORGLOCAL:
411		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
412		break;
413	default:
414		zoneid = 0;	/* XXX: treat as global. */
415		break;
416	}
417	if_inet6data_lock_done(ifp);
418
419	if (ret_id != NULL)
420		*ret_id = zoneid;
421
422	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
423		in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */
424
425	return (0);
426}
427
428/*
429 * Just clear the embedded scope identifier.  Return 0 if the original address
430 * is intact; return non 0 if the address is modified.
431 */
432int
433in6_clearscope(struct in6_addr *in6)
434{
435	int modified = 0;
436
437	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
438		if (in6->s6_addr16[1] != 0)
439			modified = 1;
440		in6->s6_addr16[1] = 0;
441	}
442
443	return (modified);
444}
445