scope6.c revision 207369
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: head/sys/netinet6/scope6.c 207369 2010-04-29 11:52:42Z bz $"); 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 69195699Srwatsonstatic 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 124138184Sgnn IF_AFDATA_LOCK(ifp); 125138184Sgnn sid = SID(ifp); 126138184Sgnn 127138184Sgnn if (!sid) { /* paranoid? */ 128138184Sgnn IF_AFDATA_UNLOCK(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 142121343Sume SCOPE6_LOCK(); 14362587Sitojun for (i = 0; i < 16; i++) { 144121161Sume if (idlist->s6id_list[i] && 145121161Sume idlist->s6id_list[i] != sid->s6id_list[i]) { 146121315Sume /* 147121315Sume * An interface zone ID must be the corresponding 148121315Sume * interface index by definition. 149121315Sume */ 150121315Sume if (i == IPV6_ADDR_SCOPE_INTFACELOCAL && 151121315Sume idlist->s6id_list[i] != ifp->if_index) { 152138184Sgnn IF_AFDATA_UNLOCK(ifp); 153138184Sgnn SCOPE6_UNLOCK(); 154121315Sume return (EINVAL); 155121315Sume } 156121315Sume 15762587Sitojun if (i == IPV6_ADDR_SCOPE_LINKLOCAL && 158181803Sbz idlist->s6id_list[i] > V_if_index) { 15962587Sitojun /* 16062587Sitojun * XXX: theoretically, there should be no 16162587Sitojun * relationship between link IDs and interface 16262587Sitojun * IDs, but we check the consistency for 16362587Sitojun * safety in later use. 16462587Sitojun */ 165138184Sgnn IF_AFDATA_UNLOCK(ifp); 166138184Sgnn SCOPE6_UNLOCK(); 167120856Sume return (EINVAL); 16862587Sitojun } 16962587Sitojun 17062587Sitojun /* 17162587Sitojun * XXX: we must need lots of work in this case, 17262587Sitojun * but we simply set the new value in this initial 17362587Sitojun * implementation. 17462587Sitojun */ 175121161Sume sid->s6id_list[i] = idlist->s6id_list[i]; 17662587Sitojun } 17762587Sitojun } 178121343Sume SCOPE6_UNLOCK(); 179138184Sgnn IF_AFDATA_UNLOCK(ifp); 18062587Sitojun 181120856Sume return (error); 18262587Sitojun} 18362587Sitojun 18462587Sitojunint 185171259Sdelphijscope6_get(struct ifnet *ifp, struct scope6_id *idlist) 18662587Sitojun{ 187138184Sgnn /* We only need to lock the interface's afdata for SID() to work. */ 188138184Sgnn IF_AFDATA_LOCK(ifp); 189121161Sume struct scope6_id *sid = SID(ifp); 190121161Sume 191138184Sgnn if (sid == NULL) { /* paranoid? */ 192138184Sgnn IF_AFDATA_UNLOCK(ifp); 193120856Sume return (EINVAL); 194138184Sgnn } 19562587Sitojun 196121343Sume SCOPE6_LOCK(); 197121161Sume *idlist = *sid; 198121343Sume SCOPE6_UNLOCK(); 19962587Sitojun 200138184Sgnn IF_AFDATA_UNLOCK(ifp); 201120856Sume return (0); 20262587Sitojun} 20362587Sitojun 20462587Sitojun 20562587Sitojun/* 20662587Sitojun * Get a scope of the address. Node-local, link-local, site-local or global. 20762587Sitojun */ 20862587Sitojunint 209171259Sdelphijin6_addrscope(struct in6_addr *addr) 21062587Sitojun{ 21162587Sitojun int scope; 21262587Sitojun 213121315Sume if (addr->s6_addr[0] == 0xfe) { 214121315Sume scope = addr->s6_addr[1] & 0xc0; 21562587Sitojun 21662587Sitojun switch (scope) { 21762587Sitojun case 0x80: 21862587Sitojun return IPV6_ADDR_SCOPE_LINKLOCAL; 21962587Sitojun break; 22062587Sitojun case 0xc0: 22162587Sitojun return IPV6_ADDR_SCOPE_SITELOCAL; 22262587Sitojun break; 22362587Sitojun default: 22462587Sitojun return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */ 22562587Sitojun break; 22662587Sitojun } 22762587Sitojun } 22862587Sitojun 22962587Sitojun 230121315Sume if (addr->s6_addr[0] == 0xff) { 231121315Sume scope = addr->s6_addr[1] & 0x0f; 23262587Sitojun 23362587Sitojun /* 23462587Sitojun * due to other scope such as reserved, 23562587Sitojun * return scope doesn't work. 23662587Sitojun */ 23762587Sitojun switch (scope) { 238121315Sume case IPV6_ADDR_SCOPE_INTFACELOCAL: 239121315Sume return IPV6_ADDR_SCOPE_INTFACELOCAL; 24062587Sitojun break; 24162587Sitojun case IPV6_ADDR_SCOPE_LINKLOCAL: 24262587Sitojun return IPV6_ADDR_SCOPE_LINKLOCAL; 24362587Sitojun break; 24462587Sitojun case IPV6_ADDR_SCOPE_SITELOCAL: 24562587Sitojun return IPV6_ADDR_SCOPE_SITELOCAL; 24662587Sitojun break; 24762587Sitojun default: 24862587Sitojun return IPV6_ADDR_SCOPE_GLOBAL; 24962587Sitojun break; 25062587Sitojun } 25162587Sitojun } 25262587Sitojun 253121315Sume /* 254121315Sume * Regard loopback and unspecified addresses as global, since 255121315Sume * they have no ambiguity. 256121315Sume */ 25793128Sume if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) { 258121315Sume if (addr->s6_addr[15] == 1) /* loopback */ 25962587Sitojun return IPV6_ADDR_SCOPE_LINKLOCAL; 260121315Sume if (addr->s6_addr[15] == 0) /* unspecified */ 261121315Sume return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */ 26262587Sitojun } 26362587Sitojun 26462587Sitojun return IPV6_ADDR_SCOPE_GLOBAL; 26562587Sitojun} 26662587Sitojun 267171259Sdelphij/* 268171259Sdelphij * ifp - note that this might be NULL 269171259Sdelphij */ 270171259Sdelphij 27162587Sitojunvoid 272171259Sdelphijscope6_setdefault(struct ifnet *ifp) 27362587Sitojun{ 274183550Szec 27562587Sitojun /* 276138184Sgnn * Currently, this function just sets the default "interfaces" 277121161Sume * and "links" according to the given interface. 27862587Sitojun * We might eventually have to separate the notion of "link" from 27962587Sitojun * "interface" and provide a user interface to set the default. 28062587Sitojun */ 281121343Sume SCOPE6_LOCK(); 28262587Sitojun if (ifp) { 283181803Sbz V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 28462587Sitojun ifp->if_index; 285181803Sbz V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 286121161Sume ifp->if_index; 287120941Sume } else { 288181803Sbz V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0; 289181803Sbz V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0; 29062587Sitojun } 291121343Sume SCOPE6_UNLOCK(); 29262587Sitojun} 29362587Sitojun 29462587Sitojunint 295171259Sdelphijscope6_get_default(struct scope6_id *idlist) 29662587Sitojun{ 297121343Sume 298121343Sume SCOPE6_LOCK(); 299181803Sbz *idlist = V_sid_default; 300121343Sume SCOPE6_UNLOCK(); 30162587Sitojun 302120856Sume return (0); 30362587Sitojun} 30462587Sitojun 30562587Sitojunu_int32_t 306171259Sdelphijscope6_addr2default(struct in6_addr *addr) 30762587Sitojun{ 308121343Sume u_int32_t id; 309121343Sume 310121161Sume /* 311121161Sume * special case: The loopback address should be considered as 312121161Sume * link-local, but there's no ambiguity in the syntax. 313121161Sume */ 314121161Sume if (IN6_IS_ADDR_LOOPBACK(addr)) 315121161Sume return (0); 316121161Sume 317121343Sume /* 318121343Sume * XXX: 32-bit read is atomic on all our platforms, is it OK 319121343Sume * not to lock here? 320121343Sume */ 321121343Sume SCOPE6_LOCK(); 322181803Sbz id = V_sid_default.s6id_list[in6_addrscope(addr)]; 323121343Sume SCOPE6_UNLOCK(); 324121343Sume return (id); 32562587Sitojun} 326148385Sume 327148385Sume/* 328148385Sume * Validate the specified scope zone ID in the sin6_scope_id field. If the ID 329148385Sume * is unspecified (=0), needs to be specified, and the default zone ID can be 330148385Sume * used, the default value will be used. 331148385Sume * This routine then generates the kernel-internal form: if the address scope 332148385Sume * of is interface-local or link-local, embed the interface index in the 333148385Sume * address. 334148385Sume */ 335148385Sumeint 336171259Sdelphijsa6_embedscope(struct sockaddr_in6 *sin6, int defaultok) 337148385Sume{ 338148385Sume struct ifnet *ifp; 339148385Sume u_int32_t zoneid; 340148385Sume 341148385Sume if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok) 342148385Sume zoneid = scope6_addr2default(&sin6->sin6_addr); 343148385Sume 344148385Sume if (zoneid != 0 && 345148385Sume (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) || 346148385Sume IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) { 347148385Sume /* 348148385Sume * At this moment, we only check interface-local and 349148385Sume * link-local scope IDs, and use interface indices as the 350148385Sume * zone IDs assuming a one-to-one mapping between interfaces 351148385Sume * and links. 352148385Sume */ 353181803Sbz if (V_if_index < zoneid) 354148385Sume return (ENXIO); 355148385Sume ifp = ifnet_byindex(zoneid); 356148385Sume if (ifp == NULL) /* XXX: this can happen for some OS */ 357148385Sume return (ENXIO); 358148385Sume 359148385Sume /* XXX assignment to 16bit from 32bit variable */ 360148385Sume sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff); 361148385Sume 362148385Sume sin6->sin6_scope_id = 0; 363148385Sume } 364148385Sume 365148385Sume return 0; 366148385Sume} 367148385Sume 368148385Sume/* 369148385Sume * generate standard sockaddr_in6 from embedded form. 370148385Sume */ 371148385Sumeint 372171259Sdelphijsa6_recoverscope(struct sockaddr_in6 *sin6) 373148385Sume{ 374165118Sbz char ip6buf[INET6_ADDRSTRLEN]; 375148385Sume u_int32_t zoneid; 376148385Sume 377148385Sume if (sin6->sin6_scope_id != 0) { 378148385Sume log(LOG_NOTICE, 379148385Sume "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n", 380165118Sbz ip6_sprintf(ip6buf, &sin6->sin6_addr), sin6->sin6_scope_id); 381148385Sume /* XXX: proceed anyway... */ 382148385Sume } 383148385Sume if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) || 384148385Sume IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) { 385148385Sume /* 386148385Sume * KAME assumption: link id == interface id 387148385Sume */ 388148385Sume zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]); 389148385Sume if (zoneid) { 390148385Sume /* sanity check */ 391181803Sbz if (zoneid < 0 || V_if_index < zoneid) 392148385Sume return (ENXIO); 393148385Sume if (!ifnet_byindex(zoneid)) 394148385Sume return (ENXIO); 395148385Sume sin6->sin6_addr.s6_addr16[1] = 0; 396148385Sume sin6->sin6_scope_id = zoneid; 397148385Sume } 398148385Sume } 399148385Sume 400148385Sume return 0; 401148385Sume} 402148385Sume 403148385Sume/* 404148385Sume * Determine the appropriate scope zone ID for in6 and ifp. If ret_id is 405148385Sume * non NULL, it is set to the zone ID. If the zone ID needs to be embedded 406148385Sume * in the in6_addr structure, in6 will be modified. 407171259Sdelphij * 408171259Sdelphij * ret_id - unnecessary? 409148385Sume */ 410148385Sumeint 411171259Sdelphijin6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id) 412148385Sume{ 413148385Sume int scope; 414148385Sume u_int32_t zoneid = 0; 415148396Sume struct scope6_id *sid; 416148385Sume 417148396Sume IF_AFDATA_LOCK(ifp); 418148396Sume 419148396Sume sid = SID(ifp); 420148396Sume 421148385Sume#ifdef DIAGNOSTIC 422148385Sume if (sid == NULL) { /* should not happen */ 423148385Sume panic("in6_setscope: scope array is NULL"); 424148385Sume /* NOTREACHED */ 425148385Sume } 426148385Sume#endif 427148385Sume 428148385Sume /* 429148385Sume * special case: the loopback address can only belong to a loopback 430148385Sume * interface. 431148385Sume */ 432148385Sume if (IN6_IS_ADDR_LOOPBACK(in6)) { 433148399Sume if (!(ifp->if_flags & IFF_LOOPBACK)) { 434148396Sume IF_AFDATA_UNLOCK(ifp); 435148385Sume return (EINVAL); 436148399Sume } else { 437148385Sume if (ret_id != NULL) 438148385Sume *ret_id = 0; /* there's no ambiguity */ 439148396Sume IF_AFDATA_UNLOCK(ifp); 440148385Sume return (0); 441148385Sume } 442148385Sume } 443148385Sume 444148385Sume scope = in6_addrscope(in6); 445148385Sume 446148396Sume SCOPE6_LOCK(); 447148385Sume switch (scope) { 448148385Sume case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */ 449148385Sume zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL]; 450148385Sume break; 451148385Sume 452148385Sume case IPV6_ADDR_SCOPE_LINKLOCAL: 453148385Sume zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]; 454148385Sume break; 455148385Sume 456148385Sume case IPV6_ADDR_SCOPE_SITELOCAL: 457148385Sume zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]; 458148385Sume break; 459148385Sume 460148385Sume case IPV6_ADDR_SCOPE_ORGLOCAL: 461148385Sume zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]; 462148385Sume break; 463148385Sume 464148385Sume default: 465148385Sume zoneid = 0; /* XXX: treat as global. */ 466148385Sume break; 467148385Sume } 468148396Sume SCOPE6_UNLOCK(); 469148396Sume IF_AFDATA_UNLOCK(ifp); 470148385Sume 471148385Sume if (ret_id != NULL) 472148385Sume *ret_id = zoneid; 473148385Sume 474148385Sume if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) 475148385Sume in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */ 476148385Sume 477148385Sume return (0); 478148385Sume} 479148385Sume 480148385Sume/* 481148385Sume * Just clear the embedded scope identifier. Return 0 if the original address 482148385Sume * is intact; return non 0 if the address is modified. 483148385Sume */ 484148385Sumeint 485171259Sdelphijin6_clearscope(struct in6_addr *in6) 486148385Sume{ 487148385Sume int modified = 0; 488148385Sume 489148385Sume if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) { 490148385Sume if (in6->s6_addr16[1] != 0) 491148385Sume modified = 1; 492148385Sume in6->s6_addr16[1] = 0; 493148385Sume } 494148385Sume 495148385Sume return (modified); 496148385Sume} 497