diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst
index a5c250044500..12f82da52684 100644
--- a/Documentation/networking/ip-sysctl.rst
+++ b/Documentation/networking/ip-sysctl.rst
@@ -1357,6 +1357,14 @@ mc_forwarding - BOOLEAN
conf/all/mc_forwarding must also be set to TRUE to enable multicast
routing for the interface
+mc_snooping - BOOLEAN
+ Enable multicast snooping on the interface. This allows any given
+ multicast group to be received without explicitly being joined.
+ The kernel needs to be compiled with CONFIG_MROUTE and/or
+ CONFIG_IPV6_MROUTE.
+ conf/all/mc_snooping must also be set to TRUE to enable multicast
+ snooping for the interface.
+
medium_id - INTEGER
Integer value used to differentiate the devices by the medium they
are attached to. Two devices can have different id values when
diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h
index 53aa0343bf69..071edf7d4f9c 100644
--- a/include/linux/inetdevice.h
+++ b/include/linux/inetdevice.h
@@ -95,6 +95,7 @@ static inline void ipv4_devconf_setall(struct in_device *in_dev)
#define IN_DEV_FORWARD(in_dev) IN_DEV_CONF_GET((in_dev), FORWARDING)
#define IN_DEV_MFORWARD(in_dev) IN_DEV_ANDCONF((in_dev), MC_FORWARDING)
+#define IN_DEV_MSNOOPING(in_dev) IN_DEV_ANDCONF((in_dev), MC_SNOOPING)
#define IN_DEV_BFORWARD(in_dev) IN_DEV_ANDCONF((in_dev), BC_FORWARDING)
#define IN_DEV_RPFILTER(in_dev) IN_DEV_MAXCONF((in_dev), RP_FILTER)
#define IN_DEV_SRC_VMARK(in_dev) IN_DEV_ORCONF((in_dev), SRC_VMARK)
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 70b2ad3b9884..d88c34b1b3ae 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -52,6 +52,7 @@ struct ipv6_devconf {
#endif
#ifdef CONFIG_IPV6_MROUTE
__s32 mc_forwarding;
+ __s32 mc_snooping;
#endif
__s32 disable_ipv6;
__s32 drop_unicast_in_l2_multicast;diff --git a/include/uapi/linux/ip.h b/include/uapi/linux/ip.h
index e42d13b55cf3..07956b4613d0 100644
--- a/include/uapi/linux/ip.h
+++ b/include/uapi/linux/ip.h
@@ -169,6 +169,7 @@ enum
IPV4_DEVCONF_DROP_UNICAST_IN_L2_MULTICAST,
IPV4_DEVCONF_DROP_GRATUITOUS_ARP,
IPV4_DEVCONF_BC_FORWARDING,
+ IPV4_DEVCONF_MC_SNOOPING,
__IPV4_DEVCONF_MAX
};
diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
index 70603775fe91..aa9389e1c1fd 100644
--- a/include/uapi/linux/ipv6.h
+++ b/include/uapi/linux/ipv6.h
@@ -190,6 +190,7 @@ enum {
DEVCONF_NDISC_TCLASS,
DEVCONF_RPL_SEG_ENABLED,
DEVCONF_RA_DEFRTR_METRIC,
+ DEVCONF_MC_SNOOPING,
DEVCONF_MAX
};
diff --git a/include/uapi/linux/netconf.h b/include/uapi/linux/netconf.h
index fac4edd55379..5259742a700b 100644
--- a/include/uapi/linux/netconf.h
+++ b/include/uapi/linux/netconf.h
@@ -19,6 +19,7 @@ enum {
NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
NETCONFA_INPUT,
NETCONFA_BC_FORWARDING,
+ NETCONFA_MC_SNOOPING,
__NETCONFA_MAX
};
#define NETCONFA_MAX (__NETCONFA_MAX - 1)diff --git a/include/uapi/linux/sysctl.h b/include/uapi/linux/sysctl.h
index 1e05d3caa712..1b7be9dc78de 100644
--- a/include/uapi/linux/sysctl.h
+++ b/include/uapi/linux/sysctl.h
@@ -482,6 +482,7 @@ enum
NET_IPV4_CONF_PROMOTE_SECONDARIES=20,
NET_IPV4_CONF_ARP_ACCEPT=21,
NET_IPV4_CONF_ARP_NOTIFY=22,
+ NET_IPV4_CONF_MC_SNOOPING=23,
};
/* /proc/sys/net/ipv4/netfilter */
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 50deeff48c8b..3e4ac6aead9d 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -2014,6 +2014,8 @@ static int inet_netconf_msgsize_devconf(int type)
size += nla_total_size(4);
if (all || type == NETCONFA_MC_FORWARDING)
size += nla_total_size(4);
+ if (all || type == NETCONFA_MC_SNOOPING)
+ size += nla_total_size(4);
if (all || type == NETCONFA_BC_FORWARDING)
size += nla_total_size(4);
if (all || type == NETCONFA_PROXY_NEIGH)
@@ -2062,6 +2064,10 @@ static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
nla_put_s32(skb, NETCONFA_MC_FORWARDING,
IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
goto nla_put_failure;
+ if ((all || type == NETCONFA_MC_SNOOPING) &&
+ nla_put_s32(skb, NETCONFA_MC_SNOOPING,
+ IPV4_DEVCONF(*devconf, NETCONFA_MC_SNOOPING)) < 0)
+ goto nla_put_failure;
if ((all || type == NETCONFA_BC_FORWARDING) &&
nla_put_s32(skb, NETCONFA_BC_FORWARDING,
IPV4_DEVCONF(*devconf, BC_FORWARDING)) < 0)
@@ -2506,6 +2512,7 @@ static struct devinet_sysctl_table {
DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
devinet_sysctl_forward),
DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
+ DEVINET_SYSCTL_RW_ENTRY(MC_SNOOPING, "mc_snooping"),
DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"),
DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 7b272bbed2b4..cd5a837dfb0c 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -2692,6 +2692,11 @@ int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u
struct ip_sf_list *psf;
int rv = 0;
+#ifdef CONFIG_IP_MROUTE
+ if (IN_DEV_MSNOOPING(in_dev))
+ return 1;
+#endif /* CONFIG_IP_MROUTE */
+
mc_hash = rcu_dereference(in_dev->mc_hash);
if (mc_hash) {
u32 hash = hash_32((__force u32)mc_addr, MC_HASH_SZ_LOG);diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 048570900fdf..b92ac4e8f37d 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -502,6 +502,8 @@ static int inet6_netconf_msgsize_devconf(int type)
#ifdef CONFIG_IPV6_MROUTE
if (all || type == NETCONFA_MC_FORWARDING)
size += nla_total_size(4);
+ if (all || type == NETCONFA_MC_SNOOPING)
+ size += nla_total_size(4);
#endif
if (all || type == NETCONFA_PROXY_NEIGH)
size += nla_total_size(4);
@@ -546,6 +548,10 @@ static int inet6_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
nla_put_s32(skb, NETCONFA_MC_FORWARDING,
devconf->mc_forwarding) < 0)
goto nla_put_failure;
+ if ((all || type == NETCONFA_MC_SNOOPING) &&
+ nla_put_s32(skb, NETCONFA_MC_SNOOPING,
+ devconf->mc_snooping) < 0)
+ goto nla_put_failure;
#endif
if ((all || type == NETCONFA_PROXY_NEIGH) &&
nla_put_s32(skb, NETCONFA_PROXY_NEIGH, devconf->proxy_ndp) < 0)
@@ -5503,6 +5509,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
#endif
#ifdef CONFIG_IPV6_MROUTE
array[DEVCONF_MC_FORWARDING] = cnf->mc_forwarding;
+ array[DEVCONF_MC_SNOOPING] = cnf->mc_snooping;
#endif
array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6;
array[DEVCONF_ACCEPT_DAD] = cnf->accept_dad;
@@ -6786,6 +6793,13 @@ static const struct ctl_table addrconf_sysctl[] = {
.mode = 0444,
.proc_handler = proc_dointvec,
},
+ {
+ .procname = "mc_snooping",
+ .data = &ipv6_devconf.mc_snooping,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
#endif
{
.procname = "disable_ipv6",diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 54ec163fbafa..25046ee8276f 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1013,6 +1013,11 @@ bool ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group,
struct ifmcaddr6 *mc;
bool rv = false;
+#ifdef CONFIG_IPV6_MROUTE
+ if (dev_net(dev)->ipv6.devconf_all->mc_snooping)
+ return true;
+#endif
+
rcu_read_lock();
idev = __in6_dev_get(dev);
if (idev) {--
2.32.0