in6_ifattach.c revision 1.7
1/* $OpenBSD: in6_ifattach.c,v 1.7 2000/03/02 09:44:28 itojun Exp $ */ 2/* $KAME: in6_ifattach.c,v 1.39 2000/03/02 09:24:45 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 58static struct in6_addr llsol; 59 60struct in6_ifstat **in6_ifstat = NULL; 61struct icmp6_ifstat **icmp6_ifstat = NULL; 62size_t in6_ifstatmax = 0; 63size_t icmp6_ifstatmax = 0; 64unsigned long in6_maxmtu = 0; 65 66int found_first_ifid = 0; 67#define IFID_LEN 8 68static u_int8_t first_ifid[IFID_LEN]; 69 70static int laddr_to_eui64 __P((u_int8_t *, u_int8_t *, size_t)); 71static int gen_rand_eui64 __P((u_int8_t *)); 72 73static int 74laddr_to_eui64(dst, src, len) 75 u_int8_t *dst; 76 u_int8_t *src; 77 size_t len; 78{ 79 static u_int8_t zero[8]; 80 81 bzero(zero, sizeof(zero)); 82 83 switch (len) { 84 case 6: 85 if (bcmp(zero, src, 6) == 0) 86 return EINVAL; 87 dst[0] = src[0]; 88 dst[1] = src[1]; 89 dst[2] = src[2]; 90 dst[3] = 0xff; 91 dst[4] = 0xfe; 92 dst[5] = src[3]; 93 dst[6] = src[4]; 94 dst[7] = src[5]; 95 break; 96 case 8: 97 if (bcmp(zero, src, 8) == 0) 98 return EINVAL; 99 bcopy(src, dst, len); 100 break; 101 default: 102 return EINVAL; 103 } 104 105 return 0; 106} 107 108/* 109 * Generate a last-resort interface identifier, when the machine has no 110 * IEEE802/EUI64 address sources. 111 * The address should be random, and should not change across reboot. 112 */ 113static int 114gen_rand_eui64(dst) 115 u_int8_t *dst; 116{ 117 MD5_CTX ctxt; 118 u_int8_t digest[16]; 119 120 /* generate 8bytes of pseudo-random value. */ 121 bzero(&ctxt, sizeof(ctxt)); 122 MD5Init(&ctxt); 123 MD5Update(&ctxt, hostname, hostnamelen); 124 MD5Final(digest, &ctxt); 125 126 /* assumes sizeof(digest) > sizeof(first_ifid) */ 127 bcopy(digest, dst, 8); 128 129 /* make sure to set "u" bit to local, and "g" bit to individual. */ 130 dst[0] &= 0xfe; 131 dst[0] |= 0x02; /* EUI64 "local" */ 132 133 return 0; 134} 135 136/* 137 * Find first ifid on list of interfaces. 138 * This is assumed that ifp0's interface token (for example, IEEE802 MAC) 139 * is globally unique. We may need to have a flag parameter in the future. 140 */ 141int 142in6_ifattach_getifid(ifp0) 143 struct ifnet *ifp0; 144{ 145 struct ifnet *ifp; 146 struct ifaddr *ifa; 147 u_int8_t *addr = NULL; 148 int addrlen = 0; 149 struct sockaddr_dl *sdl; 150 151 if (found_first_ifid) 152 return 0; 153 154 for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) 155 { 156 if (ifp0 != NULL && ifp0 != ifp) 157 continue; 158 for (ifa = ifp->if_addrlist.tqh_first; 159 ifa; 160 ifa = ifa->ifa_list.tqe_next) 161 { 162 if (ifa->ifa_addr->sa_family != AF_LINK) 163 continue; 164 sdl = (struct sockaddr_dl *)ifa->ifa_addr; 165 if (sdl == NULL) 166 continue; 167 if (sdl->sdl_alen == 0) 168 continue; 169 switch (ifp->if_type) { 170 case IFT_ETHER: 171 case IFT_FDDI: 172 case IFT_ATM: 173 /* IEEE802/EUI64 cases - what others? */ 174 addr = LLADDR(sdl); 175 addrlen = sdl->sdl_alen; 176 /* 177 * to copy ifid from IEEE802/EUI64 interface, 178 * u bit of the source needs to be 0. 179 */ 180 if ((addr[0] & 0x02) != 0) 181 break; 182 goto found; 183 case IFT_ARCNET: 184 /* 185 * ARCnet interface token cannot be used as 186 * globally unique identifier due to its 187 * small bitwidth. 188 */ 189 break; 190 default: 191 break; 192 } 193 } 194 } 195#ifdef DEBUG 196 printf("in6_ifattach_getifid: failed to get EUI64"); 197#endif 198 return EADDRNOTAVAIL; 199 200found: 201 if (laddr_to_eui64(first_ifid, addr, addrlen) == 0) 202 found_first_ifid = 1; 203 204 if (found_first_ifid) { 205 printf("%s: supplying EUI64: " 206 "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", 207 if_name(ifp), 208 first_ifid[0], first_ifid[1], 209 first_ifid[2], first_ifid[3], 210 first_ifid[4], first_ifid[5], 211 first_ifid[6], first_ifid[7]); 212 213 /* invert u bit to convert EUI64 to RFC2373 interface ID. */ 214 first_ifid[0] ^= 0x02; 215 216 return 0; 217 } else { 218#ifdef DEBUG 219 printf("in6_ifattach_getifid: failed to get EUI64"); 220#endif 221 return EADDRNOTAVAIL; 222 } 223} 224 225/* 226 * XXX multiple loopback interface needs more care. for instance, 227 * nodelocal address needs to be configured onto only one of them. 228 */ 229void 230in6_ifattach(ifp, type, laddr, noloop) 231 struct ifnet *ifp; 232 u_int type; 233 caddr_t laddr; 234 /* size_t laddrlen; */ 235 int noloop; 236{ 237 static size_t if_indexlim = 8; 238 struct sockaddr_in6 mltaddr; 239 struct sockaddr_in6 mltmask; 240 struct sockaddr_in6 gate; 241 struct sockaddr_in6 mask; 242 struct in6_ifaddr *ia, *ib, *oia; 243 struct ifaddr *ifa; 244 int rtflag = 0; 245 246 if (type == IN6_IFT_P2P && found_first_ifid == 0) { 247 printf("%s: no ifid available for IPv6 link-local address\n", 248 if_name(ifp)); 249#if 0 250 return; 251#else 252 /* last resort */ 253 if (gen_rand_eui64(first_ifid) == 0) { 254 printf("%s: using random value as EUI64: " 255 "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", 256 if_name(ifp), 257 first_ifid[0], first_ifid[1], 258 first_ifid[2], first_ifid[3], 259 first_ifid[4], first_ifid[5], 260 first_ifid[6], first_ifid[7]); 261 /* 262 * invert u bit to convert EUI64 to RFC2373 interface 263 * ID. 264 */ 265 first_ifid[0] ^= 0x02; 266 267 found_first_ifid = 1; 268 } 269#endif 270 } 271 272 if ((ifp->if_flags & IFF_MULTICAST) == 0) { 273 printf("%s: not multicast capable, IPv6 not enabled\n", 274 if_name(ifp)); 275 return; 276 } 277 278 /* 279 * We have some arrays that should be indexed by if_index. 280 * since if_index will grow dynamically, they should grow too. 281 * struct in6_ifstat **in6_ifstat 282 * struct icmp6_ifstat **icmp6_ifstat 283 */ 284 if (in6_ifstat == NULL || icmp6_ifstat == NULL 285 || if_index >= if_indexlim) { 286 size_t n; 287 caddr_t q; 288 size_t olim; 289 290 olim = if_indexlim; 291 while (if_index >= if_indexlim) 292 if_indexlim <<= 1; 293 294 /* grow in6_ifstat */ 295 n = if_indexlim * sizeof(struct in6_ifstat *); 296 q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK); 297 bzero(q, n); 298 if (in6_ifstat) { 299 bcopy((caddr_t)in6_ifstat, q, 300 olim * sizeof(struct in6_ifstat *)); 301 free((caddr_t)in6_ifstat, M_IFADDR); 302 } 303 in6_ifstat = (struct in6_ifstat **)q; 304 in6_ifstatmax = if_indexlim; 305 306 /* grow icmp6_ifstat */ 307 n = if_indexlim * sizeof(struct icmp6_ifstat *); 308 q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK); 309 bzero(q, n); 310 if (icmp6_ifstat) { 311 bcopy((caddr_t)icmp6_ifstat, q, 312 olim * sizeof(struct icmp6_ifstat *)); 313 free((caddr_t)icmp6_ifstat, M_IFADDR); 314 } 315 icmp6_ifstat = (struct icmp6_ifstat **)q; 316 icmp6_ifstatmax = if_indexlim; 317 } 318 319 /* 320 * To prevent to assign link-local address to PnP network 321 * cards multiple times. 322 * This is lengthy for P2P and LOOP but works. 323 */ 324 ifa = TAILQ_FIRST(&ifp->if_addrlist); 325 if (ifa != NULL) { 326 for ( ; ifa; ifa = TAILQ_NEXT(ifa, ifa_list)) { 327 if (ifa->ifa_addr->sa_family != AF_INET6) 328 continue; 329 if (IN6_IS_ADDR_LINKLOCAL(&satosin6(ifa->ifa_addr)->sin6_addr)) 330 return; 331 } 332 } else { 333 TAILQ_INIT(&ifp->if_addrlist); 334 } 335 336 /* 337 * link-local address 338 */ 339 ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK); 340 bzero((caddr_t)ia, sizeof(*ia)); 341 ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; 342 if (ifp->if_flags & IFF_POINTOPOINT) 343 ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; 344 else 345 ia->ia_ifa.ifa_dstaddr = NULL; 346 ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; 347 ia->ia_ifp = ifp; 348 349 TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); 350 ia->ia_ifa.ifa_refcnt++; 351 352 /* 353 * Also link into the IPv6 address chain beginning with in6_ifaddr. 354 * kazu opposed it, but itojun & jinmei wanted. 355 */ 356 if ((oia = in6_ifaddr) != NULL) { 357 for (; oia->ia_next; oia = oia->ia_next) 358 continue; 359 oia->ia_next = ia; 360 } else 361 in6_ifaddr = ia; 362 ia->ia_ifa.ifa_refcnt++; 363 364 ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); 365 ia->ia_prefixmask.sin6_family = AF_INET6; 366 ia->ia_prefixmask.sin6_addr = in6mask64; 367 368 bzero(&ia->ia_addr, sizeof(struct sockaddr_in6)); 369 ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); 370 ia->ia_addr.sin6_family = AF_INET6; 371 ia->ia_addr.sin6_addr.s6_addr16[0] = htons(0xfe80); 372 ia->ia_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); 373 ia->ia_addr.sin6_addr.s6_addr32[1] = 0; 374 375 switch (type) { 376 case IN6_IFT_LOOP: 377 ia->ia_addr.sin6_addr.s6_addr32[2] = 0; 378 ia->ia_addr.sin6_addr.s6_addr32[3] = htonl(1); 379 break; 380 case IN6_IFT_802: 381 ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; 382 ia->ia_ifa.ifa_flags |= RTF_CLONING; 383 rtflag = RTF_CLONING; 384 /* fall through */ 385 case IN6_IFT_P2P802: 386 if (laddr == NULL) 387 break; 388 /* XXX use laddrlen */ 389 if (laddr_to_eui64(&ia->ia_addr.sin6_addr.s6_addr8[8], 390 laddr, 6) != 0) { 391 break; 392 } 393 /* invert u bit to convert EUI64 to RFC2373 interface ID. */ 394 ia->ia_addr.sin6_addr.s6_addr8[8] ^= 0x02; 395 if (found_first_ifid == 0) 396 in6_ifattach_getifid(ifp); 397 bzero(&ia->ia_dstaddr, sizeof(struct sockaddr_in6)); 398 ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6); 399 ia->ia_dstaddr.sin6_family = AF_INET6; 400 break; 401 case IN6_IFT_P2P: 402 bcopy((caddr_t)first_ifid, 403 (caddr_t)&ia->ia_addr.sin6_addr.s6_addr8[8], 404 IFID_LEN); 405 bzero(&ia->ia_dstaddr, sizeof(struct sockaddr_in6)); 406 ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6); 407 ia->ia_dstaddr.sin6_family = AF_INET6; 408 break; 409 case IN6_IFT_ARCNET: 410 ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; 411 ia->ia_ifa.ifa_flags |= RTF_CLONING; 412 rtflag = RTF_CLONING; 413 if (laddr == NULL) 414 break; 415 416 /* make non-global IF id out of link-level address */ 417 bzero(&ia->ia_addr.sin6_addr.s6_addr8[8], 7); 418 ia->ia_addr.sin6_addr.s6_addr8[15] = *laddr; 419 } 420 421 ia->ia_ifa.ifa_metric = ifp->if_metric; 422 423 if (ifp->if_ioctl != NULL) { 424 int s; 425 int error; 426 427 /* 428 * give the interface a chance to initialize, in case this 429 * is the first address to be added. 430 */ 431 s = splimp(); 432 error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); 433 splx(s); 434 435 if (error) { 436 switch (error) { 437 case EAFNOSUPPORT: 438 printf("%s: IPv6 not supported\n", 439 if_name(ifp)); 440 break; 441 default: 442 printf("%s: SIOCSIFADDR error %d\n", 443 if_name(ifp), error); 444 break; 445 } 446 447 /* undo changes */ 448 TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); 449 IFAFREE(&ia->ia_ifa); 450 if (oia) 451 oia->ia_next = ia->ia_next; 452 else 453 in6_ifaddr = ia->ia_next; 454 IFAFREE(&ia->ia_ifa); 455 return; 456 } 457 } 458 459 /* add route to the interface. */ 460 rtrequest(RTM_ADD, 461 (struct sockaddr *)&ia->ia_addr, 462 (struct sockaddr *)&ia->ia_addr, 463 (struct sockaddr *)&ia->ia_prefixmask, 464 RTF_UP|rtflag, 465 (struct rtentry **)0); 466 ia->ia_flags |= IFA_ROUTE; 467 468 if (type == IN6_IFT_P2P || type == IN6_IFT_P2P802) { 469 /* 470 * route local address to loopback 471 */ 472 bzero(&gate, sizeof(gate)); 473 gate.sin6_len = sizeof(struct sockaddr_in6); 474 gate.sin6_family = AF_INET6; 475 gate.sin6_addr = in6addr_loopback; 476 bzero(&mask, sizeof(mask)); 477 mask.sin6_len = sizeof(struct sockaddr_in6); 478 mask.sin6_family = AF_INET6; 479 mask.sin6_addr = in6mask64; 480 rtrequest(RTM_ADD, 481 (struct sockaddr *)&ia->ia_addr, 482 (struct sockaddr *)&gate, 483 (struct sockaddr *)&mask, 484 RTF_UP|RTF_HOST, 485 (struct rtentry **)0); 486 } 487 488 /* 489 * loopback address 490 */ 491 ib = (struct in6_ifaddr *)NULL; 492 if (type == IN6_IFT_LOOP) { 493 ib = (struct in6_ifaddr *) 494 malloc(sizeof(*ib), M_IFADDR, M_WAITOK); 495 bzero((caddr_t)ib, sizeof(*ib)); 496 ib->ia_ifa.ifa_addr = (struct sockaddr *)&ib->ia_addr; 497 ib->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ib->ia_dstaddr; 498 ib->ia_ifa.ifa_netmask = (struct sockaddr *)&ib->ia_prefixmask; 499 ib->ia_ifp = ifp; 500 501 ia->ia_next = ib; 502 TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ib, 503 ifa_list); 504 ib->ia_ifa.ifa_refcnt++; 505 506 ib->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); 507 ib->ia_prefixmask.sin6_family = AF_INET6; 508 ib->ia_prefixmask.sin6_addr = in6mask128; 509 ib->ia_addr.sin6_len = sizeof(struct sockaddr_in6); 510 ib->ia_addr.sin6_family = AF_INET6; 511 ib->ia_addr.sin6_addr = in6addr_loopback; 512 513 /* 514 * Always initialize ia_dstaddr (= broadcast address) 515 * to loopback address, to make getifaddr happier. 516 * 517 * For BSDI, it is mandatory. The BSDI version of 518 * ifa_ifwithroute() rejects to add a route to the loopback 519 * interface. Even for other systems, loopback looks somewhat 520 * special. 521 */ 522 ib->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6); 523 ib->ia_dstaddr.sin6_family = AF_INET6; 524 ib->ia_dstaddr.sin6_addr = in6addr_loopback; 525 526 ib->ia_ifa.ifa_metric = ifp->if_metric; 527 528 rtrequest(RTM_ADD, 529 (struct sockaddr *)&ib->ia_addr, 530 (struct sockaddr *)&ib->ia_addr, 531 (struct sockaddr *)&ib->ia_prefixmask, 532 RTF_UP|RTF_HOST, 533 (struct rtentry **)0); 534 535 ib->ia_flags |= IFA_ROUTE; 536 } 537 538 /* 539 * join multicast 540 */ 541 if (ifp->if_flags & IFF_MULTICAST) { 542 int error; /* not used */ 543 544 /* Restore saved multicast addresses(if any). */ 545 in6_restoremkludge(ia, ifp); 546 547 bzero(&mltmask, sizeof(mltmask)); 548 mltmask.sin6_len = sizeof(struct sockaddr_in6); 549 mltmask.sin6_family = AF_INET6; 550 mltmask.sin6_addr = in6mask32; 551 552 /* 553 * join link-local all-nodes address 554 */ 555 bzero(&mltaddr, sizeof(mltaddr)); 556 mltaddr.sin6_len = sizeof(struct sockaddr_in6); 557 mltaddr.sin6_family = AF_INET6; 558 mltaddr.sin6_addr = in6addr_linklocal_allnodes; 559 mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); 560 rtrequest(RTM_ADD, 561 (struct sockaddr *)&mltaddr, 562 (struct sockaddr *)&ia->ia_addr, 563 (struct sockaddr *)&mltmask, 564 RTF_UP|RTF_CLONING, /* xxx */ 565 (struct rtentry **)0); 566 (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); 567 568 if (type == IN6_IFT_LOOP) { 569 /* 570 * join node-local all-nodes address 571 */ 572 mltaddr.sin6_addr = in6addr_nodelocal_allnodes; 573 rtrequest(RTM_ADD, 574 (struct sockaddr *)&mltaddr, 575 (struct sockaddr *)&ib->ia_addr, 576 (struct sockaddr *)&mltmask, 577 RTF_UP, 578 (struct rtentry **)0); 579 (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); 580 } else { 581 /* 582 * join solicited multicast address 583 */ 584 bzero(&llsol, sizeof(llsol)); 585 llsol.s6_addr16[0] = htons(0xff02); 586 llsol.s6_addr16[1] = htons(ifp->if_index); 587 llsol.s6_addr32[1] = 0; 588 llsol.s6_addr32[2] = htonl(1); 589 llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3]; 590 llsol.s6_addr8[12] = 0xff; 591 (void)in6_addmulti(&llsol, ifp, &error); 592 } 593 } 594 595 /* update dynamically. */ 596 if (in6_maxmtu < ifp->if_mtu) 597 in6_maxmtu = ifp->if_mtu; 598 599 if (in6_ifstat[ifp->if_index] == NULL) { 600 in6_ifstat[ifp->if_index] = (struct in6_ifstat *) 601 malloc(sizeof(struct in6_ifstat), M_IFADDR, M_WAITOK); 602 bzero(in6_ifstat[ifp->if_index], sizeof(struct in6_ifstat)); 603 } 604 if (icmp6_ifstat[ifp->if_index] == NULL) { 605 icmp6_ifstat[ifp->if_index] = (struct icmp6_ifstat *) 606 malloc(sizeof(struct icmp6_ifstat), M_IFADDR, M_WAITOK); 607 bzero(icmp6_ifstat[ifp->if_index], sizeof(struct icmp6_ifstat)); 608 } 609 610 /* initialize NDP variables */ 611 nd6_ifattach(ifp); 612 613 /* mark the address TENTATIVE, if needed. */ 614 switch (ifp->if_type) { 615 case IFT_ARCNET: 616 case IFT_ETHER: 617 case IFT_FDDI: 618#if 0 619 case IFT_ATM: 620 case IFT_SLIP: 621 case IFT_PPP: 622#endif 623 ia->ia6_flags |= IN6_IFF_TENTATIVE; 624 /* nd6_dad_start() will be called in in6_if_up */ 625 break; 626 case IFT_DUMMY: 627 case IFT_GIF: /*XXX*/ 628 case IFT_LOOP: 629 case IFT_FAITH: 630 default: 631 break; 632 } 633 634 return; 635} 636 637/* 638 * NOTE: in6_ifdetach() does not support loopback if at this moment. 639 */ 640void 641in6_ifdetach(ifp) 642 struct ifnet *ifp; 643{ 644 struct in6_ifaddr *ia, *oia; 645 struct ifaddr *ifa; 646 struct rtentry *rt; 647 short rtflags; 648 struct sockaddr_in6 sin6; 649 struct in6_multi *in6m; 650 651 /* nuke prefix list. this may try to remove some of ifaddrs as well */ 652 in6_purgeprefix(ifp); 653 654 /* remove neighbor management table */ 655 nd6_purge(ifp); 656 657 for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) 658 { 659 if (ifa->ifa_addr->sa_family != AF_INET6 660 || !IN6_IS_ADDR_LINKLOCAL(&satosin6(&ifa->ifa_addr)->sin6_addr)) { 661 continue; 662 } 663 664 ia = (struct in6_ifaddr *)ifa; 665 666 /* leave from all multicast groups joined */ 667 while ((in6m = LIST_FIRST(&oia->ia6_multiaddrs)) != NULL) 668 in6_delmulti(in6m); 669 670 /* remove from the routing table */ 671 if ((ia->ia_flags & IFA_ROUTE) 672 && (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0))) { 673 rtflags = rt->rt_flags; 674 rtfree(rt); 675 rtrequest(RTM_DELETE, 676 (struct sockaddr *)&ia->ia_addr, 677 (struct sockaddr *)&ia->ia_addr, 678 (struct sockaddr *)&ia->ia_prefixmask, 679 rtflags, (struct rtentry **)0); 680 } 681 682 /* remove from the linked list */ 683 TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); 684 685 /* also remove from the IPv6 address chain(itojun&jinmei) */ 686 oia = ia; 687 if (oia == (ia = in6_ifaddr)) 688 in6_ifaddr = ia->ia_next; 689 else { 690 while (ia->ia_next && (ia->ia_next != oia)) 691 ia = ia->ia_next; 692 if (ia->ia_next) 693 ia->ia_next = oia->ia_next; 694#ifdef DEBUG 695 else 696 printf("%s: didn't unlink in6ifaddr from " 697 "list\n", if_name(ifp)); 698#endif 699 } 700 701 free(ia, M_IFADDR); 702 } 703 704 /* cleanup multicast address kludge table, if there is any */ 705 in6_purgemkludge(ifp); 706 707 /* remove neighbor management table */ 708 nd6_purge(ifp); 709 710 /* remove route to link-local allnodes multicast (ff02::1) */ 711 bzero(&sin6, sizeof(sin6)); 712 sin6.sin6_len = sizeof(struct sockaddr_in6); 713 sin6.sin6_family = AF_INET6; 714 sin6.sin6_addr = in6addr_linklocal_allnodes; 715 sin6.sin6_addr.s6_addr16[1] = htons(ifp->if_index); 716 if ((rt = rtalloc1((struct sockaddr *)&sin6, 0)) != NULL) { 717 rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt), 718 rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0); 719 rtfree(rt); 720 } 721} 722