summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/netinet/ip_mroute.c92
1 files changed, 87 insertions, 5 deletions
diff --git a/sys/netinet/ip_mroute.c b/sys/netinet/ip_mroute.c
index c5f686e..5f5fb2f 100644
--- a/sys/netinet/ip_mroute.c
+++ b/sys/netinet/ip_mroute.c
@@ -124,6 +124,10 @@ static MALLOC_DEFINE(M_MRTABLE, "mroutetbl", "multicast routing tables");
* to cover not only the specific data structure but also related data
* structures. It may be better to add more fine-grained locking later;
* it's not clear how performance-critical this code is.
+ *
+ * XXX: This module could particularly benefit from being cleaned
+ * up to use the <sys/queue.h> macros.
+ *
*/
static struct mrtstat mrtstat;
@@ -160,6 +164,8 @@ static struct mtx vif_mtx;
static u_char nexpire[MFCTBLSIZ];
+static eventhandler_tag if_detach_event_tag = NULL;
+
static struct callout expire_upcalls_ch;
#define EXPIRE_TIMEOUT (hz / 4) /* 4x / second */
@@ -304,8 +310,10 @@ static int X_mrt_ioctl(int cmd, caddr_t data);
static int get_sg_cnt(struct sioc_sg_req *);
static int get_vif_cnt(struct sioc_vif_req *);
+static void if_detached_event(void *arg __unused, struct ifnet *);
static int ip_mrouter_init(struct socket *, int);
static int add_vif(struct vifctl *);
+static int del_vif_locked(vifi_t);
static int del_vif(vifi_t);
static int add_mfc(struct mfcctl2 *);
static int del_mfc(struct mfcctl2 *);
@@ -652,6 +660,66 @@ ip_mrouter_reset(void)
static struct mtx mrouter_mtx; /* used to synch init/done work */
+static void
+if_detached_event(void *arg __unused, struct ifnet *ifp)
+{
+ vifi_t vifi;
+ int i;
+ struct mfc *mfc;
+ struct mfc *nmfc;
+ struct mfc **ppmfc; /* Pointer to previous node's next-pointer */
+ struct rtdetq *pq;
+ struct rtdetq *npq;
+
+ mtx_lock(&mrouter_mtx);
+ if (ip_mrouter == NULL) {
+ mtx_unlock(&mrouter_mtx);
+ }
+
+ /*
+ * Tear down multicast forwarder state associated with this ifnet.
+ * 1. Walk the vif list, matching vifs against this ifnet.
+ * 2. Walk the multicast forwarding cache (mfc) looking for
+ * inner matches with this vif's index.
+ * 3. Free any pending mbufs for this mfc.
+ * 4. Free the associated mfc entry and state associated with this vif.
+ * Be very careful about unlinking from a singly-linked list whose
+ * "head node" is a pointer in a simple array.
+ * 5. Free vif state. This should disable ALLMULTI on the interface.
+ */
+ VIF_LOCK();
+ MFC_LOCK();
+ for (vifi = 0; vifi < numvifs; vifi++) {
+ if (viftable[vifi].v_ifp != ifp)
+ continue;
+ for (i = 0; i < MFCTBLSIZ; i++) {
+ ppmfc = &mfctable[i];
+ for (mfc = mfctable[i]; mfc != NULL; ) {
+ nmfc = mfc->mfc_next;
+ if (mfc->mfc_parent == vifi) {
+ for (pq = mfc->mfc_stall; pq != NULL; ) {
+ npq = pq->next;
+ m_freem(pq->m);
+ free(pq, M_MRTABLE);
+ pq = npq;
+ }
+ free_bw_list(mfc->mfc_bw_meter);
+ free(mfc, M_MRTABLE);
+ *ppmfc = nmfc;
+ } else {
+ ppmfc = &mfc->mfc_next;
+ }
+ mfc = nmfc;
+ }
+ }
+ del_vif_locked(vifi);
+ }
+ MFC_UNLOCK();
+ VIF_UNLOCK();
+
+ mtx_unlock(&mrouter_mtx);
+}
+
/*
* Enable multicast routing
*/
@@ -675,6 +743,11 @@ ip_mrouter_init(struct socket *so, int version)
return EADDRINUSE;
}
+ if_detach_event_tag = EVENTHANDLER_REGISTER(ifnet_departure_event,
+ if_detached_event, NULL, EVENTHANDLER_PRI_ANY);
+ if (if_detach_event_tag == NULL)
+ return (ENOMEM);
+
callout_reset(&expire_upcalls_ch, EXPIRE_TIMEOUT, expire_upcalls, NULL);
callout_reset(&bw_upcalls_ch, BW_UPCALLS_PERIOD,
@@ -749,6 +822,7 @@ X_ip_mrouter_done(void)
numvifs = 0;
pim_assert = 0;
VIF_UNLOCK();
+ EVENTHANDLER_DEREGISTER(ifnet_departure_event, if_detach_event_tag);
/*
* Free all multicast forwarding cache entries.
@@ -1086,19 +1160,17 @@ add_vif(struct vifctl *vifcp)
* Delete a vif from the vif table
*/
static int
-del_vif(vifi_t vifi)
+del_vif_locked(vifi_t vifi)
{
struct vif *vifp;
- VIF_LOCK();
+ VIF_LOCK_ASSERT();
if (vifi >= numvifs) {
- VIF_UNLOCK();
return EINVAL;
}
vifp = &viftable[vifi];
if (vifp->v_lcl_addr.s_addr == INADDR_ANY) {
- VIF_UNLOCK();
return EADDRNOTAVAIL;
}
@@ -1137,9 +1209,19 @@ del_vif(vifi_t vifi)
break;
numvifs = vifi;
+ return 0;
+}
+
+static int
+del_vif(vifi_t vifi)
+{
+ int cc;
+
+ VIF_LOCK();
+ cc = del_vif_locked(vifi);
VIF_UNLOCK();
- return 0;
+ return cc;
}
/*
OpenPOWER on IntegriCloud