Skip to contentSkip to conversation

Multicast optimizations for bridges

PATCH Superseded Linus Lüssing 2 participants 6 messages Jul 29, 2014 – Aug 7, 2014

v6 of 9 Superseded Latest is v9 →

lore
Share & tools
lore
#0 lore raw

v6 cover Multicast optimizations for bridges

This patchset enables the usage of the batman-adv multicast optimizations for scenarios involving bridges on top of e.g. bat0, too.

Along come two more patches adding according debugging facilities to make it possible for the user to check why the multicast optimizations might not work ideally to give hints about what they might change about their topology.

Cheers, Linus

Changes in v6:
* New PATCH 2/3 inserted, moving logging to separate patch
* More verbose logging added to PATCH 2/3:
Bridge and querier state changes are logged too
* upper case to lower case for kernel doc of batadv_mcast_flags_open (PATCH 2/3)
* Adding note to kernel doc of batadv_mcast_get_bridge about increased refcount (PATCH 1/3)
* Printing some lines about current bridge and querier state to debugfs too (PATCH 3/3)

Changes in v5 (PATCH 2/2 only):
* s/dat_cache/mcast_flags/ in kerneldoc (copy&paste error)

Changes in v4 (PATCH 2/2 only):
* initial {ad,e}dition of this patch

Changes in v3 (PATCH 1/2 only):
* Removed "RFC" tag in title again: The stubs and new export are upstream in net-next and therefore going to be included in 3.17
* Added some debug output:
* Two warning messages:

    -> Old kernel version or no bridge IGMP/MLD snooping compiled
  * New batman-adv log-level "mcast":
    -> Logging mcast flag changes

(a third debugging facility, a new table for debugfs for a global
mcast flag overview will be added in a separate patch later as discussed with Simon)

Changes in v2 (PATCH 1/2 only):
* fetching local (= on this same kernel) multicast listeners from the bridge instead of the bat0 interface if a bridge is present
- just like ip addresses and routes should be used from br0, the same goes for multicast listeners
* beautification of batadv_mcast_mla_br_addr_cpy(), now using already present functions from the kernel instead of own, hackish approach
* changed names of some goto-labels (not using "skip" anymore)
* using new, third bridge multicast export (because this export is not upstream yet, I've added the "RFC" in the title):
br_multicast_has_querier_anywhere()
* adding compat stubs for two bridge multicast exports, to make batman-adv compile- and usable even if a 3.16 kernel was compiled without bridge code - the stubs are supposed to be upstream in the bridge code in 3.17 (therefore just 'compat')
* updated kerneldocs for batadv_mcast_mla_bridge_get() and batadv_mcast_mla_softif_get()
* The two sentences in the commit message starting with "Queriers: ..." were slightly modified to include the third bridge multicast export

#1 lore raw

PATCH v6 1/3 batman-adv: Add multicast optimization support for bridged setups

So far, the recently added multicast optimizations did not support a configuration where a bridge is on top of the soft-interface (e.g. bat0).

Now the Linux bridge code is able to provide us with the missing bits:

Multicast Listeners: The bridge hands us a list of multicast listeners it has detected behind the bridge which we will announce through the mesh via the translation table, allowing other nodes to direct multicast packets to these clients behind the bridge even with enabled multicast optimizations.

Queriers: The bridge informs us whether there is a selected IGMP or MLD querier behind the bridge or if there is no querier at all. In these cases we need all according IPv4 or IPv6 multicast traffic directed to us.

These two parts together allow us to serve all multicast listeners with IPv6 link-local multicast packets even when bridges are involved and our multicast optimizations enabled.

---
 compat.c    |   39 ++++++++++++++
 compat.h    |   32 +++++++++++
 main.h      |    6 ++-
 multicast.c |  169 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 4 files changed, 227 insertions(+), 19 deletions(-)
compat.c +39 -0
index 3dbf9d2..31546f5 100644
@@ -109,3 +109,42 @@ void batadv_free_rcu_tvlv_handler(struct rcu_head *rcu)
 }
 
 #endif /* < KERNEL_VERSION(3, 0, 0) */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0)
+
+int br_multicast_list_adjacent(struct net_device *dev,
+			       struct list_head *br_ip_list)
+{
+	return 0;
+}
+
+bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto)
+{
+	return false;
+}
+
+#endif /* < KERNEL_VERSION(3, 16, 0) */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+
+bool br_multicast_has_querier_anywhere(struct net_device *dev, int proto)
+{
+	pr_warn_once("Old kernel detected (< 3.17) - multicast optimizations disabled\n");
+
+	return false;
+}
+
+#if !(IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING))
+int br_multicast_list_adjacent(struct net_device *dev,
+			       struct list_head *br_ip_list)
+{
+	return 0;
+}
+
+bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto)
+{
+	return false;
+}
+#endif
+
+#endif /* < KERNEL_VERSION(3, 17, 0) */
compat.h +32 -0
index ed5b815..14de948 100644
@@ -240,6 +240,12 @@ static inline void skb_reset_mac_len(struct sk_buff *skb)
 
 #endif /* < KERNEL_VERSION(3, 0, 0) */
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
+
+#define IS_ENABLED(x) defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+
+#endif /* < KERNEL_VERSION(3, 1, 0) */
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
 
 #define batadv_interface_add_vid(x, y, z) \
