in6_ifattach.c revision 1.7 1 /* $NetBSD: in6_ifattach.c,v 1.7 1999/09/13 12:15:55 itojun 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
38 #include <net/if.h>
39 #include <net/if_dl.h>
40 #include <net/if_types.h>
41 #include <net/route.h>
42
43 #include <netinet/in.h>
44 #include <netinet/in_var.h>
45 #ifndef __NetBSD__
46 #include <netinet/if_ether.h>
47 #endif
48
49 #include <netinet6/in6.h>
50 #include <netinet6/ip6.h>
51 #include <netinet6/ip6_var.h>
52 #include <netinet6/in6_ifattach.h>
53 #include <netinet6/ip6.h>
54 #include <netinet6/ip6_var.h>
55 #include <netinet6/nd6.h>
56
57 static struct in6_addr llsol;
58
59 struct in6_addr **in6_iflladdr = NULL;
60 unsigned long in6_maxmtu = 0;
61
62 int found_first_ifid = 0;
63 #define IFID_LEN 8
64 static char first_ifid[IFID_LEN];
65
66 static int laddr_to_eui64 __P((u_int8_t *, u_int8_t *, size_t));
67
68 static int
69 laddr_to_eui64(dst, src, len)
70 u_int8_t *dst;
71 u_int8_t *src;
72 size_t len;
73 {
74 switch (len) {
75 case 6:
76 dst[0] = src[0];
77 dst[1] = src[1];
78 dst[2] = src[2];
79 dst[3] = 0xff;
80 dst[4] = 0xfe;
81 dst[5] = src[3];
82 dst[6] = src[4];
83 dst[7] = src[5];
84 break;
85 case 8:
86 bcopy(src, dst, len);
87 break;
88 default:
89 return EINVAL;
90 }
91
92 return 0;
93 }
94
95 /*
96 * Find first ifid on list of interfaces.
97 * This is assumed that ifp0's interface token (for example, IEEE802 MAC)
98 * is globally unique. We may need to have a flag parameter in the future.
99 */
100 int
101 in6_ifattach_getifid(ifp0)
102 struct ifnet *ifp0;
103 {
104 struct ifnet *ifp;
105 struct ifaddr *ifa;
106 u_int8_t *addr = NULL;
107 int addrlen = 0;
108 struct sockaddr_dl *sdl;
109
110 if (found_first_ifid)
111 return 0;
112
113 for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) {
114 if (ifp0 != NULL && ifp0 != ifp)
115 continue;
116 for (ifa = ifp->if_addrlist.tqh_first;
117 ifa;
118 ifa = ifa->ifa_list.tqe_next) {
119 if (ifa->ifa_addr->sa_family != AF_LINK)
120 continue;
121 sdl = (struct sockaddr_dl *)ifa->ifa_addr;
122 if (sdl == NULL)
123 continue;
124 if (sdl->sdl_alen == 0)
125 continue;
126 switch (ifp->if_type) {
127 case IFT_ETHER:
128 case IFT_FDDI:
129 case IFT_ATM:
130 /* IEEE802/EUI64 cases - what others? */
131 addr = LLADDR(sdl);
132 addrlen = sdl->sdl_alen;
133 /*
134 * to copy ifid from IEEE802/EUI64 interface,
135 * u bit of the source needs to be 0.
136 */
137 if ((addr[0] & 0x02) != 0)
138 break;
139 goto found;
140 default:
141 break;
142 }
143 }
144 }
145 #ifdef DEBUG
146 printf("in6_ifattach_getifid: failed to get EUI64");
147 #endif
148 return EADDRNOTAVAIL;
149
150 found:
151 if (laddr_to_eui64(first_ifid, addr, addrlen) == 0)
152 found_first_ifid = 1;
153
154 if (found_first_ifid) {
155 printf("%s: supplying EUI64: "
156 "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
157 ifp->if_xname,
158 first_ifid[0] & 0xff, first_ifid[1] & 0xff,
159 first_ifid[2] & 0xff, first_ifid[3] & 0xff,
160 first_ifid[4] & 0xff, first_ifid[5] & 0xff,
161 first_ifid[6] & 0xff, first_ifid[7] & 0xff);
162
163 /* invert u bit to convert EUI64 to RFC2373 interface ID. */
164 first_ifid[0] ^= 0x02;
165
166 return 0;
167 } else {
168 #ifdef DEBUG
169 printf("in6_ifattach_getifid: failed to get EUI64");
170 #endif
171 return EADDRNOTAVAIL;
172 }
173 }
174
175 /*
176 * add link-local address to *pseudo* p2p interfaces.
177 * get called when the first MAC address is made available in in6_ifattach().
178 *
179 * XXX I start feeling this as a bad idea. (itojun)
180 */
181 void
182 in6_ifattach_p2p()
183 {
184 struct ifnet *ifp;
185
186 /* prevent infinite loop. just in case. */
187 if (found_first_ifid == 0)
188 return;
189
190 for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) {
191 switch (ifp->if_type) {
192 case IFT_GIF:
193 /* pseudo interfaces - safe to initialize here */
194 in6_ifattach(ifp, IN6_IFT_P2P, 0, 0);
195 break;
196 case IFT_FAITH:
197 /* this mistakingly becomes IFF_UP */
198 break;
199 case IFT_SLIP:
200 /* IPv6 is not supported */
201 break;
202 case IFT_PPP:
203 /* this is not a pseudo interface, skip it */
204 break;
205 default:
206 break;
207 }
208 }
209 }
210
211 void
212 in6_ifattach(ifp, type, laddr, noloop)
213 struct ifnet *ifp;
214 u_int type;
215 caddr_t laddr;
216 /* size_t laddrlen; */
217 int noloop;
218 {
219 static size_t if_indexlim = 8;
220 struct sockaddr_in6 mltaddr;
221 struct sockaddr_in6 mltmask;
222 struct sockaddr_in6 gate;
223 struct sockaddr_in6 mask;
224
225 struct in6_ifaddr *ia, *ib, *oia;
226 struct ifaddr *ifa;
227 int rtflag = 0;
228
229 if (type == IN6_IFT_P2P && found_first_ifid == 0) {
230 printf("%s: no ifid available for IPv6 link-local address\n",
231 ifp->if_xname);
232 return;
233 }
234
235 if ((ifp->if_flags & IFF_MULTICAST) == 0) {
236 printf("%s: not multicast capable, IPv6 not enabled\n",
237 ifp->if_xname);
238 return;
239 }
240
241 /*
242 * We have some arrays that should be indexed by if_index.
243 * since if_index will grow dynamically, they should grow too.
244 * struct in6_addr **in6_iflladdr
245 */
246 if (in6_iflladdr == NULL || if_index >= if_indexlim) {
247 size_t n;
248 caddr_t q;
249 size_t olim;
250
251 olim = if_indexlim;
252 while (if_index >= if_indexlim)
253 if_indexlim <<= 1;
254
255 /* grow in6_iflladdr */
256 n = if_indexlim * sizeof(struct in6_addr *);
257 q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK);
258 bzero(q, n);
259 if (in6_iflladdr) {
260 bcopy((caddr_t)in6_iflladdr, q,
261 olim * sizeof(struct in6_addr *));
262 free((caddr_t)in6_iflladdr, M_IFADDR);
263 }
264 in6_iflladdr = (struct in6_addr **)q;
265 }
266
267 /*
268 * To prevent to assign link-local address to PnP network
269 * cards multiple times.
270 * This is lengthy for P2P and LOOP but works.
271 */
272 ifa = TAILQ_FIRST(&ifp->if_addrlist);
273 if (ifa != NULL) {
274 for ( ; ifa; ifa = TAILQ_NEXT(ifa, ifa_list)) {
275 if (ifa->ifa_addr->sa_family != AF_INET6)
276 continue;
277 if (IN6_IS_ADDR_LINKLOCAL(&satosin6(ifa->ifa_addr)->sin6_addr))
278 return;
279 }
280 } else {
281 TAILQ_INIT(&ifp->if_addrlist);
282 }
283
284 /*
285 * link-local address
286 */
287 ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK);
288 bzero((caddr_t)ia, sizeof(*ia));
289 ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
290 ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
291 ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask;
292 ia->ia_ifp = ifp;
293 TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
294 /*
295 * Also link into the IPv6 address chain beginning with in6_ifaddr.
296 * kazu opposed it, but itojun & jinmei wanted.
297 */
298 if ((oia = in6_ifaddr) != NULL) {
299 for (; oia->ia_next; oia = oia->ia_next)
300 continue;
301 oia->ia_next = ia;
302 } else
303 in6_ifaddr = ia;
304
305 ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
306 ia->ia_prefixmask.sin6_family = AF_INET6;
307 ia->ia_prefixmask.sin6_addr = in6mask64;
308
309 bzero(&ia->ia_addr, sizeof(struct sockaddr_in6));
310 ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
311 ia->ia_addr.sin6_family = AF_INET6;
312 ia->ia_addr.sin6_addr.s6_addr16[0] = htons(0xfe80);
313 ia->ia_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
314 ia->ia_addr.sin6_addr.s6_addr32[1] = 0;
315
316 switch (type) {
317 case IN6_IFT_LOOP:
318 ia->ia_addr.sin6_addr.s6_addr32[2] = 0;
319 ia->ia_addr.sin6_addr.s6_addr32[3] = htonl(1);
320 break;
321 case IN6_IFT_802:
322 ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
323 ia->ia_ifa.ifa_flags |= RTF_CLONING;
324 rtflag = RTF_CLONING;
325 /* fall through */
326 case IN6_IFT_P2P802:
327 if (laddr == NULL)
328 break;
329 /* XXX use laddrlen */
330 if (laddr_to_eui64(&ia->ia_addr.sin6_addr.s6_addr8[8],
331 laddr, 6) != 0) {
332 break;
333 }
334 /* invert u bit to convert EUI64 to RFC2373 interface ID. */
335 ia->ia_addr.sin6_addr.s6_addr8[8] ^= 0x02;
336 if (found_first_ifid == 0) {
337 if (in6_ifattach_getifid(ifp) == 0)
338 in6_ifattach_p2p();
339 }
340 break;
341 case IN6_IFT_P2P:
342 bcopy((caddr_t)first_ifid,
343 (caddr_t)&ia->ia_addr.sin6_addr.s6_addr8[8],
344 IFID_LEN);
345 break;
346 }
347
348 ia->ia_ifa.ifa_metric = ifp->if_metric;
349
350 if (ifp->if_ioctl != NULL) {
351 int s;
352 int error;
353
354 /*
355 * give the interface a chance to initialize, in case this
356 * is the first address to be added.
357 */
358 s = splimp();
359 error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia);
360 splx(s);
361
362 if (error) {
363 switch (error) {
364 case EAFNOSUPPORT:
365 printf("%s: IPv6 not supported\n",
366 ifp->if_xname);
367 break;
368 default:
369 printf("%s: SIOCSIFADDR error %d\n",
370 ifp->if_xname, error);
371 break;
372 }
373
374 /* undo changes */
375 TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
376 if (oia)
377 oia->ia_next = ia->ia_next;
378 else
379 in6_ifaddr = ia->ia_next;
380 free(ia, M_IFADDR);
381 return;
382 }
383 }
384
385 /* add route to the interface. */
386 rtrequest(RTM_ADD,
387 (struct sockaddr *)&ia->ia_addr,
388 (struct sockaddr *)&ia->ia_addr,
389 (struct sockaddr *)&ia->ia_prefixmask,
390 RTF_UP|rtflag,
391 (struct rtentry **)0);
392 ia->ia_flags |= IFA_ROUTE;
393
394 if (type == IN6_IFT_P2P || type == IN6_IFT_P2P802) {
395 /*
396 * route local address to loopback
397 */
398 bzero(&gate, sizeof(gate));
399 gate.sin6_len = sizeof(struct sockaddr_in6);
400 gate.sin6_family = AF_INET6;
401 gate.sin6_addr = in6addr_loopback;
402 bzero(&mask, sizeof(mask));
403 mask.sin6_len = sizeof(struct sockaddr_in6);
404 mask.sin6_family = AF_INET6;
405 mask.sin6_addr = in6mask64;
406 rtrequest(RTM_ADD,
407 (struct sockaddr *)&ia->ia_addr,
408 (struct sockaddr *)&gate,
409 (struct sockaddr *)&mask,
410 RTF_UP|RTF_HOST,
411 (struct rtentry **)0);
412 }
413
414 /*
415 * loopback address
416 */
417 ib = (struct in6_ifaddr *)NULL;
418 if (type == IN6_IFT_LOOP) {
419 ib = (struct in6_ifaddr *)
420 malloc(sizeof(*ib), M_IFADDR, M_WAITOK);
421 bzero((caddr_t)ib, sizeof(*ib));
422 ib->ia_ifa.ifa_addr = (struct sockaddr *)&ib->ia_addr;
423 ib->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ib->ia_dstaddr;
424 ib->ia_ifa.ifa_netmask = (struct sockaddr *)&ib->ia_prefixmask;
425 ib->ia_ifp = ifp;
426
427 ia->ia_next = ib;
428 TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ib,
429 ifa_list);
430
431 ib->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
432 ib->ia_prefixmask.sin6_family = AF_INET6;
433 ib->ia_prefixmask.sin6_addr = in6mask128;
434 ib->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
435 ib->ia_addr.sin6_family = AF_INET6;
436 ib->ia_addr.sin6_addr = in6addr_loopback;
437 #ifdef __bsdi__
438 /*
439 * It is necessary to set the loopback address to the dstaddr
440 * field at least for BSDI. Without this setting, the BSDI
441 * version of ifa_ifwithroute() rejects to add a route
442 * to the loopback interface.
443 */
444 ib->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
445 ib->ia_dstaddr.sin6_family = AF_INET6;
446 ib->ia_dstaddr.sin6_addr = in6addr_loopback;
447 #endif /* __bsdi__ */
448
449 ib->ia_ifa.ifa_metric = ifp->if_metric;
450
451 rtrequest(RTM_ADD,
452 (struct sockaddr *)&ib->ia_addr,
453 (struct sockaddr *)&ib->ia_addr,
454 (struct sockaddr *)&ib->ia_prefixmask,
455 RTF_UP|RTF_HOST,
456 (struct rtentry **)0);
457
458 ib->ia_flags |= IFA_ROUTE;
459 }
460
461 /*
462 * join multicast
463 */
464 if (ifp->if_flags & IFF_MULTICAST) {
465 int error; /* not used */
466
467 #if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
468 /* Restore saved multicast addresses(if any). */
469 in6_restoremkludge(ia, ifp);
470 #endif
471
472 bzero(&mltmask, sizeof(mltmask));
473 mltmask.sin6_len = sizeof(struct sockaddr_in6);
474 mltmask.sin6_family = AF_INET6;
475 mltmask.sin6_addr = in6mask32;
476
477 /*
478 * join link-local all-nodes address
479 */
480 bzero(&mltaddr, sizeof(mltaddr));
481 mltaddr.sin6_len = sizeof(struct sockaddr_in6);
482 mltaddr.sin6_family = AF_INET6;
483 mltaddr.sin6_addr = in6addr_linklocal_allnodes;
484 mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
485 rtrequest(RTM_ADD,
486 (struct sockaddr *)&mltaddr,
487 (struct sockaddr *)&ia->ia_addr,
488 (struct sockaddr *)&mltmask,
489 RTF_UP|RTF_CLONING, /* xxx */
490 (struct rtentry **)0);
491 (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
492
493 if (type == IN6_IFT_LOOP) {
494 /*
495 * join node-local all-nodes address
496 */
497 mltaddr.sin6_addr = in6addr_nodelocal_allnodes;
498 rtrequest(RTM_ADD,
499 (struct sockaddr *)&mltaddr,
500 (struct sockaddr *)&ib->ia_addr,
501 (struct sockaddr *)&mltmask,
502 RTF_UP,
503 (struct rtentry **)0);
504 (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
505 } else {
506 /*
507 * join solicited multicast address
508 */
509 bzero(&llsol, sizeof(llsol));
510 llsol.s6_addr16[0] = htons(0xff02);
511 llsol.s6_addr16[1] = htons(ifp->if_index);
512 llsol.s6_addr32[1] = 0;
513 llsol.s6_addr32[2] = htonl(1);
514 llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3];
515 llsol.s6_addr8[12] = 0xff;
516 (void)in6_addmulti(&llsol, ifp, &error);
517 }
518 }
519
520 /* update dynamically. */
521 in6_iflladdr[ifp->if_index] = &ia->ia_addr.sin6_addr;
522 if (in6_maxmtu < ifp->if_mtu)
523 in6_maxmtu = ifp->if_mtu;
524
525 /* initialize NDP variables */
526 nd6_ifattach(ifp);
527
528 /* mark the address TENTATIVE, if needed. */
529 switch (ifp->if_type) {
530 case IFT_ETHER:
531 case IFT_FDDI:
532 #if 0
533 case IFT_ATM:
534 case IFT_SLIP:
535 case IFT_PPP:
536 #endif
537 ia->ia6_flags |= IN6_IFF_TENTATIVE;
538 /* nd6_dad_start() will be called in in6_if_up */
539 break;
540 case IFT_GIF:
541 case IFT_LOOP:
542 case IFT_FAITH:
543 default:
544 break;
545 }
546
547 return;
548 }
549
550 void
551 in6_ifdetach(ifp)
552 struct ifnet *ifp;
553 {
554 struct in6_ifaddr *ia, *oia;
555 struct ifaddr *ifa;
556 struct rtentry *rt;
557 short rtflags;
558
559 for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) {
560 if (ifa->ifa_addr->sa_family != AF_INET6
561 || !IN6_IS_ADDR_LINKLOCAL(&satosin6(&ifa->ifa_addr)->sin6_addr)) {
562 continue;
563 }
564
565 ia = (struct in6_ifaddr *)ifa;
566
567 /* remove from the routing table */
568 if ((ia->ia_flags & IFA_ROUTE)
569 && (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0))) {
570 rtflags = rt->rt_flags;
571 rtfree(rt);
572 rtrequest(RTM_DELETE,
573 (struct sockaddr *)&ia->ia_addr,
574 (struct sockaddr *)&ia->ia_addr,
575 (struct sockaddr *)&ia->ia_prefixmask,
576 rtflags, (struct rtentry **)0);
577 }
578
579 /* remove from the linked list */
580 TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
581
582 /* also remove from the IPv6 address chain(itojun&jinmei) */
583 oia = ia;
584 if (oia == (ia = in6_ifaddr))
585 in6_ifaddr = ia->ia_next;
586 else {
587 while (ia->ia_next && (ia->ia_next != oia))
588 ia = ia->ia_next;
589 if (ia->ia_next)
590 ia->ia_next = oia->ia_next;
591 #ifdef DEBUG
592 else
593 printf("%s: didn't unlink in6ifaddr from "
594 "list\n", ifp->if_xname);
595 #endif
596 }
597
598 free(ia, M_IFADDR);
599 }
600 }
601