Skip to content

Commit 687937a

Browse files
T-Xsimonwunderlich
authored andcommitted
batman-adv: Add multicast optimization support for bridged setups
With this patch we are finally able to support multicast optimizations in bridged setups, too. So far, if a bridge was added on top of a soft-interface (e.g. bat0) the batman-adv multicast optimizations needed to be disabled to avoid packetloss. Current Linux bridge implementations and API can now provide us with the so far missing information about interested but "remote" multicast receivers behind bridge ports. The Linux bridge performs the detection of remote participants interested in multicast packets with its own and mature so called IGMP and MLD snooping code and stores that in its database. With the new API provided by the bridge batman-adv can now simply hook into this database. We then reliably announce the gathered multicast listeners to other nodes through the batman-adv translation table. Additionally, the Linux bridge provides us with the information about whether an IGMP/MLD querier exists. If there is none then we need to disable multicast optimizations as we cannot learn about multicast listeners on external, bridged-in host then. Tested-by: Simon Wunderlich <[email protected]> Signed-off-by: Linus Lüssing <[email protected]> Signed-off-by: Marek Lindner <[email protected]> Signed-off-by: Sven Eckelmann <[email protected]> Signed-off-by: Simon Wunderlich <[email protected]>
1 parent bd2a979 commit 687937a

File tree

3 files changed

+177
-19
lines changed

3 files changed

+177
-19
lines changed

net/batman-adv/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ config BATMAN_ADV_NC
6666

6767
config BATMAN_ADV_MCAST
6868
bool "Multicast optimisation"
69-
depends on BATMAN_ADV && INET
69+
depends on BATMAN_ADV && INET && !(BRIDGE=m && BATMAN_ADV=y)
7070
default n
7171
help
7272
This option enables the multicast optimisation which aims to

net/batman-adv/multicast.c

Lines changed: 163 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <linux/etherdevice.h>
2727
#include <linux/fs.h>
2828
#include <linux/icmpv6.h>
29+
#include <linux/if_bridge.h>
2930
#include <linux/if_ether.h>
3031
#include <linux/igmp.h>
3132
#include <linux/in.h>
@@ -36,6 +37,7 @@
3637
#include <linux/list.h>
3738
#include <linux/lockdep.h>
3839
#include <linux/netdevice.h>
40+
#include <linux/printk.h>
3941
#include <linux/rculist.h>
4042
#include <linux/rcupdate.h>
4143
#include <linux/skbuff.h>
@@ -45,31 +47,67 @@
4547
#include <linux/string.h>
4648
#include <linux/types.h>
4749
#include <net/addrconf.h>
50+
#include <net/if_inet6.h>
51+
#include <net/ip.h>
4852
#include <net/ipv6.h>
4953

5054
#include "packet.h"
5155
#include "translation-table.h"
5256

