xref: /netbsd-src/sys/netinet6/scope6.c (revision fc4f42693f9b1c31f39f9cf50af1bf2010325808)
1 /*	$NetBSD: scope6.c,v 1.19 2018/02/01 16:36:01 maxv Exp $	*/
2 /*	$KAME$	*/
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/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: scope6.c,v 1.19 2018/02/01 16:36:01 maxv Exp $");
35 
36 #include <sys/param.h>
37 #include <sys/malloc.h>
38 #include <sys/mbuf.h>
39 #include <sys/socket.h>
40 #include <sys/systm.h>
41 #include <sys/queue.h>
42 #include <sys/syslog.h>
43 
44 #include <net/if.h>
45 #include <net/net_osdep.h>
46 
47 #include <netinet/in.h>
48 
49 #include <netinet6/in6_var.h>
50 #include <netinet6/scope6_var.h>
51 
52 #ifdef ENABLE_DEFAULT_SCOPE
53 int ip6_use_defzone = 1;
54 #else
55 int ip6_use_defzone = 0;
56 #endif
57 
58 static struct scope6_id sid_default;
59 #define SID(ifp) \
60     ((ifp)->if_afdata[AF_INET6] == NULL ? NULL : \
61 	((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->scope6_id)
62 
63 void
64 scope6_init(void)
65 {
66 
67 	memset(&sid_default, 0, sizeof(sid_default));
68 }
69 
70 struct scope6_id *
71 scope6_ifattach(struct ifnet *ifp)
72 {
73 	struct scope6_id *sid;
74 
75 	sid = malloc(sizeof(*sid), M_IFADDR, M_WAITOK | M_ZERO);
76 
77 	/*
78 	 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
79 	 * Should we rather hardcode here?
80 	 */
81 	sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index;
82 	sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
83 #ifdef MULTI_SCOPE
84 	/* by default, we don't care about scope boundary for these scopes. */
85 	sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
86 	sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
87 #endif
88 
89 	return sid;
90 }
91 
92 void
93 scope6_ifdetach(struct scope6_id *sid)
94 {
95 
96 	free(sid, M_IFADDR);
97 }
98 
99 int
100 scope6_set(struct ifnet *ifp, const struct scope6_id *idlist)
101 {
102 	int i;
103 	int error = 0;
104 	struct scope6_id *sid = SID(ifp);
105 
106 	if (!sid)	/* paranoid? */
107 		return EINVAL;
108 
109 	/*
110 	 * XXX: We need more consistency checks of the relationship among
111 	 * scopes (e.g. an organization should be larger than a site).
112 	 */
113 
114 	/*
115 	 * TODO(XXX): after setting, we should reflect the changes to
116 	 * interface addresses, routing table entries, PCB entries...
117 	 */
118 
119 	for (i = 0; i < 16; i++) {
120 		if (idlist->s6id_list[i] &&
121 		    idlist->s6id_list[i] != sid->s6id_list[i]) {
122 			int s;
123 			/*
124 			 * An interface zone ID must be the corresponding
125 			 * interface index by definition.
126 			 */
127 			if (i == IPV6_ADDR_SCOPE_INTFACELOCAL &&
128 			    idlist->s6id_list[i] != ifp->if_index)
129 				return EINVAL;
130 
131 			s = pserialize_read_enter();
132 			if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
133 			    !if_byindex(idlist->s6id_list[i])) {
134 				/*
135 				 * XXX: theoretically, there should be no
136 				 * relationship between link IDs and interface
137 				 * IDs, but we check the consistency for
138 				 * safety in later use.
139 				 */
140 				pserialize_read_exit(s);
141 				return EINVAL;
142 			}
143 			pserialize_read_exit(s);
144 
145 			/*
146 			 * XXX: we must need lots of work in this case,
147 			 * but we simply set the new value in this initial
148 			 * implementation.
149 			 */
150 			sid->s6id_list[i] = idlist->s6id_list[i];
151 		}
152 	}
153 
154 	return error;
155 }
156 
157 int
158 scope6_get(const struct ifnet *ifp, struct scope6_id *idlist)
159 {
160 	/* We only need to lock the interface's afdata for SID() to work. */
161 	const struct scope6_id *sid = SID(ifp);
162 
163 	if (sid == NULL)	/* paranoid? */
164 		return EINVAL;
165 
166 	*idlist = *sid;
167 
168 	return 0;
169 }
170 
171 /*
172  * Get a scope of the address. Interface-local, link-local, site-local
173  * or global.
174  */
175 int
176 in6_addrscope(const struct in6_addr *addr)
177 {
178 	int scope;
179 
180 	if (addr->s6_addr[0] == 0xfe) {
181 		scope = addr->s6_addr[1] & 0xc0;
182 
183 		switch (scope) {
184 		case 0x80:
185 			return IPV6_ADDR_SCOPE_LINKLOCAL;
186 		case 0xc0:
187 			return IPV6_ADDR_SCOPE_SITELOCAL;
188 		default:
189 			return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
190 		}
191 	}
192 
193 	if (addr->s6_addr[0] == 0xff) {
194 		scope = addr->s6_addr[1] & 0x0f;
195 
196 		/*
197 		 * due to other scope such as reserved,
198 		 * return scope doesn't work.
199 		 */
200 		switch (scope) {
201 		case IPV6_ADDR_SCOPE_INTFACELOCAL:
202 			return IPV6_ADDR_SCOPE_INTFACELOCAL;
203 		case IPV6_ADDR_SCOPE_LINKLOCAL:
204 			return IPV6_ADDR_SCOPE_LINKLOCAL;
205 		case IPV6_ADDR_SCOPE_SITELOCAL:
206 			return IPV6_ADDR_SCOPE_SITELOCAL;
207 		default:
208 			return IPV6_ADDR_SCOPE_GLOBAL;
209 		}
210 	}
211 
212 	if (memcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
213 		if (addr->s6_addr[15] == 1) /* loopback */
214 			return IPV6_ADDR_SCOPE_LINKLOCAL;
215 		if (addr->s6_addr[15] == 0) {
216 			/*
217 			 * Regard the unspecified addresses as global,
218 			 * since it has no ambiguity.
219 			 * XXX: not sure if it's correct...
220 			 */
221 			return IPV6_ADDR_SCOPE_GLOBAL;
222 		}
223 	}
224 
225 	return IPV6_ADDR_SCOPE_GLOBAL;
226 }
227 
228 /* note that ifp argument might be NULL */
229 void
230 scope6_setdefault(struct ifnet *ifp)
231 {
232 
233 	/*
234 	 * Currently, this function just sets the default "interfaces"
235 	 * and "links" according to the given interface.
236 	 * We might eventually have to separate the notion of "link" from
237 	 * "interface" and provide a user interface to set the default.
238 	 */
239 	if (ifp) {
240 		sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] =
241 			ifp->if_index;
242 		sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
243 			ifp->if_index;
244 	} else {
245 		sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0;
246 		sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
247 	}
248 }
249 
250 int
251 scope6_get_default(struct scope6_id *idlist)
252 {
253 
254 	*idlist = sid_default;
255 
256 	return 0;
257 }
258 
259 uint32_t
260 scope6_addr2default(const struct in6_addr *addr)
261 {
262 	uint32_t id;
263 
264 	/*
265 	 * special case: The loopback address should be considered as
266 	 * link-local, but there's no ambiguity in the syntax.
267 	 */
268 	if (IN6_IS_ADDR_LOOPBACK(addr))
269 		return 0;
270 
271 	/*
272 	 * XXX: 32-bit read is atomic on all our platforms, is it OK
273 	 * not to lock here?
274 	 */
275 	id = sid_default.s6id_list[in6_addrscope(addr)];
276 
277 	return id;
278 }
279 
280 /*
281  * Validate the specified scope zone ID in the sin6_scope_id field.  If the ID
282  * is unspecified (=0), needs to be specified, and the default zone ID can be
283  * used, the default value will be used.
284  * This routine then generates the kernel-internal form: if the address scope
285  * of is interface-local or link-local, embed the interface index in the
286  * address.
287  */
288 int
289 sa6_embedscope(struct sockaddr_in6 *sin6, int defaultok)
290 {
291 	struct ifnet *ifp;
292 	uint32_t zoneid;
293 
294 	if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok)
295 		zoneid = scope6_addr2default(&sin6->sin6_addr);
296 
297 	if (zoneid != 0 &&
298 	    (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
299 	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) {
300 		int s;
301 		/*
302 		 * At this moment, we only check interface-local and
303 		 * link-local scope IDs, and use interface indices as the
304 		 * zone IDs assuming a one-to-one mapping between interfaces
305 		 * and links.
306 		 */
307 		s = pserialize_read_enter();
308 		ifp = if_byindex(zoneid);
309 		if (ifp == NULL) {
310 			pserialize_read_exit(s);
311 			return ENXIO;
312 		}
313 		pserialize_read_exit(s);
314 
315 		/* XXX assignment to 16bit from 32bit variable */
316 		sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);
317 
318 		sin6->sin6_scope_id = 0;
319 	}
320 
321 	return 0;
322 }
323 
324 struct sockaddr *
325 sockaddr_in6_externalize(struct sockaddr *dst, socklen_t socklen,
326     const struct sockaddr *src)
327 {
328 	struct sockaddr_in6 *sin6;
329 
330 	sin6 = satosin6(sockaddr_copy(dst, socklen, src));
331 
332 	if (sin6 == NULL || sa6_recoverscope(sin6) != 0)
333 		return NULL;
334 
335 	return dst;
336 }
337 
338 /*
339  * generate standard sockaddr_in6 from embedded form.
340  */
341 int
342 sa6_recoverscope(struct sockaddr_in6 *sin6)
343 {
344 	uint32_t zoneid;
345 	char ip6buf[INET6_ADDRSTRLEN];
346 
347 	if (sin6->sin6_scope_id != 0) {
348 		log(LOG_NOTICE,
349 		    "%s: assumption failure (non 0 ID): %s%%%d\n", __func__,
350 		    IN6_PRINT(ip6buf, &sin6->sin6_addr), sin6->sin6_scope_id);
351 		/* XXX: proceed anyway... */
352 	}
353 	if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
354 	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
355 		/*
356 		 * KAME assumption: link id == interface id
357 		 */
358 		zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
359 		if (zoneid) {
360 			int s = pserialize_read_enter();
361 			if (!if_byindex(zoneid)) {
362 				pserialize_read_exit(s);
363 				return ENXIO;
364 			}
365 			pserialize_read_exit(s);
366 			sin6->sin6_addr.s6_addr16[1] = 0;
367 			sin6->sin6_scope_id = zoneid;
368 		}
369 	}
370 
371 	return 0;
372 }
373 
374 int
375 in6_setzoneid(struct in6_addr *in6, uint32_t zoneid)
376 {
377 	if (IN6_IS_SCOPE_EMBEDDABLE(in6))
378 		in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */
379 
380 	return 0;
381 }
382 
383 /*
384  * Determine the appropriate scope zone ID for in6 and ifp.  If ret_id is
385  * non NULL, it is set to the zone ID.  If the zone ID needs to be embedded
386  * in the in6_addr structure, in6 will be modified.
387  */
388 int
389 in6_setscope(struct in6_addr *in6, const struct ifnet *ifp, uint32_t *ret_id)
390 {
391 	int scope;
392 	uint32_t zoneid = 0;
393 	const struct scope6_id *sid = SID(ifp);
394 
395 	if (sid == NULL) {
396 		log(LOG_NOTICE, "%s: no scope id for %s\n", __func__,
397 		    if_name(ifp));
398 		return EINVAL;
399 	}
400 
401 	/*
402 	 * special case: the loopback address can only belong to a loopback
403 	 * interface.
404 	 */
405 	if (IN6_IS_ADDR_LOOPBACK(in6)) {
406 		if (!(ifp->if_flags & IFF_LOOPBACK)) {
407 			char ip6buf[INET6_ADDRSTRLEN];
408 			log(LOG_NOTICE, "%s: can't set scope for not loopback "
409 			    "interface %s and loopback address %s\n",
410 			    __func__, if_name(ifp), IN6_PRINT(ip6buf, in6));
411 			return EINVAL;
412 		} else {
413 			if (ret_id != NULL)
414 				*ret_id = 0; /* there's no ambiguity */
415 			return 0;
416 		}
417 	}
418 
419 	scope = in6_addrscope(in6);
420 
421 	switch (scope) {
422 	case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
423 		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
424 		break;
425 
426 	case IPV6_ADDR_SCOPE_LINKLOCAL:
427 		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
428 		break;
429 
430 	case IPV6_ADDR_SCOPE_SITELOCAL:
431 		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
432 		break;
433 
434 	case IPV6_ADDR_SCOPE_ORGLOCAL:
435 		zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
436 		break;
437 
438 	default:
439 		zoneid = 0;	/* XXX: treat as global. */
440 		break;
441 	}
442 
443 	if (ret_id != NULL)
444 		*ret_id = zoneid;
445 
446 	return in6_setzoneid(in6, zoneid);
447 }
448 
449 const char *
450 in6_getscopename(const struct in6_addr *addr)
451 {
452 	switch (in6_addrscope(addr)) {
453 	case IPV6_ADDR_SCOPE_INTFACELOCAL:
454 		return "interface";
455 #if IPV6_ADDR_SCOPE_INTFACELOCAL != IPV6_ADDR_SCOPE_NODELOCAL
456 	case IPV6_ADDR_SCOPE_NODELOCAL:
457 		return "node";
458 #endif
459 	case IPV6_ADDR_SCOPE_LINKLOCAL:
460 		return "link";
461 	case IPV6_ADDR_SCOPE_SITELOCAL:
462 		return "site";
463 	case IPV6_ADDR_SCOPE_ORGLOCAL:
464 		return "organization";
465 	case IPV6_ADDR_SCOPE_GLOBAL:
466 		return "global";
467 	default:
468 		return "unknown";
469 	}
470 }
471 
472 /*
473  * Just clear the embedded scope identifier.  Return 0 if the original address
474  * is intact; return non 0 if the address is modified.
475  */
476 int
477 in6_clearscope(struct in6_addr *in6)
478 {
479 	int modified = 0;
480 
481 	if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
482 		if (in6->s6_addr16[1] != 0)
483 			modified = 1;
484 		in6->s6_addr16[1] = 0;
485 	}
486 
487 	return modified;
488 }
489