Subject: Re: kern/29580
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: Greg Troxel <gdt@ir.bbn.com>
List: netbsd-bugs
Date: 05/12/2005 17:18:01
The following reply was made to PR kern/29580; it has been noted by GNATS.

From: Greg Troxel <gdt@ir.bbn.com>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: kern/29580
Date: Thu, 12 May 2005 13:16:38 -0400

 The following patch causes PRU_PURGEIF to be invoked even if no
 addresses are configured.  This may not include all protocols that
 might store pointers, but does include the common INET/INET6 ones.
 
 Index: sys/protosw.h
 ===================================================================
 RCS file: /SINEW-CVS/netbsd/src/sys/sys/protosw.h,v
 retrieving revision 1.1.1.3
 diff -u -r1.1.1.3 protosw.h
 --- sys/protosw.h	22 Apr 2004 01:34:17 -0000	1.1.1.3
 +++ sys/protosw.h	12 May 2005 17:09:02 -0000
 @@ -114,6 +114,9 @@
  #define	PR_LASTHDR	0x40		/* enforce ipsec policy; last header */
  #define	PR_ABRTACPTDIS	0x80		/* abort on accept(2) to disconnected
  					   socket */
 +#define PR_PURGEIF	0x100		/* might store struct ifnet pointer;
 +					   PRU_PURGEIF must be called on ifnet
 +					   deletion */
  
  /*
   * The arguments to usrreq are:
 Index: net/if.c
 ===================================================================
 RCS file: /SINEW-CVS/netbsd/src/sys/net/if.c,v
 retrieving revision 1.4
 diff -u -r1.4 if.c
 --- net/if.c	4 Feb 2005 14:57:25 -0000	1.4
 +++ net/if.c	12 May 2005 17:09:02 -0000
 @@ -647,10 +647,39 @@
  			(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.  Protocols which might
 +		 * store ifnet pointers are marked with PR_PURGEIF.
 +		 */
 +		for (pr = dp->dom_protosw;
 +		     pr < dp->dom_protoswNPROTOSW; pr++) {
 +			so.so_proto = pr;
 +			if (pr->pr_usrreq != NULL &&
 +			    pr->pr_flags & PR_PURGEIF) {
 +				(void) (*pr->pr_usrreq)(&so,
 +				    PRU_PURGEIF, NULL, NULL,
 +				    (struct mbuf *) ifp, curproc);
 +				purged = 1;
 +			}
 +		}
  	}
  
  	/* Announce that the interface is gone. */
 Index: netinet/in_proto.c
 ===================================================================
 RCS file: /SINEW-CVS/netbsd/src/sys/netinet/in_proto.c,v
 retrieving revision 1.1.1.3
 diff -u -r1.1.1.3 in_proto.c
 --- netinet/in_proto.c	31 Jan 2005 23:49:36 -0000	1.1.1.3
 +++ netinet/in_proto.c	12 May 2005 17:09:02 -0000
 @@ -156,17 +156,17 @@
    0,
    ip_init,	0,		ip_slowtimo,	ip_drain,	NULL
  },
 -{ SOCK_DGRAM,	&inetdomain,	IPPROTO_UDP,	PR_ATOMIC|PR_ADDR,
 +{ SOCK_DGRAM,	&inetdomain,	IPPROTO_UDP,	PR_ATOMIC|PR_ADDR|PR_PURGEIF,
    udp_input,	0,		udp_ctlinput,	ip_ctloutput,
    udp_usrreq,
    udp_init,	0,		0,		0,		NULL
  },
 -{ SOCK_STREAM,	&inetdomain,	IPPROTO_TCP,	PR_CONNREQUIRED|PR_WANTRCVD|PR_LISTEN|PR_ABRTACPTDIS,
 +{ SOCK_STREAM,	&inetdomain,	IPPROTO_TCP,	PR_CONNREQUIRED|PR_WANTRCVD|PR_LISTEN|PR_ABRTACPTDIS|PR_PURGEIF,
    tcp_input,	0,		tcp_ctlinput,	tcp_ctloutput,
    tcp_usrreq,
    tcp_init,	0,		tcp_slowtimo,	tcp_drain,	NULL
  },
 -{ SOCK_RAW,	&inetdomain,	IPPROTO_RAW,	PR_ATOMIC|PR_ADDR,
 +{ SOCK_RAW,	&inetdomain,	IPPROTO_RAW,	PR_ATOMIC|PR_ADDR|PR_PURGEIF,
    rip_input,	rip_output,	rip_ctlinput,	rip_ctloutput,
    rip_usrreq,
    0,		0,		0,		0,
 Index: netinet6/in6_proto.c
 ===================================================================
 RCS file: /SINEW-CVS/netbsd/src/sys/netinet6/in6_proto.c,v
 retrieving revision 1.1.1.3
 diff -u -r1.1.1.3 in6_proto.c
 --- netinet6/in6_proto.c	23 Jan 2005 18:41:57 -0000	1.1.1.3
 +++ netinet6/in6_proto.c	12 May 2005 17:09:02 -0000
 @@ -136,13 +136,13 @@
    ip6_init,	0,		frag6_slowtimo,	frag6_drain,
    NULL,
  },
 -{ SOCK_DGRAM,	&inet6domain,	IPPROTO_UDP,	PR_ATOMIC|PR_ADDR,
 +{ SOCK_DGRAM,	&inet6domain,	IPPROTO_UDP,	PR_ATOMIC|PR_ADDR|PR_PURGEIF,
    udp6_input,	0,		udp6_ctlinput,	ip6_ctloutput,
    udp6_usrreq,	udp6_init,
    0,		0,		0,
    NULL,
  },
 -{ SOCK_STREAM,	&inet6domain,	IPPROTO_TCP,	PR_CONNREQUIRED|PR_WANTRCVD|PR_LISTEN|PR_ABRTACPTDIS,
 +{ SOCK_STREAM,	&inet6domain,	IPPROTO_TCP,	PR_CONNREQUIRED|PR_WANTRCVD|PR_LISTEN|PR_ABRTACPTDIS|PR_PURGEIF,
    tcp6_input,	0,		tcp6_ctlinput,	tcp_ctloutput,
    tcp_usrreq,
  #ifdef INET	/* don't call initialization and timeout routines twice */
 @@ -152,7 +152,7 @@
  #endif
    NULL,
  },
 -{ SOCK_RAW,	&inet6domain,	IPPROTO_RAW,	PR_ATOMIC|PR_ADDR,
 +{ SOCK_RAW,	&inet6domain,	IPPROTO_RAW,	PR_ATOMIC|PR_ADDR|PR_PURGEIF,
    rip6_input,	rip6_output,	rip6_ctlinput,	rip6_ctloutput,
    rip6_usrreq,
    0,		0,		0,		0,