Subject: interface destruction issues with multicast membership
To: None <tech-net@netbsd.org>
From: Greg Troxel <gdt@ir.bbn.com>
List: tech-net
Date: 05/10/2005 15:00:27
Earlier, I wrote about trouble with destroying a ppp(4) interface when
it had not IP addresses but did have multicast memberships.  The
udp_usrreq PRU_PURGEIF did not get invoked due to not having
configured addresses.

I patched sys/net/if.c to unconditionally call foo_usrreq for all
protocols in all domains, and got a panic for an unhandled request in
raw_usrreq called from key_usrreq.  My thinking is that telling a
protocol that an interface is gone is always legitimate, even if the
protocol doesn't care, and thus such usrreq functions should be
adjusted to just return quietly in such cases.  Would a patch that had
the below changes and also such "if PURGEIF return 0" cleanups be
reasonable?


Index: if.c
===================================================================
RCS file: /SINEW-CVS/netbsd/src/sys/net/if.c,v
retrieving revision 1.4
diff -u -r1.4 if.c
--- if.c	4 Feb 2005 14:57:25 -0000	1.4
+++ if.c	10 May 2005 18:31:33 -0000
@@ -647,10 +647,40 @@
 			(void) (*rnh->rnh_walktree)(rnh, if_rt_walktree, ifp);
 	}
 
+	/*
+	 * Perhaps this should remove multicast memberships, but inet
+	 * domain does not have a dom_ifdetach function.
+	 */
 	DOMAIN_FOREACH(dp) {
 		if (dp->dom_ifdetach && ifp->if_afdata[dp->dom_family])
 			(*dp->dom_ifdetach)(ifp,
 			    ifp->if_afdata[dp->dom_family]);
+
+		/*
+		 * One would expect multicast memberships (INET and
+		 * INET6) on UDP sockets to be purged by the PURGEIF
+		 * calls above, but if all addresses were removed from
+		 * the interface prior to destruction, the calls will
+		 * not be made (e.g. ppp, for which pppd(8) generally
+		 * removees addresses before destroying the
+		 * interface).  Because there is no invariant that
+		 * multicast memberships only exist for interfaces
+		 * with IPv4 addresses, we must call PURGEIF
+		 * regardless of addresses.  Rather than trying to
+		 * call it for only those protocols which might need
+		 * it, we simply call it for all protocols; interface
+		 * destruction is infrequent.
+		 */
+		for (pr = dp->dom_protosw;
+		     pr < dp->dom_protoswNPROTOSW; pr++) {
+			so.so_proto = pr;
+			if (pr->pr_usrreq != NULL) {
+				(void) (*pr->pr_usrreq)(&so,
+				    PRU_PURGEIF, NULL, NULL,
+				    (struct mbuf *) ifp, curproc);
+				purged = 1;
+			}
+		}
 	}
 
 	/* Announce that the interface is gone. */



-- 
        Greg Troxel <gdt@ir.bbn.com>