All of lore.kernel.org
 help / color / mirror / Atom feed
* [B.A.T.M.A.N.] [PATCHv12 0/4] Multicast optimizations for bridges
@ 2016-03-16  4:06 Linus Lüssing
  2016-03-16  4:06 ` [B.A.T.M.A.N.] [PATCHv12 1/4] batman-adv: Always flood IGMP/MLD reports Linus Lüssing
                   ` (3 more replies)
  0 siblings, 4 replies; 14+ messages in thread
From: Linus Lüssing @ 2016-03-16  4:06 UTC (permalink / raw)
  To: b.a.t.m.a.n

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

The first one alters the forwarding behaviour for IGMP and MLD reports
which is a prerequisite before enabling multicast optimizations in
bridged setups. It also increases the MCAST TVLV version number to 2.
More on the issue of insufficient report handling with the v1
MCAST TVLV can be found here:

http://www.open-mesh.org/projects/batman-adv/wiki/Multicast-optimizations-listener-reports

The second patch finally enables the multicast optimizations for
bridged setups.

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.


This patchset can be found in the current linus/multicast-bridge
branch.

Cheers, Linus


-----

Changes in v12:
* Rebase to current master branch
  (automatic 3-way merge, then batadv_hardif_free_ref() -> batadv_hardif_put())
* Adding Linux upstream fix to compat-sources/net/core/skbuff.c
  -> "net: fix bridge multicast packet checksum validation"
     (9b368814b33, present since v4.5, queued for stable)

Changes in v11:
* Rewording of commit messages (PATCH 1/4+2/4)
* Rebasing to master (solving conflict with kernel doc "return" changes)
* Updating kerneldoc to proper "Return:" syntax
* Using eth_zero_addr() over memset (fixes a new checkpatch warning)
* Changing uint8_t to u8 and uint32_t to u32 (fixes new checkpatch warnings)
* Turning some batadv_dbg() into batadv_info(), making querier presence more
  visible (i.e. in dmesg too) (PATCH 3/4)

