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