@@ -481,6 +481,15 @@ enum batadv_nl_attrs {
*/
BATADV_ATTR_MULTICAST_FANOUT,
+ /**
+ * @BATADV_ATTR_MULTICAST_MLD_RTR_ONLY_ENABLED: defines how IGMP/MLD
+ * reports are forwarded in the mesh. If set to non-zero then IGMP/MLD
+ * reports are only forwarded to detected multicast routers. If set to
+ * zero then they are flooded instead.
+ * Warning: The former is experimental and potentially unsafe!
+ */
+ BATADV_ATTR_MULTICAST_MLD_RTR_ONLY_ENABLED,
+
/* add attributes above here, update the policy in netlink.c */
/**
@@ -1008,8 +1008,14 @@ static int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv,
if (!pskb_may_pull(skb, sizeof(struct ethhdr) + sizeof(*iphdr)))
return -ENOMEM;
- if (batadv_mcast_is_report_ipv4(skb))
+ if (batadv_mcast_is_report_ipv4(skb)) {
+ if (atomic_read(&bat_priv->multicast_mld_rtr_only)) {
+ *is_routable = IGMP_HOST_MEMBERSHIP_REPORT;
+ return 0;
+ }
+
return -EINVAL;
+ }
iphdr = ip_hdr(skb);
@@ -1072,8 +1078,14 @@ static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv,
if (!pskb_may_pull(skb, sizeof(struct ethhdr) + sizeof(*ip6hdr)))
return -ENOMEM;
- if (batadv_mcast_is_report_ipv6(skb))
+ if (batadv_mcast_is_report_ipv6(skb)) {
+ if (atomic_read(&bat_priv->multicast_mld_rtr_only)) {
+ *is_routable = ICMPV6_MGM_REPORT;
+ return 0;
+ }
+
return -EINVAL;
+ }
ip6hdr = ipv6_hdr(skb);
@@ -1160,17 +1172,19 @@ static int batadv_mcast_forw_want_all_ip_count(struct batadv_priv *bat_priv,
* @protocol: the ethernet protocol type to count multicast routers for
*
* Return: the number of nodes which want all routable IPv4 multicast traffic
- * if the protocol is ETH_P_IP or the number of nodes which want all routable
- * IPv6 traffic if the protocol is ETH_P_IPV6. Otherwise returns 0.
+ * if the protocol is ETH_P_IP or IGMP_HOST_MEMBERSHIP_REPORT. Or the number of
+ * nodes which want all routable IPv6 traffic if the protocol is ETH_P_IPV6 or
+ * ICMPV6_MGM_REPORT. Otherwise returns 0.
*/
-
static int batadv_mcast_forw_rtr_count(struct batadv_priv *bat_priv,
int protocol)
{
switch (protocol) {
case ETH_P_IP:
+ case IGMP_HOST_MEMBERSHIP_REPORT:
return atomic_read(&bat_priv->mcast.num_want_all_rtr4);
case ETH_P_IPV6:
+ case ICMPV6_MGM_REPORT:
return atomic_read(&bat_priv->mcast.num_want_all_rtr6);
default:
return 0;
@@ -1234,10 +1248,11 @@ enum batadv_forw_mode
batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
unsigned short vid, int *is_routable)
{
- int ret, tt_count, ip_count, unsnoop_count, total_count;
+ atomic_t *unsnoop_cnt_atom = &bat_priv->mcast.num_want_all_unsnoopables;
+ int ret, ip_count, rtr_count, total_count;
+ int tt_count = 0, unsnoop_count = 0;
bool is_unsnoopable = false;
struct ethhdr *ethhdr;
- int rtr_count = 0;
ret = batadv_mcast_forw_mode_check(bat_priv, skb, &is_unsnoopable,
is_routable);
@@ -1248,11 +1263,17 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
ethhdr = eth_hdr(skb);
- tt_count = batadv_tt_global_hash_count(bat_priv, ethhdr->h_dest,
- BATADV_NO_FLAGS);
- ip_count = batadv_mcast_forw_want_all_ip_count(bat_priv, ethhdr);
- unsnoop_count = !is_unsnoopable ? 0 :
- atomic_read(&bat_priv->mcast.num_want_all_unsnoopables);
+ if (*is_routable != IGMP_HOST_MEMBERSHIP_REPORT &&
+ *is_routable != ICMPV6_MGM_REPORT) {
+ tt_count = batadv_tt_global_hash_count(bat_priv, ethhdr->h_dest,
+ BATADV_NO_FLAGS);
+
+ if (is_unsnoopable)
+ unsnoop_count = atomic_read(unsnoop_cnt_atom);
+ }
+
+ ip_count = batadv_mcast_forw_want_all_ip_count(bat_priv,
+ ethhdr);
rtr_count = batadv_mcast_forw_rtr_count(bat_priv, *is_routable);
total_count = tt_count + ip_count + unsnoop_count + rtr_count;
@@ -1540,8 +1561,10 @@ batadv_mcast_forw_want_rtr(struct batadv_priv *bat_priv,
{
switch (ntohs(eth_hdr(skb)->h_proto)) {
case ETH_P_IP:
+ case IGMP_HOST_MEMBERSHIP_REPORT:
return batadv_mcast_forw_want_all_rtr4(bat_priv, skb, vid);
case ETH_P_IPV6:
+ case ICMPV6_MGM_REPORT:
return batadv_mcast_forw_want_all_rtr6(bat_priv, skb, vid);
default:
/* we shouldn't be here... */
@@ -1571,12 +1594,17 @@ int batadv_mcast_forw_send(struct batadv_priv *bat_priv, struct sk_buff *skb,
{
int ret;
+ if (is_routable == IGMP_HOST_MEMBERSHIP_REPORT ||
+ is_routable == ICMPV6_MGM_REPORT)
+ goto skip_mc_listeners;
+
ret = batadv_mcast_forw_tt(bat_priv, skb, vid);
if (ret != NET_XMIT_SUCCESS) {
kfree_skb(skb);
return ret;
}
+skip_mc_listeners:
ret = batadv_mcast_forw_want_all(bat_priv, skb, vid);
if (ret != NET_XMIT_SUCCESS) {
kfree_skb(skb);
@@ -145,6 +145,7 @@ static const struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = {
[BATADV_ATTR_LOG_LEVEL] = { .type = NLA_U32 },
[BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED] = { .type = NLA_U8 },
[BATADV_ATTR_MULTICAST_FANOUT] = { .type = NLA_U32 },
+ [BATADV_ATTR_MULTICAST_MLD_RTR_ONLY_ENABLED] = { .type = NLA_U8 },
[BATADV_ATTR_NETWORK_CODING_ENABLED] = { .type = NLA_U8 },
[BATADV_ATTR_ORIG_INTERVAL] = { .type = NLA_U32 },
[BATADV_ATTR_ELP_INTERVAL] = { .type = NLA_U32 },
@@ -345,6 +346,10 @@ static int batadv_netlink_mesh_fill(struct sk_buff *msg,
if (nla_put_u32(msg, BATADV_ATTR_MULTICAST_FANOUT,
atomic_read(&bat_priv->multicast_fanout)))
goto nla_put_failure;
+
+ if (nla_put_u8(msg, BATADV_ATTR_MULTICAST_MLD_RTR_ONLY_ENABLED,
+ atomic_read(&bat_priv->multicast_mld_rtr_only)))
+ goto nla_put_failure;
#endif /* CONFIG_BATMAN_ADV_MCAST */
#ifdef CONFIG_BATMAN_ADV_NC
@@ -588,6 +593,18 @@ static int batadv_netlink_set_mesh(struct sk_buff *skb, struct genl_info *info)
atomic_set(&bat_priv->multicast_fanout, nla_get_u32(attr));
}
+
+ if (info->attrs[BATADV_ATTR_MULTICAST_MLD_RTR_ONLY_ENABLED]) {
+ u8 mld_rtr_only;
+
+ attr = info->attrs[BATADV_ATTR_MULTICAST_MLD_RTR_ONLY_ENABLED];
+ mld_rtr_only = !!nla_get_u8(attr);
+ if (mld_rtr_only)
+ batadv_info(bat_priv->soft_iface,
+ "Warning: MLD-RTR-ONLY is experimental and has known, broken scenarios\n");
+
+ atomic_set(&bat_priv->multicast_mld_rtr_only, mld_rtr_only);
+ }
#endif /* CONFIG_BATMAN_ADV_MCAST */
#ifdef CONFIG_BATMAN_ADV_NC
@@ -762,6 +762,7 @@ static int batadv_softif_init_late(struct net_device *dev)
#ifdef CONFIG_BATMAN_ADV_MCAST
atomic_set(&bat_priv->multicast_mode, 1);
atomic_set(&bat_priv->multicast_fanout, 16);
+ atomic_set(&bat_priv->multicast_mld_rtr_only, 0);
atomic_set(&bat_priv->mcast.num_want_all_unsnoopables, 0);
atomic_set(&bat_priv->mcast.num_want_all_ipv4, 0);
atomic_set(&bat_priv->mcast.num_want_all_ipv6, 0);
@@ -1683,6 +1683,13 @@ struct batadv_priv {
* multicast-to-unicast conversion
*/
atomic_t multicast_fanout;
+
+ /**
+ * @multicast_mld_rtr_only: bool indicating whether to send IGMP/MLD
+ * reports only to multicast routers or to flood them otherwise.
+ * Warning: The former is experimental and potentially unsafe!
+ */
+ atomic_t multicast_mld_rtr_only;
#endif
/** @orig_interval: OGM broadcast interval in milliseconds */