Changes in v10:
- PATCH 1/4:
* Former "[PATCH 4/4] batman-adv: Forward IGMP/MLD reports to selected querier (only)"
  substituted by the interim branch "batman-adv: Always flood IGMP/MLD reports"
  with the following changes:
  * Increased compatibility to mcast-v1-tvlv nodes by
    registering a v1 tvlv container and handler, too
    (note: PATCH 2/4 needs to still unregister the handler
     if the node is bridged bc. it can't "trust" v1 nodes)
  * Removed skb_set_network_header() call
    (extra patch pending for review for maint)
* Compared to patchset v9 this substitution makes things
  compilable again for kernels < 2.6.35
- PATCH 2/4:
* Moved enum introduction of BATADV_DBG_MCAST to PATCH 3/4,
  not needed in PATCH 2/4 yet
* Adjusted compat code to new compat layout
* Adjustments to batadv_mcast_mla_tvlv_update() to fit now
  preceding PATCH 1/4
* bat_priv->mcast.bridged flag already introduced here
  (instead of in PATCH 3/4) because we need it for the new
  mcast-v1-tvlv handling

---
  Interim Changlog of
  "batman-adv: Always flood IGMP/MLD reports"

  v1:
   * Removed query snooping and state
   * Squashed all three patches into one
   * Renamed "batadv_mcast_tvlv_ogm_handler_v1()" to *_v2()
   * Added explicit icmpv6.h include
   * Rebased on top of master

-----
    Interim Interim Changelog of
    "batman-adv: Unicasting multicast reports to querier-node only"
    v6:
     * compat: copied copyright headers from original upstream c files
     * compat: unified ordering in compat c files:
       -> copyright header, then includes, then kernel specific functions
    v5:
     * Removed RFC tag: Needed exports got merged to net-next and are going to
       be available with Linux 4.2
     * Redid compat solution - now fully backwards compatible down to 2.6.33
    v4:
     * excluded bridge part from this patchset, they should
       hopefully be added to net-next soon
     * Added a compat solution (PATCH 3/3)
     * Removed Kconfig-depends as by David's suggestion the needed parsing
       functions for MLD are going to be forced built-ins even if IPv6 is
       going to be built as a module
     * Removed unused variable 'int ret' in batadv_mcast_is_report_ipv6()
     * Adjusted to new folder structure
    v3:
     * Adding Kconfig-depends and #if's
       (so basically adding similar dependancy constraints as the bridge code
        has, except that there are no depends if batman-adv gets compiled without
        multicast optimizations)
       -> the case of IPv6=M and batman-adv=y is still impossible if multicast
          optimizations are enabled; but I don't see the practical demand for that
          either - people who use IPv6 as a module will probably also want to
          use batman-adv as a module
    v2:
     * various bugfixes (now runtime tested, too - should(tm) work)
     * added netdev+bridge mailinglists
-----
---

Changes in v9:
- PATCH 1/4:
* fix: added compat code for pr_warn_once()
* compat fix for bridge export stubs: fixes compile error
  with kernels < 3.16 without bridge (snooping) support
- PATCH 2/4:
* perform updates of variables within bat_priv->mcast.querier_ipv{4,6}
  individually (there's a new, third member in 4/4 which shouldn't be
  overriden)
* PATCH 4/4: NEW

Changes in v8 (thanks to Simon's suggestions):
- PATCH 2/3:
* print shadowing status log of an appearing and shadowing querier, too
  (the bridge-querier-existence call has an additional 10s delay
   to ensure reports had their time to arrive -
   the bridge-querier-port call doesn't have that)
- PATCH 3/3:
* changing debugfs output from "+" and "-" to "U/4/6" and "."
* fixing "no querier present" logic (introduced in [PATCHv7 3/3])

Changes in v7 (thanks to Simon's suggestions):
- PATCH 2/3:
* renaming old/new_querier to old/new_state
* slightly extended kerneldoc of batadv_mcast_querier_log()
* removing words "good" and "bad" from debug output
* simplified batadv_mcast_flags_log()
* assignment instead of memset in batadv_mcast_mla_tvlv_update()
  and batadv_softif_init_late()
* simple struct member assignments instead of one complex struct
  assigment
* removing unnecessary memcmp's
* substituting return statement for an if-block in
  batadv_mcast_querier_log() and batadv_mcast_bridge_log()
* print "Unsnoopables(U)-flag" instead of just "U-flag"
- PATCH 3/3:
* use bat_priv values instead of querying bridge ABI in
  batadv_mcast_flags_print_header()

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


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [B.A.T.M.A.N.] [PATCHv12 1/4] batman-adv: Always flood IGMP/MLD reports
  2016-03-16  4:06 [B.A.T.M.A.N.] [PATCHv12 0/4] Multicast optimizations for bridges Linus Lüssing
@ 2016-03-16  4:06 ` Linus Lüssing
  2016-04-07 13:03   ` Simon Wunderlich
  2016-03-16  4:06 ` [B.A.T.M.A.N.] [PATCHv12 2/4] batman-adv: Add multicast optimization support for bridged setups Linus Lüssing
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 14+ messages in thread
From: Linus Lüssing @ 2016-03-16  4:06 UTC (permalink / raw)
  To: b.a.t.m.a.n

With this patch IGMP or MLD reports are always flooded. This is
necessary for the upcoming bridge integration to function without
multicast packet loss.

With the report handling so far bridges might miss interested multicast
listeners, leading to wrongly excluding ports from multicast packet
forwarding.

Currently we are treating IGMP/MLD reports, the messages bridges use to
learn about interested multicast listeners, just as any other multicast
packet: We try to send them to nodes matching its multicast destination.

Unfortunately, the destination address of reports of the older
IGMPv2/MLDv1 protocol families do not strictly adhere to their own
protocol: More precisely, the interested receiver, an IGMPv2 or MLDv1
querier, itself usually does not listen to the multicast destination
address of any reports.

Therefore with this patch we are simply excluding IGMP/MLD reports from
the multicast forwarding code path and keep flooding them. By that
any bridge receives them and can properly learn about listeners.

To avoid compatibility issues with older nodes not yet implementing this
report handling, we need to force them to flood reports: We do this by
bumping the multicast TVLV version to 2, effectively disabling their
multicast optimization.

Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
---
 Makefile                              |    4 +
 compat-include/linux/igmp.h           |   13 ++
 compat-include/linux/skbuff.h         |   14 ++
 compat-include/net/addrconf.h         |   13 ++
 compat-include/net/ip6_checksum.h     |   18 +++
 compat-include/net/ipv6.h             |   17 +++
 compat-include/net/mld.h              |   52 +++++++
 compat-sources/Makefile               |    3 +
 compat-sources/net/core/skbuff.c      |  214 +++++++++++++++++++++++++++++
 compat-sources/net/ipv4/igmp.c        |  241 +++++++++++++++++++++++++++++++++
 compat-sources/net/ipv6/mcast_snoop.c |  216 +++++++++++++++++++++++++++++
 net/batman-adv/multicast.c            |  120 ++++++++++++++--
 12 files changed, 916 insertions(+), 9 deletions(-)
 create mode 100644 compat-include/linux/igmp.h
 create mode 100644 compat-include/net/addrconf.h
 create mode 100644 compat-include/net/ip6_checksum.h
 create mode 100644 compat-include/net/ipv6.h
 create mode 100644 compat-include/net/mld.h
 create mode 100644 compat-sources/Makefile
 create mode 100644 compat-sources/net/core/skbuff.c
 create mode 100644 compat-sources/net/ipv4/igmp.c
 create mode 100644 compat-sources/net/ipv6/mcast_snoop.c

diff --git a/Makefile b/Makefile
index 5d2c058..fd6237c 100644
--- a/Makefile
+++ b/Makefile
@@ -52,6 +52,10 @@ ifneq ($(REVISION),)
 NOSTDINC_FLAGS += -DBATADV_SOURCE_VERSION=\"$(REVISION)\"
 endif
 
+include $(PWD)/compat-sources/Makefile
+
+export batman-adv-y
+
 BUILD_FLAGS := \
 	M=$(PWD)/net/batman-adv \
 	CONFIG_BATMAN_ADV=m \
diff --git a/compat-include/linux/igmp.h b/compat-include/linux/igmp.h
new file mode 100644
index 0000000..f61ab79
--- /dev/null
+++ b/compat-include/linux/igmp.h
@@ -0,0 +1,13 @@
+#ifndef _NET_BATMAN_ADV_COMPAT_LINUX_IGMP_H_
+#define _NET_BATMAN_ADV_COMPAT_LINUX_IGMP_H_
+
+#include <linux/version.h>
+#include_next <linux/igmp.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
+
+int ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed);
+
+#endif /* < KERNEL_VERSION(4, 2, 0) */
+
+#endif	/* _NET_BATMAN_ADV_COMPAT_LINUX_IGMP_H_ */
diff --git a/compat-include/linux/skbuff.h b/compat-include/linux/skbuff.h
index 96b018c..f4ae619 100644
--- a/compat-include/linux/skbuff.h
+++ b/compat-include/linux/skbuff.h
@@ -89,6 +89,20 @@ static inline void skb_reset_mac_len(struct sk_buff *skb)
 
 #define pskb_copy_for_clone pskb_copy
 
+__sum16 skb_checksum_simple_validate(struct sk_buff *skb);
+
+__sum16
+skb_checksum_validate(struct sk_buff *skb, int proto,
+		      __wsum (*compute_pseudo)(struct sk_buff *skb, int proto));
+
 #endif /* < KERNEL_VERSION(3, 16, 0) */
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
+
+struct sk_buff *skb_checksum_trimmed(struct sk_buff *skb,
+				     unsigned int transport_len,
+				     __sum16(*skb_chkf)(struct sk_buff *skb));
+
+#endif /* < KERNEL_VERSION(4, 2, 0) */
+
 #endif	/* _NET_BATMAN_ADV_COMPAT_LINUX_SKBUFF_H_ */
diff --git a/compat-include/net/addrconf.h b/compat-include/net/addrconf.h
new file mode 100644
index 0000000..69c45d0
--- /dev/null
+++ b/compat-include/net/addrconf.h
@@ -0,0 +1,13 @@
+#ifndef _NET_BATMAN_ADV_COMPAT_NET_ADDRCONF_H_
+#define _NET_BATMAN_ADV_COMPAT_NET_ADDRCONF_H_
+
+#include <linux/version.h>
+#include_next <net/addrconf.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
+
+int ipv6_mc_check_mld(struct sk_buff *skb, struct sk_buff **skb_trimmed);
+
+#endif /* < KERNEL_VERSION(4, 2, 0) */
+
+#endif	/* _NET_BATMAN_ADV_COMPAT_NET_ADDRCONF_H_ */
diff --git a/compat-include/net/ip6_checksum.h b/compat-include/net/ip6_checksum.h
new file mode 100644
index 0000000..fda0c07
--- /dev/null
+++ b/compat-include/net/ip6_checksum.h
@@ -0,0 +1,18 @@
+#ifndef _NET_BATMAN_ADV_COMPAT_NET_IP6_CHECKSUM_H_
+#define _NET_BATMAN_ADV_COMPAT_NET_IP6_CHECKSUM_H_
+
+#include <linux/version.h>
+#include_next <net/ip6_checksum.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0)
+
+static inline __wsum ip6_compute_pseudo(struct sk_buff *skb, int proto)
+{
+	return ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+					    &ipv6_hdr(skb)->daddr,
+					    skb->len, proto, 0));
+}
+
+#endif /* < KERNEL_VERSION(3, 16, 0) */
+
+#endif	/* _NET_BATMAN_ADV_COMPAT_NET_IP6_CHECKSUM_H_ */
diff --git a/compat-include/net/ipv6.h b/compat-include/net/ipv6.h
new file mode 100644
index 0000000..1e190d8
--- /dev/null
+++ b/compat-include/net/ipv6.h
@@ -0,0 +1,17 @@
+#ifndef _NET_BATMAN_ADV_COMPAT_NET_IPV6_H_
+#define _NET_BATMAN_ADV_COMPAT_NET_IPV6_H_
+
+#include <linux/version.h>
+#include_next <net/ipv6.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+
+#define ipv6_skip_exthdr(skb, start, nexthdrp, frag_offp) \
+	({ \
+		(void)frag_offp; \
+		ipv6_skip_exthdr(skb, start, nexthdrp); \
+	})
+
+#endif /* < KERNEL_VERSION(3, 3, 0) */
+
+#endif /* _NET_BATMAN_ADV_COMPAT_NET_IPV6_H_ */
diff --git a/compat-include/net/mld.h b/compat-include/net/mld.h
new file mode 100644
index 0000000..e041eb6
--- /dev/null
+++ b/compat-include/net/mld.h
@@ -0,0 +1,52 @@
+#ifndef _NET_BATMAN_ADV_COMPAT_NET_MLD_H_
+#define _NET_BATMAN_ADV_COMPAT_NET_MLD_H_
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
+#include_next <net/mld.h>
+#endif /* >= KERNEL_VERSION(2, 6, 35) */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+struct mld_msg {
+	struct icmp6hdr		mld_hdr;
+	struct in6_addr		mld_mca;
+};
+
+#define mld_type		mld_hdr.icmp6_type
+
+struct mld2_grec {
+	__u8		grec_type;
+	__u8		grec_auxwords;
+	__be16		grec_nsrcs;
+	struct in6_addr	grec_mca;
+	struct in6_addr	grec_src[0];
+};
+
+struct mld2_report {
+	struct icmp6hdr		mld2r_hdr;
+	struct mld2_grec	mld2r_grec[0];
+};
+
+struct mld2_query {
+	struct icmp6hdr		mld2q_hdr;
+	struct in6_addr		mld2q_mca;
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8			mld2q_qrv:3,
+				mld2q_suppress:1,
+				mld2q_resv2:4;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	__u8			mld2q_resv2:4,
+				mld2q_suppress:1,
+				mld2q_qrv:3;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+	__u8			mld2q_qqic;
+	__be16			mld2q_nsrcs;
+	struct in6_addr		mld2q_srcs[0];
+};
+
+#endif /* < KERNEL_VERSION(2, 6, 35) */
+
+#endif	/* _NET_BATMAN_ADV_COMPAT_NET_MLD_H_ */
diff --git a/compat-sources/Makefile b/compat-sources/Makefile
new file mode 100644
index 0000000..c364ded
--- /dev/null
+++ b/compat-sources/Makefile
@@ -0,0 +1,3 @@
+batman-adv-y += ../../compat-sources/net/core/skbuff.o
+batman-adv-y += ../../compat-sources/net/ipv4/igmp.o
+batman-adv-y += ../../compat-sources/net/ipv6/mcast_snoop.o
diff --git a/compat-sources/net/core/skbuff.c b/compat-sources/net/core/skbuff.c
new file mode 100644
index 0000000..801c6cf
--- /dev/null
+++ b/compat-sources/net/core/skbuff.c
@@ -0,0 +1,214 @@
+/*
+ *	Routines having to do with the 'struct sk_buff' memory handlers.
+ *
+ *	Authors:	Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *			Florian La Roche <rzsfl@rz.uni-sb.de>
+ *
+ *	Fixes:
+ *		Alan Cox	:	Fixed the worst of the load
+ *					balancer bugs.
+ *		Dave Platt	:	Interrupt stacking fix.
+ *	Richard Kooijman	:	Timestamp fixes.
+ *		Alan Cox	:	Changed buffer format.
+ *		Alan Cox	:	destructor hook for AF_UNIX etc.
+ *		Linus Torvalds	:	Better skb_clone.
+ *		Alan Cox	:	Added skb_copy.
+ *		Alan Cox	:	Added all the changed routines Linus
+ *					only put in the headers
+ *		Ray VanTassle	:	Fixed --skb->lock in free
+ *		Alan Cox	:	skb_copy copy arp field
+ *		Andi Kleen	:	slabified it.
+ *		Robert Olsson	:	Removed skb_head_pool
+ *
+ *	NOTE:
+ *		The __skb_ routines should be called with interrupts
+ *	disabled, or you better be *real* sure that the operation is atomic
+ *	with respect to whatever list is being frobbed (e.g. via lock_sock()
+ *	or via disabling bottom half handlers, etc).
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/ipv6.h>
+#include <linux/skbuff.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0)
+
+/* Compare with:
+ * "bridge: multicast: call skb_checksum_{simple_, }validate"
+ */
+__sum16 skb_checksum_simple_validate(struct sk_buff *skb)
+{
+	switch (skb->ip_summed) {
+	case CHECKSUM_COMPLETE:
+		if (!csum_fold(skb->csum))
+			break;
+		/* fall through */
+	case CHECKSUM_NONE:
+		skb->csum = 0;
+		if (skb_checksum_complete(skb))
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Watch out: Not as generic as upstream
+ * - redefines this method to only fit with ICMPV6
+ *
+ * Compare with:
+ * "bridge: multicast: call skb_checksum_{simple_, }validate"
+ */
+__sum16
+skb_checksum_validate(struct sk_buff *skb, int proto,
+		      __wsum (*compute_pseudo)(struct sk_buff *skb, int proto))
+{
+	const struct ipv6hdr *ip6h = ipv6_hdr(skb);
+
+	switch (skb->ip_summed) {
+	case CHECKSUM_COMPLETE:
+		if (!csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, skb->len,
+				     IPPROTO_ICMPV6, skb->csum))
+			break;
+		/*FALLTHROUGH*/
+	case CHECKSUM_NONE:
+		skb->csum = ~csum_unfold(csum_ipv6_magic(&ip6h->saddr,
+							 &ip6h->daddr,
+							 skb->len,
+							 IPPROTO_ICMPV6, 0));
+		if (__skb_checksum_complete(skb))
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+#endif /* < KERNEL_VERSION(3, 16, 0) */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
+
+static inline void skb_postpush_rcsum(struct sk_buff *skb,
+                                   const void *start, unsigned int len)
+{
+	/* For performing the reverse operation to skb_postpull_rcsum(),
+	 * we can instead of ...
+	 *
+	 *   skb->csum = csum_add(skb->csum, csum_partial(start, len, 0));
+	 *
+	 * ... just use this equivalent version here to save a few
+	 * instructions. Feeding csum of 0 in csum_partial() and later
+	 * on adding skb->csum is equivalent to feed skb->csum in the
+	 * first place.
+	 */
+	if (skb->ip_summed == CHECKSUM_COMPLETE)
+		skb->csum = csum_partial(start, len, skb->csum);
+}
+
+/**
+ *	skb_push_rcsum - push skb and update receive checksum
+ *	@skb: buffer to update
+ *	@len: length of data pulled
+ *
+ *	This function performs an skb_push on the packet and updates
+ *	the CHECKSUM_COMPLETE checksum.  It should be used on
+ *	receive path processing instead of skb_push unless you know
+ *	that the checksum difference is zero (e.g., a valid IP header)
+ *	or you are setting ip_summed to CHECKSUM_NONE.
+ */
+static unsigned char *skb_push_rcsum(struct sk_buff *skb, unsigned len)
+{
+	skb_push(skb, len);
+	skb_postpush_rcsum(skb, skb->data, len);
+	return skb->data;
+}
+
+/**
+ * skb_checksum_maybe_trim - maybe trims the given skb
+ * @skb: the skb to check
+ * @transport_len: the data length beyond the network header
+ *
+ * Checks whether the given skb has data beyond the given transport length.
+ * If so, returns a cloned skb trimmed to this transport length.
+ * Otherwise returns the provided skb. Returns NULL in error cases
+ * (e.g. transport_len exceeds skb length or out-of-memory).
+ *
+ * Caller needs to set the skb transport header and release the returned skb.
+ * Provided skb is consumed.
+ */
+static struct sk_buff *skb_checksum_maybe_trim(struct sk_buff *skb,
+					       unsigned int transport_len)
+{
+	struct sk_buff *skb_chk;
+	unsigned int len = skb_transport_offset(skb) + transport_len;
+	int ret;
+
+	if (skb->len < len) {
+		kfree_skb(skb);
+		return NULL;
+	} else if (skb->len == len) {
+		return skb;
+	}
+
+	skb_chk = skb_clone(skb, GFP_ATOMIC);
+	kfree_skb(skb);
+
+	if (!skb_chk)
+		return NULL;
+
+	ret = pskb_trim_rcsum(skb_chk, len);
+	if (ret) {
+		kfree_skb(skb_chk);
+		return NULL;
+	}
+
+	return skb_chk;
+}
+
+/**
+ * skb_checksum_trimmed - validate checksum of an skb
+ * @skb: the skb to check
+ * @transport_len: the data length beyond the network header
+ * @skb_chkf: checksum function to use
+ *
+ * Applies the given checksum function skb_chkf to the provided skb.
+ * Returns a checked and maybe trimmed skb. Returns NULL on error.
+ *
+ * If the skb has data beyond the given transport length, then a
+ * trimmed & cloned skb is checked and returned.
+ *
+ * Caller needs to set the skb transport header and release the returned skb.
+ * Provided skb is consumed.
+ */
+struct sk_buff *skb_checksum_trimmed(struct sk_buff *skb,
+				     unsigned int transport_len,
+				     __sum16(*skb_chkf)(struct sk_buff *skb))
+{
+	struct sk_buff *skb_chk;
+	unsigned int offset = skb_transport_offset(skb);
+	__sum16 ret;
+
+	skb_chk = skb_checksum_maybe_trim(skb, transport_len);
+	if (!skb_chk)
+		return NULL;
+
+	if (!pskb_may_pull(skb_chk, offset)) {
+		kfree_skb(skb_chk);
+		return NULL;
+	}
+
+	skb_pull_rcsum(skb_chk, offset);
+	ret = skb_chkf(skb_chk);
+	skb_push_rcsum(skb_chk, offset);
+
+	if (ret) {
+		kfree_skb(skb_chk);
+		return NULL;
+	}
+
+	return skb_chk;
+}
+
+#endif /* < KERNEL_VERSION(4, 2, 0) */
diff --git a/compat-sources/net/ipv4/igmp.c b/compat-sources/net/ipv4/igmp.c
new file mode 100644
index 0000000..457a05e
--- /dev/null
+++ b/compat-sources/net/ipv4/igmp.c
@@ -0,0 +1,241 @@
+/*
+ *	Linux NET3:	Internet Group Management Protocol  [IGMP]
+ *
+ *	This code implements the IGMP protocol as defined in RFC1112. There has
+ *	been a further revision of this protocol since which is now supported.
+ *
+ *	If you have trouble with this module be careful what gcc you have used,
+ *	the older version didn't come out right using gcc 2.5.8, the newer one
+ *	seems to fall out with gcc 2.6.2.
+ *
+ *	Authors:
+ *		Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ *
+ *	Fixes:
+ *
+ *		Alan Cox	:	Added lots of __inline__ to optimise
+ *					the memory usage of all the tiny little
+ *					functions.
+ *		Alan Cox	:	Dumped the header building experiment.
+ *		Alan Cox	:	Minor tweaks ready for multicast routing
+ *					and extended IGMP protocol.
+ *		Alan Cox	:	Removed a load of inline directives. Gcc 2.5.8
+ *					writes utterly bogus code otherwise (sigh)
+ *					fixed IGMP loopback to behave in the manner
+ *					desired by mrouted, fixed the fact it has been
+ *					broken since 1.3.6 and cleaned up a few minor
+ *					points.
+ *
+ *		Chih-Jen Chang	:	Tried to revise IGMP to Version 2
+ *		Tsu-Sheng Tsao		E-mail: chihjenc@scf.usc.edu and tsusheng@scf.usc.edu
+ *					The enhancements are mainly based on Steve Deering's
+ * 					ipmulti-3.5 source code.
+ *		Chih-Jen Chang	:	Added the igmp_get_mrouter_info and
+ *		Tsu-Sheng Tsao		igmp_set_mrouter_info to keep track of
+ *					the mrouted version on that device.
+ *		Chih-Jen Chang	:	Added the max_resp_time parameter to
+ *		Tsu-Sheng Tsao		igmp_heard_query(). Using this parameter
+ *					to identify the multicast router version
+ *					and do what the IGMP version 2 specified.
+ *		Chih-Jen Chang	:	Added a timer to revert to IGMP V2 router
+ *		Tsu-Sheng Tsao		if the specified time expired.
+ *		Alan Cox	:	Stop IGMP from 0.0.0.0 being accepted.
+ *		Alan Cox	:	Use GFP_ATOMIC in the right places.
+ *		Christian Daudt :	igmp timer wasn't set for local group
+ *					memberships but was being deleted,
+ *					which caused a "del_timer() called
+ *					from %p with timer not initialized\n"
+ *					message (960131).
+ *		Christian Daudt :	removed del_timer from
+ *					igmp_timer_expire function (960205).
+ *             Christian Daudt :       igmp_heard_report now only calls
+ *                                     igmp_timer_expire if tm->running is
+ *                                     true (960216).
+ *		Malcolm Beattie :	ttl comparison wrong in igmp_rcv made
+ *					igmp_heard_query never trigger. Expiry
+ *					miscalculation fixed in igmp_heard_query
+ *					and random() made to return unsigned to
+ *					prevent negative expiry times.
+ *		Alexey Kuznetsov:	Wrong group leaving behaviour, backport
+ *					fix from pending 2.1.x patches.
+ *		Alan Cox:		Forget to enable FDDI support earlier.
+ *		Alexey Kuznetsov:	Fixed leaving groups on device down.
+ *		Alexey Kuznetsov:	Accordance to igmp-v2-06 draft.
+ *		David L Stevens:	IGMPv3 support, with help from
+ *					Vinay Kulkarni
+ */
+
+#include <linux/igmp.h>
+#include <linux/ip.h>
+#include <linux/skbuff.h>
+#include <net/ip.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
+
+static int ip_mc_check_iphdr(struct sk_buff *skb)
+{
+	const struct iphdr *iph;
+	unsigned int len;
+	unsigned int offset = skb_network_offset(skb) + sizeof(*iph);
+
+	if (!pskb_may_pull(skb, offset))
+		return -EINVAL;
+
+	iph = ip_hdr(skb);
+
+	if (iph->version != 4 || ip_hdrlen(skb) < sizeof(*iph))
+		return -EINVAL;
+
+	offset += ip_hdrlen(skb) - sizeof(*iph);
+
+	if (!pskb_may_pull(skb, offset))
+		return -EINVAL;
+
+	iph = ip_hdr(skb);
+
+	if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
+		return -EINVAL;
+
+	len = skb_network_offset(skb) + ntohs(iph->tot_len);
+	if (skb->len < len || len < offset)
+		return -EINVAL;
+
+	skb_set_transport_header(skb, offset);
+
+	return 0;
+}
+
+static int ip_mc_check_igmp_reportv3(struct sk_buff *skb)
+{
+	unsigned int len = skb_transport_offset(skb);
+
+	len += sizeof(struct igmpv3_report);
+
+	return pskb_may_pull(skb, len) ? 0 : -EINVAL;
+}
+
+static int ip_mc_check_igmp_query(struct sk_buff *skb)
+{
+	unsigned int len = skb_transport_offset(skb);
+
+	len += sizeof(struct igmphdr);
+	if (skb->len < len)
+		return -EINVAL;
+
+	/* IGMPv{1,2}? */
+	if (skb->len != len) {
+		/* or IGMPv3? */
+		len += sizeof(struct igmpv3_query) - sizeof(struct igmphdr);
+		if (skb->len < len || !pskb_may_pull(skb, len))
+			return -EINVAL;
+	}
+
+	/* RFC2236+RFC3376 (IGMPv2+IGMPv3) require the multicast link layer
+	 * all-systems destination addresses (224.0.0.1) for general queries
+	 */
+	if (!igmp_hdr(skb)->group &&
+	    ip_hdr(skb)->daddr != htonl(INADDR_ALLHOSTS_GROUP))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ip_mc_check_igmp_msg(struct sk_buff *skb)
+{
+	switch (igmp_hdr(skb)->type) {
+	case IGMP_HOST_LEAVE_MESSAGE:
+	case IGMP_HOST_MEMBERSHIP_REPORT:
+	case IGMPV2_HOST_MEMBERSHIP_REPORT:
+		/* fall through */
+		return 0;
+	case IGMPV3_HOST_MEMBERSHIP_REPORT:
+		return ip_mc_check_igmp_reportv3(skb);
+	case IGMP_HOST_MEMBERSHIP_QUERY:
+		return ip_mc_check_igmp_query(skb);
+	default:
+		return -ENOMSG;
+	}
+}
+
+static inline __sum16 ip_mc_validate_checksum(struct sk_buff *skb)
+{
+	return skb_checksum_simple_validate(skb);
+}
+
+static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed)
+
+{
+	struct sk_buff *skb_chk;
+	unsigned int transport_len;
+	unsigned int len = skb_transport_offset(skb) + sizeof(struct igmphdr);
+	int ret;
+
+	transport_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb);
+
+	skb_get(skb);
+	skb_chk = skb_checksum_trimmed(skb, transport_len,
+				       ip_mc_validate_checksum);
+	if (!skb_chk)
+		return -EINVAL;
+
+	if (!pskb_may_pull(skb_chk, len)) {
+		kfree_skb(skb_chk);
+		return -EINVAL;
+	}
+
+	ret = ip_mc_check_igmp_msg(skb_chk);
+	if (ret) {
+		kfree_skb(skb_chk);
+		return ret;
+	}
+
+	if (skb_trimmed)
+		*skb_trimmed = skb_chk;
+	else
+		kfree_skb(skb_chk);
+
+	return 0;
+}
+
+/**
+ * ip_mc_check_igmp - checks whether this is a sane IGMP packet
+ * @skb: the skb to validate
+ * @skb_trimmed: to store an skb pointer trimmed to IPv4 packet tail (optional)
+ *
+ * Checks whether an IPv4 packet is a valid IGMP packet. If so sets
+ * skb network and transport headers accordingly and returns zero.
+ *
+ * -EINVAL: A broken packet was detected, i.e. it violates some internet
+ *  standard
+ * -ENOMSG: IP header validation succeeded but it is not an IGMP packet.
+ * -ENOMEM: A memory allocation failure happened.
+ *
+ * Optionally, an skb pointer might be provided via skb_trimmed (or set it
+ * to NULL): After parsing an IGMP packet successfully it will point to
+ * an skb which has its tail aligned to the IP packet end. This might
+ * either be the originally provided skb or a trimmed, cloned version if
+ * the skb frame had data beyond the IP packet. A cloned skb allows us
+ * to leave the original skb and its full frame unchanged (which might be
+ * desirable for layer 2 frame jugglers).
+ *
+ * The caller needs to release a reference count from any returned skb_trimmed.
+ */
+int ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed)
+{
+	int ret = ip_mc_check_iphdr(skb);
+
+	if (ret < 0)
+		return ret;
+
+	if (ip_hdr(skb)->protocol != IPPROTO_IGMP)
+		return -ENOMSG;
+
+	return __ip_mc_check_igmp(skb, skb_trimmed);
+}
+
+#endif /* < KERNEL_VERSION(4, 2, 0) */
diff --git a/compat-sources/net/ipv6/mcast_snoop.c b/compat-sources/net/ipv6/mcast_snoop.c
new file mode 100644
index 0000000..3f46ed3
--- /dev/null
+++ b/compat-sources/net/ipv6/mcast_snoop.c
@@ -0,0 +1,216 @@
+/* Copyright (C) 2010: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
+ * Copyright (C) 2015: Linus Lüssing <linus.luessing@c0d3.blue>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Based on the MLD support added to br_multicast.c by YOSHIFUJI Hideaki.
+ */
+
+#include <linux/skbuff.h>
+#include <net/ipv6.h>
+#include <net/mld.h>
+#include <net/addrconf.h>
+#include <net/ip6_checksum.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
+
+static int ipv6_mc_check_ip6hdr(struct sk_buff *skb)
+{
+	const struct ipv6hdr *ip6h;
+	unsigned int len;
+	unsigned int offset = skb_network_offset(skb) + sizeof(*ip6h);
+
+	if (!pskb_may_pull(skb, offset))
+		return -EINVAL;
+
+	ip6h = ipv6_hdr(skb);
+
+	if (ip6h->version != 6)
+		return -EINVAL;
+
+	len = offset + ntohs(ip6h->payload_len);
+	if (skb->len < len || len <= offset)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ipv6_mc_check_exthdrs(struct sk_buff *skb)
+{
+	const struct ipv6hdr *ip6h;
+	int offset;
+	u8 nexthdr;
+	__be16 frag_off;
+
+	ip6h = ipv6_hdr(skb);
+
+	if (ip6h->nexthdr != IPPROTO_HOPOPTS)
+		return -ENOMSG;
+
+	nexthdr = ip6h->nexthdr;
+	offset = skb_network_offset(skb) + sizeof(*ip6h);
+	offset = ipv6_skip_exthdr(skb, offset, &nexthdr, &frag_off);
+
+	if (offset < 0)
+		return -EINVAL;
+
+	if (nexthdr != IPPROTO_ICMPV6)
+		return -ENOMSG;
+
+	skb_set_transport_header(skb, offset);
+
+	return 0;
+}
+
+static int ipv6_mc_check_mld_reportv2(struct sk_buff *skb)
+{
+	unsigned int len = skb_transport_offset(skb);
+
+	len += sizeof(struct mld2_report);
+
+	return pskb_may_pull(skb, len) ? 0 : -EINVAL;
+}
+
+static int ipv6_mc_check_mld_query(struct sk_buff *skb)
+{
+	struct mld_msg *mld;
+	unsigned int len = skb_transport_offset(skb);
+
+	/* RFC2710+RFC3810 (MLDv1+MLDv2) require link-local source addresses */
+	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL))
+		return -EINVAL;
+
+	len += sizeof(struct mld_msg);
+	if (skb->len < len)
+		return -EINVAL;
+
+	/* MLDv1? */
+	if (skb->len != len) {
+		/* or MLDv2? */
+		len += sizeof(struct mld2_query) - sizeof(struct mld_msg);
+		if (skb->len < len || !pskb_may_pull(skb, len))
+			return -EINVAL;
+	}
+
+	mld = (struct mld_msg *)skb_transport_header(skb);
+
+	/* RFC2710+RFC3810 (MLDv1+MLDv2) require the multicast link layer
+	 * all-nodes destination address (ff02::1) for general queries
+	 */
+	if (ipv6_addr_any(&mld->mld_mca) &&
+	    !ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ipv6_mc_check_mld_msg(struct sk_buff *skb)
+{
+	struct mld_msg *mld = (struct mld_msg *)skb_transport_header(skb);
+
+	switch (mld->mld_type) {
+	case ICMPV6_MGM_REDUCTION:
+	case ICMPV6_MGM_REPORT:
+		/* fall through */
+		return 0;
+	case ICMPV6_MLD2_REPORT:
+		return ipv6_mc_check_mld_reportv2(skb);
+	case ICMPV6_MGM_QUERY:
+		return ipv6_mc_check_mld_query(skb);
+	default:
+		return -ENOMSG;
+	}
+}
+
+static inline __sum16 ipv6_mc_validate_checksum(struct sk_buff *skb)
+{
+	return skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo);
+}
+
+static int __ipv6_mc_check_mld(struct sk_buff *skb,
+			       struct sk_buff **skb_trimmed)
+
+{
+	struct sk_buff *skb_chk = NULL;
+	unsigned int transport_len;
+	unsigned int len = skb_transport_offset(skb) + sizeof(struct mld_msg);
+	int ret;
+
+	transport_len = ntohs(ipv6_hdr(skb)->payload_len);
+	transport_len -= skb_transport_offset(skb) - sizeof(struct ipv6hdr);
+
+	skb_get(skb);
+	skb_chk = skb_checksum_trimmed(skb, transport_len,
+				       ipv6_mc_validate_checksum);
+	if (!skb_chk)
+		return -EINVAL;
+
+	if (!pskb_may_pull(skb_chk, len)) {
+		kfree_skb(skb_chk);
+		return -EINVAL;
+	}
+
+	ret = ipv6_mc_check_mld_msg(skb_chk);
+	if (ret) {
+		kfree_skb(skb_chk);
+		return ret;
+	}
+
+	if (skb_trimmed)
+		*skb_trimmed = skb_chk;
+	else
+		kfree_skb(skb_chk);
+
+	return 0;
+}
+
+/**
+ * ipv6_mc_check_mld - checks whether this is a sane MLD packet
+ * @skb: the skb to validate
+ * @skb_trimmed: to store an skb pointer trimmed to IPv6 packet tail (optional)
+ *
+ * Checks whether an IPv6 packet is a valid MLD packet. If so sets
+ * skb network and transport headers accordingly and returns zero.
+ *
+ * -EINVAL: A broken packet was detected, i.e. it violates some internet
+ *  standard
+ * -ENOMSG: IP header validation succeeded but it is not an MLD packet.
+ * -ENOMEM: A memory allocation failure happened.
+ *
+ * Optionally, an skb pointer might be provided via skb_trimmed (or set it
+ * to NULL): After parsing an MLD packet successfully it will point to
+ * an skb which has its tail aligned to the IP packet end. This might
+ * either be the originally provided skb or a trimmed, cloned version if
+ * the skb frame had data beyond the IP packet. A cloned skb allows us
+ * to leave the original skb and its full frame unchanged (which might be
+ * desirable for layer 2 frame jugglers).
+ *
+ * The caller needs to release a reference count from any returned skb_trimmed.
+ */
+int ipv6_mc_check_mld(struct sk_buff *skb, struct sk_buff **skb_trimmed)
+{
+	int ret;
+
+	ret = ipv6_mc_check_ip6hdr(skb);
+	if (ret < 0)
+		return ret;
+
+	ret = ipv6_mc_check_exthdrs(skb);
+	if (ret < 0)
+		return ret;
+
+	return __ipv6_mc_check_mld(skb, skb_trimmed);
+}
+
+#endif /* < KERNEL_VERSION(4, 2, 0) */
diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c
index 8caa2c7..19cc404 100644
--- a/net/batman-adv/multicast.c
+++ b/net/batman-adv/multicast.c
@@ -25,7 +25,9 @@
 #include <linux/errno.h>
 #include <linux/etherdevice.h>
 #include <linux/fs.h>
+#include <linux/icmpv6.h>
 #include <linux/if_ether.h>
+#include <linux/igmp.h>
 #include <linux/in6.h>
 #include <linux/in.h>
 #include <linux/ip.h>
@@ -44,10 +46,17 @@
 #include <linux/types.h>
 #include <net/addrconf.h>
 #include <net/ipv6.h>
+#include <net/mld.h>
 
 #include "packet.h"
 #include "translation-table.h"
 
+static void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv,
+					  struct batadv_orig_node *orig,
+					  u8 flags,
+					  void *tvlv_value,
+					  u16 tvlv_value_len);
+
 /**
  * batadv_mcast_mla_softif_get - get softif multicast listeners
  * @dev: the device to collect multicast addresses from
@@ -214,6 +223,34 @@ static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv)
 }
 
 /**
+ * batadv_mcast_handler_register - register a multicast tvlv handler
+ * @bat_priv: the bat priv with all the soft interface information
+ * @version: tvlv handler version to be registered
+ *
+ * Registers a multicast tvlv handler of the given version number.
+ */
+static void
+batadv_mcast_handler_register(struct batadv_priv *bat_priv, u8 version)
+{
+	batadv_tvlv_handler_register(bat_priv, batadv_mcast_tvlv_ogm_handler,
+				     NULL, BATADV_TVLV_MCAST, version,
+				     BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
+}
+
+/**
+ * batadv_mcast_handler_unregister - unregister a multicast tvlv handler
+ * @bat_priv: the bat priv with all the soft interface information
+ * @version: tvlv handler version to be registered
+ *
+ * Unregisters a multicast tvlv handler of the given version number.
+ */
+static void
+batadv_mcast_handler_unregister(struct batadv_priv *bat_priv, u8 version)
+{
+	batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_MCAST, version);
+}
+
+/**
  * batadv_mcast_mla_tvlv_update - update multicast tvlv
  * @bat_priv: the bat priv with all the soft interface information
  *
@@ -237,6 +274,9 @@ static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv)
 		if (bat_priv->mcast.enabled) {
 			batadv_tvlv_container_unregister(bat_priv,
 							 BATADV_TVLV_MCAST, 1);
+			batadv_tvlv_container_unregister(bat_priv,
+							 BATADV_TVLV_MCAST, 2);
+			batadv_mcast_handler_unregister(bat_priv, 1);
 			bat_priv->mcast.enabled = false;
 		}
 
@@ -247,6 +287,9 @@ static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv)
 	    mcast_data.flags != bat_priv->mcast.flags) {
 		batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 1,
 					       &mcast_data, sizeof(mcast_data));
+		batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 2,
+					       &mcast_data, sizeof(mcast_data));
+		batadv_mcast_handler_register(bat_priv, 1);
 		bat_priv->mcast.flags = mcast_data.flags;
 		bat_priv->mcast.enabled = true;
 	}
@@ -283,6 +326,31 @@ out:
 }
 
 /**
+ * batadv_mcast_is_report_ipv4 - check for IGMP reports
+ * @skb: the ethernet frame destined for the mesh
+ *
+ * This call might reallocate skb data.
+ *
+ * Checks whether the given frame is a valid IGMP report.
+ *
+ * Return: If so then true, otherwise false.
+ */
+static bool batadv_mcast_is_report_ipv4(struct sk_buff *skb)
+{
+	if (ip_mc_check_igmp(skb, NULL) < 0)
+		return false;
+
+	switch (igmp_hdr(skb)->type) {
+	case IGMP_HOST_MEMBERSHIP_REPORT:
+	case IGMPV2_HOST_MEMBERSHIP_REPORT:
+	case IGMPV3_HOST_MEMBERSHIP_REPORT:
+		return true;
+	}
+
+	return false;
+}
+
+/**
  * batadv_mcast_forw_mode_check_ipv4 - check for optimized forwarding potential
  * @bat_priv: the bat priv with all the soft interface information
  * @skb: the IPv4 packet to check
@@ -304,6 +372,9 @@ 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))
+		return -EINVAL;
+
 	iphdr = ip_hdr(skb);
 
 	/* TODO: Implement Multicast Router Discovery (RFC4286),
@@ -320,6 +391,31 @@ static int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv,
 	return 0;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+/**
+ * batadv_mcast_is_report_ipv6 - check for MLD reports
+ * @skb: the ethernet frame destined for the mesh
+ *
+ * This call might reallocate skb data.
+ *
+ * Checks whether the given frame is a valid MLD report.
+ *
+ * Return: If so then true, otherwise false.
+ */
+static bool batadv_mcast_is_report_ipv6(struct sk_buff *skb)
+{
+	if (ipv6_mc_check_mld(skb, NULL) < 0)
+		return false;
+
+	switch (icmp6_hdr(skb)->icmp6_type) {
+	case ICMPV6_MGM_REPORT:
+	case ICMPV6_MLD2_REPORT:
+		return true;
+	}
+
+	return false;
+}
+
 /**
  * batadv_mcast_forw_mode_check_ipv6 - check for optimized forwarding potential
  * @bat_priv: the bat priv with all the soft interface information
@@ -341,6 +437,9 @@ 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))
+		return -EINVAL;
+
 	ip6hdr = ipv6_hdr(skb);
 
 	/* TODO: Implement Multicast Router Discovery (RFC4286),
@@ -357,6 +456,7 @@ static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv,
 
 	return 0;
 }
+#endif
 
 /**
  * batadv_mcast_forw_mode_check - check for optimized forwarding potential
@@ -385,9 +485,11 @@ static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv,
 	case ETH_P_IP:
 		return batadv_mcast_forw_mode_check_ipv4(bat_priv, skb,
 							 is_unsnoopable);
+#if IS_ENABLED(CONFIG_IPV6)
 	case ETH_P_IPV6:
 		return batadv_mcast_forw_mode_check_ipv6(bat_priv, skb,
 							 is_unsnoopable);
+#endif
 	default:
 		return -EINVAL;
 	}
@@ -727,18 +829,18 @@ static void batadv_mcast_want_ipv6_update(struct batadv_priv *bat_priv,
 }
 
 /**
- * batadv_mcast_tvlv_ogm_handler_v1 - process incoming multicast tvlv container
+ * batadv_mcast_tvlv_ogm_handler - process incoming multicast tvlv container
  * @bat_priv: the bat priv with all the soft interface information
  * @orig: the orig_node of the ogm
  * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
  * @tvlv_value: tvlv buffer containing the multicast data
  * @tvlv_value_len: tvlv buffer length
  */
-static void batadv_mcast_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
-					     struct batadv_orig_node *orig,
-					     u8 flags,
-					     void *tvlv_value,
-					     u16 tvlv_value_len)
+static void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv,
+					  struct batadv_orig_node *orig,
+					  u8 flags,
+					  void *tvlv_value,
+					  u16 tvlv_value_len)
 {
 	bool orig_mcast_enabled = !(flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
 	u8 mcast_flags = BATADV_NO_FLAGS;
@@ -788,9 +890,7 @@ static void batadv_mcast_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
  */
 void batadv_mcast_init(struct batadv_priv *bat_priv)
 {
-	batadv_tvlv_handler_register(bat_priv, batadv_mcast_tvlv_ogm_handler_v1,
-				     NULL, BATADV_TVLV_MCAST, 1,
-				     BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
+	batadv_mcast_handler_register(bat_priv, 2);
 }
 
 /**
@@ -800,7 +900,9 @@ void batadv_mcast_init(struct batadv_priv *bat_priv)
 void batadv_mcast_free(struct batadv_priv *bat_priv)
 {
 	batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_MCAST, 1);
+	batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_MCAST, 2);
 	batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_MCAST, 1);
+	batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_MCAST, 2);
 
 	spin_lock_bh(&bat_priv->tt.commit_lock);
 	batadv_mcast_mla_tt_retract(bat_priv, NULL);
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [B.A.T.M.A.N.] [PATCHv12 2/4] batman-adv: Add multicast optimization support for bridged setups
  2016-03-16  4:06 [B.A.T.M.A.N.] [PATCHv12 0/4] Multicast optimizations for bridges Linus Lüssing
  2016-03-16  4:06 ` [B.A.T.M.A.N.] [PATCHv12 1/4] batman-adv: Always flood IGMP/MLD reports Linus Lüssing
@ 2016-03-16  4:06 ` Linus Lüssing
  2016-04-07 13:43   ` Sven Eckelmann
  2016-04-07 13:50   ` Sven Eckelmann
  2016-03-16  4:06 ` [B.A.T.M.A.N.] [PATCHv12 3/4] batman-adv: Adding logging of mcast flag changes Linus Lüssing
  2016-03-16  4:06 ` [B.A.T.M.A.N.] [PATCHv12 4/4] batman-adv: Add debugfs table for mcast flags Linus Lüssing
  3 siblings, 2 replies; 14+ messages in thread
From: Linus Lüssing @ 2016-03-16  4:06 UTC (permalink / raw)
  To: b.a.t.m.a.n

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.

Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
---
 compat-include/linux/etherdevice.h       |    9 ++
 compat-include/linux/if_bridge.h         |   37 ++++++
 compat-include/linux/printk.h            |    7 ++
 compat-sources/Makefile                  |    1 +
 compat-sources/net/bridge/br_multicast.c |   33 ++++++
 net/batman-adv/multicast.c               |  185 ++++++++++++++++++++++++++----
 net/batman-adv/types.h                   |    2 +
 7 files changed, 253 insertions(+), 21 deletions(-)
 create mode 100644 compat-include/linux/if_bridge.h
 create mode 100644 compat-sources/net/bridge/br_multicast.c

diff --git a/compat-include/linux/etherdevice.h b/compat-include/linux/etherdevice.h
index f120202..721945c 100644
--- a/compat-include/linux/etherdevice.h
+++ b/compat-include/linux/etherdevice.h
@@ -35,6 +35,15 @@ static inline void batadv_eth_hw_addr_random(struct net_device *dev)
 
 #endif /* < KERNEL_VERSION(3, 4, 0) */
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)
+
+static inline void eth_zero_addr(u8 *addr)
+{
+	memset(addr, 0x00, ETH_ALEN);
+}
+
+#endif /* < KERNEL_VERSION(3, 7, 0) */
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
 
 #define ether_addr_equal_unaligned(_a, _b) (memcmp(_a, _b, ETH_ALEN) == 0)
diff --git a/compat-include/linux/if_bridge.h b/compat-include/linux/if_bridge.h
new file mode 100644
index 0000000..3eea164
--- /dev/null
+++ b/compat-include/linux/if_bridge.h
@@ -0,0 +1,37 @@
+#ifndef _NET_BATMAN_ADV_COMPAT_LINUX_IF_BRIDGE_H_
+#define _NET_BATMAN_ADV_COMPAT_LINUX_IF_BRIDGE_H_
+
+#include <linux/version.h>
+#include_next <linux/if_bridge.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0)
+
+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_LINUX_IF_BRIDGE_H_ */
diff --git a/compat-include/linux/printk.h b/compat-include/linux/printk.h
index ecf4289..f5031ab 100644
--- a/compat-include/linux/printk.h
+++ b/compat-include/linux/printk.h
@@ -32,6 +32,13 @@
 
 #endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
+
+#define pr_warn_once(fmt, ...) \
+	printk_once(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
+
+#endif /* < KERNEL_VERSION(2, 6, 38) */
+
 #endif	/* _NET_BATMAN_ADV_COMPAT_LINUX_PRINTK_H_ */
 
 #ifndef pr_fmt
diff --git a/compat-sources/Makefile b/compat-sources/Makefile
index c364ded..ae3fbd3 100644
--- a/compat-sources/Makefile
+++ b/compat-sources/Makefile
@@ -1,3 +1,4 @@
 batman-adv-y += ../../compat-sources/net/core/skbuff.o
 batman-adv-y += ../../compat-sources/net/ipv4/igmp.o
 batman-adv-y += ../../compat-sources/net/ipv6/mcast_snoop.o
+batman-adv-y += ../../compat-sources/net/bridge/br_multicast.o
diff --git a/compat-sources/net/bridge/br_multicast.c b/compat-sources/net/bridge/br_multicast.c
new file mode 100644
index 0000000..3451ab6
--- /dev/null
+++ b/compat-sources/net/bridge/br_multicast.c
@@ -0,0 +1,33 @@
+#include <linux/if_bridge.h>
+#include <linux/printk.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0) || \
+    LINUX_VERSION_CODE == KERNEL_VERSION(3, 16, 0) && \
+	(!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 /* < KERNEL_VERSION(3, 16, 0) ||
+	* !IS_ENABLED(CONFIG_BRIDGE) ||
+	* !IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING) */
+
+#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;
+}
+
+#endif /* < KERNEL_VERSION(3, 17, 0) */
diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c
index 19cc404..34abe39 100644
--- a/net/batman-adv/multicast.c
+++ b/net/batman-adv/multicast.c
@@ -26,6 +26,7 @@
 #include <linux/etherdevice.h>
 #include <linux/fs.h>
 #include <linux/icmpv6.h>
+#include <linux/if_bridge.h>
 #include <linux/if_ether.h>
 #include <linux/igmp.h>
 #include <linux/in6.h>
@@ -45,6 +46,7 @@
 #include <linux/string.h>
 #include <linux/types.h>
 #include <net/addrconf.h>
+#include <net/ip.h>
 #include <net/ipv6.h>
 #include <net/mld.h>
 
@@ -58,12 +60,45 @@ static void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv,
 					  u16 tvlv_value_len);
 
 /**
+ * batadv_mcast_get_bridge - get the bridge on top of the softif if it exists
+ * @soft_iface: netdev struct of the mesh interface
+ *
+ * If the given soft interface has a bridge on top then the refcount
+ * of the according net device is increased.
+ *
+ * Return: NULL if no such bridge exists. Otherwise the net device of the
+ * bridge.
+ */
+static struct net_device *batadv_mcast_get_bridge(struct net_device *soft_iface)
+{
+	struct net_device *upper = soft_iface;
+
+	rcu_read_lock();
+	do {
+		upper = netdev_master_upper_dev_get_rcu(upper);
+	} while (upper && !(upper->priv_flags & IFF_EBRIDGE));
+
+	if (upper)
+		dev_hold(upper);
+	rcu_read_unlock();
+
+	return upper;
+}
+
+/**
  * batadv_mcast_mla_softif_get - get softif multicast listeners
  * @dev: the device to collect multicast addresses from
  * @mcast_list: a list to put found addresses into
  *
- * Collect multicast addresses of the local multicast listeners
- * on the given soft interface, dev, in the given mcast_list.
+ * Collects multicast addresses of multicast listeners residing
+ * on this kernel on the given soft interface, dev, in
+ * the given mcast_list. In general, multicast listeners provided by
+ * your multicast receiving applications run directly on this node.
+ *
+ * If there is a bridge interface on top of dev, collects from that one
+ * instead. Just like with IP addresses and routes, multicast listeners
+ * will(/should) register to the bridge interface instead of an
+ * enslaved bat0.
  *
  * Return: -ENOMEM on memory allocation error or the number of
  * items added to the mcast_list otherwise.
@@ -71,12 +106,13 @@ static void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv,
 static int batadv_mcast_mla_softif_get(struct net_device *dev,
 				       struct hlist_head *mcast_list)
 {
+	struct net_device *bridge = batadv_mcast_get_bridge(dev);
 	struct netdev_hw_addr *mc_list_entry;
 	struct batadv_hw_addr *new;
 	int ret = 0;
 
-	netif_addr_lock_bh(dev);
-	netdev_for_each_mc_addr(mc_list_entry, dev) {
+	netif_addr_lock_bh(bridge ? bridge : dev);
+	netdev_for_each_mc_addr(mc_list_entry, bridge ? bridge : dev) {
 		new = kmalloc(sizeof(*new), GFP_ATOMIC);
 		if (!new) {
 			ret = -ENOMEM;
@@ -87,7 +123,10 @@ static int batadv_mcast_mla_softif_get(struct net_device *dev,
 		hlist_add_head(&new->list, mcast_list);
 		ret++;
 	}
-	netif_addr_unlock_bh(dev);
+	netif_addr_unlock_bh(bridge ? bridge : dev);
+
+	if (bridge)
+		dev_put(bridge);
 
 	return ret;
 }
@@ -113,6 +152,83 @@ static bool batadv_mcast_mla_is_duplicate(u8 *mcast_addr,
 }
 
 /**
+ * batadv_mcast_mla_br_addr_cpy - copy a bridge multicast address
+ * @dst: destination to write to - a multicast MAC address
+ * @src: source to read from - a multicast IP address
+ *
+ * Converts a given multicast IPv4/IPv6 address from a bridge
+ * to its matching multicast MAC address and copies it into the given
+ * destination buffer.
+ *
+ * Caller needs to make sure the destination buffer can hold
+ * at least ETH_ALEN bytes.
+ */
+static void batadv_mcast_mla_br_addr_cpy(char *dst, const struct br_ip *src)
+{
+	if (src->proto == htons(ETH_P_IP))
+		ip_eth_mc_map(src->u.ip4, dst);
+#if IS_ENABLED(CONFIG_IPV6)
+	else if (src->proto == htons(ETH_P_IPV6))
+		ipv6_eth_mc_map(&src->u.ip6, dst);
+#endif
+	else
+		eth_zero_addr(dst);
+}
+
+/**
+ * batadv_mcast_mla_bridge_get - get bridged-in multicast listeners
+ * @dev: a bridge slave whose bridge to collect multicast addresses from
+ * @mcast_list: a list to put found addresses into
+ *
+ * Collects multicast addresses of multicast listeners residing
+ * on foreign, non-mesh devices which we gave access to our mesh via
+ * a bridge on top of the given soft interface, dev, in the given
+ * mcast_list.
+ *
+ * Return: -ENOMEM on memory allocation error or the number of
+ * items added to the mcast_list otherwise.
+ */
+static int batadv_mcast_mla_bridge_get(struct net_device *dev,
+				       struct hlist_head *mcast_list)
+{
+	struct list_head bridge_mcast_list = LIST_HEAD_INIT(bridge_mcast_list);
+	struct br_ip_list *br_ip_entry, *tmp;
+	struct batadv_hw_addr *new;
+	u8 mcast_addr[ETH_ALEN];
+	int ret;
+
+	/* we don't need to detect these devices/listeners, the IGMP/MLD
+	 * snooping code of the Linux bridge already does that for us
+	 */
+	ret = br_multicast_list_adjacent(dev, &bridge_mcast_list);
+	if (ret < 0)
+		goto out;
+
+	list_for_each_entry(br_ip_entry, &bridge_mcast_list, list) {
+		batadv_mcast_mla_br_addr_cpy(mcast_addr, &br_ip_entry->addr);
+		if (batadv_mcast_mla_is_duplicate(mcast_addr, mcast_list))
+			continue;
+
+		new = kmalloc(sizeof(*new), GFP_ATOMIC);
+		if (!new) {
+			ret = -ENOMEM;
+			break;
+		}
+
+		ether_addr_copy(new->addr, mcast_addr);
+		hlist_add_head(&new->list, mcast_list);
+	}
+
+out:
+	list_for_each_entry_safe(br_ip_entry, tmp, &bridge_mcast_list, list) {
+		list_del(&br_ip_entry->list);
+		kfree(br_ip_entry);
+	}
+
+	return ret;
+}
+
+/**
  * batadv_mcast_mla_list_free - free a list of multicast addresses
  * @bat_priv: the bat priv with all the soft interface information
  * @mcast_list: the list to free
@@ -257,44 +373,67 @@ batadv_mcast_handler_unregister(struct batadv_priv *bat_priv, u8 version)
  * Updates the own multicast tvlv with our current multicast related settings,
  * capabilities and inabilities.
  *
- * Return: true if the tvlv container is registered afterwards. Otherwise
- * returns false.
+ * Return: false if we want all IPv4 && IPv6 multicast traffic and true
+ * otherwise.
  */
 static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv)
 {
 	struct batadv_tvlv_mcast_data mcast_data;
+	struct net_device *dev = bat_priv->soft_iface;
 
 	mcast_data.flags = BATADV_NO_FLAGS;
 	memset(mcast_data.reserved, 0, sizeof(mcast_data.reserved));
 
-	/* Avoid attaching MLAs, if there is a bridge on top of our soft
-	 * interface, we don't support that yet (TODO)
-	 */
-	if (batadv_mcast_has_bridge(bat_priv)) {
-		if (bat_priv->mcast.enabled) {
-			batadv_tvlv_container_unregister(bat_priv,
-							 BATADV_TVLV_MCAST, 1);
-			batadv_tvlv_container_unregister(bat_priv,
-							 BATADV_TVLV_MCAST, 2);
-			batadv_mcast_handler_unregister(bat_priv, 1);
-			bat_priv->mcast.enabled = false;
+	if (!batadv_mcast_has_bridge(bat_priv)) {
+		if (bat_priv->mcast.bridged || !bat_priv->mcast.enabled) {
+			batadv_mcast_handler_register(bat_priv, 1);
+			bat_priv->mcast.bridged = false;
 		}
 
-		return false;
+		goto update;
 	}
 
+	if (bat_priv->mcast.enabled && !bat_priv->mcast.bridged)
+		batadv_mcast_handler_unregister(bat_priv, 1);
+
+	bat_priv->mcast.bridged = true;
+
+#if !IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING)
+	pr_warn_once("No bridge IGMP snooping compiled - multicast optimizations disabled\n");
+#endif
+
+	mcast_data.flags |= BATADV_MCAST_WANT_ALL_UNSNOOPABLES;
+
+	/* 1) If no querier exists at all, then multicast listeners on
+	 *    our local TT clients behind the bridge will keep silent.
+	 * 2) If the selected querier is on one of our local TT clients,
+	 *    behind the bridge, then this querier might shadow multicast
+	 *    listeners on our local TT clients, behind this bridge.
+	 *
+	 * 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))
+		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))
+		mcast_data.flags |= BATADV_MCAST_WANT_ALL_IPV6;
+
+update:
 	if (!bat_priv->mcast.enabled ||
 	    mcast_data.flags != bat_priv->mcast.flags) {
 		batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 1,
 					       &mcast_data, sizeof(mcast_data));
 		batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 2,
 					       &mcast_data, sizeof(mcast_data));
-		batadv_mcast_handler_register(bat_priv, 1);
 		bat_priv->mcast.flags = mcast_data.flags;
 		bat_priv->mcast.enabled = true;
 	}
 
-	return true;
+	return !(mcast_data.flags &
+		 (BATADV_MCAST_WANT_ALL_IPV4 + BATADV_MCAST_WANT_ALL_IPV6));
 }
 
 /**
@@ -317,6 +456,10 @@ void batadv_mcast_mla_update(struct batadv_priv *bat_priv)
 	if (ret < 0)
 		goto out;
 
+	ret = batadv_mcast_mla_bridge_get(soft_iface, &mcast_list);
+	if (ret < 0)
+		goto out;
+
 update:
 	batadv_mcast_mla_tt_retract(bat_priv, &mcast_list);
 	batadv_mcast_mla_tt_add(bat_priv, &mcast_list);
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 9abfb3e..f793c57 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -751,6 +751,7 @@ struct batadv_priv_dat {
  * @want_all_ipv6_list: a list of orig_nodes wanting all IPv6 multicast traffic
  * @flags: the flags we have last sent in our mcast tvlv
  * @enabled: whether the multicast tvlv is currently enabled
+ * @bridged: whether the soft interface 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
@@ -765,6 +766,7 @@ struct batadv_priv_mcast {
 	struct hlist_head want_all_ipv6_list;
 	u8 flags;
 	bool enabled;
+	bool bridged;
 	atomic_t num_disabled;
 	atomic_t num_want_all_unsnoopables;
 	atomic_t num_want_all_ipv4;
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [B.A.T.M.A.N.] [PATCHv12 3/4] batman-adv: Adding logging of mcast flag changes
  2016-03-16  4:06 [B.A.T.M.A.N.] [PATCHv12 0/4] Multicast optimizations for bridges Linus Lüssing
  2016-03-16  4:06 ` [B.A.T.M.A.N.] [PATCHv12 1/4] batman-adv: Always flood IGMP/MLD reports Linus Lüssing
  2016-03-16  4:06 ` [B.A.T.M.A.N.] [PATCHv12 2/4] batman-adv: Add multicast optimization support for bridged setups Linus Lüssing
@ 2016-03-16  4:06 ` Linus Lüssing
  2016-03-16  4:06 ` [B.A.T.M.A.N.] [PATCHv12 4/4] batman-adv: Add debugfs table for mcast flags Linus Lüssing
  3 siblings, 0 replies; 14+ messages in thread
From: Linus Lüssing @ 2016-03-16  4:06 UTC (permalink / raw)
  To: b.a.t.m.a.n

With this patch changes relevant to a node's own multicast flags are
printed to the 'mcast' log level.

Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
---
 net/batman-adv/main.h           |    4 +-
 net/batman-adv/multicast.c      |  164 +++++++++++++++++++++++++++++++++++----
 net/batman-adv/soft-interface.c |    4 +
 net/batman-adv/types.h          |   15 ++++
 4 files changed, 170 insertions(+), 17 deletions(-)

diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index e602408..c1c6a32 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -227,6 +227,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 {
@@ -236,7 +237,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
diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c
index 34abe39..41ba2e8 100644
--- a/net/batman-adv/multicast.c
+++ b/net/batman-adv/multicast.c
@@ -367,6 +367,122 @@ batadv_mcast_handler_unregister(struct batadv_priv *bat_priv, u8 version)
 }
 
 /**
+ * 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_state: the previous querier state on our link
+ * @new_state: 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: Snooping switches will directly
+ * forward listener reports to the querier, therefore batman-adv and
+ * the bridge will potentially not see these listeners - the querier is
+ * potentially shadowing listeners from us then.
+ *
+ * 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_state,
+			 struct batadv_mcast_querier_state *new_state)
+{
+	if (!old_state->exists && new_state->exists)
+		batadv_info(bat_priv->soft_iface, "%s Querier appeared\n",
+			    str_proto);
+	else if (old_state->exists && !new_state->exists)
+		batadv_info(bat_priv->soft_iface,
+			    "%s Querier disappeared - multicast optimizations disabled\n",
+			    str_proto);
+	else if (!bat_priv->mcast.bridged && !new_state->exists)
+		batadv_info(bat_priv->soft_iface,
+			    "No %s Querier present - multicast optimizations disabled\n",
+			    str_proto);
+
+	if (new_state->exists) {
+		if ((!old_state->shadowing && new_state->shadowing) ||
+		    (!old_state->exists && new_state->shadowing))
+			batadv_dbg(BATADV_DBG_MCAST, bat_priv,
+				   "%s Querier is behind our bridged segment: Might shadow listeners\n",
+				   str_proto);
+		else if (old_state->shadowing && !new_state->shadowing)
+			batadv_dbg(BATADV_DBG_MCAST, bat_priv,
+				   "%s Querier is not behind our bridged segment\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 Unsnoopables(U)-flag\n");
+	else if (bat_priv->mcast.bridged && !bridged)
+		batadv_dbg(BATADV_DBG_MCAST, bat_priv,
+			   "Bridge removed: Unsetting Unsnoopables(U)-flag\n");
+
+	if (bridged) {
+		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, u8 flags)
+{
+	u8 old_flags = bat_priv->mcast.flags;
+	char str_old_flags[] = "[...]";
+
+	sprintf(str_old_flags, "[%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' : '.');
+
+	batadv_dbg(BATADV_DBG_MCAST, bat_priv,
+		   "Changing multicast flags from '%s' to '[%c%c%c]'\n",
+		   bat_priv->mcast.enabled ? str_old_flags : "<undefined>",
+		   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
  *
@@ -379,29 +495,28 @@ batadv_mcast_handler_unregister(struct batadv_priv *bat_priv, u8 version)
 static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv)
 {
 	struct batadv_tvlv_mcast_data mcast_data;
+	struct batadv_mcast_querier_state querier4 = {false, false};
+	struct batadv_mcast_querier_state querier6 = {false, false};
 	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));
 
-	if (!batadv_mcast_has_bridge(bat_priv)) {
-		if (bat_priv->mcast.bridged || !bat_priv->mcast.enabled) {
-			batadv_mcast_handler_register(bat_priv, 1);
-			bat_priv->mcast.bridged = false;
-		}
-
+	bridged = batadv_mcast_has_bridge(bat_priv);
+	if (!bridged)
 		goto update;
-	}
-
-	if (bat_priv->mcast.enabled && !bat_priv->mcast.bridged)
-		batadv_mcast_handler_unregister(bat_priv, 1);
-
-	bat_priv->mcast.bridged = true;
 
 #if !IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING)
 	pr_warn_once("No bridge IGMP snooping compiled - multicast optimizations disabled\n");
 #endif
 
+	querier4.exists = br_multicast_has_querier_anywhere(dev, ETH_P_IP);
+	querier4.shadowing = br_multicast_has_querier_adjacent(dev, ETH_P_IP);
+
+	querier6.exists = br_multicast_has_querier_anywhere(dev, ETH_P_IPV6);
+	querier6.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
@@ -413,17 +528,34 @@ 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 (!querier4.exists || querier4.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 (!querier6.exists || querier6.shadowing)
 		mcast_data.flags |= BATADV_MCAST_WANT_ALL_IPV6;
 
 update:
+	batadv_mcast_bridge_log(bat_priv, bridged, &querier4, &querier6);
+
+	bat_priv->mcast.querier_ipv4.exists = querier4.exists;
+	bat_priv->mcast.querier_ipv4.shadowing = querier4.shadowing;
+
+	bat_priv->mcast.querier_ipv6.exists = querier6.exists;
+	bat_priv->mcast.querier_ipv6.shadowing = querier6.shadowing;
+
+	if (bridged) {
+		if (!bat_priv->mcast.enabled || !bat_priv->mcast.bridged)
+			batadv_mcast_handler_register(bat_priv, 1);
+	} else {
+		if (bat_priv->mcast.bridged)
+			batadv_mcast_handler_unregister(bat_priv, 1);
+	}
+
+	bat_priv->mcast.bridged = bridged;
+
 	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));
 		batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 2,
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
index 7679f3a..2d0cd6f 100644
--- a/net/batman-adv/soft-interface.c
+++ b/net/batman-adv/soft-interface.c
@@ -791,6 +791,10 @@ 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.exists = false;
+	bat_priv->mcast.querier_ipv4.shadowing = false;
+	bat_priv->mcast.querier_ipv6.exists = false;
+	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);
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index f793c57..330e9de 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -743,12 +743,25 @@ 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
+ * @querier_ipv6: the current state of an MLD querier in the mesh
  * @flags: the flags we have last sent in our mcast tvlv
  * @enabled: whether the multicast tvlv is currently enabled
  * @bridged: whether the soft interface has a bridge on top
@@ -764,6 +777,8 @@ 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;
 	u8 flags;
 	bool enabled;
 	bool bridged;
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [B.A.T.M.A.N.] [PATCHv12 4/4] batman-adv: Add debugfs table for mcast flags
  2016-03-16  4:06 [B.A.T.M.A.N.] [PATCHv12 0/4] Multicast optimizations for bridges Linus Lüssing
                   ` (2 preceding siblings ...)
  2016-03-16  4:06 ` [B.A.T.M.A.N.] [PATCHv12 3/4] batman-adv: Adding logging of mcast flag changes Linus Lüssing
@ 2016-03-16  4:06 ` Linus Lüssing
  3 siblings, 0 replies; 14+ messages in thread
From: Linus Lüssing @ 2016-03-16  4:06 UTC (permalink / raw)
  To: b.a.t.m.a.n

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.

Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
---
 net/batman-adv/debugfs.c   |   21 +++++++++
 net/batman-adv/multicast.c |  101 ++++++++++++++++++++++++++++++++++++++++++++
 net/batman-adv/multicast.h |    2 +
 3 files changed, 124 insertions(+)

diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c
index 3dc5208..2cbff01 100644
--- a/net/batman-adv/debugfs.c
+++ b/net/batman-adv/debugfs.c
@@ -48,6 +48,7 @@
 #include "distributed-arp-table.h"
 #include "gateway_client.h"
 #include "icmp_socket.h"
+#include "multicast.h"
 #include "network-coding.h"
 #include "originator.h"
 #include "translation-table.h"
@@ -363,6 +364,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),		\
@@ -404,6 +419,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_neighbors,
@@ -421,6 +439,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,
 };
 
diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c
index 41ba2e8..f68903f 100644
--- a/net/batman-adv/multicast.c
+++ b/net/batman-adv/multicast.c
@@ -50,6 +50,8 @@
 #include <net/ipv6.h>
 #include <net/mld.h>
 
+#include "hard-interface.h"
+#include "hash.h"
 #include "packet.h"
 #include "translation-table.h"
 
@@ -1169,6 +1171,105 @@ 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.
+ */
+static void batadv_mcast_flags_print_header(struct batadv_priv *bat_priv,
+					    struct seq_file *seq)
+{
+	u8 flags = bat_priv->mcast.flags;
+	char querier4, querier6, shadowing4, shadowing6;
+	bool bridged = bat_priv->mcast.bridged;
+
+	if (bridged) {
+		querier4 = bat_priv->mcast.querier_ipv4.exists ? '.' : '4';
+		querier6 = bat_priv->mcast.querier_ipv6.exists ? '.' : '6';
+		shadowing4 = bat_priv->mcast.querier_ipv4.shadowing ? '4' : '.';
+		shadowing6 = bat_priv->mcast.querier_ipv6.shadowing ? '6' : '.';
+	} else {
+		querier4 = '?';
+		querier6 = '?';
+		shadowing4 = '?';
+		shadowing6 = '?';
+	}
+
+	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 ? 'U' : '.');
+	seq_printf(seq, "* No IGMP/MLD Querier [4/6]:\t\t%c/%c\n",
+		   querier4, querier6);
+	seq_printf(seq, "* Shadowing IGMP/MLD Querier [4/6]:\t%c/%c\n",
+		   shadowing4, shadowing6);
+	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.
+ */
+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;
+	u8 flags;
+	u32 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();
+	}
+
+	batadv_hardif_put(primary_if);
+
+	return 0;
+}
+
+/**
  * batadv_mcast_free - free the multicast optimizations structures
  * @bat_priv: the bat priv with all the soft interface information
  */
