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