@@ -444,6 +450,32 @@ static int __batadv_interface_kill_vid(struct net_device *dev, __be16 proto,\
 
 #define pskb_copy_for_clone pskb_copy
 
+int br_multicast_list_adjacent(struct net_device *dev,
+			       struct list_head *br_ip_list);
+bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto);
+
+struct br_ip {
+	union {
+		__be32  ip4;
+#if IS_ENABLED(CONFIG_IPV6)
+		struct in6_addr ip6;
+#endif
+	} u;
+	__be16          proto;
+	__u16           vid;
+};
+
+struct br_ip_list {
+	struct list_head list;
+	struct br_ip addr;
+};
+
 #endif /* < KERNEL_VERSION(3, 16, 0) */
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+
+bool br_multicast_has_querier_anywhere(struct net_device *dev, int proto);
+
+#endif /* < KERNEL_VERSION(3, 17, 0) */
+
 #endif /* _NET_BATMAN_ADV_COMPAT_H_ */
main.h +5 -1
index 4bb500b..1e1e000 100644
@@ -169,6 +169,7 @@ enum batadv_uev_type {
 #include <linux/netdevice.h>	/* netdevice */
 #include <linux/etherdevice.h>  /* ethernet address classification */
 #include <linux/if_ether.h>	/* ethernet header */
+#include <linux/if_bridge.h>	/* bridge / multicast-snooping communication */
 #include <linux/poll.h>		/* poll_table */
 #include <linux/kthread.h>	/* kernel threads */
 #include <linux/pkt_sched.h>	/* schedule types */
@@ -177,6 +178,7 @@ enum batadv_uev_type {
 #include <linux/slab.h>
 #include <net/sock.h>		/* struct sock */
 #include <net/addrconf.h>	/* ipv6 address stuff */
+#include <net/ip.h>
 #include <linux/ip.h>
 #include <net/rtnetlink.h>
 #include <linux/jiffies.h>
@@ -223,6 +225,7 @@ __be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr);
  * @BATADV_DBG_BLA: bridge loop avoidance messages
  * @BATADV_DBG_DAT: ARP snooping and DAT related messages
  * @BATADV_DBG_NC: network coding related messages
+ * @BATADV_DBG_MCAST: multicast related messages
  * @BATADV_DBG_ALL: the union of all the above log levels
  */
 enum batadv_dbg_level {
@@ -232,7 +235,8 @@ enum batadv_dbg_level {
 	BATADV_DBG_BLA    = BIT(3),
 	BATADV_DBG_DAT    = BIT(4),
 	BATADV_DBG_NC	  = BIT(5),
-	BATADV_DBG_ALL    = 63,
+	BATADV_DBG_MCAST  = BIT(6),
+	BATADV_DBG_ALL    = 127,
 };
 
 #ifdef CONFIG_BATMAN_ADV_DEBUG
multicast.c +151 -18

230 lines not on this page · raw message

Signature · 1 line
1.7.10.4
#2 lore raw

PATCH v6 2/3 batman-adv: Adding 'mcast' log level

In reply to Linus Lüssing

This patch adds an 'mcast' log level. Currently, it will print changes relevant to a nodes own multicast flag changes.

---
 multicast.c      |  152 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 soft-interface.c |    5 ++
 types.h          |   17 ++++++
 3 files changed, 169 insertions(+), 5 deletions(-)
multicast.c +147 -5
index c05f64a..2a80997 100644
@@ -292,6 +292,123 @@ static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv)
 }
 
 /**
+ * batadv_mcast_querier_log - debug output regarding the querier status on link
+ * @bat_priv: the bat priv with all the soft interface information
+ * @str_proto: a string for the querier protocol (e.g. "IGMP" or "MLD")
+ * @old_querier: the previous querier state on our link
+ * @new_querier: the new querier state on our link
+ *
+ * Outputs debug messages to the logging facility with log level 'mcast'
+ * regarding changes to the querier status on the link which are relevant
+ * to our multicast optimizations.
+ *
+ * Usually this is about whether a querier appeared or vanished in
+ * our mesh or whether the querier is in the suboptimal position of being
+ * behind our local bridge segment.
+ *
+ * This is only interesting for nodes with a bridge on top of their
+ * soft interface.
+ */
+static void
+batadv_mcast_querier_log(struct batadv_priv *bat_priv, char *str_proto,
+			 struct batadv_mcast_querier_state *old_querier,
+			 struct batadv_mcast_querier_state *new_querier)
+{
+	if (!old_querier->exists && new_querier->exists)
+		batadv_dbg(BATADV_DBG_MCAST, bat_priv,
+			   "%s Querier appeared (good!)\n", str_proto);
+	else if (old_querier->exists && !new_querier->exists)
+		batadv_dbg(BATADV_DBG_MCAST, bat_priv,
+			   "%s Querier disappeared (bad)\n", str_proto);
+	else if (!bat_priv->mcast.bridged && !new_querier->exists)
+		batadv_dbg(BATADV_DBG_MCAST, bat_priv,
+			   "Note: No %s Querier present\n", str_proto);
+
+	if (!new_querier->exists)
+		return;
+
+	if (!old_querier->shadowing && new_querier->shadowing)
+		batadv_dbg(BATADV_DBG_MCAST, bat_priv,
+			   "%s Querier is behind our bridged segment: Might shadow listeners (bad)\n",
+			   str_proto);
+	else if (old_querier->shadowing && !new_querier->shadowing)
+		batadv_dbg(BATADV_DBG_MCAST, bat_priv,
+			   "%s Querier is not behind our bridged segment (good!)\n",
+			   str_proto);
+}
+
+/**
+ * batadv_mcast_bridge_log - debug output for topology changes in bridged setups
+ * @bat_priv: the bat priv with all the soft interface information
+ * @bridged: a flag about whether the soft interface is currently bridged or not
+ * @querier_ipv4: (maybe) new status of a potential, selected IGMP querier
+ * @querier_ipv6: (maybe) new status of a potential, selected MLD querier
+ *
+ * If no bridges are ever used on this node, then this function does nothing.
+ *
+ * Otherwise this function outputs debug information to the 'mcast' log level
+ * which might be relevant to our multicast optimizations.
+ *
+ * More precisely, it outputs information when a bridge interface is added or
+ * removed from a soft interface. And when a bridge is present, it further
+ * outputs information about the querier state which is relevant for the
+ * multicast flags this node is going to set.
+ */
+static void
+batadv_mcast_bridge_log(struct batadv_priv *bat_priv, bool bridged,
+			struct batadv_mcast_querier_state *querier_ipv4,
+			struct batadv_mcast_querier_state *querier_ipv6)
+{
+	if (!bat_priv->mcast.bridged && bridged)
+		batadv_dbg(BATADV_DBG_MCAST, bat_priv,
+			   "Bridge added: Setting U-flag\n");
+	else if (bat_priv->mcast.bridged && !bridged)
+		batadv_dbg(BATADV_DBG_MCAST, bat_priv,
+			   "Bridge removed: Unsetting U-flag\n");
+	if (!bridged)
+		return;
+
+	batadv_mcast_querier_log(bat_priv, "IGMP",
+				 &bat_priv->mcast.querier_ipv4, querier_ipv4);
+	batadv_mcast_querier_log(bat_priv, "MLD",
+				 &bat_priv->mcast.querier_ipv6, querier_ipv6);
+}
+
+/**
+ * batadv_mcast_flags_logs - output debug information about mcast flag changes
+ * @bat_priv: the bat priv with all the soft interface information
+ * @mcast_flags: flags indicating the new multicast state
+ *
+ * Whenever the multicast flags this nodes announces changes (@mcast_flags vs.
+ * bat_priv->mcast.flags), this notifies userspace via the 'mcast' log level.
+ */
+static void batadv_mcast_flags_log(struct batadv_priv *bat_priv, uint8_t flags)
+{
+	char str_undefined[] = "<undefined>";
+	char str_flags[] = "[...]";
+	char *str_flags_old = str_undefined;
+	uint8_t old_flags = bat_priv->mcast.flags;
+
+	if (!bat_priv->mcast.enabled)
+		goto print;
+
+	str_flags_old = str_flags;
+
+	sprintf(str_flags_old, "[%c%c%c]",
+		old_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES ? 'U' : '.',
+		old_flags & BATADV_MCAST_WANT_ALL_IPV4 ? '4' : '.',
+		old_flags & BATADV_MCAST_WANT_ALL_IPV6 ? '6' : '.');
+
+print:
+	batadv_dbg(BATADV_DBG_MCAST, bat_priv,
+		   "Changing multicast flags from '%s' to '[%c%c%c]'\n",
+		   str_flags_old,
+		   flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES ? 'U' : '.',
+		   flags & BATADV_MCAST_WANT_ALL_IPV4 ? '4' : '.',
+		   flags & BATADV_MCAST_WANT_ALL_IPV6 ? '6' : '.');
+}
+
+/**
  * batadv_mcast_mla_tvlv_update - update multicast tvlv
  * @bat_priv: the bat priv with all the soft interface information
  *
@@ -304,18 +421,33 @@ static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv)
 static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv)
 {
 	struct batadv_tvlv_mcast_data mcast_data;
+	struct batadv_mcast_querier_state querier_ipv4, querier_ipv6;
 	struct net_device *dev = bat_priv->soft_iface;
+	bool bridged;
 
 	mcast_data.flags = BATADV_NO_FLAGS;
 	memset(mcast_data.reserved, 0, sizeof(mcast_data.reserved));
+	memset(&querier_ipv4, 0, sizeof(querier_ipv4));
+	memset(&querier_ipv6, 0, sizeof(querier_ipv6));
 
-	if (!batadv_mcast_has_bridge(bat_priv))
+	bridged = batadv_mcast_has_bridge(bat_priv);
+	if (!bridged)
 		goto update;
 
 #if !IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING)
 	pr_warn_once("No bridge IGMP snooping compiled - multicast optimizations disabled\n");
 #endif
 
+	querier_ipv4 = (struct batadv_mcast_querier_state){
+		.exists = br_multicast_has_querier_anywhere(dev, ETH_P_IP),
+		.shadowing = br_multicast_has_querier_adjacent(dev, ETH_P_IP),
+	};
+
+	querier_ipv6 = (struct batadv_mcast_querier_state){
+		.exists = br_multicast_has_querier_anywhere(dev, ETH_P_IPV6),
+		.shadowing = br_multicast_has_querier_adjacent(dev, ETH_P_IPV6),
+	};
+
 	mcast_data.flags |= BATADV_MCAST_WANT_ALL_UNSNOOPABLES;
 
 	/* 1) If no querier exists at all, then multicast listeners on
@@ -327,21 +459,31 @@ static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv)
 	 * In both cases, we will signalize other batman nodes that
 	 * we need all multicast traffic of the according protocol.
 	 */
