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