Subject: Re: Possible memory leak for multicast all-hosts address?
To: Shiva Shenoy <shiva_s@yahoo.com>
From: None <itojun@iijlab.net>
List: tech-net
Date: 10/08/2000 10:51:45
>>When an IP interface is configured using ifconfig, I
>>see that a 
>>INADDR_ALLHOSTS_GROUP address gets attached to that
>>interface:
>>ini_ifinit() in netinet/in.c
>>if (ifp->if_flags & IFF_MULTICAST) {
>>	struct in_addr addr;
>>	addr.s_addr = INADDR_ALLHOSTS_GROUP;
>>	in_addmulti(&addr, ifp);
>>}
>>But I dont see associated in_delmulti() when an
>>interface is deleted - "ifconfig de0 delete"
>>(Should that have been added to in_purgeaddr()?)
>	it looks your observation correct.  we need some improvement here.

	the patch should do the right thing.  please test.

itojun


Index: in.c
===================================================================
RCS file: /cvsroot/syssrc/sys/netinet/in.c,v
retrieving revision 1.63
diff -c -r1.63 in.c
*** in.c	2000/10/06 05:07:41	1.63
--- in.c	2000/10/08 01:50:16
***************
*** 145,150 ****
--- 145,162 ----
  int hostzeroisbroadcast = HOSTZEROBROADCAST;
  
  /*
+  * This structure is used to keep track of in6_multi chains which belong to
+  * deleted interface addresses.
+  */
+ static LIST_HEAD(, multi_kludge) in_mk; /* XXX BSS initialization */
+ 
+ struct multi_kludge {
+ 	LIST_ENTRY(multi_kludge) mk_entry;
+ 	struct ifnet *mk_ifp;
+ 	LIST_HEAD(, in_multi) mk_head;
+ };
+ 
+ /*
   * Return 1 if an internet address is for a ``local'' host
   * (one to which we have a connection).  If subnetsarelocal
   * is true, this includes other subnets of the local net.
***************
*** 557,562 ****
--- 569,587 ----
  	TAILQ_REMOVE(&ifp->if_addrlist, &ia->ia_ifa, ifa_list);
  	IFAFREE(&ia->ia_ifa);
  	TAILQ_REMOVE(&in_ifaddr, ia, ia_list);
+ 	if (LIST_FIRST(&ia->ia_multiaddrs) != NULL) {
+ 		/*
+ 		 * XXX thorpej@netbsd.org -- if the interface is going
+ 		 * XXX away, don't save the multicast entries, delete them!
+ 		 */
+ 		if (ia->ia_ifa.ifa_ifp->if_output == if_nulloutput) {
+ 			struct in_multi *inm;
+ 
+ 			while ((inm = LIST_FIRST(&ia->ia_multiaddrs)) != NULL)
+ 				in_delmulti(inm);
+ 		} else
+ 			in_savemkludge(ia);
+ 	}
  	IFAFREE(&ia->ia_ifa);
  	in_setmaxmtu();
  }
***************
*** 573,578 ****
--- 598,604 ----
  			continue;
  		in_purgeaddr(ifa, ifp);
  	}
+ 	in_purgemkludge(ifp);
  }
  
  /*
***************
*** 864,869 ****
--- 890,900 ----
  	if (error == EEXIST)
  		error = 0;
  	/*
+ 	 * recover multicast kludge entry, if there is.
+ 	 */
+ 	if (ifp->if_flags & IFF_MULTICAST)
+ 		in_restoremkludge(ia, ifp);
+ 	/*
  	 * If the interface supports multicast, join the "all hosts"
  	 * multicast group on that interface.
  	 */
***************
*** 917,922 ****
--- 948,1048 ----
  			return 1;
  	return (0);
  #undef ia
