in6_ifattach.c revision 54263
1170530Ssam/* 2178354Ssam * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 3170530Ssam * All rights reserved. 4170530Ssam * 5170530Ssam * Redistribution and use in source and binary forms, with or without 6170530Ssam * modification, are permitted provided that the following conditions 7170530Ssam * are met: 8170530Ssam * 1. Redistributions of source code must retain the above copyright 9170530Ssam * notice, this list of conditions and the following disclaimer. 10170530Ssam * 2. Redistributions in binary form must reproduce the above copyright 11170530Ssam * notice, this list of conditions and the following disclaimer in the 12170530Ssam * documentation and/or other materials provided with the distribution. 13170530Ssam * 3. Neither the name of the project nor the names of its contributors 14170530Ssam * may be used to endorse or promote products derived from this software 15170530Ssam * without specific prior written permission. 16170530Ssam * 17170530Ssam * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18170530Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19170530Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20170530Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21170530Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22170530Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23170530Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24170530Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25170530Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26170530Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27170530Ssam * SUCH DAMAGE. 28170530Ssam * 29170530Ssam * $FreeBSD: head/sys/netinet6/in6_ifattach.c 54263 1999-12-07 17:39:16Z shin $ 30170530Ssam */ 31170530Ssam 32170530Ssam#include <sys/param.h> 33170530Ssam#include <sys/systm.h> 34170530Ssam#include <sys/malloc.h> 35170530Ssam#include <sys/socket.h> 36178354Ssam#include <sys/sockio.h> 37170530Ssam#include <sys/kernel.h> 38170530Ssam#include <sys/md5.h> 39170530Ssam 40170530Ssam#include <net/if.h> 41170530Ssam#include <net/if_dl.h> 42170530Ssam#include <net/if_types.h> 43170530Ssam#include <net/route.h> 44170530Ssam 45170530Ssam#include <netinet/in.h> 46170530Ssam#include <netinet/in_var.h> 47170530Ssam#include <netinet/if_ether.h> 48170530Ssam 49170530Ssam#include <netinet6/in6.h> 50178354Ssam#include <netinet6/ip6.h> 51170530Ssam#include <netinet6/ip6_var.h> 52170530Ssam#include <netinet6/in6_ifattach.h> 53170530Ssam#include <netinet6/ip6.h> 54170530Ssam#include <netinet6/ip6_var.h> 55170530Ssam#include <netinet6/nd6.h> 56178354Ssam 57178354Ssam#include <net/net_osdep.h> 58178354Ssam 59178354Ssamstatic struct in6_addr llsol; 60178354Ssam 61178354Ssamstruct in6_ifstat **in6_ifstat = NULL; 62178354Ssamstruct icmp6_ifstat **icmp6_ifstat = NULL; 63178354Ssamsize_t in6_ifstatmax = 0; 64178354Ssamsize_t icmp6_ifstatmax = 0; 65178354Ssamunsigned long in6_maxmtu = 0; 66178354Ssam 67178354Ssamint found_first_ifid = 0; 68178354Ssam#define IFID_LEN 8 69178354Ssamstatic char first_ifid[IFID_LEN]; 70178354Ssam 71178354Ssamstatic int laddr_to_eui64 __P((u_int8_t *, u_int8_t *, size_t)); 72178354Ssamstatic int gen_rand_eui64 __P((u_int8_t *)); 73170530Ssam 74170530Ssamstatic int 75170530Ssamladdr_to_eui64(dst, src, len) 76170530Ssam u_int8_t *dst; 77170530Ssam u_int8_t *src; 78170530Ssam size_t len; 79170530Ssam{ 80170530Ssam static u_int8_t zero[8]; 81173273Ssam 82173273Ssam bzero(zero, sizeof(zero)); 83173273Ssam 84173273Ssam switch (len) { 85173273Ssam case 6: 86178354Ssam if (bcmp(zero, src, 6) == 0) 87178354Ssam return EINVAL; 88178354Ssam dst[0] = src[0]; 89173273Ssam dst[1] = src[1]; 90178354Ssam dst[2] = src[2]; 91178354Ssam dst[3] = 0xff; 92178354Ssam dst[4] = 0xfe; 93178354Ssam dst[5] = src[3]; 94178354Ssam dst[6] = src[4]; 95178354Ssam dst[7] = src[5]; 96178354Ssam break; 97178354Ssam case 8: 98178354Ssam if (bcmp(zero, src, 8) == 0) 99178354Ssam return EINVAL; 100178354Ssam bcopy(src, dst, len); 101178354Ssam break; 102178354Ssam default: 103170530Ssam return EINVAL; 104178354Ssam } 105178354Ssam 106170530Ssam return 0; 107170530Ssam} 108170530Ssam 109170530Ssam/* 110170530Ssam * Generate a last-resort interface identifier, when the machine has no 111170530Ssam * IEEE802/EUI64 address sources. 112170530Ssam * The address should be random, and should not change across reboot. 113170530Ssam */ 114170530Ssamstatic int 115170530Ssamgen_rand_eui64(dst) 116170530Ssam u_int8_t *dst; 117170530Ssam{ 118170530Ssam MD5_CTX ctxt; 119170530Ssam u_int8_t digest[16]; 120170530Ssam int hostnamelen = strlen(hostname); 121170530Ssam 122170530Ssam /* generate 8bytes of pseudo-random value. */ 123178354Ssam bzero(&ctxt, sizeof(ctxt)); 124170530Ssam MD5Init(&ctxt); 125170530Ssam MD5Update(&ctxt, hostname, hostnamelen); 126170530Ssam MD5Final(digest, &ctxt); 127170530Ssam 128173273Ssam /* assumes sizeof(digest) > sizeof(first_ifid) */ 129173273Ssam bcopy(digest, dst, 8); 130178354Ssam 131173273Ssam /* make sure to set "u" bit to local, and "g" bit to individual. */ 132178354Ssam dst[0] &= 0xfe; 133178354Ssam dst[0] |= 0x02; /* EUI64 "local" */ 134178354Ssam 135178354Ssam return 0; 136173273Ssam} 137178354Ssam 138178354Ssam/* 139178354Ssam * Find first ifid on list of interfaces. 140178354Ssam * This is assumed that ifp0's interface token (for example, IEEE802 MAC) 141178354Ssam * is globally unique. We may need to have a flag parameter in the future. 142178354Ssam */ 143178354Ssamint 144178354Ssamin6_ifattach_getifid(ifp0) 145178354Ssam struct ifnet *ifp0; 146178354Ssam{ 147178354Ssam struct ifnet *ifp; 148178354Ssam struct ifaddr *ifa; 149178354Ssam u_int8_t *addr = NULL; 150178354Ssam int addrlen = 0; 151178354Ssam struct sockaddr_dl *sdl; 152178354Ssam 153170530Ssam if (found_first_ifid) 154173273Ssam return 0; 155173273Ssam 156170530Ssam TAILQ_FOREACH(ifp, &ifnet, if_list) 157170530Ssam { 158178354Ssam if (ifp0 != NULL && ifp0 != ifp) 159178354Ssam continue; 160178354Ssam TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) 161178354Ssam { 162178354Ssam if (ifa->ifa_addr->sa_family != AF_LINK) 163173273Ssam continue; 164178354Ssam sdl = (struct sockaddr_dl *)ifa->ifa_addr; 165178354Ssam if (sdl == NULL) 166178354Ssam continue; 167178354Ssam if (sdl->sdl_alen == 0) 168170530Ssam continue; 169183256Ssam switch (ifp->if_type) { 170183256Ssam case IFT_ETHER: 171183256Ssam case IFT_FDDI: 172183256Ssam case IFT_ATM: 173170530Ssam /* IEEE802/EUI64 cases - what others? */ 174178354Ssam addr = LLADDR(sdl); 175178354Ssam addrlen = sdl->sdl_alen; 176178354Ssam /* 177178354Ssam * to copy ifid from IEEE802/EUI64 interface, 178178354Ssam * u bit of the source needs to be 0. 179178354Ssam */ 180170530Ssam if ((addr[0] & 0x02) != 0) 181178354Ssam break; 182178354Ssam goto found; 183178354Ssam case IFT_ARCNET: 184170530Ssam /* 185170530Ssam * ARCnet interface token cannot be used as 186170530Ssam * globally unique identifier due to its 187178354Ssam * small bitwidth. 188170530Ssam */ 189170530Ssam break; 190170530Ssam default: 191170530Ssam break; 192170530Ssam } 193170530Ssam } 194170530Ssam } 195170530Ssam#ifdef DEBUG 196170530Ssam printf("in6_ifattach_getifid: failed to get EUI64"); 197170530Ssam#endif 198170530Ssam return EADDRNOTAVAIL; 199170530Ssam 200172226Ssamfound: 201172226Ssam if (laddr_to_eui64(first_ifid, addr, addrlen) == 0) 202170530Ssam found_first_ifid = 1; 203170530Ssam 204178354Ssam if (found_first_ifid) { 205170530Ssam printf("%s: supplying EUI64: " 206170530Ssam "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", 207170530Ssam if_name(ifp), 208170530Ssam first_ifid[0] & 0xff, first_ifid[1] & 0xff, 209170530Ssam first_ifid[2] & 0xff, first_ifid[3] & 0xff, 210170530Ssam first_ifid[4] & 0xff, first_ifid[5] & 0xff, 211170530Ssam first_ifid[6] & 0xff, first_ifid[7] & 0xff); 212170530Ssam 213170530Ssam /* invert u bit to convert EUI64 to RFC2373 interface ID. */ 214170530Ssam first_ifid[0] ^= 0x02; 215170530Ssam 216170530Ssam return 0; 217170530Ssam } else { 218170530Ssam#ifdef DEBUG 219170530Ssam printf("in6_ifattach_getifid: failed to get EUI64"); 220170530Ssam#endif 221170530Ssam return EADDRNOTAVAIL; 222170530Ssam } 223170530Ssam} 224173273Ssam 225170530Ssam/* 226170530Ssam * add link-local address to *pseudo* p2p interfaces. 227170530Ssam * get called when the first MAC address is made available in in6_ifattach(). 228170530Ssam * 229170530Ssam * XXX I start considering this loop as a bad idea. (itojun) 230170530Ssam */ 231170530Ssamvoid 232170530Ssamin6_ifattach_p2p() 233170530Ssam{ 234170530Ssam struct ifnet *ifp; 235170530Ssam 236170530Ssam /* prevent infinite loop. just in case. */ 237170530Ssam if (found_first_ifid == 0) 238170530Ssam return; 239178354Ssam 240173462Ssam TAILQ_FOREACH(ifp, &ifnet, if_list) 241170530Ssam { 242170530Ssam switch (ifp->if_type) { 243170530Ssam case IFT_GIF: 244170530Ssam /* pseudo interfaces - safe to initialize here */ 245170530Ssam in6_ifattach(ifp, IN6_IFT_P2P, 0, 0); 246178354Ssam break; 247170530Ssam#ifdef IFT_DUMMY 248170530Ssam case IFT_DUMMY: 249170530Ssam#endif 250170530Ssam case IFT_FAITH: 251170530Ssam /* this mistakingly becomes IFF_UP */ 252170530Ssam break; 253170530Ssam case IFT_SLIP: 254170530Ssam /* IPv6 is not supported */ 255170530Ssam break; 256170530Ssam case IFT_PPP: 257178354Ssam /* this is not a pseudo interface, skip it */ 258173462Ssam break; 259178354Ssam default: 260170530Ssam break; 261170530Ssam } 262173462Ssam } 263170530Ssam} 264170530Ssam 265170530Ssamvoid 266178354Ssamin6_ifattach(ifp, type, laddr, noloop) 267170530Ssam struct ifnet *ifp; 268170530Ssam u_int type; 269178354Ssam caddr_t laddr; 270170530Ssam /* size_t laddrlen; */ 271170530Ssam int noloop; 272170530Ssam{ 273178354Ssam static size_t if_indexlim = 8; 274170530Ssam struct sockaddr_in6 mltaddr; 275170530Ssam struct sockaddr_in6 mltmask; 276170530Ssam struct sockaddr_in6 gate; 277170530Ssam struct sockaddr_in6 mask; 278170530Ssam 279170530Ssam struct in6_ifaddr *ia, *ib, *oia; 280170530Ssam struct ifaddr *ifa; 281170530Ssam int rtflag = 0; 282170530Ssam 283170530Ssam if (type == IN6_IFT_P2P && found_first_ifid == 0) { 284170530Ssam printf("%s: no ifid available for IPv6 link-local address\n", 285170530Ssam if_name(ifp)); 286170530Ssam /* last resort */ 287170530Ssam if (gen_rand_eui64(first_ifid) == 0) { 288170530Ssam printf("%s: using random value as EUI64: " 289170530Ssam "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", 290170530Ssam if_name(ifp), 291170530Ssam first_ifid[0] & 0xff, first_ifid[1] & 0xff, 292170530Ssam first_ifid[2] & 0xff, first_ifid[3] & 0xff, 293170530Ssam first_ifid[4] & 0xff, first_ifid[5] & 0xff, 294170530Ssam first_ifid[6] & 0xff, first_ifid[7] & 0xff); 295170530Ssam /* 296170530Ssam * invert u bit to convert EUI64 to RFC2373 interface 297170530Ssam * ID. 298170530Ssam */ 299170530Ssam first_ifid[0] ^= 0x02; 300170530Ssam 301170530Ssam found_first_ifid = 1; 302170530Ssam } 303170530Ssam } 304170530Ssam 305170530Ssam if ((ifp->if_flags & IFF_MULTICAST) == 0) { 306170530Ssam printf("%s: not multicast capable, IPv6 not enabled\n", 307170530Ssam if_name(ifp)); 308170530Ssam return; 309170530Ssam } 310178354Ssam 311178354Ssam /* 312178354Ssam * We have some arrays that should be indexed by if_index. 313178354Ssam * since if_index will grow dynamically, they should grow too. 314178354Ssam * struct in6_ifstat **in6_ifstat 315178354Ssam * struct icmp6_ifstat **icmp6_ifstat 316178354Ssam */ 317178354Ssam if (in6_ifstat == NULL || icmp6_ifstat == NULL 318178354Ssam || if_index >= if_indexlim) { 319178354Ssam size_t n; 320178354Ssam caddr_t q; 321178354Ssam size_t olim; 322178354Ssam 323178354Ssam olim = if_indexlim; 324178354Ssam while (if_index >= if_indexlim) 325178354Ssam if_indexlim <<= 1; 326178354Ssam 327178354Ssam /* grow in6_ifstat */ 328178354Ssam n = if_indexlim * sizeof(struct in6_ifstat *); 329178354Ssam q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK); 330170530Ssam bzero(q, n); 331170530Ssam if (in6_ifstat) { 332170530Ssam bcopy((caddr_t)in6_ifstat, q, 333170530Ssam olim * sizeof(struct in6_ifstat *)); 334170530Ssam free((caddr_t)in6_ifstat, M_IFADDR); 335170530Ssam } 336178354Ssam in6_ifstat = (struct in6_ifstat **)q; 337170530Ssam in6_ifstatmax = if_indexlim; 338170530Ssam 339170530Ssam /* grow icmp6_ifstat */ 340170530Ssam n = if_indexlim * sizeof(struct icmp6_ifstat *); 341170530Ssam q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK); 342183247Ssam bzero(q, n); 343170530Ssam if (icmp6_ifstat) { 344170530Ssam bcopy((caddr_t)icmp6_ifstat, q, 345170530Ssam olim * sizeof(struct icmp6_ifstat *)); 346170530Ssam free((caddr_t)icmp6_ifstat, M_IFADDR); 347170530Ssam } 348183247Ssam icmp6_ifstat = (struct icmp6_ifstat **)q; 349183247Ssam icmp6_ifstatmax = if_indexlim; 350178354Ssam } 351170530Ssam 352170530Ssam /* 353170530Ssam * To prevent to assign link-local address to PnP network 354170530Ssam * cards multiple times. 355170530Ssam * This is lengthy for P2P and LOOP but works. 356170530Ssam */ 357170530Ssam ifa = TAILQ_FIRST(&ifp->if_addrlist); 358170530Ssam if (ifa != NULL) { 359170530Ssam for ( ; ifa; ifa = TAILQ_NEXT(ifa, ifa_list)) { 360170530Ssam if (ifa->ifa_addr->sa_family != AF_INET6) 361170530Ssam continue; 362170530Ssam if (IN6_IS_ADDR_LINKLOCAL(&satosin6(ifa->ifa_addr)->sin6_addr)) 363170530Ssam return; 364178354Ssam } 365170530Ssam } else { 366170530Ssam TAILQ_INIT(&ifp->if_addrlist); 367170530Ssam } 368170530Ssam 369170530Ssam /* 370170530Ssam * link-local address 371170530Ssam */ 372170530Ssam ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK); 373170530Ssam bzero((caddr_t)ia, sizeof(*ia)); 374170530Ssam ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; 375170530Ssam ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; 376170530Ssam ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; 377170530Ssam ia->ia_ifp = ifp; 378170530Ssam TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); 379170530Ssam /* 380170530Ssam * Also link into the IPv6 address chain beginning with in6_ifaddr. 381170530Ssam * kazu opposed it, but itojun & jinmei wanted. 382170530Ssam */ 383170530Ssam if ((oia = in6_ifaddr) != NULL) { 384170530Ssam for (; oia->ia_next; oia = oia->ia_next) 385170530Ssam continue; 386170530Ssam oia->ia_next = ia; 387170530Ssam } else 388170530Ssam in6_ifaddr = ia; 389170530Ssam 390170530Ssam ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); 391170530Ssam ia->ia_prefixmask.sin6_family = AF_INET6; 392170530Ssam ia->ia_prefixmask.sin6_addr = in6mask64; 393170530Ssam 394178354Ssam bzero(&ia->ia_addr, sizeof(struct sockaddr_in6)); 395170530Ssam ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); 396173273Ssam ia->ia_addr.sin6_family = AF_INET6; 397173273Ssam ia->ia_addr.sin6_addr.s6_addr16[0] = htons(0xfe80); 398173273Ssam ia->ia_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); 399173273Ssam ia->ia_addr.sin6_addr.s6_addr32[1] = 0; 400173273Ssam 401178354Ssam switch (type) { 402170530Ssam case IN6_IFT_LOOP: 403170530Ssam ia->ia_addr.sin6_addr.s6_addr32[2] = 0; 404173273Ssam ia->ia_addr.sin6_addr.s6_addr32[3] = htonl(1); 405170530Ssam break; 406173273Ssam case IN6_IFT_802: 407170530Ssam ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; 408170530Ssam ia->ia_ifa.ifa_flags |= RTF_CLONING; 409173273Ssam rtflag = RTF_CLONING; 410170530Ssam /* fall through */ 411178354Ssam case IN6_IFT_P2P802: 412170530Ssam if (laddr == NULL) 413170530Ssam break; 414170530Ssam /* XXX use laddrlen */ 415173273Ssam if (laddr_to_eui64(&ia->ia_addr.sin6_addr.s6_addr8[8], 416170530Ssam laddr, 6) != 0) { 417170530Ssam break; 418170530Ssam } 419170530Ssam /* invert u bit to convert EUI64 to RFC2373 interface ID. */ 420170530Ssam ia->ia_addr.sin6_addr.s6_addr8[8] ^= 0x02; 421173273Ssam if (found_first_ifid == 0) { 422178354Ssam if (in6_ifattach_getifid(ifp) == 0) 423173273Ssam in6_ifattach_p2p(); 424170530Ssam } 425173273Ssam break; 426170530Ssam case IN6_IFT_P2P: 427170530Ssam bcopy((caddr_t)first_ifid, 428170530Ssam (caddr_t)&ia->ia_addr.sin6_addr.s6_addr8[8], 429173273Ssam IFID_LEN); 430170530Ssam break; 431170530Ssam case IN6_IFT_ARCNET: 432173273Ssam ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; 433173273Ssam ia->ia_ifa.ifa_flags |= RTF_CLONING; 434173273Ssam rtflag = RTF_CLONING; 435173273Ssam if (laddr == NULL) 436173273Ssam break; 437173273Ssam 438173273Ssam /* make non-global IF id out of link-level address */ 439173273Ssam bzero(&ia->ia_addr.sin6_addr.s6_addr8[8], 7); 440178354Ssam ia->ia_addr.sin6_addr.s6_addr8[15] = *laddr; 441173273Ssam } 442173273Ssam 443173273Ssam ia->ia_ifa.ifa_metric = ifp->if_metric; 444173273Ssam 445173273Ssam if (ifp->if_ioctl != NULL) { 446173273Ssam int s; 447173273Ssam int error; 448173273Ssam 449173273Ssam /* 450173273Ssam * give the interface a chance to initialize, in case this 451173273Ssam * is the first address to be added. 452173273Ssam */ 453173273Ssam s = splimp(); 454173273Ssam error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); 455173273Ssam splx(s); 456173273Ssam 457173273Ssam if (error) { 458173273Ssam switch (error) { 459178354Ssam case EAFNOSUPPORT: 460173273Ssam printf("%s: IPv6 not supported\n", 461173273Ssam if_name(ifp)); 462173273Ssam break; 463173273Ssam default: 464173273Ssam printf("%s: SIOCSIFADDR error %d\n", 465173273Ssam if_name(ifp), error); 466173273Ssam break; 467173273Ssam } 468173273Ssam 469173273Ssam /* undo changes */ 470173273Ssam TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); 471173273Ssam if (oia) 472173273Ssam oia->ia_next = ia->ia_next; 473173273Ssam else 474178354Ssam in6_ifaddr = ia->ia_next; 475178354Ssam free(ia, M_IFADDR); 476178354Ssam return; 477178354Ssam } 478173273Ssam } 479173273Ssam 480173273Ssam /* add route to the interface. */ 481173273Ssam rtrequest(RTM_ADD, 482173273Ssam (struct sockaddr *)&ia->ia_addr, 483173273Ssam (struct sockaddr *)&ia->ia_addr, 484173273Ssam (struct sockaddr *)&ia->ia_prefixmask, 485173273Ssam RTF_UP|rtflag, 486173273Ssam (struct rtentry **)0); 487173273Ssam ia->ia_flags |= IFA_ROUTE; 488173273Ssam 489173273Ssam if (type == IN6_IFT_P2P || type == IN6_IFT_P2P802) { 490173273Ssam /* 491178354Ssam * route local address to loopback 492173273Ssam */ 493173273Ssam bzero(&gate, sizeof(gate)); 494173273Ssam gate.sin6_len = sizeof(struct sockaddr_in6); 495173273Ssam gate.sin6_family = AF_INET6; 496173273Ssam gate.sin6_addr = in6addr_loopback; 497173273Ssam bzero(&mask, sizeof(mask)); 498173273Ssam mask.sin6_len = sizeof(struct sockaddr_in6); 499173273Ssam mask.sin6_family = AF_INET6; 500173273Ssam mask.sin6_addr = in6mask64; 501173273Ssam rtrequest(RTM_ADD, 502173273Ssam (struct sockaddr *)&ia->ia_addr, 503170530Ssam (struct sockaddr *)&gate, 504170530Ssam (struct sockaddr *)&mask, 505170530Ssam RTF_UP|RTF_HOST, 506173273Ssam (struct rtentry **)0); 507170530Ssam } 508170530Ssam 509170530Ssam /* 510170530Ssam * loopback address 511170530Ssam */ 512170530Ssam ib = (struct in6_ifaddr *)NULL; 513170530Ssam if (type == IN6_IFT_LOOP) { 514170530Ssam ib = (struct in6_ifaddr *) 515173273Ssam malloc(sizeof(*ib), M_IFADDR, M_WAITOK); 516173273Ssam bzero((caddr_t)ib, sizeof(*ib)); 517178354Ssam ib->ia_ifa.ifa_addr = (struct sockaddr *)&ib->ia_addr; 518170530Ssam ib->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ib->ia_dstaddr; 519170530Ssam ib->ia_ifa.ifa_netmask = (struct sockaddr *)&ib->ia_prefixmask; 520170530Ssam ib->ia_ifp = ifp; 521170530Ssam 522170530Ssam ia->ia_next = ib; 523170530Ssam TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ib, 524183247Ssam ifa_list); 525183247Ssam 526170530Ssam ib->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); 527170530Ssam ib->ia_prefixmask.sin6_family = AF_INET6; 528170530Ssam ib->ia_prefixmask.sin6_addr = in6mask128; 529170530Ssam ib->ia_addr.sin6_len = sizeof(struct sockaddr_in6); 530183247Ssam ib->ia_addr.sin6_family = AF_INET6; 531183247Ssam ib->ia_addr.sin6_addr = in6addr_loopback; 532183247Ssam ib->ia_ifa.ifa_metric = ifp->if_metric; 533183247Ssam 534183247Ssam rtrequest(RTM_ADD, 535183247Ssam (struct sockaddr *)&ib->ia_addr, 536183247Ssam (struct sockaddr *)&ib->ia_addr, 537170530Ssam (struct sockaddr *)&ib->ia_prefixmask, 538173273Ssam RTF_UP|RTF_HOST, 539173273Ssam (struct rtentry **)0); 540173273Ssam 541173273Ssam ib->ia_flags |= IFA_ROUTE; 542173273Ssam } 543170530Ssam 544170530Ssam /* 545170530Ssam * join multicast 546170530Ssam */ 547170530Ssam if (ifp->if_flags & IFF_MULTICAST) { 548173273Ssam int error; /* not used */ 549170530Ssam 550182827Ssam bzero(&mltmask, sizeof(mltmask)); 551182827Ssam mltmask.sin6_len = sizeof(struct sockaddr_in6); 552182827Ssam mltmask.sin6_family = AF_INET6; 553182827Ssam mltmask.sin6_addr = in6mask32; 554182827Ssam 555182827Ssam /* 556182827Ssam * join link-local all-nodes address 557182827Ssam */ 558182827Ssam bzero(&mltaddr, sizeof(mltaddr)); 559182827Ssam mltaddr.sin6_len = sizeof(struct sockaddr_in6); 560182827Ssam mltaddr.sin6_family = AF_INET6; 561182827Ssam mltaddr.sin6_addr = in6addr_linklocal_allnodes; 562182827Ssam mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); 563182827Ssam rtrequest(RTM_ADD, 564182827Ssam (struct sockaddr *)&mltaddr, 565173273Ssam (struct sockaddr *)&ia->ia_addr, 566173273Ssam (struct sockaddr *)&mltmask, 567170530Ssam RTF_UP|RTF_CLONING, /* xxx */ 568170530Ssam (struct rtentry **)0); 569170530Ssam (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); 570170530Ssam 571170530Ssam if (type == IN6_IFT_LOOP) { 572170530Ssam /* 573170530Ssam * join node-local all-nodes address 574170530Ssam */ 575170530Ssam mltaddr.sin6_addr = in6addr_nodelocal_allnodes; 576170530Ssam rtrequest(RTM_ADD, 577170530Ssam (struct sockaddr *)&mltaddr, 578173273Ssam (struct sockaddr *)&ib->ia_addr, 579170530Ssam (struct sockaddr *)&mltmask, 580170530Ssam RTF_UP, 581170530Ssam (struct rtentry **)0); 582170530Ssam (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); 583170530Ssam } else { 584170530Ssam /* 585173273Ssam * join solicited multicast address 586170530Ssam */ 587170530Ssam bzero(&llsol, sizeof(llsol)); 588170530Ssam llsol.s6_addr16[0] = htons(0xff02); 589173273Ssam llsol.s6_addr16[1] = htons(ifp->if_index); 590170530Ssam llsol.s6_addr32[1] = 0; 591170530Ssam llsol.s6_addr32[2] = htonl(1); 592170530Ssam llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3]; 593173273Ssam llsol.s6_addr8[12] = 0xff; 594170530Ssam (void)in6_addmulti(&llsol, ifp, &error); 595173273Ssam } 596173273Ssam } 597173273Ssam 598173273Ssam /* update dynamically. */ 599173273Ssam if (in6_maxmtu < ifp->if_mtu) 600173273Ssam in6_maxmtu = ifp->if_mtu; 601173273Ssam 602173273Ssam if (in6_ifstat[ifp->if_index] == NULL) { 603173273Ssam in6_ifstat[ifp->if_index] = (struct in6_ifstat *) 604173273Ssam malloc(sizeof(struct in6_ifstat), M_IFADDR, M_WAITOK); 605173273Ssam bzero(in6_ifstat[ifp->if_index], sizeof(struct in6_ifstat)); 606173273Ssam } 607173273Ssam if (icmp6_ifstat[ifp->if_index] == NULL) { 608173273Ssam icmp6_ifstat[ifp->if_index] = (struct icmp6_ifstat *) 609170530Ssam malloc(sizeof(struct icmp6_ifstat), M_IFADDR, M_WAITOK); 610173273Ssam bzero(icmp6_ifstat[ifp->if_index], sizeof(struct icmp6_ifstat)); 611173273Ssam } 612173273Ssam 613173273Ssam /* initialize NDP variables */ 614173273Ssam nd6_ifattach(ifp); 615170530Ssam 616173273Ssam /* mark the address TENTATIVE, if needed. */ 617173273Ssam switch (ifp->if_type) { 618173273Ssam case IFT_ARCNET: 619173273Ssam case IFT_ETHER: 620173273Ssam case IFT_FDDI: 621173273Ssam ia->ia6_flags |= IN6_IFF_TENTATIVE; 622173273Ssam /* nd6_dad_start() will be called in in6_if_up */ 623173273Ssam break; 624178354Ssam#ifdef IFT_DUMMY 625173273Ssam case IFT_DUMMY: 626173273Ssam#endif 627173273Ssam case IFT_GIF: /*XXX*/ 628173273Ssam case IFT_LOOP: 629173273Ssam case IFT_FAITH: 630173273Ssam default: 631173273Ssam break; 632173273Ssam } 633173273Ssam 634173273Ssam return; 635173273Ssam} 636173273Ssam 637173273Ssamvoid 638173273Ssamin6_ifdetach(ifp) 639173273Ssam struct ifnet *ifp; 640173273Ssam{ 641173273Ssam struct in6_ifaddr *ia, *oia; 642173273Ssam struct ifaddr *ifa; 643178354Ssam struct rtentry *rt; 644173273Ssam short rtflags; 645178354Ssam 646173273Ssam TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) 647173273Ssam { 648173273Ssam if (ifa->ifa_addr->sa_family != AF_INET6 649173273Ssam || !IN6_IS_ADDR_LINKLOCAL(&satosin6(&ifa->ifa_addr)->sin6_addr)) { 650173273Ssam continue; 651178354Ssam } 652173273Ssam 653173273Ssam ia = (struct in6_ifaddr *)ifa; 654173273Ssam 655173273Ssam /* remove from the routing table */ 656173273Ssam if ((ia->ia_flags & IFA_ROUTE) 657173273Ssam && (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0, 0UL))) { 658173273Ssam rtflags = rt->rt_flags; 659173273Ssam rtfree(rt); 660173273Ssam rtrequest(RTM_DELETE, 661173273Ssam (struct sockaddr *)&ia->ia_addr, 662173273Ssam (struct sockaddr *)&ia->ia_addr, 663178354Ssam (struct sockaddr *)&ia->ia_prefixmask, 664173273Ssam rtflags, (struct rtentry **)0); 665170530Ssam } 666173273Ssam 667170530Ssam /* remove from the linked list */ 668178354Ssam TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); 669170530Ssam 670173273Ssam /* also remove from the IPv6 address chain(itojun&jinmei) */ 671173273Ssam oia = ia; 672173273Ssam if (oia == (ia = in6_ifaddr)) 673173273Ssam in6_ifaddr = ia->ia_next; 674173273Ssam else { 675173273Ssam while (ia->ia_next && (ia->ia_next != oia)) 676173273Ssam ia = ia->ia_next; 677173273Ssam if (ia->ia_next) 678173273Ssam ia->ia_next = oia->ia_next; 679173273Ssam#ifdef DEBUG 680173273Ssam else 681173273Ssam printf("%s: didn't unlink in6ifaddr from " 682170530Ssam "list\n", if_name(ifp)); 683170530Ssam#endif 684173273Ssam } 685173273Ssam 686170530Ssam free(ia, M_IFADDR); 687178354Ssam } 688173273Ssam} 689178354Ssam