1*3ec4b526Smaxv /* $NetBSD: scope6.c,v 1.23 2020/06/16 17:12:18 maxv Exp $ */
278678b13Srpaulo /* $KAME$ */
378678b13Srpaulo
4c4a55989Smaxv /*
578678b13Srpaulo * Copyright (C) 2000 WIDE Project.
678678b13Srpaulo * All rights reserved.
778678b13Srpaulo *
878678b13Srpaulo * Redistribution and use in source and binary forms, with or without
978678b13Srpaulo * modification, are permitted provided that the following conditions
1078678b13Srpaulo * are met:
1178678b13Srpaulo * 1. Redistributions of source code must retain the above copyright
1278678b13Srpaulo * notice, this list of conditions and the following disclaimer.
1378678b13Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
1478678b13Srpaulo * notice, this list of conditions and the following disclaimer in the
1578678b13Srpaulo * documentation and/or other materials provided with the distribution.
1678678b13Srpaulo * 3. Neither the name of the project nor the names of its contributors
1778678b13Srpaulo * may be used to endorse or promote products derived from this software
1878678b13Srpaulo * without specific prior written permission.
1978678b13Srpaulo *
2078678b13Srpaulo * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2178678b13Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2278678b13Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2378678b13Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2478678b13Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2578678b13Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2678678b13Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2778678b13Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2878678b13Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2978678b13Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3078678b13Srpaulo * SUCH DAMAGE.
3178678b13Srpaulo */
3278678b13Srpaulo
33456279dfSlukem #include <sys/cdefs.h>
34*3ec4b526Smaxv __KERNEL_RCSID(0, "$NetBSD: scope6.c,v 1.23 2020/06/16 17:12:18 maxv Exp $");
35456279dfSlukem
3678678b13Srpaulo #include <sys/param.h>
3778678b13Srpaulo #include <sys/malloc.h>
3878678b13Srpaulo #include <sys/mbuf.h>
3978678b13Srpaulo #include <sys/socket.h>
4078678b13Srpaulo #include <sys/systm.h>
4178678b13Srpaulo #include <sys/queue.h>
4278678b13Srpaulo #include <sys/syslog.h>
4378678b13Srpaulo
4478678b13Srpaulo #include <net/if.h>
4578678b13Srpaulo
4678678b13Srpaulo #include <netinet/in.h>
4778678b13Srpaulo
4878678b13Srpaulo #include <netinet6/in6_var.h>
4978678b13Srpaulo #include <netinet6/scope6_var.h>
5078678b13Srpaulo
5178678b13Srpaulo #ifdef ENABLE_DEFAULT_SCOPE
5278678b13Srpaulo int ip6_use_defzone = 1;
5378678b13Srpaulo #else
5478678b13Srpaulo int ip6_use_defzone = 0;
5578678b13Srpaulo #endif
5678678b13Srpaulo
5778678b13Srpaulo static struct scope6_id sid_default;
5878678b13Srpaulo #define SID(ifp) \
59fa02ef2cSchristos ((ifp)->if_afdata[AF_INET6] == NULL ? NULL : \
60fa02ef2cSchristos ((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->scope6_id)
6178678b13Srpaulo
6278678b13Srpaulo void
scope6_init(void)63dc56dbbdScegger scope6_init(void)
6478678b13Srpaulo {
6578678b13Srpaulo
660131d257Srpaulo memset(&sid_default, 0, sizeof(sid_default));
6778678b13Srpaulo }
6878678b13Srpaulo
6978678b13Srpaulo struct scope6_id *
scope6_ifattach(struct ifnet * ifp)7078678b13Srpaulo scope6_ifattach(struct ifnet *ifp)
7178678b13Srpaulo {
7278678b13Srpaulo struct scope6_id *sid;
7378678b13Srpaulo
74c4a55989Smaxv sid = malloc(sizeof(*sid), M_IFADDR, M_WAITOK | M_ZERO);
7578678b13Srpaulo
7678678b13Srpaulo /*
7778678b13Srpaulo * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
7878678b13Srpaulo * Should we rather hardcode here?
7978678b13Srpaulo */
8078678b13Srpaulo sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index;
8178678b13Srpaulo sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
8278678b13Srpaulo #ifdef MULTI_SCOPE
8378678b13Srpaulo /* by default, we don't care about scope boundary for these scopes. */
8478678b13Srpaulo sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
8578678b13Srpaulo sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
8678678b13Srpaulo #endif
8778678b13Srpaulo
8878678b13Srpaulo return sid;
8978678b13Srpaulo }
9078678b13Srpaulo
9178678b13Srpaulo void
scope6_ifdetach(struct scope6_id * sid)9278678b13Srpaulo scope6_ifdetach(struct scope6_id *sid)
9378678b13Srpaulo {
9478678b13Srpaulo
9578678b13Srpaulo free(sid, M_IFADDR);
9678678b13Srpaulo }
9778678b13Srpaulo
9878678b13Srpaulo /*
9978678b13Srpaulo * Get a scope of the address. Interface-local, link-local, site-local
10078678b13Srpaulo * or global.
10178678b13Srpaulo */
10278678b13Srpaulo int
in6_addrscope(const struct in6_addr * addr)103d072fd0fSdyoung in6_addrscope(const struct in6_addr *addr)
10478678b13Srpaulo {
10578678b13Srpaulo int scope;
10678678b13Srpaulo
10778678b13Srpaulo if (addr->s6_addr[0] == 0xfe) {
10878678b13Srpaulo scope = addr->s6_addr[1] & 0xc0;
10978678b13Srpaulo
11078678b13Srpaulo switch (scope) {
11178678b13Srpaulo case 0x80:
11278678b13Srpaulo return IPV6_ADDR_SCOPE_LINKLOCAL;
11378678b13Srpaulo case 0xc0:
11478678b13Srpaulo return IPV6_ADDR_SCOPE_SITELOCAL;
11578678b13Srpaulo default:
11678678b13Srpaulo return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
11778678b13Srpaulo }
11878678b13Srpaulo }
11978678b13Srpaulo
12078678b13Srpaulo if (addr->s6_addr[0] == 0xff) {
12178678b13Srpaulo scope = addr->s6_addr[1] & 0x0f;
12278678b13Srpaulo
12378678b13Srpaulo /*
12478678b13Srpaulo * due to other scope such as reserved,
12578678b13Srpaulo * return scope doesn't work.
12678678b13Srpaulo */
12778678b13Srpaulo switch (scope) {
12878678b13Srpaulo case IPV6_ADDR_SCOPE_INTFACELOCAL:
12978678b13Srpaulo return IPV6_ADDR_SCOPE_INTFACELOCAL;
13078678b13Srpaulo case IPV6_ADDR_SCOPE_LINKLOCAL:
13178678b13Srpaulo return IPV6_ADDR_SCOPE_LINKLOCAL;
13278678b13Srpaulo case IPV6_ADDR_SCOPE_SITELOCAL:
13378678b13Srpaulo return IPV6_ADDR_SCOPE_SITELOCAL;
13478678b13Srpaulo default:
13578678b13Srpaulo return IPV6_ADDR_SCOPE_GLOBAL;
13678678b13Srpaulo }
13778678b13Srpaulo }
13878678b13Srpaulo
139d072fd0fSdyoung if (memcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
14078678b13Srpaulo if (addr->s6_addr[15] == 1) /* loopback */
14178678b13Srpaulo return IPV6_ADDR_SCOPE_LINKLOCAL;
14278678b13Srpaulo if (addr->s6_addr[15] == 0) {
14378678b13Srpaulo /*
14478678b13Srpaulo * Regard the unspecified addresses as global,
14578678b13Srpaulo * since it has no ambiguity.
14678678b13Srpaulo * XXX: not sure if it's correct...
14778678b13Srpaulo */
14878678b13Srpaulo return IPV6_ADDR_SCOPE_GLOBAL;
14978678b13Srpaulo }
15078678b13Srpaulo }
15178678b13Srpaulo
15278678b13Srpaulo return IPV6_ADDR_SCOPE_GLOBAL;
15378678b13Srpaulo }
15478678b13Srpaulo
15578678b13Srpaulo uint32_t
scope6_addr2default(const struct in6_addr * addr)156d072fd0fSdyoung scope6_addr2default(const struct in6_addr *addr)
15778678b13Srpaulo {
15878678b13Srpaulo uint32_t id;
15978678b13Srpaulo
16078678b13Srpaulo /*
16178678b13Srpaulo * special case: The loopback address should be considered as
16278678b13Srpaulo * link-local, but there's no ambiguity in the syntax.
16378678b13Srpaulo */
16478678b13Srpaulo if (IN6_IS_ADDR_LOOPBACK(addr))
165c4a55989Smaxv return 0;
16678678b13Srpaulo
16778678b13Srpaulo /*
16878678b13Srpaulo * XXX: 32-bit read is atomic on all our platforms, is it OK
16978678b13Srpaulo * not to lock here?
17078678b13Srpaulo */
17178678b13Srpaulo id = sid_default.s6id_list[in6_addrscope(addr)];
17278678b13Srpaulo
173c4a55989Smaxv return id;
17478678b13Srpaulo }
17578678b13Srpaulo
17678678b13Srpaulo /*
17778678b13Srpaulo * Validate the specified scope zone ID in the sin6_scope_id field. If the ID
17878678b13Srpaulo * is unspecified (=0), needs to be specified, and the default zone ID can be
17978678b13Srpaulo * used, the default value will be used.
18078678b13Srpaulo * This routine then generates the kernel-internal form: if the address scope
18178678b13Srpaulo * of is interface-local or link-local, embed the interface index in the
18278678b13Srpaulo * address.
18378678b13Srpaulo */
18478678b13Srpaulo int
sa6_embedscope(struct sockaddr_in6 * sin6,int defaultok)18578678b13Srpaulo sa6_embedscope(struct sockaddr_in6 *sin6, int defaultok)
18678678b13Srpaulo {
18778678b13Srpaulo struct ifnet *ifp;
18878678b13Srpaulo uint32_t zoneid;
18978678b13Srpaulo
19078678b13Srpaulo if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok)
19178678b13Srpaulo zoneid = scope6_addr2default(&sin6->sin6_addr);
19278678b13Srpaulo
19378678b13Srpaulo if (zoneid != 0 &&
19478678b13Srpaulo (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
19578678b13Srpaulo IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) {
196c7e18ccbSozaki-r int s;
19778678b13Srpaulo /*
19878678b13Srpaulo * At this moment, we only check interface-local and
19978678b13Srpaulo * link-local scope IDs, and use interface indices as the
20078678b13Srpaulo * zone IDs assuming a one-to-one mapping between interfaces
20178678b13Srpaulo * and links.
20278678b13Srpaulo */
203c7e18ccbSozaki-r s = pserialize_read_enter();
204bc9504c9Srmind ifp = if_byindex(zoneid);
205c7e18ccbSozaki-r if (ifp == NULL) {
206c7e18ccbSozaki-r pserialize_read_exit(s);
207c4a55989Smaxv return ENXIO;
208c7e18ccbSozaki-r }
209c7e18ccbSozaki-r pserialize_read_exit(s);
21078678b13Srpaulo
21178678b13Srpaulo /* XXX assignment to 16bit from 32bit variable */
21278678b13Srpaulo sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);
21378678b13Srpaulo
21478678b13Srpaulo sin6->sin6_scope_id = 0;
21578678b13Srpaulo }
21678678b13Srpaulo
21778678b13Srpaulo return 0;
21878678b13Srpaulo }
21978678b13Srpaulo
220c5d5f769Sdyoung struct sockaddr *
sockaddr_in6_externalize(struct sockaddr * dst,socklen_t socklen,const struct sockaddr * src)221c5d5f769Sdyoung sockaddr_in6_externalize(struct sockaddr *dst, socklen_t socklen,
222c5d5f769Sdyoung const struct sockaddr *src)
223c5d5f769Sdyoung {
224c5d5f769Sdyoung struct sockaddr_in6 *sin6;
225c5d5f769Sdyoung
226c5d5f769Sdyoung sin6 = satosin6(sockaddr_copy(dst, socklen, src));
227c5d5f769Sdyoung
228c5d5f769Sdyoung if (sin6 == NULL || sa6_recoverscope(sin6) != 0)
229c5d5f769Sdyoung return NULL;
230c5d5f769Sdyoung
231c5d5f769Sdyoung return dst;
232c5d5f769Sdyoung }
233c5d5f769Sdyoung
23478678b13Srpaulo /*
23578678b13Srpaulo * generate standard sockaddr_in6 from embedded form.
23678678b13Srpaulo */
23778678b13Srpaulo int
sa6_recoverscope(struct sockaddr_in6 * sin6)23878678b13Srpaulo sa6_recoverscope(struct sockaddr_in6 *sin6)
23978678b13Srpaulo {
24078678b13Srpaulo uint32_t zoneid;
24128f4c24cSryo char ip6buf[INET6_ADDRSTRLEN];
24278678b13Srpaulo
24378678b13Srpaulo if (sin6->sin6_scope_id != 0) {
24478678b13Srpaulo log(LOG_NOTICE,
245da66cce8Schristos "%s: assumption failure (non 0 ID): %s%%%d\n", __func__,
24635561f6bSchristos IN6_PRINT(ip6buf, &sin6->sin6_addr), sin6->sin6_scope_id);
24778678b13Srpaulo /* XXX: proceed anyway... */
24878678b13Srpaulo }
24978678b13Srpaulo if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
25078678b13Srpaulo IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
25178678b13Srpaulo /*
25278678b13Srpaulo * KAME assumption: link id == interface id
25378678b13Srpaulo */
25478678b13Srpaulo zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
25578678b13Srpaulo if (zoneid) {
256c7e18ccbSozaki-r int s = pserialize_read_enter();
257c7e18ccbSozaki-r if (!if_byindex(zoneid)) {
258c7e18ccbSozaki-r pserialize_read_exit(s);
259c4a55989Smaxv return ENXIO;
260c7e18ccbSozaki-r }
261c7e18ccbSozaki-r pserialize_read_exit(s);
26278678b13Srpaulo sin6->sin6_addr.s6_addr16[1] = 0;
26378678b13Srpaulo sin6->sin6_scope_id = zoneid;
26478678b13Srpaulo }
26578678b13Srpaulo }
26678678b13Srpaulo
26778678b13Srpaulo return 0;
26878678b13Srpaulo }
26978678b13Srpaulo
270d072fd0fSdyoung int
in6_setzoneid(struct in6_addr * in6,uint32_t zoneid)271d072fd0fSdyoung in6_setzoneid(struct in6_addr *in6, uint32_t zoneid)
272d072fd0fSdyoung {
273d072fd0fSdyoung if (IN6_IS_SCOPE_EMBEDDABLE(in6))
274d072fd0fSdyoung in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */
275d072fd0fSdyoung
276d072fd0fSdyoung return 0;
277d072fd0fSdyoung }
278d072fd0fSdyoung
27978678b13Srpaulo /*
28078678b13Srpaulo * Determine the appropriate scope zone ID for in6 and ifp. If ret_id is
28178678b13Srpaulo * non NULL, it is set to the zone ID. If the zone ID needs to be embedded
28278678b13Srpaulo * in the in6_addr structure, in6 will be modified.
28378678b13Srpaulo */
28478678b13Srpaulo int
in6_setscope(struct in6_addr * in6,const struct ifnet * ifp,uint32_t * ret_id)285d072fd0fSdyoung in6_setscope(struct in6_addr *in6, const struct ifnet *ifp, uint32_t *ret_id)
28678678b13Srpaulo {
28778678b13Srpaulo int scope;
28878678b13Srpaulo uint32_t zoneid = 0;
289d072fd0fSdyoung const struct scope6_id *sid = SID(ifp);
29078678b13Srpaulo
291da66cce8Schristos if (sid == NULL) {
292da66cce8Schristos log(LOG_NOTICE, "%s: no scope id for %s\n", __func__,
293da66cce8Schristos if_name(ifp));
294fa02ef2cSchristos return EINVAL;
295da66cce8Schristos }
29678678b13Srpaulo
29778678b13Srpaulo /*
29878678b13Srpaulo * special case: the loopback address can only belong to a loopback
29978678b13Srpaulo * interface.
30078678b13Srpaulo */
30178678b13Srpaulo if (IN6_IS_ADDR_LOOPBACK(in6)) {
302da66cce8Schristos if (!(ifp->if_flags & IFF_LOOPBACK)) {
303da66cce8Schristos char ip6buf[INET6_ADDRSTRLEN];
304da66cce8Schristos log(LOG_NOTICE, "%s: can't set scope for not loopback "
305da66cce8Schristos "interface %s and loopback address %s\n",
306da66cce8Schristos __func__, if_name(ifp), IN6_PRINT(ip6buf, in6));
307da66cce8Schristos return EINVAL;
308da66cce8Schristos } else {
30978678b13Srpaulo if (ret_id != NULL)
31078678b13Srpaulo *ret_id = 0; /* there's no ambiguity */
311da66cce8Schristos return 0;
31278678b13Srpaulo }
31378678b13Srpaulo }
31478678b13Srpaulo
31578678b13Srpaulo scope = in6_addrscope(in6);
31678678b13Srpaulo
31778678b13Srpaulo switch (scope) {
31878678b13Srpaulo case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
31978678b13Srpaulo zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
32078678b13Srpaulo break;
32178678b13Srpaulo
32278678b13Srpaulo case IPV6_ADDR_SCOPE_LINKLOCAL:
32378678b13Srpaulo zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
32478678b13Srpaulo break;
32578678b13Srpaulo
32678678b13Srpaulo case IPV6_ADDR_SCOPE_SITELOCAL:
32778678b13Srpaulo zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
32878678b13Srpaulo break;
32978678b13Srpaulo
33078678b13Srpaulo case IPV6_ADDR_SCOPE_ORGLOCAL:
33178678b13Srpaulo zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
33278678b13Srpaulo break;
33378678b13Srpaulo
33478678b13Srpaulo default:
33578678b13Srpaulo zoneid = 0; /* XXX: treat as global. */
33678678b13Srpaulo break;
33778678b13Srpaulo }
33878678b13Srpaulo
33978678b13Srpaulo if (ret_id != NULL)
34078678b13Srpaulo *ret_id = zoneid;
34178678b13Srpaulo
342d072fd0fSdyoung return in6_setzoneid(in6, zoneid);
34378678b13Srpaulo }
34478678b13Srpaulo
345d1456cccSchristos const char *
in6_getscopename(const struct in6_addr * addr)346d1456cccSchristos in6_getscopename(const struct in6_addr *addr)
347d1456cccSchristos {
348d1456cccSchristos switch (in6_addrscope(addr)) {
349c4a55989Smaxv case IPV6_ADDR_SCOPE_INTFACELOCAL:
350c4a55989Smaxv return "interface";
351d1456cccSchristos #if IPV6_ADDR_SCOPE_INTFACELOCAL != IPV6_ADDR_SCOPE_NODELOCAL
352c4a55989Smaxv case IPV6_ADDR_SCOPE_NODELOCAL:
353c4a55989Smaxv return "node";
354d1456cccSchristos #endif
355c4a55989Smaxv case IPV6_ADDR_SCOPE_LINKLOCAL:
356c4a55989Smaxv return "link";
357c4a55989Smaxv case IPV6_ADDR_SCOPE_SITELOCAL:
358c4a55989Smaxv return "site";
359c4a55989Smaxv case IPV6_ADDR_SCOPE_ORGLOCAL:
360c4a55989Smaxv return "organization";
361c4a55989Smaxv case IPV6_ADDR_SCOPE_GLOBAL:
362c4a55989Smaxv return "global";
363c4a55989Smaxv default:
364c4a55989Smaxv return "unknown";
365d1456cccSchristos }
366d1456cccSchristos }
367d1456cccSchristos
36878678b13Srpaulo /*
36978678b13Srpaulo * Just clear the embedded scope identifier. Return 0 if the original address
37078678b13Srpaulo * is intact; return non 0 if the address is modified.
37178678b13Srpaulo */
37278678b13Srpaulo int
in6_clearscope(struct in6_addr * in6)37378678b13Srpaulo in6_clearscope(struct in6_addr *in6)
37478678b13Srpaulo {
37578678b13Srpaulo int modified = 0;
37678678b13Srpaulo
37778678b13Srpaulo if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
37878678b13Srpaulo if (in6->s6_addr16[1] != 0)
37978678b13Srpaulo modified = 1;
38078678b13Srpaulo in6->s6_addr16[1] = 0;
38178678b13Srpaulo }
38278678b13Srpaulo
383c4a55989Smaxv return modified;
38478678b13Srpaulo }
385