-	if (!br_multicast_has_querier_anywhere(dev, ETH_P_IP) ||
-	    br_multicast_has_querier_adjacent(dev, ETH_P_IP))
+	if (!querier_ipv4.exists || querier_ipv4.shadowing)
 		mcast_data.flags |= BATADV_MCAST_WANT_ALL_IPV4;
 
-	if (!br_multicast_has_querier_anywhere(dev, ETH_P_IPV6) ||
-	    br_multicast_has_querier_adjacent(dev, ETH_P_IPV6))
+	if (!querier_ipv6.exists || querier_ipv6.shadowing)
 		mcast_data.flags |= BATADV_MCAST_WANT_ALL_IPV6;
 
 update:
+	batadv_mcast_bridge_log(bat_priv, bridged, &querier_ipv4,
+				&querier_ipv6);
+
+	if (memcmp(&bat_priv->mcast.querier_ipv4, &querier_ipv4,
+		   sizeof(querier_ipv4)))
+		bat_priv->mcast.querier_ipv4 = querier_ipv4;
+	if (memcmp(&bat_priv->mcast.querier_ipv6, &querier_ipv6,
+		   sizeof(querier_ipv6)))
+		bat_priv->mcast.querier_ipv6 = querier_ipv6;
+
 	if (!bat_priv->mcast.enabled ||
 	    mcast_data.flags != bat_priv->mcast.flags) {
+		batadv_mcast_flags_log(bat_priv, mcast_data.flags);
 		batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 1,
 					       &mcast_data, sizeof(mcast_data));
 		bat_priv->mcast.flags = mcast_data.flags;
 		bat_priv->mcast.enabled = true;
