Index: sys/netinet6/nd6_nbr.c =================================================================== --- sys/netinet6/nd6_nbr.c +++ sys/netinet6/nd6_nbr.c @@ -616,6 +616,7 @@ struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct nd_neighbor_advert *nd_na; + struct nd_defrouter *dr = NULL; struct in6_addr daddr6 = ip6->ip6_dst; struct in6_addr taddr6; int flags; @@ -634,6 +635,7 @@ size_t linkhdrsize; int lladdr_off; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; + bool nd6_locked = false; /* RFC 6980: Nodes MUST silently ignore fragments */ if(m->m_flags & M_FRAGMENTED) @@ -741,6 +743,9 @@ * discarded. */ IF_AFDATA_RLOCK(ifp); + /* Take nd6 prior to lle lock in case we call defrouter_removed(). */ + ND6_WLOCK(); + nd6_locked = true; ln = nd6_lookup(&taddr6, LLE_EXCLUSIVE, ifp); IF_AFDATA_RUNLOCK(ifp); if (ln == NULL) { @@ -869,7 +874,13 @@ struct ifnet *nd6_ifp; nd6_ifp = lltable_get_ifp(ln->lle_tbl); - if (!defrouter_remove(&ln->r_l3addr.addr6, nd6_ifp) && + + dr = defrouter_lookup_locked(&ln->r_l3addr.addr6, + nd6_ifp); + if (dr != NULL) + defrouter_unlink(dr, NULL); + + if (dr == NULL && (ND_IFINFO(nd6_ifp)->flags & ND6_IFF_ACCEPT_RTADV) != 0) /* @@ -893,6 +904,14 @@ if (ln != NULL) LLE_WUNLOCK(ln); + if (nd6_locked) + ND6_WUNLOCK(); + + if (dr != NULL) { + defrouter_del(dr); + defrouter_rele(dr); + } + if (chain != NULL) nd6_flush_holdchain(ifp, chain, &sin6); @@ -906,6 +925,9 @@ if (ln != NULL) LLE_WUNLOCK(ln); + if (nd6_locked) + ND6_WUNLOCK(); + ICMP6STAT_INC(icp6s_badna); m_freem(m); }