scope6.c revision 1.19 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