1 /* $FreeBSD$ */ 2 /* $KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $ */ 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/param.h> 34 #include <sys/malloc.h> 35 #include <sys/mbuf.h> 36 #include <sys/socket.h> 37 #include <sys/systm.h> 38 #include <sys/queue.h> 39 #include <sys/syslog.h> 40 41 #include <net/route.h> 42 #include <net/if.h> 43 44 #include <netinet/in.h> 45 46 #include <netinet6/in6_var.h> 47 #include <netinet6/scope6_var.h> 48 49 #ifdef ENABLE_DEFAULT_SCOPE 50 int ip6_use_defzone = 1; 51 #else 52 int ip6_use_defzone = 0; 53 #endif 54 55 /* 56 * The scope6_lock protects the global sid default stored in 57 * sid_default below. 58 */ 59 static struct mtx scope6_lock; 60 #define SCOPE6_LOCK_INIT() mtx_init(&scope6_lock, "scope6_lock", NULL, MTX_DEF) 61 #define SCOPE6_LOCK() mtx_lock(&scope6_lock) 62 #define SCOPE6_UNLOCK() mtx_unlock(&scope6_lock) 63 #define SCOPE6_LOCK_ASSERT() mtx_assert(&scope6_lock, MA_OWNED) 64 65 static struct scope6_id sid_default; 66 #define SID(ifp) \ 67 (((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->scope6_id) 68 69 void 70 scope6_init() 71 { 72 73 SCOPE6_LOCK_INIT(); 74 bzero(&sid_default, sizeof(sid_default)); 75 } 76 77 struct scope6_id * 78 scope6_ifattach(ifp) 79 struct ifnet *ifp; 80 { 81 struct scope6_id *sid; 82 83 sid = (struct scope6_id *)malloc(sizeof(*sid), M_IFADDR, M_WAITOK); 84 bzero(sid, sizeof(*sid)); 85 86 /* 87 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard. 88 * Should we rather hardcode here? 89 */ 90 sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index; 91 sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index; 92 #ifdef MULTI_SCOPE 93 /* by default, we don't care about scope boundary for these scopes. */ 94 sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1; 95 sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1; 96 #endif 97 98 return sid; 99 } 100 101 void 102 scope6_ifdetach(sid) 103 struct scope6_id *sid; 104 { 105 106 free(sid, M_IFADDR); 107 } 108 109 int 110 scope6_set(ifp, idlist) 111 struct ifnet *ifp; 112 struct scope6_id *idlist; 113 { 114 int i; 115 int error = 0; 116 struct scope6_id *sid = NULL; 117 118 IF_AFDATA_LOCK(ifp); 119 sid = SID(ifp); 120 121 if (!sid) { /* paranoid? */ 122 IF_AFDATA_UNLOCK(ifp); 123 return (EINVAL); 124 } 125 126 /* 127 * XXX: We need more consistency checks of the relationship among 128 * scopes (e.g. an organization should be larger than a site). 129 */ 130 131 /* 132 * TODO(XXX): after setting, we should reflect the changes to 133 * interface addresses, routing table entries, PCB entries... 134 */ 135 136 SCOPE6_LOCK(); 137 for (i = 0; i < 16; i++) { 138 if (idlist->s6id_list[i] && 139 idlist->s6id_list[i] != sid->s6id_list[i]) { 140 /* 141 * An interface zone ID must be the corresponding 142 * interface index by definition. 143 */ 144 if (i == IPV6_ADDR_SCOPE_INTFACELOCAL && 145 idlist->s6id_list[i] != ifp->if_index) { 146 IF_AFDATA_UNLOCK(ifp); 147 SCOPE6_UNLOCK(); 148 return (EINVAL); 149 } 150 151 if (i == IPV6_ADDR_SCOPE_LINKLOCAL && 152 idlist->s6id_list[i] > if_index) { 153 /* 154 * XXX: theoretically, there should be no 155 * relationship between link IDs and interface 156 * IDs, but we check the consistency for 157 * safety in later use. 158 */ 159 IF_AFDATA_UNLOCK(ifp); 160 SCOPE6_UNLOCK(); 161 return (EINVAL); 162 } 163 164 /* 165 * XXX: we must need lots of work in this case, 166 * but we simply set the new value in this initial 167 * implementation. 168 */ 169 sid->s6id_list[i] = idlist->s6id_list[i]; 170 } 171 } 172 SCOPE6_UNLOCK(); 173 IF_AFDATA_UNLOCK(ifp); 174 175 return (error); 176 } 177 178 int 179 scope6_get(ifp, idlist) 180 struct ifnet *ifp; 181 struct scope6_id *idlist; 182 { 183 /* We only need to lock the interface's afdata for SID() to work. */ 184 IF_AFDATA_LOCK(ifp); 185 struct scope6_id *sid = SID(ifp); 186 187 if (sid == NULL) { /* paranoid? */ 188 IF_AFDATA_UNLOCK(ifp); 189 return (EINVAL); 190 } 191 192 SCOPE6_LOCK(); 193 *idlist = *sid; 194 SCOPE6_UNLOCK(); 195 196 IF_AFDATA_UNLOCK(ifp); 197 return (0); 198 } 199 200 201 /* 202 * Get a scope of the address. Node-local, link-local, site-local or global. 203 */ 204 int 205 in6_addrscope(addr) 206 struct in6_addr *addr; 207 { 208 int scope; 209 210 if (addr->s6_addr[0] == 0xfe) { 211 scope = addr->s6_addr[1] & 0xc0; 212 213 switch (scope) { 214 case 0x80: 215 return IPV6_ADDR_SCOPE_LINKLOCAL; 216 break; 217 case 0xc0: 218 return IPV6_ADDR_SCOPE_SITELOCAL; 219 break; 220 default: 221 return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */ 222 break; 223 } 224 } 225 226 227 if (addr->s6_addr[0] == 0xff) { 228 scope = addr->s6_addr[1] & 0x0f; 229 230 /* 231 * due to other scope such as reserved, 232 * return scope doesn't work. 233 */ 234 switch (scope) { 235 case IPV6_ADDR_SCOPE_INTFACELOCAL: 236 return IPV6_ADDR_SCOPE_INTFACELOCAL; 237 break; 238 case IPV6_ADDR_SCOPE_LINKLOCAL: 239 return IPV6_ADDR_SCOPE_LINKLOCAL; 240 break; 241 case IPV6_ADDR_SCOPE_SITELOCAL: 242 return IPV6_ADDR_SCOPE_SITELOCAL; 243 break; 244 default: 245 return IPV6_ADDR_SCOPE_GLOBAL; 246 break; 247 } 248 } 249 250 /* 251 * Regard loopback and unspecified addresses as global, since 252 * they have no ambiguity. 253 */ 254 if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) { 255 if (addr->s6_addr[15] == 1) /* loopback */ 256 return IPV6_ADDR_SCOPE_LINKLOCAL; 257 if (addr->s6_addr[15] == 0) /* unspecified */ 258 return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */ 259 } 260 261 return IPV6_ADDR_SCOPE_GLOBAL; 262 } 263 264 void 265 scope6_setdefault(ifp) 266 struct ifnet *ifp; /* note that this might be NULL */ 267 { 268 /* 269 * Currently, this function just sets the default "interfaces" 270 * and "links" according to the given interface. 271 * We might eventually have to separate the notion of "link" from 272 * "interface" and provide a user interface to set the default. 273 */ 274 SCOPE6_LOCK(); 275 if (ifp) { 276 sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 277 ifp->if_index; 278 sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 279 ifp->if_index; 280 } else { 281 sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0; 282 sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0; 283 } 284 SCOPE6_UNLOCK(); 285 } 286 287 int 288 scope6_get_default(idlist) 289 struct scope6_id *idlist; 290 { 291 292 SCOPE6_LOCK(); 293 *idlist = sid_default; 294 SCOPE6_UNLOCK(); 295 296 return (0); 297 } 298 299 u_int32_t 300 scope6_addr2default(addr) 301 struct in6_addr *addr; 302 { 303 u_int32_t id; 304 305 /* 306 * special case: The loopback address should be considered as 307 * link-local, but there's no ambiguity in the syntax. 308 */ 309 if (IN6_IS_ADDR_LOOPBACK(addr)) 310 return (0); 311 312 /* 313 * XXX: 32-bit read is atomic on all our platforms, is it OK 314 * not to lock here? 315 */ 316 SCOPE6_LOCK(); 317 id = sid_default.s6id_list[in6_addrscope(addr)]; 318 SCOPE6_UNLOCK(); 319 return (id); 320 } 321 322 /* 323 * Validate the specified scope zone ID in the sin6_scope_id field. If the ID 324 * is unspecified (=0), needs to be specified, and the default zone ID can be 325 * used, the default value will be used. 326 * This routine then generates the kernel-internal form: if the address scope 327 * of is interface-local or link-local, embed the interface index in the 328 * address. 329 */ 330 int 331 sa6_embedscope(sin6, defaultok) 332 struct sockaddr_in6 *sin6; 333 int defaultok; 334 { 335 struct ifnet *ifp; 336 u_int32_t zoneid; 337 338 if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok) 339 zoneid = scope6_addr2default(&sin6->sin6_addr); 340 341 if (zoneid != 0 && 342 (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) || 343 IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) { 344 /* 345 * At this moment, we only check interface-local and 346 * link-local scope IDs, and use interface indices as the 347 * zone IDs assuming a one-to-one mapping between interfaces 348 * and links. 349 */ 350 if (if_index < zoneid) 351 return (ENXIO); 352 ifp = ifnet_byindex(zoneid); 353 if (ifp == NULL) /* XXX: this can happen for some OS */ 354 return (ENXIO); 355 356 /* XXX assignment to 16bit from 32bit variable */ 357 sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff); 358 359 sin6->sin6_scope_id = 0; 360 } 361 362 return 0; 363 } 364 365 /* 366 * generate standard sockaddr_in6 from embedded form. 367 */ 368 int 369 sa6_recoverscope(sin6) 370 struct sockaddr_in6 *sin6; 371 { 372 char ip6buf[INET6_ADDRSTRLEN]; 373 u_int32_t zoneid; 374 375 if (sin6->sin6_scope_id != 0) { 376 log(LOG_NOTICE, 377 "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n", 378 ip6_sprintf(ip6buf, &sin6->sin6_addr), sin6->sin6_scope_id); 379 /* XXX: proceed anyway... */ 380 } 381 if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) || 382 IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) { 383 /* 384 * KAME assumption: link id == interface id 385 */ 386 zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]); 387 if (zoneid) { 388 /* sanity check */ 389 if (zoneid < 0 || if_index < zoneid) 390 return (ENXIO); 391 if (!ifnet_byindex(zoneid)) 392 return (ENXIO); 393 sin6->sin6_addr.s6_addr16[1] = 0; 394 sin6->sin6_scope_id = zoneid; 395 } 396 } 397 398 return 0; 399 } 400 401 /* 402 * Determine the appropriate scope zone ID for in6 and ifp. If ret_id is 403 * non NULL, it is set to the zone ID. If the zone ID needs to be embedded 404 * in the in6_addr structure, in6 will be modified. 405 */ 406 int 407 in6_setscope(in6, ifp, ret_id) 408 struct in6_addr *in6; 409 struct ifnet *ifp; 410 u_int32_t *ret_id; /* unnecessary? */ 411 { 412 int scope; 413 u_int32_t zoneid = 0; 414 struct scope6_id *sid; 415 416 IF_AFDATA_LOCK(ifp); 417 418 sid = SID(ifp); 419 420 #ifdef DIAGNOSTIC 421 if (sid == NULL) { /* should not happen */ 422 panic("in6_setscope: scope array is NULL"); 423 /* NOTREACHED */ 424 } 425 #endif 426 427 /* 428 * special case: the loopback address can only belong to a loopback 429 * interface. 430 */ 431 if (IN6_IS_ADDR_LOOPBACK(in6)) { 432 if (!(ifp->if_flags & IFF_LOOPBACK)) { 433 IF_AFDATA_UNLOCK(ifp); 434 return (EINVAL); 435 } else { 436 if (ret_id != NULL) 437 *ret_id = 0; /* there's no ambiguity */ 438 IF_AFDATA_UNLOCK(ifp); 439 return (0); 440 } 441 } 442 443 scope = in6_addrscope(in6); 444 445 SCOPE6_LOCK(); 446 switch (scope) { 447 case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */ 448 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL]; 449 break; 450 451 case IPV6_ADDR_SCOPE_LINKLOCAL: 452 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]; 453 break; 454 455 case IPV6_ADDR_SCOPE_SITELOCAL: 456 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]; 457 break; 458 459 case IPV6_ADDR_SCOPE_ORGLOCAL: 460 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]; 461 break; 462 463 default: 464 zoneid = 0; /* XXX: treat as global. */ 465 break; 466 } 467 SCOPE6_UNLOCK(); 468 IF_AFDATA_UNLOCK(ifp); 469 470 if (ret_id != NULL) 471 *ret_id = zoneid; 472 473 if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) 474 in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */ 475 476 return (0); 477 } 478 479 /* 480 * Just clear the embedded scope identifier. Return 0 if the original address 481 * is intact; return non 0 if the address is modified. 482 */ 483 int 484 in6_clearscope(in6) 485 struct in6_addr *in6; 486 { 487 int modified = 0; 488 489 if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) { 490 if (in6->s6_addr16[1] != 0) 491 modified = 1; 492 in6->s6_addr16[1] = 0; 493 } 494 495 return (modified); 496 } 497