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