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