Annotation of src/sys/netinet6/in6_ifattach.c, Revision 1.9
1.9 ! is 1: /* $NetBSD: in6_ifattach.c,v 1.8 1999/09/19 21:31:34 is Exp $ */
1.3 thorpej 2:
1.2 itojun 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:
1.7 itojun 66: static int laddr_to_eui64 __P((u_int8_t *, u_int8_t *, size_t));
1.2 itojun 67:
1.7 itojun 68: static int
69: laddr_to_eui64(dst, src, len)
1.2 itojun 70: u_int8_t *dst;
71: u_int8_t *src;
1.7 itojun 72: size_t len;
1.2 itojun 73: {
1.7 itojun 74: switch (len) {
1.8 is 75: case 1:
76: bzero(dst, 7);
77: dst[7] = src[0];
78: break;
1.7 itojun 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;
1.2 itojun 97: }
98:
99: /*
1.5 itojun 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.
1.2 itojun 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:
1.8 is 134: case IFT_ARCNET:
135: /* what others? */
1.7 itojun 136: /* IEEE802/EUI64 cases - what others? */
1.2 itojun 137: addr = LLADDR(sdl);
138: addrlen = sdl->sdl_alen;
1.7 itojun 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;
1.2 itojun 145: goto found;
146: default:
147: break;
148: }
149: }
150: }
1.4 thorpej 151: #ifdef DEBUG
152: printf("in6_ifattach_getifid: failed to get EUI64");
153: #endif
1.2 itojun 154: return EADDRNOTAVAIL;
155:
156: found:
1.7 itojun 157: if (laddr_to_eui64(first_ifid, addr, addrlen) == 0)
1.2 itojun 158: found_first_ifid = 1;
159:
160: if (found_first_ifid) {
1.4 thorpej 161: printf("%s: supplying EUI64: "
162: "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
1.2 itojun 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);
1.5 itojun 168:
169: /* invert u bit to convert EUI64 to RFC2373 interface ID. */
170: first_ifid[0] ^= 0x02;
171:
1.2 itojun 172: return 0;
173: } else {
1.4 thorpej 174: #ifdef DEBUG
175: printf("in6_ifattach_getifid: failed to get EUI64");
176: #endif
1.2 itojun 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().
1.5 itojun 184: *
185: * XXX I start feeling this as a bad idea. (itojun)
1.2 itojun 186: */
187: void
188: in6_ifattach_p2p()
189: {
190: struct ifnet *ifp;
191:
192: /* prevent infinite loop. just in case. */
1.4 thorpej 193: if (found_first_ifid == 0)
1.2 itojun 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;
1.5 itojun 202: case IFT_FAITH:
203: /* this mistakingly becomes IFF_UP */
204: break;
1.2 itojun 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;
1.7 itojun 222: /* size_t laddrlen; */
1.2 itojun 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) {
1.4 thorpej 236: printf("%s: no ifid available for IPv6 link-local address\n",
1.2 itojun 237: ifp->if_xname);
238: return;
239: }
240:
241: if ((ifp->if_flags & IFF_MULTICAST) == 0) {
1.4 thorpej 242: printf("%s: not multicast capable, IPv6 not enabled\n",
1.2 itojun 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;
1.7 itojun 255: size_t olim;
1.2 itojun 256:
1.7 itojun 257: olim = if_indexlim;
258: while (if_index >= if_indexlim)
1.2 itojun 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) {
1.7 itojun 266: bcopy((caddr_t)in6_iflladdr, q,
267: olim * sizeof(struct in6_addr *));
1.2 itojun 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: }
1.7 itojun 286: } else {
1.2 itojun 287: TAILQ_INIT(&ifp->if_addrlist);
1.7 itojun 288: }
1.2 itojun 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;
1.7 itojun 335: /* XXX use laddrlen */
336: if (laddr_to_eui64(&ia->ia_addr.sin6_addr.s6_addr8[8],
337: laddr, 6) != 0) {
338: break;
339: }
1.6 itojun 340: /* invert u bit to convert EUI64 to RFC2373 interface ID. */
341: ia->ia_addr.sin6_addr.s6_addr8[8] ^= 0x02;
1.4 thorpej 342: if (found_first_ifid == 0) {
1.2 itojun 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;
1.8 is 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;
1.9 ! is 358: if (laddr_to_eui64(&ia->ia_addr.sin6_addr.s6_addr8[8],
! 359: laddr, 1) != 0) {
! 360: break;
! 361: }
1.2 itojun 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:
1.4 thorpej 381: printf("%s: IPv6 not supported\n",
1.2 itojun 382: ifp->if_xname);
383: break;
384: default:
1.4 thorpej 385: printf("%s: SIOCSIFADDR error %d\n",
1.2 itojun 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:
1.7 itojun 483: #if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
1.2 itojun 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) {
1.8 is 546: case IFT_ARCNET:
1.2 itojun 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;
1.4 thorpej 608: #ifdef DEBUG
1.2 itojun 609: else
1.4 thorpej 610: printf("%s: didn't unlink in6ifaddr from "
611: "list\n", ifp->if_xname);
612: #endif
1.2 itojun 613: }
614:
615: free(ia, M_IFADDR);
616: }
617: }
CVSweb <webmaster@jp.NetBSD.org>