1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (C) 2000 WIDE Project. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the project nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * $KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $ 32 */ 33 34 #include <sys/cdefs.h> 35 #include <sys/param.h> 36 #include <sys/malloc.h> 37 #include <sys/mbuf.h> 38 #include <sys/socket.h> 39 #include <sys/sockio.h> 40 #include <sys/systm.h> 41 #include <sys/queue.h> 42 #include <sys/sysctl.h> 43 #include <sys/syslog.h> 44 45 #include <net/if.h> 46 #include <net/if_var.h> 47 #include <net/if_private.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/ip6_var.h> 55 #include <netinet6/scope6_var.h> 56 57 #ifdef ENABLE_DEFAULT_SCOPE 58 VNET_DEFINE(int, ip6_use_defzone) = 1; 59 #else 60 VNET_DEFINE(int, ip6_use_defzone) = 0; 61 #endif 62 SYSCTL_DECL(_net_inet6_ip6); 63 64 /* 65 * The scope6_lock protects the global sid default stored in 66 * sid_default below. 67 */ 68 static struct mtx scope6_lock; 69 #define SCOPE6_LOCK_INIT() mtx_init(&scope6_lock, "scope6_lock", NULL, MTX_DEF) 70 #define SCOPE6_LOCK() mtx_lock(&scope6_lock) 71 #define SCOPE6_UNLOCK() mtx_unlock(&scope6_lock) 72 #define SCOPE6_LOCK_ASSERT() mtx_assert(&scope6_lock, MA_OWNED) 73 74 VNET_DEFINE_STATIC(struct scope6_id, sid_default); 75 #define V_sid_default VNET(sid_default) 76 77 #define SID(ifp) \ 78 (((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->scope6_id) 79 80 static int scope6_get(struct ifnet *, struct scope6_id *); 81 static int scope6_set(struct ifnet *, struct scope6_id *); 82 83 void 84 scope6_init(void) 85 { 86 87 bzero(&V_sid_default, sizeof(V_sid_default)); 88 89 if (!IS_DEFAULT_VNET(curvnet)) 90 return; 91 92 SCOPE6_LOCK_INIT(); 93 } 94 95 struct scope6_id * 96 scope6_ifattach(struct ifnet *ifp) 97 { 98 struct scope6_id *sid; 99 100 sid = malloc(sizeof(*sid), M_IFADDR, M_WAITOK | M_ZERO); 101 /* 102 * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard. 103 * Should we rather hardcode here? 104 */ 105 sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index; 106 sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index; 107 return (sid); 108 } 109 110 void 111 scope6_ifdetach(struct scope6_id *sid) 112 { 113 114 free(sid, M_IFADDR); 115 } 116 117 int 118 scope6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp) 119 { 120 struct in6_ifreq *ifr; 121 122 if (ifp->if_afdata[AF_INET6] == NULL) 123 return (EPFNOSUPPORT); 124 125 ifr = (struct in6_ifreq *)data; 126 switch (cmd) { 127 case SIOCSSCOPE6: 128 return (scope6_set(ifp, 129 (struct scope6_id *)ifr->ifr_ifru.ifru_scope_id)); 130 case SIOCGSCOPE6: 131 return (scope6_get(ifp, 132 (struct scope6_id *)ifr->ifr_ifru.ifru_scope_id)); 133 case SIOCGSCOPE6DEF: 134 return (scope6_get_default( 135 (struct scope6_id *)ifr->ifr_ifru.ifru_scope_id)); 136 default: 137 return (EOPNOTSUPP); 138 } 139 } 140 141 static int 142 scope6_set(struct ifnet *ifp, struct scope6_id *idlist) 143 { 144 int i; 145 int error = 0; 146 struct scope6_id *sid = NULL; 147 148 IF_AFDATA_WLOCK(ifp); 149 sid = SID(ifp); 150 151 if (!sid) { /* paranoid? */ 152 IF_AFDATA_WUNLOCK(ifp); 153 return (EINVAL); 154 } 155 156 /* 157 * XXX: We need more consistency checks of the relationship among 158 * scopes (e.g. an organization should be larger than a site). 159 */ 160 161 /* 162 * TODO(XXX): after setting, we should reflect the changes to 163 * interface addresses, routing table entries, PCB entries... 164 */ 165 166 for (i = 0; i < 16; i++) { 167 if (idlist->s6id_list[i] && 168 idlist->s6id_list[i] != sid->s6id_list[i]) { 169 /* 170 * An interface zone ID must be the corresponding 171 * interface index by definition. 172 */ 173 if (i == IPV6_ADDR_SCOPE_INTFACELOCAL && 174 idlist->s6id_list[i] != ifp->if_index) { 175 IF_AFDATA_WUNLOCK(ifp); 176 return (EINVAL); 177 } 178 179 if (i == IPV6_ADDR_SCOPE_LINKLOCAL) { 180 struct epoch_tracker et; 181 182 NET_EPOCH_ENTER(et); 183 if (!ifnet_byindex(idlist->s6id_list[i])) { 184 /* 185 * XXX: theoretically, there should be 186 * no relationship between link IDs and 187 * interface IDs, but we check the 188 * consistency for safety in later use. 189 */ 190 NET_EPOCH_EXIT(et); 191 IF_AFDATA_WUNLOCK(ifp); 192 return (EINVAL); 193 } 194 NET_EPOCH_EXIT(et); 195 } 196 197 /* 198 * XXX: we must need lots of work in this case, 199 * but we simply set the new value in this initial 200 * implementation. 201 */ 202 sid->s6id_list[i] = idlist->s6id_list[i]; 203 } 204 } 205 IF_AFDATA_WUNLOCK(ifp); 206 207 return (error); 208 } 209 210 static int 211 scope6_get(struct ifnet *ifp, struct scope6_id *idlist) 212 { 213 struct epoch_tracker et; 214 struct scope6_id *sid; 215 216 /* We only need to lock the interface's afdata for SID() to work. */ 217 NET_EPOCH_ENTER(et); 218 sid = SID(ifp); 219 if (sid == NULL) { /* paranoid? */ 220 NET_EPOCH_EXIT(et); 221 return (EINVAL); 222 } 223 224 *idlist = *sid; 225 226 NET_EPOCH_EXIT(et); 227 return (0); 228 } 229 230 /* 231 * Get a scope of the address. Node-local, link-local, site-local or global. 232 */ 233 int 234 in6_addrscope(const struct in6_addr *addr) 235 { 236 237 if (IN6_IS_ADDR_MULTICAST(addr)) { 238 /* 239 * Addresses with reserved value F must be treated as 240 * global multicast addresses. 241 */ 242 if (IPV6_ADDR_MC_SCOPE(addr) == 0x0f) 243 return (IPV6_ADDR_SCOPE_GLOBAL); 244 return (IPV6_ADDR_MC_SCOPE(addr)); 245 } 246 if (IN6_IS_ADDR_LINKLOCAL(addr) || 247 IN6_IS_ADDR_LOOPBACK(addr)) 248 return (IPV6_ADDR_SCOPE_LINKLOCAL); 249 if (IN6_IS_ADDR_SITELOCAL(addr)) 250 return (IPV6_ADDR_SCOPE_SITELOCAL); 251 return (IPV6_ADDR_SCOPE_GLOBAL); 252 } 253 254 /* 255 * ifp - note that this might be NULL 256 */ 257 258 void 259 scope6_setdefault(struct ifnet *ifp) 260 { 261 262 /* 263 * Currently, this function just sets the default "interfaces" 264 * and "links" according to the given interface. 265 * We might eventually have to separate the notion of "link" from 266 * "interface" and provide a user interface to set the default. 267 */ 268 SCOPE6_LOCK(); 269 if (ifp) { 270 V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 271 ifp->if_index; 272 V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 273 ifp->if_index; 274 } else { 275 V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0; 276 V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0; 277 } 278 SCOPE6_UNLOCK(); 279 } 280 281 int 282 scope6_get_default(struct scope6_id *idlist) 283 { 284 285 SCOPE6_LOCK(); 286 *idlist = V_sid_default; 287 SCOPE6_UNLOCK(); 288 289 return (0); 290 } 291 292 u_int32_t 293 scope6_addr2default(struct in6_addr *addr) 294 { 295 u_int32_t id; 296 297 /* 298 * special case: The loopback address should be considered as 299 * link-local, but there's no ambiguity in the syntax. 300 */ 301 if (IN6_IS_ADDR_LOOPBACK(addr)) 302 return (0); 303 304 /* 305 * XXX: 32-bit read is atomic on all our platforms, is it OK 306 * not to lock here? 307 */ 308 SCOPE6_LOCK(); 309 id = V_sid_default.s6id_list[in6_addrscope(addr)]; 310 SCOPE6_UNLOCK(); 311 return (id); 312 } 313 314 /* 315 * Validate the specified scope zone ID in the sin6_scope_id field. If the ID 316 * is unspecified (=0), needs to be specified, and the default zone ID can be 317 * used, the default value will be used. 318 * This routine then generates the kernel-internal form: if the address scope 319 * of is interface-local or link-local, embed the interface index in the 320 * address. 321 */ 322 int 323 sa6_embedscope(struct sockaddr_in6 *sin6, int defaultok) 324 { 325 u_int32_t zoneid; 326 327 if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok) 328 zoneid = scope6_addr2default(&sin6->sin6_addr); 329 330 if (zoneid != 0 && 331 (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) || 332 IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) { 333 struct epoch_tracker et; 334 335 /* 336 * At this moment, we only check interface-local and 337 * link-local scope IDs, and use interface indices as the 338 * zone IDs assuming a one-to-one mapping between interfaces 339 * and links. 340 */ 341 NET_EPOCH_ENTER(et); 342 if (ifnet_byindex(zoneid) == NULL) { 343 NET_EPOCH_EXIT(et); 344 return (ENXIO); 345 } 346 NET_EPOCH_EXIT(et); 347 348 /* XXX assignment to 16bit from 32bit variable */ 349 sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff); 350 sin6->sin6_scope_id = 0; 351 } 352 353 return 0; 354 } 355 356 /* 357 * generate standard sockaddr_in6 from embedded form. 358 */ 359 int 360 sa6_recoverscope(struct sockaddr_in6 *sin6) 361 { 362 char ip6buf[INET6_ADDRSTRLEN]; 363 u_int32_t zoneid; 364 365 if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) || 366 IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) { 367 /* 368 * KAME assumption: link id == interface id 369 */ 370 zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]); 371 if (zoneid) { 372 struct epoch_tracker et; 373 374 NET_EPOCH_ENTER(et); 375 /* sanity check */ 376 if (!ifnet_byindex(zoneid)) { 377 NET_EPOCH_EXIT(et); 378 return (ENXIO); 379 } 380 NET_EPOCH_EXIT(et); 381 if (sin6->sin6_scope_id != 0 && 382 zoneid != sin6->sin6_scope_id) { 383 log(LOG_NOTICE, 384 "%s: embedded scope mismatch: %s%%%d. " 385 "sin6_scope_id was overridden\n", __func__, 386 ip6_sprintf(ip6buf, &sin6->sin6_addr), 387 sin6->sin6_scope_id); 388 } 389 sin6->sin6_addr.s6_addr16[1] = 0; 390 sin6->sin6_scope_id = zoneid; 391 } 392 } 393 394 return 0; 395 } 396 397 /* 398 * Determine the appropriate scope zone ID for in6 and ifp. If ret_id is 399 * non NULL, it is set to the zone ID. If the zone ID needs to be embedded 400 * in the in6_addr structure, in6 will be modified. 401 * 402 * ret_id - unnecessary? 403 */ 404 int 405 in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id) 406 { 407 int scope; 408 u_int32_t zoneid = 0; 409 struct scope6_id *sid; 410 411 /* 412 * special case: the loopback address can only belong to a loopback 413 * interface. 414 */ 415 if (IN6_IS_ADDR_LOOPBACK(in6)) { 416 if (!(ifp->if_flags & IFF_LOOPBACK)) 417 return (EINVAL); 418 } else { 419 scope = in6_addrscope(in6); 420 if (scope == IPV6_ADDR_SCOPE_INTFACELOCAL || 421 scope == IPV6_ADDR_SCOPE_LINKLOCAL) { 422 /* 423 * Currently we use interface indices as the 424 * zone IDs for interface-local and link-local 425 * scopes. 426 */ 427 zoneid = ifp->if_index; 428 in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */ 429 } else if (scope != IPV6_ADDR_SCOPE_GLOBAL) { 430 struct epoch_tracker et; 431 432 NET_EPOCH_ENTER(et); 433 if (ifp->if_afdata[AF_INET6] == NULL) { 434 NET_EPOCH_EXIT(et); 435 return (ENETDOWN); 436 } 437 sid = SID(ifp); 438 zoneid = sid->s6id_list[scope]; 439 NET_EPOCH_EXIT(et); 440 } 441 } 442 443 if (ret_id != NULL) 444 *ret_id = zoneid; 445 446 return (0); 447 } 448 449 /* 450 * Just clear the embedded scope identifier. Return 0 if the original address 451 * is intact; return non 0 if the address is modified. 452 */ 453 int 454 in6_clearscope(struct in6_addr *in6) 455 { 456 int modified = 0; 457 458 if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) { 459 if (in6->s6_addr16[1] != 0) 460 modified = 1; 461 in6->s6_addr16[1] = 0; 462 } 463 464 return (modified); 465 } 466 467 /* 468 * Return the scope identifier or zero. 469 */ 470 uint16_t 471 in6_getscope(const struct in6_addr *in6) 472 { 473 474 if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) 475 return (in6->s6_addr16[1]); 476 477 return (0); 478 } 479 480 /* 481 * Returns scope zone id for the unicast address @in6. 482 * 483 * Returns 0 for global unicast and loopback addresses. 484 * Returns interface index for the link-local addresses. 485 */ 486 uint32_t 487 in6_get_unicast_scopeid(const struct in6_addr *in6, const struct ifnet *ifp) 488 { 489 490 if (IN6_IS_SCOPE_LINKLOCAL(in6)) 491 return (ifp->if_index); 492 return (0); 493 } 494 495 void 496 in6_set_unicast_scopeid(struct in6_addr *in6, uint32_t scopeid) 497 { 498 499 in6->s6_addr16[1] = htons(scopeid & 0xffff); 500 } 501 502 /* 503 * Return pointer to ifnet structure, corresponding to the zone id of 504 * link-local scope. 505 */ 506 struct ifnet* 507 in6_getlinkifnet(uint32_t zoneid) 508 { 509 510 return (ifnet_byindex((u_short)zoneid)); 511 } 512 513 /* 514 * Return zone id for the specified scope. 515 */ 516 uint32_t 517 in6_getscopezone(const struct ifnet *ifp, int scope) 518 { 519 520 if (scope == IPV6_ADDR_SCOPE_INTFACELOCAL || 521 scope == IPV6_ADDR_SCOPE_LINKLOCAL) 522 return (ifp->if_index); 523 if (scope >= 0 && scope < IPV6_ADDR_SCOPES_COUNT) 524 return (SID(ifp)->s6id_list[scope]); 525 return (0); 526 } 527 528 /* 529 * Extracts scope from adddress @dst, stores cleared address 530 * inside @dst and zone inside @scopeid 531 */ 532 void 533 in6_splitscope(const struct in6_addr *src, struct in6_addr *dst, 534 uint32_t *scopeid) 535 { 536 uint32_t zoneid; 537 538 *dst = *src; 539 zoneid = ntohs(in6_getscope(dst)); 540 in6_clearscope(dst); 541 *scopeid = zoneid; 542 } 543 544 /* 545 * This function is for checking sockaddr_in6 structure passed 546 * from the application level (usually). 547 * 548 * sin6_scope_id should be set for link-local unicast, link-local and 549 * interface-local multicast addresses. 550 * 551 * If it is zero, then look into default zone ids. If default zone id is 552 * not set or disabled, then return error. 553 */ 554 int 555 sa6_checkzone(struct sockaddr_in6 *sa6) 556 { 557 int scope; 558 559 scope = in6_addrscope(&sa6->sin6_addr); 560 if (scope == IPV6_ADDR_SCOPE_GLOBAL) 561 return (sa6->sin6_scope_id ? EINVAL: 0); 562 if (IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr) && 563 scope != IPV6_ADDR_SCOPE_LINKLOCAL && 564 scope != IPV6_ADDR_SCOPE_INTFACELOCAL) { 565 if (sa6->sin6_scope_id == 0 && V_ip6_use_defzone != 0) 566 sa6->sin6_scope_id = V_sid_default.s6id_list[scope]; 567 return (0); 568 } 569 /* 570 * Since ::1 address always configured on the lo0, we can 571 * automatically set its zone id, when it is not specified. 572 * Return error, when specified zone id doesn't match with 573 * actual value. 574 */ 575 if (IN6_IS_ADDR_LOOPBACK(&sa6->sin6_addr)) { 576 if (sa6->sin6_scope_id == 0) 577 sa6->sin6_scope_id = in6_getscopezone(V_loif, scope); 578 else if (sa6->sin6_scope_id != in6_getscopezone(V_loif, scope)) 579 return (EADDRNOTAVAIL); 580 } 581 /* XXX: we can validate sin6_scope_id here */ 582 if (sa6->sin6_scope_id != 0) 583 return (0); 584 if (V_ip6_use_defzone != 0) 585 sa6->sin6_scope_id = V_sid_default.s6id_list[scope]; 586 /* Return error if we can't determine zone id */ 587 return (sa6->sin6_scope_id ? 0: EADDRNOTAVAIL); 588 } 589 590 /* 591 * This function is similar to sa6_checkzone, but it uses given ifp 592 * to initialize sin6_scope_id. 593 */ 594 int 595 sa6_checkzone_ifp(struct ifnet *ifp, struct sockaddr_in6 *sa6) 596 { 597 int scope; 598 599 scope = in6_addrscope(&sa6->sin6_addr); 600 if (scope == IPV6_ADDR_SCOPE_LINKLOCAL || 601 scope == IPV6_ADDR_SCOPE_INTFACELOCAL) { 602 if (sa6->sin6_scope_id == 0) { 603 sa6->sin6_scope_id = in6_getscopezone(ifp, scope); 604 return (0); 605 } else if (sa6->sin6_scope_id != in6_getscopezone(ifp, scope)) 606 return (EADDRNOTAVAIL); 607 } 608 return (sa6_checkzone(sa6)); 609 } 610