+		bat_priv->mcast.bridged = bridged;
 	}
 
 	return !(mcast_data.flags &
soft-interface.c +5 -0
index 9bf382d..0265a58 100644
@@ -745,7 +745,12 @@ static int batadv_softif_init_late(struct net_device *dev)
 	atomic_set(&bat_priv->distributed_arp_table, 1);
 #endif
 #ifdef CONFIG_BATMAN_ADV_MCAST
+	bat_priv->mcast.querier_ipv4 = (struct batadv_mcast_querier_state){
+		.exists = false, .shadowing = false };
+	bat_priv->mcast.querier_ipv6 = (struct batadv_mcast_querier_state){
+		.exists = false, .shadowing = false };
 	bat_priv->mcast.flags = BATADV_NO_FLAGS;
+	bat_priv->mcast.bridged = false;
 	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);
types.h +17 -0
index 8854c05..b27c79d 100644
@@ -625,14 +625,28 @@ struct batadv_priv_dat {
 
 #ifdef CONFIG_BATMAN_ADV_MCAST
 /**
+ * struct batadv_mcast_querier_state - IGMP/MLD querier state when bridged
+ * @exists: whether a querier exists in the mesh
+ * @shadowing: if a querier exists, whether it is potentially shadowing
+ *  multicast listeners (i.e. querier is behind our own bridge segment)
+ */
+struct batadv_mcast_querier_state {
+	bool exists;
+	bool shadowing;
+};
+
+/**
  * struct batadv_priv_mcast - per mesh interface mcast data
  * @mla_list: list of multicast addresses we are currently announcing via TT
  * @want_all_unsnoopables_list: a list of orig_nodes wanting all unsnoopable
  *  multicast traffic
  * @want_all_ipv4_list: a list of orig_nodes wanting all IPv4 multicast traffic
  * @want_all_ipv6_list: a list of orig_nodes wanting all IPv6 multicast traffic
+ * @querier_ipv4: the current state of an IGMP querier in the mesh (if bridged)
+ * @querier_ipv6: the current state of an MLD querier in the mesh (if bridged)
  * @flags: the flags we have last sent in our mcast tvlv
  * @enabled: whether the multicast tvlv is currently enabled
+ * @bridged: whether the soft interface currently has a bridge on top
  * @num_disabled: number of nodes that have no mcast tvlv
  * @num_want_all_unsnoopables: number of nodes wanting unsnoopable IP traffic
  * @num_want_all_ipv4: counter for items in want_all_ipv4_list
@@ -645,8 +659,11 @@ struct batadv_priv_mcast {
 	struct hlist_head want_all_unsnoopables_list;
 	struct hlist_head want_all_ipv4_list;
 	struct hlist_head want_all_ipv6_list;
+	struct batadv_mcast_querier_state querier_ipv4;
+	struct batadv_mcast_querier_state querier_ipv6;
 	uint8_t flags;
 	bool enabled;
+	bool bridged;
 	atomic_t num_disabled;
 	atomic_t num_want_all_unsnoopables;
 	atomic_t num_want_all_ipv4;
Signature · 1 line
1.7.10.4
#3 lore raw

Re: [B.A.T.M.A.N.] [PATCHv6 2/3] batman-adv: Adding 'mcast' log level

Sorry, it seems the quotation is a little messed up, but I hope you can understand the rest:

Quoted 20 lines

This patch adds an 'mcast' log level. Currently, it will print changes relevant to a nodes own multicast flag changes.

---
 multicast.c      |  152
++++++++++++++++++++++++++++++++++++++++++++++++++++-- soft-interface.c | 
  5 ++
 types.h          |   17 ++++++
 3 files changed, 169 insertions(+), 5 deletions(-)
multicast.c +1 -0
index c05f64a..2a80997 100644
@@ -292,6 +292,123 @@ static bool batadv_mcast_has_bridge(struct
 batadv_priv *bat_priv) }
 
 /**
+ * batadv_mcast_querier_log - debug output regarding the querier status on
Quoted 4 lines

link + * @bat_priv: the bat priv with all the soft interface information +
* @str_proto: a string for the querier protocol (e.g. "IGMP" or "MLD") + * @old_querier: the previous querier state on our link
+ * @new_querier: the new querier state on our link

I think changing the variable name from old/new_querier to old/new_state would make this part more understandable - there are not two different queriers after all, just their state changes.

Quoted 8 lines

+ *
+ * Outputs debug messages to the logging facility with log level 'mcast'
+ * regarding changes to the querier status on the link which are relevant
+ * to our multicast optimizations.
+ *
+ * Usually this is about whether a querier appeared or vanished in
+ * our mesh or whether the querier is in the suboptimal position of being
+ * behind our local bridge segment.

Maybe it would be good to briefly explain at that point again why it's suboptimal?

Quoted 15 lines

+ *
+ * This is only interesting for nodes with a bridge on top of their
+ * soft interface.
+ */

multicast.c +11 -0
+static void
+batadv_mcast_querier_log(struct batadv_priv *bat_priv, char *str_proto,
+			 struct batadv_mcast_querier_state *old_querier,
+			 struct batadv_mcast_querier_state *new_querier)
+{
+	if (!old_querier->exists && new_querier->exists)
+		batadv_dbg(BATADV_DBG_MCAST, bat_priv,
+			   "%s Querier appeared (good!)\n", str_proto);
+	else if (old_querier->exists && !new_querier->exists)
+		batadv_dbg(BATADV_DBG_MCAST, bat_priv,
+			   "%s Querier disappeared (bad)\n", str_proto);

multicast.c static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv)

Quoted 10 lines
multicast.c +10 -0
+	else if (!bat_priv->mcast.bridged && !new_querier->exists)
+		batadv_dbg(BATADV_DBG_MCAST, bat_priv,
+			   "Note: No %s Querier present\n", str_proto);
+
+	if (!new_querier->exists)
+		return;
+
+	if (!old_querier->shadowing && new_querier->shadowing)
+		batadv_dbg(BATADV_DBG_MCAST, bat_priv,
+			   "%s Querier is behind our bridged segment: Might shadow 

listeners

multicast.c static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv) · line 337

Quoted 5 lines

(bad)\n", + str_proto);

multicast.c +4 -0
+	else if (old_querier->shadowing && !new_querier->shadowing)
+		batadv_dbg(BATADV_DBG_MCAST, bat_priv,
+			   "%s Querier is not behind our bridged segment (good!)\n",
+			   str_proto);

I'm not sure if good/bad is really useful to write here - at least we don't really tell why its good or bad?

Quoted 11 lines

[...]
+/**
+ * batadv_mcast_flags_logs - output debug information about mcast flag changes + * @bat_priv: the bat priv with all the soft interface information + * @mcast_flags: flags indicating the new multicast state
+ *
+ * Whenever the multicast flags this nodes announces changes (@mcast_flags vs. + * bat_priv->mcast.flags), this notifies userspace via the 'mcast' log level. + */
+static void batadv_mcast_flags_log(struct batadv_priv *bat_priv, uint8_t flags) +{

multicast.c static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv) · line 389

Quoted 3 lines
multicast.c +3 -0
+	char str_undefined[] = "<undefined>";
+	char str_flags[] = "[...]";
+	char *str_flags_old = str_undefined;

Can't you write str_flags_old = "<undefined>" directly? why that indirection?

multicast.c static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv) · line 400

