]> git.hungrycats.org Git - linux/commitdiff
[MULTICAST]: multicast loop with include filters fix
authorDavid Stevens <dlstevens@us.ibm.com>
Sat, 24 Jan 2004 01:59:08 +0000 (17:59 -0800)
committerJames Morris <jmorris@kernel.bkbits.net>
Sat, 24 Jan 2004 01:59:08 +0000 (17:59 -0800)
When sending a multicast and using looping back a copy to the
local machine, the interface filter checks can be done before the
source address is specified. For an INCLUDE filter, this won't match
the allowed sources and the packets won't be delivered locally,
even when the ultimate source address chosen is in the allowed list.

The patch below fixes the filter checks for both IGMPv3 and MLDv2
to only apply when a source address is available.

Thanks to Steven Hessing for reporting the problem and providing
a test case for reproducing it.

net/ipv4/igmp.c
net/ipv6/mcast.c

index b9840662ad5db9894f3e9b67a889fadad1193be7..b1f9f7d62e386cc3100b87817ac1a06139bfbea8 100644 (file)
@@ -2084,16 +2084,19 @@ int ip_check_mc(struct in_device *in_dev, u32 mc_addr, u32 src_addr, u16 proto)
        if (im && proto == IPPROTO_IGMP) {
                rv = 1;
        } else if (im) {
-               for (psf=im->sources; psf; psf=psf->sf_next) {
-                       if (psf->sf_inaddr == src_addr)
-                               break;
-               }
-               if (psf)
-                       rv = psf->sf_count[MCAST_INCLUDE] ||
-                               psf->sf_count[MCAST_EXCLUDE] !=
-                               im->sfcount[MCAST_EXCLUDE];
-               else
-                       rv = im->sfcount[MCAST_EXCLUDE] != 0;
+               if (src_addr) {
+                       for (psf=im->sources; psf; psf=psf->sf_next) {
+                               if (psf->sf_inaddr == src_addr)
+                                       break;
+                       }
+                       if (psf)
+                               rv = psf->sf_count[MCAST_INCLUDE] ||
+                                       psf->sf_count[MCAST_EXCLUDE] !=
+                                       im->sfcount[MCAST_EXCLUDE];
+                       else
+                               rv = im->sfcount[MCAST_EXCLUDE] != 0;
+               } else
+                       rv = 1; /* unspecified source; tentatively allow */
        }
        read_unlock(&in_dev->lock);
        return rv;
index 0e9dfa644e80ca1490593249985ef34602aa9aea..8c8c2c9d7ad5101c6e7faed400e7808acaf6c205 100644 (file)
@@ -918,20 +918,24 @@ int ipv6_chk_mcast_addr(struct net_device *dev, struct in6_addr *group,
                                break;
                }
                if (mc) {
-                       struct ip6_sf_list *psf;
-
-                       spin_lock_bh(&mc->mca_lock);
-                       for (psf=mc->mca_sources; psf; psf=psf->sf_next) {
-                               if (ipv6_addr_cmp(&psf->sf_addr, src_addr) == 0)
-                                       break;
-                       }
-                       if (psf)
-                               rv = psf->sf_count[MCAST_INCLUDE] ||
-                                       psf->sf_count[MCAST_EXCLUDE] !=
-                                       mc->mca_sfcount[MCAST_EXCLUDE];
-                       else
-                               rv = mc->mca_sfcount[MCAST_EXCLUDE] != 0;
-                       spin_unlock_bh(&mc->mca_lock);
+                       if (!ipv6_addr_any(src_addr)) {
+                               struct ip6_sf_list *psf;
+
+                               spin_lock_bh(&mc->mca_lock);
+                               for (psf=mc->mca_sources;psf;psf=psf->sf_next) {
+                                       if (ipv6_addr_cmp(&psf->sf_addr,
+                                           src_addr) == 0)
+                                               break;
+                               }
+                               if (psf)
+                                       rv = psf->sf_count[MCAST_INCLUDE] ||
+                                               psf->sf_count[MCAST_EXCLUDE] !=
+                                               mc->mca_sfcount[MCAST_EXCLUDE];
+                               else
+                                       rv = mc->mca_sfcount[MCAST_EXCLUDE] !=0;
+                               spin_unlock_bh(&mc->mca_lock);
+                       } else
+                               rv = 1; /* don't filter unspecified source */
                }
                read_unlock_bh(&idev->lock);
                in6_dev_put(idev);