1721 lines
54 KiB
Diff
1721 lines
54 KiB
Diff
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/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..4d7ab67
|
||
--- /dev/null
|
||
+++ b/compat-include/linux/if_bridge.h
|
||
@@ -0,0 +1,65 @@
|
||
+#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)
|
||
+
|
||
+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, 16, 0) || \
|
||
+ LINUX_VERSION_CODE == KERNEL_VERSION(3, 16, 0) && \
|
||
+ (!IS_ENABLED(CONFIG_BRIDGE) || \
|
||
+ !IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING))
|
||
+
|
||
+#define br_multicast_list_adjacent(dev, br_ip_list) \
|
||
+ batadv_br_multicast_list_adjacent(dev, br_ip_list)
|
||
+
|
||
+#define br_multicast_has_querier_adjacent(dev, proto) \
|
||
+ batadv_br_multicast_has_querier_adjacent(dev, proto)
|
||
+
|
||
+static inline int batadv_br_multicast_list_adjacent(struct net_device *dev,
|
||
+ struct list_head *br_ip_list)
|
||
+{
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static inline bool batadv_br_multicast_has_querier_adjacent(struct net_device *dev, int proto)
|
||
+{
|
||
+ return false;
|
||
+}
|
||
+
|
||
+#endif /* < KERNEL_VERSION(3, 16, 0) ||
|
||
+ * == KERNEL_VERSION(3, 16, 0) &&
|
||
+ * (!IS_ENABLED(CONFIG_BRIDGE) ||
|
||
+ * !IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING)) */
|
||
+
|
||
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
|
||
+
|
||
+static inline 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) */
|
||
+
|
||
+#endif /* _NET_BATMAN_ADV_COMPAT_LINUX_IF_BRIDGE_H_ */
|
||
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-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/debugfs.c b/net/batman-adv/debugfs.c
|
||
index 48253cf..9bf37bf 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/main.h b/net/batman-adv/main.h
|
||
index db45336..c4572b3 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 8caa2c7..0d732d7 100644
|
||
--- a/net/batman-adv/multicast.c
|
||
+++ b/net/batman-adv/multicast.c
|
||
@@ -25,7 +25,10 @@
|
||
#include <linux/errno.h>
|
||
#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>
|
||
#include <linux/in.h>
|
||
#include <linux/ip.h>
|
||
@@ -43,18 +46,55 @@
|
||
#include <linux/string.h>
|
||
#include <linux/types.h>
|
||
#include <net/addrconf.h>
|
||
+#include <net/ip.h>
|
||
#include <net/ipv6.h>
|
||
+#include <net/mld.h>
|
||
|
||
+#include "hard-interface.h"
|
||
+#include "hash.h"
|
||
#include "packet.h"
|
||
#include "translation-table.h"
|
||
|
||
/**
|
||
+ * 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.
|
||
@@ -62,12 +102,13 @@
|
||
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;
|
||
@@ -78,7 +119,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;
|
||
}
|
||
@@ -104,6 +148,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
|
||
@@ -214,44 +335,195 @@ static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv)
|
||
}
|
||
|
||
/**
|
||
+ * batadv_mcast_querier_log - debug output regarding the querier status on link
|
||
+ * @bat_priv: the bat priv with all the soft interface information
|
||
+ * @str_proto: a string for the querier protocol (e.g. "IGMP" or "MLD")
|
||
+ * @old_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
|
||
*
|
||
* 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 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));
|
||
|
||
- /* Avoid attaching MLAs, if there is a bridge on top of our soft
|
||
- * interface, we don't support that yet (TODO)
|
||
+ bridged = batadv_mcast_has_bridge(bat_priv);
|
||
+ if (!bridged)
|
||
+ goto update;
|
||
+
|
||
+#if !IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING)
|
||
+ pr_warn_once("No bridge IGMP snooping compiled - multicast optimizations disabled\n");
|
||
+#endif
|
||
+
|
||
+ 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
|
||
+ * 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 (batadv_mcast_has_bridge(bat_priv)) {
|
||
- if (bat_priv->mcast.enabled) {
|
||
- batadv_tvlv_container_unregister(bat_priv,
|
||
- BATADV_TVLV_MCAST, 1);
|
||
- bat_priv->mcast.enabled = false;
|
||
- }
|
||
+ if (!querier4.exists || querier4.shadowing)
|
||
+ mcast_data.flags |= BATADV_MCAST_WANT_ALL_IPV4;
|
||
|
||
- return false;
|
||
- }
|
||
+ 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;
|
||
+
|
||
+ bat_priv->mcast.bridged = bridged;
|
||
|
||
if (!bat_priv->mcast.enabled ||
|
||
mcast_data.flags != bat_priv->mcast.flags) {
|
||
- batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 1,
|
||
+ batadv_mcast_flags_log(bat_priv, mcast_data.flags);
|
||
+ batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 2,
|
||
&mcast_data, sizeof(mcast_data));
|
||
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));
|
||
}
|
||
|
||
/**
|
||
@@ -274,6 +546,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);
|
||
@@ -283,6 +559,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 +605,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 +624,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 +670,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 +689,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 +718,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 +1062,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,19 +1123,118 @@ 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_register(bat_priv, batadv_mcast_tvlv_ogm_handler,
|
||
+ NULL, BATADV_TVLV_MCAST, 2,
|
||
BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
|
||
}
|
||
|
||
/**
|
||
+ * 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
|
||
*/
|
||
void batadv_mcast_free(struct batadv_priv *bat_priv)
|
||
{
|
||
- batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_MCAST, 1);
|
||
- batadv_tvlv_handler_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, 2);
|
||
|
||
spin_lock_bh(&bat_priv->tt.commit_lock);
|
||
batadv_mcast_mla_tt_retract(bat_priv, NULL);
|
||
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);
|
||
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
|
||
index 8a136b6..1ab46f3 100644
|
||
--- a/net/batman-adv/soft-interface.c
|
||
+++ b/net/batman-adv/soft-interface.c
|
||
@@ -795,6 +795,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 1e47fbe..0ed0bee 100644
|
||
--- a/net/batman-adv/types.h
|
||
+++ b/net/batman-adv/types.h
|
||
@@ -745,14 +745,28 @@ struct batadv_priv_dat {
|
||
|
||
#ifdef CONFIG_BATMAN_ADV_MCAST
|
||
/**
|
||
+ * struct batadv_mcast_querier_state - IGMP/MLD querier state when bridged
|
||
+ * @exists: whether a querier exists in the mesh
|
||
+ * @shadowing: if a querier exists, whether it is potentially shadowing
|
||
+ * multicast listeners (i.e. querier is behind our own bridge segment)
|
||
+ */
|
||
+struct batadv_mcast_querier_state {
|
||
+ bool exists;
|
||
+ bool shadowing;
|
||
+};
|
||
+
|
||
+/**
|
||
* struct batadv_priv_mcast - per mesh interface mcast data
|
||
* @mla_list: list of multicast addresses we are currently announcing via TT
|
||
* @want_all_unsnoopables_list: a list of orig_nodes wanting all unsnoopable
|
||
* multicast traffic
|
||
* @want_all_ipv4_list: a list of orig_nodes wanting all IPv4 multicast traffic
|
||
* @want_all_ipv6_list: a list of orig_nodes wanting all IPv6 multicast traffic
|
||
+ * @querier_ipv4: the current state of an IGMP querier in the mesh
|
||
+ * @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
|
||
* @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,8 +779,11 @@ struct batadv_priv_mcast {
|
||
struct hlist_head want_all_unsnoopables_list;
|
||
struct hlist_head want_all_ipv4_list;
|
||
struct hlist_head want_all_ipv6_list;
|
||
+ struct batadv_mcast_querier_state querier_ipv4;
|
||
+ struct batadv_mcast_querier_state querier_ipv6;
|
||
u8 flags;
|
||
bool enabled;
|
||
+ bool bridged;
|
||
atomic_t num_disabled;
|
||
atomic_t num_want_all_unsnoopables;
|
||
atomic_t num_want_all_ipv4;
|