Commit b022a38e authored by David Stevens's avatar David Stevens Committed by James Morris

[MULTICAST]: multicast loop with include filters fix

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.
parent 672ab41e
...@@ -2084,16 +2084,19 @@ int ip_check_mc(struct in_device *in_dev, u32 mc_addr, u32 src_addr, u16 proto) ...@@ -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) { if (im && proto == IPPROTO_IGMP) {
rv = 1; rv = 1;
} else if (im) { } else if (im) {
for (psf=im->sources; psf; psf=psf->sf_next) { if (src_addr) {
if (psf->sf_inaddr == src_addr) for (psf=im->sources; psf; psf=psf->sf_next) {
break; if (psf->sf_inaddr == src_addr)
} break;
if (psf) }
rv = psf->sf_count[MCAST_INCLUDE] || if (psf)
psf->sf_count[MCAST_EXCLUDE] != rv = psf->sf_count[MCAST_INCLUDE] ||
im->sfcount[MCAST_EXCLUDE]; psf->sf_count[MCAST_EXCLUDE] !=
else im->sfcount[MCAST_EXCLUDE];
rv = im->sfcount[MCAST_EXCLUDE] != 0; else
rv = im->sfcount[MCAST_EXCLUDE] != 0;
} else
rv = 1; /* unspecified source; tentatively allow */
} }
read_unlock(&in_dev->lock); read_unlock(&in_dev->lock);
return rv; return rv;
......
...@@ -918,20 +918,24 @@ int ipv6_chk_mcast_addr(struct net_device *dev, struct in6_addr *group, ...@@ -918,20 +918,24 @@ int ipv6_chk_mcast_addr(struct net_device *dev, struct in6_addr *group,
break; break;
} }
if (mc) { if (mc) {
struct ip6_sf_list *psf; 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) { spin_lock_bh(&mc->mca_lock);
if (ipv6_addr_cmp(&psf->sf_addr, src_addr) == 0) for (psf=mc->mca_sources;psf;psf=psf->sf_next) {
break; if (ipv6_addr_cmp(&psf->sf_addr,
} src_addr) == 0)
if (psf) break;
rv = psf->sf_count[MCAST_INCLUDE] || }
psf->sf_count[MCAST_EXCLUDE] != if (psf)
mc->mca_sfcount[MCAST_EXCLUDE]; rv = psf->sf_count[MCAST_INCLUDE] ||
else psf->sf_count[MCAST_EXCLUDE] !=
rv = mc->mca_sfcount[MCAST_EXCLUDE] != 0; mc->mca_sfcount[MCAST_EXCLUDE];
spin_unlock_bh(&mc->mca_lock); 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); read_unlock_bh(&idev->lock);
in6_dev_put(idev); in6_dev_put(idev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment