in6_ifattach.c revision 1.10 1 /* $NetBSD: in6_ifattach.c,v 1.10 1999/09/20 02:35:44 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 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 }
370
371 ia->ia_ifa.ifa_metric = ifp->if_metric;
372
373 if (ifp->if_ioctl != NULL) {
374 int s;
375 int error;
376
377 /*
378 * give the interface a chance to initialize, in case this
379 * is the first address to be added.
380 */
381 s = splimp();
382 error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia);
383 splx(s);
384
385 if (error) {
386 switch (error) {
387 case EAFNOSUPPORT:
388 printf("%s: IPv6 not supported\n",
389 ifp->if_xname);
390 break;
391 default:
392 printf("%s: SIOCSIFADDR error %d\n",
393 ifp->if_xname, error);
394 break;
395 }
396
397 /* undo changes */
398 TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
399 if (oia)
400 oia->ia_next = ia->ia_next;
401 else
402 in6_ifaddr = ia->ia_next;
403 free(ia, M_IFADDR);
404 return;
405 }
406 }
407
408 /* add route to the interface. */
409 rtrequest(RTM_ADD,
410 (struct sockaddr *)&ia->ia_addr,
411 (struct sockaddr *)&ia->ia_addr,
412 (struct sockaddr *)&ia->ia_prefixmask,
413 RTF_UP|rtflag,
414 (struct rtentry **)0);
415 ia->ia_flags |= IFA_ROUTE;
416
417 if (type == IN6_IFT_P2P || type == IN6_IFT_P2P802) {
418 /*
419 * route local address to loopback
420 */
421 bzero(&gate, sizeof(gate));
422 gate.sin6_len = sizeof(struct sockaddr_in6);
423 gate.sin6_family = AF_INET6;
424 gate.sin6_addr = in6addr_loopback;
425 bzero(&mask, sizeof(mask));
426 mask.sin6_len = sizeof(struct sockaddr_in6);
427 mask.sin6_family = AF_INET6;
428 mask.sin6_addr = in6mask64;
429 rtrequest(RTM_ADD,
430 (struct sockaddr *)&ia->ia_addr,
431 (struct sockaddr *)&gate,
432 (struct sockaddr *)&mask,
433 RTF_UP|RTF_HOST,
434 (struct rtentry **)0);
435 }
436
437 /*
438 * loopback address
439 */
440 ib = (struct in6_ifaddr *)NULL;
441 if (type == IN6_IFT_LOOP) {
442 ib = (struct in6_ifaddr *)
443 malloc(sizeof(*ib), M_IFADDR, M_WAITOK);
444 bzero((caddr_t)ib, sizeof(*ib));
445 ib->ia_ifa.ifa_addr = (struct sockaddr *)&ib->ia_addr;
446 ib->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ib->ia_dstaddr;
447 ib->ia_ifa.ifa_netmask = (struct sockaddr *)&ib->ia_prefixmask;
448 ib->ia_ifp = ifp;
449
450 ia->ia_next = ib;
451 TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ib,
452 ifa_list);
453
454 ib->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
455 ib->ia_prefixmask.sin6_family = AF_INET6;
456 ib->ia_prefixmask.sin6_addr = in6mask128;
457 ib->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
458 ib->ia_addr.sin6_family = AF_INET6;
459 ib->ia_addr.sin6_addr = in6addr_loopback;
460 #ifdef __bsdi__
461 /*
462 * It is necessary to set the loopback address to the dstaddr
463 * field at least for BSDI. Without this setting, the BSDI
464 * version of ifa_ifwithroute() rejects to add a route
465 * to the loopback interface.
466 */
467 ib->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
468 ib->ia_dstaddr.sin6_family = AF_INET6;
469 ib->ia_dstaddr.sin6_addr = in6addr_loopback;
470 #endif /* __bsdi__ */
471
472 ib->ia_ifa.ifa_metric = ifp->if_metric;
473
474 rtrequest(RTM_ADD,
475 (struct sockaddr *)&ib->ia_addr,
476 (struct sockaddr *)&ib->ia_addr,
477 (struct sockaddr *)&ib->ia_prefixmask,
478 RTF_UP|RTF_HOST,
479 (struct rtentry **)0);
480
481 ib->ia_flags |= IFA_ROUTE;
482 }
483
484 /*
485 * join multicast
486 */
487 if (ifp->if_flags & IFF_MULTICAST) {
488 int error; /* not used */
489
490 #if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
491 /* Restore saved multicast addresses(if any). */
492 in6_restoremkludge(ia, ifp);
493 #endif
494
495 bzero(&mltmask, sizeof(mltmask));
496 mltmask.sin6_len = sizeof(struct sockaddr_in6);
497 mltmask.sin6_family = AF_INET6;
498 mltmask.sin6_addr = in6mask32;
499
500 /*
501 * join link-local all-nodes address
502 */
503 bzero(&mltaddr, sizeof(mltaddr));
504 mltaddr.sin6_len = sizeof(struct sockaddr_in6);
505 mltaddr.sin6_family = AF_INET6;
506 mltaddr.sin6_addr = in6addr_linklocal_allnodes;
507 mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
508 rtrequest(RTM_ADD,
509 (struct sockaddr *)&mltaddr,
510 (struct sockaddr *)&ia->ia_addr,
511 (struct sockaddr *)&mltmask,
512 RTF_UP|RTF_CLONING, /* xxx */
513 (struct rtentry **)0);
514 (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
515
516 if (type == IN6_IFT_LOOP) {
517 /*
518 * join node-local all-nodes address
519 */
520 mltaddr.sin6_addr = in6addr_nodelocal_allnodes;
521 rtrequest(RTM_ADD,
522 (struct sockaddr *)&mltaddr,
523 (struct sockaddr *)&ib->ia_addr,
524 (struct sockaddr *)&mltmask,
525 RTF_UP,
526 (struct rtentry **)0);
527 (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
528 } else {
529 /*
530 * join solicited multicast address
531 */
532 bzero(&llsol, sizeof(llsol));
533 llsol.s6_addr16[0] = htons(0xff02);
534 llsol.s6_addr16[1] = htons(ifp->if_index);
535 llsol.s6_addr32[1] = 0;
536 llsol.s6_addr32[2] = htonl(1);
537 llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3];
538 llsol.s6_addr8[12] = 0xff;
539 (void)in6_addmulti(&llsol, ifp, &error);
540 }
541 }
542
543 /* update dynamically. */
544 in6_iflladdr[ifp->if_index] = &ia->ia_addr.sin6_addr;
545 if (in6_maxmtu < ifp->if_mtu)
546 in6_maxmtu = ifp->if_mtu;
547
548 /* initialize NDP variables */
549 nd6_ifattach(ifp);
550
551 /* mark the address TENTATIVE, if needed. */
552 switch (ifp->if_type) {
553 case IFT_ARCNET:
554 case IFT_ETHER:
555 case IFT_FDDI:
556 #if 0
557 case IFT_ATM:
558 case IFT_SLIP:
559 case IFT_PPP:
560 #endif
561 ia->ia6_flags |= IN6_IFF_TENTATIVE;
562 /* nd6_dad_start() will be called in in6_if_up */
563 break;
564 case IFT_GIF:
565 case IFT_LOOP:
566 case IFT_FAITH:
567 default:
568 break;
569 }
570
571 return;
572 }
573
574 void
575 in6_ifdetach(ifp)
576 struct ifnet *ifp;
577 {
578 struct in6_ifaddr *ia, *oia;
579 struct ifaddr *ifa;
580 struct rtentry *rt;
581 short rtflags;
582
583 for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) {
584 if (ifa->ifa_addr->sa_family != AF_INET6
585 || !IN6_IS_ADDR_LINKLOCAL(&satosin6(&ifa->ifa_addr)->sin6_addr)) {
586 continue;
587 }
588
589 ia = (struct in6_ifaddr *)ifa;
590
591 /* remove from the routing table */
592 if ((ia->ia_flags & IFA_ROUTE)
593 && (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0))) {
594 rtflags = rt->rt_flags;
595 rtfree(rt);
596 rtrequest(RTM_DELETE,
597 (struct sockaddr *)&ia->ia_addr,
598 (struct sockaddr *)&ia->ia_addr,
599 (struct sockaddr *)&ia->ia_prefixmask,
600 rtflags, (struct rtentry **)0);
601 }
602
603 /* remove from the linked list */
604 TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
605
606 /* also remove from the IPv6 address chain(itojun&jinmei) */
607 oia = ia;
608 if (oia == (ia = in6_ifaddr))
609 in6_ifaddr = ia->ia_next;
610 else {
611 while (ia->ia_next && (ia->ia_next != oia))
612 ia = ia->ia_next;
613 if (ia->ia_next)
614 ia->ia_next = oia->ia_next;
615 #ifdef DEBUG
616 else
617 printf("%s: didn't unlink in6ifaddr from "
618 "list\n", ifp->if_xname);
619 #endif
620 }
621
622 free(ia, M_IFADDR);
623 }
624 }
625