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