]> git.saurik.com Git - apple/xnu.git/blame - bsd/netinet6/in6_ifattach.c
xnu-201.tar.gz
[apple/xnu.git] / bsd / netinet6 / in6_ifattach.c
CommitLineData
1c79356b
A
1/* $KAME: in6_ifattach.c,v 1.41 2000/03/16 07:05:34 jinmei Exp $ */
2
3/*
4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/malloc.h>
35#include <sys/socket.h>
36#include <sys/sockio.h>
37#include <sys/kernel.h>
38#ifdef __bsdi__
39#include <crypto/md5.h>
40#elif defined(__OpenBSD__)
41#include <sys/md5k.h>
42#else
43#include <sys/md5.h>
44#endif
45
46#include <net/if.h>
47#include <net/if_dl.h>
48#include <net/if_types.h>
49#include <net/route.h>
50
51#include <netinet/in.h>
52#include <netinet/in_var.h>
53#ifndef __NetBSD__
54#include <netinet/if_ether.h>
55#endif
56
57#include <netinet/ip6.h>
58#include <netinet6/ip6_var.h>
59#include <netinet6/in6_ifattach.h>
60#include <netinet6/ip6_var.h>
61#include <netinet6/nd6.h>
62
63#include <net/net_osdep.h>
64
65static struct in6_addr llsol;
66
67struct in6_ifstat **in6_ifstat = NULL;
68struct icmp6_ifstat **icmp6_ifstat = NULL;
69size_t in6_ifstatmax = 0;
70size_t icmp6_ifstatmax = 0;
71unsigned long in6_maxmtu = 0;
72
73int found_first_ifid = 0;
74#define IFID_LEN 8
75static u_int8_t first_ifid[IFID_LEN];
76
77static int laddr_to_eui64 __P((u_int8_t *, u_int8_t *, size_t));
78static int gen_rand_eui64 __P((u_int8_t *));
79
80#define DEBUG 1
81
82static int
83laddr_to_eui64(dst, src, len)
84 u_int8_t *dst;
85 u_int8_t *src;
86 size_t len;
87{
88 static u_int8_t zero[8];
89
90 bzero(zero, sizeof(zero));
91
92 switch (len) {
93 case 6:
94 if (bcmp(zero, src, 6) == 0)
95 return EINVAL;
96 dst[0] = src[0];
97 dst[1] = src[1];
98 dst[2] = src[2];
99 dst[3] = 0xff;
100 dst[4] = 0xfe;
101 dst[5] = src[3];
102 dst[6] = src[4];
103 dst[7] = src[5];
104 break;
105 case 8:
106 if (bcmp(zero, src, 8) == 0)
107 return EINVAL;
108 bcopy(src, dst, len);
109 break;
110 default:
111 return EINVAL;
112 }
113
114 return 0;
115}
116
117/*
118 * Generate a last-resort interface identifier, when the machine has no
119 * IEEE802/EUI64 address sources.
120 * The address should be random, and should not change across reboot.
121 */
122static int
123gen_rand_eui64(dst)
124 u_int8_t *dst;
125{
126 MD5_CTX ctxt;
127 u_int8_t digest[16];
128#if defined(__FreeBSD__) || defined (__APPLE__)
129 int hostnamelen = strlen(hostname);
130#endif
131
132 /* generate 8bytes of pseudo-random value. */
133 bzero(&ctxt, sizeof(ctxt));
134 MD5Init(&ctxt);
135 MD5Update(&ctxt, hostname, hostnamelen);
136 MD5Final(digest, &ctxt);
137
138 /* assumes sizeof(digest) > sizeof(first_ifid) */
139 bcopy(digest, dst, 8);
140
141 /* make sure to set "u" bit to local, and "g" bit to individual. */
142 dst[0] &= 0xfe;
143 dst[0] |= 0x02; /* EUI64 "local" */
144
145 return 0;
146}
147
148/*
149 * Find first ifid on list of interfaces.
150 * This is assumed that ifp0's interface token (for example, IEEE802 MAC)
151 * is globally unique. We may need to have a flag parameter in the future.
152 */
153int
154in6_ifattach_getifid(ifp0)
155 struct ifnet *ifp0;
156{
157 struct ifnet *ifp;
158 struct ifaddr *ifa;
159 u_int8_t *addr = NULL;
160 int addrlen = 0;
161 struct sockaddr_dl *sdl;
162
163 if (found_first_ifid)
164 return 0;
165
166#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
167 for (ifp = ifnet; ifp; ifp = ifp->if_next)
168#else
169 for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next)
170#endif
171 {
172 if (ifp0 != NULL && ifp0 != ifp)
173 continue;
174#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
175 for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
176#else
177 for (ifa = ifp->if_addrlist.tqh_first;
178 ifa;
179 ifa = ifa->ifa_list.tqe_next)
180#endif
181 {
182 if (ifa->ifa_addr->sa_family != AF_LINK)
183 continue;
184 sdl = (struct sockaddr_dl *)ifa->ifa_addr;
185 if (sdl == NULL)
186 continue;
187 if (sdl->sdl_alen == 0)
188 continue;
189 switch (ifp->if_type) {
190 case IFT_ETHER:
191 case IFT_FDDI:
192 case IFT_ATM:
193 /* IEEE802/EUI64 cases - what others? */
194 addr = LLADDR(sdl);
195 addrlen = sdl->sdl_alen;
196 /*
197 * to copy ifid from IEEE802/EUI64 interface,
198 * u bit of the source needs to be 0.
199 */
200 if ((addr[0] & 0x02) != 0)
201 break;
202 goto found;
203 case IFT_ARCNET:
204 /*
205 * ARCnet interface token cannot be used as
206 * globally unique identifier due to its
207 * small bitwidth.
208 */
209 break;
210 default:
211 break;
212 }
213 }
214 }
215#if DEBUG
216 printf("in6_ifattach_getifid: failed to get EUI64");
217#endif
218 return EADDRNOTAVAIL;
219
220found:
221 if (laddr_to_eui64(first_ifid, addr, addrlen) == 0)
222 found_first_ifid = 1;
223
224 if (found_first_ifid) {
225 printf("%s: supplying EUI64: "
226 "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
227 if_name(ifp),
228 first_ifid[0] & 0xff, first_ifid[1] & 0xff,
229 first_ifid[2] & 0xff, first_ifid[3] & 0xff,
230 first_ifid[4] & 0xff, first_ifid[5] & 0xff,
231 first_ifid[6] & 0xff, first_ifid[7] & 0xff);
232
233 /* invert u bit to convert EUI64 to RFC2373 interface ID. */
234 first_ifid[0] ^= 0x02;
235
236 return 0;
237 } else {
238#if DEBUG
239 printf("in6_ifattach_getifid: failed to get EUI64");
240#endif
241 return EADDRNOTAVAIL;
242 }
243}
244
245/*
246 * XXX multiple loopback interface needs more care. for instance,
247 * nodelocal address needs to be configured onto only one of them.
248 */
249void
250in6_ifattach(ifp, type, laddr, noloop)
251 struct ifnet *ifp;
252 u_int type;
253 caddr_t laddr;
254 /* size_t laddrlen; */
255 int noloop;
256{
257 static size_t if_indexlim = 8;
258 struct sockaddr_in6 mltaddr;
259 struct sockaddr_in6 mltmask;
260 struct sockaddr_in6 gate;
261 struct sockaddr_in6 mask;
262#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
263 struct ifaddr **ifap;
264#endif
265
266 struct in6_ifaddr *ia, *ib, *oia;
267 struct ifaddr *ifa;
268 int rtflag = 0;
269 int s;
270 int error;
271
272 if (type == IN6_IFT_P2P && found_first_ifid == 0) {
273 printf("%s: no ifid available for IPv6 link-local address\n",
274 if_name(ifp));
275#if 0
276 return;
277#else
278 /* last resort */
279 if (gen_rand_eui64(first_ifid) == 0) {
280 printf("%s: using random value as EUI64: "
281 "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
282 if_name(ifp),
283 first_ifid[0] & 0xff, first_ifid[1] & 0xff,
284 first_ifid[2] & 0xff, first_ifid[3] & 0xff,
285 first_ifid[4] & 0xff, first_ifid[5] & 0xff,
286 first_ifid[6] & 0xff, first_ifid[7] & 0xff);
287 /*
288 * invert u bit to convert EUI64 to RFC2373 interface
289 * ID.
290 */
291 first_ifid[0] ^= 0x02;
292
293 found_first_ifid = 1;
294 }
295#endif
296 }
297
298 if ((ifp->if_flags & IFF_MULTICAST) == 0) {
299 printf("%s: not multicast capable, IPv6 not enabled\n",
300 if_name(ifp));
301 return;
302 }
303
304 /*
305 * We have some arrays that should be indexed by if_index.
306 * since if_index will grow dynamically, they should grow too.
307 * struct in6_ifstat **in6_ifstat
308 * struct icmp6_ifstat **icmp6_ifstat
309 */
310 if (in6_ifstat == NULL || icmp6_ifstat == NULL
311 || if_index >= if_indexlim) {
312 size_t n;
313 caddr_t q;
314 size_t olim;
315
316 olim = if_indexlim;
317 while (if_index >= if_indexlim)
318 if_indexlim <<= 1;
319
320 /* grow in6_ifstat */
321 n = if_indexlim * sizeof(struct in6_ifstat *);
322 q = (caddr_t)_MALLOC(n, M_IFADDR, M_WAITOK);
323 bzero(q, n);
324 if (in6_ifstat) {
325 bcopy((caddr_t)in6_ifstat, q,
326 olim * sizeof(struct in6_ifstat *));
327 _FREE((caddr_t)in6_ifstat, M_IFADDR);
328 }
329 in6_ifstat = (struct in6_ifstat **)q;
330 in6_ifstatmax = if_indexlim;
331
332 /* grow icmp6_ifstat */
333 n = if_indexlim * sizeof(struct icmp6_ifstat *);
334 q = (caddr_t)_MALLOC(n, M_IFADDR, M_WAITOK);
335 bzero(q, n);
336 if (icmp6_ifstat) {
337 bcopy((caddr_t)icmp6_ifstat, q,
338 olim * sizeof(struct icmp6_ifstat *));
339 _FREE((caddr_t)icmp6_ifstat, M_IFADDR);
340 }
341 icmp6_ifstat = (struct icmp6_ifstat **)q;
342 icmp6_ifstatmax = if_indexlim;
343 }
344
345 /*
346 * To prevent to assign link-local address to PnP network
347 * cards multiple times.
348 * This is lengthy for P2P and LOOP but works.
349 */
350#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
351 ifa = ifp->if_addrlist;
352 if (ifa != NULL) {
353 for ( ; ifa; ifa = ifa->ifa_next) {
354 ifap = &ifa->ifa_next;
355 if (ifa->ifa_addr->sa_family != AF_INET6)
356 continue;
357 if (IN6_IS_ADDR_LINKLOCAL(&satosin6(ifa->ifa_addr)->sin6_addr))
358 return;
359 }
360 } else
361 ifap = &ifp->if_addrlist;
362#else
363 ifa = TAILQ_FIRST(&ifp->if_addrlist);
364 if (ifa != NULL) {
365 for ( ; ifa; ifa = TAILQ_NEXT(ifa, ifa_list)) {
366 if (ifa->ifa_addr->sa_family != AF_INET6)
367 continue;
368 if (IN6_IS_ADDR_LINKLOCAL(&satosin6(ifa->ifa_addr)->sin6_addr))
369 return;
370 }
371 } else {
372 TAILQ_INIT(&ifp->if_addrlist);
373 }
374#endif
375
376 /*
377 * link-local address
378 */
379 ia = (struct in6_ifaddr *)_MALLOC(sizeof(*ia), M_IFADDR, M_WAITOK);
380 bzero((caddr_t)ia, sizeof(*ia));
381 ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
382 if (ifp->if_flags & IFF_POINTOPOINT)
383 ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
384 else
385 ia->ia_ifa.ifa_dstaddr = NULL;
386 ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask;
387 ia->ia_ifp = ifp;
388#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
389 *ifap = (struct ifaddr *)ia;
390#else
391 TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
392#endif
393 ia->ia_ifa.ifa_refcnt++;
394
395 /*
396 * Also link into the IPv6 address chain beginning with in6_ifaddr.
397 * kazu opposed it, but itojun & jinmei wanted.
398 */
399 if ((oia = in6_ifaddr) != NULL) {
400 for (; oia->ia_next; oia = oia->ia_next)
401 continue;
402 oia->ia_next = ia;
403 } else
404 in6_ifaddr = ia;
405 ia->ia_ifa.ifa_refcnt++;
406
407 ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
408 ia->ia_prefixmask.sin6_family = AF_INET6;
409 ia->ia_prefixmask.sin6_addr = in6mask64;
410
411 bzero(&ia->ia_addr, sizeof(struct sockaddr_in6));
412 ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
413 ia->ia_addr.sin6_family = AF_INET6;
414 ia->ia_addr.sin6_addr.s6_addr16[0] = htons(0xfe80);
415 ia->ia_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
416 ia->ia_addr.sin6_addr.s6_addr32[1] = 0;
417
418 switch (type) {
419 case IN6_IFT_LOOP:
420 ia->ia_addr.sin6_addr.s6_addr32[2] = 0;
421 ia->ia_addr.sin6_addr.s6_addr32[3] = htonl(1);
422 if (strcmp(ifp->if_name, "lo") == 0) {
423 ia->ia_ifa.ifa_dlt = lo_attach_inet(ifp);
424 printf("in6_ifattach: IFT_LOOP setting initial ifp=%s%d initial ia=%x ifa_dlt=%x\n",
425 ifp->if_name, ifp->if_unit, ia, ia->ia_ifa.ifa_dlt);
426 }
427 break;
428 case IN6_IFT_802:
429 ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
430 ia->ia_ifa.ifa_flags |= RTF_CLONING;
431 rtflag = RTF_CLONING;
432 if (strcmp(ifp->if_name, "en") == 0) {
433 ia->ia_ifa.ifa_dlt = ether_attach_inet6(ifp);
434 printf("in6_ifattach: IFT_802 setting initial ifp=%s%d initial ia=%x ifa_dlt=%x\n",
435 ifp->if_name, ifp->if_unit, ia, ia->ia_ifa.ifa_dlt);
436 }
437
438 /* fall through */
439 case IN6_IFT_P2P802:
440 ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
441 ia->ia_ifa.ifa_flags |= RTF_CLONING;
442 rtflag = RTF_CLONING;
443 if (laddr == NULL)
444 break;
445 /* XXX use laddrlen */
446 if (laddr_to_eui64(&ia->ia_addr.sin6_addr.s6_addr8[8],
447 laddr, 6) != 0) {
448 break;
449 }
450 /* invert u bit to convert EUI64 to RFC2373 interface ID. */
451 ia->ia_addr.sin6_addr.s6_addr8[8] ^= 0x02;
452 if (found_first_ifid == 0)
453 in6_ifattach_getifid(ifp);
454 bzero(&ia->ia_dstaddr, sizeof(struct sockaddr_in6));
455 ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
456 ia->ia_dstaddr.sin6_family = AF_INET6;
457
458 if (ia->ia_ifa.ifa_dlt == 0) {
459 ia->ia_ifa.ifa_dlt = ifp;
460#if DEBUG
461 printf("in6_ifattach: IFT_P2P802 setting initial ifp=%s%d initial ia=%x ifa_dlt=%x\n",
462 ifp->if_name, ifp->if_unit, ia, ia->ia_ifa.ifa_dlt);
463#endif
464 }
465 break;
466 case IN6_IFT_P2P:
467 ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
468 ia->ia_ifa.ifa_flags |= RTF_CLONING;
469 rtflag = RTF_CLONING;
470 bcopy((caddr_t)first_ifid,
471 (caddr_t)&ia->ia_addr.sin6_addr.s6_addr8[8],
472 IFID_LEN);
473 bzero(&ia->ia_dstaddr, sizeof(struct sockaddr_in6));
474 ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
475 ia->ia_dstaddr.sin6_family = AF_INET6;
0b4e3aa0 476#if NGIF > 0
1c79356b
A
477 if (strcmp(ifp->if_name, "gif") == 0) {
478 ia->ia_ifa.ifa_dlt = gif_attach_inet(ifp);
479#if DEBUG
480 printf("in6_ifattach: IFT_P2P setting initial ifp=%s%d initial ia=%x ifa_dlt=%x\n",
481 ifp->if_name, ifp->if_unit, ia, ia->ia_ifa.ifa_dlt);
482#endif
483 }
0b4e3aa0 484#endif
1c79356b
A
485 break;
486 case IN6_IFT_ARCNET:
487 ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
488 ia->ia_ifa.ifa_flags |= RTF_CLONING;
489 rtflag = RTF_CLONING;
490 if (laddr == NULL)
491 break;
492
493 /* make non-global IF id out of link-level address */
494 bzero(&ia->ia_addr.sin6_addr.s6_addr8[8], 7);
495 ia->ia_addr.sin6_addr.s6_addr8[15] = *laddr;
496 ia->ia_ifa.ifa_dlt = ifp;
497#if DEBUG
498 printf("in6_ifattach: IFT_ARCNET setting initial ifp=%s%d initial ia=%x ifa_dlt=%x\n",
499 ifp->if_name, ifp->if_unit, ia, ia->ia_ifa.ifa_dlt);
500#endif
501 }
502
503 ia->ia_ifa.ifa_metric = ifp->if_metric;
504
505
506 /*
507 * give the interface a chance to initialize, in case this
508 * is the first address to be added.
509 */
510 s = splimp();
511#ifdef __APPLE__
512 error = dlil_ioctl(0, ifp, SIOCSIFADDR, (caddr_t)ia);
513#else
514 error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia);
515#endif
516 splx(s);
517#if DEBUG
518 printf("in6_ifattach: Calling SIOCSIFADDR for if=%s%d ia=%x error=%x\n", ifp->if_name, ifp->if_unit, ia, error);
519#endif
520 if (error == EOPNOTSUPP)
521 error = 0;
522
523 if (error) {
524 switch (error) {
525 case EAFNOSUPPORT:
526 printf("%s: IPv6 not supported\n",
527 if_name(ifp));
528 break;
529 default:
530 printf("%s: SIOCSIFADDR error %d\n",
531 if_name(ifp), error);
532 break;
533 }
534
535 /* undo changes */
536#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
537 *ifap = NULL;
538#else
539 TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
540#endif
541 IFAFREE(&ia->ia_ifa);
542 if (oia)
543 oia->ia_next = ia->ia_next;
544 else
545 in6_ifaddr = ia->ia_next;
546 IFAFREE(&ia->ia_ifa);
547 return;
548 }
549
550 /* add route to the interface. */
551 rtrequest(RTM_ADD,
552 (struct sockaddr *)&ia->ia_addr,
553 (struct sockaddr *)&ia->ia_addr,
554 (struct sockaddr *)&ia->ia_prefixmask,
555 RTF_UP|rtflag,
556 (struct rtentry **)0);
557 ia->ia_flags |= IFA_ROUTE;
558
559 if (type == IN6_IFT_P2P || type == IN6_IFT_P2P802) {
560 /*
561 * route local address to loopback
562 */
563 bzero(&gate, sizeof(gate));
564 gate.sin6_len = sizeof(struct sockaddr_in6);
565 gate.sin6_family = AF_INET6;
566 gate.sin6_addr = in6addr_loopback;
567 bzero(&mask, sizeof(mask));
568 mask.sin6_len = sizeof(struct sockaddr_in6);
569 mask.sin6_family = AF_INET6;
570 mask.sin6_addr = in6mask64;
571 rtrequest(RTM_ADD,
572 (struct sockaddr *)&ia->ia_addr,
573 (struct sockaddr *)&gate,
574 (struct sockaddr *)&mask,
575 RTF_UP|RTF_HOST,
576 (struct rtentry **)0);
577 }
578
579 /*
580 * loopback address
581 */
582 ib = (struct in6_ifaddr *)NULL;
583 if (type == IN6_IFT_LOOP) {
584 ib = (struct in6_ifaddr *)
585 _MALLOC(sizeof(*ib), M_IFADDR, M_WAITOK);
586 bzero((caddr_t)ib, sizeof(*ib));
587 ib->ia_ifa.ifa_addr = (struct sockaddr *)&ib->ia_addr;
588 ib->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ib->ia_dstaddr;
589 ib->ia_ifa.ifa_netmask = (struct sockaddr *)&ib->ia_prefixmask;
590 ib->ia_ifa.ifa_dlt = lo_attach_inet(ifp);
591 ib->ia_ifp = ifp;
592
593 ia->ia_next = ib;
594#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
595 ia->ia_ifa.ifa_next = (struct ifaddr *)ib;
596#else
597 TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ib,
598 ifa_list);
599#endif
600 ib->ia_ifa.ifa_refcnt++;
601
602 ib->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
603 ib->ia_prefixmask.sin6_family = AF_INET6;
604 ib->ia_prefixmask.sin6_addr = in6mask128;
605 ib->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
606 ib->ia_addr.sin6_family = AF_INET6;
607 ib->ia_addr.sin6_addr = in6addr_loopback;
608
609 /*
610 * Always initialize ia_dstaddr (= broadcast address)
611 * to loopback address, to make getifaddr happier.
612 *
613 * For BSDI, it is mandatory. The BSDI version of
614 * ifa_ifwithroute() rejects to add a route to the loopback
615 * interface. Even for other systems, loopback looks somewhat
616 * special.
617 */
618 ib->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
619 ib->ia_dstaddr.sin6_family = AF_INET6;
620 ib->ia_dstaddr.sin6_addr = in6addr_loopback;
621
622 ib->ia_ifa.ifa_metric = ifp->if_metric;
623
624 rtrequest(RTM_ADD,
625 (struct sockaddr *)&ib->ia_addr,
626 (struct sockaddr *)&ib->ia_addr,
627 (struct sockaddr *)&ib->ia_prefixmask,
628 RTF_UP|RTF_HOST,
629 (struct rtentry **)0);
630
631 ib->ia_flags |= IFA_ROUTE;
632 }
633
634 /*
635 * join multicast
636 */
637 if (ifp->if_flags & IFF_MULTICAST) {
638 int error; /* not used */
639
640#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined (__APPLE__)
641 /* Restore saved multicast addresses(if any). */
642 in6_restoremkludge(ia, ifp);
643#endif
644
645 bzero(&mltmask, sizeof(mltmask));
646 mltmask.sin6_len = sizeof(struct sockaddr_in6);
647 mltmask.sin6_family = AF_INET6;
648 mltmask.sin6_addr = in6mask32;
649
650 /*
651 * join link-local all-nodes address
652 */
653 bzero(&mltaddr, sizeof(mltaddr));
654 mltaddr.sin6_len = sizeof(struct sockaddr_in6);
655 mltaddr.sin6_family = AF_INET6;
656 mltaddr.sin6_addr = in6addr_linklocal_allnodes;
657 mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
658 rtrequest(RTM_ADD,
659 (struct sockaddr *)&mltaddr,
660 (struct sockaddr *)&ia->ia_addr,
661 (struct sockaddr *)&mltmask,
662 RTF_UP|RTF_CLONING, /* xxx */
663 (struct rtentry **)0);
664 (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
665
666 if (type == IN6_IFT_LOOP) {
667 /*
668 * join node-local all-nodes address
669 */
670 mltaddr.sin6_addr = in6addr_nodelocal_allnodes;
671 rtrequest(RTM_ADD,
672 (struct sockaddr *)&mltaddr,
673 (struct sockaddr *)&ib->ia_addr,
674 (struct sockaddr *)&mltmask,
675 RTF_UP,
676 (struct rtentry **)0);
677 (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
678 } else {
679 /*
680 * join solicited multicast address
681 */
682 bzero(&llsol, sizeof(llsol));
683 llsol.s6_addr16[0] = htons(0xff02);
684 llsol.s6_addr16[1] = htons(ifp->if_index);
685 llsol.s6_addr32[1] = 0;
686 llsol.s6_addr32[2] = htonl(1);
687 llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3];
688 llsol.s6_addr8[12] = 0xff;
689 (void)in6_addmulti(&llsol, ifp, &error);
690 }
691 }
692
693 /* update dynamically. */
694 if (in6_maxmtu < ifp->if_mtu)
695 in6_maxmtu = ifp->if_mtu;
696
697 if (in6_ifstat[ifp->if_index] == NULL) {
698 in6_ifstat[ifp->if_index] = (struct in6_ifstat *)
699 _MALLOC(sizeof(struct in6_ifstat), M_IFADDR, M_WAITOK);
700 bzero(in6_ifstat[ifp->if_index], sizeof(struct in6_ifstat));
701 }
702 if (icmp6_ifstat[ifp->if_index] == NULL) {
703 icmp6_ifstat[ifp->if_index] = (struct icmp6_ifstat *)
704 _MALLOC(sizeof(struct icmp6_ifstat), M_IFADDR, M_WAITOK);
705 bzero(icmp6_ifstat[ifp->if_index], sizeof(struct icmp6_ifstat));
706 }
707
708 /* initialize NDP variables */
709 nd6_ifattach(ifp);
710
711 /* mark the address TENTATIVE, if needed. */
712 switch (ifp->if_type) {
713 case IFT_ARCNET:
714 case IFT_ETHER:
715 case IFT_FDDI:
716#if 0
717 case IFT_ATM:
718 case IFT_SLIP:
719 case IFT_PPP:
720#endif
721 ia->ia6_flags |= IN6_IFF_TENTATIVE;
722 /* nd6_dad_start() will be called in in6_if_up */
723 break;
724 case IFT_DUMMY:
725 case IFT_GIF: /*XXX*/
726 case IFT_LOOP:
727 case IFT_FAITH:
728 default:
729 break;
730 }
731
732 return;
733}
734
735/*
736 * NOTE: in6_ifdetach() does not support loopback if at this moment.
737 */
738void
739in6_ifdetach(ifp)
740 struct ifnet *ifp;
741{
742 struct in6_ifaddr *ia, *oia;
743 struct ifaddr *ifa;
744#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
745 struct ifaddr *ifaprev = NULL;
746#endif
747 struct rtentry *rt;
748 short rtflags;
749 struct sockaddr_in6 sin6;
750 struct in6_multi *in6m;
751#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined (__APPLE__)
752 struct in6_multi *in6m_next;
753#endif
754
755 /* nuke prefix list. this may try to remove some of ifaddrs as well */
756 in6_purgeprefix(ifp);
757
758 /* remove neighbor management table */
759 nd6_purge(ifp);
760
761#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
762 for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
763#else
764 for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
765#endif
766 {
767 if (ifa->ifa_addr->sa_family != AF_INET6
768 || !IN6_IS_ADDR_LINKLOCAL(&satosin6(&ifa->ifa_addr)->sin6_addr)) {
769#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
770 ifaprev = ifa;
771#endif
772 continue;
773 }
774
775 ia = (struct in6_ifaddr *)ifa;
776
777#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined (__APPLE__)
778 /* leave from all multicast groups joined */
779 while ((in6m = LIST_FIRST(&oia->ia6_multiaddrs)) != NULL)
780 in6_delmulti(in6m);
781#endif
782
783 /* remove from the routing table */
784 if ((ia->ia_flags & IFA_ROUTE)
785 && (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0
786#if defined (__FreeBSD__) || defined (__APPLE__)
787 , 0UL
788#endif
789 ))) {
790 rtflags = rt->rt_flags;
791 rtfree(rt);
792 rtrequest(RTM_DELETE,
793 (struct sockaddr *)&ia->ia_addr,
794 (struct sockaddr *)&ia->ia_addr,
795 (struct sockaddr *)&ia->ia_prefixmask,
796 rtflags, (struct rtentry **)0);
797 }
798
799 /* remove from the linked list */
800#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
801 if (ifaprev)
802 ifaprev->ifa_next = ifa->ifa_next;
803 else
804 ifp->if_addrlist = ifa->ifa_next;
805#else
806 TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
807#endif
808
809 /* also remove from the IPv6 address chain(itojun&jinmei) */
810 oia = ia;
811 if (oia == (ia = in6_ifaddr))
812 in6_ifaddr = ia->ia_next;
813 else {
814 while (ia->ia_next && (ia->ia_next != oia))
815 ia = ia->ia_next;
816 if (ia->ia_next)
817 ia->ia_next = oia->ia_next;
818#if DEBUG
819 else
820 printf("%s: didn't unlink in6ifaddr from "
821 "list\n", if_name(ifp));
822#endif
823 }
824
825 _FREE(ia, M_IFADDR);
826 }
827
828#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined (__APPLE__)
829 /* leave from all multicast groups joined */
830 for (in6m = LIST_FIRST(&in6_multihead); in6m; in6m = in6m_next) {
831 in6m_next = LIST_NEXT(in6m, in6m_entry);
832 if (in6m->in6m_ifp != ifp)
833 continue;
834 in6_delmulti(in6m);
835 in6m = NULL;
836 }
837#endif
838
839#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__APPLE__)
840 /* cleanup multicast address kludge table, if there is any */
841 in6_purgemkludge(ifp);
842#endif
843
844 /* remove neighbor management table */
845 nd6_purge(ifp);
846
847 /* remove route to link-local allnodes multicast (ff02::1) */
848 bzero(&sin6, sizeof(sin6));
849 sin6.sin6_len = sizeof(struct sockaddr_in6);
850 sin6.sin6_family = AF_INET6;
851 sin6.sin6_addr = in6addr_linklocal_allnodes;
852 sin6.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
853#if !defined(__FreeBSD__) && !defined (__APPLE__)
854 if ((rt = rtalloc1((struct sockaddr *)&sin6, 0)) != NULL)
855#else
856 if ((rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL)) != NULL)
857#endif
858 {
859 rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt),
860 rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0);
861 rtfree(rt);
862 }
863}