Date: Wed, 18 May 2011 08:58:54 -0700 (PDT) From: Doug Ambrisko <ambrisko@ambrisko.com> To: freebsd-net@freebsd.org Subject: IPv6 ifconfig down/up issue versus IPv4 Message-ID: <201105181558.p4IFws95036355@ambrisko.com>
next in thread | raw e-mail | index | archive | help
--ELM1305734334-34657-0_ Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="US-ASCII" Recently, I ran into a problem in which IPv4 works fine but IPv6 doesn't. I use a trick to "fail-over" NICs via ifconfig down the bad link and then ifconfig up the good link. Both NICs have the same IP. The 2nd IP is assigned when the 1st NIC is down. This works fine with IPv4 since on down it removes the NIC's local route that was set when the IP was assigned ie. the /x network. In the IPv4 stack it calls if_up and if_down to purge the route and if_up to recreate the NIC's route. Only when you delete the IP does the route go away. This doesn't happen with IPv6, instead the route is created when the NIC is first assigned an IP and persists when the NIC is down and even when another is up. You can see this with route get or netstat -r pointing to a NIC that doesn't exist. I have a prototype/first hack to fix it by modeling the IPv4 paradigm and deal with the routes/multicast groups. I'd like someone to take a look and tell me what I messed up and how it should be done better. This appears to be a fairly simple bug in FreeBSD that was just never looked at before since it is a bit of a corner case. A lot of this problem can be seen tested via netstat -r ndp -a ifmcstat Thanks, Doug A. diff -upr ../src/sys/net/if.c sys/net/if.c --- ../src/sys/net/if.c 2009-07-27 12:48:10.000000000 -0700 +++ sys/net/if.c 2011-05-17 12:51:38.000000000 -0700 @@ -1449,6 +1449,9 @@ if_unroute(struct ifnet *ifp, int flag, carp_carpdev_state(ifp->if_carp); #endif rt_ifmsg(ifp); +#ifdef INET6 + in6_if_down(ifp); +#endif } /* diff -upr ../src/sys/netinet6/in6.c sys/netinet6/in6.c --- ../src/sys/netinet6/in6.c 2009-07-27 12:48:10.000000000 -0700 +++ sys/netinet6/in6.c 2011-05-17 12:59:27.000000000 -0700 @@ -132,6 +132,9 @@ static void in6_unlink_ifa(struct in6_if struct in6_multihead in6_multihead; /* XXX BSS initialization */ int (*faithprefix_p)(struct in6_addr *); +static int in6_join_multicast_groups(struct ifnet *ifp, + struct in6_aliasreq *ifra, struct in6_ifaddr *ia, int flags, + int hostIsNew); /* * Subroutine for in6_ifaddloop() and in6_ifremloop(). @@ -788,6 +791,7 @@ in6_control(struct socket *so, u_long cm return (0); } + /* * Update parameters of an IPv6 interface address. * If necessary, a new entry is created and linked into address chains. @@ -802,10 +806,6 @@ in6_update_ifa(struct ifnet *ifp, struct struct in6_ifaddr *oia; struct sockaddr_in6 dst6; struct in6_addrlifetime *lt; - struct in6_multi_mship *imm; - struct in6_multi *in6m_sol; - struct rtentry *rt; - int delay; char ip6buf[INET6_ADDRSTRLEN]; /* Validate parameters */ @@ -1049,6 +1049,41 @@ in6_update_ifa(struct ifnet *ifp, struct * not just go to unlink. */ + if ((error = in6_join_multicast_groups(ifp, ifra, ia, flags, hostIsNew))) { + goto cleanup; + } + + + return (error); + + unlink: + /* + * XXX: if a change of an existing address failed, keep the entry + * anyway. + */ + if (hostIsNew) + in6_unlink_ifa(ia, ifp); + return (error); + + cleanup: + in6_purgeaddr(&ia->ia_ifa); + return error; +} + +static int +in6_join_multicast_groups(struct ifnet *ifp, struct in6_aliasreq *ifra, + struct in6_ifaddr *ia, int flags, int hostIsNew) +{ + int error = 0; + struct in6_multi_mship *imm; + struct rtentry *rt; + struct in6_multi *in6m_sol; + int delay; + char ip6buf[INET6_ADDRSTRLEN]; + +/* Up the interface */ +ifp->if_flags |= IFF_UP; + /* Join necessary multicast groups */ in6m_sol = NULL; if ((ifp->if_flags & IFF_MULTICAST) != 0) { @@ -1263,7 +1295,7 @@ in6_update_ifa(struct ifnet *ifp, struct ip6_sprintf(ip6buf, &mltaddr.sin6_addr), if_name(ifp), error)); goto cleanup; - } + } LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); #undef MLTMASK_LEN } @@ -1306,20 +1338,8 @@ in6_update_ifa(struct ifnet *ifp, struct nd6_dad_start((struct ifaddr *)ia, delay); } + cleanup: return (error); - - unlink: - /* - * XXX: if a change of an existing address failed, keep the entry - * anyway. - */ - if (hostIsNew) - in6_unlink_ifa(ia, ifp); - return (error); - - cleanup: - in6_purgeaddr(&ia->ia_ifa); - return error; } void @@ -1727,7 +1747,7 @@ in6_ifinit(struct ifnet *ifp, struct in6 /* we could do in(6)_socktrim here, but just omit it at this moment. */ - if (newhost && nd6_need_cache(ifp) != 0) { + if (newhost) { /* * set the rtrequest function to create llinfo. It also * adjust outgoing interface of the route for the local @@ -2120,6 +2140,93 @@ in6_ifawithifp(struct ifnet *ifp, struct return NULL; } +extern struct inpcbinfo udbinfo; +extern struct inpcbinfo ripcbinfo; +/*static*/ void in6_purgemaddrs(struct ifnet *); + +void +in6_if_down(struct ifnet *ifp) +{ + struct in6_ifaddr *ia /*, *oia*/; + struct ifaddr *ifa, *next; + struct rtentry *rt; + short rtflags; + struct sockaddr_in6 sin6; + struct in6_multi_mship *imm; + + /* remove neighbor management table */ + nd6_purge(ifp); + + /* undo everything done by in6_ifattach(), just in case */ + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = next) { + next = ifa->ifa_list.tqe_next; + + if (ifa->ifa_addr->sa_family != AF_INET6 + /* DJA || !IN6_IS_ADDR_LINKLOCAL(&satosin6(&ifa->ifa_addr)->sin6_addr) */) { + continue; + } + + ia = (struct in6_ifaddr *)ifa; + + /* + * leave from multicast groups we have joined for the interface + */ + while ((imm = ia->ia6_memberships.lh_first) != NULL) { + LIST_REMOVE(imm, i6mm_chain); + in6_leavegroup(imm); + } + +#define rtinitflags(x) \ + ((((x)->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) != 0) \ + ? RTF_HOST : RTF_UP) + /* remove from the routing table */ + if ((ia->ia_flags & IFA_ROUTE) && + (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0, 0UL))) { + rtflags = rt->rt_flags; + rtfree(rt); + if(ifa->ifa_addr->sa_family == AF_INET6) + rtinit(ifa, (int)RTM_DELETE, + rtinitflags(ifp)); + } + + } + + in6_pcbpurgeif0(&udbinfo, ifp); + in6_pcbpurgeif0(&ripcbinfo, ifp); + /* leave from all multicast groups joined */ + in6_purgemaddrs(ifp); + + /* + * remove neighbor management table. we call it twice just to make + * sure we nuke everything. maybe we need just one call. + * XXX: since the first call did not release addresses, some prefixes + * might remain. We should call nd6_purge() again to release the + * prefixes after removing all addresses above. + * (Or can we just delay calling nd6_purge until at this point?) + */ + nd6_purge(ifp); + + /* remove route to link-local allnodes multicast (ff02::1) */ + bzero(&sin6, sizeof(sin6)); + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = in6addr_linklocal_allnodes; + if (in6_setscope(&sin6.sin6_addr, ifp, NULL)) + /* XXX: should not fail */ + return; + /* XXX grab lock first to avoid LOR */ + if (rt_tables[0][AF_INET6] != NULL) { + RADIX_NODE_HEAD_LOCK(rt_tables[0][AF_INET6]); + rt = rtalloc1((struct sockaddr *)&sin6, 0, RTF_RNH_LOCKED); + if (rt) { + if (rt->rt_ifp == ifp) + rtexpunge(rt); + RTFREE_LOCKED(rt); + } + RADIX_NODE_HEAD_UNLOCK(rt_tables[0][AF_INET6]); + } +} + /* * perform DAD when interface becomes IFF_UP. */ @@ -2128,20 +2235,92 @@ in6_if_up(struct ifnet *ifp) { struct ifaddr *ifa; struct in6_ifaddr *ia; + struct in6_aliasreq ifra_store, *ifra; + /* DJA */ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ia = (struct in6_ifaddr *)ifa; - if (ia->ia6_flags & IN6_IFF_TENTATIVE) { + + + in6_ifinit(ifp, ia, &ia->ia_addr, 1); + + ifra = &ifra_store; + bzero(&ifra_store, sizeof(ifra_store)); + bcopy(if_name(ifp), ifra_store.ifra_name, + sizeof(ifra_store.ifra_name)); + bcopy(&ia->ia_addr, &ifra_store.ifra_addr, + sizeof(ifra_store.ifra_addr)); + bcopy(&ia->ia_dstaddr, &ifra_store.ifra_dstaddr, + sizeof(ifra_store.ifra_dstaddr)); + bcopy(&ia->ia_prefixmask, &ifra_store.ifra_prefixmask, + sizeof(ifra_store.ifra_prefixmask)); + ifra_store.ifra_flags = ia->ia6_flags; + bcopy(&ia->ia6_lifetime, &ifra_store.ifra_lifetime, + sizeof(ifra->ifra_lifetime)); + + /* Join necessary multicast groups */ + if (in6_join_multicast_groups(ifp, ifra, ia, 0, 0)) { + in6_purgeaddr(&ia->ia_ifa); + continue; + } + + int i, error = 0; + struct nd_prefixctl pr0; + struct nd_prefix *pr; + /* + * then, make the prefix on-link on the interface. + * XXX: we'd rather create the prefix before the address, but + * we need at least one address to install the corresponding + * interface route, so we configure the address first. + */ + + /* + * convert mask to prefix length (prefixmask has already + * been validated in in6_update_ifa(). + */ + bzero(&pr0, sizeof(pr0)); + pr0.ndpr_ifp = ifp; + pr0.ndpr_plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, + NULL); + + if (pr0.ndpr_plen == 128) { + continue; /* we don't need to install a host route. */ + } + pr0.ndpr_prefix = ifra->ifra_addr; + /* apply the mask for safety. */ + for (i = 0; i < 4; i++) { + pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &= + ifra->ifra_prefixmask.sin6_addr.s6_addr32[i]; + } + + /* + * XXX: since we don't have an API to set prefix (not address) + * lifetimes, we just use the same lifetimes as addresses. + * The (temporarily) installed lifetimes can be overridden by + * later advertised RAs (when accept_rtadv is non 0), which is + * an intended behavior. + */ + pr0.ndpr_raf_onlink = 1; /* should be configurable? */ + pr0.ndpr_raf_auto = + ((ifra->ifra_flags & IN6_IFF_AUTOCONF) != 0); + pr0.ndpr_vltime = ifra->ifra_lifetime.ia6t_vltime; + pr0.ndpr_pltime = ifra->ifra_lifetime.ia6t_pltime; + + /* add the prefix if not yet. */ + if ((pr = nd6_prefix_lookup(&pr0)) == NULL) { /* - * The TENTATIVE flag was likely set by hand - * beforehand, implicitly indicating the need for DAD. - * We may be able to skip the random delay in this - * case, but we impose delays just in case. + * nd6_prelist_add will install the corresponding + * interface route. */ - nd6_dad_start(ifa, - arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz)); + if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0) + return; + if (pr == NULL) { + log(LOG_ERR, "nd6_prelist_add succeeded but " + "no prefix\n"); + return; /* XXX panic here? */ + } } } Only in sys/netinet6: in6.c.orig Only in sys/netinet6: in6.c~ diff -upr ../src/sys/netinet6/in6.h sys/netinet6/in6.h --- ../src/sys/netinet6/in6.h 2009-04-14 20:14:26.000000000 -0700 +++ sys/netinet6/in6.h 2011-04-19 09:41:23.000000000 -0700 @@ -620,6 +620,7 @@ int in6_localaddr __P((struct in6_addr * int in6_addrscope __P((struct in6_addr *)); struct in6_ifaddr *in6_ifawithifp __P((struct ifnet *, struct in6_addr *)); extern void in6_if_up __P((struct ifnet *)); +extern void in6_if_down __P((struct ifnet *)); struct sockaddr; extern u_char ip6_protox[]; diff -upr ../src/sys/netinet6/in6_ifattach.c sys/netinet6/in6_ifattach.c --- ../src/sys/netinet6/in6_ifattach.c 2009-04-14 20:14:26.000000000 -0700 +++ sys/netinet6/in6_ifattach.c 2011-04-19 10:29:31.000000000 -0700 @@ -78,7 +78,7 @@ static int generate_tmp_ifid(u_int8_t *, static int get_ifid(struct ifnet *, struct ifnet *, struct in6_addr *); static int in6_ifattach_linklocal(struct ifnet *, struct ifnet *); static int in6_ifattach_loopback(struct ifnet *); -static void in6_purgemaddrs(struct ifnet *); +/*static*/ void in6_purgemaddrs(struct ifnet *); #define EUI64_GBIT 0x01 #define EUI64_UBIT 0x02 @@ -886,7 +886,7 @@ in6_tmpaddrtimer(void *ignored_arg) splx(s); } -static void +/*static*/ void in6_purgemaddrs(struct ifnet *ifp) { struct in6_multi *in6m; --ELM1305734334-34657-0_ Content-Transfer-Encoding: 7bit Content-Type: text/x-patch Content-Disposition: attachment; filename="ipv6_down_up_routing.patch" Content-Description: diff -upr ../src/sys/net/if.c sys/net/if.c --- ../src/sys/net/if.c 2009-07-27 12:48:10.000000000 -0700 +++ sys/net/if.c 2011-05-17 12:51:38.000000000 -0700 @@ -1449,6 +1449,9 @@ if_unroute(struct ifnet *ifp, int flag, carp_carpdev_state(ifp->if_carp); #endif rt_ifmsg(ifp); +#ifdef INET6 + in6_if_down(ifp); +#endif } /* diff -upr ../src/sys/netinet6/in6.c sys/netinet6/in6.c --- ../src/sys/netinet6/in6.c 2009-07-27 12:48:10.000000000 -0700 +++ sys/netinet6/in6.c 2011-05-17 12:59:27.000000000 -0700 @@ -132,6 +132,9 @@ static void in6_unlink_ifa(struct in6_if struct in6_multihead in6_multihead; /* XXX BSS initialization */ int (*faithprefix_p)(struct in6_addr *); +static int in6_join_multicast_groups(struct ifnet *ifp, + struct in6_aliasreq *ifra, struct in6_ifaddr *ia, int flags, + int hostIsNew); /* * Subroutine for in6_ifaddloop() and in6_ifremloop(). @@ -788,6 +791,7 @@ in6_control(struct socket *so, u_long cm return (0); } + /* * Update parameters of an IPv6 interface address. * If necessary, a new entry is created and linked into address chains. @@ -802,10 +806,6 @@ in6_update_ifa(struct ifnet *ifp, struct struct in6_ifaddr *oia; struct sockaddr_in6 dst6; struct in6_addrlifetime *lt; - struct in6_multi_mship *imm; - struct in6_multi *in6m_sol; - struct rtentry *rt; - int delay; char ip6buf[INET6_ADDRSTRLEN]; /* Validate parameters */ @@ -1049,6 +1049,41 @@ in6_update_ifa(struct ifnet *ifp, struct * not just go to unlink. */ + if ((error = in6_join_multicast_groups(ifp, ifra, ia, flags, hostIsNew))) { + goto cleanup; + } + + + return (error); + + unlink: + /* + * XXX: if a change of an existing address failed, keep the entry + * anyway. + */ + if (hostIsNew) + in6_unlink_ifa(ia, ifp); + return (error); + + cleanup: + in6_purgeaddr(&ia->ia_ifa); + return error; +} + +static int +in6_join_multicast_groups(struct ifnet *ifp, struct in6_aliasreq *ifra, + struct in6_ifaddr *ia, int flags, int hostIsNew) +{ + int error = 0; + struct in6_multi_mship *imm; + struct rtentry *rt; + struct in6_multi *in6m_sol; + int delay; + char ip6buf[INET6_ADDRSTRLEN]; + +/* Up the interface */ +ifp->if_flags |= IFF_UP; + /* Join necessary multicast groups */ in6m_sol = NULL; if ((ifp->if_flags & IFF_MULTICAST) != 0) { @@ -1263,7 +1295,7 @@ in6_update_ifa(struct ifnet *ifp, struct ip6_sprintf(ip6buf, &mltaddr.sin6_addr), if_name(ifp), error)); goto cleanup; - } + } LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); #undef MLTMASK_LEN } @@ -1306,20 +1338,8 @@ in6_update_ifa(struct ifnet *ifp, struct nd6_dad_start((struct ifaddr *)ia, delay); } + cleanup: return (error); - - unlink: - /* - * XXX: if a change of an existing address failed, keep the entry - * anyway. - */ - if (hostIsNew) - in6_unlink_ifa(ia, ifp); - return (error); - - cleanup: - in6_purgeaddr(&ia->ia_ifa); - return error; } void @@ -1727,7 +1747,7 @@ in6_ifinit(struct ifnet *ifp, struct in6 /* we could do in(6)_socktrim here, but just omit it at this moment. */ - if (newhost && nd6_need_cache(ifp) != 0) { + if (newhost) { /* * set the rtrequest function to create llinfo. It also * adjust outgoing interface of the route for the local @@ -2120,6 +2140,93 @@ in6_ifawithifp(struct ifnet *ifp, struct return NULL; } +extern struct inpcbinfo udbinfo; +extern struct inpcbinfo ripcbinfo; +/*static*/ void in6_purgemaddrs(struct ifnet *); + +void +in6_if_down(struct ifnet *ifp) +{ + struct in6_ifaddr *ia /*, *oia*/; + struct ifaddr *ifa, *next; + struct rtentry *rt; + short rtflags; + struct sockaddr_in6 sin6; + struct in6_multi_mship *imm; + + /* remove neighbor management table */ + nd6_purge(ifp); + + /* undo everything done by in6_ifattach(), just in case */ + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = next) { + next = ifa->ifa_list.tqe_next; + + if (ifa->ifa_addr->sa_family != AF_INET6 + /* DJA || !IN6_IS_ADDR_LINKLOCAL(&satosin6(&ifa->ifa_addr)->sin6_addr) */) { + continue; + } + + ia = (struct in6_ifaddr *)ifa; + + /* + * leave from multicast groups we have joined for the interface + */ + while ((imm = ia->ia6_memberships.lh_first) != NULL) { + LIST_REMOVE(imm, i6mm_chain); + in6_leavegroup(imm); + } + +#define rtinitflags(x) \ + ((((x)->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) != 0) \ + ? RTF_HOST : RTF_UP) + /* remove from the routing table */ + if ((ia->ia_flags & IFA_ROUTE) && + (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0, 0UL))) { + rtflags = rt->rt_flags; + rtfree(rt); + if(ifa->ifa_addr->sa_family == AF_INET6) + rtinit(ifa, (int)RTM_DELETE, + rtinitflags(ifp)); + } + + } + + in6_pcbpurgeif0(&udbinfo, ifp); + in6_pcbpurgeif0(&ripcbinfo, ifp); + /* leave from all multicast groups joined */ + in6_purgemaddrs(ifp); + + /* + * remove neighbor management table. we call it twice just to make + * sure we nuke everything. maybe we need just one call. + * XXX: since the first call did not release addresses, some prefixes + * might remain. We should call nd6_purge() again to release the + * prefixes after removing all addresses above. + * (Or can we just delay calling nd6_purge until at this point?) + */ + nd6_purge(ifp); + + /* remove route to link-local allnodes multicast (ff02::1) */ + bzero(&sin6, sizeof(sin6)); + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = in6addr_linklocal_allnodes; + if (in6_setscope(&sin6.sin6_addr, ifp, NULL)) + /* XXX: should not fail */ + return; + /* XXX grab lock first to avoid LOR */ + if (rt_tables[0][AF_INET6] != NULL) { + RADIX_NODE_HEAD_LOCK(rt_tables[0][AF_INET6]); + rt = rtalloc1((struct sockaddr *)&sin6, 0, RTF_RNH_LOCKED); + if (rt) { + if (rt->rt_ifp == ifp) + rtexpunge(rt); + RTFREE_LOCKED(rt); + } + RADIX_NODE_HEAD_UNLOCK(rt_tables[0][AF_INET6]); + } +} + /* * perform DAD when interface becomes IFF_UP. */ @@ -2128,20 +2235,92 @@ in6_if_up(struct ifnet *ifp) { struct ifaddr *ifa; struct in6_ifaddr *ia; + struct in6_aliasreq ifra_store, *ifra; + /* DJA */ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ia = (struct in6_ifaddr *)ifa; - if (ia->ia6_flags & IN6_IFF_TENTATIVE) { + + + in6_ifinit(ifp, ia, &ia->ia_addr, 1); + + ifra = &ifra_store; + bzero(&ifra_store, sizeof(ifra_store)); + bcopy(if_name(ifp), ifra_store.ifra_name, + sizeof(ifra_store.ifra_name)); + bcopy(&ia->ia_addr, &ifra_store.ifra_addr, + sizeof(ifra_store.ifra_addr)); + bcopy(&ia->ia_dstaddr, &ifra_store.ifra_dstaddr, + sizeof(ifra_store.ifra_dstaddr)); + bcopy(&ia->ia_prefixmask, &ifra_store.ifra_prefixmask, + sizeof(ifra_store.ifra_prefixmask)); + ifra_store.ifra_flags = ia->ia6_flags; + bcopy(&ia->ia6_lifetime, &ifra_store.ifra_lifetime, + sizeof(ifra->ifra_lifetime)); + + /* Join necessary multicast groups */ + if (in6_join_multicast_groups(ifp, ifra, ia, 0, 0)) { + in6_purgeaddr(&ia->ia_ifa); + continue; + } + + int i, error = 0; + struct nd_prefixctl pr0; + struct nd_prefix *pr; + /* + * then, make the prefix on-link on the interface. + * XXX: we'd rather create the prefix before the address, but + * we need at least one address to install the corresponding + * interface route, so we configure the address first. + */ + + /* + * convert mask to prefix length (prefixmask has already + * been validated in in6_update_ifa(). + */ + bzero(&pr0, sizeof(pr0)); + pr0.ndpr_ifp = ifp; + pr0.ndpr_plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, + NULL); + + if (pr0.ndpr_plen == 128) { + continue; /* we don't need to install a host route. */ + } + pr0.ndpr_prefix = ifra->ifra_addr; + /* apply the mask for safety. */ + for (i = 0; i < 4; i++) { + pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &= + ifra->ifra_prefixmask.sin6_addr.s6_addr32[i]; + } + + /* + * XXX: since we don't have an API to set prefix (not address) + * lifetimes, we just use the same lifetimes as addresses. + * The (temporarily) installed lifetimes can be overridden by + * later advertised RAs (when accept_rtadv is non 0), which is + * an intended behavior. + */ + pr0.ndpr_raf_onlink = 1; /* should be configurable? */ + pr0.ndpr_raf_auto = + ((ifra->ifra_flags & IN6_IFF_AUTOCONF) != 0); + pr0.ndpr_vltime = ifra->ifra_lifetime.ia6t_vltime; + pr0.ndpr_pltime = ifra->ifra_lifetime.ia6t_pltime; + + /* add the prefix if not yet. */ + if ((pr = nd6_prefix_lookup(&pr0)) == NULL) { /* - * The TENTATIVE flag was likely set by hand - * beforehand, implicitly indicating the need for DAD. - * We may be able to skip the random delay in this - * case, but we impose delays just in case. + * nd6_prelist_add will install the corresponding + * interface route. */ - nd6_dad_start(ifa, - arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz)); + if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0) + return; + if (pr == NULL) { + log(LOG_ERR, "nd6_prelist_add succeeded but " + "no prefix\n"); + return; /* XXX panic here? */ + } } } Only in sys/netinet6: in6.c.orig Only in sys/netinet6: in6.c~ diff -upr ../src/sys/netinet6/in6.h sys/netinet6/in6.h --- ../src/sys/netinet6/in6.h 2009-04-14 20:14:26.000000000 -0700 +++ sys/netinet6/in6.h 2011-04-19 09:41:23.000000000 -0700 @@ -620,6 +620,7 @@ int in6_localaddr __P((struct in6_addr * int in6_addrscope __P((struct in6_addr *)); struct in6_ifaddr *in6_ifawithifp __P((struct ifnet *, struct in6_addr *)); extern void in6_if_up __P((struct ifnet *)); +extern void in6_if_down __P((struct ifnet *)); struct sockaddr; extern u_char ip6_protox[]; diff -upr ../src/sys/netinet6/in6_ifattach.c sys/netinet6/in6_ifattach.c --- ../src/sys/netinet6/in6_ifattach.c 2009-04-14 20:14:26.000000000 -0700 +++ sys/netinet6/in6_ifattach.c 2011-04-19 10:29:31.000000000 -0700 @@ -78,7 +78,7 @@ static int generate_tmp_ifid(u_int8_t *, static int get_ifid(struct ifnet *, struct ifnet *, struct in6_addr *); static int in6_ifattach_linklocal(struct ifnet *, struct ifnet *); static int in6_ifattach_loopback(struct ifnet *); -static void in6_purgemaddrs(struct ifnet *); +/*static*/ void in6_purgemaddrs(struct ifnet *); #define EUI64_GBIT 0x01 #define EUI64_UBIT 0x02 @@ -886,7 +886,7 @@ in6_tmpaddrtimer(void *ignored_arg) splx(s); } -static void +/*static*/ void in6_purgemaddrs(struct ifnet *ifp) { struct in6_multi *in6m; --ELM1305734334-34657-0_--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201105181558.p4IFws95036355>