Quoted 11 lines
multicast.c +11 -0
+	uint8_t old_flags = bat_priv->mcast.flags;
+
+	if (!bat_priv->mcast.enabled)
+		goto print;
+
+	str_flags_old = str_flags;
+
+	sprintf(str_flags_old, "[%c%c%c]",
+		old_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES ? 'U' : '.',
+		old_flags & BATADV_MCAST_WANT_ALL_IPV4 ? '4' : '.',
+		old_flags & BATADV_MCAST_WANT_ALL_IPV6 ? '6' : '.');

Ah, you want to reuse this buffer? hmm ...

multicast.c static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv) · line 408

Quoted 8 lines
multicast.c +8 -0
+
+print:
+	batadv_dbg(BATADV_DBG_MCAST, bat_priv,
+		   "Changing multicast flags from '%s' to '[%c%c%c]'\n",
+		   str_flags_old,
+		   flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES ? 'U' : '.',
+		   flags & BATADV_MCAST_WANT_ALL_IPV4 ? '4' : '.',
+		   flags & BATADV_MCAST_WANT_ALL_IPV6 ? '6' : '.');

I think this overly complex to do.

How about always writing str_flags_old accoding to old flags, but then do:

batadv_dbg(BATADV_DBG_MCAST, bat_priv,

		   "Changing multicast flags from '%s' to '[%c%c%c]'\n",
		   bat_priv->mcast.enabled ? str_flags_old : "<undefined>",
		   flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES ? 'U' : '.',
		   flags & BATADV_MCAST_WANT_ALL_IPV4 ? '4' : '.',
		   flags & BATADV_MCAST_WANT_ALL_IPV6 ? '6' : '.');
