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