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