scope6.c revision 243029
1139823Simp/*- 21541Srgrimes * Copyright (C) 2000 WIDE Project. 31541Srgrimes * All rights reserved. 41541Srgrimes * 51541Srgrimes * Redistribution and use in source and binary forms, with or without 61541Srgrimes * modification, are permitted provided that the following conditions 71541Srgrimes * are met: 81541Srgrimes * 1. Redistributions of source code must retain the above copyright 91541Srgrimes * notice, this list of conditions and the following disclaimer. 101541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111541Srgrimes * notice, this list of conditions and the following disclaimer in the 121541Srgrimes * documentation and/or other materials provided with the distribution. 131541Srgrimes * 3. Neither the name of the project nor the names of its contributors 141541Srgrimes * may be used to endorse or promote products derived from this software 151541Srgrimes * without specific prior written permission. 161541Srgrimes * 171541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 181541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 211541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271541Srgrimes * SUCH DAMAGE. 281541Srgrimes * 291541Srgrimes * $KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $ 301541Srgrimes */ 311541Srgrimes 3236503Speter#include <sys/cdefs.h> 331541Srgrimes__FBSDID("$FreeBSD: head/sys/netinet6/scope6.c 243029 2012-11-14 17:23:48Z ae $"); 341541Srgrimes 3583651Speter#include <sys/param.h> 3683651Speter#include <sys/malloc.h> 3783651Speter#include <sys/mbuf.h> 381541Srgrimes#include <sys/socket.h> 391541Srgrimes#include <sys/systm.h> 401541Srgrimes#include <sys/queue.h> 411541Srgrimes#include <sys/syslog.h> 421541Srgrimes 4383651Speter#include <net/if.h> 44100134Salfred#include <net/vnet.h> 45100134Salfred 461541Srgrimes#include <netinet/in.h> 4748274Speter 4848274Speter#include <netinet/ip6.h> 4960041Sphk#include <netinet6/in6_var.h> 5031886Sbde#include <netinet6/ip6_var.h> 511541Srgrimes#include <netinet6/scope6_var.h> 521541Srgrimes 531541Srgrimes#ifdef ENABLE_DEFAULT_SCOPE 541541SrgrimesVNET_DEFINE(int, ip6_use_defzone) = 1; 551541Srgrimes#else 56150634SjhbVNET_DEFINE(int, ip6_use_defzone) = 0; 571541Srgrimes#endif 581541Srgrimes 599336Sdfr/* 6083700Speter * The scope6_lock protects the global sid default stored in 612997Swollman * sid_default below. 622997Swollman */ 6383651Speterstatic struct mtx scope6_lock; 641541Srgrimes#define SCOPE6_LOCK_INIT() mtx_init(&scope6_lock, "scope6_lock", NULL, MTX_DEF) 653305Sphk#define SCOPE6_LOCK() mtx_lock(&scope6_lock) 6612662Sdg#define SCOPE6_UNLOCK() mtx_unlock(&scope6_lock) 6712662Sdg#define SCOPE6_LOCK_ASSERT() mtx_assert(&scope6_lock, MA_OWNED) 6892783Sjeff 693305Sphkstatic VNET_DEFINE(struct scope6_id, sid_default); 701541Srgrimes#define V_sid_default VNET(sid_default) 719336Sdfr 7283651Speter#define SID(ifp) \ 731541Srgrimes (((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->scope6_id) 7483651Speter 751541Srgrimesvoid 761541Srgrimesscope6_init(void) 771541Srgrimes{ 781541Srgrimes 791541Srgrimes bzero(&V_sid_default, sizeof(V_sid_default)); 801541Srgrimes 811541Srgrimes if (!IS_DEFAULT_VNET(curvnet)) 8289094Smsmith return; 8389094Smsmith 8489094Smsmith SCOPE6_LOCK_INIT(); 8589094Smsmith} 8689094Smsmith 871541Srgrimesstruct scope6_id * 881541Srgrimesscope6_ifattach(struct ifnet *ifp) 89129639Srwatson{ 90129639Srwatson struct scope6_id *sid; 9183651Speter 9283651Speter sid = (struct scope6_id *)malloc(sizeof(*sid), M_IFADDR, M_WAITOK); 9312911Sphk bzero(sid, sizeof(*sid)); 9489094Smsmith 959336Sdfr /* 96184588Sdfr * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard. 979759Sbde * Should we rather hardcode here? 989759Sbde */ 999759Sbde sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index; 1009759Sbde sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index; 101184588Sdfr#ifdef MULTI_SCOPE 1029759Sbde /* by default, we don't care about scope boundary for these scopes. */ 103168268Sjhb sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1; 104168268Sjhb sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1; 105168268Sjhb#endif 10638894Sbde 107129639Srwatson return sid; 108129639Srwatson} 1099336Sdfr 1109336Sdfrvoid 1119336Sdfrscope6_ifdetach(struct scope6_id *sid) 112129639Srwatson{ 1139336Sdfr 1149336Sdfr free(sid, M_IFADDR); 1159336Sdfr} 1169336Sdfr 1179336Sdfrint 1189336Sdfrscope6_set(struct ifnet *ifp, struct scope6_id *idlist) 1199336Sdfr{ 1209336Sdfr int i; 1219336Sdfr int error = 0; 1229336Sdfr struct scope6_id *sid = NULL; 1239336Sdfr 1249336Sdfr IF_AFDATA_LOCK(ifp); 1259336Sdfr sid = SID(ifp); 1269336Sdfr 1279336Sdfr if (!sid) { /* paranoid? */ 1289336Sdfr IF_AFDATA_UNLOCK(ifp); 1299336Sdfr return (EINVAL); 1309336Sdfr } 1319336Sdfr 1329336Sdfr /* 1339336Sdfr * XXX: We need more consistency checks of the relationship among 1349336Sdfr * scopes (e.g. an organization should be larger than a site). 1359336Sdfr */ 1369336Sdfr 1379336Sdfr /* 1389336Sdfr * TODO(XXX): after setting, we should reflect the changes to 1399336Sdfr * interface addresses, routing table entries, PCB entries... 1409336Sdfr */ 141129639Srwatson 1429336Sdfr for (i = 0; i < 16; i++) { 1439336Sdfr if (idlist->s6id_list[i] && 1449336Sdfr idlist->s6id_list[i] != sid->s6id_list[i]) { 1459336Sdfr /* 1469336Sdfr * An interface zone ID must be the corresponding 1479336Sdfr * interface index by definition. 1489336Sdfr */ 1499336Sdfr if (i == IPV6_ADDR_SCOPE_INTFACELOCAL && 1509336Sdfr idlist->s6id_list[i] != ifp->if_index) { 1519336Sdfr IF_AFDATA_UNLOCK(ifp); 1529336Sdfr return (EINVAL); 1539336Sdfr } 1549336Sdfr 1559336Sdfr if (i == IPV6_ADDR_SCOPE_LINKLOCAL && 1569336Sdfr idlist->s6id_list[i] > V_if_index) { 1579336Sdfr /* 1589336Sdfr * XXX: theoretically, there should be no 1599336Sdfr * relationship between link IDs and interface 1609336Sdfr * IDs, but we check the consistency for 1619336Sdfr * safety in later use. 1629336Sdfr */ 1639336Sdfr IF_AFDATA_UNLOCK(ifp); 1649336Sdfr return (EINVAL); 1659336Sdfr } 1669336Sdfr 1679336Sdfr /* 1689336Sdfr * XXX: we must need lots of work in this case, 169102236Sphk * but we simply set the new value in this initial 170102236Sphk * implementation. 1719336Sdfr */ 172129639Srwatson sid->s6id_list[i] = idlist->s6id_list[i]; 173102236Sphk } 174102236Sphk } 175102236Sphk IF_AFDATA_UNLOCK(ifp); 176102236Sphk 177102236Sphk return (error); 178102236Sphk} 179102236Sphk 180102236Sphkint 181102236Sphkscope6_get(struct ifnet *ifp, struct scope6_id *idlist) 182102236Sphk{ 183102236Sphk /* We only need to lock the interface's afdata for SID() to work. */ 184102236Sphk IF_AFDATA_LOCK(ifp); 185102236Sphk struct scope6_id *sid = SID(ifp); 186102236Sphk 187102236Sphk if (sid == NULL) { /* paranoid? */ 1889336Sdfr IF_AFDATA_UNLOCK(ifp); 1899336Sdfr return (EINVAL); 1909336Sdfr } 1919336Sdfr 1929336Sdfr *idlist = *sid; 1939336Sdfr 1949336Sdfr IF_AFDATA_UNLOCK(ifp); 1959336Sdfr return (0); 1969336Sdfr} 1979336Sdfr 1989336Sdfr 1999336Sdfr/* 2009336Sdfr * Get a scope of the address. Node-local, link-local, site-local or global. 201129639Srwatson */ 2029336Sdfrint 2039336Sdfrin6_addrscope(struct in6_addr *addr) 2049336Sdfr{ 2059336Sdfr int scope; 206129639Srwatson 2079336Sdfr if (addr->s6_addr[0] == 0xfe) { 2089336Sdfr scope = addr->s6_addr[1] & 0xc0; 2099336Sdfr 2109336Sdfr switch (scope) { 2119336Sdfr case 0x80: 2129336Sdfr return IPV6_ADDR_SCOPE_LINKLOCAL; 2139336Sdfr break; 2149336Sdfr case 0xc0: 215129639Srwatson return IPV6_ADDR_SCOPE_SITELOCAL; 2169336Sdfr break; 2179336Sdfr default: 2189336Sdfr return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */ 2199336Sdfr break; 2209336Sdfr } 2219336Sdfr } 2229336Sdfr 2239336Sdfr 2249336Sdfr if (addr->s6_addr[0] == 0xff) { 2259336Sdfr scope = addr->s6_addr[1] & 0x0f; 2269336Sdfr 2279336Sdfr /* 2289336Sdfr * due to other scope such as reserved, 2299336Sdfr * return scope doesn't work. 2309336Sdfr */ 231129639Srwatson switch (scope) { 2329336Sdfr case IPV6_ADDR_SCOPE_INTFACELOCAL: 2339336Sdfr return IPV6_ADDR_SCOPE_INTFACELOCAL; 2349336Sdfr break; 2359336Sdfr case IPV6_ADDR_SCOPE_LINKLOCAL: 2369336Sdfr return IPV6_ADDR_SCOPE_LINKLOCAL; 2379336Sdfr break; 2389336Sdfr case IPV6_ADDR_SCOPE_SITELOCAL: 2399336Sdfr return IPV6_ADDR_SCOPE_SITELOCAL; 2409336Sdfr break; 2419336Sdfr default: 2429336Sdfr return IPV6_ADDR_SCOPE_GLOBAL; 2439336Sdfr break; 244129639Srwatson } 2459336Sdfr } 2469336Sdfr 2479336Sdfr /* 2489336Sdfr * Regard loopback and unspecified addresses as global, since 2499336Sdfr * they have no ambiguity. 2509336Sdfr */ 2519336Sdfr if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) { 2529336Sdfr if (addr->s6_addr[15] == 1) /* loopback */ 253129639Srwatson return IPV6_ADDR_SCOPE_LINKLOCAL; 2549336Sdfr if (addr->s6_addr[15] == 0) /* unspecified */ 2559336Sdfr return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */ 2569336Sdfr } 2579336Sdfr 2589336Sdfr return IPV6_ADDR_SCOPE_GLOBAL; 2599336Sdfr} 2609336Sdfr 2619336Sdfr/* 2629336Sdfr * ifp - note that this might be NULL 2639336Sdfr */ 2649336Sdfr 265129639Srwatsonvoid 2669336Sdfrscope6_setdefault(struct ifnet *ifp) 2679336Sdfr{ 2689336Sdfr 2699336Sdfr /* 2709336Sdfr * Currently, this function just sets the default "interfaces" 2719336Sdfr * and "links" according to the given interface. 2729336Sdfr * We might eventually have to separate the notion of "link" from 2739336Sdfr * "interface" and provide a user interface to set the default. 2749336Sdfr */ 2759336Sdfr SCOPE6_LOCK(); 2769336Sdfr if (ifp) { 277129639Srwatson V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 2789336Sdfr ifp->if_index; 2799336Sdfr V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 2809336Sdfr ifp->if_index; 2819336Sdfr } else { 2829336Sdfr V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0; 2839336Sdfr V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0; 2849336Sdfr } 2859336Sdfr SCOPE6_UNLOCK(); 2869336Sdfr} 2879336Sdfr 2889336Sdfrint 2899336Sdfrscope6_get_default(struct scope6_id *idlist) 2909336Sdfr{ 2919336Sdfr 292129639Srwatson SCOPE6_LOCK(); 2939336Sdfr *idlist = V_sid_default; 2949336Sdfr SCOPE6_UNLOCK(); 2959336Sdfr 2969336Sdfr return (0); 2979336Sdfr} 2989336Sdfr 2999336Sdfru_int32_t 3009336Sdfrscope6_addr2default(struct in6_addr *addr) 3019336Sdfr{ 3029336Sdfr u_int32_t id; 3039336Sdfr 3049336Sdfr /* 3059336Sdfr * special case: The loopback address should be considered as 3069336Sdfr * link-local, but there's no ambiguity in the syntax. 3079336Sdfr */ 3089336Sdfr if (IN6_IS_ADDR_LOOPBACK(addr)) 309129639Srwatson return (0); 3109336Sdfr 3119336Sdfr /* 3129336Sdfr * XXX: 32-bit read is atomic on all our platforms, is it OK 3139336Sdfr * not to lock here? 3149336Sdfr */ 3159336Sdfr SCOPE6_LOCK(); 3169336Sdfr id = V_sid_default.s6id_list[in6_addrscope(addr)]; 3179336Sdfr SCOPE6_UNLOCK(); 3189336Sdfr return (id); 3199336Sdfr} 3209336Sdfr 3219336Sdfr/* 3229336Sdfr * Validate the specified scope zone ID in the sin6_scope_id field. If the ID 3239336Sdfr * is unspecified (=0), needs to be specified, and the default zone ID can be 3249336Sdfr * used, the default value will be used. 3259336Sdfr * This routine then generates the kernel-internal form: if the address scope 326129639Srwatson * of is interface-local or link-local, embed the interface index in the 3279336Sdfr * address. 3289336Sdfr */ 3299336Sdfrint 3309336Sdfrsa6_embedscope(struct sockaddr_in6 *sin6, int defaultok) 3319336Sdfr{ 3329336Sdfr struct ifnet *ifp; 3339336Sdfr u_int32_t zoneid; 3349336Sdfr 3359336Sdfr if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok) 3369336Sdfr zoneid = scope6_addr2default(&sin6->sin6_addr); 3379336Sdfr 3389336Sdfr if (zoneid != 0 && 3399336Sdfr (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) || 3409336Sdfr IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) { 3419336Sdfr /* 3429336Sdfr * At this moment, we only check interface-local and 343129639Srwatson * link-local scope IDs, and use interface indices as the 3449336Sdfr * zone IDs assuming a one-to-one mapping between interfaces 3459336Sdfr * and links. 3469336Sdfr */ 3479336Sdfr if (V_if_index < zoneid) 3489336Sdfr return (ENXIO); 3499336Sdfr ifp = ifnet_byindex(zoneid); 3509336Sdfr if (ifp == NULL) /* XXX: this can happen for some OS */ 3519336Sdfr return (ENXIO); 3529336Sdfr 3539336Sdfr /* XXX assignment to 16bit from 32bit variable */ 3549336Sdfr sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff); 3559336Sdfr 3569336Sdfr sin6->sin6_scope_id = 0; 3579336Sdfr } 3589336Sdfr 3599336Sdfr return 0; 3609336Sdfr} 361129639Srwatson 3629336Sdfr/* 3639336Sdfr * generate standard sockaddr_in6 from embedded form. 3649336Sdfr */ 3659336Sdfrint 3669336Sdfrsa6_recoverscope(struct sockaddr_in6 *sin6) 3679336Sdfr{ 3689336Sdfr char ip6buf[INET6_ADDRSTRLEN]; 3699336Sdfr u_int32_t zoneid; 3709336Sdfr 3719336Sdfr if (sin6->sin6_scope_id != 0) { 3729336Sdfr log(LOG_NOTICE, 3739336Sdfr "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n", 3749336Sdfr ip6_sprintf(ip6buf, &sin6->sin6_addr), sin6->sin6_scope_id); 375129639Srwatson /* XXX: proceed anyway... */ 3769336Sdfr } 3779336Sdfr if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) || 3789336Sdfr IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) { 3799336Sdfr /* 3809336Sdfr * KAME assumption: link id == interface id 3819336Sdfr */ 3829336Sdfr zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]); 3839336Sdfr if (zoneid) { 3849336Sdfr /* sanity check */ 3859336Sdfr if (V_if_index < zoneid) 3869336Sdfr return (ENXIO); 3879336Sdfr if (!ifnet_byindex(zoneid)) 3889336Sdfr return (ENXIO); 3899336Sdfr sin6->sin6_addr.s6_addr16[1] = 0; 3909336Sdfr sin6->sin6_scope_id = zoneid; 3919336Sdfr } 3929336Sdfr } 393129639Srwatson 3949336Sdfr return 0; 3959336Sdfr} 3969336Sdfr 3979336Sdfr/* 3989336Sdfr * Determine the appropriate scope zone ID for in6 and ifp. If ret_id is 3999336Sdfr * non NULL, it is set to the zone ID. If the zone ID needs to be embedded 4009336Sdfr * in the in6_addr structure, in6 will be modified. 4019336Sdfr * 4029336Sdfr * ret_id - unnecessary? 4039336Sdfr */ 4049336Sdfrint 4059336Sdfrin6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id) 4069336Sdfr{ 4079336Sdfr int scope; 4089336Sdfr u_int32_t zoneid = 0; 4099336Sdfr struct scope6_id *sid; 4109336Sdfr 4119336Sdfr IF_AFDATA_LOCK(ifp); 4129336Sdfr 4139336Sdfr sid = SID(ifp); 4149336Sdfr 4159336Sdfr#ifdef DIAGNOSTIC 416129639Srwatson if (sid == NULL) { /* should not happen */ 4179336Sdfr panic("in6_setscope: scope array is NULL"); 4189336Sdfr /* NOTREACHED */ 4199336Sdfr } 4209336Sdfr#endif 4219336Sdfr 4229336Sdfr /* 4239336Sdfr * special case: the loopback address can only belong to a loopback 4249336Sdfr * interface. 4259336Sdfr */ 4269336Sdfr if (IN6_IS_ADDR_LOOPBACK(in6)) { 4279336Sdfr if (!(ifp->if_flags & IFF_LOOPBACK)) { 4289336Sdfr IF_AFDATA_UNLOCK(ifp); 4299336Sdfr return (EINVAL); 4309336Sdfr } else { 4319336Sdfr if (ret_id != NULL) 4329336Sdfr *ret_id = 0; /* there's no ambiguity */ 4339336Sdfr IF_AFDATA_UNLOCK(ifp); 4349336Sdfr return (0); 4359336Sdfr } 436129639Srwatson } 4379336Sdfr 4389336Sdfr scope = in6_addrscope(in6); 4399336Sdfr switch (scope) { 4409336Sdfr case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */ 4419336Sdfr zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL]; 4429336Sdfr break; 4439336Sdfr 4449336Sdfr case IPV6_ADDR_SCOPE_LINKLOCAL: 4459336Sdfr zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]; 4469336Sdfr break; 4479336Sdfr 4489336Sdfr case IPV6_ADDR_SCOPE_SITELOCAL: 449129639Srwatson zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]; 4509336Sdfr break; 4519336Sdfr 4529336Sdfr case IPV6_ADDR_SCOPE_ORGLOCAL: 4539336Sdfr zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]; 4549336Sdfr break; 4559336Sdfr 4569336Sdfr default: 4579336Sdfr zoneid = 0; /* XXX: treat as global. */ 4589336Sdfr break; 4599336Sdfr } 4609336Sdfr IF_AFDATA_UNLOCK(ifp); 4619336Sdfr 4629336Sdfr if (ret_id != NULL) 463129639Srwatson *ret_id = zoneid; 4649336Sdfr 4659336Sdfr if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) 4669336Sdfr in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */ 4679336Sdfr 4689336Sdfr return (0); 4699336Sdfr} 4709336Sdfr 4719336Sdfr/* 472129639Srwatson * Just clear the embedded scope identifier. Return 0 if the original address 4739336Sdfr * is intact; return non 0 if the address is modified. 4749336Sdfr */ 4759336Sdfrint 4769336Sdfrin6_clearscope(struct in6_addr *in6) 4779336Sdfr{ 4789336Sdfr int modified = 0; 4799336Sdfr 480129639Srwatson if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) { 4819336Sdfr if (in6->s6_addr16[1] != 0) 4829336Sdfr modified = 1; 4839336Sdfr in6->s6_addr16[1] = 0; 4849336Sdfr } 4859336Sdfr 4869336Sdfr return (modified); 4879336Sdfr} 488129639Srwatson 4899336Sdfr/* 4909336Sdfr * Return the scope identifier or zero. 4919336Sdfr */ 4929336Sdfruint16_t 4939336Sdfrin6_getscope(struct in6_addr *in6) 4949336Sdfr{ 4959336Sdfr 4969336Sdfr if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) 497129639Srwatson return (in6->s6_addr16[1]); 4989336Sdfr 4999336Sdfr return (0); 5009336Sdfr} 5019336Sdfr