Quoted 9 lines
multicast.c +3 -0
+}
+
+/**
  * batadv_mcast_mla_tvlv_update - update multicast tvlv
  * @bat_priv: the bat priv with all the soft interface information
  *
@@ -304,18 +421,33 @@ static bool batadv_mcast_has_bridge(struct

batadv_priv *bat_priv) static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv) {

multicast.c static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv) · line 431

Quoted 9 lines
multicast.c +4 -0
 	struct batadv_tvlv_mcast_data mcast_data;
+	struct batadv_mcast_querier_state querier_ipv4, querier_ipv6;
 	struct net_device *dev = bat_priv->soft_iface;
+	bool bridged;
 
 	mcast_data.flags = BATADV_NO_FLAGS;
 	memset(mcast_data.reserved, 0, sizeof(mcast_data.reserved));
+	memset(&querier_ipv4, 0, sizeof(querier_ipv4));
+	memset(&querier_ipv6, 0, sizeof(querier_ipv6));

since it just has two elements, you could do that above in the initialization:

struct batadv_mcast_querier_state querier_ipv4 = {false, false}; struct batadv_mcast_querier_state querier_ipv6 = {false, false};

Quoted 10 lines
multicast.c +2 -1
-	if (!batadv_mcast_has_bridge(bat_priv))
+	bridged = batadv_mcast_has_bridge(bat_priv);
+	if (!bridged)
 		goto update;
 
 #if !IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING)
 	pr_warn_once("No bridge IGMP snooping compiled - multicast optimizations

disabled\n"); #endif

multicast.c static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv) · line 450

Quoted 10 lines
multicast.c +10 -0
+	querier_ipv4 = (struct batadv_mcast_querier_state){
+		.exists = br_multicast_has_querier_anywhere(dev, ETH_P_IP),
+		.shadowing = br_multicast_has_querier_adjacent(dev, ETH_P_IP),
+	};
+
+	querier_ipv6 = (struct batadv_mcast_querier_state){
+		.exists = br_multicast_has_querier_anywhere(dev, ETH_P_IPV6),
+		.shadowing = br_multicast_has_querier_adjacent(dev, ETH_P_IPV6),
+	};
+

That would be shorter and more readable by just writing:

querier_ipv4.exists = br_multicast_has_querier_anywhere(dev, ETH_P_IP); querier_ipv4.shadowing = br_multicast_has_querier_adjacent(dev, ETH_P_IP);

querier_ipv6.exists = br_multicast_has_querier_anywhere(dev, ETH_P_IPV6); querier_ipv6.shadowing = br_multicast_has_querier_adjacent(dev, ETH_P_IPV6);

Quoted 19 lines
multicast.c +0 -0
 	mcast_data.flags |= BATADV_MCAST_WANT_ALL_UNSNOOPABLES;
 
 	/* 1) If no querier exists at all, then multicast listeners on
@@ -327,21 +459,31 @@ static bool batadv_mcast_mla_tvlv_update(struct

batadv_priv *bat_priv) * In both cases, we will signalize other batman nodes that

multicast.c +2 -4
 	 * we need all multicast traffic of the according protocol.
 	 */
-	if (!br_multicast_has_querier_anywhere(dev, ETH_P_IP) ||
-	    br_multicast_has_querier_adjacent(dev, ETH_P_IP))
+	if (!querier_ipv4.exists || querier_ipv4.shadowing)
 		mcast_data.flags |= BATADV_MCAST_WANT_ALL_IPV4;
 
-	if (!br_multicast_has_querier_anywhere(dev, ETH_P_IPV6) ||
-	    br_multicast_has_querier_adjacent(dev, ETH_P_IPV6))
+	if (!querier_ipv6.exists || querier_ipv6.shadowing)
 		mcast_data.flags |= BATADV_MCAST_WANT_ALL_IPV6;
 
 update:

multicast.c static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv) · line 478

Quoted 10 lines
multicast.c +10 -0
+	batadv_mcast_bridge_log(bat_priv, bridged, &querier_ipv4,
+				&querier_ipv6);
+
+	if (memcmp(&bat_priv->mcast.querier_ipv4, &querier_ipv4,
+		   sizeof(querier_ipv4)))
+		bat_priv->mcast.querier_ipv4 = querier_ipv4;
+	if (memcmp(&bat_priv->mcast.querier_ipv6, &querier_ipv6,
+		   sizeof(querier_ipv6)))
+		bat_priv->mcast.querier_ipv6 = querier_ipv6;
+

Why comparing and then assigning? You could assign always instead, that should eventually not cost more than this construction.

Quoted 16 lines
multicast.c +2 -0
 	if (!bat_priv->mcast.enabled ||
 	    mcast_data.flags != bat_priv->mcast.flags) {
+		batadv_mcast_flags_log(bat_priv, mcast_data.flags);
 		batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 1,
 					       &mcast_data, sizeof(mcast_data));
 		bat_priv->mcast.flags = mcast_data.flags;
 		bat_priv->mcast.enabled = true;
