diff --git a/patches/openwrt/0016-kernel-bridge-Implement-MLD-Querier-wake-up-calls-Android-bug-workaround.patch b/patches/openwrt/0016-kernel-bridge-Implement-MLD-Querier-wake-up-calls-Android-bug-workaround.patch new file mode 100644 index 0000000000000000000000000000000000000000..ad9cc4f1546dfef308aec986c7fe0c506e57708e --- /dev/null +++ b/patches/openwrt/0016-kernel-bridge-Implement-MLD-Querier-wake-up-calls-Android-bug-workaround.patch @@ -0,0 +1,2605 @@ +From: Linus Lüssing <linus.luessing@c0d3.blue> +Date: Tue, 30 Jun 2020 18:01:56 +0200 +Subject: kernel: bridge: Implement MLD Querier wake-up calls / Android bug workaround + +Implement a configurable MLD Querier wake-up calls "feature" which +works around a widely spread Android bug in connection with IGMP/MLD +snooping. + +Currently there are mobile devices (e.g. Android) which are not able +to receive and respond to MLD Queries reliably because the Wifi driver +filters a lot of ICMPv6 when the device is asleep - including +MLD. This in turn breaks IPv6 communication when MLD Snooping is +enabled. However there is one ICMPv6 type which is allowed to pass and +which can be used to wake up the mobile device: ICMPv6 Echo Requests. + +If this bridge is the selected MLD Querier then setting +"multicast_wakeupcall" to a number n greater than 0 will send n +ICMPv6 Echo Requests to each host behind this port to wake +them up with each MLD Query. Upon receiving a matching ICMPv6 Echo +Reply an MLD Query with a unicast ethernet destination will be sent +to the specific host(s). + +Link: https://issuetracker.google.com/issues/149630944 +Link: https://github.com/freifunk-gluon/gluon/issues/1832 + +Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue> + +diff --git a/package/network/config/netifd/patches/0001-bridge-Add-multicast_wakeupcall-option.patch b/package/network/config/netifd/patches/0001-bridge-Add-multicast_wakeupcall-option.patch +new file mode 100644 +index 0000000000000000000000000000000000000000..7ae2960d51d78b03be368bbfd17aff9219da524d +--- /dev/null ++++ b/package/network/config/netifd/patches/0001-bridge-Add-multicast_wakeupcall-option.patch +@@ -0,0 +1,169 @@ ++From 026c823dc34c34393498f10a489ea8773866e9e4 Mon Sep 17 00:00:00 2001 ++From: =?UTF-8?q?Linus=20L=C3=BCssing?= <linus.luessing@c0d3.blue> ++Date: Sun, 5 Jul 2020 23:33:51 +0200 ++Subject: [PATCH] bridge: Add multicast_wakeupcall option ++MIME-Version: 1.0 ++Content-Type: text/plain; charset=UTF-8 ++Content-Transfer-Encoding: 8bit ++ ++This makes the new per bridge port multicast_wakeupcall feature ++for the Linux bridge configurable for wireless interfaces and enables it ++by default for an AP interface. ++ ++The MLD Querier wake-up calls "feature" works around a widely spread Android ++bug in connection with IGMP/MLD snooping. ++ ++Currently there are mobile devices (e.g. Android) which are not able ++to receive and respond to MLD Queries reliably because the Wifi driver ++filters a lot of ICMPv6 when the device is asleep - including ++MLD. This in turn breaks IPv6 communication when MLD Snooping is ++enabled. However there is one ICMPv6 type which is allowed to pass and ++which can be used to wake up the mobile device: ICMPv6 Echo Requests. ++ ++If this bridge is the selected MLD Querier then setting ++"multicast_wakeupcall" to a number n greater than 0 will send n ++ICMPv6 Echo Requests to each host behind this port to wake ++them up with each MLD Query. Upon receiving a matching ICMPv6 Echo ++Reply an MLD Query with a unicast ethernet destination will be sent ++to the specific host(s). ++ ++Link: https://issuetracker.google.com/issues/149630944 ++Link: https://github.com/freifunk-gluon/gluon/issues/1832 ++ ++Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue> ++--- ++ device.c | 9 +++++++++ ++ device.h | 21 ++++++++++++--------- ++ system-linux.c | 13 +++++++++++++ ++ 3 files changed, 34 insertions(+), 9 deletions(-) ++ ++diff --git a/device.c b/device.c ++index 128151a..2e8c24c 100644 ++--- a/device.c +++++ b/device.c ++@@ -50,6 +50,7 @@ static const struct blobmsg_policy dev_attrs[__DEV_ATTR_MAX] = { ++ [DEV_ATTR_NEIGHGCSTALETIME] = { .name = "neighgcstaletime", .type = BLOBMSG_TYPE_INT32 }, ++ [DEV_ATTR_DADTRANSMITS] = { .name = "dadtransmits", .type = BLOBMSG_TYPE_INT32 }, ++ [DEV_ATTR_MULTICAST_TO_UNICAST] = { .name = "multicast_to_unicast", .type = BLOBMSG_TYPE_BOOL }, +++ [DEV_ATTR_MULTICAST_WAKEUPCALL] = { .name = "multicast_wakeupcall", .type = BLOBMSG_TYPE_INT32 }, ++ [DEV_ATTR_MULTICAST_ROUTER] = { .name = "multicast_router", .type = BLOBMSG_TYPE_INT32 }, ++ [DEV_ATTR_MULTICAST_FAST_LEAVE] = { .name = "multicast_fast_leave", . type = BLOBMSG_TYPE_BOOL }, ++ [DEV_ATTR_MULTICAST] = { .name ="multicast", .type = BLOBMSG_TYPE_BOOL }, ++@@ -223,6 +224,7 @@ device_merge_settings(struct device *dev, struct device_settings *n) ++ n->multicast = s->flags & DEV_OPT_MULTICAST ? ++ s->multicast : os->multicast; ++ n->multicast_to_unicast = s->multicast_to_unicast; +++ n->multicast_wakeupcall = s->multicast_wakeupcall; ++ n->multicast_router = s->multicast_router; ++ n->multicast_fast_leave = s->multicast_fast_leave; ++ n->learning = s->learning; ++@@ -330,6 +332,11 @@ device_init_settings(struct device *dev, struct blob_attr **tb) ++ s->flags |= DEV_OPT_MULTICAST_TO_UNICAST; ++ } ++ +++ if ((cur = tb[DEV_ATTR_MULTICAST_WAKEUPCALL])) { +++ s->multicast_wakeupcall = blobmsg_get_u32(cur); +++ s->flags |= DEV_OPT_MULTICAST_WAKEUPCALL; +++ } +++ ++ if ((cur = tb[DEV_ATTR_MULTICAST_ROUTER])) { ++ s->multicast_router = blobmsg_get_u32(cur); ++ if (s->multicast_router <= 2) ++@@ -1028,6 +1035,8 @@ device_dump_status(struct blob_buf *b, struct device *dev) ++ blobmsg_add_u32(b, "dadtransmits", st.dadtransmits); ++ if (st.flags & DEV_OPT_MULTICAST_TO_UNICAST) ++ blobmsg_add_u8(b, "multicast_to_unicast", st.multicast_to_unicast); +++ if (st.flags & DEV_OPT_MULTICAST_WAKEUPCALL) +++ blobmsg_add_u32(b, "multicast_wakeupcall", st.multicast_wakeupcall); ++ if (st.flags & DEV_OPT_MULTICAST_ROUTER) ++ blobmsg_add_u32(b, "multicast_router", st.multicast_router); ++ if (st.flags & DEV_OPT_MULTICAST_FAST_LEAVE) ++diff --git a/device.h b/device.h ++index 5f3fae2..4935db0 100644 ++--- a/device.h +++++ b/device.h ++@@ -42,6 +42,7 @@ enum { ++ DEV_ATTR_NEIGHREACHABLETIME, ++ DEV_ATTR_DADTRANSMITS, ++ DEV_ATTR_MULTICAST_TO_UNICAST, +++ DEV_ATTR_MULTICAST_WAKEUPCALL, ++ DEV_ATTR_MULTICAST_ROUTER, ++ DEV_ATTR_MULTICAST_FAST_LEAVE, ++ DEV_ATTR_MULTICAST, ++@@ -95,15 +96,16 @@ enum { ++ DEV_OPT_MTU6 = (1 << 12), ++ DEV_OPT_DADTRANSMITS = (1 << 13), ++ DEV_OPT_MULTICAST_TO_UNICAST = (1 << 14), ++- DEV_OPT_MULTICAST_ROUTER = (1 << 15), ++- DEV_OPT_MULTICAST = (1 << 16), ++- DEV_OPT_LEARNING = (1 << 17), ++- DEV_OPT_UNICAST_FLOOD = (1 << 18), ++- DEV_OPT_NEIGHGCSTALETIME = (1 << 19), ++- DEV_OPT_MULTICAST_FAST_LEAVE = (1 << 20), ++- DEV_OPT_SENDREDIRECTS = (1 << 21), ++- DEV_OPT_NEIGHLOCKTIME = (1 << 22), ++- DEV_OPT_ISOLATE = (1 << 23), +++ DEV_OPT_MULTICAST_WAKEUPCALL = (1 << 15), +++ DEV_OPT_MULTICAST_ROUTER = (1 << 16), +++ DEV_OPT_MULTICAST = (1 << 17), +++ DEV_OPT_LEARNING = (1 << 18), +++ DEV_OPT_UNICAST_FLOOD = (1 << 19), +++ DEV_OPT_NEIGHGCSTALETIME = (1 << 20), +++ DEV_OPT_MULTICAST_FAST_LEAVE = (1 << 21), +++ DEV_OPT_SENDREDIRECTS = (1 << 22), +++ DEV_OPT_NEIGHLOCKTIME = (1 << 23), +++ DEV_OPT_ISOLATE = (1 << 24), ++ }; ++ ++ /* events broadcasted to all users of a device */ ++@@ -164,6 +166,7 @@ struct device_settings { ++ int neigh4locktime; ++ unsigned int dadtransmits; ++ bool multicast_to_unicast; +++ unsigned int multicast_wakeupcall; ++ unsigned int multicast_router; ++ bool multicast_fast_leave; ++ bool multicast; ++diff --git a/system-linux.c b/system-linux.c ++index acfd40e..f1abccf 100644 ++--- a/system-linux.c +++++ b/system-linux.c ++@@ -355,6 +355,11 @@ static void system_bridge_set_multicast_to_unicast(struct device *dev, const cha ++ system_set_dev_sysctl("/sys/class/net/%s/brport/multicast_to_unicast", dev->ifname, val); ++ } ++ +++static void system_bridge_set_multicast_wakeupcall(struct device *dev, const char *val) +++{ +++ system_set_dev_sysctl("/sys/class/net/%s/brport/multicast_wakeupcall", dev->ifname, val); +++} +++ ++ static void system_bridge_set_multicast_fast_leave(struct device *dev, const char *val) ++ { ++ system_set_dev_sysctl("/sys/class/net/%s/brport/multicast_fast_leave", dev->ifname, val); ++@@ -784,8 +789,10 @@ static char *system_get_bridge(const char *name, char *buf, int buflen) ++ static void ++ system_bridge_set_wireless(struct device *bridge, struct device *dev) ++ { +++ unsigned int mcast_wakeupcall = dev->wireless_ap ? 2 : 0; ++ bool mcast_to_ucast = dev->wireless_ap; ++ bool hairpin = true; +++ char buf[64]; ++ ++ if (bridge->settings.flags & DEV_OPT_MULTICAST_TO_UNICAST && ++ !bridge->settings.multicast_to_unicast) ++@@ -796,6 +803,12 @@ system_bridge_set_wireless(struct device *bridge, struct device *dev) ++ ++ system_bridge_set_multicast_to_unicast(dev, mcast_to_ucast ? "1" : "0"); ++ system_bridge_set_hairpin_mode(dev, hairpin ? "1" : "0"); +++ +++ if (bridge->settings.flags & DEV_OPT_MULTICAST_WAKEUPCALL) +++ mcast_wakeupcall = dev->settings.multicast_wakeupcall; +++ +++ snprintf(buf, sizeof(buf), "%u", mcast_wakeupcall); +++ system_bridge_set_multicast_wakeupcall(dev, buf); ++ } ++ ++ int system_bridge_addif(struct device *bridge, struct device *dev) ++-- ++2.27.0 ++ +diff --git a/target/linux/generic/backport-4.14/111-bridge-simplify-ip_mc_check_igmp-and-ipv6_mc_check_m.patch b/target/linux/generic/backport-4.14/111-bridge-simplify-ip_mc_check_igmp-and-ipv6_mc_check_m.patch +new file mode 100644 +index 0000000000000000000000000000000000000000..e8f60cc4cc267d3bb1c38770b7e1447765514f2b +--- /dev/null ++++ b/target/linux/generic/backport-4.14/111-bridge-simplify-ip_mc_check_igmp-and-ipv6_mc_check_m.patch +@@ -0,0 +1,506 @@ ++From 2a0f1172f505121c75313f6cce2874476b82ce45 Mon Sep 17 00:00:00 2001 ++From: =?UTF-8?q?Linus=20L=C3=BCssing?= <linus.luessing@c0d3.blue> ++Date: Mon, 21 Jan 2019 07:26:25 +0100 ++Subject: [PATCH] bridge: simplify ip_mc_check_igmp() and ++ ipv6_mc_check_mld() calls ++MIME-Version: 1.0 ++Content-Type: text/plain; charset=UTF-8 ++Content-Transfer-Encoding: 8bit ++ ++This patch refactors ip_mc_check_igmp(), ipv6_mc_check_mld() and ++their callers (more precisely, the Linux bridge) to not rely on ++the skb_trimmed parameter anymore. ++ ++An skb with its tail trimmed to the IP packet length was initially ++introduced for the following three reasons: ++ ++1) To be able to verify the ICMPv6 checksum. ++2) To be able to distinguish the version of an IGMP or MLD query. ++ They are distinguishable only by their size. ++3) To avoid parsing data for an IGMPv3 or MLDv2 report that is ++ beyond the IP packet but still within the skb. ++ ++The first case still uses a cloned and potentially trimmed skb to ++verfiy. However, there is no need to propagate it to the caller. ++For the second and third case explicit IP packet length checks were ++added. ++ ++This hopefully makes ip_mc_check_igmp() and ipv6_mc_check_mld() easier ++to read and verfiy, as well as easier to use. ++ ++Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue> ++Signed-off-by: David S. Miller <davem@davemloft.net> ++--- ++ include/linux/igmp.h | 11 +++++++- ++ include/linux/ip.h | 5 ++++ ++ include/linux/ipv6.h | 6 ++++ ++ include/net/addrconf.h | 12 +++++++- ++ net/batman-adv/multicast.c | 4 +-- ++ net/bridge/br_multicast.c | 56 ++++++++++++++++++-------------------- ++ net/ipv4/igmp.c | 23 +++------------- ++ net/ipv6/mcast_snoop.c | 24 +++------------- ++ 8 files changed, 69 insertions(+), 72 deletions(-) ++ ++diff --git a/include/linux/igmp.h b/include/linux/igmp.h ++index f8231854b5d6..858143489a2a 100644 ++--- a/include/linux/igmp.h +++++ b/include/linux/igmp.h ++@@ -18,6 +18,7 @@ ++ #include <linux/skbuff.h> ++ #include <linux/timer.h> ++ #include <linux/in.h> +++#include <linux/ip.h> ++ #include <linux/refcount.h> ++ #include <uapi/linux/igmp.h> ++ ++@@ -106,6 +107,14 @@ struct ip_mc_list { ++ #define IGMPV3_QQIC(value) IGMPV3_EXP(0x80, 4, 3, value) ++ #define IGMPV3_MRC(value) IGMPV3_EXP(0x80, 4, 3, value) ++ +++static inline int ip_mc_may_pull(struct sk_buff *skb, unsigned int len) +++{ +++ if (skb_transport_offset(skb) + ip_transport_len(skb) < len) +++ return -EINVAL; +++ +++ return pskb_may_pull(skb, len); +++} +++ ++ extern int ip_check_mc_rcu(struct in_device *dev, __be32 mc_addr, __be32 src_addr, u8 proto); ++ extern int igmp_rcv(struct sk_buff *); ++ extern int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr); ++@@ -128,6 +137,6 @@ extern void ip_mc_unmap(struct in_device *); ++ extern void ip_mc_remap(struct in_device *); ++ extern void ip_mc_dec_group(struct in_device *in_dev, __be32 addr); ++ extern void ip_mc_inc_group(struct in_device *in_dev, __be32 addr); ++-int ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed); +++int ip_mc_check_igmp(struct sk_buff *skb); ++ ++ #endif ++diff --git a/include/linux/ip.h b/include/linux/ip.h ++index 492bc6513533..482b7b7c9f30 100644 ++--- a/include/linux/ip.h +++++ b/include/linux/ip.h ++@@ -34,4 +34,9 @@ static inline struct iphdr *ipip_hdr(const struct sk_buff *skb) ++ { ++ return (struct iphdr *)skb_transport_header(skb); ++ } +++ +++static inline unsigned int ip_transport_len(const struct sk_buff *skb) +++{ +++ return ntohs(ip_hdr(skb)->tot_len) - skb_network_header_len(skb); +++} ++ #endif /* _LINUX_IP_H */ ++diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h ++index 067a6fa675ed..f2128d94a680 100644 ++--- a/include/linux/ipv6.h +++++ b/include/linux/ipv6.h ++@@ -103,6 +103,12 @@ static inline struct ipv6hdr *ipipv6_hdr(const struct sk_buff *skb) ++ return (struct ipv6hdr *)skb_transport_header(skb); ++ } ++ +++static inline unsigned int ipv6_transport_len(const struct sk_buff *skb) +++{ +++ return ntohs(ipv6_hdr(skb)->payload_len) + sizeof(struct ipv6hdr) - +++ skb_network_header_len(skb); +++} +++ ++ /* ++ This structure contains results of exthdrs parsing ++ as offsets from skb->nh. ++diff --git a/include/net/addrconf.h b/include/net/addrconf.h ++index f30ee99a1d72..0eb1e1f6ea9a 100644 ++--- a/include/net/addrconf.h +++++ b/include/net/addrconf.h ++@@ -49,6 +49,7 @@ struct prefix_info { ++ struct in6_addr prefix; ++ }; ++ +++#include <linux/ipv6.h> ++ #include <linux/netdevice.h> ++ #include <net/if_inet6.h> ++ #include <net/ipv6.h> ++@@ -189,6 +190,15 @@ u32 ipv6_addr_label(struct net *net, const struct in6_addr *addr, ++ /* ++ * multicast prototypes (mcast.c) ++ */ +++static inline int ipv6_mc_may_pull(struct sk_buff *skb, +++ unsigned int len) +++{ +++ if (skb_transport_offset(skb) + ipv6_transport_len(skb) < len) +++ return -EINVAL; +++ +++ return pskb_may_pull(skb, len); +++} +++ ++ int ipv6_sock_mc_join(struct sock *sk, int ifindex, ++ const struct in6_addr *addr); ++ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, ++@@ -207,7 +217,7 @@ void ipv6_mc_unmap(struct inet6_dev *idev); ++ void ipv6_mc_remap(struct inet6_dev *idev); ++ void ipv6_mc_init_dev(struct inet6_dev *idev); ++ void ipv6_mc_destroy_dev(struct inet6_dev *idev); ++-int ipv6_mc_check_mld(struct sk_buff *skb, struct sk_buff **skb_trimmed); +++int ipv6_mc_check_mld(struct sk_buff *skb); ++ void addrconf_dad_failure(struct inet6_ifaddr *ifp); ++ ++ bool ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group, ++diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c ++index d47865e0e697..9b8f118c69f9 100644 ++--- a/net/batman-adv/multicast.c +++++ b/net/batman-adv/multicast.c ++@@ -611,7 +611,7 @@ static void batadv_mcast_mla_update(struct work_struct *work) ++ */ ++ static bool batadv_mcast_is_report_ipv4(struct sk_buff *skb) ++ { ++- if (ip_mc_check_igmp(skb, NULL) < 0) +++ if (ip_mc_check_igmp(skb) < 0) ++ return false; ++ ++ switch (igmp_hdr(skb)->type) { ++@@ -677,7 +677,7 @@ static int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv, ++ */ ++ static bool batadv_mcast_is_report_ipv6(struct sk_buff *skb) ++ { ++- if (ipv6_mc_check_mld(skb, NULL) < 0) +++ if (ipv6_mc_check_mld(skb) < 0) ++ return false; ++ ++ switch (icmp6_hdr(skb)->icmp6_type) { ++diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c ++index b24782d53474..5224e9b3c46d 100644 ++--- a/net/bridge/br_multicast.c +++++ b/net/bridge/br_multicast.c ++@@ -1128,7 +1128,7 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br, ++ ++ for (i = 0; i < num; i++) { ++ len += sizeof(*grec); ++- if (!pskb_may_pull(skb, len)) +++ if (!ip_mc_may_pull(skb, len)) ++ return -EINVAL; ++ ++ grec = (void *)(skb->data + len - sizeof(*grec)); ++@@ -1137,7 +1137,7 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br, ++ nsrcs = ntohs(grec->grec_nsrcs); ++ ++ len += nsrcs * 4; ++- if (!pskb_may_pull(skb, len)) +++ if (!ip_mc_may_pull(skb, len)) ++ return -EINVAL; ++ ++ /* We treat this as an IGMPv2 report for now. */ ++@@ -1176,15 +1176,17 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br, ++ struct sk_buff *skb, ++ u16 vid) ++ { +++ unsigned int nsrcs_offset; ++ const unsigned char *src; ++ struct icmp6hdr *icmp6h; ++ struct mld2_grec *grec; +++ unsigned int grec_len; ++ int i; ++ int len; ++ int num; ++ int err = 0; ++ ++- if (!pskb_may_pull(skb, sizeof(*icmp6h))) +++ if (!ipv6_mc_may_pull(skb, sizeof(*icmp6h))) ++ return -EINVAL; ++ ++ icmp6h = icmp6_hdr(skb); ++@@ -1195,23 +1197,26 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br, ++ __be16 *_nsrcs, __nsrcs; ++ u16 nsrcs; ++ ++- _nsrcs = skb_header_pointer(skb, ++- len + offsetof(struct mld2_grec, ++- grec_nsrcs), +++ nsrcs_offset = len + offsetof(struct mld2_grec, grec_nsrcs); +++ +++ if (skb_transport_offset(skb) + ipv6_transport_len(skb) < +++ nsrcs_offset + sizeof(_nsrcs)) +++ return -EINVAL; +++ +++ _nsrcs = skb_header_pointer(skb, nsrcs_offset, ++ sizeof(__nsrcs), &__nsrcs); ++ if (!_nsrcs) ++ return -EINVAL; ++ ++ nsrcs = ntohs(*_nsrcs); +++ grec_len = sizeof(*grec) + +++ sizeof(struct in6_addr) * nsrcs; ++ ++- if (!pskb_may_pull(skb, ++- len + sizeof(*grec) + ++- sizeof(struct in6_addr) * nsrcs)) +++ if (!ipv6_mc_may_pull(skb, len + grec_len)) ++ return -EINVAL; ++ ++ grec = (struct mld2_grec *)(skb->data + len); ++- len += sizeof(*grec) + ++- sizeof(struct in6_addr) * nsrcs; +++ len += grec_len; ++ ++ /* We treat these as MLDv1 reports for now. */ ++ switch (grec->grec_type) { ++@@ -1403,6 +1408,7 @@ static int br_ip4_multicast_query(struct net_bridge *br, ++ struct sk_buff *skb, ++ u16 vid) ++ { +++ unsigned int transport_len = ip_transport_len(skb); ++ const struct iphdr *iph = ip_hdr(skb); ++ struct igmphdr *ih = igmp_hdr(skb); ++ struct net_bridge_mdb_entry *mp; ++@@ -1412,7 +1418,6 @@ static int br_ip4_multicast_query(struct net_bridge *br, ++ struct br_ip saddr; ++ unsigned long max_delay; ++ unsigned long now = jiffies; ++- unsigned int offset = skb_transport_offset(skb); ++ __be32 group; ++ int err = 0; ++ ++@@ -1423,14 +1428,14 @@ static int br_ip4_multicast_query(struct net_bridge *br, ++ ++ group = ih->group; ++ ++- if (skb->len == offset + sizeof(*ih)) { +++ if (transport_len == sizeof(*ih)) { ++ max_delay = ih->code * (HZ / IGMP_TIMER_SCALE); ++ ++ if (!max_delay) { ++ max_delay = 10 * HZ; ++ group = 0; ++ } ++- } else if (skb->len >= offset + sizeof(*ih3)) { +++ } else if (transport_len >= sizeof(*ih3)) { ++ ih3 = igmpv3_query_hdr(skb); ++ if (ih3->nsrcs) ++ goto out; ++@@ -1482,6 +1487,7 @@ static int br_ip6_multicast_query(struct net_bridge *br, ++ struct sk_buff *skb, ++ u16 vid) ++ { +++ unsigned int transport_len = ipv6_transport_len(skb); ++ struct mld_msg *mld; ++ struct net_bridge_mdb_entry *mp; ++ struct mld2_query *mld2q; ++@@ -1500,7 +1506,7 @@ static int br_ip6_multicast_query(struct net_bridge *br, ++ (port && port->state == BR_STATE_DISABLED)) ++ goto out; ++ ++- if (skb->len == offset + sizeof(*mld)) { +++ if (transport_len == sizeof(*mld)) { ++ if (!pskb_may_pull(skb, offset + sizeof(*mld))) { ++ err = -EINVAL; ++ goto out; ++@@ -1771,12 +1777,11 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, ++ struct sk_buff *skb, ++ u16 vid) ++ { ++- struct sk_buff *skb_trimmed = NULL; ++ const unsigned char *src; ++ struct igmphdr *ih; ++ int err; ++ ++- err = ip_mc_check_igmp(skb, &skb_trimmed); +++ err = ip_mc_check_igmp(skb); ++ ++ if (err == -ENOMSG) { ++ if (!ipv4_is_local_multicast(ip_hdr(skb)->daddr)) { ++@@ -1802,19 +1807,16 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, ++ err = br_ip4_multicast_add_group(br, port, ih->group, vid, src); ++ break; ++ case IGMPV3_HOST_MEMBERSHIP_REPORT: ++- err = br_ip4_multicast_igmp3_report(br, port, skb_trimmed, vid); +++ err = br_ip4_multicast_igmp3_report(br, port, skb, vid); ++ break; ++ case IGMP_HOST_MEMBERSHIP_QUERY: ++- err = br_ip4_multicast_query(br, port, skb_trimmed, vid); +++ err = br_ip4_multicast_query(br, port, skb, vid); ++ break; ++ case IGMP_HOST_LEAVE_MESSAGE: ++ br_ip4_multicast_leave_group(br, port, ih->group, vid, src); ++ break; ++ } ++ ++- if (skb_trimmed && skb_trimmed != skb) ++- kfree_skb(skb_trimmed); ++- ++ br_multicast_count(br, port, skb, BR_INPUT_SKB_CB(skb)->igmp, ++ BR_MCAST_DIR_RX); ++ ++@@ -1827,12 +1829,11 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, ++ struct sk_buff *skb, ++ u16 vid) ++ { ++- struct sk_buff *skb_trimmed = NULL; ++ const unsigned char *src; ++ struct mld_msg *mld; ++ int err; ++ ++- err = ipv6_mc_check_mld(skb, &skb_trimmed); +++ err = ipv6_mc_check_mld(skb); ++ ++ if (err == -ENOMSG) { ++ if (!ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr)) ++@@ -1854,10 +1855,10 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, ++ src); ++ break; ++ case ICMPV6_MLD2_REPORT: ++- err = br_ip6_multicast_mld2_report(br, port, skb_trimmed, vid); +++ err = br_ip6_multicast_mld2_report(br, port, skb, vid); ++ break; ++ case ICMPV6_MGM_QUERY: ++- err = br_ip6_multicast_query(br, port, skb_trimmed, vid); +++ err = br_ip6_multicast_query(br, port, skb, vid); ++ break; ++ case ICMPV6_MGM_REDUCTION: ++ src = eth_hdr(skb)->h_source; ++@@ -1865,9 +1866,6 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, ++ break; ++ } ++ ++- if (skb_trimmed && skb_trimmed != skb) ++- kfree_skb(skb_trimmed); ++- ++ br_multicast_count(br, port, skb, BR_INPUT_SKB_CB(skb)->igmp, ++ BR_MCAST_DIR_RX); ++ ++diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c ++index b6f0ee01f2e0..d6a222032be3 100644 ++--- a/net/ipv4/igmp.c +++++ b/net/ipv4/igmp.c ++@@ -1538,7 +1538,7 @@ 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) +++static int __ip_mc_check_igmp(struct sk_buff *skb) ++ ++ { ++ struct sk_buff *skb_chk; ++@@ -1560,16 +1560,10 @@ static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) ++ if (ret) ++ goto err; ++ ++- if (skb_trimmed) ++- *skb_trimmed = skb_chk; ++- /* free now unneeded clone */ ++- else if (skb_chk != skb) ++- kfree_skb(skb_chk); ++- ++ ret = 0; ++ ++ err: ++- if (ret && skb_chk && skb_chk != skb) +++ if (skb_chk && skb_chk != skb) ++ kfree_skb(skb_chk); ++ ++ return ret; ++@@ -1578,7 +1572,6 @@ static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) ++ /** ++ * 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 transport header accordingly and returns zero. ++@@ -1588,18 +1581,10 @@ static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) ++ * -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). ++- * ++ * Caller needs to set the skb network header and free any returned skb if it ++ * differs from the provided skb. ++ */ ++-int ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) +++int ip_mc_check_igmp(struct sk_buff *skb) ++ { ++ int ret = ip_mc_check_iphdr(skb); ++ ++@@ -1609,7 +1594,7 @@ int ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) ++ if (ip_hdr(skb)->protocol != IPPROTO_IGMP) ++ return -ENOMSG; ++ ++- return __ip_mc_check_igmp(skb, skb_trimmed); +++ return __ip_mc_check_igmp(skb); ++ } ++ EXPORT_SYMBOL(ip_mc_check_igmp); ++ ++diff --git a/net/ipv6/mcast_snoop.c b/net/ipv6/mcast_snoop.c ++index 9405b04eecc6..1a917dc80d5e 100644 ++--- a/net/ipv6/mcast_snoop.c +++++ b/net/ipv6/mcast_snoop.c ++@@ -136,8 +136,7 @@ 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) +++static int __ipv6_mc_check_mld(struct sk_buff *skb) ++ ++ { ++ struct sk_buff *skb_chk = NULL; ++@@ -160,16 +159,10 @@ static int __ipv6_mc_check_mld(struct sk_buff *skb, ++ if (ret) ++ goto err; ++ ++- if (skb_trimmed) ++- *skb_trimmed = skb_chk; ++- /* free now unneeded clone */ ++- else if (skb_chk != skb) ++- kfree_skb(skb_chk); ++- ++ ret = 0; ++ ++ err: ++- if (ret && skb_chk && skb_chk != skb) +++ if (skb_chk && skb_chk != skb) ++ kfree_skb(skb_chk); ++ ++ return ret; ++@@ -178,7 +171,6 @@ static int __ipv6_mc_check_mld(struct sk_buff *skb, ++ /** ++ * 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 transport header accordingly and returns zero. ++@@ -188,18 +180,10 @@ static int __ipv6_mc_check_mld(struct sk_buff *skb, ++ * -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). ++- * ++ * Caller needs to set the skb network header and free any returned skb if it ++ * differs from the provided skb. ++ */ ++-int ipv6_mc_check_mld(struct sk_buff *skb, struct sk_buff **skb_trimmed) +++int ipv6_mc_check_mld(struct sk_buff *skb) ++ { ++ int ret; ++ ++@@ -211,6 +195,6 @@ int ipv6_mc_check_mld(struct sk_buff *skb, struct sk_buff **skb_trimmed) ++ if (ret < 0) ++ return ret; ++ ++- return __ipv6_mc_check_mld(skb, skb_trimmed); +++ return __ipv6_mc_check_mld(skb); ++ } ++ EXPORT_SYMBOL(ipv6_mc_check_mld); ++-- ++2.27.0 ++ +diff --git a/target/linux/generic/backport-4.14/112-bridge-simplify-ip_mc_check_igmp-and-ipv6_mc_check_m.patch b/target/linux/generic/backport-4.14/112-bridge-simplify-ip_mc_check_igmp-and-ipv6_mc_check_m.patch +new file mode 100644 +index 0000000000000000000000000000000000000000..f6478bd664c8e2c366ba11c1fb9690ff77d22223 +--- /dev/null ++++ b/target/linux/generic/backport-4.14/112-bridge-simplify-ip_mc_check_igmp-and-ipv6_mc_check_m.patch +@@ -0,0 +1,234 @@ ++From 96d914ab71c2540e8c492cbe6877854d17a5ec4c Mon Sep 17 00:00:00 2001 ++From: =?UTF-8?q?Linus=20L=C3=BCssing?= <linus.luessing@c0d3.blue> ++Date: Mon, 21 Jan 2019 07:26:26 +0100 ++Subject: [PATCH] bridge: simplify ip_mc_check_igmp() and ++ ipv6_mc_check_mld() internals ++MIME-Version: 1.0 ++Content-Type: text/plain; charset=UTF-8 ++Content-Transfer-Encoding: 8bit ++ ++With this patch the internal use of the skb_trimmed is reduced to ++the ICMPv6/IGMP checksum verification. And for the length checks ++the newly introduced helper functions are used instead of calculating ++and checking with skb->len directly. ++ ++These changes should hopefully make it easier to verify that length ++checks are performed properly. ++ ++Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue> ++Signed-off-by: David S. Miller <davem@davemloft.net> ++--- ++ net/ipv4/igmp.c | 51 +++++++++++++++------------------- ++ net/ipv6/mcast_snoop.c | 62 ++++++++++++++++++++---------------------- ++ 2 files changed, 52 insertions(+), 61 deletions(-) ++ ++diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c ++index d6a222032be3..9033c8cb0e95 100644 ++--- a/net/ipv4/igmp.c +++++ b/net/ipv4/igmp.c ++@@ -1487,22 +1487,22 @@ static int ip_mc_check_igmp_reportv3(struct sk_buff *skb) ++ ++ len += sizeof(struct igmpv3_report); ++ ++- return pskb_may_pull(skb, len) ? 0 : -EINVAL; +++ return ip_mc_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; +++ unsigned int transport_len = ip_transport_len(skb); +++ unsigned int len; ++ ++ /* IGMPv{1,2}? */ ++- if (skb->len != len) { +++ if (transport_len != sizeof(struct igmphdr)) { ++ /* or IGMPv3? */ ++- len += sizeof(struct igmpv3_query) - sizeof(struct igmphdr); ++- if (skb->len < len || !pskb_may_pull(skb, len)) +++ if (transport_len < sizeof(struct igmpv3_query)) +++ return -EINVAL; +++ +++ len = skb_transport_offset(skb) + sizeof(struct igmpv3_query); +++ if (!ip_mc_may_pull(skb, len)) ++ return -EINVAL; ++ } ++ ++@@ -1538,35 +1538,24 @@ 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) ++- +++static int ip_mc_check_igmp_csum(struct sk_buff *skb) ++ { ++- struct sk_buff *skb_chk; ++- unsigned int transport_len; ++ unsigned int len = skb_transport_offset(skb) + sizeof(struct igmphdr); ++- int ret = -EINVAL; +++ unsigned int transport_len = ip_transport_len(skb); +++ struct sk_buff *skb_chk; ++ ++- transport_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb); +++ if (!ip_mc_may_pull(skb, len)) +++ return -EINVAL; ++ ++ skb_chk = skb_checksum_trimmed(skb, transport_len, ++ ip_mc_validate_checksum); ++ if (!skb_chk) ++- goto err; +++ return -EINVAL; ++ ++- if (!pskb_may_pull(skb_chk, len)) ++- goto err; ++- ++- ret = ip_mc_check_igmp_msg(skb_chk); ++- if (ret) ++- goto err; ++- ++- ret = 0; ++- ++-err: ++- if (skb_chk && skb_chk != skb) +++ if (skb_chk != skb) ++ kfree_skb(skb_chk); ++ ++- return ret; +++ return 0; ++ } ++ ++ /** ++@@ -1594,7 +1583,11 @@ int ip_mc_check_igmp(struct sk_buff *skb) ++ if (ip_hdr(skb)->protocol != IPPROTO_IGMP) ++ return -ENOMSG; ++ ++- return __ip_mc_check_igmp(skb); +++ ret = ip_mc_check_igmp_csum(skb); +++ if (ret < 0) +++ return ret; +++ +++ return ip_mc_check_igmp_msg(skb); ++ } ++ EXPORT_SYMBOL(ip_mc_check_igmp); ++ ++diff --git a/net/ipv6/mcast_snoop.c b/net/ipv6/mcast_snoop.c ++index 1a917dc80d5e..a72ddfc40eb3 100644 ++--- a/net/ipv6/mcast_snoop.c +++++ b/net/ipv6/mcast_snoop.c ++@@ -77,27 +77,27 @@ static int ipv6_mc_check_mld_reportv2(struct sk_buff *skb) ++ ++ len += sizeof(struct mld2_report); ++ ++- return pskb_may_pull(skb, len) ? 0 : -EINVAL; +++ return ipv6_mc_may_pull(skb, len) ? 0 : -EINVAL; ++ } ++ ++ static int ipv6_mc_check_mld_query(struct sk_buff *skb) ++ { +++ unsigned int transport_len = ipv6_transport_len(skb); ++ struct mld_msg *mld; ++- unsigned int len = skb_transport_offset(skb); +++ unsigned int len; ++ ++ /* 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) { +++ if (transport_len != sizeof(struct mld_msg)) { ++ /* or MLDv2? */ ++- len += sizeof(struct mld2_query) - sizeof(struct mld_msg); ++- if (skb->len < len || !pskb_may_pull(skb, len)) +++ if (transport_len < sizeof(struct mld2_query)) +++ return -EINVAL; +++ +++ len = skb_transport_offset(skb) + sizeof(struct mld2_query); +++ if (!ipv6_mc_may_pull(skb, len)) ++ return -EINVAL; ++ } ++ ++@@ -115,7 +115,13 @@ static int ipv6_mc_check_mld_query(struct sk_buff *skb) ++ ++ static int ipv6_mc_check_mld_msg(struct sk_buff *skb) ++ { ++- struct mld_msg *mld = (struct mld_msg *)skb_transport_header(skb); +++ unsigned int len = skb_transport_offset(skb) + sizeof(struct mld_msg); +++ struct mld_msg *mld; +++ +++ if (!ipv6_mc_may_pull(skb, len)) +++ return -EINVAL; +++ +++ mld = (struct mld_msg *)skb_transport_header(skb); ++ ++ switch (mld->mld_type) { ++ case ICMPV6_MGM_REDUCTION: ++@@ -136,36 +142,24 @@ 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) ++- +++static int ipv6_mc_check_icmpv6(struct sk_buff *skb) ++ { ++- struct sk_buff *skb_chk = NULL; ++- unsigned int transport_len; ++- unsigned int len = skb_transport_offset(skb) + sizeof(struct mld_msg); ++- int ret = -EINVAL; +++ unsigned int len = skb_transport_offset(skb) + sizeof(struct icmp6hdr); +++ unsigned int transport_len = ipv6_transport_len(skb); +++ struct sk_buff *skb_chk; ++ ++- transport_len = ntohs(ipv6_hdr(skb)->payload_len); ++- transport_len -= skb_transport_offset(skb) - sizeof(struct ipv6hdr); +++ if (!ipv6_mc_may_pull(skb, len)) +++ return -EINVAL; ++ ++ skb_chk = skb_checksum_trimmed(skb, transport_len, ++ ipv6_mc_validate_checksum); ++ if (!skb_chk) ++- goto err; +++ return -EINVAL; ++ ++- if (!pskb_may_pull(skb_chk, len)) ++- goto err; ++- ++- ret = ipv6_mc_check_mld_msg(skb_chk); ++- if (ret) ++- goto err; ++- ++- ret = 0; ++- ++-err: ++- if (skb_chk && skb_chk != skb) +++ if (skb_chk != skb) ++ kfree_skb(skb_chk); ++ ++- return ret; +++ return 0; ++ } ++ ++ /** ++@@ -195,6 +189,10 @@ int ipv6_mc_check_mld(struct sk_buff *skb) ++ if (ret < 0) ++ return ret; ++ ++- return __ipv6_mc_check_mld(skb); +++ ret = ipv6_mc_check_icmpv6(skb); +++ if (ret < 0) +++ return ret; +++ +++ return ipv6_mc_check_mld_msg(skb); ++ } ++ EXPORT_SYMBOL(ipv6_mc_check_mld); ++-- ++2.27.0 ++ +diff --git a/target/linux/generic/backport-4.14/113-bridge-join-all-snoopers-multicast-address.patch b/target/linux/generic/backport-4.14/113-bridge-join-all-snoopers-multicast-address.patch +new file mode 100644 +index 0000000000000000000000000000000000000000..d98a80ffd5c28ab03deb17acd025a8c323898922 +--- /dev/null ++++ b/target/linux/generic/backport-4.14/113-bridge-join-all-snoopers-multicast-address.patch +@@ -0,0 +1,174 @@ ++From 8fec90b6958b01ea8487cf9c821c39067644756a Mon Sep 17 00:00:00 2001 ++From: =?UTF-8?q?Linus=20L=C3=BCssing?= <linus.luessing@c0d3.blue> ++Date: Mon, 21 Jan 2019 07:26:27 +0100 ++Subject: [PATCH] bridge: join all-snoopers multicast address ++MIME-Version: 1.0 ++Content-Type: text/plain; charset=UTF-8 ++Content-Transfer-Encoding: 8bit ++ ++Next to snooping IGMP/MLD queries RFC4541, section 2.1.1.a) recommends ++to snoop multicast router advertisements to detect multicast routers. ++ ++Multicast router advertisements are sent to an "all-snoopers" ++multicast address. To be able to receive them reliably, we need to ++join this group. ++ ++Otherwise other snooping switches might refrain from forwarding these ++advertisements to us. ++ ++Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue> ++Signed-off-by: David S. Miller <davem@davemloft.net> ++--- ++ include/uapi/linux/in.h | 9 ++--- ++ net/bridge/br_multicast.c | 72 ++++++++++++++++++++++++++++++++++++++- ++ net/ipv6/mcast.c | 2 ++ ++ 3 files changed, 78 insertions(+), 5 deletions(-) ++ ++diff --git a/include/uapi/linux/in.h b/include/uapi/linux/in.h ++index 48e8a225b985..478443169386 100644 ++--- a/include/uapi/linux/in.h +++++ b/include/uapi/linux/in.h ++@@ -288,10 +288,11 @@ struct sockaddr_in { ++ #define IN_LOOPBACK(a) ((((long int) (a)) & 0xff000000) == 0x7f000000) ++ ++ /* Defines for Multicast INADDR */ ++-#define INADDR_UNSPEC_GROUP 0xe0000000U /* 224.0.0.0 */ ++-#define INADDR_ALLHOSTS_GROUP 0xe0000001U /* 224.0.0.1 */ ++-#define INADDR_ALLRTRS_GROUP 0xe0000002U /* 224.0.0.2 */ ++-#define INADDR_MAX_LOCAL_GROUP 0xe00000ffU /* 224.0.0.255 */ +++#define INADDR_UNSPEC_GROUP 0xe0000000U /* 224.0.0.0 */ +++#define INADDR_ALLHOSTS_GROUP 0xe0000001U /* 224.0.0.1 */ +++#define INADDR_ALLRTRS_GROUP 0xe0000002U /* 224.0.0.2 */ +++#define INADDR_ALLSNOOPERS_GROUP 0xe000006aU /* 224.0.0.106 */ +++#define INADDR_MAX_LOCAL_GROUP 0xe00000ffU /* 224.0.0.255 */ ++ #endif ++ ++ /* <asm/byteorder.h> contains the htonl type stuff.. */ ++diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c ++index 5224e9b3c46d..917cc9d13ea9 100644 ++--- a/net/bridge/br_multicast.c +++++ b/net/bridge/br_multicast.c ++@@ -1970,6 +1970,68 @@ void br_multicast_init(struct net_bridge *br) ++ #endif ++ } ++ +++static void br_ip4_multicast_join_snoopers(struct net_bridge *br) +++{ +++ struct in_device *in_dev = in_dev_get(br->dev); +++ +++ if (!in_dev) +++ return; +++ +++ ip_mc_inc_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP)); +++ in_dev_put(in_dev); +++} +++ +++#if IS_ENABLED(CONFIG_IPV6) +++static void br_ip6_multicast_join_snoopers(struct net_bridge *br) +++{ +++ struct in6_addr addr; +++ +++ ipv6_addr_set(&addr, htonl(0xff020000), 0, 0, htonl(0x6a)); +++ ipv6_dev_mc_inc(br->dev, &addr); +++} +++#else +++static inline void br_ip6_multicast_join_snoopers(struct net_bridge *br) +++{ +++} +++#endif +++ +++static void br_multicast_join_snoopers(struct net_bridge *br) +++{ +++ br_ip4_multicast_join_snoopers(br); +++ br_ip6_multicast_join_snoopers(br); +++} +++ +++static void br_ip4_multicast_leave_snoopers(struct net_bridge *br) +++{ +++ struct in_device *in_dev = in_dev_get(br->dev); +++ +++ if (WARN_ON(!in_dev)) +++ return; +++ +++ ip_mc_dec_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP)); +++ in_dev_put(in_dev); +++} +++ +++#if IS_ENABLED(CONFIG_IPV6) +++static void br_ip6_multicast_leave_snoopers(struct net_bridge *br) +++{ +++ struct in6_addr addr; +++ +++ ipv6_addr_set(&addr, htonl(0xff020000), 0, 0, htonl(0x6a)); +++ ipv6_dev_mc_dec(br->dev, &addr); +++} +++#else +++static inline void br_ip6_multicast_leave_snoopers(struct net_bridge *br) +++{ +++} +++#endif +++ +++static void br_multicast_leave_snoopers(struct net_bridge *br) +++{ +++ br_ip4_multicast_leave_snoopers(br); +++ br_ip6_multicast_leave_snoopers(br); +++} +++ ++ static void __br_multicast_open(struct net_bridge *br, ++ struct bridge_mcast_own_query *query) ++ { ++@@ -1983,6 +2045,9 @@ static void __br_multicast_open(struct net_bridge *br, ++ ++ void br_multicast_open(struct net_bridge *br) ++ { +++ if (!br->multicast_disabled) +++ br_multicast_join_snoopers(br); +++ ++ __br_multicast_open(br, &br->ip4_own_query); ++ #if IS_ENABLED(CONFIG_IPV6) ++ __br_multicast_open(br, &br->ip6_own_query); ++@@ -1998,6 +2063,9 @@ void br_multicast_stop(struct net_bridge *br) ++ del_timer_sync(&br->ip6_other_query.timer); ++ del_timer_sync(&br->ip6_own_query.timer); ++ #endif +++ +++ if (!br->multicast_disabled) +++ br_multicast_leave_snoopers(br); ++ } ++ ++ void br_multicast_dev_del(struct net_bridge *br) ++@@ -2152,8 +2220,10 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val) ++ ++ br_mc_disabled_update(br->dev, !val); ++ br->multicast_disabled = !val; ++- if (br->multicast_disabled) +++ if (br->multicast_disabled) { +++ br_multicast_leave_snoopers(br); ++ goto unlock; +++ } ++ ++ if (!netif_running(br->dev)) ++ goto unlock; ++diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c ++index 611dc5d55fa0..d907c938ec1a 100644 ++--- a/net/ipv6/mcast.c +++++ b/net/ipv6/mcast.c ++@@ -915,6 +915,7 @@ int ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr) ++ ma_put(mc); ++ return 0; ++ } +++EXPORT_SYMBOL(ipv6_dev_mc_inc); ++ ++ /* ++ * device multicast group del ++@@ -962,6 +963,7 @@ int ipv6_dev_mc_dec(struct net_device *dev, const struct in6_addr *addr) ++ ++ return err; ++ } +++EXPORT_SYMBOL(ipv6_dev_mc_dec); ++ ++ /* ++ * check if the interface/address pair is valid ++-- ++2.27.0 ++ +diff --git a/target/linux/generic/backport-4.14/114-bridge-Snoop-Multicast-Router-Advertisements.patch b/target/linux/generic/backport-4.14/114-bridge-Snoop-Multicast-Router-Advertisements.patch +new file mode 100644 +index 0000000000000000000000000000000000000000..92279ce9ee866dfd006e5937b718ac7bcb7e7c6a +--- /dev/null ++++ b/target/linux/generic/backport-4.14/114-bridge-Snoop-Multicast-Router-Advertisements.patch +@@ -0,0 +1,246 @@ ++From 690f6ed25b5fac9408fdebb7b4e8131f5b0a5c86 Mon Sep 17 00:00:00 2001 ++From: =?UTF-8?q?Linus=20L=C3=BCssing?= <linus.luessing@c0d3.blue> ++Date: Mon, 21 Jan 2019 07:26:28 +0100 ++Subject: [PATCH] bridge: Snoop Multicast Router Advertisements ++MIME-Version: 1.0 ++Content-Type: text/plain; charset=UTF-8 ++Content-Transfer-Encoding: 8bit ++ ++When multiple multicast routers are present in a broadcast domain then ++only one of them will be detectable via IGMP/MLD query snooping. The ++multicast router with the lowest IP address will become the selected and ++active querier while all other multicast routers will then refrain from ++sending queries. ++ ++To detect such rather silent multicast routers, too, RFC4286 ++("Multicast Router Discovery") provides a standardized protocol to ++detect multicast routers for multicast snooping switches. ++ ++This patch implements the necessary MRD Advertisement message parsing ++and after successful processing adds such routers to the internal ++multicast router list. ++ ++Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue> ++Signed-off-by: David S. Miller <davem@davemloft.net> ++--- ++ include/linux/in.h | 5 ++++ ++ include/net/addrconf.h | 15 ++++++++++ ++ include/uapi/linux/icmpv6.h | 2 ++ ++ include/uapi/linux/igmp.h | 1 + ++ net/bridge/br_multicast.c | 55 +++++++++++++++++++++++++++++++++++++ ++ net/ipv6/mcast_snoop.c | 5 +++- ++ 6 files changed, 82 insertions(+), 1 deletion(-) ++ ++diff --git a/include/linux/in.h b/include/linux/in.h ++index 31b493734763..435e7f2a513a 100644 ++--- a/include/linux/in.h +++++ b/include/linux/in.h ++@@ -60,6 +60,11 @@ static inline bool ipv4_is_lbcast(__be32 addr) ++ return addr == htonl(INADDR_BROADCAST); ++ } ++ +++static inline bool ipv4_is_all_snoopers(__be32 addr) +++{ +++ return addr == htonl(INADDR_ALLSNOOPERS_GROUP); +++} +++ ++ static inline bool ipv4_is_zeronet(__be32 addr) ++ { ++ return (addr & htonl(0xff000000)) == htonl(0x00000000); ++diff --git a/include/net/addrconf.h b/include/net/addrconf.h ++index 0eb1e1f6ea9a..c93530f9e8e4 100644 ++--- a/include/net/addrconf.h +++++ b/include/net/addrconf.h ++@@ -217,6 +217,7 @@ void ipv6_mc_unmap(struct inet6_dev *idev); ++ void ipv6_mc_remap(struct inet6_dev *idev); ++ void ipv6_mc_init_dev(struct inet6_dev *idev); ++ void ipv6_mc_destroy_dev(struct inet6_dev *idev); +++int ipv6_mc_check_icmpv6(struct sk_buff *skb); ++ int ipv6_mc_check_mld(struct sk_buff *skb); ++ void addrconf_dad_failure(struct inet6_ifaddr *ifp); ++ ++@@ -445,6 +446,20 @@ static inline bool ipv6_addr_is_solict_mult(const struct in6_addr *addr) ++ #endif ++ } ++ +++static inline bool ipv6_addr_is_all_snoopers(const struct in6_addr *addr) +++{ +++#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64 +++ __be64 *p = (__be64 *)addr; +++ +++ return ((p[0] ^ cpu_to_be64(0xff02000000000000UL)) | +++ (p[1] ^ cpu_to_be64(0x6a))) == 0UL; +++#else +++ return ((addr->s6_addr32[0] ^ htonl(0xff020000)) | +++ addr->s6_addr32[1] | addr->s6_addr32[2] | +++ (addr->s6_addr32[3] ^ htonl(0x0000006a))) == 0; +++#endif +++} +++ ++ #ifdef CONFIG_PROC_FS ++ int if6_proc_init(void); ++ void if6_proc_exit(void); ++diff --git a/include/uapi/linux/icmpv6.h b/include/uapi/linux/icmpv6.h ++index caf8dc019250..325395f56bfa 100644 ++--- a/include/uapi/linux/icmpv6.h +++++ b/include/uapi/linux/icmpv6.h ++@@ -108,6 +108,8 @@ struct icmp6hdr { ++ #define ICMPV6_MOBILE_PREFIX_SOL 146 ++ #define ICMPV6_MOBILE_PREFIX_ADV 147 ++ +++#define ICMPV6_MRDISC_ADV 151 +++ ++ /* ++ * Codes for Destination Unreachable ++ */ ++diff --git a/include/uapi/linux/igmp.h b/include/uapi/linux/igmp.h ++index 7e44ac02ca18..90c28bc466c6 100644 ++--- a/include/uapi/linux/igmp.h +++++ b/include/uapi/linux/igmp.h ++@@ -93,6 +93,7 @@ struct igmpv3_query { ++ #define IGMP_MTRACE_RESP 0x1e ++ #define IGMP_MTRACE 0x1f ++ +++#define IGMP_MRDISC_ADV 0x30 /* From RFC4286 */ ++ ++ /* ++ * Use the BSD names for these for compatibility ++diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c ++index 917cc9d13ea9..8f0bd1133bbd 100644 ++--- a/net/bridge/br_multicast.c +++++ b/net/bridge/br_multicast.c ++@@ -14,6 +14,7 @@ ++ #include <linux/export.h> ++ #include <linux/if_ether.h> ++ #include <linux/igmp.h> +++#include <linux/in.h> ++ #include <linux/jhash.h> ++ #include <linux/kernel.h> ++ #include <linux/log2.h> ++@@ -29,10 +30,12 @@ ++ #include <net/ip.h> ++ #include <net/switchdev.h> ++ #if IS_ENABLED(CONFIG_IPV6) +++#include <linux/icmpv6.h> ++ #include <net/ipv6.h> ++ #include <net/mld.h> ++ #include <net/ip6_checksum.h> ++ #include <net/addrconf.h> +++#include <net/ipv6.h> ++ #endif ++ ++ #include "br_private.h" ++@@ -1772,6 +1775,19 @@ static void br_multicast_pim(struct net_bridge *br, ++ br_multicast_mark_router(br, port); ++ } ++ +++static int br_ip4_multicast_mrd_rcv(struct net_bridge *br, +++ struct net_bridge_port *port, +++ struct sk_buff *skb) +++{ +++ if (ip_hdr(skb)->protocol != IPPROTO_IGMP || +++ igmp_hdr(skb)->type != IGMP_MRDISC_ADV) +++ return -ENOMSG; +++ +++ br_multicast_mark_router(br, port); +++ +++ return 0; +++} +++ ++ static int br_multicast_ipv4_rcv(struct net_bridge *br, ++ struct net_bridge_port *port, ++ struct sk_buff *skb, ++@@ -1789,7 +1805,15 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, ++ } else if (pim_ipv4_all_pim_routers(ip_hdr(skb)->daddr)) { ++ if (ip_hdr(skb)->protocol == IPPROTO_PIM) ++ br_multicast_pim(br, port, skb); +++ } else if (ipv4_is_all_snoopers(ip_hdr(skb)->daddr)) { +++ err = br_ip4_multicast_mrd_rcv(br, port, skb); +++ +++ if (err < 0 && err != -ENOMSG) { +++ br_multicast_err_count(br, port, skb->protocol); +++ return err; +++ } ++ } +++ ++ return 0; ++ } else if (err < 0) { ++ br_multicast_err_count(br, port, skb->protocol); ++@@ -1824,6 +1848,27 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, ++ } ++ ++ #if IS_ENABLED(CONFIG_IPV6) +++static int br_ip6_multicast_mrd_rcv(struct net_bridge *br, +++ struct net_bridge_port *port, +++ struct sk_buff *skb) +++{ +++ int ret; +++ +++ if (ipv6_hdr(skb)->nexthdr != IPPROTO_ICMPV6) +++ return -ENOMSG; +++ +++ ret = ipv6_mc_check_icmpv6(skb); +++ if (ret < 0) +++ return ret; +++ +++ if (icmp6_hdr(skb)->icmp6_type != ICMPV6_MRDISC_ADV) +++ return -ENOMSG; +++ +++ br_multicast_mark_router(br, port); +++ +++ return 0; +++} +++ ++ static int br_multicast_ipv6_rcv(struct net_bridge *br, ++ struct net_bridge_port *port, ++ struct sk_buff *skb, ++@@ -1838,6 +1883,16 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, ++ if (err == -ENOMSG) { ++ if (!ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr)) ++ BR_INPUT_SKB_CB(skb)->mrouters_only = 1; +++ +++ if (ipv6_addr_is_all_snoopers(&ipv6_hdr(skb)->daddr)) { +++ err = br_ip6_multicast_mrd_rcv(br, port, skb); +++ +++ if (err < 0 && err != -ENOMSG) { +++ br_multicast_err_count(br, port, skb->protocol); +++ return err; +++ } +++ } +++ ++ return 0; ++ } else if (err < 0) { ++ br_multicast_err_count(br, port, skb->protocol); ++diff --git a/net/ipv6/mcast_snoop.c b/net/ipv6/mcast_snoop.c ++index a72ddfc40eb3..55e2ac179f28 100644 ++--- a/net/ipv6/mcast_snoop.c +++++ b/net/ipv6/mcast_snoop.c ++@@ -41,6 +41,8 @@ static int ipv6_mc_check_ip6hdr(struct sk_buff *skb) ++ if (skb->len < len || len <= offset) ++ return -EINVAL; ++ +++ skb_set_transport_header(skb, offset); +++ ++ return 0; ++ } ++ ++@@ -142,7 +144,7 @@ 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_icmpv6(struct sk_buff *skb) +++int ipv6_mc_check_icmpv6(struct sk_buff *skb) ++ { ++ unsigned int len = skb_transport_offset(skb) + sizeof(struct icmp6hdr); ++ unsigned int transport_len = ipv6_transport_len(skb); ++@@ -161,6 +163,7 @@ static int ipv6_mc_check_icmpv6(struct sk_buff *skb) ++ ++ return 0; ++ } +++EXPORT_SYMBOL(ipv6_mc_check_icmpv6); ++ ++ /** ++ * ipv6_mc_check_mld - checks whether this is a sane MLD packet ++-- ++2.27.0 ++ +diff --git a/target/linux/generic/backport-4.14/115-bridge-remove-duplicated-include-from-br_multicast.c.patch b/target/linux/generic/backport-4.14/115-bridge-remove-duplicated-include-from-br_multicast.c.patch +new file mode 100644 +index 0000000000000000000000000000000000000000..ce8b19454986f2c61a9cda24f61a9eef5eaacf56 +--- /dev/null ++++ b/target/linux/generic/backport-4.14/115-bridge-remove-duplicated-include-from-br_multicast.c.patch +@@ -0,0 +1,28 @@ ++From 995dbd5e30c99deb8f0c91264ae8d75951b832f7 Mon Sep 17 00:00:00 2001 ++From: YueHaibing <yuehaibing@huawei.com> ++Date: Fri, 25 Jan 2019 10:59:09 +0800 ++Subject: [PATCH] bridge: remove duplicated include from br_multicast.c ++ ++Remove duplicated include. ++ ++Signed-off-by: YueHaibing <yuehaibing@huawei.com> ++Signed-off-by: David S. Miller <davem@davemloft.net> ++--- ++ net/bridge/br_multicast.c | 1 - ++ 1 file changed, 1 deletion(-) ++ ++diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c ++index 8f0bd1133bbd..4c1b9c0a290b 100644 ++--- a/net/bridge/br_multicast.c +++++ b/net/bridge/br_multicast.c ++@@ -35,7 +35,6 @@ ++ #include <net/mld.h> ++ #include <net/ip6_checksum.h> ++ #include <net/addrconf.h> ++-#include <net/ipv6.h> ++ #endif ++ ++ #include "br_private.h" ++-- ++2.27.0 ++ +diff --git a/target/linux/generic/backport-4.14/116-net-Fix-ip_mc_-dec-inc-_group-allocation-context.patch b/target/linux/generic/backport-4.14/116-net-Fix-ip_mc_-dec-inc-_group-allocation-context.patch +new file mode 100644 +index 0000000000000000000000000000000000000000..cb22c3fb97d232e8664569a5842d8c1bbd7c9596 +--- /dev/null ++++ b/target/linux/generic/backport-4.14/116-net-Fix-ip_mc_-dec-inc-_group-allocation-context.patch +@@ -0,0 +1,221 @@ ++From cb12869990893ec947c2d97b0e64449174c72d4a Mon Sep 17 00:00:00 2001 ++From: Florian Fainelli <f.fainelli@gmail.com> ++Date: Fri, 1 Feb 2019 20:20:52 -0800 ++Subject: [PATCH] net: Fix ip_mc_{dec,inc}_group allocation context ++ ++After 4effd28c1245 ("bridge: join all-snoopers multicast address"), I ++started seeing the following sleep in atomic warnings: ++ ++[ 26.763893] BUG: sleeping function called from invalid context at mm/slab.h:421 ++[ 26.771425] in_atomic(): 1, irqs_disabled(): 0, pid: 1658, name: sh ++[ 26.777855] INFO: lockdep is turned off. ++[ 26.781916] CPU: 0 PID: 1658 Comm: sh Not tainted 5.0.0-rc4 #20 ++[ 26.787943] Hardware name: BCM97278SV (DT) ++[ 26.792118] Call trace: ++[ 26.794645] dump_backtrace+0x0/0x170 ++[ 26.798391] show_stack+0x24/0x30 ++[ 26.801787] dump_stack+0xa4/0xe4 ++[ 26.805182] ___might_sleep+0x208/0x218 ++[ 26.809102] __might_sleep+0x78/0x88 ++[ 26.812762] kmem_cache_alloc_trace+0x64/0x28c ++[ 26.817301] igmp_group_dropped+0x150/0x230 ++[ 26.821573] ip_mc_dec_group+0x1b0/0x1f8 ++[ 26.825585] br_ip4_multicast_leave_snoopers.isra.11+0x174/0x190 ++[ 26.831704] br_multicast_toggle+0x78/0xcc ++[ 26.835887] store_bridge_parm+0xc4/0xfc ++[ 26.839894] multicast_snooping_store+0x3c/0x4c ++[ 26.844517] dev_attr_store+0x44/0x5c ++[ 26.848262] sysfs_kf_write+0x50/0x68 ++[ 26.852006] kernfs_fop_write+0x14c/0x1b4 ++[ 26.856102] __vfs_write+0x60/0x190 ++[ 26.859668] vfs_write+0xc8/0x168 ++[ 26.863059] ksys_write+0x70/0xc8 ++[ 26.866449] __arm64_sys_write+0x24/0x30 ++[ 26.870458] el0_svc_common+0xa0/0x11c ++[ 26.874291] el0_svc_handler+0x38/0x70 ++[ 26.878120] el0_svc+0x8/0xc ++ ++while toggling the bridge's multicast_snooping attribute dynamically. ++ ++Pass a gfp_t down to igmpv3_add_delrec(), introduce ++__igmp_group_dropped() and introduce __ip_mc_dec_group() to take a gfp_t ++argument. ++ ++Similarly introduce ____ip_mc_inc_group() and __ip_mc_inc_group() to ++allow caller to specify gfp_t. ++ ++IPv6 part of the patch appears fine. ++ ++Fixes: 4effd28c1245 ("bridge: join all-snoopers multicast address") ++Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> ++Signed-off-by: David S. Miller <davem@davemloft.net> ++--- ++ include/linux/igmp.h | 8 +++++++- ++ net/bridge/br_multicast.c | 4 ++-- ++ net/ipv4/igmp.c | 33 +++++++++++++++++++++++---------- ++ 3 files changed, 32 insertions(+), 13 deletions(-) ++ ++diff --git a/include/linux/igmp.h b/include/linux/igmp.h ++index 858143489a2a..a9cccb4fcb29 100644 ++--- a/include/linux/igmp.h +++++ b/include/linux/igmp.h ++@@ -135,7 +135,13 @@ extern void ip_mc_up(struct in_device *); ++ extern void ip_mc_down(struct in_device *); ++ extern void ip_mc_unmap(struct in_device *); ++ extern void ip_mc_remap(struct in_device *); ++-extern void ip_mc_dec_group(struct in_device *in_dev, __be32 addr); +++extern void __ip_mc_dec_group(struct in_device *in_dev, __be32 addr, gfp_t gfp); +++static inline void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) +++{ +++ return __ip_mc_dec_group(in_dev, addr, GFP_KERNEL); +++} +++extern void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr, +++ gfp_t gfp); ++ extern void ip_mc_inc_group(struct in_device *in_dev, __be32 addr); ++ int ip_mc_check_igmp(struct sk_buff *skb); ++ ++diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c ++index 4c1b9c0a290b..779af4efea27 100644 ++--- a/net/bridge/br_multicast.c +++++ b/net/bridge/br_multicast.c ++@@ -2031,7 +2031,7 @@ static void br_ip4_multicast_join_snoopers(struct net_bridge *br) ++ if (!in_dev) ++ return; ++ ++- ip_mc_inc_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP)); +++ __ip_mc_inc_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP), GFP_ATOMIC); ++ in_dev_put(in_dev); ++ } ++ ++@@ -2062,7 +2062,7 @@ static void br_ip4_multicast_leave_snoopers(struct net_bridge *br) ++ if (WARN_ON(!in_dev)) ++ return; ++ ++- ip_mc_dec_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP)); +++ __ip_mc_dec_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP), GFP_ATOMIC); ++ in_dev_put(in_dev); ++ } ++ ++diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c ++index 9033c8cb0e95..862f25f19441 100644 ++--- a/net/ipv4/igmp.c +++++ b/net/ipv4/igmp.c ++@@ -162,7 +162,8 @@ static int unsolicited_report_interval(struct in_device *in_dev) ++ return interval_jiffies; ++ } ++ ++-static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im); +++static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im, +++ gfp_t gfp); ++ static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im); ++ static void igmpv3_clear_delrec(struct in_device *in_dev); ++ static int sf_setstate(struct ip_mc_list *pmc); ++@@ -1152,7 +1153,8 @@ static void ip_mc_filter_del(struct in_device *in_dev, __be32 addr) ++ /* ++ * deleted ip_mc_list manipulation ++ */ ++-static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im) +++static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im, +++ gfp_t gfp) ++ { ++ struct ip_mc_list *pmc; ++ struct net *net = dev_net(in_dev->dev); ++@@ -1163,7 +1165,7 @@ static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im) ++ * for deleted items allows change reports to use common code with ++ * non-deleted or query-response MCA's. ++ */ ++- pmc = kzalloc(sizeof(*pmc), GFP_KERNEL); +++ pmc = kzalloc(sizeof(*pmc), gfp); ++ if (!pmc) ++ return; ++ spin_lock_init(&pmc->lock); ++@@ -1264,7 +1266,7 @@ static void igmpv3_clear_delrec(struct in_device *in_dev) ++ } ++ #endif ++ ++-static void igmp_group_dropped(struct ip_mc_list *im) +++static void __igmp_group_dropped(struct ip_mc_list *im, gfp_t gfp) ++ { ++ struct in_device *in_dev = im->interface; ++ #ifdef CONFIG_IP_MULTICAST ++@@ -1295,13 +1297,18 @@ static void igmp_group_dropped(struct ip_mc_list *im) ++ return; ++ } ++ /* IGMPv3 */ ++- igmpv3_add_delrec(in_dev, im); +++ igmpv3_add_delrec(in_dev, im, gfp); ++ ++ igmp_ifc_event(in_dev); ++ } ++ #endif ++ } ++ +++static void igmp_group_dropped(struct ip_mc_list *im) +++{ +++ __igmp_group_dropped(im, GFP_KERNEL); +++} +++ ++ static void igmp_group_added(struct ip_mc_list *im) ++ { ++ struct in_device *in_dev = im->interface; ++@@ -1396,7 +1403,7 @@ static void ip_mc_hash_remove(struct in_device *in_dev, ++ * A socket has joined a multicast group on device dev. ++ */ ++ ++-void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) +++void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr, gfp_t gfp) ++ { ++ struct ip_mc_list *im; ++ #ifdef CONFIG_IP_MULTICAST ++@@ -1413,7 +1420,7 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) ++ } ++ } ++ ++- im = kzalloc(sizeof(*im), GFP_KERNEL); +++ im = kzalloc(sizeof(*im), gfp); ++ if (!im) ++ goto out; ++ ++@@ -1446,6 +1453,12 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) ++ out: ++ return; ++ } +++EXPORT_SYMBOL(__ip_mc_inc_group); +++ +++void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) +++{ +++ __ip_mc_inc_group(in_dev, addr, GFP_KERNEL); +++} ++ EXPORT_SYMBOL(ip_mc_inc_group); ++ ++ static int ip_mc_check_iphdr(struct sk_buff *skb) ++@@ -1628,7 +1641,7 @@ static void ip_mc_rejoin_groups(struct in_device *in_dev) ++ * A socket has left a multicast group on device dev ++ */ ++ ++-void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) +++void __ip_mc_dec_group(struct in_device *in_dev, __be32 addr, gfp_t gfp) ++ { ++ struct ip_mc_list *i; ++ struct ip_mc_list __rcu **ip; ++@@ -1643,7 +1656,7 @@ void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) ++ ip_mc_hash_remove(in_dev, i); ++ *ip = i->next_rcu; ++ in_dev->mc_count--; ++- igmp_group_dropped(i); +++ __igmp_group_dropped(i, gfp); ++ ip_mc_clear_src(i); ++ ++ if (!in_dev->dead) ++@@ -1656,7 +1669,7 @@ void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) ++ } ++ } ++ } ++-EXPORT_SYMBOL(ip_mc_dec_group); +++EXPORT_SYMBOL(__ip_mc_dec_group); ++ ++ /* Device changing type */ ++ ++-- ++2.27.0 ++ +diff --git a/target/linux/generic/backport-4.14/117-net-remove-unneeded-switch-fall-through.patch b/target/linux/generic/backport-4.14/117-net-remove-unneeded-switch-fall-through.patch +new file mode 100644 +index 0000000000000000000000000000000000000000..0d390f1d2f1b4a3542257e14761a7e1a9d9072c3 +--- /dev/null ++++ b/target/linux/generic/backport-4.14/117-net-remove-unneeded-switch-fall-through.patch +@@ -0,0 +1,42 @@ ++From 4b3920aedc1b730583dccaeb702ab67722b09b5c Mon Sep 17 00:00:00 2001 ++From: Li RongQing <lirongqing@baidu.com> ++Date: Tue, 19 Feb 2019 10:15:56 +0800 ++Subject: [PATCH] net: remove unneeded switch fall-through ++ ++This case block has been terminated by a return, so not need ++a switch fall-through ++ ++Signed-off-by: Li RongQing <lirongqing@baidu.com> ++Signed-off-by: David S. Miller <davem@davemloft.net> ++--- ++ net/ipv4/igmp.c | 1 - ++ net/ipv6/mcast_snoop.c | 1 - ++ 2 files changed, 2 deletions(-) ++ ++diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c ++index 862f25f19441..9fbf4f9117bb 100644 ++--- a/net/ipv4/igmp.c +++++ b/net/ipv4/igmp.c ++@@ -1535,7 +1535,6 @@ static int ip_mc_check_igmp_msg(struct sk_buff *skb) ++ 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); ++diff --git a/net/ipv6/mcast_snoop.c b/net/ipv6/mcast_snoop.c ++index 55e2ac179f28..dddd75d1be0e 100644 ++--- a/net/ipv6/mcast_snoop.c +++++ b/net/ipv6/mcast_snoop.c ++@@ -128,7 +128,6 @@ static int ipv6_mc_check_mld_msg(struct sk_buff *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); ++-- ++2.27.0 ++ +diff --git a/target/linux/generic/backport-4.14/118-ipv6-Fix-return-value-of-ipv6_mc_may_pull-for-malfor.patch b/target/linux/generic/backport-4.14/118-ipv6-Fix-return-value-of-ipv6_mc_may_pull-for-malfor.patch +new file mode 100644 +index 0000000000000000000000000000000000000000..c17e2b4bd7b499b92a224450be0dca311d62027a +--- /dev/null ++++ b/target/linux/generic/backport-4.14/118-ipv6-Fix-return-value-of-ipv6_mc_may_pull-for-malfor.patch +@@ -0,0 +1,47 @@ ++From fc8cb7c0bf06853ea1f6ee14409b7f91b0acbe85 Mon Sep 17 00:00:00 2001 ++From: Stefano Brivio <sbrivio@redhat.com> ++Date: Tue, 13 Aug 2019 00:46:01 +0200 ++Subject: [PATCH] ipv6: Fix return value of ipv6_mc_may_pull() for ++ malformed packets ++ ++Commit ba5ea614622d ("bridge: simplify ip_mc_check_igmp() and ++ipv6_mc_check_mld() calls") replaces direct calls to pskb_may_pull() ++in br_ipv6_multicast_mld2_report() with calls to ipv6_mc_may_pull(), ++that returns -EINVAL on buffers too short to be valid IPv6 packets, ++while maintaining the previous handling of the return code. ++ ++This leads to the direct opposite of the intended effect: if the ++packet is malformed, -EINVAL evaluates as true, and we'll happily ++proceed with the processing. ++ ++Return 0 if the packet is too short, in the same way as this was ++fixed for IPv4 by commit 083b78a9ed64 ("ip: fix ip_mc_may_pull() ++return value"). ++ ++I don't have a reproducer for this, unlike the one referred to by ++the IPv4 commit, but this is clearly broken. ++ ++Fixes: ba5ea614622d ("bridge: simplify ip_mc_check_igmp() and ipv6_mc_check_mld() calls") ++Signed-off-by: Stefano Brivio <sbrivio@redhat.com> ++Acked-by: Guillaume Nault <gnault@redhat.com> ++Signed-off-by: David S. Miller <davem@davemloft.net> ++--- ++ include/net/addrconf.h | 2 +- ++ 1 file changed, 1 insertion(+), 1 deletion(-) ++ ++diff --git a/include/net/addrconf.h b/include/net/addrconf.h ++index c93530f9e8e4..ea97f70d66b8 100644 ++--- a/include/net/addrconf.h +++++ b/include/net/addrconf.h ++@@ -194,7 +194,7 @@ static inline int ipv6_mc_may_pull(struct sk_buff *skb, ++ unsigned int len) ++ { ++ if (skb_transport_offset(skb) + ipv6_transport_len(skb) < len) ++- return -EINVAL; +++ return 0; ++ ++ return pskb_may_pull(skb, len); ++ } ++-- ++2.27.0 ++ +diff --git a/target/linux/generic/backport-4.14/119-timers-Add-a-function-to-start-reduce-a-timer.patch b/target/linux/generic/backport-4.14/119-timers-Add-a-function-to-start-reduce-a-timer.patch +new file mode 100644 +index 0000000000000000000000000000000000000000..92bb9275df9d54778ce8f00b1cb6e999eae40606 +--- /dev/null ++++ b/target/linux/generic/backport-4.14/119-timers-Add-a-function-to-start-reduce-a-timer.patch +@@ -0,0 +1,201 @@ ++From dbc2cc8bcbdb0765516a7d0201f3337e66f4f2fc Mon Sep 17 00:00:00 2001 ++From: David Howells <dhowells@redhat.com> ++Date: Thu, 9 Nov 2017 12:35:07 +0000 ++Subject: [PATCH] timers: Add a function to start/reduce a timer ++ ++Add a function, similar to mod_timer(), that will start a timer if it isn't ++running and will modify it if it is running and has an expiry time longer ++than the new time. If the timer is running with an expiry time that's the ++same or sooner, no change is made. ++ ++The function looks like: ++ ++ int timer_reduce(struct timer_list *timer, unsigned long expires); ++ ++This can be used by code such as networking code to make it easier to share ++a timer for multiple timeouts. For instance, in upcoming AF_RXRPC code, ++the rxrpc_call struct will maintain a number of timeouts: ++ ++ unsigned long ack_at; ++ unsigned long resend_at; ++ unsigned long ping_at; ++ unsigned long expect_rx_by; ++ unsigned long expect_req_by; ++ unsigned long expect_term_by; ++ ++each of which is set independently of the others. With timer reduction ++available, when the code needs to set one of the timeouts, it only needs to ++look at that timeout and then call timer_reduce() to modify the timer, ++starting it or bringing it forward if necessary. There is no need to refer ++to the other timeouts to see which is earliest and no need to take any lock ++other than, potentially, the timer lock inside timer_reduce(). ++ ++Note, that this does not protect against concurrent invocations of any of ++the timer functions. ++ ++As an example, the expect_rx_by timeout above, which terminates a call if ++we don't get a packet from the server within a certain time window, would ++be set something like this: ++ ++ unsigned long now = jiffies; ++ unsigned long expect_rx_by = now + packet_receive_timeout; ++ WRITE_ONCE(call->expect_rx_by, expect_rx_by); ++ timer_reduce(&call->timer, expect_rx_by); ++ ++The timer service code (which might, say, be in a work function) would then ++check all the timeouts to see which, if any, had triggered, deal with ++those: ++ ++ t = READ_ONCE(call->ack_at); ++ if (time_after_eq(now, t)) { ++ cmpxchg(&call->ack_at, t, now + MAX_JIFFY_OFFSET); ++ set_bit(RXRPC_CALL_EV_ACK, &call->events); ++ } ++ ++and then restart the timer if necessary by finding the soonest timeout that ++hasn't yet passed and then calling timer_reduce(). ++ ++The disadvantage of doing things this way rather than comparing the timers ++each time and calling mod_timer() is that you *will* take timer events ++unless you can finish what you're doing and delete the timer in time. ++ ++The advantage of doing things this way is that you don't need to use a lock ++to work out when the next timer should be set, other than the timer's own ++lock - which you might not have to take. ++ ++[ tglx: Fixed weird formatting and adopted it to pending changes ] ++ ++Signed-off-by: David Howells <dhowells@redhat.com> ++Signed-off-by: Thomas Gleixner <tglx@linutronix.de> ++Cc: keyrings@vger.kernel.org ++Cc: linux-afs@lists.infradead.org ++Link: https://lkml.kernel.org/r/151023090769.23050.1801643667223880753.stgit@warthog.procyon.org.uk ++--- ++ include/linux/timer.h | 1 + ++ kernel/time/timer.c | 45 ++++++++++++++++++++++++++++++++++++------- ++ 2 files changed, 39 insertions(+), 7 deletions(-) ++ ++diff --git a/include/linux/timer.h b/include/linux/timer.h ++index e0ea1fe87572..db65e00fb7b2 100644 ++--- a/include/linux/timer.h +++++ b/include/linux/timer.h ++@@ -202,6 +202,7 @@ extern void add_timer_on(struct timer_list *timer, int cpu); ++ extern int del_timer(struct timer_list * timer); ++ extern int mod_timer(struct timer_list *timer, unsigned long expires); ++ extern int mod_timer_pending(struct timer_list *timer, unsigned long expires); +++extern int timer_reduce(struct timer_list *timer, unsigned long expires); ++ ++ /* ++ * The jiffies value which is added to now, when there is no timer ++diff --git a/kernel/time/timer.c b/kernel/time/timer.c ++index 9f8e8892e5b0..07892e658cbd 100644 ++--- a/kernel/time/timer.c +++++ b/kernel/time/timer.c ++@@ -927,8 +927,11 @@ static struct timer_base *lock_timer_base(struct timer_list *timer, ++ } ++ } ++ +++#define MOD_TIMER_PENDING_ONLY 0x01 +++#define MOD_TIMER_REDUCE 0x02 +++ ++ static inline int ++-__mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only) +++__mod_timer(struct timer_list *timer, unsigned long expires, unsigned int options) ++ { ++ struct timer_base *base, *new_base; ++ unsigned int idx = UINT_MAX; ++@@ -948,7 +951,11 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only) ++ * larger granularity than you would get from adding a new ++ * timer with this expiry. ++ */ ++- if (timer->expires == expires) +++ long diff = timer->expires - expires; +++ +++ if (!diff) +++ return 1; +++ if (options & MOD_TIMER_REDUCE && diff <= 0) ++ return 1; ++ ++ /* ++@@ -960,6 +967,12 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only) ++ base = lock_timer_base(timer, &flags); ++ forward_timer_base(base); ++ +++ if (timer_pending(timer) && (options & MOD_TIMER_REDUCE) && +++ time_before_eq(timer->expires, expires)) { +++ ret = 1; +++ goto out_unlock; +++ } +++ ++ clk = base->clk; ++ idx = calc_wheel_index(expires, clk); ++ ++@@ -969,7 +982,10 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only) ++ * subsequent call will exit in the expires check above. ++ */ ++ if (idx == timer_get_idx(timer)) { ++- timer->expires = expires; +++ if (!(options & MOD_TIMER_REDUCE)) +++ timer->expires = expires; +++ else if (time_after(timer->expires, expires)) +++ timer->expires = expires; ++ ret = 1; ++ goto out_unlock; ++ } ++@@ -979,7 +995,7 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only) ++ } ++ ++ ret = detach_if_pending(timer, base, false); ++- if (!ret && pending_only) +++ if (!ret && (options & MOD_TIMER_PENDING_ONLY)) ++ goto out_unlock; ++ ++ new_base = get_target_base(base, timer->flags); ++@@ -1040,7 +1056,7 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only) ++ */ ++ int mod_timer_pending(struct timer_list *timer, unsigned long expires) ++ { ++- return __mod_timer(timer, expires, true); +++ return __mod_timer(timer, expires, MOD_TIMER_PENDING_ONLY); ++ } ++ EXPORT_SYMBOL(mod_timer_pending); ++ ++@@ -1066,10 +1082,25 @@ EXPORT_SYMBOL(mod_timer_pending); ++ */ ++ int mod_timer(struct timer_list *timer, unsigned long expires) ++ { ++- return __mod_timer(timer, expires, false); +++ return __mod_timer(timer, expires, 0); ++ } ++ EXPORT_SYMBOL(mod_timer); ++ +++/** +++ * timer_reduce - Modify a timer's timeout if it would reduce the timeout +++ * @timer: The timer to be modified +++ * @expires: New timeout in jiffies +++ * +++ * timer_reduce() is very similar to mod_timer(), except that it will only +++ * modify a running timer if that would reduce the expiration time (it will +++ * start a timer that isn't running). +++ */ +++int timer_reduce(struct timer_list *timer, unsigned long expires) +++{ +++ return __mod_timer(timer, expires, MOD_TIMER_REDUCE); +++} +++EXPORT_SYMBOL(timer_reduce); +++ ++ /** ++ * add_timer - start a timer ++ * @timer: the timer to be added ++@@ -1742,7 +1773,7 @@ signed long __sched schedule_timeout(signed long timeout) ++ expire = timeout + jiffies; ++ ++ setup_timer_on_stack(&timer, process_timeout, (unsigned long)current); ++- __mod_timer(&timer, expire, false); +++ __mod_timer(&timer, expire, 0); ++ schedule(); ++ del_singleshot_timer_sync(&timer); ++ ++-- ++2.27.0 ++ +diff --git a/target/linux/generic/config-4.14 b/target/linux/generic/config-4.14 +index d54ede9efda0a3ffd84e9a0c49dc410a01737d82..15b50523bf55d9a77fc1655ec6ba6ffde6d93a3e 100644 +--- a/target/linux/generic/config-4.14 ++++ b/target/linux/generic/config-4.14 +@@ -628,6 +628,7 @@ CONFIG_BRIDGE=y + # CONFIG_BRIDGE_EBT_T_NAT is not set + # CONFIG_BRIDGE_EBT_VLAN is not set + CONFIG_BRIDGE_IGMP_SNOOPING=y ++CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS=y + # CONFIG_BRIDGE_NETFILTER is not set + # CONFIG_BRIDGE_NF_EBTABLES is not set + CONFIG_BRIDGE_VLAN_FILTERING=y +diff --git a/target/linux/generic/pending-4.14/151-bridge-Implement-MLD-Querier-wake-up-calls-Android-b.patch b/target/linux/generic/pending-4.14/151-bridge-Implement-MLD-Querier-wake-up-calls-Android-b.patch +new file mode 100644 +index 0000000000000000000000000000000000000000..d02a4bc64deafa7fe2e17e9d17b259cea999c13b +--- /dev/null ++++ b/target/linux/generic/pending-4.14/151-bridge-Implement-MLD-Querier-wake-up-calls-Android-b.patch +@@ -0,0 +1,632 @@ ++From 95479b1485191f0e7b95f9b37df0b39c751a5b56 Mon Sep 17 00:00:00 2001 ++From: =?UTF-8?q?Linus=20L=C3=BCssing?= <linus.luessing@c0d3.blue> ++Date: Mon, 29 Jun 2020 19:04:05 +0200 ++Subject: [PATCH] bridge: Implement MLD Querier wake-up calls / Android bug ++ workaround ++MIME-Version: 1.0 ++Content-Type: text/plain; charset=UTF-8 ++Content-Transfer-Encoding: 8bit ++ ++Implement a configurable MLD Querier wake-up calls "feature" which ++works around a widely spread Android bug in connection with IGMP/MLD ++snooping. ++ ++Currently there are mobile devices (e.g. Android) which are not able ++to receive and respond to MLD Queries reliably because the Wifi driver ++filters a lot of ICMPv6 when the device is asleep - including ++MLD. This in turn breaks IPv6 communication when MLD Snooping is ++enabled. However there is one ICMPv6 type which is allowed to pass and ++which can be used to wake up the mobile device: ICMPv6 Echo Requests. ++ ++If this bridge is the selected MLD Querier then setting ++"multicast_wakeupcall" to a number n greater than 0 will send n ++ICMPv6 Echo Requests to each host behind this port to wake ++them up with each MLD Query. Upon receiving a matching ICMPv6 Echo ++Reply an MLD Query with a unicast ethernet destination will be sent ++to the specific host(s). ++ ++Link: https://issuetracker.google.com/issues/149630944 ++Link: https://github.com/freifunk-gluon/gluon/issues/1832 ++ ++Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue> ++--- ++ include/linux/if_bridge.h | 1 + ++ include/uapi/linux/if_link.h | 1 + ++ net/bridge/Kconfig | 26 ++++ ++ net/bridge/br_fdb.c | 10 ++ ++ net/bridge/br_input.c | 4 +- ++ net/bridge/br_multicast.c | 288 ++++++++++++++++++++++++++++++++++- ++ net/bridge/br_netlink.c | 19 +++ ++ net/bridge/br_private.h | 19 +++ ++ net/bridge/br_sysfs_if.c | 18 +++ ++ 9 files changed, 378 insertions(+), 8 deletions(-) ++ ++diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h ++index 78d2ed50c524..44fa4235c8e8 100644 ++--- a/include/linux/if_bridge.h +++++ b/include/linux/if_bridge.h ++@@ -50,6 +50,7 @@ struct br_ip_list { ++ #define BR_VLAN_TUNNEL BIT(13) ++ #define BR_BCAST_FLOOD BIT(14) ++ #define BR_ISOLATED BIT(16) +++#define BR_MULTICAST_WAKEUPCALL BIT(17) ++ ++ #define BR_DEFAULT_AGEING_TIME (300 * HZ) ++ ++diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h ++index 45529dba6554..d019754d6870 100644 ++--- a/include/uapi/linux/if_link.h +++++ b/include/uapi/linux/if_link.h ++@@ -328,6 +328,7 @@ enum { ++ IFLA_BRPORT_BCAST_FLOOD, ++ IFLA_BRPORT_NEIGH_SUPPRESS, ++ IFLA_BRPORT_ISOLATED, +++ IFLA_BRPORT_MCAST_WAKEUPCALL, ++ __IFLA_BRPORT_MAX ++ }; ++ #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) ++diff --git a/net/bridge/Kconfig b/net/bridge/Kconfig ++index aa0d3b2f1bb7..66495bc07dfc 100644 ++--- a/net/bridge/Kconfig +++++ b/net/bridge/Kconfig ++@@ -47,6 +47,32 @@ config BRIDGE_IGMP_SNOOPING ++ ++ If unsure, say Y. ++ +++config BRIDGE_IGMP_SNOOPING_WAKEUPCALLS +++ bool "MLD Querier wake-up calls" +++ depends on BRIDGE_IGMP_SNOOPING +++ depends on IPV6 +++ help +++ If you say Y here, then the MLD Snooping Querier will be built +++ with a per bridge port wake-up call "feature"/workaround. +++ +++ Currently there are mobile devices (e.g. Android) which are not able +++ to receive and respond to MLD Queries reliably because the Wifi driver +++ filters a lot of ICMPv6 when the device is asleep - including MLD. +++ This in turn breaks IPv6 communication when MLD Snooping is enabled. +++ However there is one ICMPv6 type which is allowed to pass and +++ which can be used to wake up the mobile device: ICMPv6 Echo Requests. +++ +++ If this bridge is the selected MLD Querier then setting +++ "multicast_wakeupcall" to a number n greater than 0 will send n +++ ICMPv6 Echo Requests to each host behind this port to wake them up +++ with each MLD Query. Upon receiving a matching ICMPv6 Echo Reply +++ an MLD Query with a unicast ethernet destination will be sent to the +++ specific host(s). +++ +++ Say N to exclude this support and reduce the binary size. +++ +++ If unsure, say N. +++ ++ config BRIDGE_VLAN_FILTERING ++ bool "VLAN filtering" ++ depends on BRIDGE ++diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c ++index 4ea5c8bbe286..180555d2f21b 100644 ++--- a/net/bridge/br_fdb.c +++++ b/net/bridge/br_fdb.c ++@@ -81,6 +81,10 @@ static void fdb_rcu_free(struct rcu_head *head) ++ { ++ struct net_bridge_fdb_entry *ent ++ = container_of(head, struct net_bridge_fdb_entry, rcu); +++ +++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS +++ del_timer_sync(&ent->wakeupcall_timer); +++#endif ++ kmem_cache_free(br_fdb_cache, ent); ++ } ++ ++@@ -498,6 +502,12 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, ++ fdb->added_by_external_learn = 0; ++ fdb->offloaded = 0; ++ fdb->updated = fdb->used = jiffies; +++ +++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS +++ timer_setup(&fdb->wakeupcall_timer, +++ br_multicast_send_wakeupcall, 0); +++#endif +++ ++ hlist_add_head_rcu(&fdb->hlist, head); ++ } ++ return fdb; ++diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c ++index 66175565efa6..cc8f04408981 100644 ++--- a/net/bridge/br_input.c +++++ b/net/bridge/br_input.c ++@@ -200,8 +200,10 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb ++ if (dst) { ++ unsigned long now = jiffies; ++ ++- if (dst->is_local) +++ if (dst->is_local) { +++ br_multicast_wakeupcall_rcv(br, p, skb, vid); ++ return br_pass_frame_up(skb); +++ } ++ ++ if (now != dst->used) ++ dst->used = now; ++diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c ++index 779af4efea27..6d8d1cf2ddc6 100644 ++--- a/net/bridge/br_multicast.c +++++ b/net/bridge/br_multicast.c ++@@ -463,10 +463,11 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br, ++ #if IS_ENABLED(CONFIG_IPV6) ++ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br, ++ const struct in6_addr *grp, ++- u8 *igmp_type) +++ u8 *igmp_type, +++ bool delay) ++ { +++ unsigned long interval = 0; ++ struct mld2_query *mld2q; ++- unsigned long interval; ++ struct ipv6hdr *ip6h; ++ struct mld_msg *mldq; ++ size_t mld_hdr_size; ++@@ -525,9 +526,13 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br, ++ ++ /* ICMPv6 */ ++ skb_set_transport_header(skb, skb->len); ++- interval = ipv6_addr_any(grp) ? ++- br->multicast_query_response_interval : ++- br->multicast_last_member_interval; +++ if (delay) { +++ interval = ipv6_addr_any(grp) ? +++ br->multicast_query_response_interval : +++ br->multicast_last_member_interval; +++ interval = jiffies_to_msecs(interval); +++ } +++ ++ *igmp_type = ICMPV6_MGM_QUERY; ++ switch (br->multicast_mld_version) { ++ case 1: ++@@ -535,7 +540,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br, ++ mldq->mld_type = ICMPV6_MGM_QUERY; ++ mldq->mld_code = 0; ++ mldq->mld_cksum = 0; ++- mldq->mld_maxdelay = htons((u16)jiffies_to_msecs(interval)); +++ mldq->mld_maxdelay = htons((u16)interval); ++ mldq->mld_reserved = 0; ++ mldq->mld_mca = *grp; ++ mldq->mld_cksum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, ++@@ -584,7 +589,7 @@ static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br, ++ #if IS_ENABLED(CONFIG_IPV6) ++ case htons(ETH_P_IPV6): ++ return br_ip6_multicast_alloc_query(br, &addr->u.ip6, ++- igmp_type); +++ igmp_type, true); ++ #endif ++ } ++ return NULL; ++@@ -906,6 +911,172 @@ static void br_multicast_select_own_querier(struct net_bridge *br, ++ #endif ++ } ++ +++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS +++ +++#define BR_MC_WAKEUP_ID htons(0xEC6B) /* random identifier */ +++#define BR_MC_ETH_ZERO { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } +++#define BR_MC_IN6_ZERO \ +++{ \ +++ .s6_addr32[0] = 0, .s6_addr32[1] = 0, \ +++ .s6_addr32[2] = 0, .s6_addr32[3] = 0, \ +++} +++ +++#define BR_MC_IN6_FE80 \ +++{ \ +++ .s6_addr32[0] = htonl(0xfe800000), \ +++ .s6_addr32[1] = 0, \ +++ .s6_addr32[2] = htonl(0x000000ff), \ +++ .s6_addr32[3] = htonl(0xfe000000), \ +++} +++ +++#define BR_MC_ECHO_LEN sizeof(pkt->echohdr) +++ +++static struct sk_buff *br_multicast_alloc_wakeupcall(struct net_bridge *br, +++ struct net_bridge_port *port, +++ u8 *eth_dst) +++{ +++ struct in6_addr ip6_src, ip6_dst = BR_MC_IN6_FE80; +++ struct sk_buff *skb; +++ __wsum csum_part; +++ __sum16 csum; +++ +++ struct wakeupcall_pkt { +++ struct ethhdr ethhdr; +++ struct ipv6hdr ip6hdr; +++ struct icmp6hdr echohdr; +++ } __packed; +++ +++ struct wakeupcall_pkt *pkt; +++ +++ static const struct wakeupcall_pkt __pkt_template = { +++ .ethhdr = { +++ .h_dest = BR_MC_ETH_ZERO, // update +++ .h_source = BR_MC_ETH_ZERO, // update +++ .h_proto = htons(ETH_P_IPV6), +++ }, +++ .ip6hdr = { +++ .priority = 0, +++ .version = 0x6, +++ .flow_lbl = { 0x00, 0x00, 0x00 }, +++ .payload_len = htons(BR_MC_ECHO_LEN), +++ .nexthdr = IPPROTO_ICMPV6, +++ .hop_limit = 1, +++ .saddr = BR_MC_IN6_ZERO, // update +++ .daddr = BR_MC_IN6_ZERO, // update +++ }, +++ .echohdr = { +++ .icmp6_type = ICMPV6_ECHO_REQUEST, +++ .icmp6_code = 0, +++ .icmp6_cksum = 0, // update +++ .icmp6_dataun.u_echo = { +++ .identifier = BR_MC_WAKEUP_ID, +++ .sequence = 0, +++ }, +++ }, +++ }; +++ +++ memcpy(&ip6_dst.s6_addr32[2], ð_dst[0], ETH_ALEN / 2); +++ memcpy(&ip6_dst.s6_addr[13], ð_dst[3], ETH_ALEN / 2); +++ ip6_dst.s6_addr[8] ^= 0x02; +++ if (ipv6_dev_get_saddr(dev_net(br->dev), br->dev, &ip6_dst, 0, +++ &ip6_src)) +++ return NULL; +++ +++ skb = netdev_alloc_skb_ip_align(br->dev, sizeof(*pkt)); +++ if (!skb) +++ return NULL; +++ +++ skb->protocol = htons(ETH_P_IPV6); +++ skb->dev = port->dev; +++ +++ pkt = (struct wakeupcall_pkt *)skb->data; +++ *pkt = __pkt_template; +++ +++ ether_addr_copy(pkt->ethhdr.h_source, br->dev->dev_addr); +++ ether_addr_copy(pkt->ethhdr.h_dest, eth_dst); +++ +++ pkt->ip6hdr.saddr = ip6_src; +++ pkt->ip6hdr.daddr = ip6_dst; +++ +++ csum_part = csum_partial(&pkt->echohdr, sizeof(pkt->echohdr), 0); +++ csum = csum_ipv6_magic(&ip6_src, &ip6_dst, sizeof(pkt->echohdr), +++ IPPROTO_ICMPV6, csum_part); +++ pkt->echohdr.icmp6_cksum = csum; +++ +++ skb_reset_mac_header(skb); +++ skb_set_network_header(skb, offsetof(struct wakeupcall_pkt, ip6hdr)); +++ skb_set_transport_header(skb, offsetof(struct wakeupcall_pkt, echohdr)); +++ skb_put(skb, sizeof(*pkt)); +++ __skb_pull(skb, sizeof(pkt->ethhdr)); +++ +++ return skb; +++} +++ +++void br_multicast_send_wakeupcall(struct timer_list *t) +++{ +++ struct net_bridge_fdb_entry *fdb = from_timer(fdb, t, wakeupcall_timer); +++ struct net_bridge_port *port = fdb->dst; +++ struct net_bridge *br = port->br; +++ struct sk_buff *skb, *skb0; +++ int i; +++ +++ skb0 = br_multicast_alloc_wakeupcall(br, port, fdb->addr.addr); +++ if (!skb0) +++ return; +++ +++ for (i = port->wakeupcall_num_rings; i > 0; i--) { +++ if (i > 1) { +++ skb = skb_clone(skb0, GFP_ATOMIC); +++ if (!skb) { +++ kfree_skb(skb0); +++ break; +++ } +++ } else { +++ skb = skb0; +++ } +++ +++ NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, +++ dev_net(port->dev), NULL, skb, NULL, skb->dev, +++ br_dev_queue_push_xmit); +++ } +++} +++ +++static void br_multicast_schedule_wakeupcalls(struct net_bridge *br, +++ struct net_bridge_port *port, +++ const struct in6_addr *group) +++{ +++ struct net_bridge_fdb_entry *fdb; +++ unsigned long delay; +++ int i; +++ +++ rcu_read_lock(); +++ for (i = 0; i < BR_HASH_SIZE; i++) { +++ hlist_for_each_entry_rcu(fdb, &br->hash[i], hlist) { +++ if (!fdb->dst || fdb->dst->dev != port->dev) +++ continue; +++ +++ /* Wake-up calls to VLANs unsupported for now */ +++ if (fdb->vlan_id) +++ continue; +++ +++ /* Spread the ICMPv6 Echo Requests to avoid congestion. +++ * We then won't use a max response delay for the +++ * queries later, as that would be redundant. Spread +++ * randomly by a little less than max response delay to +++ * anticipate the extra round trip. +++ */ +++ delay = ipv6_addr_any(group) ? +++ br->multicast_query_response_interval : +++ br->multicast_last_member_interval; +++ delay = prandom_u32() % (3 * delay / 4); +++ +++ timer_reduce(&fdb->wakeupcall_timer, jiffies + delay); +++ } +++ } +++ rcu_read_unlock(); +++} +++#endif /* CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS */ +++ ++ static void __br_multicast_send_query(struct net_bridge *br, ++ struct net_bridge_port *port, ++ struct br_ip *ip) ++@@ -924,6 +1095,13 @@ static void __br_multicast_send_query(struct net_bridge *br, ++ NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, ++ dev_net(port->dev), NULL, skb, NULL, skb->dev, ++ br_dev_queue_push_xmit); +++ +++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS +++ if (port->wakeupcall_num_rings && +++ ip->proto == htons(ETH_P_IPV6)) +++ br_multicast_schedule_wakeupcalls(br, port, +++ &ip->u.ip6); +++#endif ++ } else { ++ br_multicast_select_own_querier(br, ip, skb); ++ br_multicast_count(br, port, skb, igmp_type, ++@@ -1952,6 +2130,93 @@ int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port, ++ return ret; ++ } ++ +++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS +++ +++static bool br_multicast_wakeupcall_check(struct net_bridge *br, +++ struct net_bridge_port *port, +++ struct sk_buff *skb, u16 vid) +++{ +++ struct ethhdr *eth = eth_hdr(skb); +++ const struct ipv6hdr *ip6h; +++ unsigned int offset, len; +++ struct icmp6hdr *icmp6h; +++ +++ /* Wake-up calls to VLANs unsupported for now */ +++ if (!port->wakeupcall_num_rings || vid || +++ eth->h_proto != htons(ETH_P_IPV6)) +++ return false; +++ +++ if (!ether_addr_equal(eth->h_dest, br->dev->dev_addr) || +++ is_multicast_ether_addr(eth->h_source) || +++ is_zero_ether_addr(eth->h_source)) +++ return false; +++ +++ offset = skb_network_offset(skb) + sizeof(*ip6h); +++ if (!pskb_may_pull(skb, offset)) +++ return false; +++ +++ ip6h = ipv6_hdr(skb); +++ +++ if (ip6h->version != 6) +++ return false; +++ +++ len = offset + ntohs(ip6h->payload_len); +++ if (skb->len < len || len <= offset) +++ return false; +++ +++ if (ip6h->nexthdr != IPPROTO_ICMPV6) +++ return false; +++ +++ skb_set_transport_header(skb, offset); +++ +++ if (ipv6_mc_check_icmpv6(skb) < 0) +++ return false; +++ +++ icmp6h = (struct icmp6hdr *)skb_transport_header(skb); +++ if (icmp6h->icmp6_type != ICMPV6_ECHO_REPLY || +++ icmp6h->icmp6_dataun.u_echo.identifier != BR_MC_WAKEUP_ID) +++ return false; +++ +++ return true; +++} +++ +++static void br_multicast_wakeupcall_send_mldq(struct net_bridge *br, +++ struct net_bridge_port *port, +++ const u8 *eth_dst) +++{ +++ const struct in6_addr grp = BR_MC_IN6_ZERO; +++ struct sk_buff *skb; +++ u8 igmp_type; +++ +++ /* we might have been triggered by multicast-address-specific query +++ * but reply with a general MLD query for now to keep things simple +++ */ +++ skb = br_ip6_multicast_alloc_query(br, &grp, &igmp_type, false); +++ if (!skb) +++ return; +++ +++ skb->dev = port->dev; +++ ether_addr_copy(eth_hdr(skb)->h_dest, eth_dst); +++ +++ br_multicast_count(br, port, skb, igmp_type, +++ BR_MCAST_DIR_TX); +++ NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, +++ dev_net(port->dev), NULL, skb, NULL, skb->dev, +++ br_dev_queue_push_xmit); +++} +++ +++void br_multicast_wakeupcall_rcv(struct net_bridge *br, +++ struct net_bridge_port *port, +++ struct sk_buff *skb, u16 vid) +++{ +++ if (!br_multicast_wakeupcall_check(br, port, skb, vid)) +++ return; +++ +++ br_multicast_wakeupcall_send_mldq(br, port, eth_hdr(skb)->h_source); +++} +++ +++#endif /* CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS */ +++ ++ static void br_multicast_query_expired(struct net_bridge *br, ++ struct bridge_mcast_own_query *query, ++ struct bridge_mcast_querier *querier) ++@@ -2239,6 +2504,15 @@ int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val) ++ return err; ++ } ++ +++int br_multicast_set_wakeupcall(struct net_bridge_port *p, unsigned long val) +++{ +++ if (val > U8_MAX) +++ return -EINVAL; +++ +++ p->wakeupcall_num_rings = val; +++ return 0; +++} +++ ++ static void br_multicast_start_querier(struct net_bridge *br, ++ struct bridge_mcast_own_query *query) ++ { ++diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c ++index 2bf371d9c6d9..85f44a15a97a 100644 ++--- a/net/bridge/br_netlink.c +++++ b/net/bridge/br_netlink.c ++@@ -152,6 +152,9 @@ static inline size_t br_port_info_size(void) ++ + nla_total_size_64bit(sizeof(u64)) /* IFLA_BRPORT_HOLD_TIMER */ ++ #ifdef CONFIG_BRIDGE_IGMP_SNOOPING ++ + nla_total_size(sizeof(u8)) /* IFLA_BRPORT_MULTICAST_ROUTER */ +++#endif +++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS +++ + nla_total_size(sizeof(u8)) /* IFLA_BRPORT_MCAST_WAKEUPCALL */ ++ #endif ++ + 0; ++ } ++@@ -231,6 +234,11 @@ static int br_port_fill_attrs(struct sk_buff *skb, ++ p->multicast_router)) ++ return -EMSGSIZE; ++ #endif +++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS +++ if (nla_put_u8(skb, IFLA_BRPORT_MCAST_WAKEUPCALL, +++ p->wakeupcall_num_rings)) +++ return -EMSGSIZE; +++#endif ++ ++ return 0; ++ } ++@@ -637,6 +645,7 @@ static const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = { ++ [IFLA_BRPORT_PROXYARP_WIFI] = { .type = NLA_U8 }, ++ [IFLA_BRPORT_MULTICAST_ROUTER] = { .type = NLA_U8 }, ++ [IFLA_BRPORT_MCAST_TO_UCAST] = { .type = NLA_U8 }, +++ [IFLA_BRPORT_MCAST_WAKEUPCALL] = { .type = NLA_U8 }, ++ [IFLA_BRPORT_MCAST_FLOOD] = { .type = NLA_U8 }, ++ [IFLA_BRPORT_BCAST_FLOOD] = { .type = NLA_U8 }, ++ [IFLA_BRPORT_ISOLATED] = { .type = NLA_U8 }, ++@@ -781,6 +790,16 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) ++ if (err) ++ return err; ++ +++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS +++ if (tb[IFLA_BRPORT_MCAST_WAKEUPCALL]) { +++ u8 wakeupcall = nla_get_u8(tb[IFLA_BRPORT_MCAST_WAKEUPCALL]); +++ +++ err = br_multicast_set_wakeupcall(p, wakeupcall); +++ if (err) +++ return err; +++ } +++#endif +++ ++ br_port_flags_change(p, old_flags ^ p->flags); ++ return 0; ++ } ++diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h ++index 32f0d61c1803..bbf7513c1d16 100644 ++--- a/net/bridge/br_private.h +++++ b/net/bridge/br_private.h ++@@ -178,6 +178,10 @@ struct net_bridge_fdb_entry { ++ unsigned long used; ++ ++ struct rcu_head rcu; +++ +++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS +++ struct timer_list wakeupcall_timer; +++#endif ++ }; ++ ++ #define MDB_PG_FLAGS_PERMANENT BIT(0) ++@@ -256,6 +260,7 @@ struct net_bridge_port { ++ struct timer_list multicast_router_timer; ++ struct hlist_head mglist; ++ struct hlist_node rlist; +++ u8 wakeupcall_num_rings; ++ #endif ++ ++ #ifdef CONFIG_SYSFS ++@@ -790,6 +795,20 @@ static inline int br_multicast_igmp_type(const struct sk_buff *skb) ++ } ++ #endif ++ +++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS +++void br_multicast_wakeupcall_rcv(struct net_bridge *br, +++ struct net_bridge_port *port, +++ struct sk_buff *skb, u16 vid); +++void br_multicast_send_wakeupcall(struct timer_list *t); +++int br_multicast_set_wakeupcall(struct net_bridge_port *p, unsigned long val); +++#else +++static inline void br_multicast_wakeupcall_rcv(struct net_bridge *br, +++ struct net_bridge_port *port, +++ struct sk_buff *skb, u16 vid) +++{ +++} +++#endif /* CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS */ +++ ++ /* br_vlan.c */ ++ #ifdef CONFIG_BRIDGE_VLAN_FILTERING ++ bool br_allowed_ingress(const struct net_bridge *br, ++diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c ++index b51aefa636b6..e9f1f5ccec3e 100644 ++--- a/net/bridge/br_sysfs_if.c +++++ b/net/bridge/br_sysfs_if.c ++@@ -194,6 +194,21 @@ BRPORT_ATTR_FLAG(multicast_fast_leave, BR_MULTICAST_FAST_LEAVE); ++ BRPORT_ATTR_FLAG(multicast_to_unicast, BR_MULTICAST_TO_UNICAST); ++ #endif ++ +++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS +++static ssize_t show_multicast_wakeupcall(struct net_bridge_port *p, char *buf) +++{ +++ return sprintf(buf, "%d\n", p->wakeupcall_num_rings); +++} +++ +++static int store_multicast_wakeupcall(struct net_bridge_port *p, +++ unsigned long v) +++{ +++ return br_multicast_set_wakeupcall(p, v); +++} +++static BRPORT_ATTR(multicast_wakeupcall, 0644, show_multicast_wakeupcall, +++ store_multicast_wakeupcall); +++#endif +++ ++ static const struct brport_attribute *brport_attrs[] = { ++ &brport_attr_path_cost, ++ &brport_attr_priority, ++@@ -219,6 +234,9 @@ static const struct brport_attribute *brport_attrs[] = { ++ &brport_attr_multicast_router, ++ &brport_attr_multicast_fast_leave, ++ &brport_attr_multicast_to_unicast, +++#endif +++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS +++ &brport_attr_multicast_wakeupcall, ++ #endif ++ &brport_attr_proxyarp, ++ &brport_attr_proxyarp_wifi, ++-- ++2.28.0.rc1 ++ diff --git a/patches/packages/routing/0002-batman-adv-compat-remove-ip_mc_check_igmp-ipv6_mc_check_mld.patch b/patches/packages/routing/0002-batman-adv-compat-remove-ip_mc_check_igmp-ipv6_mc_check_mld.patch new file mode 100644 index 0000000000000000000000000000000000000000..1114be8ece4125da04be704db4d0cb81f4d39199 --- /dev/null +++ b/patches/packages/routing/0002-batman-adv-compat-remove-ip_mc_check_igmp-ipv6_mc_check_mld.patch @@ -0,0 +1,57 @@ +From: Linus Lüssing <linus.luessing@c0d3.blue> +Date: Sun, 5 Jul 2020 04:02:17 +0200 +Subject: batman-adv: compat: remove ip_mc_check_igmp() + ipv6_mc_check_mld() + +The upstream Linux patches which reduced the number of arguments for +these functions from two to one were added to OpenWrt. Therefore compat +code for them is no more needed. + +Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue> + +diff --git a/batman-adv/src/compat-hacks.h b/batman-adv/src/compat-hacks.h +index d8de483a10243c77b2c5f49720a39dedb1404f01..1cbcfcf070c7dd943574612c0d13f886c88ecc25 100644 +--- a/batman-adv/src/compat-hacks.h ++++ b/batman-adv/src/compat-hacks.h +@@ -53,39 +53,12 @@ int ipv6_mc_check_mld(struct sk_buff *skb); + + #elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0) + ++#undef ip_mc_check_igmp ++#undef ipv6_mc_check_mld ++ + #include_next <linux/igmp.h> + #include_next <net/addrconf.h> + +-static inline int batadv_ipv6_mc_check_mld1(struct sk_buff *skb) +-{ +- return ipv6_mc_check_mld(skb, NULL); +-} +- +-static inline int batadv_ipv6_mc_check_mld2(struct sk_buff *skb, +- struct sk_buff **skb_trimmed) +-{ +- return ipv6_mc_check_mld(skb, skb_trimmed); +-} +- +-#define ipv6_mc_check_mld_get(_1, _2, ipv6_mc_check_mld_name, ...) ipv6_mc_check_mld_name +-#define ipv6_mc_check_mld(...) \ +- ipv6_mc_check_mld_get(__VA_ARGS__, batadv_ipv6_mc_check_mld2, batadv_ipv6_mc_check_mld1)(__VA_ARGS__) +- +-static inline int batadv_ip_mc_check_igmp1(struct sk_buff *skb) +-{ +- return ip_mc_check_igmp(skb, NULL); +-} +- +-static inline int batadv_ip_mc_check_igmp2(struct sk_buff *skb, +- struct sk_buff **skb_trimmed) +-{ +- return ip_mc_check_igmp(skb, skb_trimmed); +-} +- +-#define ip_mc_check_igmp_get(_1, _2, ip_mc_check_igmp_name, ...) ip_mc_check_igmp_name +-#define ip_mc_check_igmp(...) \ +- ip_mc_check_igmp_get(__VA_ARGS__, batadv_ip_mc_check_igmp2, batadv_ip_mc_check_igmp1)(__VA_ARGS__) +- + #endif /* < KERNEL_VERSION(4, 2, 0) */ + + #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)