diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 919b2641deaa..5deefe95da6d 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -901,6 +901,7 @@ in6_update_ifa(ifp, ifra, ia, flags) if (ia == NULL) return (ENOBUFS); bzero((caddr_t)ia, sizeof(*ia)); + LIST_INIT(&ia->ia6_memberships); /* Initialize the address and masks, and put time stamp */ IFA_LOCK_INIT(&ia->ia_ifa); ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; @@ -1058,7 +1059,7 @@ in6_update_ifa(ifp, ifra, ia, flags) (MAX_RTR_SOLICITATION_DELAY * hz); } imm = in6_joingroup(ifp, &llsol, &error, delay); - if (error != 0) { + if (imm == NULL) { nd6log((LOG_WARNING, "in6_update_ifa: addmulti failed for " "%s on %s (errno=%d)\n", @@ -1067,6 +1068,8 @@ in6_update_ifa(ifp, ifra, ia, flags) in6_purgeaddr((struct ifaddr *)ia); return (error); } + LIST_INSERT_HEAD(&ia->ia6_memberships, + imm, i6mm_chain); in6m_sol = imm->i6mm_maddr; bzero(&mltmask, sizeof(mltmask)); @@ -1148,6 +1151,7 @@ in6_update_ifa(ifp, ifra, ia, flags) if_name(ifp), error)); goto cleanup; } + LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); /* * join node information group address @@ -1173,6 +1177,9 @@ in6_update_ifa(ifp, ifra, ia, flags) ip6_sprintf(&mltaddr.sin6_addr), if_name(ifp), error)); /* XXX not very fatal, go on... */ + } else { + LIST_INSERT_HEAD(&ia->ia6_memberships, + imm, i6mm_chain); } } #undef hostnamelen @@ -1235,6 +1242,7 @@ in6_update_ifa(ifp, ifra, ia, flags) if_name(ifp), error)); goto cleanup; } + LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); #undef MLTMASK_LEN } @@ -1298,6 +1306,7 @@ in6_purgeaddr(ifa) { struct ifnet *ifp = ifa->ifa_ifp; struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa; + struct in6_multi_mship *imm; /* stop DAD processing */ nd6_dad_stop(ifa); @@ -1324,24 +1333,12 @@ in6_purgeaddr(ifa) /* Remove ownaddr's loopback rtentry, if it exists. */ in6_ifremloop(&(ia->ia_ifa)); - if (ifp->if_flags & IFF_MULTICAST) { - /* - * delete solicited multicast addr for deleting host id - */ - struct in6_multi *in6m; - struct in6_addr llsol; - bzero(&llsol, sizeof(struct in6_addr)); - llsol.s6_addr32[0] = IPV6_ADDR_INT32_MLL; - llsol.s6_addr32[1] = 0; - llsol.s6_addr32[2] = htonl(1); - llsol.s6_addr32[3] = - ia->ia_addr.sin6_addr.s6_addr32[3]; - llsol.s6_addr8[12] = 0xff; - (void)in6_setscope(&llsol, ifp, NULL); /* XXX proceed anyway */ - - IN6_LOOKUP_MULTI(llsol, ifp, in6m); - if (in6m) - in6_delmulti(in6m); + /* + * 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); } in6_unlink_ifa(ia, ifp); diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c index 6b7d022ea6e7..218cbc0415df 100644 --- a/sys/netinet6/in6_ifattach.c +++ b/sys/netinet6/in6_ifattach.c @@ -733,8 +733,8 @@ in6_ifdetach(ifp) struct rtentry *rt; short rtflags; struct sockaddr_in6 sin6; - struct in6_multi *in6m; - struct in6_multi *in6m_next; + struct in6_multi *in6m, *in6m_next; + struct in6_multi_mship *imm; /* remove neighbor management table */ nd6_purge(ifp); @@ -758,6 +758,14 @@ in6_ifdetach(ifp) 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); + } + /* remove from the routing table */ if ((ia->ia_flags & IFA_ROUTE) && (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0, 0UL))) { @@ -803,6 +811,8 @@ in6_ifdetach(ifp) in6m_next = LIST_NEXT(in6m, in6m_entry); if (in6m->in6m_ifp != ifp) continue; + printf("in6_ifdetach: in6m=%p (ref=%d), ifp=%p\n", in6m, + in6m->in6m_ifma->ifma_refcount, ifp); in6_delmulti(in6m); in6m = NULL; } diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h index 881b21f8b914..4bd687c18870 100644 --- a/sys/netinet6/in6_var.h +++ b/sys/netinet6/in6_var.h @@ -115,6 +115,9 @@ struct in6_ifaddr { /* back pointer to the ND prefix (for autoconfigured addresses only) */ struct nd_prefix *ia6_ndpr; + + /* multicast addresses joined from the kernel */ + LIST_HEAD(, in6_multi_mship) ia6_memberships; }; /* control structure to manage address selection policy */