[MCAST]: Fix filter leak on device removal.
authorDavid L Stevens <dlstevens@us.ibm.com>
Thu, 17 Aug 2006 23:27:39 +0000 (16:27 -0700)
committerDavid S. Miller <davem@sunset.davemloft.net>
Thu, 17 Aug 2006 23:29:57 +0000 (16:29 -0700)
This fixes source filter leakage when a device is removed and a
process leaves the group thereafter.

This also includes corresponding fixes for IPv6 multicast source
filters on device removal.

Signed-off-by: David L Stevens <dlstevens@us.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/igmp.c
net/ipv6/mcast.c

index e981369ebe13d2b10611ae3ec6134993bc08acc3..8e8117c19e4db24ad74d55cfe540fb0d4c9816f3 100644 (file)
@@ -1793,29 +1793,35 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
        struct in_device *in_dev;
        u32 group = imr->imr_multiaddr.s_addr;
        u32 ifindex;
+       int ret = -EADDRNOTAVAIL;
 
        rtnl_lock();
        in_dev = ip_mc_find_dev(imr);
-       if (!in_dev) {
-               rtnl_unlock();
-               return -ENODEV;
-       }
        ifindex = imr->imr_ifindex;
        for (imlp = &inet->mc_list; (iml = *imlp) != NULL; imlp = &iml->next) {
-               if (iml->multi.imr_multiaddr.s_addr == group &&
-                   iml->multi.imr_ifindex == ifindex) {
-                       (void) ip_mc_leave_src(sk, iml, in_dev);
+               if (iml->multi.imr_multiaddr.s_addr != group)
+                       continue;
+               if (ifindex) {
+                       if (iml->multi.imr_ifindex != ifindex)
+                               continue;
+               } else if (imr->imr_address.s_addr && imr->imr_address.s_addr !=
+                               iml->multi.imr_address.s_addr)
+                       continue;
 
-                       *imlp = iml->next;
+               (void) ip_mc_leave_src(sk, iml, in_dev);
 
+               *imlp = iml->next;
+
+               if (in_dev)
                        ip_mc_dec_group(in_dev, group);
-                       rtnl_unlock();
-                       sock_kfree_s(sk, iml, sizeof(*iml));
-                       return 0;
-               }
+               rtnl_unlock();
+               sock_kfree_s(sk, iml, sizeof(*iml));
+               return 0;
        }
+       if (!in_dev)
+               ret = -ENODEV;
        rtnl_unlock();
-       return -EADDRNOTAVAIL;
+       return ret;
 }
 
 int ip_mc_source(int add, int omode, struct sock *sk, struct
index 9d697d4dcffccde33651b99a5e9b7fe9b37ddf48..639eb20c9f1fd08181c104593f3f39bc8bad3e0d 100644 (file)
@@ -268,13 +268,14 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
                        if ((dev = dev_get_by_index(mc_lst->ifindex)) != NULL) {
                                struct inet6_dev *idev = in6_dev_get(dev);
 
+                               (void) ip6_mc_leave_src(sk, mc_lst, idev);
                                if (idev) {
-                                       (void) ip6_mc_leave_src(sk,mc_lst,idev);
                                        __ipv6_dev_mc_dec(idev, &mc_lst->addr);
                                        in6_dev_put(idev);
                                }
                                dev_put(dev);
-                       }
+                       } else
+                               (void) ip6_mc_leave_src(sk, mc_lst, NULL);
                        sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
                        return 0;
                }
@@ -334,13 +335,14 @@ void ipv6_sock_mc_close(struct sock *sk)
                if (dev) {
                        struct inet6_dev *idev = in6_dev_get(dev);
 
+                       (void) ip6_mc_leave_src(sk, mc_lst, idev);
                        if (idev) {
-                               (void) ip6_mc_leave_src(sk, mc_lst, idev);
                                __ipv6_dev_mc_dec(idev, &mc_lst->addr);
                                in6_dev_put(idev);
                        }
                        dev_put(dev);
-               }
+               } else
+                       (void) ip6_mc_leave_src(sk, mc_lst, NULL);
 
                sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));