xref: /freebsd/sys/netinet6/scope6.c (revision aa79fe245de7616cda41b69a296a5ce209c95c45)
1 /*-
2  * Copyright (C) 2000 WIDE Project.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the project nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  *	$KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $
30  */
31 
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
35 #include <sys/param.h>
36 #include <sys/malloc.h>
37 #include <sys/mbuf.h>
38 #include <sys/socket.h>
39 #include <sys/systm.h>
40 #include <sys/queue.h>
41 #include <sys/syslog.h>
42 #include <sys/vimage.h>
43 
44 #include <net/route.h>
45 #include <net/if.h>
46 #include <net/vnet.h>
47 
48 #include <netinet/in.h>
49 
50 #include <netinet/ip6.h>
51 #include <netinet6/in6_var.h>
52 #include <netinet6/scope6_var.h>
53 #include <netinet6/vinet6.h>
54 
55 
56 /*
57  * The scope6_lock protects the global sid default stored in
58  * sid_default below.
59  */
60 static struct mtx scope6_lock;
61 #define	SCOPE6_LOCK_INIT()	mtx_init(&scope6_lock, "scope6_lock", NULL, MTX_DEF)
62 #define	SCOPE6_LOCK()		mtx_lock(&scope6_lock)
63 #define	SCOPE6_UNLOCK()		mtx_unlock(&scope6_lock)
64 #define	SCOPE6_LOCK_ASSERT()	mtx_assert(&scope6_lock, MA_OWNED)
65 
66 #ifdef VIMAGE_GLOBALS
67 static struct scope6_id sid_default;
68 int ip6_use_defzone;
69 #endif
70 
71 #define SID(ifp) \
72 	(((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->scope6_id)
73 
74 void
75 scope6_init(void)
76 {
77 	INIT_VNET_INET6(curvnet);
78 
79 #ifdef ENABLE_DEFAULT_SCOPE
80 	V_ip6_use_defzone = 1;
81 #else
82 	V_ip6_use_defzone = 0;
83 #endif
84 	bzero(&V_sid_default, sizeof(V_sid_default));
85 
86 	if (!IS_DEFAULT_VNET(curvnet))
87 		return;
88 
89 	SCOPE6_LOCK_INIT();
90 }
91 
92 struct scope6_id *
93 scope6_ifattach(struct ifnet *ifp)
94 {
95 	struct scope6_id *sid;
96 
97 	sid = (struct scope6_id *)malloc(sizeof(*sid), M_IFADDR, M_WAITOK);
98 	bzero(sid, sizeof(*sid));
99 
100 	/*
101 	 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
102 	 * Should we rather hardcode here?
103 	 */
104 	sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index;
105 	sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
106 #ifdef MULTI_SCOPE
107 	/* by default, we don't care about scope boundary for these scopes. */
108 	sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
109 	sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
110 #endif
111 
112 	return sid;
113 }
114 
115 void
116 scope6_ifdetach(struct scope6_id *sid)
117 {
118 
119 	free(sid, M_IFADDR);
120 }
121 
122 int
123 scope6_set(struct ifnet *ifp, struct scope6_id *idlist)
124 {
125 	INIT_VNET_NET(ifp->if_vnet);
126 	int i;
127 	int error = 0;
128 	struct scope6_id *sid = NULL;
129 
130 	IF_AFDATA_LOCK(ifp);
131 	sid = SID(ifp);
132 
133 	if (!sid) {	/* paranoid? */
134 		IF_AFDATA_UNLOCK(ifp);
135 		return (EINVAL);
136 	}
137 
138 	/*
139 	 * XXX: We need more consistency checks of the relationship among
140 	 * scopes (e.g. an organization should be larger than a site).
141 	 */
142 
143 	/*
144 	 * TODO(XXX): after setting, we should reflect the changes to
145 	 * interface addresses, routing table entries, PCB entries...
146 	 */
147 
148 	SCOPE6_LOCK();
149 	for (i = 0; i < 16; i++) {
150 		if (idlist->s6id_list[i] &&
151 		    idlist->s6id_list[i] != sid->s6id_list[i]) {
152 			/*
153 			 * An interface zone ID must be the corresponding
154 			 * interface index by definition.
155 			 */
156 			if (i == IPV6_ADDR_SCOPE_INTFACELOCAL &&
157 			    idlist->s6id_list[i] != ifp->if_index) {
158 				IF_AFDATA_UNLOCK(ifp);
159 				SCOPE6_UNLOCK();
160 				return (EINVAL);
161 			}
162 
163 			if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
164 			    idlist->s6id_list[i] > V_if_index) {
165 				/*
166 				 * XXX: theoretically, there should be no
167 				 * relationship between link IDs and interface
168 				 * IDs, but we check the consistency for
169 				 * safety in later use.
170 				 */
171 				IF_AFDATA_UNLOCK(ifp);
172 				SCOPE6_UNLOCK();
173 				return (EINVAL);
174 			}
175 
176 			/*
177 			 * XXX: we must need lots of work in this case,
178 			 * but we simply set the new value in this initial
179 			 * implementation.
180 			 */
181 			sid->s6id_list[i] = idlist->s6id_list[i];
182 		}
183 	}
184 	SCOPE6_UNLOCK();
185 	IF_AFDATA_UNLOCK(ifp);
186 
187 	return (error);
188 }
189 
190 int
191 scope6_get(struct ifnet *ifp, struct scope6_id *idlist)
192 {
193 	/* We only need to lock the interface's afdata for SID() to work. */
194 	IF_AFDATA_LOCK(ifp);
195 	struct scope6_id *sid = SID(ifp);
196 
197 	if (sid == NULL) {	/* paranoid? */
198 		IF_AFDATA_UNLOCK(ifp);
199 		return (EINVAL);
200 	}
201 
202 	SCOPE6_LOCK();
203 	*idlist = *sid;
204 	SCOPE6_UNLOCK();
205 
206 	IF_AFDATA_UNLOCK(ifp);
207 	return (0);
208 }
209 
210 
211 /*
212  * Get a scope of the address. Node-local, link-local, site-local or global.
213  */
214 int
215 in6_addrscope(struct in6_addr *addr)
216 {
217 	int scope;
218 
219 	if (addr->s6_addr[0] == 0xfe) {
220 		scope = addr->s6_addr[1] & 0xc0;
221 
222 		switch (scope) {
223 		case 0x80:
224 			return IPV6_ADDR_SCOPE_LINKLOCAL;
225 			break;
226 		case 0xc0:
227 			return IPV6_ADDR_SCOPE_SITELOCAL;
228 			break;
229 		default:
230 			return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
231 			break;
232 		}
233 	}
234 
235 
236 	if (addr->s6_addr[0] == 0xff) {
237 		scope = addr->s6_addr[1] & 0x0f;
238 
239 		/*
240 		 * due to other scope such as reserved,
241 		 * return scope doesn't work.
242 		 */
243 		switch (scope) {
244 		case IPV6_ADDR_SCOPE_INTFACELOCAL:
245 			return IPV6_ADDR_SCOPE_INTFACELOCAL;
246 			break;
247 		case IPV6_ADDR_SCOPE_LINKLOCAL:
248 			return IPV6_ADDR_SCOPE_LINKLOCAL;
249 			break;
250 		case IPV6_ADDR_SCOPE_SITELOCAL:
251 			return IPV6_ADDR_SCOPE_SITELOCAL;
252 			break;
253 		default:
254 			return IPV6_ADDR_SCOPE_GLOBAL;
255 			break;
256 		}
257 	}
258 
259 	/*
260 	 * Regard loopback and unspecified addresses as global, since
261 	 * they have no ambiguity.
262 	 */
263 	if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
264 		if (addr->s6_addr[15] == 1) /* loopback */
265 			return IPV6_ADDR_SCOPE_LINKLOCAL;
266 		if (addr->s6_addr[15] == 0) /* unspecified */
267 			return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */
268 	}
269 
270 	return IPV6_ADDR_SCOPE_GLOBAL;
271 }
272 
273 /*
274  * ifp - note that this might be NULL
275  */
276 
277 void
278 scope6_setdefault(struct ifnet *ifp)
279 {
280 	INIT_VNET_INET6(ifp->if_vnet);
281 
282 	/*
283 	 * Currently, this function just sets the default "interfaces"
284 	 * and "links" according to the given interface.
285 	 * We might eventually have to separate the notion of "link" from
286 	 * "interface" and provide a user interface to set the default.
287 	 */
288 	SCOPE6_LOCK();
289 	if (ifp) {
290 		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] =
291 			ifp->if_index;
292 		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
293 			ifp->if_index;
294 	} else {
295 		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0;
296 		V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
297 	}
298 	SCOPE6_UNLOCK();
299 }
300 
301 int
302 scope6_get_default(struct scope6_id *idlist)
303 {
304 	INIT_VNET_INET6(curvnet);
305 
306 	SCOPE6_LOCK();
307 	*idlist = V_sid_default;
308 	SCOPE6_UNLOCK();
309 
310 	return (0);
311 }
312 
313 u_int32_t
314 scope6_addr2default(struct in6_addr *addr)
315 {
316 	INIT_VNET_INET6(curvnet);
317 	u_int32_t id;
318 
319 	/*
320 	 * special case: The loopback address should be considered as
321 	 * link-local, but there's no ambiguity in the syntax.
322 	 */
323 	if (IN6_IS_ADDR_LOOPBACK(addr))
324 		return (0);
325 
326 	/*
327 	 * XXX: 32-bit read is atomic on all our platforms, is it OK
328 	 * not to lock here?
329 	 */
330 	SCOPE6_LOCK();
331 	id = V_sid_default.s6id_list[in6_addrscope(addr)];
332 	SCOPE6_UNLOCK();
333 	return (id);
334 }
335 
336 /*
337  * Validate the specified scope zone ID in the sin6_scope_id field.  If the ID
338  * is unspecified (=0), needs to be specified, and the default zone ID can be
339  * used, the default value will be used.
340  * This routine then generates the kernel-internal form: if the address scope
341  * of is interface-local or link-local, embed the interface index in the
342  * address.
343  */
344 int
345 sa6_embedscope(struct sockaddr_in6 *sin6, int defaultok)
346 {
347 	INIT_VNET_NET(curvnet);
348 	struct ifnet *ifp;
349 	u_int32_t zoneid;
350 
351 	if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok)
352 		zoneid = scope6_addr2default(&sin6->sin6_addr);
353 
354 	if (zoneid != 0 &&
355 	    (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
356 	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) {
357 		/*
358 		 * At this moment, we only check interface-local and
359 		 * link-local scope IDs, and use interface indices as the
360 		 * zone IDs assuming a one-to-one mapping between interfaces
361 		 * and links.
362 		 */
363 		if (V_if_index < zoneid)
364 			return (ENXIO);
365 		ifp = ifnet_byindex(zoneid);
366 		if (ifp == NULL) /* XXX: this can happen for some OS */
367 			return (ENXIO);
368 
369 		/* XXX assignment to 16bit from 32bit variable */
370 		sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);
371 
372 		sin6->sin6_scope_id = 0;
373 	}
374 
375 	return 0;
376 }
377 
378 /*
379  * generate standard sockaddr_in6 from embedded form.
380  */
381 int
382 sa6_recoverscope(struct sockaddr_in6 *sin6)
383 {
384 	INIT_VNET_NET(curvnet);
385 	char ip6buf[INET6_ADDRSTRLEN];
386 	u_int32_t zoneid;
387 
388 	if (sin6->sin6_scope_id != 0) {
389 		log(LOG_NOTICE,
390 		    "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
391 		    ip6_sprintf(ip6buf, &sin6->sin6_addr), sin6->sin6_scope_id);
392 		/* XXX: proceed anyway... */
393 	}
394 	if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
395 	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
396 		/*
397 		 * KAME assumption: link id == interface id
398 		 */
399 		zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
400 		if (zoneid) {
401 			/* sanity check */
402 			if (zoneid < 0 || V_if_index < zoneid)
403 				return (ENXIO);
404 			if (!ifnet_byindex(zoneid))
405 				return (ENXIO);
406 			sin6->sin6_addr.s6_addr16[1] = 0;
407 			sin6->sin6_scope_id = zoneid;
408 		}
409 	}
410 
411 	return 0;
412 }
413 
414 /*
415  * Determine the appropriate scope zone ID for in6 and ifp.  If ret_id is
416  * non NULL, it is set to the zone ID.  If the zone ID needs to be embedded
417  * in the in6_addr structure, in6 will be modified.
418  *
419  * ret_id - unnecessary?
420  */
421 int
422 in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id)
423 {
424 	int scope;
425 	u_int32_t zoneid = 0;
426 	struct scope6_id *sid;
427 
428 	IF_AFDATA_LOCK(ifp);
429 
430 	sid = SID(ifp);
431 
432 #ifdef DIAGNOSTIC
433 	if (sid == NULL) { /* should not happen */
434 		panic("in6_setscope: scope array is NULL");
435 		/* NOTREACHED */
436 	}
437 #endif
438 
439 	/*
440 	 * special case: the loopback address can only belong to a loopback
441 	 * interface.
442 	 */
443 	if (IN6_IS_ADDR_LOOPBACK(in6)) {
444 		if (!(ifp->if_flags & IFF_LOOPBACK)) {
445 			IF_AFDATA_UNLOCK(ifp);
446 			return (EINVAL);
447 		} else {
448 			if (ret_id != NULL)
449 				*ret_id = 0; /* there's no ambiguity */
450 			IF_AFDATA_UNLOCK(ifp);
451 			return (0);
452 		}
453 	}
454 
455 	scope = in6_addrscope(in6);
456 
457 	SCOPE6_LOCK();
458 	switch (scope) {
459 	case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
460 		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
461 		break;
462 
463 	case IPV6_ADDR_SCOPE_LINKLOCAL:
464 		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
465 		break;
466 
467 	case IPV6_ADDR_SCOPE_SITELOCAL:
468 		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
469 		break;
470 
471 	case IPV6_ADDR_SCOPE_ORGLOCAL:
472 		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
473 		break;
474 
475 	default:
476 		zoneid = 0;	/* XXX: treat as global. */
477 		break;
478 	}
479 	SCOPE6_UNLOCK();
480 	IF_AFDATA_UNLOCK(ifp);
481 
482 	if (ret_id != NULL)
483 		*ret_id = zoneid;
484 
485 	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
486 		in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */
487 
488 	return (0);
489 }
490 
491 /*
492  * Just clear the embedded scope identifier.  Return 0 if the original address
493  * is intact; return non 0 if the address is modified.
494  */
495 int
496 in6_clearscope(struct in6_addr *in6)
497 {
498 	int modified = 0;
499 
500 	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
501 		if (in6->s6_addr16[1] != 0)
502 			modified = 1;
503 		in6->s6_addr16[1] = 0;
504 	}
505 
506 	return (modified);
507 }
508