+		bat_priv->mcast.bridged = bridged;
 	}
 
 	return !(mcast_data.flags &
soft-interface.c +0 -0
index 9bf382d..0265a58 100644

soft-interface.c static int batadv_softif_init_late(struct net_device · line 751

Quoted 7 lines
soft-interface.c +4 -0
@@ -745,7 +745,12 @@ static int batadv_softif_init_late(struct net_device
 *dev) atomic_set(&bat_priv->distributed_arp_table, 1);
 #endif
 #ifdef CONFIG_BATMAN_ADV_MCAST
+	bat_priv->mcast.querier_ipv4 = (struct batadv_mcast_querier_state){
+		.exists = false, .shadowing = false };
+	bat_priv->mcast.querier_ipv6 = (struct batadv_mcast_querier_state){
+		.exists = false, .shadowing = false };

yet again, just do:

bat_priv->mcast.querier_ipv4.exists = false;
bat_priv->mcast.querier_ipv4.shadowing = false;
bat_priv->mcast.querier_ipv6.exists = false;
bat_priv->mcast.querier_ipv6.shadowing = false;

[...]
Cheers,
Simon

#4 lore raw

PATCH v6 3/3 batman-adv: Add debugfs table for mcast flags

In reply to Linus Lüssing

This patch adds a debugfs table with originators and their according multicast flags to help users figure out why multicast optimizations might be enabled or disabled for them.

---
 debugfs.c   |   21 ++++++++++++
 multicast.c |  110 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 multicast.h |    2 ++
 3 files changed, 133 insertions(+)
debugfs.c +21 -0
index a12e25e..cd7c282 100644
@@ -30,6 +30,7 @@
 #include "bridge_loop_avoidance.h"
 #include "distributed-arp-table.h"
 #include "network-coding.h"
+#include "multicast.h"
 
 static struct dentry *batadv_debugfs;
 
@@ -332,6 +333,20 @@ static int batadv_nc_nodes_open(struct inode *inode, struct file *file)
 }
 #endif
 
+#ifdef CONFIG_BATMAN_ADV_MCAST
+/**
+ * batadv_mcast_flags_open - prepare file handler for reads from mcast_flags
+ * @inode: inode which was opened
+ * @file: file handle to be initialized
+ */
+static int batadv_mcast_flags_open(struct inode *inode, struct file *file)
+{
+	struct net_device *net_dev = (struct net_device *)inode->i_private;
+
+	return single_open(file, batadv_mcast_flags_seq_print_text, net_dev);
+}
+#endif
+
 #define BATADV_DEBUGINFO(_name, _mode, _open)		\
 struct batadv_debuginfo batadv_debuginfo_##_name = {	\
 	.attr = { .name = __stringify(_name),		\
@@ -372,6 +387,9 @@ static BATADV_DEBUGINFO(transtable_local, S_IRUGO,
 #ifdef CONFIG_BATMAN_ADV_NC
 static BATADV_DEBUGINFO(nc_nodes, S_IRUGO, batadv_nc_nodes_open);
 #endif
+#ifdef CONFIG_BATMAN_ADV_MCAST
+static BATADV_DEBUGINFO(mcast_flags, S_IRUGO, batadv_mcast_flags_open);
+#endif
 
 static struct batadv_debuginfo *batadv_mesh_debuginfos[] = {
 	&batadv_debuginfo_originators,
@@ -388,6 +406,9 @@ static struct batadv_debuginfo *batadv_mesh_debuginfos[] = {
 #ifdef CONFIG_BATMAN_ADV_NC
 	&batadv_debuginfo_nc_nodes,
 #endif
+#ifdef CONFIG_BATMAN_ADV_MCAST
+	&batadv_debuginfo_mcast_flags,
+#endif
 	NULL,
 };
 
multicast.c +110 -0

116 lines not on this page · raw message

multicast.h +2 -0
index 73b5d45..5344160 100644
@@ -42,6 +42,8 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
 
 void batadv_mcast_init(struct batadv_priv *bat_priv);
 
+int batadv_mcast_flags_seq_print_text(struct seq_file *seq, void *offset);
+
 void batadv_mcast_free(struct batadv_priv *bat_priv);
 
 void batadv_mcast_purge_orig(struct batadv_orig_node *orig_node);
Signature · 1 line
1.7.10.4
#5 lore raw

Re: [B.A.T.M.A.N.] [PATCHv6 3/3] batman-adv: Add debugfs table for mcast flags

Quoted 21 lines
multicast.c +1 -0
@@ -995,6 +995,116 @@ void batadv_mcast_init(struct batadv_priv *bat_priv)
 }
 
 /**
+ * batadv_mcast_flags_print_header - print own mcast flags to debugfs

table + * @bat_priv: the bat priv with all the soft interface information
+ * @seq: debugfs table seq_file struct
+ *
+ * Prints our own multicast flags including a more specific reason why
+ * they are set, that is prints the bridge and querier state too, to
+ * the debugfs table specified via @seq.
+ */

multicast.c +7 -0
+static void batadv_mcast_flags_print_header(struct batadv_priv *bat_priv,
+					    struct seq_file *seq)
+{
+	struct net_device *dev = bat_priv->soft_iface;
+	uint8_t flags = bat_priv->mcast.flags;
+	char ip4_querier_c, ip6_querier_c, ip4_shadowed_c, ip6_shadowed_c;
+	bool bridged = batadv_mcast_has_bridge(bat_priv);

multicast.c void batadv_mcast_init(struct batadv_priv *bat_priv) · line 1022

Quoted 10 lines
multicast.c +10 -0
+
+	if (bridged) {
+		bool ip4_querier, ip6_querier, ip4_shadowed, ip6_shadowed;
+
+		ip4_querier = br_multicast_has_querier_anywhere(dev, ETH_P_IP);
+		ip6_querier = br_multicast_has_querier_anywhere(dev,
+								ETH_P_IPV6);
+		ip4_shadowed = br_multicast_has_querier_adjacent(dev, ETH_P_IP);
+		ip6_shadowed = br_multicast_has_querier_adjacent(dev,
+								 ETH_P_IPV6);

Isn't this info already in bat_priv->querier_ipv4/v6->exists/shadowed? Should avoid the 80 lines workarounds you are having here, or maybe you are able to squash that with the lines below.

Quoted 7 lines
multicast.c +7 -0
+
+		ip4_querier_c = !ip4_querier ? '+' : '-';
+		ip6_querier_c = !ip6_querier ? '+' : '-';
+		ip4_shadowed_c = ip4_shadowed ? '+' : '-';
+		ip6_shadowed_c = ip6_shadowed ? '+' : '-';
+	} else {
+		ip4_querier_c = '?';

multicast.c void batadv_mcast_init(struct batadv_priv *bat_priv) · line 1039

Quoted 10 lines
multicast.c +10 -0
+		ip6_querier_c = '?';
+		ip4_shadowed_c = '?';
+		ip6_shadowed_c = '?';
+	}
+
+	seq_printf(seq, "Multicast flags (own flags: [%c%c%c])\n",
+		   flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES ? 'U' : '.',
+		   flags & BATADV_MCAST_WANT_ALL_IPV4 ? '4' : '.',
+		   flags & BATADV_MCAST_WANT_ALL_IPV6 ? '6' : '.');
+	seq_printf(seq, "* Bridged [U]\t\t\t\t%c\n", bridged ? '+' : '-');

What does the U mean? Unsnoopables? The U-flag? (just noticed that this "U- Flag" is also used without explanation in the previous patch).

Quoted 61 lines
multicast.c +10 -0
+	seq_printf(seq, "* No IGMP/MLD Querier [4/6]:\t\t%c/%c\n",
+		   ip4_querier_c, ip6_querier_c);
+	seq_printf(seq, "* Shadowing IGMP/MLD Querier [4/6]:\t%c/%c\n",
+		   ip4_shadowed_c, ip6_shadowed_c);
+	seq_puts(seq, "-------------------------------------------\n");
+	seq_printf(seq, "       %-10s %s\n", "Originator", "Flags");
+}
+
+/**
+ * batadv_mcast_flags_seq_print_text - print the mcast flags of other

nodes + * @seq: seq file to print on
+ * @offset: not used
+ *
+ * This prints a table of (primary) originators and their according
+ * multicast flags, including (in the header) our own.
+ */

multicast.c +45 -0
+int batadv_mcast_flags_seq_print_text(struct seq_file *seq, void *offset)
+{
+	struct net_device *net_dev = (struct net_device *)seq->private;
+	struct batadv_priv *bat_priv = netdev_priv(net_dev);
+	struct batadv_hard_iface *primary_if;
+	struct batadv_hashtable *hash = bat_priv->orig_hash;
+	struct batadv_orig_node *orig_node;
+	struct hlist_head *head;
+	uint8_t flags;
+	uint32_t i;
+
+	primary_if = batadv_seq_print_text_primary_if_get(seq);
+	if (!primary_if)
+		return 0;
+
+	batadv_mcast_flags_print_header(bat_priv, seq);
+
+	for (i = 0; i < hash->size; i++) {
+		head = &hash->table[i];
+
+		rcu_read_lock();
+		hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
+			if (!(orig_node->capa_initialized &
+			      BATADV_ORIG_CAPA_HAS_MCAST))
+				continue;
+
+			flags = orig_node->mcast_flags;
+
+			if (!(orig_node->capabilities &
+			      BATADV_ORIG_CAPA_HAS_MCAST)) {
+				seq_printf(seq, "%pM -\n", orig_node->orig);
+				continue;
+			}
+
+			seq_printf(seq, "%pM [%c%c%c]\n", orig_node->orig,
+				   (flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES ?
+				    'U' : '.'),
+				   (flags & BATADV_MCAST_WANT_ALL_IPV4 ?
+				    '4' : '.'),
+				   (flags & BATADV_MCAST_WANT_ALL_IPV6 ?
+				    '6' : '.'));
+		}
+		rcu_read_unlock();
+	}
+

multicast.c void batadv_mcast_init(struct batadv_priv *bat_priv) · line 1110

Quoted 10 lines
multicast.c +7 -0
+	batadv_hardif_free_ref(primary_if);
+
+	return 0;
+}
+
+
+/**
  * batadv_mcast_free - free the multicast optimizations structures
  * @bat_priv: the bat priv with all the soft interface information
  */

The rest looks good ... I'll need to test all that later. :)

Thanks,
Simon