1139826Simp/*- 262587Sitojun * Copyright (C) 2000 WIDE Project. 362587Sitojun * All rights reserved. 4120941Sume * 562587Sitojun * Redistribution and use in source and binary forms, with or without 662587Sitojun * modification, are permitted provided that the following conditions 762587Sitojun * are met: 862587Sitojun * 1. Redistributions of source code must retain the above copyright 962587Sitojun * notice, this list of conditions and the following disclaimer. 1062587Sitojun * 2. Redistributions in binary form must reproduce the above copyright 1162587Sitojun * notice, this list of conditions and the following disclaimer in the 1262587Sitojun * documentation and/or other materials provided with the distribution. 1362587Sitojun * 3. Neither the name of the project nor the names of its contributors 1462587Sitojun * may be used to endorse or promote products derived from this software 1562587Sitojun * without specific prior written permission. 16120941Sume * 1762587Sitojun * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 1862587Sitojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1962587Sitojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2062587Sitojun * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2162587Sitojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2262587Sitojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2362587Sitojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2462587Sitojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2562587Sitojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2662587Sitojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2762587Sitojun * SUCH DAMAGE. 28174510Sobrien * 29174510Sobrien * $KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $ 3062587Sitojun */ 3162587Sitojun 32174510Sobrien#include <sys/cdefs.h> 33174510Sobrien__FBSDID("$FreeBSD$"); 34174510Sobrien 3562587Sitojun#include <sys/param.h> 3662587Sitojun#include <sys/malloc.h> 3762587Sitojun#include <sys/mbuf.h> 3862587Sitojun#include <sys/socket.h> 3962587Sitojun#include <sys/systm.h> 4078064Sume#include <sys/queue.h> 41148385Sume#include <sys/syslog.h> 4262587Sitojun 4362587Sitojun#include <net/if.h> 44185571Sbz#include <net/vnet.h> 4562587Sitojun 4662587Sitojun#include <netinet/in.h> 47185571Sbz 48183550Szec#include <netinet/ip6.h> 4962587Sitojun#include <netinet6/in6_var.h> 50195699Srwatson#include <netinet6/ip6_var.h> 5162587Sitojun#include <netinet6/scope6_var.h> 5262587Sitojun 53207369Sbz#ifdef ENABLE_DEFAULT_SCOPE 54207369SbzVNET_DEFINE(int, ip6_use_defzone) = 1; 55207369Sbz#else 56207369SbzVNET_DEFINE(int, ip6_use_defzone) = 0; 57207369Sbz#endif 58148385Sume 59121343Sume/* 60138184Sgnn * The scope6_lock protects the global sid default stored in 61138184Sgnn * sid_default below. 62121343Sume */ 63121343Sumestatic struct mtx scope6_lock; 64121343Sume#define SCOPE6_LOCK_INIT() mtx_init(&scope6_lock, "scope6_lock", NULL, MTX_DEF) 65121343Sume#define SCOPE6_LOCK() mtx_lock(&scope6_lock) 66121343Sume#define SCOPE6_UNLOCK() mtx_unlock(&scope6_lock) 67121343Sume#define SCOPE6_LOCK_ASSERT() mtx_assert(&scope6_lock, MA_OWNED) 68121343Sume 69215701Sdimstatic VNET_DEFINE(struct scope6_id, sid_default); 70195727Srwatson#define V_sid_default VNET(sid_default) 71195699Srwatson 72121161Sume#define SID(ifp) \ 73121161Sume (((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->scope6_id) 7462587Sitojun 7562587Sitojunvoid 76171259Sdelphijscope6_init(void) 77121161Sume{ 78121161Sume 79190787Szec bzero(&V_sid_default, sizeof(V_sid_default)); 80190787Szec 81190787Szec if (!IS_DEFAULT_VNET(curvnet)) 82190787Szec return; 83190787Szec 84121343Sume SCOPE6_LOCK_INIT(); 85121161Sume} 86121161Sume 87121161Sumestruct scope6_id * 88171259Sdelphijscope6_ifattach(struct ifnet *ifp) 8962587Sitojun{ 90121161Sume struct scope6_id *sid; 9162587Sitojun 92121161Sume sid = (struct scope6_id *)malloc(sizeof(*sid), M_IFADDR, M_WAITOK); 93121161Sume bzero(sid, sizeof(*sid)); 9462587Sitojun 9562587Sitojun /* 9662587Sitojun * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard. 9762587Sitojun * Should we rather hardcode here? 9862587Sitojun */ 99121315Sume sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index; 100121161Sume sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index; 10162587Sitojun#ifdef MULTI_SCOPE 10262587Sitojun /* by default, we don't care about scope boundary for these scopes. */ 103121161Sume sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1; 104121161Sume sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1; 10562587Sitojun#endif 10662587Sitojun 107121161Sume return sid; 10862587Sitojun} 10962587Sitojun 110121161Sumevoid 111171259Sdelphijscope6_ifdetach(struct scope6_id *sid) 112121161Sume{ 113121161Sume 114121161Sume free(sid, M_IFADDR); 115121161Sume} 116121161Sume 11762587Sitojunint 118171259Sdelphijscope6_set(struct ifnet *ifp, struct scope6_id *idlist) 11962587Sitojun{ 120138184Sgnn int i; 12162587Sitojun int error = 0; 122138184Sgnn struct scope6_id *sid = NULL; 12362587Sitojun 124243382Sae IF_AFDATA_WLOCK(ifp); 125138184Sgnn sid = SID(ifp); 126138184Sgnn 127138184Sgnn if (!sid) { /* paranoid? */ 128243382Sae IF_AFDATA_WUNLOCK(ifp); 129120856Sume return (EINVAL); 130138184Sgnn } 13162587Sitojun 13262587Sitojun /* 13362587Sitojun * XXX: We need more consistency checks of the relationship among 13462587Sitojun * scopes (e.g. an organization should be larger than a site). 13562587Sitojun */ 13662587Sitojun 13762587Sitojun /* 13862587Sitojun * TODO(XXX): after setting, we should reflect the changes to 139120941Sume * interface addresses, routing table entries, PCB entries... 14062587Sitojun */ 14162587Sitojun 14262587Sitojun for (i = 0; i < 16; i++) { 143121161Sume if (idlist->s6id_list[i] && 144121161Sume idlist->s6id_list[i] != sid->s6id_list[i]) { 145121315Sume /* 146121315Sume * An interface zone ID must be the corresponding 147121315Sume * interface index by definition. 148121315Sume */ 149121315Sume if (i == IPV6_ADDR_SCOPE_INTFACELOCAL && 150121315Sume idlist->s6id_list[i] != ifp->if_index) { 151243382Sae IF_AFDATA_WUNLOCK(ifp); 152121315Sume return (EINVAL); 153121315Sume } 154121315Sume 15562587Sitojun if (i == IPV6_ADDR_SCOPE_LINKLOCAL && 156181803Sbz idlist->s6id_list[i] > V_if_index) { 15762587Sitojun /* 15862587Sitojun * XXX: theoretically, there should be no 15962587Sitojun * relationship between link IDs and interface 16062587Sitojun * IDs, but we check the consistency for 16162587Sitojun * safety in later use. 16262587Sitojun */ 163243382Sae IF_AFDATA_WUNLOCK(ifp); 164120856Sume return (EINVAL); 16562587Sitojun } 16662587Sitojun 16762587Sitojun /* 16862587Sitojun * XXX: we must need lots of work in this case, 16962587Sitojun * but we simply set the new value in this initial 17062587Sitojun * implementation. 17162587Sitojun */ 172121161Sume sid->s6id_list[i] = idlist->s6id_list[i]; 17362587Sitojun } 17462587Sitojun } 175243382Sae IF_AFDATA_WUNLOCK(ifp); 17662587Sitojun 177120856Sume return (error); 17862587Sitojun} 17962587Sitojun 18062587Sitojunint 181171259Sdelphijscope6_get(struct ifnet *ifp, struct scope6_id *idlist) 18262587Sitojun{ 183243382Sae struct scope6_id *sid; 184243382Sae 185138184Sgnn /* We only need to lock the interface's afdata for SID() to work. */ 186243382Sae IF_AFDATA_RLOCK(ifp); 187243382Sae sid = SID(ifp); 188138184Sgnn if (sid == NULL) { /* paranoid? */ 189243382Sae IF_AFDATA_RUNLOCK(ifp); 190120856Sume return (EINVAL); 191138184Sgnn } 19262587Sitojun 193121161Sume *idlist = *sid; 19462587Sitojun 195243382Sae IF_AFDATA_RUNLOCK(ifp); 196120856Sume return (0); 19762587Sitojun} 19862587Sitojun 19962587Sitojun 20062587Sitojun/* 20162587Sitojun * Get a scope of the address. Node-local, link-local, site-local or global. 20262587Sitojun */ 20362587Sitojunint 204171259Sdelphijin6_addrscope(struct in6_addr *addr) 20562587Sitojun{ 20662587Sitojun int scope; 20762587Sitojun 208121315Sume if (addr->s6_addr[0] == 0xfe) { 209121315Sume scope = addr->s6_addr[1] & 0xc0; 21062587Sitojun 21162587Sitojun switch (scope) { 21262587Sitojun case 0x80: 21362587Sitojun return IPV6_ADDR_SCOPE_LINKLOCAL; 21462587Sitojun break; 21562587Sitojun case 0xc0: 21662587Sitojun return IPV6_ADDR_SCOPE_SITELOCAL; 21762587Sitojun break; 21862587Sitojun default: 21962587Sitojun return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */ 22062587Sitojun break; 22162587Sitojun } 22262587Sitojun } 22362587Sitojun 22462587Sitojun 225121315Sume if (addr->s6_addr[0] == 0xff) { 226121315Sume scope = addr->s6_addr[1] & 0x0f; 22762587Sitojun 22862587Sitojun /* 22962587Sitojun * due to other scope such as reserved, 23062587Sitojun * return scope doesn't work. 23162587Sitojun */ 23262587Sitojun switch (scope) { 233121315Sume case IPV6_ADDR_SCOPE_INTFACELOCAL: 234121315Sume return IPV6_ADDR_SCOPE_INTFACELOCAL; 23562587Sitojun break; 23662587Sitojun case IPV6_ADDR_SCOPE_LINKLOCAL: 23762587Sitojun return IPV6_ADDR_SCOPE_LINKLOCAL; 23862587Sitojun break; 23962587Sitojun case IPV6_ADDR_SCOPE_SITELOCAL: 24062587Sitojun return IPV6_ADDR_SCOPE_SITELOCAL; 24162587Sitojun break; 24262587Sitojun default: 24362587Sitojun return IPV6_ADDR_SCOPE_GLOBAL; 24462587Sitojun break; 24562587Sitojun } 24662587Sitojun } 24762587Sitojun 248121315Sume /* 249121315Sume * Regard loopback and unspecified addresses as global, since 250121315Sume * they have no ambiguity. 251121315Sume */ 25293128Sume if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) { 253121315Sume if (addr->s6_addr[15] == 1) /* loopback */ 25462587Sitojun return IPV6_ADDR_SCOPE_LINKLOCAL; 255121315Sume if (addr->s6_addr[15] == 0) /* unspecified */ 256121315Sume return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */ 25762587Sitojun } 25862587Sitojun 25962587Sitojun return IPV6_ADDR_SCOPE_GLOBAL; 26062587Sitojun} 26162587Sitojun 262171259Sdelphij/* 263171259Sdelphij * ifp - note that this might be NULL 264171259Sdelphij */ 265171259Sdelphij 26662587Sitojunvoid 267171259Sdelphijscope6_setdefault(struct ifnet *ifp) 26862587Sitojun{ 269183550Szec 27062587Sitojun /* 271138184Sgnn * Currently, this function just sets the default "interfaces" 272121161Sume * and "links" according to the given interface. 27362587Sitojun * We might eventually have to separate the notion of "link" from 27462587Sitojun * "interface" and provide a user interface to set the default. 27562587Sitojun */ 276121343Sume SCOPE6_LOCK(); 27762587Sitojun if (ifp) { 278181803Sbz V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 27962587Sitojun ifp->if_index; 280181803Sbz V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 281121161Sume ifp->if_index; 282120941Sume } else { 283181803Sbz V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0; 284181803Sbz V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0; 28562587Sitojun } 286121343Sume SCOPE6_UNLOCK(); 28762587Sitojun} 28862587Sitojun 28962587Sitojunint 290171259Sdelphijscope6_get_default(struct scope6_id *idlist) 29162587Sitojun{ 292121343Sume 293121343Sume SCOPE6_LOCK(); 294181803Sbz *idlist = V_sid_default; 295121343Sume SCOPE6_UNLOCK(); 29662587Sitojun 297120856Sume return (0); 29862587Sitojun} 29962587Sitojun 30062587Sitojunu_int32_t 301171259Sdelphijscope6_addr2default(struct in6_addr *addr) 30262587Sitojun{ 303121343Sume u_int32_t id; 304121343Sume 305121161Sume /* 306121161Sume * special case: The loopback address should be considered as 307121161Sume * link-local, but there's no ambiguity in the syntax. 308121161Sume */ 309121161Sume if (IN6_IS_ADDR_LOOPBACK(addr)) 310121161Sume return (0); 311121161Sume 312121343Sume /* 313121343Sume * XXX: 32-bit read is atomic on all our platforms, is it OK 314121343Sume * not to lock here? 315121343Sume */ 316121343Sume SCOPE6_LOCK(); 317181803Sbz id = V_sid_default.s6id_list[in6_addrscope(addr)]; 318121343Sume SCOPE6_UNLOCK(); 319121343Sume return (id); 32062587Sitojun} 321148385Sume 322148385Sume/* 323148385Sume * Validate the specified scope zone ID in the sin6_scope_id field. If the ID 324148385Sume * is unspecified (=0), needs to be specified, and the default zone ID can be 325148385Sume * used, the default value will be used. 326148385Sume * This routine then generates the kernel-internal form: if the address scope 327148385Sume * of is interface-local or link-local, embed the interface index in the 328148385Sume * address. 329148385Sume */ 330148385Sumeint 331171259Sdelphijsa6_embedscope(struct sockaddr_in6 *sin6, int defaultok) 332148385Sume{ 333148385Sume struct ifnet *ifp; 334148385Sume u_int32_t zoneid; 335148385Sume 336148385Sume if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok) 337148385Sume zoneid = scope6_addr2default(&sin6->sin6_addr); 338148385Sume 339148385Sume if (zoneid != 0 && 340148385Sume (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) || 341148385Sume IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) { 342148385Sume /* 343148385Sume * At this moment, we only check interface-local and 344148385Sume * link-local scope IDs, and use interface indices as the 345148385Sume * zone IDs assuming a one-to-one mapping between interfaces 346148385Sume * and links. 347148385Sume */ 348181803Sbz if (V_if_index < zoneid) 349148385Sume return (ENXIO); 350148385Sume ifp = ifnet_byindex(zoneid); 351148385Sume if (ifp == NULL) /* XXX: this can happen for some OS */ 352148385Sume return (ENXIO); 353148385Sume 354148385Sume /* XXX assignment to 16bit from 32bit variable */ 355148385Sume sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff); 356148385Sume 357148385Sume sin6->sin6_scope_id = 0; 358148385Sume } 359148385Sume 360148385Sume return 0; 361148385Sume} 362148385Sume 363148385Sume/* 364148385Sume * generate standard sockaddr_in6 from embedded form. 365148385Sume */ 366148385Sumeint 367171259Sdelphijsa6_recoverscope(struct sockaddr_in6 *sin6) 368148385Sume{ 369165118Sbz char ip6buf[INET6_ADDRSTRLEN]; 370148385Sume u_int32_t zoneid; 371148385Sume 372148385Sume if (sin6->sin6_scope_id != 0) { 373148385Sume log(LOG_NOTICE, 374148385Sume "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n", 375165118Sbz ip6_sprintf(ip6buf, &sin6->sin6_addr), sin6->sin6_scope_id); 376148385Sume /* XXX: proceed anyway... */ 377148385Sume } 378148385Sume if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) || 379148385Sume IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) { 380148385Sume /* 381148385Sume * KAME assumption: link id == interface id 382148385Sume */ 383148385Sume zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]); 384148385Sume if (zoneid) { 385148385Sume /* sanity check */ 386243382Sae if (V_if_index < zoneid) 387148385Sume return (ENXIO); 388148385Sume if (!ifnet_byindex(zoneid)) 389148385Sume return (ENXIO); 390148385Sume sin6->sin6_addr.s6_addr16[1] = 0; 391148385Sume sin6->sin6_scope_id = zoneid; 392148385Sume } 393148385Sume } 394148385Sume 395148385Sume return 0; 396148385Sume} 397148385Sume 398148385Sume/* 399148385Sume * Determine the appropriate scope zone ID for in6 and ifp. If ret_id is 400148385Sume * non NULL, it is set to the zone ID. If the zone ID needs to be embedded 401148385Sume * in the in6_addr structure, in6 will be modified. 402171259Sdelphij * 403171259Sdelphij * ret_id - unnecessary? 404148385Sume */ 405148385Sumeint 406171259Sdelphijin6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id) 407148385Sume{ 408148385Sume int scope; 409148385Sume u_int32_t zoneid = 0; 410148396Sume struct scope6_id *sid; 411148385Sume 412243382Sae IF_AFDATA_RLOCK(ifp); 413148396Sume 414148396Sume sid = SID(ifp); 415148396Sume 416148385Sume#ifdef DIAGNOSTIC 417148385Sume if (sid == NULL) { /* should not happen */ 418148385Sume panic("in6_setscope: scope array is NULL"); 419148385Sume /* NOTREACHED */ 420148385Sume } 421148385Sume#endif 422148385Sume 423148385Sume /* 424148385Sume * special case: the loopback address can only belong to a loopback 425148385Sume * interface. 426148385Sume */ 427148385Sume if (IN6_IS_ADDR_LOOPBACK(in6)) { 428148399Sume if (!(ifp->if_flags & IFF_LOOPBACK)) { 429243382Sae IF_AFDATA_RUNLOCK(ifp); 430148385Sume return (EINVAL); 431148399Sume } else { 432148385Sume if (ret_id != NULL) 433148385Sume *ret_id = 0; /* there's no ambiguity */ 434243382Sae IF_AFDATA_RUNLOCK(ifp); 435148385Sume return (0); 436148385Sume } 437148385Sume } 438148385Sume 439148385Sume scope = in6_addrscope(in6); 440148385Sume switch (scope) { 441148385Sume case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */ 442148385Sume zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL]; 443148385Sume break; 444148385Sume 445148385Sume case IPV6_ADDR_SCOPE_LINKLOCAL: 446148385Sume zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]; 447148385Sume break; 448148385Sume 449148385Sume case IPV6_ADDR_SCOPE_SITELOCAL: 450148385Sume zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]; 451148385Sume break; 452148385Sume 453148385Sume case IPV6_ADDR_SCOPE_ORGLOCAL: 454148385Sume zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]; 455148385Sume break; 456148385Sume 457148385Sume default: 458148385Sume zoneid = 0; /* XXX: treat as global. */ 459148385Sume break; 460148385Sume } 461243382Sae IF_AFDATA_RUNLOCK(ifp); 462148385Sume 463148385Sume if (ret_id != NULL) 464148385Sume *ret_id = zoneid; 465148385Sume 466148385Sume if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) 467148385Sume in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */ 468148385Sume 469148385Sume return (0); 470148385Sume} 471148385Sume 472148385Sume/* 473148385Sume * Just clear the embedded scope identifier. Return 0 if the original address 474148385Sume * is intact; return non 0 if the address is modified. 475148385Sume */ 476148385Sumeint 477171259Sdelphijin6_clearscope(struct in6_addr *in6) 478148385Sume{ 479148385Sume int modified = 0; 480148385Sume 481148385Sume if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) { 482148385Sume if (in6->s6_addr16[1] != 0) 483148385Sume modified = 1; 484148385Sume in6->s6_addr16[1] = 0; 485148385Sume } 486148385Sume 487148385Sume return (modified); 488148385Sume} 489238224Sbz 490238224Sbz/* 491238224Sbz * Return the scope identifier or zero. 492238224Sbz */ 493238224Sbzuint16_t 494238224Sbzin6_getscope(struct in6_addr *in6) 495238224Sbz{ 496238224Sbz 497238224Sbz if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) 498238224Sbz return (in6->s6_addr16[1]); 499238224Sbz 500238224Sbz return (0); 501238224Sbz} 502