diff options
| author | Mark Johnston <markj@FreeBSD.org> | 2026-03-30 13:33:32 +0000 |
|---|---|---|
| committer | Mark Johnston <markj@FreeBSD.org> | 2026-03-30 16:08:21 +0000 |
| commit | a1c042a9641c9df08010e87d0928703849ff9853 (patch) | |
| tree | 8467b3fca5ca7bb801782eca866f62ef50f8a0b4 | |
| parent | 82272cbf56aa5646611c6f1049501cc345fcf6ec (diff) | |
ip6_mroute: Handle interface detach events
When an interface goes away we need to make sure the v6 multicast
routing tables don't carry any dangling references to the ifnet. The v4
code handles this already. Copy the approach there and use an
eventhandler to purge the corresponding MIF, if one exists, and further
go through all routes in the FIB and remove references to the interface.
MFC after: 2 weeks
Sponsored by: Stormshield
Sponsored by: Klara, Inc.
Differential Revision: https://reviews.freebsd.org/D55246
| -rw-r--r-- | sys/netinet6/ip6_mroute.c | 69 |
1 files changed, 67 insertions, 2 deletions
diff --git a/sys/netinet6/ip6_mroute.c b/sys/netinet6/ip6_mroute.c index f9541203a747..9a7f611b8b4c 100644 --- a/sys/netinet6/ip6_mroute.c +++ b/sys/netinet6/ip6_mroute.c @@ -211,7 +211,7 @@ VNET_DEFINE_STATIC(struct mf6ctable *, mfctables); VNET_DEFINE_STATIC(uint32_t, nmfctables); #define V_nmfctables VNET(nmfctables) -static eventhandler_tag rtnumfibs_change_tag; +static eventhandler_tag ifdetach_tag, rtnumfibs_change_tag; static int sysctl_mfctable(SYSCTL_HANDLER_ARGS) @@ -791,6 +791,20 @@ add_m6if(struct mf6ctable *mfct, int fibnum, struct mif6ctl *mifcp) return (0); } +static void +expire_mf6c(struct mf6c *mfc) +{ + struct rtdetq *rte; + + while ((rte = mfc->mf6c_stall) != NULL) { + mfc->mf6c_stall = rte->next; + m_freem(rte->m); + free(rte, M_MRTABLE6); + } + + free(mfc, M_MRTABLE6); +} + /* * Delete a mif from the mif table */ @@ -810,9 +824,29 @@ del_m6if_locked(struct mf6ctable *mfct, mifi_t mifi) return (EINVAL); if (!(mifp->m6_flags & MIFF_REGISTER)) { - /* XXX: TODO: Maintain an ALLMULTI refcount in struct ifnet. */ ifp = mifp->m6_ifp; if_allmulti(ifp, 0); + + MFC6_LOCK(); + for (int i = 0; i < MF6CTBLSIZ; i++) { + struct mf6c *mfc, **nmfc; + + nmfc = &mfct->mfchashtbl[i]; + while ((mfc = *nmfc) != NULL) { + if (mfc->mf6c_parent == mifi) { + *nmfc = mfc->mf6c_next; + if (mfc->mf6c_expire) + mfct->nexpire[i]--; + expire_mf6c(mfc); + } else { + /* Remove this mif from the ifset */ + if (IF_ISSET(mifi, &mfc->mf6c_ifset)) + IF_CLR(mifi, &mfc->mf6c_ifset); + nmfc = &mfc->mf6c_next; + } + } + } + MFC6_UNLOCK(); } else { if (mfct->register_mif != (mifi_t)-1 && mfct->register_if != NULL) { @@ -1955,6 +1989,32 @@ pim6_input(struct mbuf *m, int off, int proto, void *arg __unused) } static void +ip6_mrouter_ifdetach(void *arg __unused, struct ifnet *ifp) +{ + struct mf6ctable *mfct; + + if (!V_ip6_mrouting_enabled) + return; + for (int i = 0; i < V_nmfctables; i++) { + mfct = &V_mfctables[i]; + + MIF6_LOCK(); +restart: + for (mifi_t mifi = 0; mifi < mfct->nummifs; mifi++) { + int error __diagused; + + if (mfct->miftable[mifi].m6_ifp != ifp) + continue; + error = del_m6if_locked(mfct, mifi); + KASSERT(error == 0, + ("del_m6if_locked(%s) %d", ifp->if_xname, error)); + goto restart; + } + MIF6_UNLOCK(); + } +} + +static void ip6_mroute_rtnumfibs_change(void *arg __unused, uint32_t ntables) { struct mf6ctable *mfctables, *omfctables; @@ -2010,6 +2070,9 @@ ip6_mroute_modevent(module_t mod, int type, void *unused) MFC6_LOCK_INIT(); MIF6_LOCK_INIT(); + ifdetach_tag = EVENTHANDLER_REGISTER( + ifnet_departure_event, ip6_mrouter_ifdetach, + NULL, EVENTHANDLER_PRI_ANY); rtnumfibs_change_tag = EVENTHANDLER_REGISTER( rtnumfibs_change, ip6_mroute_rtnumfibs_change, NULL, EVENTHANDLER_PRI_ANY); @@ -2035,6 +2098,8 @@ ip6_mroute_modevent(module_t mod, int type, void *unused) if (V_ip6_mrouting_enabled) return (EBUSY); + EVENTHANDLER_DEREGISTER(ifnet_departure_event, + ifdetach_tag); EVENTHANDLER_DEREGISTER(rtnumfibs_change, rtnumfibs_change_tag); |
