gluon/patches/packages/routing/0002-batman-adv-mesh-wide-multi-target-multicast-to-unicast-support.patch
Linus Lüssing 1c5d7f91ca batman-adv: mesh-wide, multi-target multicast-to-unicast support
We already have multicast-to-unicast support between client and node.
And batman-adv has multicast-to-unicast conversion capabilities if there
is only one receiver. Now this patchset adds node-to-node
multicast-to-unicast support to multiple targets as well.

This should save airtime for ICMPv6 Router Solicitations, for instance.
Here we have multiple targets for the same IPv6 multicast destination
address. However, the number of destination nodes is still rather small
in this case, i.e. all gateways, making multiple unicast transmissions
less costly than flooding a packet through the whole mesh.

Also, this allows playing with multicast streaming to a limited set of
destination nodes (default setting: 16).

Also, nearly all nodes should be running a batman-adv version which
was compiled with CONFIG_BATMAN_ADV_MCAST=y (e.g. Gluon v2018.1 or
later) for this feature to take effect.

Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
2019-02-16 23:37:19 +01:00

972 lines
36 KiB
Diff

From: Linus Lüssing <linus.luessing@c0d3.blue>
Date: Thu, 29 Mar 2018 20:44:01 +0200
Subject: batman-adv: mesh-wide, multi-target multicast-to-unicast support
We already have multicast-to-unicast support between client and node.
And batman-adv has multicast-to-unicast conversion capabilities if there
is only one receiver. Now this patchset adds node-to-node
multicast-to-unicast support to multiple targets as well.
This should save airtime for ICMPv6 Router Solicitations, for instance.
Here we have multiple targets for the same IPv6 multicast destination
address. However, the number of destination nodes is still rather small
in this case, i.e. all gateways, making multiple unicast transmissions
less costly than flooding a packet through the whole mesh.
Also, this allows playing with multicast streaming to a limited set of
destination nodes (default setting: 16).
Also, nearly all nodes should be running a batman-adv version which
was compiled with CONFIG_BATMAN_ADV_MCAST=y (e.g. Gluon v2018.1 or
later) for this feature to take effect.
Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
diff --git a/batman-adv/files/etc/config/batman-adv b/batman-adv/files/etc/config/batman-adv
index 6e6017a31468c8e595ec13b09e27529f28f3eaec..8a4e881537ca48dc8e611bab9e22918a8dd02fe4 100644
--- a/batman-adv/files/etc/config/batman-adv
+++ b/batman-adv/files/etc/config/batman-adv
@@ -12,9 +12,11 @@ config 'mesh' 'bat0'
option 'bridge_loop_avoidance'
option 'distributed_arp_table'
option 'multicast_mode'
+ option 'multicast_fanout'
option 'network_coding'
option 'hop_penalty'
option 'isolation_mark'
+ option 'noflood_mark'
# yet another batX instance
# config 'mesh' 'bat5'
diff --git a/batman-adv/files/lib/batman-adv/config.sh b/batman-adv/files/lib/batman-adv/config.sh
index 3a746a3c0e280b9a01ae67ea27bde65619b77ebc..d45c4009d24d0d2c416d23134aed755bc36b9413 100644
--- a/batman-adv/files/lib/batman-adv/config.sh
+++ b/batman-adv/files/lib/batman-adv/config.sh
@@ -12,8 +12,8 @@ bat_config()
{
local mesh="$1"
local aggregated_ogms ap_isolation bonding bridge_loop_avoidance distributed_arp_table fragmentation
- local gw_bandwidth gw_mode gw_sel_class isolation_mark hop_penalty multicast_mode network_coding log_level
- local orig_interval
+ local gw_bandwidth gw_mode gw_sel_class isolation_mark hop_penalty multicast_fanout multicast_mode
+ local network_coding noflood_mark log_level orig_interval
config_get aggregated_ogms "$mesh" aggregated_ogms
config_get ap_isolation "$mesh" ap_isolation
@@ -26,8 +26,10 @@ bat_config()
config_get gw_sel_class "$mesh" gw_sel_class
config_get hop_penalty "$mesh" hop_penalty
config_get isolation_mark "$mesh" isolation_mark
+ config_get multicast_fanout "$mesh" multicast_fanout
config_get multicast_mode "$mesh" multicast_mode
config_get network_coding "$mesh" network_coding
+ config_get noflood_mark "$mesh" noflood_mark
config_get log_level "$mesh" log_level
config_get orig_interval "$mesh" orig_interval
@@ -44,8 +46,10 @@ bat_config()
[ -n "$gw_sel_class" ] && echo $gw_sel_class > /sys/class/net/$mesh/mesh/gw_sel_class
[ -n "$hop_penalty" ] && echo $hop_penalty > /sys/class/net/$mesh/mesh/hop_penalty
[ -n "$isolation_mark" ] && echo $isolation_mark > /sys/class/net/$mesh/mesh/isolation_mark
+ [ -n "$multicast_fanout" ] && echo $multicast_fanout > /sys/class/net/$mesh/mesh/multicast_fanout 2>&-
[ -n "$multicast_mode" ] && echo $multicast_mode > /sys/class/net/$mesh/mesh/multicast_mode 2>&-
[ -n "$network_coding" ] && echo $network_coding > /sys/class/net/$mesh/mesh/network_coding 2>&-
+ [ -n "$noflood_mark" ] && echo $noflood_mark > /sys/class/net/$mesh/mesh/noflood_mark
[ -n "$log_level" ] && echo $log_level > /sys/class/net/$mesh/mesh/log_level 2>&-
[ -n "$orig_interval" ] && echo $orig_interval > /sys/class/net/$mesh/mesh/orig_interval
}
diff --git a/batman-adv/patches/0024-batman-adv-Avoid-old-nodes-disabling-multicast-optim.patch b/batman-adv/patches/0024-batman-adv-Avoid-old-nodes-disabling-multicast-optim.patch
new file mode 100644
index 0000000000000000000000000000000000000000..f08b4d665c222a0362cfe6c232bd38d03280e301
--- /dev/null
+++ b/batman-adv/patches/0024-batman-adv-Avoid-old-nodes-disabling-multicast-optim.patch
@@ -0,0 +1,117 @@
+From a250a536f3672d354bc3c47adf7d721137673314 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Linus=20L=C3=BCssing?= <linus.luessing@c0d3.blue>
+Date: Sat, 24 Mar 2018 23:39:11 +0100
+Subject: [PATCH] batman-adv: Avoid old nodes disabling multicast optimizations
+ completely
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Instead of disabling multicast optimizations mesh-wide once a node with
+no multicast optimizations capabilities joins the mesh, do the
+following:
+
+Just insert such nodes into the WANT_ALL_IPV4/IPV6 lists. This is
+sufficient to avoid multicast packet loss to such unsupportive nodes.
+
+Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
+---
+ net/batman-adv/multicast.c | 29 ++++++-----------------------
+ net/batman-adv/soft-interface.c | 1 -
+ net/batman-adv/types.h | 3 ---
+ 3 files changed, 6 insertions(+), 27 deletions(-)
+
+diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c
+index a35f597e..86725d79 100644
+--- a/net/batman-adv/multicast.c
++++ b/net/batman-adv/multicast.c
+@@ -815,9 +815,6 @@ static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv,
+ if (!atomic_read(&bat_priv->multicast_mode))
+ return -EINVAL;
+
+- if (atomic_read(&bat_priv->mcast.num_disabled))
+- return -EINVAL;
+-
+ switch (ntohs(ethhdr->h_proto)) {
+ case ETH_P_IP:
+ return batadv_mcast_forw_mode_check_ipv4(bat_priv, skb,
+@@ -1183,33 +1180,23 @@ static void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv,
+ {
+ bool orig_mcast_enabled = !(flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
+ u8 mcast_flags = BATADV_NO_FLAGS;
+- bool orig_initialized;
+
+ if (orig_mcast_enabled && tvlv_value &&
+ tvlv_value_len >= sizeof(mcast_flags))
+ mcast_flags = *(u8 *)tvlv_value;
+
++ if (!orig_mcast_enabled) {
++ mcast_flags |= BATADV_MCAST_WANT_ALL_IPV4;
++ mcast_flags |= BATADV_MCAST_WANT_ALL_IPV6;
++ }
++
+ spin_lock_bh(&orig->mcast_handler_lock);
+- orig_initialized = test_bit(BATADV_ORIG_CAPA_HAS_MCAST,
+- &orig->capa_initialized);
+
+- /* If mcast support is turned on decrease the disabled mcast node
+- * counter only if we had increased it for this node before. If this
+- * is a completely new orig_node no need to decrease the counter.
+- */
+ if (orig_mcast_enabled &&
+ !test_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities)) {
+- if (orig_initialized)
+- atomic_dec(&bat_priv->mcast.num_disabled);
+ set_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities);
+- /* If mcast support is being switched off or if this is an initial
+- * OGM without mcast support then increase the disabled mcast
+- * node counter.
+- */
+ } else if (!orig_mcast_enabled &&
+- (test_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities) ||
+- !orig_initialized)) {
+- atomic_inc(&bat_priv->mcast.num_disabled);
++ test_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities)) {
+ clear_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities);
+ }
+
+@@ -1595,10 +1582,6 @@ void batadv_mcast_purge_orig(struct batadv_orig_node *orig)
+
+ spin_lock_bh(&orig->mcast_handler_lock);
+
+- if (!test_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities) &&
+- test_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capa_initialized))
+- atomic_dec(&bat_priv->mcast.num_disabled);
+-
+ batadv_mcast_want_unsnoop_update(bat_priv, orig, BATADV_NO_FLAGS);
+ batadv_mcast_want_ipv4_update(bat_priv, orig, BATADV_NO_FLAGS);
+ batadv_mcast_want_ipv6_update(bat_priv, orig, BATADV_NO_FLAGS);
+diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
+index d3f540ba..8fd8d806 100644
+--- a/net/batman-adv/soft-interface.c
++++ b/net/batman-adv/soft-interface.c
+@@ -809,7 +809,6 @@ static int batadv_softif_init_late(struct net_device *dev)
+ bat_priv->mcast.querier_ipv6.shadowing = false;
+ bat_priv->mcast.flags = BATADV_NO_FLAGS;
+ atomic_set(&bat_priv->multicast_mode, 1);
+- atomic_set(&bat_priv->mcast.num_disabled, 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);
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 476b052a..0174f79e 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1212,9 +1212,6 @@ struct batadv_priv_mcast {
+ /** @bridged: whether the soft interface has a bridge on top */
+ bool bridged;
+
+- /** @num_disabled: number of nodes that have no mcast tvlv */
+- atomic_t num_disabled;
+-
+ /**
+ * @num_want_all_unsnoopables: number of nodes wanting unsnoopable IP
+ * traffic
+--
+2.11.0
+
diff --git a/batman-adv/patches/0025-batman-adv-Add-multicast-to-unicast-support-for-mult.patch b/batman-adv/patches/0025-batman-adv-Add-multicast-to-unicast-support-for-mult.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c0df317107061e8781fa1a1a90eb3b1602aa8f65
--- /dev/null
+++ b/batman-adv/patches/0025-batman-adv-Add-multicast-to-unicast-support-for-mult.patch
@@ -0,0 +1,497 @@
+From 8d0866c33c2f0b49bd6faf2cb7b9d935fc969918 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Linus=20L=C3=BCssing?= <linus.luessing@c0d3.blue>
+Date: Thu, 1 Mar 2018 02:32:47 +0100
+Subject: [PATCH] batman-adv: Add multicast-to-unicast support for multiple
+ targets
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+With this patch multicast packets with a limited number of destinations
+(current default: 16) will be split and transmitted by the originator as
+individual unicast transmissions.
+
+Wifi broadcasts with their low bitrate are still a costly undertaking.
+In a mesh network this cost multiplies with the overall size of the mesh
+network. Therefore using multiple unicast transmissions instead of
+broadcast flooding is almost always less burdensome for the mesh
+network.
+
+The maximum amount of unicast packets can be configured via the newly
+introduced multicast_fanout parameter. If this limit is exceeded
+distribution will fall back to classic broadcast flooding.
+
+The multicast-to-unicast conversion is performed on the initial
+multicast sender node and counts on a final destination node, mesh-wide
+basis (and not next hop, neighbor node basis).
+
+Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
+---
+ Documentation/ABI/testing/sysfs-class-net-mesh | 9 +
+ net/batman-adv/multicast.c | 242 ++++++++++++++++++++++++-
+ net/batman-adv/multicast.h | 18 ++
+ net/batman-adv/soft-interface.c | 8 +-
+ net/batman-adv/sysfs.c | 3 +
+ net/batman-adv/translation-table.c | 6 +-
+ net/batman-adv/translation-table.h | 4 +
+ net/batman-adv/types.h | 6 +
+ 8 files changed, 290 insertions(+), 6 deletions(-)
+
+diff --git a/Documentation/ABI/testing/sysfs-class-net-mesh b/Documentation/ABI/testing/sysfs-class-net-mesh
+index c2b956d4..18734a36 100644
+--- a/Documentation/ABI/testing/sysfs-class-net-mesh
++++ b/Documentation/ABI/testing/sysfs-class-net-mesh
+@@ -76,6 +76,15 @@ Description:
+ is used to classify clients as "isolated" by the
+ Extended Isolation feature.
+
++What: /sys/class/net/<mesh_iface>/mesh/multicast_fanout
++Date: Feb 2018
++Contact: Linus Lüssing <linus.luessing@c0d3.blue>
++Description:
++ Defines the maximum number of packet copies that may
++ be generated for a multicast-to-unicast conversion.
++ Once this limit is exceeded distribution will fall
++ back to broadcast.
++
+ What: /sys/class/net/<mesh_iface>/mesh/multicast_mode
+ Date: Feb 2014
+ Contact: Linus Lüssing <linus.luessing@web.de>
+diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c
+index 86725d79..4e18ca6c 100644
+--- a/net/batman-adv/multicast.c
++++ b/net/batman-adv/multicast.c
+@@ -66,6 +66,7 @@
+ #include "hash.h"
+ #include "log.h"
+ #include "netlink.h"
++#include "send.h"
+ #include "soft-interface.h"
+ #include "translation-table.h"
+ #include "tvlv.h"
+@@ -992,6 +993,7 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
+ int ret, tt_count, ip_count, unsnoop_count, total_count;
+ bool is_unsnoopable = false;
+ struct ethhdr *ethhdr;
++ unsigned int mcast_fanout;
+
+ ret = batadv_mcast_forw_mode_check(bat_priv, skb, &is_unsnoopable);
+ if (ret == -ENOMEM)
+@@ -1025,8 +1027,246 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
+ case 0:
+ return BATADV_FORW_NONE;
+ default:
+- return BATADV_FORW_ALL;
++ mcast_fanout = atomic_read(&bat_priv->multicast_fanout);
++
++ if (!unsnoop_count && total_count <= mcast_fanout)
++ return BATADV_FORW_SOME;
+ }
++
++ return BATADV_FORW_ALL;
++}
++
++/**
++ * batadv_mcast_forw_tt_send() - send a packet to multicast listeners
++ * @bat_priv: the bat priv with all the soft interface information
++ * @skb: the multicast packet to transmit
++ * @vid: the vlan identifier
++ * @limit: number of remaining, maximum transmissions
++ *
++ * Sends copies of a frame with multicast destination to any multicast
++ * listener registered in the translation table. A transmission is performed
++ * via a batman-adv unicast packet for each such destination node.
++ *
++ * Return: NET_XMIT_DROP if limit was reached or on memory allocation failure,
++ * NET_XMIT_SUCCESS otherwise.
++ */
++static int
++batadv_mcast_forw_tt_send(struct batadv_priv *bat_priv, struct sk_buff *skb,
++ unsigned short vid, unsigned int *limit)
++{
++ unsigned int limit_tmp = *limit;
++ int ret = NET_XMIT_SUCCESS;
++ struct sk_buff *newskb;
++
++ struct batadv_tt_orig_list_entry *orig_entry;
++
++ struct batadv_tt_global_entry *tt_global;
++ const u8 *addr = eth_hdr(skb)->h_dest;
++
++ tt_global = batadv_tt_global_hash_find(bat_priv, addr, vid);
++ if (!tt_global)
++ goto out;
++
++ rcu_read_lock();
++ hlist_for_each_entry_rcu(orig_entry, &tt_global->orig_list, list) {
++ if (!limit_tmp) {
++ ret = NET_XMIT_DROP;
++ break;
++ }
++
++ newskb = skb_copy(skb, GFP_ATOMIC);
++ if (!newskb) {
++ ret = NET_XMIT_DROP;
++ break;
++ }
++
++ batadv_send_skb_unicast(bat_priv, newskb, BATADV_UNICAST, 0,
++ orig_entry->orig_node, vid);
++ limit_tmp--;
++ }
++ rcu_read_unlock();
++
++ batadv_tt_global_entry_put(tt_global);
++ *limit = limit_tmp;
++
++out:
++ return ret;
++}
++
++/**
++ * batadv_mcast_forw_want_all_ipv4_send() - send to nodes with want-all-ipv4
++ * @bat_priv: the bat priv with all the soft interface information
++ * @skb: the multicast packet to transmit
++ * @vid: the vlan identifier
++ * @limit: number of remaining, maximum transmissions
++ *
++ * Sends copies of a frame with multicast destination to any node with a
++ * BATADV_MCAST_WANT_ALL_IPV4 flag set. A transmission is performed via a
++ * batman-adv unicast packet for each such destination node.
++ *
++ * Return: NET_XMIT_DROP if limit was reached or on memory allocation failure,
++ * NET_XMIT_SUCCESS otherwise.
++ */
++static int
++batadv_mcast_forw_want_all_ipv4_send(struct batadv_priv *bat_priv,
++ struct sk_buff *skb, unsigned short vid,
++ unsigned int *limit)
++{
++ struct batadv_orig_node *orig_node;
++ unsigned int limit_tmp = *limit;
++ int ret = NET_XMIT_SUCCESS;
++ struct sk_buff *newskb;
++
++ rcu_read_lock();
++ hlist_for_each_entry_rcu(orig_node,
++ &bat_priv->mcast.want_all_ipv4_list,
++ mcast_want_all_ipv4_node) {
++ if (!limit_tmp) {
++ ret = NET_XMIT_DROP;
++ break;
++ }
++
++ newskb = skb_copy(skb, GFP_ATOMIC);
++ if (!newskb) {
++ ret = NET_XMIT_DROP;
++ break;
++ }
++
++ batadv_send_skb_unicast(bat_priv, newskb, BATADV_UNICAST, 0,
++ orig_node, vid);
++ limit_tmp--;
++ }
++ rcu_read_unlock();
++
++ *limit = limit_tmp;
++ return ret;
++}
++
++/**
++ * batadv_mcast_forw_want_all_ipv6_send() - send to nodes with want-all-ipv6
++ * @bat_priv: the bat priv with all the soft interface information
++ * @skb: The multicast packet to transmit
++ * @vid: the vlan identifier
++ * @limit: number of remaining, maximum transmissions
++ *
++ * Sends copies of a frame with multicast destination to any node with a
++ * BATADV_MCAST_WANT_ALL_IPV6 flag set. A transmission is performed via a
++ * batman-adv unicast packet for each such destination node.
++ *
++ * Return: NET_XMIT_DROP if limit was reached or on memory allocation failure,
++ * NET_XMIT_SUCCESS otherwise.
++ */
++static int
++batadv_mcast_forw_want_all_ipv6_send(struct batadv_priv *bat_priv,
++ struct sk_buff *skb, unsigned short vid,
++ unsigned int *limit)
++{
++ struct batadv_orig_node *orig_node;
++ unsigned int limit_tmp = *limit;
++ int ret = NET_XMIT_SUCCESS;
++ struct sk_buff *newskb;
++
++ rcu_read_lock();
++ hlist_for_each_entry_rcu(orig_node,
++ &bat_priv->mcast.want_all_ipv6_list,
++ mcast_want_all_ipv6_node) {
++ if (!limit_tmp) {
++ ret = NET_XMIT_DROP;
++ break;
++ }
++
++ newskb = skb_copy(skb, GFP_ATOMIC);
++ if (!newskb) {
++ ret = NET_XMIT_DROP;
++ break;
++ }
++
++ batadv_send_skb_unicast(bat_priv, newskb, BATADV_UNICAST, 0,
++ orig_node, vid);
++ limit_tmp--;
++ }
++ rcu_read_unlock();
++
++ *limit = limit_tmp;
++ return ret;
++}
++
++/**
++ * batadv_mcast_forw_want_all_send() - send packet to nodes in a want-all list
++ * @bat_priv: the bat priv with all the soft interface information
++ * @skb: the multicast packet to transmit
++ * @vid: the vlan identifier
++ * @limit: number of remaining, maximum transmissions
++ *
++ * Sends copies of a frame with multicast destination to any node with a
++ * BATADV_MCAST_WANT_ALL_IPV4 or BATADV_MCAST_WANT_ALL_IPV6 flag set. A
++ * transmission is performed via a batman-adv unicast packet for each such
++ * destination node.
++ *
++ * Return: NET_XMIT_DROP if limit was reached, on memory allocation failure
++ * or if the protocol family is neither IPv4 nor IPv6. NET_XMIT_SUCCESS
++ * otherwise.
++ */
++static int
++batadv_mcast_forw_want_all_send(struct batadv_priv *bat_priv,
++ struct sk_buff *skb, unsigned short vid,
++ unsigned int *limit)
++{
++ switch (ntohs(eth_hdr(skb)->h_proto)) {
++ case ETH_P_IP:
++ return batadv_mcast_forw_want_all_ipv4_send(bat_priv, skb, vid,
++ limit);
++ case ETH_P_IPV6:
++ return batadv_mcast_forw_want_all_ipv6_send(bat_priv, skb, vid,
++ limit);
++ default:
++ /* we shouldn't be here... */
++ return NET_XMIT_DROP;
++ }
++}
++
++/**
++ * batadv_mcast_forw_send() - send packet to any detected multicast recpient
++ * @bat_priv: the bat priv with all the soft interface information
++ * @skb: the multicast packet to transmit
++ * @vid: the vlan identifier
++ * @limit: number of remaining, maximum transmissions
++ *
++ * Sends copies of a frame with multicast destination to any node that signaled
++ * interest in it, that is either via the translation table or the according
++ * want-all flags. A transmission is performed via a batman-adv unicast packet
++ * for each such destination node.
++ *
++ * The given skb is consumed/freed.
++ *
++ * Return: NET_XMIT_DROP if limit was reached, on memory allocation failure
++ * or if the protocol family is neither IPv4 nor IPv6. NET_XMIT_SUCCESS
++ * otherwise.
++ */
++int batadv_mcast_forw_send(struct batadv_priv *bat_priv, struct sk_buff *skb,
++ unsigned short vid)
++{
++ /* The previous forw mode check will try to limit to the configured
++ * fanout. Here, we allow a little bit of flexibility in case some
++ * new listeners might have joined between these function calls.
++ */
++ unsigned int limit = 2 * atomic_read(&bat_priv->multicast_fanout);
++ int ret;
++
++ ret = batadv_mcast_forw_tt_send(bat_priv, skb, vid, &limit);
++ if (ret != NET_XMIT_SUCCESS) {
++ kfree_skb(skb);
++ return ret;
++ }
++
++ ret = batadv_mcast_forw_want_all_send(bat_priv, skb, vid, &limit);
++ if (ret != NET_XMIT_SUCCESS) {
++ kfree_skb(skb);
++ return ret;
++ }
++
++ consume_skb(skb);
++ return ret;
+ }
+
+ /**
+diff --git a/net/batman-adv/multicast.h b/net/batman-adv/multicast.h
+index 3b04ab13..825472c7 100644
+--- a/net/batman-adv/multicast.h
++++ b/net/batman-adv/multicast.h
+@@ -36,6 +36,13 @@ enum batadv_forw_mode {
+ BATADV_FORW_ALL,
+
+ /**
++ * @BATADV_FORW_SOME: forward the packet to some nodes (currently via
++ * a multicast-to-unicast conversion and the BATMAN unicast routing
++ * protocol)
++ */
++ BATADV_FORW_SOME,
++
++ /**
+ * @BATADV_FORW_SINGLE: forward the packet to a single node (currently
+ * via the BATMAN unicast routing protocol)
+ */
+@@ -51,6 +58,9 @@ enum batadv_forw_mode
+ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
+ struct batadv_orig_node **mcast_single_orig);
+
++int batadv_mcast_forw_send(struct batadv_priv *bat_priv, struct sk_buff *skb,
++ unsigned short vid);
++
+ void batadv_mcast_init(struct batadv_priv *bat_priv);
+
+ int batadv_mcast_flags_seq_print_text(struct seq_file *seq, void *offset);
+@@ -73,6 +83,14 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
+ return BATADV_FORW_ALL;
+ }
+
++static inline int
++batadv_mcast_forw_send(struct batadv_priv *bat_priv, struct sk_buff *skb,
++ unsigned short vid)
++{
++ kfree_skb(skb);
++ return NET_XMIT_DROP;
++}
++
+ static inline int batadv_mcast_init(struct batadv_priv *bat_priv)
+ {
+ return 0;
+diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
+index 8fd8d806..e43b38c3 100644
+--- a/net/batman-adv/soft-interface.c
++++ b/net/batman-adv/soft-interface.c
+@@ -209,7 +209,7 @@ static int batadv_interface_tx(struct sk_buff *skb,
+ unsigned short vid;
+ u32 seqno;
+ int gw_mode;
+- enum batadv_forw_mode forw_mode;
++ enum batadv_forw_mode forw_mode = BATADV_FORW_SINGLE;
+ struct batadv_orig_node *mcast_single_orig = NULL;
+ int network_offset = ETH_HLEN;
+
+@@ -308,7 +308,8 @@ static int batadv_interface_tx(struct sk_buff *skb,
+ if (forw_mode == BATADV_FORW_NONE)
+ goto dropped;
+
+- if (forw_mode == BATADV_FORW_SINGLE)
++ if (forw_mode == BATADV_FORW_SINGLE ||
++ forw_mode == BATADV_FORW_SOME)
+ do_bcast = false;
+ }
+ }
+@@ -368,6 +369,8 @@ static int batadv_interface_tx(struct sk_buff *skb,
+ ret = batadv_send_skb_unicast(bat_priv, skb,
+ BATADV_UNICAST, 0,
+ mcast_single_orig, vid);
++ } else if (forw_mode == BATADV_FORW_SOME) {
++ ret = batadv_mcast_forw_send(bat_priv, skb, vid);
+ } else {
+ if (batadv_dat_snoop_outgoing_arp_request(bat_priv,
+ skb))
+@@ -809,6 +812,7 @@ static int batadv_softif_init_late(struct net_device *dev)
+ bat_priv->mcast.querier_ipv6.shadowing = false;
+ bat_priv->mcast.flags = BATADV_NO_FLAGS;
+ atomic_set(&bat_priv->multicast_mode, 1);
++ atomic_set(&bat_priv->multicast_fanout, 16);
+ 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);
+diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c
+index 09427fc6..57b728b3 100644
+--- a/net/batman-adv/sysfs.c
++++ b/net/batman-adv/sysfs.c
+@@ -697,6 +697,8 @@ static BATADV_ATTR(gw_bandwidth, 0644, batadv_show_gw_bwidth,
+ batadv_store_gw_bwidth);
+ #ifdef CONFIG_BATMAN_ADV_MCAST
+ BATADV_ATTR_SIF_BOOL(multicast_mode, 0644, NULL);
++BATADV_ATTR_SIF_UINT(multicast_fanout, multicast_fanout, 0644, 1, INT_MAX,
++ NULL);
+ #endif
+ #ifdef CONFIG_BATMAN_ADV_DEBUG
+ BATADV_ATTR_SIF_UINT(log_level, log_level, 0644, 0, BATADV_DBG_ALL, NULL);
+@@ -718,6 +720,7 @@ static struct batadv_attribute *batadv_mesh_attrs[] = {
+ #endif
+ #ifdef CONFIG_BATMAN_ADV_MCAST
+ &batadv_attr_multicast_mode,
++ &batadv_attr_multicast_fanout,
+ #endif
+ &batadv_attr_fragmentation,
+ &batadv_attr_routing_algo,
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index d21624c4..a6fe45bf 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -61,6 +61,7 @@
+ #include "log.h"
+ #include "netlink.h"
+ #include "originator.h"
++#include "send.h"
+ #include "soft-interface.h"
+ #include "tvlv.h"
+
+@@ -205,7 +206,7 @@ batadv_tt_local_hash_find(struct batadv_priv *bat_priv, const u8 *addr,
+ * Return: a pointer to the corresponding tt_global_entry struct if the client
+ * is found, NULL otherwise.
+ */
+-static struct batadv_tt_global_entry *
++struct batadv_tt_global_entry *
+ batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const u8 *addr,
+ unsigned short vid)
+ {
+@@ -300,8 +301,7 @@ static void batadv_tt_global_entry_release(struct kref *ref)
+ * possibly release it
+ * @tt_global_entry: tt_global_entry to be free'd
+ */
+-static void
+-batadv_tt_global_entry_put(struct batadv_tt_global_entry *tt_global_entry)
++void batadv_tt_global_entry_put(struct batadv_tt_global_entry *tt_global_entry)
+ {
+ kref_put(&tt_global_entry->common.refcount,
+ batadv_tt_global_entry_release);
+diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h
+index 01b6c8ea..0981db6c 100644
+--- a/net/batman-adv/translation-table.h
++++ b/net/batman-adv/translation-table.h
+@@ -41,6 +41,10 @@ int batadv_tt_global_dump(struct sk_buff *msg, struct netlink_callback *cb);
+ void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig_node,
+ s32 match_vid, const char *message);
++struct batadv_tt_global_entry *
++batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const u8 *addr,
++ unsigned short vid);
++void batadv_tt_global_entry_put(struct batadv_tt_global_entry *tt_global_entry);
+ int batadv_tt_global_hash_count(struct batadv_priv *bat_priv,
+ const u8 *addr, unsigned short vid);
+ struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv,
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 0174f79e..47690038 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1552,6 +1552,12 @@ struct batadv_priv {
+ * node's sender/originating side
+ */
+ atomic_t multicast_mode;
++
++ /**
++ * @multicast_fanout: Maximum number of packet copies to generate for a
++ * multicast-to-unicast conversion
++ */
++ atomic_t multicast_fanout;
+ #endif
+
+ /** @orig_interval: OGM broadcast interval in milliseconds */
+--
+2.11.0
+
diff --git a/batman-adv/patches/0026-batman-adv-Introduce-no-noflood-mark.patch b/batman-adv/patches/0026-batman-adv-Introduce-no-noflood-mark.patch
new file mode 100644
index 0000000000000000000000000000000000000000..091f4347597e67a2aa1cf64d937f3cd2f45e65a9
--- /dev/null
+++ b/batman-adv/patches/0026-batman-adv-Introduce-no-noflood-mark.patch
@@ -0,0 +1,262 @@
+From 6a729232b4d46cc8b99eb0ed89547de2dddfc33f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Linus=20L=C3=BCssing?= <linus.luessing@c0d3.blue>
+Date: Sat, 31 Mar 2018 03:36:19 +0200
+Subject: [PATCH] batman-adv: Introduce no noflood mark
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This mark prevents a multicast packet being flooded through the whole
+mesh. The advantage of marking certain multicast packets via e.g.
+ebtables instead of dropping is then the following:
+
+This allows an administrator to let specific multicast packets pass as
+long as they are forwarded to a limited number of nodes only and are
+therefore creating no burdon to unrelated nodes.
+
+Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
+---
+ Documentation/ABI/testing/sysfs-class-net-mesh | 8 ++
+ net/batman-adv/soft-interface.c | 20 +++++
+ net/batman-adv/sysfs.c | 103 +++++++++++++++++++++----
+ net/batman-adv/types.h | 12 +++
+ 4 files changed, 128 insertions(+), 15 deletions(-)
+
+diff --git a/Documentation/ABI/testing/sysfs-class-net-mesh b/Documentation/ABI/testing/sysfs-class-net-mesh
+index 18734a36..477287c4 100644
+--- a/Documentation/ABI/testing/sysfs-class-net-mesh
++++ b/Documentation/ABI/testing/sysfs-class-net-mesh
+@@ -76,6 +76,14 @@ Description:
+ is used to classify clients as "isolated" by the
+ Extended Isolation feature.
+
++What: /sys/class/net/<mesh_iface>/mesh/noflood_mark
++Date: Mar 2018
++Contact: Linus Lüssing <linus.luessing@c0d3.blue>
++Description:
++ Defines the noflood mark (and its bitmask) which
++ will drop frames with a matching mark if they were
++ to be flooded.
++
+ What: /sys/class/net/<mesh_iface>/mesh/multicast_fanout
+ Date: Feb 2018
+ Contact: Linus Lüssing <linus.luessing@c0d3.blue>
+diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
+index e43b38c3..ac272cbe 100644
+--- a/net/batman-adv/soft-interface.c
++++ b/net/batman-adv/soft-interface.c
+@@ -188,6 +188,23 @@ static void batadv_interface_set_rx_mode(struct net_device *dev)
+ {
+ }
+
++/**
++ * batadv_send_skb_has_noflood_mark() - check if packet has a noflood mark
++ * @bat_priv: the bat priv with all the soft interface information
++ * @skb: the packet to check
++ *
++ * Return: True if the skb's mark matches a configured noflood mark and
++ * noflood mark mask. False otherwise.
++ */
++static bool
++batadv_skb_has_noflood_mark(struct batadv_priv *bat_priv, struct sk_buff *skb)
++{
++ u32 match_mark = skb->mark & bat_priv->noflood_mark_mask;
++
++ return bat_priv->noflood_mark_mask &&
++ match_mark == bat_priv->noflood_mark;
++}
++
+ static int batadv_interface_tx(struct sk_buff *skb,
+ struct net_device *soft_iface)
+ {
+@@ -329,6 +346,9 @@ static int batadv_interface_tx(struct sk_buff *skb,
+ if (batadv_dat_snoop_outgoing_arp_request(bat_priv, skb))
+ brd_delay = msecs_to_jiffies(ARP_REQ_DELAY);
+
++ if (batadv_skb_has_noflood_mark(bat_priv, skb))
++ goto dropped;
++
+ if (batadv_skb_head_push(skb, sizeof(*bcast_packet)) < 0)
+ goto dropped;
+
+diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c
+index 57b728b3..0e164c15 100644
+--- a/net/batman-adv/sysfs.c
++++ b/net/batman-adv/sysfs.c
+@@ -627,28 +627,24 @@ static ssize_t batadv_show_isolation_mark(struct kobject *kobj,
+ }
+
+ /**
+- * batadv_store_isolation_mark() - parse and store the isolation mark/mask
+- * entered by the user
+- * @kobj: kobject representing the private mesh sysfs directory
+- * @attr: the batman-adv attribute the user is interacting with
++ * batadv_store_parse_mark() - parse a mark and mask
+ * @buff: the buffer containing the user data
+- * @count: number of bytes in the buffer
++ * @mark: the variable to store the mark in
++ * @mask: the variable to store the mask in
+ *
+- * Return: 'count' on success or a negative error code in case of failure
++ * Parses a string for a mark and mask. The format is expected to consist of
++ * two 32 bit hexadecimal numbers delimited by a '/'.
++ *
++ * Return: 0 on success, -EINVAL on error.
+ */
+-static ssize_t batadv_store_isolation_mark(struct kobject *kobj,
+- struct attribute *attr, char *buff,
+- size_t count)
++static int batadv_store_parse_mark(char *buff, u32 *mark, u32 *mask)
+ {
+- struct net_device *net_dev = batadv_kobj_to_netdev(kobj);
+- struct batadv_priv *bat_priv = netdev_priv(net_dev);
+- u32 mark, mask;
+ char *mask_ptr;
+
+ /* parse the mask if it has been specified, otherwise assume the mask is
+ * the biggest possible
+ */
+- mask = 0xFFFFFFFF;
++ *mask = 0xFFFFFFFF;
+ mask_ptr = strchr(buff, '/');
+ if (mask_ptr) {
+ *mask_ptr = '\0';
+@@ -657,12 +653,36 @@ static ssize_t batadv_store_isolation_mark(struct kobject *kobj,
+ /* the mask must be entered in hex base as it is going to be a
+ * bitmask and not a prefix length
+ */
+- if (kstrtou32(mask_ptr, 16, &mask) < 0)
++ if (kstrtou32(mask_ptr, 16, mask) < 0)
+ return -EINVAL;
+ }
+
+ /* the mark can be entered in any base */
+- if (kstrtou32(buff, 0, &mark) < 0)
++ if (kstrtou32(buff, 0, mark) < 0)
++ return -EINVAL;
++
++ return 0;
++}
++
++/**
++ * batadv_store_isolation_mark() - parse and store the isolation mark/mask
++ * entered by the user
++ * @kobj: kobject representing the private mesh sysfs directory
++ * @attr: the batman-adv attribute the user is interacting with
++ * @buff: the buffer containing the user data
++ * @count: number of bytes in the buffer
++ *
++ * Return: 'count' on success or a negative error code in case of failure
++ */
++static ssize_t batadv_store_isolation_mark(struct kobject *kobj,
++ struct attribute *attr, char *buff,
++ size_t count)
++{
++ struct net_device *net_dev = batadv_kobj_to_netdev(kobj);
++ struct batadv_priv *bat_priv = netdev_priv(net_dev);
++ u32 mark, mask;
++
++ if (batadv_store_parse_mark(buff, &mark, &mask) < 0)
+ return -EINVAL;
+
+ bat_priv->isolation_mark_mask = mask;
+@@ -676,6 +696,56 @@ static ssize_t batadv_store_isolation_mark(struct kobject *kobj,
+ return count;
+ }
+
++/**
++ * batadv_show_noflood_mark() - print the current noflood mark/mask
++ * @kobj: kobject representing the private mesh sysfs directory
++ * @attr: the batman-adv attribute the user is interacting with
++ * @buff: the buffer that will contain the data to send back to the user
++ *
++ * Return: the number of bytes written into 'buff' on success or a negative
++ * error code in case of failure
++ */
++static ssize_t batadv_show_noflood_mark(struct kobject *kobj,
++ struct attribute *attr, char *buff)
++{
++ struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);
++
++ return sprintf(buff, "%#.8x/%#.8x\n", bat_priv->noflood_mark,
++ bat_priv->noflood_mark_mask);
++}
++
++/**
++ * batadv_store_noflood_mark() - parse and store the noflood mark/mask
++ * entered by the user
++ * @kobj: kobject representing the private mesh sysfs directory
++ * @attr: the batman-adv attribute the user is interacting with
++ * @buff: the buffer containing the user data
++ * @count: number of bytes in the buffer
++ *
++ * Return: 'count' on success or a negative error code in case of failure
++ */
++static ssize_t batadv_store_noflood_mark(struct kobject *kobj,
++ struct attribute *attr, char *buff,
++ size_t count)
++{
++ struct net_device *net_dev = batadv_kobj_to_netdev(kobj);
++ struct batadv_priv *bat_priv = netdev_priv(net_dev);
++ u32 mark, mask;
++
++ if (batadv_store_parse_mark(buff, &mark, &mask) < 0)
++ return -EINVAL;
++
++ bat_priv->noflood_mark_mask = mask;
++ /* erase bits not covered by the mask */
++ bat_priv->noflood_mark = mark & bat_priv->noflood_mark_mask;
++
++ batadv_info(net_dev,
++ "New skb noflood mark: %#.8x/%#.8x\n",
++ bat_priv->noflood_mark, bat_priv->noflood_mark_mask);
++
++ return count;
++}
++
+ BATADV_ATTR_SIF_BOOL(aggregated_ogms, 0644, NULL);
+ BATADV_ATTR_SIF_BOOL(bonding, 0644, NULL);
+ #ifdef CONFIG_BATMAN_ADV_BLA
+@@ -708,6 +778,8 @@ BATADV_ATTR_SIF_BOOL(network_coding, 0644, batadv_nc_status_update);
+ #endif
+ static BATADV_ATTR(isolation_mark, 0644, batadv_show_isolation_mark,
+ batadv_store_isolation_mark);
++static BATADV_ATTR(noflood_mark, 0644, batadv_show_noflood_mark,
++ batadv_store_noflood_mark);
+
+ static struct batadv_attribute *batadv_mesh_attrs[] = {
+ &batadv_attr_aggregated_ogms,
+@@ -736,6 +808,7 @@ static struct batadv_attribute *batadv_mesh_attrs[] = {
+ &batadv_attr_network_coding,
+ #endif
+ &batadv_attr_isolation_mark,
++ &batadv_attr_noflood_mark,
+ NULL,
+ };
+
+diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
+index 47690038..44ab319f 100644
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1586,6 +1586,18 @@ struct batadv_priv {
+ */
+ u32 isolation_mark_mask;
+
++ /**
++ * @noflood_mark: the skb->mark value used to allow directed targeting
++ * only
++ */
++ u32 noflood_mark;
++
++ /**
++ * @noflood_mark_mask: bitmask identifying the bits in skb->mark to be
++ * used for the noflood mark
++ */
++ u32 noflood_mark_mask;
++
+ /** @bcast_seqno: last sent broadcast packet sequence number */
+ atomic_t bcast_seqno;
+
+--
+2.11.0
+