57+
/**
58+
* batadv_mcast_get_bridge - get the bridge on top of the softif if it exists
59+
* @soft_iface: netdev struct of the mesh interface
60+
*
61+
* If the given soft interface has a bridge on top then the refcount
62+
* of the according net device is increased.
63+
*
64+
* Return: NULL if no such bridge exists. Otherwise the net device of the
65+
* bridge.
66+
*/
67+
static struct net_device *batadv_mcast_get_bridge(struct net_device *soft_iface)
68+
{
69+
struct net_device *upper = soft_iface;
70+
71+
rcu_read_lock();
72+
do {
73+
upper = netdev_master_upper_dev_get_rcu(upper);
74+
} while (upper && !(upper->priv_flags & IFF_EBRIDGE));
75+
76+
if (upper)
77+
dev_hold(upper);
78+
rcu_read_unlock();
79+
80+
return upper;
81+
}
82+
5383
/**
5484
* batadv_mcast_mla_softif_get - get softif multicast listeners
5585
* @dev: the device to collect multicast addresses from
5686
* @mcast_list: a list to put found addresses into
5787
*
58-
* Collect multicast addresses of the local multicast listeners
59-
* on the given soft interface, dev, in the given mcast_list.
88+
* Collects multicast addresses of multicast listeners residing
89+
* on this kernel on the given soft interface, dev, in
90+
* the given mcast_list. In general, multicast listeners provided by
91+
* your multicast receiving applications run directly on this node.
92+
*
93+
* If there is a bridge interface on top of dev, collects from that one
94+
* instead. Just like with IP addresses and routes, multicast listeners
95+
* will(/should) register to the bridge interface instead of an
96+
* enslaved bat0.
6097
*
6198
* Return: -ENOMEM on memory allocation error or the number of
6299
* items added to the mcast_list otherwise.
63100
*/
64101
static int batadv_mcast_mla_softif_get(struct net_device *dev,
65102
struct hlist_head *mcast_list)
66103
{
104+
struct net_device *bridge = batadv_mcast_get_bridge(dev);
67105
struct netdev_hw_addr *mc_list_entry;
68106
struct batadv_hw_addr *new;
69107
int ret = 0;
70108

71-
netif_addr_lock_bh(dev);
72-
netdev_for_each_mc_addr(mc_list_entry, dev) {
109+
netif_addr_lock_bh(bridge ? bridge : dev);
110+
netdev_for_each_mc_addr(mc_list_entry, bridge ? bridge : dev) {
73111
new = kmalloc(sizeof(*new), GFP_ATOMIC);
74112
if (!new) {
75113
ret = -ENOMEM;
@@ -80,7 +118,10 @@ static int batadv_mcast_mla_softif_get(struct net_device *dev,
80118
hlist_add_head(&new->list, mcast_list);
81119
ret++;
82120
}
83-
netif_addr_unlock_bh(dev);
121+
netif_addr_unlock_bh(bridge ? bridge : dev);
122+
123+
if (bridge)
124+
dev_put(bridge);
84125

85126
return ret;
86127
}
@@ -105,6 +146,83 @@ static bool batadv_mcast_mla_is_duplicate(u8 *mcast_addr,
105146
return false;
106147
}
107148

149+
/**
150+
* batadv_mcast_mla_br_addr_cpy - copy a bridge multicast address
151+
* @dst: destination to write to - a multicast MAC address
152+
* @src: source to read from - a multicast IP address
153+
*
154+
* Converts a given multicast IPv4/IPv6 address from a bridge
155+
* to its matching multicast MAC address and copies it into the given
156+
* destination buffer.
157+
*
158+
* Caller needs to make sure the destination buffer can hold
159+
* at least ETH_ALEN bytes.
160+
*/
161+
static void batadv_mcast_mla_br_addr_cpy(char *dst, const struct br_ip *src)
162+
{
163+
if (src->proto == htons(ETH_P_IP))
164+
ip_eth_mc_map(src->u.ip4, dst);
165+
#if IS_ENABLED(CONFIG_IPV6)
166+
else if (src->proto == htons(ETH_P_IPV6))
167+
ipv6_eth_mc_map(&src->u.ip6, dst);
168+
#endif
169+
else
170+
eth_zero_addr(dst);
171+
}
172+
173+
/**
174+
* batadv_mcast_mla_bridge_get - get bridged-in multicast listeners
175+
* @dev: a bridge slave whose bridge to collect multicast addresses from
176+
* @mcast_list: a list to put found addresses into
177+
*
178+
* Collects multicast addresses of multicast listeners residing
179+
* on foreign, non-mesh devices which we gave access to our mesh via
180+
* a bridge on top of the given soft interface, dev, in the given
181+
* mcast_list.
182+
*
183+
* Return: -ENOMEM on memory allocation error or the number of
184+
* items added to the mcast_list otherwise.
185+
*/
186+
static int batadv_mcast_mla_bridge_get(struct net_device *dev,
187+
struct hlist_head *mcast_list)
188+
{
189+
struct list_head bridge_mcast_list = LIST_HEAD_INIT(bridge_mcast_list);
190+
struct br_ip_list *br_ip_entry, *tmp;
191+
struct batadv_hw_addr *new;
192+
u8 mcast_addr[ETH_ALEN];
193+
int ret;
194+
195+
/* we don't need to detect these devices/listeners, the IGMP/MLD
196+
* snooping code of the Linux bridge already does that for us
197+
*/
198+
ret = br_multicast_list_adjacent(dev, &bridge_mcast_list);
199+
if (ret < 0)
200+
goto out;
201+
202+
list_for_each_entry(br_ip_entry, &bridge_mcast_list, list) {
203+
batadv_mcast_mla_br_addr_cpy(mcast_addr, &br_ip_entry->addr);
204+
if (batadv_mcast_mla_is_duplicate(mcast_addr, mcast_list))
205+
continue;
206+
207+
new = kmalloc(sizeof(*new), GFP_ATOMIC);
208+
if (!new) {
209+
ret = -ENOMEM;
210+
break;
211+
}
212+
213+
ether_addr_copy(new->addr, mcast_addr);
214+
hlist_add_head(&new->list, mcast_list);
215+
}
216+
217+
out:
218+
list_for_each_entry_safe(br_ip_entry, tmp, &bridge_mcast_list, list) {
219+
list_del(&br_ip_entry->list);
220+
kfree(br_ip_entry);
221+
}
222+
223+
return ret;
224+
}
225+
108226
/**
109227
* batadv_mcast_mla_list_free - free a list of multicast addresses
110228
* @bat_priv: the bat priv with all the soft interface information
@@ -222,29 +340,51 @@ static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv)
222340
* Updates the own multicast tvlv with our current multicast related settings,
223341
* capabilities and inabilities.
224342
*
225-
* Return: true if the tvlv container is registered afterwards. Otherwise
226-
* returns false.
343+
* Return: false if we want all IPv4 && IPv6 multicast traffic and true
344+
* otherwise.
227345
*/
228346
static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv)
229347
{
230348
struct batadv_tvlv_mcast_data mcast_data;
349+
struct batadv_mcast_querier_state querier4 = {false, false};
350+
struct batadv_mcast_querier_state querier6 = {false, false};
351+
struct net_device *dev = bat_priv->soft_iface;
231352

232353
mcast_data.flags = BATADV_NO_FLAGS;
233354
memset(mcast_data.reserved, 0, sizeof(mcast_data.reserved));
234355

235-
/* Avoid attaching MLAs, if there is a bridge on top of our soft
236-
* interface, we don't support that yet (TODO)
356+
bat_priv->mcast.bridged = batadv_mcast_has_bridge(bat_priv);
357+
if (!bat_priv->mcast.bridged)
358+
goto update;
359+
360+
#if !IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING)
361+
pr_warn_once("No bridge IGMP snooping compiled - multicast optimizations disabled\n");
362+
#endif
363+
364+
querier4.exists = br_multicast_has_querier_anywhere(dev, ETH_P_IP);
365+
querier4.shadowing = br_multicast_has_querier_adjacent(dev, ETH_P_IP);
366+
367+
querier6.exists = br_multicast_has_querier_anywhere(dev, ETH_P_IPV6);
368+
querier6.shadowing = br_multicast_has_querier_adjacent(dev, ETH_P_IPV6);
369+
370+
mcast_data.flags |= BATADV_MCAST_WANT_ALL_UNSNOOPABLES;
371+
372+
/* 1) If no querier exists at all, then multicast listeners on
373+
* our local TT clients behind the bridge will keep silent.
374+
* 2) If the selected querier is on one of our local TT clients,
375+
* behind the bridge, then this querier might shadow multicast
376+
* listeners on our local TT clients, behind this bridge.
377+
*
378+
* In both cases, we will signalize other batman nodes that
379+
* we need all multicast traffic of the according protocol.
237380
*/
238-
if (batadv_mcast_has_bridge(bat_priv)) {
239-
if (bat_priv->mcast.enabled) {
240-
batadv_tvlv_container_unregister(bat_priv,
241-
BATADV_TVLV_MCAST, 2);
242-
bat_priv->mcast.enabled = false;
243-
}
381+
if (!querier4.exists || querier4.shadowing)
382+
mcast_data.flags |= BATADV_MCAST_WANT_ALL_IPV4;
244383

245-
return false;
246-
}
384+
if (!querier6.exists || querier6.shadowing)
385+
mcast_data.flags |= BATADV_MCAST_WANT_ALL_IPV6;
247386

387+
update:
248388
if (!bat_priv->mcast.enabled ||
249389
mcast_data.flags != bat_priv->mcast.flags) {
250390
batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 2,
@@ -253,7 +393,8 @@ static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv)
253393
bat_priv->mcast.enabled = true;
254394
}
255395

256-
return true;
396+
return !(mcast_data.flags &
397+
(BATADV_MCAST_WANT_ALL_IPV4 + BATADV_MCAST_WANT_ALL_IPV6));
257398
}
258399

259400
/**
@@ -276,6 +417,10 @@ void batadv_mcast_mla_update(struct batadv_priv *bat_priv)
276417
if (ret < 0)
277418
goto out;
278419

420+
ret = batadv_mcast_mla_bridge_get(soft_iface, &mcast_list);
421+
if (ret < 0)
422+
goto out;
423+
279424
update:
280425
batadv_mcast_mla_tt_retract(bat_priv, &mcast_list);
281426
batadv_mcast_mla_tt_add(bat_priv, &mcast_list);

net/batman-adv/types.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,17 @@ struct batadv_priv_dat {
754754
#endif
755755

756756
#ifdef CONFIG_BATMAN_ADV_MCAST
757+
/**
758+
* struct batadv_mcast_querier_state - IGMP/MLD querier state when bridged
759+
* @exists: whether a querier exists in the mesh
760+
* @shadowing: if a querier exists, whether it is potentially shadowing
761+
* multicast listeners (i.e. querier is behind our own bridge segment)
762+
*/
763+
struct batadv_mcast_querier_state {
764+
bool exists;
765+
bool shadowing;
766+
};
767+
757768
/**
758769
* struct batadv_priv_mcast - per mesh interface mcast data
759770
* @mla_list: list of multicast addresses we are currently announcing via TT
@@ -763,6 +774,7 @@ struct batadv_priv_dat {
763774
* @want_all_ipv6_list: a list of orig_nodes wanting all IPv6 multicast traffic
764775
* @flags: the flags we have last sent in our mcast tvlv
765776
* @enabled: whether the multicast tvlv is currently enabled
777+
* @bridged: whether the soft interface has a bridge on top
766778
* @num_disabled: number of nodes that have no mcast tvlv
767779
* @num_want_all_unsnoopables: number of nodes wanting unsnoopable IP traffic
768780
* @num_want_all_ipv4: counter for items in want_all_ipv4_list
@@ -777,6 +789,7 @@ struct batadv_priv_mcast {
777789
struct hlist_head want_all_ipv6_list;
778790
u8 flags;
779791
bool enabled;
792+
bool bridged;
780793
atomic_t num_disabled;
781794
atomic_t num_want_all_unsnoopables;
782795
atomic_t num_want_all_ipv4;

0 commit comments

Comments
 (0)