1 /* $NetBSD: in6_ifattach.c,v 1.121 2022/12/22 02:52:35 msaitoh Exp $ */
2 /* $KAME: in6_ifattach.c,v 1.124 2001/07/18 08:32:51 jinmei Exp $ */
3
4 /*
5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: in6_ifattach.c,v 1.121 2022/12/22 02:52:35 msaitoh Exp $");
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kmem.h>
39 #include <sys/socket.h>
40 #include <sys/sockio.h>
41 #include <sys/kernel.h>
42 #include <sys/syslog.h>
43 #include <sys/md5.h>
44 #include <sys/socketvar.h>
45 #include <sys/cprng.h>
46
47 #include <net/if.h>
48 #include <net/if_dl.h>
49 #include <net/if_types.h>
50 #include <net/route.h>
51
52 #include <netinet/in.h>
53 #include <netinet/in_var.h>
54
55 #include <netinet/ip6.h>
56 #include <netinet6/in6_ifattach.h>
57 #include <netinet6/ip6_var.h>
58 #include <netinet6/nd6.h>
59 #include <netinet6/ip6_mroute.h>
60 #include <netinet6/scope6_var.h>
61
62 int ip6_auto_linklocal = 1; /* enable by default */
63
64 #if 0
65 static int get_hostid_ifid(struct ifnet *, struct in6_addr *);
66 #endif
67 static int get_ifid(struct ifnet *, struct ifnet *, struct in6_addr *);
68 static int in6_ifattach_linklocal(struct ifnet *, struct ifnet *);
69 static int in6_ifattach_loopback(struct ifnet *);
70
71 #define EUI64_GBIT 0x01
72 #define EUI64_UBIT 0x02
73 #define EUI64_TO_IFID(in6) do {(in6)->s6_addr[8] ^= EUI64_UBIT; } while (/*CONSTCOND*/ 0)
74 #define EUI64_GROUP(in6) ((in6)->s6_addr[8] & EUI64_GBIT)
75 #define EUI64_INDIVIDUAL(in6) (!EUI64_GROUP(in6))
76 #define EUI64_LOCAL(in6) ((in6)->s6_addr[8] & EUI64_UBIT)
77 #define EUI64_UNIVERSAL(in6) (!EUI64_LOCAL(in6))
78
79 #define IFID_LOCAL(in6) (!EUI64_LOCAL(in6))
80 #define IFID_UNIVERSAL(in6) (!EUI64_UNIVERSAL(in6))
81
82 #if 0
83 /*
84 * Generate a last-resort interface identifier from hostid.
85 * works only for certain architectures (like sparc).
86 * also, using hostid itself may constitute a privacy threat, much worse
87 * than MAC addresses (hostids are used for software licensing).
88 * maybe we should use MD5(hostid) instead.
89 *
90 * in6 - upper 64bits are preserved
91 */
92 static int
93 get_hostid_ifid(struct ifnet *ifp, struct in6_addr *in6)
94 {
95 int off, len;
96 static const uint8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
97 static const uint8_t allone[8] =
98 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
99
100 if (!hostid)
101 return -1;
102
103 /* get up to 8 bytes from the hostid field - should we get */
104 len = (sizeof(hostid) > 8) ? 8 : sizeof(hostid);
105 off = sizeof(*in6) - len;
106 memcpy(&in6->s6_addr[off], &hostid, len);
107
108 /* make sure we do not return anything bogus */
109 if (memcmp(&in6->s6_addr[8], allzero, sizeof(allzero)))
110 return -1;
111 if (memcmp(&in6->s6_addr[8], allone, sizeof(allone)))
112 return -1;
113
114 /* make sure to set "u" bit to local, and "g" bit to individual. */
115 in6->s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */
116 in6->s6_addr[8] |= EUI64_UBIT; /* u bit to "local" */
117
118 /* convert EUI64 into IPv6 interface identifier */
119 EUI64_TO_IFID(in6);
120
121 return 0;
122 }
123 #endif
124
125 /*
126 * Generate a last-resort interface identifier, when the machine has no
127 * IEEE802/EUI64 address sources.
128 * The goal here is to get an interface identifier that is
129 * (1) random enough and (2) does not change across reboot.
130 * We currently use MD5(hostname) for it.
131 */
132 static int
133 get_rand_ifid(struct in6_addr *in6) /* upper 64bits are preserved */
134 {
135 MD5_CTX ctxt;
136 u_int8_t digest[16];
137
138 #if 0
139 /* we need at least several letters as seed for ifid */
140 if (hostnamelen < 3)
141 return -1;
142 #endif
143
144 /* generate 8 bytes of pseudo-random value. */
145 memset(&ctxt, 0, sizeof(ctxt));
146 MD5Init(&ctxt);
147 MD5Update(&ctxt, (u_char *)hostname, hostnamelen);
148 MD5Final(digest, &ctxt);
149
150 /* assumes sizeof(digest) > sizeof(ifid) */
151 memcpy(&in6->s6_addr[8], digest, 8);
152
153 /* make sure to set "u" bit to local, and "g" bit to individual. */
154 in6->s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */
155 in6->s6_addr[8] |= EUI64_UBIT; /* u bit to "local" */
156
157 /* convert EUI64 into IPv6 interface identifier */
158 EUI64_TO_IFID(in6);
159
160 return 0;
161 }
162
163 /*
164 * Get interface identifier for the specified interface.
165 *
166 * in6 - upper 64bits are preserved
167 */
168 int
169 in6_get_hw_ifid(struct ifnet *ifp, struct in6_addr *in6)
170 {
171 struct ifaddr *ifa;
172 const struct sockaddr_dl *sdl = NULL;
173 const char *addr = NULL; /* XXX gcc 4.8 -Werror=maybe-uninitialized */
174 size_t addrlen = 0; /* XXX gcc 4.8 -Werror=maybe-uninitialized */
175 static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
176 static u_int8_t allone[8] =
177 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
178 int s;
179
180 s = pserialize_read_enter();
181 IFADDR_READER_FOREACH(ifa, ifp) {
182 const struct sockaddr_dl *tsdl;
183 if (ifa->ifa_addr->sa_family != AF_LINK)
184 continue;
185 tsdl = satocsdl(ifa->ifa_addr);
186 if (tsdl == NULL || tsdl->sdl_alen == 0)
187 continue;
188 if (sdl == NULL || ifa == ifp->if_dl || ifa == ifp->if_hwdl) {
189 sdl = tsdl;
190 addr = CLLADDR(sdl);
191 addrlen = sdl->sdl_alen;
192 }
193 if (ifa == ifp->if_hwdl)
194 break;
195 }
196 pserialize_read_exit(s);
197
198 if (sdl == NULL)
199 return -1;
200
201 switch (ifp->if_type) {
202 case IFT_IEEE1394:
203 case IFT_IEEE80211:
204 /* IEEE1394 uses 16byte length address starting with EUI64 */
205 if (addrlen > 8)
206 addrlen = 8;
207 break;
208 default:
209 break;
210 }
211
212 /* get EUI64 */
213 switch (ifp->if_type) {
214 /* IEEE802/EUI64 cases - what others? */
215 case IFT_ETHER:
216 case IFT_ATM:
217 case IFT_IEEE1394:
218 case IFT_IEEE80211:
219 /* look at IEEE802/EUI64 only */
220 if (addrlen != 8 && addrlen != 6)
221 return -1;
222
223 /*
224 * check for invalid MAC address - on bsdi, we see it a lot
225 * since wildboar configures all-zero MAC on pccard before
226 * card insertion.
227 */
228 if (memcmp(addr, allzero, addrlen) == 0)
229 return -1;
230 if (memcmp(addr, allone, addrlen) == 0)
231 return -1;
232
233 /* make EUI64 address */
234 if (addrlen == 8)
235 memcpy(&in6->s6_addr[8], addr, 8);
236 else if (addrlen == 6) {
237 in6->s6_addr[8] = addr[0];
238 in6->s6_addr[9] = addr[1];
239 in6->s6_addr[10] = addr[2];
240 in6->s6_addr[11] = 0xff;
241 in6->s6_addr[12] = 0xfe;
242 in6->s6_addr[13] = addr[3];
243 in6->s6_addr[14] = addr[4];
244 in6->s6_addr[15] = addr[5];
245 }
246 break;
247
248 case IFT_ARCNET:
249 if (addrlen != 1)
250 return -1;
251 if (!addr[0])
252 return -1;
253
254 memset(&in6->s6_addr[8], 0, 8);
255 in6->s6_addr[15] = addr[0];
256
257 /*
258 * due to insufficient bitwidth, we mark it local.
259 */
260 in6->s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */
261 in6->s6_addr[8] |= EUI64_UBIT; /* u bit to "local" */
262 break;
263
264 case IFT_GIF:
265 #ifdef IFT_STF
266 case IFT_STF:
267 #endif
268 /*
269 * RFC2893 says: "SHOULD use IPv4 address as ifid source".
270 * however, IPv4 address is not very suitable as unique
271 * identifier source (can be renumbered).
272 * we don't do this.
273 */
274 return -1;
275
276 default:
277 return -1;
278 }
279
280 /* sanity check: g bit must not indicate "group" */
281 if (EUI64_GROUP(in6))
282 return -1;
283
284 /* convert EUI64 into IPv6 interface identifier */
285 EUI64_TO_IFID(in6);
286
287 /*
288 * sanity check: ifid must not be all zero, avoid conflict with
289 * subnet router anycast
290 */
291 if ((in6->s6_addr[8] & ~(EUI64_GBIT | EUI64_UBIT)) == 0x00 &&
292 memcmp(&in6->s6_addr[9], allzero, 7) == 0) {
293 return -1;
294 }
295
296 return 0;
297 }
298
299 /*
300 * Get interface identifier for the specified interface. If it is not
301 * available on ifp0, borrow interface identifier from other information
302 * sources.
303 *
304 * altifp - secondary EUI64 source
305 */
306 static int
307 get_ifid(struct ifnet *ifp0, struct ifnet *altifp,
308 struct in6_addr *in6)
309 {
310 struct ifnet *ifp;
311 int s;
312
313 /* first, try to get it from the interface itself */
314 if (in6_get_hw_ifid(ifp0, in6) == 0) {
315 nd6log(LOG_DEBUG, "%s: got interface identifier from itself\n",
316 if_name(ifp0));
317 goto success;
318 }
319
320 /* try secondary EUI64 source. this basically is for ATM PVC */
321 if (altifp && in6_get_hw_ifid(altifp, in6) == 0) {
322 nd6log(LOG_DEBUG, "%s: got interface identifier from %s\n",
323 if_name(ifp0), if_name(altifp));
324 goto success;
325 }
326
327 /* next, try to get it from some other hardware interface */
328 s = pserialize_read_enter();
329 IFNET_READER_FOREACH(ifp) {
330 if (ifp == ifp0)
331 continue;
332 if (in6_get_hw_ifid(ifp, in6) != 0)
333 continue;
334
335 /*
336 * to borrow ifid from other interface, ifid needs to be
337 * globally unique
338 */
339 if (IFID_UNIVERSAL(in6)) {
340 nd6log(LOG_DEBUG,
341 "%s: borrow interface identifier from %s\n",
342 if_name(ifp0), if_name(ifp));
343 pserialize_read_exit(s);
344 goto success;
345 }
346 }
347 pserialize_read_exit(s);
348
349 #if 0
350 /* get from hostid - only for certain architectures */
351 if (get_hostid_ifid(ifp, in6) == 0) {
352 nd6log(LOG_DEBUG,
353 "%s: interface identifier generated by hostid\n",
354 if_name(ifp0));
355 goto success;
356 }
357 #endif
358
359 /* last resort: get from random number source */
360 if (get_rand_ifid(in6) == 0) {
361 nd6log(LOG_DEBUG,
362 "%s: interface identifier generated by random number\n",
363 if_name(ifp0));
364 goto success;
365 }
366
367 printf("%s: failed to get interface identifier\n", if_name(ifp0));
368 return -1;
369
370 success:
371 nd6log(LOG_INFO, "%s: ifid: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
372 if_name(ifp0), in6->s6_addr[8], in6->s6_addr[9], in6->s6_addr[10],
373 in6->s6_addr[11], in6->s6_addr[12], in6->s6_addr[13],
374 in6->s6_addr[14], in6->s6_addr[15]);
375 return 0;
376 }
377
378 /*
379 * altifp - secondary EUI64 source
380 */
381
382 static int
383 in6_ifattach_linklocal(struct ifnet *ifp, struct ifnet *altifp)
384 {
385 struct in6_aliasreq ifra;
386 int error;
387
388 /*
389 * configure link-local address.
390 */
391 memset(&ifra, 0, sizeof(ifra));
392
393 /*
394 * in6_update_ifa() does not use ifra_name, but we accurately set it
395 * for safety.
396 */
397 strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name));
398
399 ifra.ifra_addr.sin6_family = AF_INET6;
400 ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
401 ifra.ifra_addr.sin6_addr.s6_addr32[0] = htonl(0xfe800000);
402 ifra.ifra_addr.sin6_addr.s6_addr32[1] = 0;
403 if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
404 ifra.ifra_addr.sin6_addr.s6_addr32[2] = 0;
405 ifra.ifra_addr.sin6_addr.s6_addr32[3] = htonl(1);
406 } else {
407 if (get_ifid(ifp, altifp, &ifra.ifra_addr.sin6_addr) != 0) {
408 nd6log(LOG_ERR,
409 "%s: no ifid available\n", if_name(ifp));
410 return -1;
411 }
412 }
413 if (in6_setscope(&ifra.ifra_addr.sin6_addr, ifp, NULL))
414 return -1;
415
416 sockaddr_in6_init(&ifra.ifra_prefixmask, &in6mask64, 0, 0, 0);
417 /* link-local addresses should NEVER expire. */
418 ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
419 ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
420
421 /*
422 * Now call in6_update_ifa() to do a bunch of procedures to configure
423 * a link-local address. We can set the 3rd argument to NULL, because
424 * we know there's no other link-local address on the interface
425 * and therefore we are adding one (instead of updating one).
426 */
427 if ((error = in6_update_ifa(ifp, &ifra, IN6_IFAUPDATE_DADDELAY)) != 0) {
428 /*
429 * XXX: When the interface does not support IPv6, this call
430 * would fail in the SIOCINITIFADDR ioctl. I believe the
431 * notification is rather confusing in this case, so just
432 * suppress it. (jinmei@kame.net 20010130)
433 */
434 if (error != EAFNOSUPPORT)
435 nd6log(LOG_NOTICE,
436 "failed to configure a link-local address on %s "
437 "(errno=%d)\n",
438 if_name(ifp), error);
439 return -1;
440 }
441
442 return 0;
443 }
444
445 /*
446 * ifp - must be IFT_LOOP
447 */
448
449 static int
450 in6_ifattach_loopback(struct ifnet *ifp)
451 {
452 struct in6_aliasreq ifra;
453 int error;
454
455 memset(&ifra, 0, sizeof(ifra));
456
457 /*
458 * in6_update_ifa() does not use ifra_name, but we accurately set it
459 * for safety.
460 */
461 strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name));
462
463 sockaddr_in6_init(&ifra.ifra_prefixmask, &in6mask128, 0, 0, 0);
464
465 /*
466 * Always initialize ia_dstaddr (= broadcast address) to loopback
467 * address. Follows IPv4 practice - see in_ifinit().
468 */
469 sockaddr_in6_init(&ifra.ifra_dstaddr, &in6addr_loopback, 0, 0, 0);
470
471 sockaddr_in6_init(&ifra.ifra_addr, &in6addr_loopback, 0, 0, 0);
472
473 /* the loopback address should NEVER expire. */
474 ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
475 ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
476
477 /* we don't need to perform DAD on loopback interfaces. */
478 ifra.ifra_flags |= IN6_IFF_NODAD;
479
480 /*
481 * We are sure that this is a newly assigned address, so we can set
482 * NULL to the 3rd arg.
483 */
484 if ((error = in6_update_ifa(ifp, &ifra, 0)) != 0) {
485 nd6log(LOG_ERR, "failed to configure "
486 "the loopback address on %s (errno=%d)\n",
487 if_name(ifp), error);
488 return -1;
489 }
490
491 return 0;
492 }
493
494 /*
495 * compute NI group address, based on the current hostname setting.
496 * see draft-ietf-ipngwg-icmp-name-lookup-* (04 and later).
497 *
498 * when ifp == NULL, the caller is responsible for filling scopeid.
499 */
500 int
501 in6_nigroup(struct ifnet *ifp, const char *name, int namelen,
502 struct sockaddr_in6 *sa6)
503 {
504 const char *p;
505 u_int8_t *q;
506 MD5_CTX ctxt;
507 u_int8_t digest[16];
508 u_int8_t l;
509 u_int8_t n[64]; /* a single label must not exceed 63 chars */
510
511 if (!namelen || !name)
512 return -1;
513
514 p = name;
515 while (p && *p && *p != '.' && p - name < namelen)
516 p++;
517 if (p - name > sizeof(n) - 1)
518 return -1; /* label too long */
519 l = p - name;
520 strncpy((char *)n, name, l);
521 n[(int)l] = '\0';
522 for (q = n; *q; q++) {
523 if ('A' <= *q && *q <= 'Z')
524 *q = *q - 'A' + 'a';
525 }
526
527 /* generate 8 bytes of pseudo-random value. */
528 memset(&ctxt, 0, sizeof(ctxt));
529 MD5Init(&ctxt);
530 MD5Update(&ctxt, &l, sizeof(l));
531 MD5Update(&ctxt, n, l);
532 MD5Final(digest, &ctxt);
533
534 memset(sa6, 0, sizeof(*sa6));
535 sa6->sin6_family = AF_INET6;
536 sa6->sin6_len = sizeof(*sa6);
537 sa6->sin6_addr.s6_addr16[0] = htons(0xff02);
538 sa6->sin6_addr.s6_addr8[11] = 2;
539 memcpy(&sa6->sin6_addr.s6_addr32[3], digest,
540 sizeof(sa6->sin6_addr.s6_addr32[3]));
541 if (in6_setscope(&sa6->sin6_addr, ifp, NULL))
542 return -1; /* XXX: should not fail */
543
544 return 0;
545 }
546
547 /*
548 * XXX multiple loopback interface needs more care. for instance,
549 * nodelocal address needs to be configured onto only one of them.
550 * XXX multiple link-local address case
551 *
552 * altifp - secondary EUI64 source
553 */
554 void
555 in6_ifattach(struct ifnet *ifp, struct ifnet *altifp)
556 {
557 struct in6_ifaddr *ia;
558 struct in6_addr in6;
559
560 KASSERT(IFNET_LOCKED(ifp));
561
562 /* some of the interfaces are inherently not IPv6 capable */
563 switch (ifp->if_type) {
564 case IFT_BRIDGE:
565 case IFT_L2TP:
566 case IFT_IEEE8023ADLAG:
567 #ifdef IFT_PFLOG
568 case IFT_PFLOG:
569 #endif
570 #ifdef IFT_PFSYNC
571 case IFT_PFSYNC:
572 #endif
573 ND_IFINFO(ifp)->flags &= ~ND6_IFF_AUTO_LINKLOCAL;
574 ND_IFINFO(ifp)->flags |= ND6_IFF_IFDISABLED;
575 return;
576 }
577
578 /*
579 * if link mtu is too small, don't try to configure IPv6.
580 * remember there could be some link-layer that has special
581 * fragmentation logic.
582 */
583 if (ifp->if_mtu < IPV6_MMTU) {
584 nd6log(LOG_INFO, "%s has too small MTU, IPv6 not enabled\n",
585 if_name(ifp));
586 return;
587 }
588
589 /*
590 * quirks based on interface type
591 */
592 switch (ifp->if_type) {
593 #ifdef IFT_STF
594 case IFT_STF:
595 /*
596 * 6to4 interface is a very special kind of beast.
597 * no multicast, no linklocal. RFC2529 specifies how to make
598 * linklocals for 6to4 interface, but there's no use and
599 * it is rather harmful to have one.
600 */
601 ND_IFINFO(ifp)->flags &= ~ND6_IFF_AUTO_LINKLOCAL;
602 return;
603 #endif
604 case IFT_CARP:
605 return;
606 default:
607 break;
608 }
609
610 /*
611 * usually, we require multicast capability to the interface
612 */
613 if ((ifp->if_flags & IFF_MULTICAST) == 0) {
614 nd6log(LOG_INFO,
615 "%s is not multicast capable, IPv6 not enabled\n",
616 if_name(ifp));
617 return;
618 }
619
620 /*
621 * assign loopback address for loopback interface.
622 * XXX multiple loopback interface case.
623 */
624 if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
625 in6 = in6addr_loopback;
626 /* These are safe and atomic thanks to IFNET_LOCK */
627 if (in6ifa_ifpwithaddr(ifp, &in6) == NULL) {
628 if (in6_ifattach_loopback(ifp) != 0)
629 return;
630 }
631 }
632
633 /*
634 * assign a link-local address, if there's none.
635 */
636 if (!(ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) &&
637 ND_IFINFO(ifp)->flags & ND6_IFF_AUTO_LINKLOCAL) {
638 int bound = curlwp_bind();
639 struct psref psref;
640 ia = in6ifa_ifpforlinklocal_psref(ifp, 0, &psref);
641 if (ia == NULL && in6_ifattach_linklocal(ifp, altifp) != 0) {
642 printf("%s: cannot assign link-local address\n",
643 ifp->if_xname);
644 }
645 ia6_release(ia, &psref);
646 curlwp_bindx(bound);
647 }
648 }
649
650 /*
651 * NOTE: in6_ifdetach() does not support loopback if at this moment.
652 * We don't need this function in bsdi, because interfaces are never removed
653 * from the ifnet list in bsdi.
654 */
655 void
656 in6_ifdetach(struct ifnet *ifp)
657 {
658
659 /* nuke any of IPv6 addresses we have */
660 if_purgeaddrs(ifp, AF_INET6, in6_purgeaddr);
661
662 in6_purge_multi(ifp);
663
664 /* remove ip6_mrouter stuff */
665 ip6_mrouter_detach(ifp);
666
667 /* remove neighbor management table */
668 nd6_purge(ifp, NULL);
669 }
Cache object: c4e66c59e0b376a9bb038c89b4d97f67
|