in6_ifattach.c revision 62587
1/* $FreeBSD: head/sys/netinet6/in6_ifattach.c 62587 2000-07-04 16:35:15Z itojun $ */ 2/* $KAME: in6_ifattach.c,v 1.61 2000/06/13 08:15:27 itojun Exp $ */ 3 4/* 5 * Copyright (C) 1995, 1996, 1997, and 1998 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/systm.h> 35#include <sys/malloc.h> 36#include <sys/socket.h> 37#include <sys/sockio.h> 38#include <sys/kernel.h> 39#include <sys/md5.h> 40 41#include <net/if.h> 42#include <net/if_dl.h> 43#include <net/if_types.h> 44#include <net/route.h> 45 46#include <netinet/in.h> 47#include <netinet/in_var.h> 48#include <netinet/if_ether.h> 49 50#include <netinet/ip6.h> 51#include <netinet6/ip6_var.h> 52#include <netinet6/in6_ifattach.h> 53#include <netinet6/ip6_var.h> 54#include <netinet6/nd6.h> 55#include <netinet6/scope6_var.h> 56 57#include <net/net_osdep.h> 58 59struct in6_ifstat **in6_ifstat = NULL; 60struct icmp6_ifstat **icmp6_ifstat = NULL; 61size_t in6_ifstatmax = 0; 62size_t icmp6_ifstatmax = 0; 63unsigned long in6_maxmtu = 0; 64 65static int get_rand_ifid __P((struct ifnet *, struct in6_addr *)); 66static int get_hw_ifid __P((struct ifnet *, struct in6_addr *)); 67static int get_ifid __P((struct ifnet *, struct ifnet *, struct in6_addr *)); 68static int in6_ifattach_addaddr __P((struct ifnet *, struct in6_ifaddr *)); 69static int in6_ifattach_linklocal __P((struct ifnet *, struct ifnet *)); 70static int in6_ifattach_loopback __P((struct ifnet *)); 71static int nigroup __P((struct ifnet *, const char *, int, struct in6_addr *)); 72 73#define EUI64_GBIT 0x01 74#define EUI64_UBIT 0x02 75#define EUI64_TO_IFID(in6) do {(in6)->s6_addr[8] ^= EUI64_UBIT; } while (0) 76#define EUI64_GROUP(in6) ((in6)->s6_addr[8] & EUI64_GBIT) 77#define EUI64_INDIVIDUAL(in6) (!EUI64_GROUP(in6)) 78#define EUI64_LOCAL(in6) ((in6)->s6_addr[8] & EUI64_UBIT) 79#define EUI64_UNIVERSAL(in6) (!EUI64_LOCAL(in6)) 80 81#define IFID_LOCAL(in6) (!EUI64_LOCAL(in6)) 82#define IFID_UNIVERSAL(in6) (!EUI64_UNIVERSAL(in6)) 83 84/* 85 * Generate a last-resort interface identifier, when the machine has no 86 * IEEE802/EUI64 address sources. 87 * The goal here is to get an interface identifier that is 88 * (1) random enough and (2) does not change across reboot. 89 * We currently use MD5(hostname) for it. 90 */ 91static int 92get_rand_ifid(ifp, in6) 93 struct ifnet *ifp; 94 struct in6_addr *in6; /*upper 64bits are preserved */ 95{ 96 MD5_CTX ctxt; 97 u_int8_t digest[16]; 98 int hostnamelen = strlen(hostname); 99 100#if 0 101 /* we need at least several letters as seed for ifid */ 102 if (hostnamelen < 3) 103 return -1; 104#endif 105 106 /* generate 8 bytes of pseudo-random value. */ 107 bzero(&ctxt, sizeof(ctxt)); 108 MD5Init(&ctxt); 109 MD5Update(&ctxt, hostname, hostnamelen); 110 MD5Final(digest, &ctxt); 111 112 /* assumes sizeof(digest) > sizeof(ifid) */ 113 bcopy(digest, &in6->s6_addr[8], 8); 114 115 /* make sure to set "u" bit to local, and "g" bit to individual. */ 116 in6->s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */ 117 in6->s6_addr[8] |= EUI64_UBIT; /* u bit to "local" */ 118 119 /* convert EUI64 into IPv6 interface identifier */ 120 EUI64_TO_IFID(in6); 121 122 return 0; 123} 124 125/* 126 * Get interface identifier for the specified interface. 127 * XXX assumes single sockaddr_dl (AF_LINK address) per an interface 128 */ 129static int 130get_hw_ifid(ifp, in6) 131 struct ifnet *ifp; 132 struct in6_addr *in6; /*upper 64bits are preserved */ 133{ 134 struct ifaddr *ifa; 135 struct sockaddr_dl *sdl; 136 u_int8_t *addr; 137 size_t addrlen; 138 static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 139 static u_int8_t allone[8] = 140 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 141 142 for (ifa = ifp->if_addrlist.tqh_first; 143 ifa; 144 ifa = ifa->ifa_list.tqe_next) 145 { 146 if (ifa->ifa_addr->sa_family != AF_LINK) 147 continue; 148 sdl = (struct sockaddr_dl *)ifa->ifa_addr; 149 if (sdl == NULL) 150 continue; 151 if (sdl->sdl_alen == 0) 152 continue; 153 154 goto found; 155 } 156 157 return -1; 158 159found: 160 addr = LLADDR(sdl); 161 addrlen = sdl->sdl_alen; 162 163 /* get EUI64 */ 164 switch (ifp->if_type) { 165 case IFT_ETHER: 166 case IFT_FDDI: 167 case IFT_ATM: 168 /* IEEE802/EUI64 cases - what others? */ 169 170 /* look at IEEE802/EUI64 only */ 171 if (addrlen != 8 && addrlen != 6) 172 return -1; 173 174 /* 175 * check for invalid MAC address - on bsdi, we see it a lot 176 * since wildboar configures all-zero MAC on pccard before 177 * card insertion. 178 */ 179 if (bcmp(addr, allzero, addrlen) == 0) 180 return -1; 181 if (bcmp(addr, allone, addrlen) == 0) 182 return -1; 183 184 /* make EUI64 address */ 185 if (addrlen == 8) 186 bcopy(addr, &in6->s6_addr[8], 8); 187 else if (addrlen == 6) { 188 in6->s6_addr[8] = addr[0]; 189 in6->s6_addr[9] = addr[1]; 190 in6->s6_addr[10] = addr[2]; 191 in6->s6_addr[11] = 0xff; 192 in6->s6_addr[12] = 0xfe; 193 in6->s6_addr[13] = addr[3]; 194 in6->s6_addr[14] = addr[4]; 195 in6->s6_addr[15] = addr[5]; 196 } 197 break; 198 199 case IFT_ARCNET: 200 if (addrlen != 1) 201 return -1; 202 if (!addr[0]) 203 return -1; 204 205 bzero(&in6->s6_addr[8], 8); 206 in6->s6_addr[15] = addr[0]; 207 208 /* 209 * due to insufficient bitwidth, we mark it local. 210 */ 211 in6->s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */ 212 in6->s6_addr[8] |= EUI64_UBIT; /* u bit to "local" */ 213 break; 214 215 case IFT_GIF: 216#ifdef IFT_STF 217 case IFT_STF: 218#endif 219 /* 220 * mech-06 says: "SHOULD use IPv4 address as ifid source". 221 * however, IPv4 address is not very suitable as unique 222 * identifier source (can be renumbered). 223 * we don't do this. 224 */ 225 return -1; 226 227 default: 228 return -1; 229 } 230 231 /* sanity check: g bit must not indicate "group" */ 232 if (EUI64_GROUP(in6)) 233 return -1; 234 235 /* convert EUI64 into IPv6 interface identifier */ 236 EUI64_TO_IFID(in6); 237 238 /* 239 * sanity check: ifid must not be all zero, avoid conflict with 240 * subnet router anycast 241 */ 242 if ((in6->s6_addr[8] & ~(EUI64_GBIT | EUI64_UBIT)) == 0x00 && 243 bcmp(&in6->s6_addr[9], allzero, 7) == 0) { 244 return -1; 245 } 246 247 return 0; 248} 249 250/* 251 * Get interface identifier for the specified interface. If it is not 252 * available on ifp0, borrow interface identifier from other information 253 * sources. 254 */ 255static int 256get_ifid(ifp0, altifp, in6) 257 struct ifnet *ifp0; 258 struct ifnet *altifp; /*secondary EUI64 source*/ 259 struct in6_addr *in6; 260{ 261 struct ifnet *ifp; 262 263 /* first, try to get it from the interface itself */ 264 if (get_hw_ifid(ifp0, in6) == 0) { 265#ifdef ND6_DEBUG 266 printf("%s: got interface identifier from itself\n", 267 if_name(ifp0)); 268#endif 269 goto success; 270 } 271 272 /* try secondary EUI64 source. this basically is for ATM PVC */ 273 if (altifp && get_hw_ifid(altifp, in6) == 0) { 274#ifdef ND6_DEBUG 275 printf("%s: got interface identifier from %s\n", 276 if_name(ifp0), if_name(altifp)); 277#endif 278 goto success; 279 } 280 281 /* next, try to get it from some other hardware interface */ 282 for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) 283 { 284 if (ifp == ifp0) 285 continue; 286 if (get_hw_ifid(ifp, in6) != 0) 287 continue; 288 289 /* 290 * to borrow ifid from other interface, ifid needs to be 291 * globally unique 292 */ 293 if (IFID_UNIVERSAL(in6)) { 294 295#ifdef ND6_DEBUG 296 printf("%s: borrow interface identifier from %s\n", 297 if_name(ifp0), if_name(ifp)); 298#endif 299 goto success; 300 } 301 } 302 303 /* last resort: get from random number source */ 304 if (get_rand_ifid(ifp, in6) == 0) { 305#ifdef ND6_DEBUG 306 printf("%s: interface identifier generated by random number\n", 307 if_name(ifp0)); 308#endif 309 goto success; 310 } 311 312 printf("%s: failed to get interface identifier", if_name(ifp0)); 313 return -1; 314 315success: 316#ifdef ND6_DEBUG 317 printf("%s: ifid: " 318 "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", 319 if_name(ifp0), 320 in6->s6_addr[8], in6->s6_addr[9], 321 in6->s6_addr[10], in6->s6_addr[11], 322 in6->s6_addr[12], in6->s6_addr[13], 323 in6->s6_addr[14], in6->s6_addr[15]); 324#endif 325 return 0; 326} 327 328/* 329 * configure IPv6 interface address. XXX code duplicated with in.c 330 */ 331static int 332in6_ifattach_addaddr(ifp, ia) 333 struct ifnet *ifp; 334 struct in6_ifaddr *ia; 335{ 336 struct in6_ifaddr *oia; 337 struct ifaddr *ifa; 338 int error; 339 int rtflag; 340 struct in6_addr llsol; 341 342 /* 343 * initialize if_addrlist, if we are the very first one 344 */ 345 ifa = TAILQ_FIRST(&ifp->if_addrlist); 346 if (ifa == NULL) { 347 TAILQ_INIT(&ifp->if_addrlist); 348 } 349 350 /* 351 * link the interface address to global list 352 */ 353 TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); 354 /* gain a refcnt for the link from if_addrlist */ 355 ia->ia_ifa.ifa_refcnt++; 356 357 /* 358 * Also link into the IPv6 address chain beginning with in6_ifaddr. 359 * kazu opposed it, but itojun & jinmei wanted. 360 */ 361 if ((oia = in6_ifaddr) != NULL) { 362 for (; oia->ia_next; oia = oia->ia_next) 363 continue; 364 oia->ia_next = ia; 365 } else 366 in6_ifaddr = ia; 367 /* gain another refcnt for the link from in6_ifaddr */ 368 ia->ia_ifa.ifa_refcnt++; 369 370 /* 371 * give the interface a chance to initialize, in case this 372 * is the first address to be added. 373 */ 374 if (ifp->if_ioctl != NULL) { 375 int s; 376 s = splimp(); 377 error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); 378 splx(s); 379 } else 380 error = 0; 381 if (error) { 382 switch (error) { 383 case EAFNOSUPPORT: 384 printf("%s: IPv6 not supported\n", if_name(ifp)); 385 break; 386 default: 387 printf("%s: SIOCSIFADDR error %d\n", if_name(ifp), 388 error); 389 break; 390 } 391 392 /* undo changes */ 393 TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); 394 IFAFREE(&ia->ia_ifa); 395 if (oia) 396 oia->ia_next = ia->ia_next; 397 else 398 in6_ifaddr = ia->ia_next; 399 IFAFREE(&ia->ia_ifa); 400 return -1; 401 } 402 403 /* configure link-layer address resolution */ 404 rtflag = 0; 405 if (IN6_ARE_ADDR_EQUAL(&ia->ia_prefixmask.sin6_addr, &in6mask128)) 406 rtflag = RTF_HOST; 407 else { 408 switch (ifp->if_type) { 409 case IFT_LOOP: 410#ifdef IFT_STF 411 case IFT_STF: 412#endif 413 rtflag = 0; 414 break; 415 default: 416 ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; 417 ia->ia_ifa.ifa_flags |= RTF_CLONING; 418 rtflag = RTF_CLONING; 419 break; 420 } 421 } 422 423 /* add route to the interface. */ 424 { 425 int e; 426 427 e = rtrequest(RTM_ADD, 428 (struct sockaddr *)&ia->ia_addr, 429 (struct sockaddr *)&ia->ia_addr, 430 (struct sockaddr *)&ia->ia_prefixmask, 431 RTF_UP | rtflag, 432 (struct rtentry **)0); 433 if (e) { 434 printf("in6_ifattach_addaddr: rtrequest failed. errno = %d\n", e); 435 } 436 } 437 ia->ia_flags |= IFA_ROUTE; 438 439 if ((rtflag & RTF_CLONING) != 0 && 440 (ifp->if_flags & IFF_MULTICAST) != 0) { 441 /* 442 * join solicited multicast address 443 */ 444 bzero(&llsol, sizeof(llsol)); 445 llsol.s6_addr16[0] = htons(0xff02); 446 llsol.s6_addr16[1] = htons(ifp->if_index); 447 llsol.s6_addr32[1] = 0; 448 llsol.s6_addr32[2] = htonl(1); 449 llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3]; 450 llsol.s6_addr8[12] = 0xff; 451 (void)in6_addmulti(&llsol, ifp, &error); 452 453 /* XXX should we run DAD on other interface types? */ 454 switch (ifp->if_type) { 455#if 1 456 case IFT_ARCNET: 457 case IFT_ETHER: 458 case IFT_FDDI: 459#else 460 default: 461#endif 462 /* mark the address TENTATIVE, if needed. */ 463 ia->ia6_flags |= IN6_IFF_TENTATIVE; 464 /* nd6_dad_start() will be called in in6_if_up */ 465 } 466 } 467 468 return 0; 469} 470 471static int 472in6_ifattach_linklocal(ifp, altifp) 473 struct ifnet *ifp; 474 struct ifnet *altifp; /*secondary EUI64 source*/ 475{ 476 struct in6_ifaddr *ia; 477 478 /* 479 * configure link-local address 480 */ 481 ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK); 482 bzero((caddr_t)ia, sizeof(*ia)); 483 ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; 484 if (ifp->if_flags & IFF_POINTOPOINT) 485 ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; 486 else 487 ia->ia_ifa.ifa_dstaddr = NULL; 488 ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; 489 ia->ia_ifp = ifp; 490 491 bzero(&ia->ia_prefixmask, sizeof(ia->ia_prefixmask)); 492 ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); 493 ia->ia_prefixmask.sin6_family = AF_INET6; 494#ifdef SCOPEDROUTING 495 /* take into accound the sin6_scope_id field for routing */ 496 ia->ia_prefixmask.sin6_scope_id = 0xffffffff; 497#endif 498 ia->ia_prefixmask.sin6_addr = in6mask64; 499 500 /* just in case */ 501 bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr)); 502 ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6); 503 ia->ia_dstaddr.sin6_family = AF_INET6; 504 505 bzero(&ia->ia_addr, sizeof(ia->ia_addr)); 506 ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); 507 ia->ia_addr.sin6_family = AF_INET6; 508 ia->ia_addr.sin6_addr.s6_addr16[0] = htons(0xfe80); 509 ia->ia_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); 510 ia->ia_addr.sin6_addr.s6_addr32[1] = 0; 511 if (ifp->if_flags & IFF_LOOPBACK) { 512 ia->ia_addr.sin6_addr.s6_addr32[2] = 0; 513 ia->ia_addr.sin6_addr.s6_addr32[3] = htonl(1); 514 } else { 515 if (get_ifid(ifp, altifp, &ia->ia_addr.sin6_addr) != 0) { 516#ifdef ND6_DEBUG 517 printf("%s: no ifid available\n", if_name(ifp)); 518#endif 519 free(ia, M_IFADDR); 520 return -1; 521 } 522 } 523#ifdef SCOPEDROUTING 524 ia->ia_addr.sin6_scope_id = in6_addr2scopeid(ifp, 525 &ia->ia_addr.sin6_addr); 526#endif 527 528 ia->ia_ifa.ifa_metric = ifp->if_metric; 529 530 if (in6_ifattach_addaddr(ifp, ia) != 0) { 531 /* ia will be freed on failure */ 532 return -1; 533 } 534 535 return 0; 536} 537 538static int 539in6_ifattach_loopback(ifp) 540 struct ifnet *ifp; /* must be IFT_LOOP */ 541{ 542 struct in6_ifaddr *ia; 543 544 /* 545 * configure link-local address 546 */ 547 ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK); 548 bzero((caddr_t)ia, sizeof(*ia)); 549 ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; 550 ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; 551 ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; 552 ia->ia_ifp = ifp; 553 554 bzero(&ia->ia_prefixmask, sizeof(ia->ia_prefixmask)); 555 ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); 556 ia->ia_prefixmask.sin6_family = AF_INET6; 557 ia->ia_prefixmask.sin6_addr = in6mask128; 558 559 /* 560 * Always initialize ia_dstaddr (= broadcast address) to loopback 561 * address, to make getifaddr happier. 562 * 563 * For BSDI, it is mandatory. The BSDI version of 564 * ifa_ifwithroute() rejects to add a route to the loopback 565 * interface. Even for other systems, loopback looks somewhat 566 * special. 567 */ 568 bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr)); 569 ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6); 570 ia->ia_dstaddr.sin6_family = AF_INET6; 571 ia->ia_dstaddr.sin6_addr = in6addr_loopback; 572 573 bzero(&ia->ia_addr, sizeof(ia->ia_addr)); 574 ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); 575 ia->ia_addr.sin6_family = AF_INET6; 576 ia->ia_addr.sin6_addr = in6addr_loopback; 577 578 ia->ia_ifa.ifa_metric = ifp->if_metric; 579 580 if (in6_ifattach_addaddr(ifp, ia) != 0) { 581 /* ia will be freed on failure */ 582 return -1; 583 } 584 585 return 0; 586} 587 588/* 589 * compute NI group address, based on the current hostname setting. 590 * see draft-ietf-ipngwg-icmp-name-lookup-* (04 and later). 591 * 592 * when ifp == NULL, the caller is responsible for filling scopeid. 593 */ 594static int 595nigroup(ifp, name, namelen, in6) 596 struct ifnet *ifp; 597 const char *name; 598 int namelen; 599 struct in6_addr *in6; 600{ 601 const char *p; 602 MD5_CTX ctxt; 603 u_int8_t digest[16]; 604 char l; 605 606 if (!namelen || !name) 607 return -1; 608 609 p = name; 610 while (p && *p && *p != '.' && p - name < namelen) 611 p++; 612 if (p - name > 63) 613 return -1; /*label too long*/ 614 l = p - name; 615 616 /* generate 8 bytes of pseudo-random value. */ 617 bzero(&ctxt, sizeof(ctxt)); 618 MD5Init(&ctxt); 619 MD5Update(&ctxt, &l, sizeof(l)); 620 /* LINTED const cast */ 621 MD5Update(&ctxt, (void *)name, p - name); 622 MD5Final(digest, &ctxt); 623 624 bzero(in6, sizeof(*in6)); 625 in6->s6_addr16[0] = htons(0xff02); 626 if (ifp) 627 in6->s6_addr16[1] = htons(ifp->if_index); 628 in6->s6_addr8[11] = 2; 629 bcopy(digest, &in6->s6_addr32[3], sizeof(in6->s6_addr32[3])); 630 631 return 0; 632} 633 634void 635in6_nigroup_attach(name, namelen) 636 const char *name; 637 int namelen; 638{ 639 struct ifnet *ifp; 640 struct sockaddr_in6 mltaddr; 641 struct in6_multi *in6m; 642 int error; 643 644 bzero(&mltaddr, sizeof(mltaddr)); 645 mltaddr.sin6_family = AF_INET6; 646 mltaddr.sin6_len = sizeof(struct sockaddr_in6); 647 if (nigroup(NULL, name, namelen, &mltaddr.sin6_addr) != 0) 648 return; 649 650 for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) 651 { 652 mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); 653 IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); 654 if (!in6m) 655 (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); 656 } 657} 658 659void 660in6_nigroup_detach(name, namelen) 661 const char *name; 662 int namelen; 663{ 664 struct ifnet *ifp; 665 struct sockaddr_in6 mltaddr; 666 struct in6_multi *in6m; 667 668 bzero(&mltaddr, sizeof(mltaddr)); 669 mltaddr.sin6_family = AF_INET6; 670 mltaddr.sin6_len = sizeof(struct sockaddr_in6); 671 if (nigroup(NULL, name, namelen, &mltaddr.sin6_addr) != 0) 672 return; 673 674 for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) 675 { 676 mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); 677 IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); 678 if (in6m) 679 in6_delmulti(in6m); 680 } 681} 682 683/* 684 * XXX multiple loopback interface needs more care. for instance, 685 * nodelocal address needs to be configured onto only one of them. 686 * XXX multiple link-local address case 687 */ 688void 689in6_ifattach(ifp, altifp) 690 struct ifnet *ifp; 691 struct ifnet *altifp; /* secondary EUI64 source */ 692{ 693 static size_t if_indexlim = 8; 694 struct sockaddr_in6 mltaddr; 695 struct sockaddr_in6 mltmask; 696 struct sockaddr_in6 gate; 697 struct sockaddr_in6 mask; 698 struct in6_ifaddr *ia; 699 struct in6_addr in6; 700 int hostnamelen = strlen(hostname); 701 702 /* 703 * We have some arrays that should be indexed by if_index. 704 * since if_index will grow dynamically, they should grow too. 705 * struct in6_ifstat **in6_ifstat 706 * struct icmp6_ifstat **icmp6_ifstat 707 */ 708 if (in6_ifstat == NULL || icmp6_ifstat == NULL || 709 if_index >= if_indexlim) { 710 size_t n; 711 caddr_t q; 712 size_t olim; 713 714 olim = if_indexlim; 715 while (if_index >= if_indexlim) 716 if_indexlim <<= 1; 717 718 /* grow in6_ifstat */ 719 n = if_indexlim * sizeof(struct in6_ifstat *); 720 q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK); 721 bzero(q, n); 722 if (in6_ifstat) { 723 bcopy((caddr_t)in6_ifstat, q, 724 olim * sizeof(struct in6_ifstat *)); 725 free((caddr_t)in6_ifstat, M_IFADDR); 726 } 727 in6_ifstat = (struct in6_ifstat **)q; 728 in6_ifstatmax = if_indexlim; 729 730 /* grow icmp6_ifstat */ 731 n = if_indexlim * sizeof(struct icmp6_ifstat *); 732 q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK); 733 bzero(q, n); 734 if (icmp6_ifstat) { 735 bcopy((caddr_t)icmp6_ifstat, q, 736 olim * sizeof(struct icmp6_ifstat *)); 737 free((caddr_t)icmp6_ifstat, M_IFADDR); 738 } 739 icmp6_ifstat = (struct icmp6_ifstat **)q; 740 icmp6_ifstatmax = if_indexlim; 741 } 742 743 /* initialize scope identifiers */ 744 scope6_ifattach(ifp); 745 746 /* 747 * quirks based on interface type 748 */ 749 switch (ifp->if_type) { 750#ifdef IFT_STF 751 case IFT_STF: 752 /* 753 * 6to4 interface is a very speical kind of beast. 754 * no multicast, no linklocal (based on 03 draft). 755 */ 756 goto statinit; 757#endif 758 default: 759 break; 760 } 761 762 /* 763 * usually, we require multicast capability to the interface 764 */ 765 if ((ifp->if_flags & IFF_MULTICAST) == 0) { 766 printf("%s: not multicast capable, IPv6 not enabled\n", 767 if_name(ifp)); 768 return; 769 } 770 771 /* 772 * assign link-local address, if there's none 773 */ 774 ia = in6ifa_ifpforlinklocal(ifp, 0); 775 if (ia == NULL) { 776 if (in6_ifattach_linklocal(ifp, altifp) != 0) 777 return; 778 ia = in6ifa_ifpforlinklocal(ifp, 0); 779 780 if (ia == NULL) { 781 printf("%s: failed to add link-local address", 782 if_name(ifp)); 783 784 /* we can't initialize multicasts without link-local */ 785 goto statinit; 786 } 787 } 788 789 if (ifp->if_flags & IFF_POINTOPOINT) { 790 /* 791 * route local address to loopback 792 */ 793 bzero(&gate, sizeof(gate)); 794 gate.sin6_len = sizeof(struct sockaddr_in6); 795 gate.sin6_family = AF_INET6; 796 gate.sin6_addr = in6addr_loopback; 797 bzero(&mask, sizeof(mask)); 798 mask.sin6_len = sizeof(struct sockaddr_in6); 799 mask.sin6_family = AF_INET6; 800 mask.sin6_addr = in6mask64; 801 rtrequest(RTM_ADD, 802 (struct sockaddr *)&ia->ia_addr, 803 (struct sockaddr *)&gate, 804 (struct sockaddr *)&mask, 805 RTF_UP|RTF_HOST, 806 (struct rtentry **)0); 807 } 808 809 /* 810 * assign loopback address for loopback interface 811 * XXX multiple loopback interface case 812 */ 813 in6 = in6addr_loopback; 814 if (ifp->if_flags & IFF_LOOPBACK) { 815 if (in6ifa_ifpwithaddr(ifp, &in6) == NULL) { 816 if (in6_ifattach_loopback(ifp) != 0) 817 return; 818 } 819 } 820 821#ifdef DIAGNOSTIC 822 if (!ia) { 823 panic("ia == NULL in in6_ifattach"); 824 /*NOTREACHED*/ 825 } 826#endif 827 828 /* 829 * join multicast 830 */ 831 if (ifp->if_flags & IFF_MULTICAST) { 832 int error; /* not used */ 833 struct in6_multi *in6m; 834 835 bzero(&mltmask, sizeof(mltmask)); 836 mltmask.sin6_len = sizeof(struct sockaddr_in6); 837 mltmask.sin6_family = AF_INET6; 838 mltmask.sin6_addr = in6mask32; 839 840 /* 841 * join link-local all-nodes address 842 */ 843 bzero(&mltaddr, sizeof(mltaddr)); 844 mltaddr.sin6_len = sizeof(struct sockaddr_in6); 845 mltaddr.sin6_family = AF_INET6; 846 mltaddr.sin6_addr = in6addr_linklocal_allnodes; 847 mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); 848 849 IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); 850 if (in6m == NULL) { 851 rtrequest(RTM_ADD, 852 (struct sockaddr *)&mltaddr, 853 (struct sockaddr *)&ia->ia_addr, 854 (struct sockaddr *)&mltmask, 855 RTF_UP|RTF_CLONING, /* xxx */ 856 (struct rtentry **)0); 857 (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); 858 } 859 860 /* 861 * join node information group address 862 */ 863 if (nigroup(ifp, hostname, hostnamelen, &mltaddr.sin6_addr) 864 == 0) { 865 IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); 866 if (in6m == NULL && ia != NULL) { 867 (void)in6_addmulti(&mltaddr.sin6_addr, 868 ifp, &error); 869 } 870 } 871 872 if (ifp->if_flags & IFF_LOOPBACK) { 873 in6 = in6addr_loopback; 874 ia = in6ifa_ifpwithaddr(ifp, &in6); 875 /* 876 * join node-local all-nodes address, on loopback 877 */ 878 mltaddr.sin6_addr = in6addr_nodelocal_allnodes; 879 880 IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); 881 if (in6m == NULL && ia != NULL) { 882 rtrequest(RTM_ADD, 883 (struct sockaddr *)&mltaddr, 884 (struct sockaddr *)&ia->ia_addr, 885 (struct sockaddr *)&mltmask, 886 RTF_UP, 887 (struct rtentry **)0); 888 (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); 889 } 890 } 891 } 892 893statinit:; 894 895 /* update dynamically. */ 896 if (in6_maxmtu < ifp->if_mtu) 897 in6_maxmtu = ifp->if_mtu; 898 899 if (in6_ifstat[ifp->if_index] == NULL) { 900 in6_ifstat[ifp->if_index] = (struct in6_ifstat *) 901 malloc(sizeof(struct in6_ifstat), M_IFADDR, M_WAITOK); 902 bzero(in6_ifstat[ifp->if_index], sizeof(struct in6_ifstat)); 903 } 904 if (icmp6_ifstat[ifp->if_index] == NULL) { 905 icmp6_ifstat[ifp->if_index] = (struct icmp6_ifstat *) 906 malloc(sizeof(struct icmp6_ifstat), M_IFADDR, M_WAITOK); 907 bzero(icmp6_ifstat[ifp->if_index], sizeof(struct icmp6_ifstat)); 908 } 909 910 /* initialize NDP variables */ 911 nd6_ifattach(ifp); 912} 913 914/* 915 * NOTE: in6_ifdetach() does not support loopback if at this moment. 916 */ 917void 918in6_ifdetach(ifp) 919 struct ifnet *ifp; 920{ 921 struct in6_ifaddr *ia, *oia; 922 struct ifaddr *ifa, *next; 923 struct rtentry *rt; 924 short rtflags; 925 struct sockaddr_in6 sin6; 926 struct in6_multi *in6m; 927 struct in6_multi *in6m_next; 928 929 /* nuke prefix list. this may try to remove some of ifaddrs as well */ 930 in6_purgeprefix(ifp); 931 932 /* remove neighbor management table */ 933 nd6_purge(ifp); 934 935 /* nuke any of IPv6 addresses we have */ 936 for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = next) 937 { 938 next = ifa->ifa_list.tqe_next; 939 if (ifa->ifa_addr->sa_family != AF_INET6) 940 continue; 941 in6_purgeaddr(ifa, ifp); 942 } 943 944 /* undo everything done by in6_ifattach(), just in case */ 945 for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = next) 946 { 947 next = ifa->ifa_list.tqe_next; 948 949 950 if (ifa->ifa_addr->sa_family != AF_INET6 951 || !IN6_IS_ADDR_LINKLOCAL(&satosin6(&ifa->ifa_addr)->sin6_addr)) { 952 continue; 953 } 954 955 ia = (struct in6_ifaddr *)ifa; 956 957 /* remove from the routing table */ 958 if ((ia->ia_flags & IFA_ROUTE) 959 && (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0, 0UL))) { 960 rtflags = rt->rt_flags; 961 rtfree(rt); 962 rtrequest(RTM_DELETE, 963 (struct sockaddr *)&ia->ia_addr, 964 (struct sockaddr *)&ia->ia_addr, 965 (struct sockaddr *)&ia->ia_prefixmask, 966 rtflags, (struct rtentry **)0); 967 } 968 969 /* remove from the linked list */ 970 TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); 971 IFAFREE(&ia->ia_ifa); 972 973 /* also remove from the IPv6 address chain(itojun&jinmei) */ 974 oia = ia; 975 if (oia == (ia = in6_ifaddr)) 976 in6_ifaddr = ia->ia_next; 977 else { 978 while (ia->ia_next && (ia->ia_next != oia)) 979 ia = ia->ia_next; 980 if (ia->ia_next) 981 ia->ia_next = oia->ia_next; 982#ifdef ND6_DEBUG 983 else 984 printf("%s: didn't unlink in6ifaddr from " 985 "list\n", if_name(ifp)); 986#endif 987 } 988 989 IFAFREE(&oia->ia_ifa); 990 } 991 992 /* leave from all multicast groups joined */ 993 for (in6m = LIST_FIRST(&in6_multihead); in6m; in6m = in6m_next) { 994 in6m_next = LIST_NEXT(in6m, in6m_entry); 995 if (in6m->in6m_ifp != ifp) 996 continue; 997 in6_delmulti(in6m); 998 in6m = NULL; 999 } 1000 1001 /* remove neighbor management table */ 1002 nd6_purge(ifp); 1003 1004 /* remove route to link-local allnodes multicast (ff02::1) */ 1005 bzero(&sin6, sizeof(sin6)); 1006 sin6.sin6_len = sizeof(struct sockaddr_in6); 1007 sin6.sin6_family = AF_INET6; 1008 sin6.sin6_addr = in6addr_linklocal_allnodes; 1009 sin6.sin6_addr.s6_addr16[1] = htons(ifp->if_index); 1010 if ((rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL)) != NULL) 1011 { 1012 rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt), 1013 rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0); 1014 rtfree(rt); 1015 } 1016} 1017