]>
Commit | Line | Data |
---|---|---|
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 | ||
65 | static struct in6_addr llsol; | |
66 | ||
67 | struct in6_ifstat **in6_ifstat = NULL; | |
68 | struct icmp6_ifstat **icmp6_ifstat = NULL; | |
69 | size_t in6_ifstatmax = 0; | |
70 | size_t icmp6_ifstatmax = 0; | |
71 | unsigned long in6_maxmtu = 0; | |
72 | ||
73 | int found_first_ifid = 0; | |
74 | #define IFID_LEN 8 | |
75 | static u_int8_t first_ifid[IFID_LEN]; | |
76 | ||
77 | static int laddr_to_eui64 __P((u_int8_t *, u_int8_t *, size_t)); | |
78 | static int gen_rand_eui64 __P((u_int8_t *)); | |
79 | ||
80 | #define DEBUG 1 | |
81 | ||
82 | static int | |
83 | laddr_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 | */ | |
122 | static int | |
123 | gen_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 | */ | |
153 | int | |
154 | in6_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 | ||
220 | found: | |
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 | */ | |
249 | void | |
250 | in6_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 | */ | |
738 | void | |
739 | in6_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 | } |