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