diff --git a/patches/openwrt/0007-kernel-bridge-Implement-MLD-Querier-wake-up-calls-Android-bug-workaround.patch b/patches/openwrt/0007-kernel-bridge-Implement-MLD-Querier-wake-up-calls-Android-bug-workaround.patch new file mode 100644 index 00000000..d1c35c67 --- /dev/null +++ b/patches/openwrt/0007-kernel-bridge-Implement-MLD-Querier-wake-up-calls-Android-bug-workaround.patch @@ -0,0 +1,914 @@ +From: Linus Lüssing +Date: Sat, 1 Jan 2022 10:09:13 +0100 +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 + +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..e3da684dc950ea4c226705d27b23b0470499d54b +--- /dev/null ++++ b/package/network/config/netifd/patches/0001-bridge-Add-multicast_wakeupcall-option.patch +@@ -0,0 +1,176 @@ ++From 6db5050d9e046a486260ea23a77118db4f8a6672 Mon Sep 17 00:00:00 2001 ++From: =?UTF-8?q?Linus=20L=C3=BCssing?= ++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 ++--- ++ device.c | 9 +++++++++ ++ device.h | 37 ++++++++++++++++++++----------------- ++ system-linux.c | 13 +++++++++++++ ++ 3 files changed, 42 insertions(+), 17 deletions(-) ++ ++--- a/device.c +++++ b/device.c ++@@ -47,6 +47,7 @@ static const struct blobmsg_policy dev_a ++ [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 }, ++@@ -261,6 +262,7 @@ device_merge_settings(struct device *dev ++ 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; ++@@ -386,6 +388,11 @@ device_init_settings(struct device *dev, ++ 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) ++@@ -1186,6 +1193,8 @@ device_dump_status(struct blob_buf *b, s ++ 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) ++--- a/device.h +++++ b/device.h ++@@ -44,6 +44,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, ++@@ -109,23 +110,24 @@ enum { ++ DEV_OPT_MTU6 = (1ULL << 12), ++ DEV_OPT_DADTRANSMITS = (1ULL << 13), ++ DEV_OPT_MULTICAST_TO_UNICAST = (1ULL << 14), ++- DEV_OPT_MULTICAST_ROUTER = (1ULL << 15), ++- DEV_OPT_MULTICAST = (1ULL << 16), ++- DEV_OPT_LEARNING = (1ULL << 17), ++- DEV_OPT_UNICAST_FLOOD = (1ULL << 18), ++- DEV_OPT_NEIGHGCSTALETIME = (1ULL << 19), ++- DEV_OPT_MULTICAST_FAST_LEAVE = (1ULL << 20), ++- DEV_OPT_SENDREDIRECTS = (1ULL << 21), ++- DEV_OPT_NEIGHLOCKTIME = (1ULL << 22), ++- DEV_OPT_ISOLATE = (1ULL << 23), ++- DEV_OPT_IP6SEGMENTROUTING = (1ULL << 24), ++- DEV_OPT_DROP_V4_UNICAST_IN_L2_MULTICAST = (1ULL << 25), ++- DEV_OPT_DROP_V6_UNICAST_IN_L2_MULTICAST = (1ULL << 26), ++- DEV_OPT_DROP_GRATUITOUS_ARP = (1ULL << 27), ++- DEV_OPT_DROP_UNSOLICITED_NA = (1ULL << 28), ++- DEV_OPT_ARP_ACCEPT = (1ULL << 29), ++- DEV_OPT_SPEED = (1ULL << 30), ++- DEV_OPT_DUPLEX = (1ULL << 31), +++ DEV_OPT_MULTICAST_WAKEUPCALL = (1ULL << 15), +++ DEV_OPT_MULTICAST_ROUTER = (1ULL << 16), +++ DEV_OPT_MULTICAST = (1ULL << 17), +++ DEV_OPT_LEARNING = (1ULL << 18), +++ DEV_OPT_UNICAST_FLOOD = (1ULL << 19), +++ DEV_OPT_NEIGHGCSTALETIME = (1ULL << 20), +++ DEV_OPT_MULTICAST_FAST_LEAVE = (1ULL << 21), +++ DEV_OPT_SENDREDIRECTS = (1ULL << 22), +++ DEV_OPT_NEIGHLOCKTIME = (1ULL << 23), +++ DEV_OPT_ISOLATE = (1ULL << 24), +++ DEV_OPT_IP6SEGMENTROUTING = (1ULL << 25), +++ DEV_OPT_DROP_V4_UNICAST_IN_L2_MULTICAST = (1ULL << 26), +++ DEV_OPT_DROP_V6_UNICAST_IN_L2_MULTICAST = (1ULL << 27), +++ DEV_OPT_DROP_GRATUITOUS_ARP = (1ULL << 28), +++ DEV_OPT_DROP_UNSOLICITED_NA = (1ULL << 29), +++ DEV_OPT_ARP_ACCEPT = (1ULL << 30), +++ DEV_OPT_SPEED = (1ULL << 31), +++ DEV_OPT_DUPLEX = (1ULL << 32), ++ }; ++ ++ /* events broadcasted to all users of a device */ ++@@ -187,6 +189,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; ++--- a/system-linux.c +++++ b/system-linux.c ++@@ -465,6 +465,11 @@ static void system_bridge_set_multicast_ ++ system_set_dev_sysfs("brport/multicast_to_unicast", dev->ifname, val); ++ } ++ +++static void system_bridge_set_multicast_wakeupcall(struct device *dev, const char *val) +++{ +++ system_set_dev_sysfs("brport/multicast_wakeupcall", dev->ifname, val); +++} +++ ++ static void system_bridge_set_multicast_fast_leave(struct device *dev, const char *val) ++ { ++ system_set_dev_sysfs("brport/multicast_fast_leave", dev->ifname, val); ++@@ -844,8 +849,10 @@ static char *system_get_bridge(const cha ++ 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; +++ char buf[64]; ++ ++ if (bridge->settings.flags & DEV_OPT_MULTICAST_TO_UNICAST && ++ !bridge->settings.multicast_to_unicast) ++@@ -858,6 +865,12 @@ system_bridge_set_wireless(struct device ++ system_bridge_set_multicast_to_unicast(dev, mcast_to_ucast ? "1" : "0"); ++ system_bridge_set_hairpin_mode(dev, hairpin ? "1" : "0"); ++ system_bridge_set_proxyarp_wifi(dev, dev->wireless_proxyarp ? "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) +diff --git a/target/linux/generic/config-5.10 b/target/linux/generic/config-5.10 +index e557379d240044333de97019e649b1c25b37bdeb..c75ef262a3ae1bdb846e55d600939ab54f18728c 100644 +--- a/target/linux/generic/config-5.10 ++++ b/target/linux/generic/config-5.10 +@@ -736,6 +736,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_MRP is not set + # CONFIG_BRIDGE_NETFILTER is not set + # CONFIG_BRIDGE_NF_EBTABLES is not set +diff --git a/target/linux/generic/hack-5.10/601-bridge-Implement-MLD-Querier-wake-up-calls-Android-b.patch b/target/linux/generic/hack-5.10/601-bridge-Implement-MLD-Querier-wake-up-calls-Android-b.patch +new file mode 100644 +index 0000000000000000000000000000000000000000..17df35fe4a812b5b5118beb9239bf825be7b67ed +--- /dev/null ++++ b/target/linux/generic/hack-5.10/601-bridge-Implement-MLD-Querier-wake-up-calls-Android-b.patch +@@ -0,0 +1,687 @@ ++From e0fd3ecf9b4f6825ad0380b759baf88ef508bed5 Mon Sep 17 00:00:00 2001 ++From: =?UTF-8?q?Linus=20L=C3=BCssing?= ++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 ++--- ++ include/linux/if_bridge.h | 1 + ++ include/net/addrconf.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 | 289 ++++++++++++++++++++++++++++++++++- ++ net/bridge/br_netlink.c | 19 +++ ++ net/bridge/br_private.h | 19 +++ ++ net/bridge/br_sysfs_if.c | 18 +++ ++ net/core/rtnetlink.c | 2 +- ++ net/ipv6/mcast_snoop.c | 3 +- ++ 12 files changed, 383 insertions(+), 10 deletions(-) ++ ++diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h ++index ea1c7d151f46..d7714f60b88a 100644 ++--- a/include/linux/if_bridge.h +++++ b/include/linux/if_bridge.h ++@@ -58,6 +58,7 @@ struct br_ip_list { ++ #define BR_MRP_LOST_IN_CONT BIT(19) ++ #define BR_BPDU_FILTER BIT(20) ++ #define BR_OFFLOAD BIT(21) +++#define BR_MULTICAST_WAKEUPCALL BIT(22) ++ ++ #define BR_DEFAULT_AGEING_TIME (300 * HZ) ++ ++diff --git a/include/net/addrconf.h b/include/net/addrconf.h ++index e7ce719838b5..af8f153e1b5f 100644 ++--- a/include/net/addrconf.h +++++ b/include/net/addrconf.h ++@@ -235,6 +235,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 sk_buff *skb, struct inet6_ifaddr *ifp); ++ ++diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h ++index ffeb05c061d2..1956ab99e97d 100644 ++--- a/include/uapi/linux/if_link.h +++++ b/include/uapi/linux/if_link.h ++@@ -525,6 +525,7 @@ enum { ++ IFLA_BRPORT_MRP_RING_OPEN, ++ IFLA_BRPORT_MRP_IN_OPEN, ++ IFLA_BRPORT_BPDU_FILTER, +++ 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 80879196560c..056e80bf00c4 100644 ++--- a/net/bridge/Kconfig +++++ b/net/bridge/Kconfig ++@@ -48,6 +48,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 bc6c8d5cd1e1..98ca86b7ca42 100644 ++--- a/net/bridge/br_fdb.c +++++ b/net/bridge/br_fdb.c ++@@ -85,6 +85,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); ++ } ++ ++@@ -516,6 +520,12 @@ static struct net_bridge_fdb_entry *fdb_create(struct net_bridge *br, ++ fdb->updated = fdb->used = jiffies; ++ INIT_HLIST_HEAD(&fdb->offload_in); ++ INIT_HLIST_HEAD(&fdb->offload_out); +++ +++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS +++ timer_setup(&fdb->wakeupcall_timer, +++ br_multicast_send_wakeupcall, 0); +++#endif +++ ++ if (rhashtable_lookup_insert_fast(&br->fdb_hash_tbl, ++ &fdb->rhnode, ++ br_fdb_rht_params)) { ++diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c ++index 64f3eaa3879d..a449a1f60f1b 100644 ++--- a/net/bridge/br_input.c +++++ b/net/bridge/br_input.c ++@@ -163,8 +163,10 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb ++ if (dst) { ++ unsigned long now = jiffies; ++ ++- if (test_bit(BR_FDB_LOCAL, &dst->flags)) +++ if (test_bit(BR_FDB_LOCAL, &dst->flags)) { +++ 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 e5328a2777ec..a2f45bd523ab 100644 ++--- a/net/bridge/br_multicast.c +++++ b/net/bridge/br_multicast.c ++@@ -833,15 +833,16 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br, ++ const struct in6_addr *group, ++ bool with_srcs, bool over_llqt, ++ u8 sflag, u8 *igmp_type, ++- bool *need_rexmit) +++ bool *need_rexmit, +++ bool delay) ++ { ++ struct net_bridge_port *p = pg ? pg->key.port : NULL; ++ struct net_bridge_group_src *ent; ++ size_t pkt_size, mld_hdr_size; ++ unsigned long now = jiffies; +++ unsigned long interval = 0; ++ struct mld2_query *mld2q; ++ void *csum_start = NULL; ++- unsigned long interval; ++ __sum16 *csum = NULL; ++ struct ipv6hdr *ip6h; ++ struct mld_msg *mldq; ++@@ -922,9 +923,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(group) ? ++- br->multicast_query_response_interval : ++- br->multicast_last_member_interval; +++ if (delay) { +++ interval = ipv6_addr_any(group) ? +++ 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: ++@@ -932,7 +937,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 = *group; ++ csum = &mldq->mld_cksum; ++@@ -1022,7 +1027,7 @@ static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br, ++ &ip6_dst, &group->dst.ip6, ++ with_srcs, over_lmqt, ++ sflag, igmp_type, ++- need_rexmit); +++ need_rexmit, true); ++ } ++ #endif ++ } ++@@ -1427,6 +1432,168 @@ 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->key.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; +++ +++ rcu_read_lock(); +++ hlist_for_each_entry_rcu(fdb, &br->fdb_list, fdb_node) { +++ if (!fdb->dst || fdb->dst->dev != port->dev) +++ continue; +++ +++ /* Wake-up calls to VLANs unsupported for now */ +++ if (fdb->key.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 net_bridge_port_group *pg, ++@@ -1455,6 +1622,13 @@ static void __br_multicast_send_query(struct net_bridge *br, ++ 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 && +++ group->proto == htons(ETH_P_IPV6)) +++ br_multicast_schedule_wakeupcalls(br, port, +++ &group->dst.ip6); +++#endif +++ ++ if (over_lmqt && with_srcs && sflag) { ++ over_lmqt = false; ++ goto again_under_lmqt; ++@@ -3164,6 +3338,98 @@ 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 group = BR_MC_IN6_ZERO; +++ struct in6_addr ip6_dst; +++ 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 +++ */ +++ ipv6_addr_set(&ip6_dst, htonl(0xff020000), 0, 0, htonl(1)); +++ +++ skb = br_ip6_multicast_alloc_query(br, NULL, &ip6_dst, &group, false, +++ false, false, &igmp_type, NULL, +++ 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) ++@@ -3444,6 +3710,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 e8116a4438b9..b544889fb7fe 100644 ++--- a/net/bridge/br_netlink.c +++++ b/net/bridge/br_netlink.c ++@@ -151,6 +151,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 ++ + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_GROUP_FWD_MASK */ ++ + nla_total_size(sizeof(u8)) /* IFLA_BRPORT_MRP_RING_OPEN */ ++@@ -243,6 +246,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 ++ ++ /* we might be called only with br->lock */ ++ rcu_read_lock(); ++@@ -723,6 +731,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_VLAN_TUNNEL] = { .type = NLA_U8 }, ++@@ -872,6 +881,16 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) ++ } ++ #endif ++ +++#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 +++ ++ if (tb[IFLA_BRPORT_GROUP_FWD_MASK]) { ++ u16 fwd_mask = nla_get_u16(tb[IFLA_BRPORT_GROUP_FWD_MASK]); ++ ++diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h ++index 61b5b81b6828..6390d2fe88f6 100644 ++--- a/net/bridge/br_private.h +++++ b/net/bridge/br_private.h ++@@ -214,6 +214,10 @@ struct net_bridge_fdb_entry { ++ }; ++ struct rcu_head rcu; ++ }; +++ +++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS +++ struct timer_list wakeupcall_timer; +++#endif ++ }; ++ ++ #define MDB_PG_FLAGS_PERMANENT BIT(0) ++@@ -333,6 +337,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 ++@@ -1065,6 +1070,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 969eb6ca0776..88b74a5a7ccb 100644 ++--- a/net/bridge/br_sysfs_if.c +++++ b/net/bridge/br_sysfs_if.c ++@@ -254,6 +254,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, ++@@ -279,6 +294,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, ++diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c ++index ccb62f7d1054..3b269aafd3b5 100644 ++--- a/net/core/rtnetlink.c +++++ b/net/core/rtnetlink.c ++@@ -55,7 +55,7 @@ ++ #include ++ ++ #define RTNL_MAX_TYPE 50 ++-#define RTNL_SLAVE_MAX_TYPE 37 +++#define RTNL_SLAVE_MAX_TYPE 38 ++ ++ struct rtnl_link { ++ rtnl_doit_func doit; ++diff --git a/net/ipv6/mcast_snoop.c b/net/ipv6/mcast_snoop.c ++index 04d5fcdfa6e0..9a5061edbaf3 100644 ++--- a/net/ipv6/mcast_snoop.c +++++ b/net/ipv6/mcast_snoop.c ++@@ -131,7 +131,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); ++@@ -150,6 +150,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.36.1 ++