diff --git a/net/batman-adv/multicast.h b/net/batman-adv/multicast.h
index 80bceec..df744c6 100644
--- a/net/batman-adv/multicast.h
+++ b/net/batman-adv/multicast.h
@@ -46,6 +46,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);
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [B.A.T.M.A.N.] [PATCHv12 1/4] batman-adv: Always flood IGMP/MLD reports
  2016-03-16  4:06 ` [B.A.T.M.A.N.] [PATCHv12 1/4] batman-adv: Always flood IGMP/MLD reports Linus Lüssing
@ 2016-04-07 13:03   ` Simon Wunderlich
  2016-04-07 13:24     ` Sven Eckelmann
                       ` (2 more replies)
  0 siblings, 3 replies; 14+ messages in thread
From: Simon Wunderlich @ 2016-04-07 13:03 UTC (permalink / raw)
  To: b.a.t.m.a.n

[-- Attachment #1: Type: text/plain, Size: 2214 bytes --]

On Wednesday 16 March 2016 05:06:42 Linus Lüssing wrote:
> With this patch IGMP or MLD reports are always flooded. This is
> necessary for the upcoming bridge integration to function without
> multicast packet loss.
> 
> With the report handling so far bridges might miss interested multicast
> listeners, leading to wrongly excluding ports from multicast packet
> forwarding.
> 
> Currently we are treating IGMP/MLD reports, the messages bridges use to
> learn about interested multicast listeners, just as any other multicast
> packet: We try to send them to nodes matching its multicast destination.
> 
> Unfortunately, the destination address of reports of the older
> IGMPv2/MLDv1 protocol families do not strictly adhere to their own
> protocol: More precisely, the interested receiver, an IGMPv2 or MLDv1
> querier, itself usually does not listen to the multicast destination
> address of any reports.
> 
> Therefore with this patch we are simply excluding IGMP/MLD reports from
> the multicast forwarding code path and keep flooding them. By that
> any bridge receives them and can properly learn about listeners.
> 
> To avoid compatibility issues with older nodes not yet implementing this
> report handling, we need to force them to flood reports: We do this by
> bumping the multicast TVLV version to 2, effectively disabling their
> multicast optimization.

It looks like you register two containers, one with version 1 and version 2, 
both with the same content. Do we really need to have both containers? 
Wouldn't it be sufficient to register only version 2 and leave version 1 out?

Other than that, I just found a minor style thing, see below.

> 
> Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
> diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c
> index 8caa2c7..19cc404 100644
> --- a/net/batman-adv/multicast.c
> +++ b/net/batman-adv/multicast.c
> @@ -320,6 +391,31 @@ static int batadv_mcast_forw_mode_check_ipv4(struct
> batadv_priv *bat_priv, return 0;
>  }
> 
> +#if IS_ENABLED(CONFIG_IPV6)
> +/**
> + * batadv_mcast_is_report_ipv6 - check for MLD reports

put a space after the dash?

Cheers,
     Simon

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [B.A.T.M.A.N.] [PATCHv12 1/4] batman-adv: Always flood IGMP/MLD reports
  2016-04-07 13:03   ` Simon Wunderlich
@ 2016-04-07 13:24     ` Sven Eckelmann
  2016-04-10 12:07     ` Linus Lüssing
  2016-04-19  2:12     ` Linus Lüssing
  2 siblings, 0 replies; 14+ messages in thread
From: Sven Eckelmann @ 2016-04-07 13:24 UTC (permalink / raw)
  To: Linus Lüssing; +Cc: b.a.t.m.a.n

[-- Attachment #1: Type: text/plain, Size: 822 bytes --]

On Thursday 07 April 2016 15:03:11 Simon Wunderlich wrote:
> > 
> > Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
> > diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c
> > index 8caa2c7..19cc404 100644
> > --- a/net/batman-adv/multicast.c
> > +++ b/net/batman-adv/multicast.c
> > @@ -320,6 +391,31 @@ static int batadv_mcast_forw_mode_check_ipv4(struct
> > batadv_priv *bat_priv, return 0;
> >  }
> > 
> > +#if IS_ENABLED(CONFIG_IPV6)
> > +/**
> > + * batadv_mcast_is_report_ipv6 - check for MLD reports
> 
> put a space after the dash?

Just to clarify: There is some kind of symbol but it is not a simple space.
Right now it is 0xe280af [1]. This is a little bit too fancy for a simple
space (0x20).

Kind regards,
	Sven

[1] https://vazor.com/unicode/c202F.html

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [B.A.T.M.A.N.] [PATCHv12 2/4] batman-adv: Add multicast optimization support for bridged setups
  2016-03-16  4:06 ` [B.A.T.M.A.N.] [PATCHv12 2/4] batman-adv: Add multicast optimization support for bridged setups Linus Lüssing
@ 2016-04-07 13:43   ` Sven Eckelmann
  2016-04-07 13:50   ` Sven Eckelmann
  1 sibling, 0 replies; 14+ messages in thread
From: Sven Eckelmann @ 2016-04-07 13:43 UTC (permalink / raw)
  To: b.a.t.m.a.n

[-- Attachment #1: Type: text/plain, Size: 336 bytes --]

On Wednesday 16 March 2016 05:06:43 Linus Lüssing wrote:
> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
> +
> +#define pr_warn_once(fmt, ...) \
> +       printk_once(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
> +
> +#endif /* < KERNEL_VERSION(2, 6, 38) */
> +

Please drop support for kernels < 3.2.

Kind regards,
	Sven

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [B.A.T.M.A.N.] [PATCHv12 2/4] batman-adv: Add multicast optimization support for bridged setups
  2016-03-16  4:06 ` [B.A.T.M.A.N.] [PATCHv12 2/4] batman-adv: Add multicast optimization support for bridged setups Linus Lüssing
  2016-04-07 13:43   ` Sven Eckelmann
@ 2016-04-07 13:50   ` Sven Eckelmann
  2016-04-19  2:22     ` Linus Lüssing
  1 sibling, 1 reply; 14+ messages in thread
From: Sven Eckelmann @ 2016-04-07 13:50 UTC (permalink / raw)
  To: b.a.t.m.a.n

[-- Attachment #1: Type: text/plain, Size: 1290 bytes --]

On Wednesday 16 March 2016 05:06:43 Linus Lüssing wrote:
> --- /dev/null
> +++ b/compat-sources/net/bridge/br_multicast.c
> @@ -0,0 +1,33 @@
> +#include <linux/if_bridge.h>
> +#include <linux/printk.h>
> +
> +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0) || \
> +    LINUX_VERSION_CODE == KERNEL_VERSION(3, 16, 0) && \
> +       (!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 /* < KERNEL_VERSION(3, 16, 0) ||
> +       * !IS_ENABLED(CONFIG_BRIDGE) ||
> +       * !IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING) */
> +
> +#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;
> +}
> +
> +#endif /* < KERNEL_VERSION(3, 17, 0) */

Why must there be a special compat-sources/*.c file for these tiny
functions?

Kind regards,
	Sven

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [B.A.T.M.A.N.] [PATCHv12 1/4] batman-adv: Always flood IGMP/MLD reports
  2016-04-07 13:03   ` Simon Wunderlich
  2016-04-07 13:24     ` Sven Eckelmann
@ 2016-04-10 12:07     ` Linus Lüssing
  2016-04-10 12:43       ` Linus Lüssing
  2016-04-19  2:12     ` Linus Lüssing
  2 siblings, 1 reply; 14+ messages in thread
From: Linus Lüssing @ 2016-04-10 12:07 UTC (permalink / raw)
  To: The list for a Better Approach To Mobile Ad-hoc Networking

On Thu, Apr 07, 2016 at 03:03:11PM +0200, Simon Wunderlich wrote:
> > To avoid compatibility issues with older nodes not yet implementing this
> > report handling, we need to force them to flood reports: We do this by
> > bumping the multicast TVLV version to 2, effectively disabling their
> > multicast optimization.
> 
> It looks like you register two containers, one with version 1 and version 2, 
> both with the same content. Do we really need to have both containers? 
> Wouldn't it be sufficient to register only version 2 and leave version 1 out?

The idea is to avoid unexpected regressions:

The advantage of registering two containers lies in a setup where you have a
non-bridged network with multicast TVLV v1 and rely on these optimizations.
If a new node with both containers is added, then the old nodes
won't mind. If only container v2 were registered, old nodes would
switch to flooding. Which might be seen as an undesired
regression, depending on the requirements of the network.

Note that if the new node has a bridge, then the TVLV v1 container
gets unregistered and only the v2 one is announced (see PATCH 2/4).
This is because we have to, otherwise we might have packet loss
(which is even worse than flooding).


The registeration of two containers is probably a minor improvement.
Haven't heard of any actual setups which would need it yet. On the
other hand it's not that much extra code, so it felt like a good
idea to add it for some extra compatibility courtesy.


I don't feel that strongly for it though. Let me know what you or
others would prefer.

Regards, Linus

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [B.A.T.M.A.N.] [PATCHv12 1/4] batman-adv: Always flood IGMP/MLD reports
  2016-04-10 12:07     ` Linus Lüssing
@ 2016-04-10 12:43       ` Linus Lüssing
  0 siblings, 0 replies; 14+ messages in thread
From: Linus Lüssing @ 2016-04-10 12:43 UTC (permalink / raw)
  To: The list for a Better Approach To Mobile Ad-hoc Networking

On Sun, Apr 10, 2016 at 02:07:48PM +0200, Linus Lüssing wrote:
> On Thu, Apr 07, 2016 at 03:03:11PM +0200, Simon Wunderlich wrote:
> > > To avoid compatibility issues with older nodes not yet implementing this
> > > report handling, we need to force them to flood reports: We do this by
> > > bumping the multicast TVLV version to 2, effectively disabling their
> > > multicast optimization.
> > 
> > It looks like you register two containers, one with version 1 and version 2, 
> > both with the same content. Do we really need to have both containers? 
> > Wouldn't it be sufficient to register only version 2 and leave version 1 out?
> 
> The idea is to avoid unexpected regressions:
> 
[...]
> The registeration of two containers is probably a minor improvement.
> Haven't heard of any actual setups which would need it yet. On the
> other hand it's not that much extra code, so it felt like a good
> idea to add it for some extra compatibility courtesy.

My intention would be to have it just as a transitional mechanism,
too. Removing it in 2-4 years maybe.

Regards, Linus

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [B.A.T.M.A.N.] [PATCHv12 1/4] batman-adv: Always flood IGMP/MLD reports
  2016-04-07 13:03   ` Simon Wunderlich
  2016-04-07 13:24     ` Sven Eckelmann
  2016-04-10 12:07     ` Linus Lüssing
@ 2016-04-19  2:12     ` Linus Lüssing
  2 siblings, 0 replies; 14+ messages in thread
From: Linus Lüssing @ 2016-04-19  2:12 UTC (permalink / raw)
  To: The list for a Better Approach To Mobile Ad-hoc Networking

On Thu, Apr 07, 2016 at 03:03:11PM +0200, Simon Wunderlich wrote:
> It looks like you register two containers, one with version 1 and version 2, 
> both with the same content. Do we really need to have both containers? 
> Wouldn't it be sufficient to register only version 2 and leave version 1 out?

After a short off-list discussion with Simon, removed it now to simplify
things, avoiding minor corner case optimizations.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [B.A.T.M.A.N.] [PATCHv12 2/4] batman-adv: Add multicast optimization support for bridged setups
  2016-04-07 13:50   ` Sven Eckelmann
@ 2016-04-19  2:22     ` Linus Lüssing
  2016-04-24  7:07       ` Sven Eckelmann
  0 siblings, 1 reply; 14+ messages in thread
From: Linus Lüssing @ 2016-04-19  2:22 UTC (permalink / raw)
  To: The list for a Better Approach To Mobile Ad-hoc Networking

On Thu, Apr 07, 2016 at 03:50:39PM +0200, Sven Eckelmann wrote:
> On Wednesday 16 March 2016 05:06:43 Linus Lüssing wrote:
> > --- /dev/null
> > +++ b/compat-sources/net/bridge/br_multicast.c
> > @@ -0,0 +1,33 @@
> > +#include <linux/if_bridge.h>
> > +#include <linux/printk.h>
> > +
> > +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0) || \
> > +    LINUX_VERSION_CODE == KERNEL_VERSION(3, 16, 0) && \
> > +       (!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 /* < KERNEL_VERSION(3, 16, 0) ||
> > +       * !IS_ENABLED(CONFIG_BRIDGE) ||
> > +       * !IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING) */
> > +
> > +#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;
> > +}
> > +
> > +#endif /* < KERNEL_VERSION(3, 17, 0) */
> 
> Why must there be a special compat-sources/*.c file for these tiny
> functions?

I think it is necessary because of the missing stubs in 3.16.
3.16 has the function declarations in if_bridge.h but without
function definitions if the kernel is build without bridge
(snooping) support.

Adding simple inline functions in compat-include/linux/if_bridge.h
for these functions would yield a compile-time error for the 3.16
kernel without bridge (snooping) support case: The declaration is
always there in 3.16's if_bridge.h include which would clash
with a self-made inline function in our compat-include's
if_bridge.h.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [B.A.T.M.A.N.] [PATCHv12 2/4] batman-adv: Add multicast optimization support for bridged setups
  2016-04-19  2:22     ` Linus Lüssing
@ 2016-04-24  7:07       ` Sven Eckelmann
  0 siblings, 0 replies; 14+ messages in thread
From: Sven Eckelmann @ 2016-04-24  7:07 UTC (permalink / raw)
  To: b.a.t.m.a.n

[-- Attachment #1: Type: text/plain, Size: 941 bytes --]

On Tuesday 19 April 2016 04:22:18 Linus Lüssing wrote:
[...]
> > Why must there be a special compat-sources/*.c file for these tiny
> > functions?
> 
> I think it is necessary because of the missing stubs in 3.16.
> 3.16 has the function declarations in if_bridge.h but without
> function definitions if the kernel is build without bridge
> (snooping) support.
> 
> Adding simple inline functions in compat-include/linux/if_bridge.h
> for these functions would yield a compile-time error for the 3.16
> kernel without bridge (snooping) support case: The declaration is
> always there in 3.16's if_bridge.h include which would clash
> with a self-made inline function in our compat-include's
> if_bridge.h.

Couldn't you #define the names to a different name which you then use. An
example for this can be found at

https://git.open-mesh.org/batman-adv.git/blob/HEAD:/compat-include/linux/ethtool.h

Kind regards,
	Sven

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2016-04-24  7:07 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-03-16  4:06 [B.A.T.M.A.N.] [PATCHv12 0/4] Multicast optimizations for bridges Linus Lüssing
2016-03-16  4:06 ` [B.A.T.M.A.N.] [PATCHv12 1/4] batman-adv: Always flood IGMP/MLD reports Linus Lüssing
2016-04-07 13:03   ` Simon Wunderlich
2016-04-07 13:24     ` Sven Eckelmann
2016-04-10 12:07     ` Linus Lüssing
2016-04-10 12:43       ` Linus Lüssing
2016-04-19  2:12     ` Linus Lüssing
2016-03-16  4:06 ` [B.A.T.M.A.N.] [PATCHv12 2/4] batman-adv: Add multicast optimization support for bridged setups Linus Lüssing
2016-04-07 13:43   ` Sven Eckelmann
2016-04-07 13:50   ` Sven Eckelmann
2016-04-19  2:22     ` Linus Lüssing
2016-04-24  7:07       ` Sven Eckelmann
2016-03-16  4:06 ` [B.A.T.M.A.N.] [PATCHv12 3/4] batman-adv: Adding logging of mcast flag changes Linus Lüssing
2016-03-16  4:06 ` [B.A.T.M.A.N.] [PATCHv12 4/4] batman-adv: Add debugfs table for mcast flags Linus Lüssing

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.