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