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