diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 6b3c558a4f23..3978c9f2d1c5 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -1413,6 +1413,25 @@ static void ip_mc_hash_remove(struct in_device *in_dev,
*mc_hash = im->next_hash;
}
+static struct ip_mc_list *ip_mc_hash_lookup(struct ip_mc_list __rcu **mc_hash,
+ __be32 mc_addr)
+{
+ struct ip_mc_list *im;
+ u32 hash;
+
+ if (mc_hash) {
+ hash = hash_32((__force u32)mc_addr, MC_HASH_SZ_LOG);
+ for (im = rcu_dereference(mc_hash[hash]);
+ im != NULL;
+ im = rcu_dereference(im->next_hash)) {
+ if (im->multiaddr == mc_addr)
+ break;
+ }
+ }
+
+ return im;
+}
+
/*
* A socket has joined a multicast group on device dev.@@ -2166,7 +2185,7 @@ static int __ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr,
ASSERT_RTNL();
- if (!ipv4_is_multicast(addr))
+ if (!ipv4_is_multicast(addr) && addr != htonl(INADDR_ANY))
return -EINVAL;
in_dev = ip_mc_find_dev(net, imr);
@@ -2627,6 +2646,11 @@ int ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr,
rcu_read_lock();
for_each_pmc_rcu(inet, pmc) {
+ if (pmc->multi.imr_multiaddr.s_addr == htonl(INADDR_ANY) &&
+ pmc->multi.imr_ifindex == dif) {
+ ret = 1;
+ goto unlock;
+ }
if (pmc->multi.imr_multiaddr.s_addr == loc_addr &&
(pmc->multi.imr_ifindex == dif ||
(sdif && pmc->multi.imr_ifindex == sdif)))@@ -2695,18 +2719,18 @@ int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u
mc_hash = rcu_dereference(in_dev->mc_hash);
if (mc_hash) {
- u32 hash = hash_32((__force u32)mc_addr, MC_HASH_SZ_LOG);
-
- for (im = rcu_dereference(mc_hash[hash]);
- im != NULL;
- im = rcu_dereference(im->next_hash)) {
- if (im->multiaddr == mc_addr)
- break;
+ im = ip_mc_hash_lookup(mc_hash, mc_addr);
+ if (!im) {
+ if (ip_mc_hash_lookup(mc_hash, htonl(INADDR_ANY)))
+ return 1;
}
+
} else {
for_each_pmc_rcu(in_dev, im) {
if (im->multiaddr == mc_addr)
break;
+ if (im->multiaddr == htonl(INADDR_ANY))
+ return 1;
}
}
if (im && proto == IPPROTO_IGMP) {diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 54ec163fbafa..7acf5b3cb435 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -177,7 +177,7 @@ static int __ipv6_sock_mc_join(struct sock *sk, int ifindex,
ASSERT_RTNL();
- if (!ipv6_addr_is_multicast(addr))
+ if (!ipv6_addr_is_multicast(addr) && !ipv6_addr_any(addr))
return -EINVAL;
for_each_pmc_socklock(np, sk, mc_lst) {@@ -254,7 +254,7 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
ASSERT_RTNL();
- if (!ipv6_addr_is_multicast(addr))
+ if (!ipv6_addr_is_multicast(addr) && !ipv6_addr_any(addr))
return -EINVAL;
for (lnk = &np->ipv6_mc_list;
@@ -374,7 +374,7 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
source = &((struct sockaddr_in6 *)&pgsr->gsr_source)->sin6_addr;
group = &((struct sockaddr_in6 *)&pgsr->gsr_group)->sin6_addr;
- if (!ipv6_addr_is_multicast(group))
+ if (!ipv6_addr_is_multicast(group) && !ipv6_addr_any(group))
return -EINVAL;
idev = ip6_mc_find_dev_rtnl(net, group, pgsr->gsr_interface);
@@ -497,7 +497,7 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf,
group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr;
- if (!ipv6_addr_is_multicast(group))
+ if (!ipv6_addr_is_multicast(group) && !ipv6_addr_any(group))
return -EINVAL;
if (gsf->gf_fmode != MCAST_INCLUDE &&
gsf->gf_fmode != MCAST_EXCLUDE)
@@ -585,7 +585,7 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr;
- if (!ipv6_addr_is_multicast(group))
+ if (!ipv6_addr_is_multicast(group) && !ipv6_addr_any(group))
return -EINVAL;
/* changes to the ipv6_mc_list require the socket lock and
@@ -634,6 +634,10 @@ bool inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr,
for_each_pmc_rcu(np, mc) {
if (ipv6_addr_equal(&mc->addr, mc_addr))
break;
+ if (ipv6_addr_any(&mc->addr)) {
+ rcu_read_unlock();
+ return rv;
+ }
}
if (!mc) {
rcu_read_unlock();@@ -1019,8 +1023,12 @@ bool ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group,
for_each_mc_rcu(idev, mc) {
if (ipv6_addr_equal(&mc->mca_addr, group))
break;
+ if (ipv6_addr_any(&mc->mca_addr)) {
+ rv = true;
+ break;
+ }
}
- if (mc) {
+ if (mc && !ipv6_addr_any(&mc->mca_addr)) {
if (src_addr && !ipv6_addr_any(src_addr)) {
struct ip6_sf_list *psf;
--
2.32.0