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 *));