+ }
+ 
+ /*
+  * Multicast address kludge:
+  * If there were any multicast addresses attached to this interface address,
+  * either move them to another address on this interface, or save them until
+  * such time as this interface is reconfigured for IPv6.
+  */
+ void
+ in_savemkludge(oia)
+ 	struct in_ifaddr *oia;
+ {
+ 	struct in_ifaddr *ia;
+ 	struct in_multi *inm, *next;
+ 
+ 	IFP_TO_IA(oia->ia_ifp, ia);
+ 	if (ia) {	/* there is another address */
+ 		for (inm = oia->ia_multiaddrs.lh_first; inm; inm = next){
+ 			next = inm->inm_list.le_next;
+ 			IFAFREE(&inm->inm_ia->ia_ifa);
+ 			IFAREF(&ia->ia_ifa);
+ 			inm->inm_ia = ia;
+ 			LIST_INSERT_HEAD(&ia->ia_multiaddrs, inm, inm_list);
+ 		}
+ 	} else {	/* last address on this if deleted, save */
+ 		struct multi_kludge *mk;
+ 
+ 		mk = malloc(sizeof(*mk), M_IPMADDR, M_WAITOK);
+ 
+ 		LIST_INIT(&mk->mk_head);
+ 		mk->mk_ifp = oia->ia_ifp;
+ 
+ 		for (inm = oia->ia_multiaddrs.lh_first; inm; inm = next){
+ 			next = inm->inm_list.le_next;
+ 			IFAFREE(&inm->inm_ia->ia_ifa); /* release reference */
+ 			inm->inm_ia = NULL;
+ 			LIST_INSERT_HEAD(&mk->mk_head, inm, inm_list);
+ 		}
+ 
+ 		if (mk->mk_head.lh_first != NULL) {
+ 			LIST_INSERT_HEAD(&in_mk, mk, mk_entry);
+ 		} else {
+ 			FREE(mk, M_IPMADDR);
+ 		}
+ 	}
+ }
+ 
+ /*
+  * Continuation of multicast address hack:
+  * If there was a multicast group list previously saved for this interface,
+  * then we re-attach it to the first address configured on the i/f.
+  */
+ void
+ in_restoremkludge(ia, ifp)
+ 	struct in_ifaddr *ia;
+ 	struct ifnet *ifp;
+ {
+ 	struct multi_kludge *mk;
+ 
+ 	for (mk = in_mk.lh_first; mk; mk = mk->mk_entry.le_next) {
+ 		if (mk->mk_ifp == ifp) {
+ 			struct in_multi *inm, *next;
+ 
+ 			for (inm = mk->mk_head.lh_first; inm; inm = next){
+ 				next = inm->inm_list.le_next;
+ 				inm->inm_ia = ia;
+ 				IFAREF(&ia->ia_ifa);
+ 				LIST_INSERT_HEAD(&ia->ia_multiaddrs,
+ 						 inm, inm_list);
+ 			}
+ 			LIST_REMOVE(mk, mk_entry);
+ 			free(mk, M_IPMADDR);
+ 			break;
+ 		}
+ 	}
+ }
+ 
+ void
+ in_purgemkludge(ifp)
+ 	struct ifnet *ifp;
+ {
+ 	struct multi_kludge *mk;
+ 	struct in_multi *inm;
+ 
+ 	for (mk = in_mk.lh_first; mk; mk = mk->mk_entry.le_next) {
+ 		if (mk->mk_ifp != ifp)
+ 			continue;
+ 
+ 		/* leave from all multicast groups joined */
+ 		while ((inm = LIST_FIRST(&mk->mk_head)) != NULL)
+ 			in_delmulti(inm);
+ 		LIST_REMOVE(mk, mk_entry);
+ 		free(mk, M_IPMADDR);
+ 		break;
+ 	}
  }
  
  /*
Index: in_var.h
===================================================================
RCS file: /cvsroot/syssrc/sys/netinet/in_var.h,v
retrieving revision 1.39
diff -c -r1.39 in_var.h
*** in_var.h	2000/03/30 13:24:57	1.39
--- in_var.h	2000/10/08 01:50:17
***************
*** 304,309 ****
--- 304,312 ----
  
  int	in_ifinit __P((struct ifnet *,
  	    struct in_ifaddr *, struct sockaddr_in *, int));
+ void	in_savemkludge __P((struct in_ifaddr *));
+ void	in_restoremkludge __P((struct in_ifaddr *, struct ifnet *));
+ void	in_purgemkludge __P((struct ifnet *));
  struct	in_multi *in_addmulti __P((struct in_addr *, struct ifnet *));
  void	in_delmulti __P((struct in_multi *));
  void	in_ifscrub __P((struct ifnet *, struct in_ifaddr *));