diff --git a/package/gluon-core/files/lib/gluon/upgrade/110-network b/package/gluon-core/files/lib/gluon/upgrade/110-network index d83f6d65..abe812a3 100755 --- a/package/gluon-core/files/lib/gluon/upgrade/110-network +++ b/package/gluon-core/files/lib/gluon/upgrade/110-network @@ -9,11 +9,12 @@ uci:section('network', 'interface', 'wan', { ifname = sysconfig.wan_ifname, type = 'bridge', - igmp_snooping = 0, peerdns = 0, auto = 1, } ) +uci:delete('network', 'wan', 'igmp_snooping') +uci:delete('network', 'wan', 'multicast_querier') if not uci:get('network', 'wan', 'proto') then uci:set('network', 'wan', 'proto', 'dhcp') @@ -56,5 +57,31 @@ uci:save('network') uci:commit('network') +uci:section('firewall', 'rule', 'wan_igmp', + { + name = 'Allow-IGMP', + src = 'wan', + proto = 'igmp', + family = 'ipv4', + target = 'ACCEPT', + } +) + +uci:section('firewall', 'rule', 'wan_mld', + { + name = 'Allow-MLD', + src = 'wan', + proto = 'icmp', + src_ip = 'fe80::/10', + icmp_type = { '130/0', '131/0', '143/0', }, + family = 'ipv6', + target = 'ACCEPT', + } +) + +uci:save('firewall') +uci:commit('firewall') + + sysctl.set('net.ipv6.conf.all.accept_ra', 0) sysctl.set('net.ipv6.conf.default.accept_ra', 0) diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/100-mcast-chain b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/100-mcast-chain index ec0013a3..ccbd5904 100644 --- a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/100-mcast-chain +++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/100-mcast-chain @@ -1 +1,2 @@ chain('MULTICAST_OUT', 'DROP') +chain('MULTICAST_IN', 'ACCEPT', 'nat') diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-icmpv6 b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-icmpv6 index a7b67414..338a6934 100644 --- a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-icmpv6 +++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-icmpv6 @@ -1,2 +1,4 @@ -rule 'MULTICAST_OUT -p IPv6 --ip6-protocol 0 -j RETURN' -- hop-by-hop +rule 'MULTICAST_OUT -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 130 -j DROP' -- MLD query +rule 'MULTICAST_OUT -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 131 -j DROP' -- MLDv1 report +rule 'MULTICAST_OUT -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 143 -j DROP' -- MLDv2 report rule 'MULTICAST_OUT -p IPv6 --ip6-protocol ipv6-icmp -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-igmp b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-igmp deleted file mode 100644 index 2d3814ae..00000000 --- a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-igmp +++ /dev/null @@ -1 +0,0 @@ -rule 'MULTICAST_OUT -p IPv4 --ip-protocol igmp -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-drop-igmp b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-drop-igmp new file mode 100644 index 00000000..a7d03920 --- /dev/null +++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-drop-igmp @@ -0,0 +1 @@ +rule('MULTICAST_IN -p IPv4 --ip-protocol igmp -j DROP', 'nat') diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-drop-mld b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-drop-mld new file mode 100644 index 00000000..197ec283 --- /dev/null +++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-drop-mld @@ -0,0 +1,3 @@ +rule('MULTICAST_IN -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 130 -j DROP', 'nat') -- MLD query +rule('MULTICAST_IN -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 131 -j DROP', 'nat') -- MLDv1 report +rule('MULTICAST_IN -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 143 -j DROP', 'nat') -- MLDv2 report diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/300-mcast b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/300-mcast index c52f122f..53944dc7 100644 --- a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/300-mcast +++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/300-mcast @@ -1,2 +1,4 @@ rule 'FORWARD --logical-out br-client -o bat0 -d Multicast -j MULTICAST_OUT' rule 'OUTPUT --logical-out br-client -o bat0 -d Multicast -j MULTICAST_OUT' + +rule('PREROUTING --logical-in br-client -i bat0 -d Multicast -j MULTICAST_IN', 'nat') diff --git a/package/gluon-ebtables/files/etc/init.d/gluon-ebtables b/package/gluon-ebtables/files/etc/init.d/gluon-ebtables index 5a770452..e6bffe96 100755 --- a/package/gluon-ebtables/files/etc/init.d/gluon-ebtables +++ b/package/gluon-ebtables/files/etc/init.d/gluon-ebtables @@ -24,12 +24,15 @@ exec_file() { local file="$1" /usr/bin/lua -e " - function rule(command) + function rule(command, table) + table = table or 'filter' os.execute($EBTABLES_RULE) end - function chain(name, policy) + function chain(name, policy, table) + table = table or 'filter' os.execute($EBTABLES_CHAIN) end + " "$file" } @@ -48,8 +51,8 @@ exec_all() { start() { ( - export EBTABLES_RULE='"ebtables -A " .. command' - export EBTABLES_CHAIN='"ebtables -N " .. name .. " -P " .. policy' + export EBTABLES_RULE='"ebtables -t " .. table .. " -A " .. command' + export EBTABLES_CHAIN='"ebtables -t " .. table .. " -N " .. name .. " -P " .. policy' if [ -z "$1" ]; then exec_all '' @@ -61,8 +64,8 @@ start() { stop() { ( - export EBTABLES_RULE='"ebtables -D " .. command' - export EBTABLES_CHAIN='"ebtables -X " .. name' + export EBTABLES_RULE='"ebtables -t " .. table .. " -D " .. command' + export EBTABLES_CHAIN='"ebtables -t " .. table .. " -X " .. name' if [ -z "$1" ]; then exec_all '-r' diff --git a/package/gluon-mesh-batman-adv-core/files/lib/gluon/upgrade/310-gluon-mesh-batman-adv-core-mesh b/package/gluon-mesh-batman-adv-core/files/lib/gluon/upgrade/310-gluon-mesh-batman-adv-core-mesh index fe576e33..48bcdc49 100755 --- a/package/gluon-mesh-batman-adv-core/files/lib/gluon/upgrade/310-gluon-mesh-batman-adv-core-mesh +++ b/package/gluon-mesh-batman-adv-core/files/lib/gluon/upgrade/310-gluon-mesh-batman-adv-core-mesh @@ -50,7 +50,7 @@ if type(ifname) == 'string' then end end -uci:set('network', 'client', 'igmp_snooping', 0) +uci:delete('network', 'client', 'igmp_snooping') uci:set('network', 'client', 'macaddr', sysconfig.primary_mac) uci:set('network', 'client', 'peerdns', 1) uci:set('network', 'client', 'sourcefilter', 0) @@ -61,6 +61,7 @@ uci:section('network', 'interface', 'bat0', ifname = 'bat0', proto = 'none', macaddr = sysconfig.primary_mac, + multicast_router = 2, } ) diff --git a/patches/openwrt/0025-netifd-bridge-related-fixes-config-options.patch b/patches/openwrt/0025-netifd-bridge-related-fixes-config-options.patch new file mode 100644 index 00000000..33df12ef --- /dev/null +++ b/patches/openwrt/0025-netifd-bridge-related-fixes-config-options.patch @@ -0,0 +1,549 @@ +From: Linus Lüssing +Date: Sat, 5 Sep 2015 02:22:27 +0200 +Subject: netifd: bridge related fixes + config options + +This commit backports upstream patches in netifd / OpenWRT trunk, +regarding bridges: + +* bridge: Fix multicast_to_unicast feature by hairpin+isolate +* bridge: Allow setting multicast_to_unicast option +* bridge: Allow setting multicast_router option + +Signed-off-by: Linus Lüssing + +diff --git a/package/network/config/netifd/patches/0001-bridge-Fix-multicast_to_unicast-feature-by-hairpin-i.patch b/package/network/config/netifd/patches/0001-bridge-Fix-multicast_to_unicast-feature-by-hairpin-i.patch +new file mode 100644 +index 0000000..3e4cbc5 +--- /dev/null ++++ b/package/network/config/netifd/patches/0001-bridge-Fix-multicast_to_unicast-feature-by-hairpin-i.patch +@@ -0,0 +1,201 @@ ++From 484614cf607e2a1220e39111468c525d3ae96497 Mon Sep 17 00:00:00 2001 ++From: =?UTF-8?q?Linus=20L=C3=BCssing?= ++Date: Sun, 23 Aug 2015 17:19:26 +0200 ++Subject: [PATCH 1/3] bridge: Fix multicast_to_unicast feature by ++ hairpin+isolate ++MIME-Version: 1.0 ++Content-Type: text/plain; charset=UTF-8 ++Content-Transfer-Encoding: 8bit ++ ++All IGMP and MLD versions suffer from a specific limitation (from a ++snooping switch perspective): Report suppression. ++ ++Once a listener hears an IGMPv2/3 or MLDv1 report for the same group ++itself participates in then it might (if this listener is an IGMPv3 or ++MLDv2 listener) or will (if this is an IGMPv1/2 or MLDv1 listener) ++refrain from sending its own report. ++ ++Therefore we might currently miss such surpressing listeners as they ++won't receive the multicast packet with the mangled, unicasted ++destination. ++ ++Fixing this by first isolating the STAs and giving the bridge more ++control over traffic forwarding. E.g. refraining to forward listener ++reports to other STAs. ++ ++For broadcast and unicast traffic to an STA on the same AP, the hairpin ++feature of the bridge will reflect such traffic back to the AP ++interface. However, if the AP interface is actually configured to ++isolate STAs, then hairpin is kept disabled. ++ ++Signed-off-by: Linus Lüssing ++--- ++ device.h | 1 + ++ scripts/netifd-wireless.sh | 15 +++++++++++++++ ++ system-linux.c | 26 ++++++++++++++++++++------ ++ wireless.c | 12 +++++++++++- ++ wireless.h | 1 + ++ 5 files changed, 48 insertions(+), 7 deletions(-) ++ ++diff --git a/device.h b/device.h ++index 3001f10..47ef77b 100644 ++--- a/device.h +++++ b/device.h ++@@ -167,6 +167,7 @@ struct device { ++ bool iface_config; ++ bool default_config; ++ bool wireless; +++ bool wireless_isolate; ++ ++ struct interface *config_iface; ++ ++diff --git a/scripts/netifd-wireless.sh b/scripts/netifd-wireless.sh ++index c5d8a96..f981f1b 100644 ++--- a/scripts/netifd-wireless.sh +++++ b/scripts/netifd-wireless.sh ++@@ -248,12 +248,26 @@ wireless_vif_parse_encryption() { ++ esac ++ } ++ +++_wireless_set_brsnoop_isolation() { +++ local multicast_to_unicast="$1" +++ local isolate +++ +++ json_get_var isolate isolate +++ +++ [ $isolate -gt 0 -o -z "$network_bridge" ] && return +++ +++ [ -z "$multicast_to_unicast" ] && multicast_to_unicast=1 +++ [ $multicast_to_unicast -gt 0 ] && json_add_boolean isolate 1 +++} +++ ++ for_each_interface() { ++ local _w_types="$1"; shift ++ local _w_ifaces _w_iface ++ local _w_type ++ local _w_found ++ +++ local multicast_to_unicast +++ ++ json_get_keys _w_ifaces interfaces ++ json_select interfaces ++ for _w_iface in $_w_ifaces; do ++@@ -261,6 +275,7 @@ for_each_interface() { ++ if [ -n "$_w_types" ]; then ++ json_get_var network_bridge bridge ++ json_select config +++ _wireless_set_brsnoop_isolation "$multicast_to_unicast" ++ json_get_var _w_type mode ++ json_select .. ++ _w_types=" $_w_types " ++diff --git a/system-linux.c b/system-linux.c ++index 6dc9acd..ff25259 100644 ++--- a/system-linux.c +++++ b/system-linux.c ++@@ -310,6 +310,16 @@ static void system_set_neigh6reachabletime(struct device *dev, const char *val) ++ system_set_dev_sysctl("/proc/sys/net/ipv6/neigh/%s/base_reachable_time_ms", dev->ifname, val); ++ } ++ +++static void system_bridge_set_multicast_to_unicast(struct device *dev, const char *val) +++{ +++ system_set_dev_sysctl("/sys/class/net/%s/brport/multicast_to_unicast", dev->ifname, val); +++} +++ +++static void system_bridge_set_hairpin_mode(struct device *dev, const char *val) +++{ +++ system_set_dev_sysctl("/sys/class/net/%s/brport/hairpin_mode", dev->ifname, val); +++} +++ ++ static int system_get_sysctl(const char *path, char *buf, const size_t buf_sz) ++ { ++ int fd = -1, ret = -1; ++@@ -545,12 +555,16 @@ static char *system_get_bridge(const char *name, char *buf, int buflen) ++ return path + 1; ++ } ++ ++-static void system_bridge_set_wireless(const char *bridge, const char *dev) +++static void +++system_bridge_set_wireless(struct device *dev) ++ { ++- snprintf(dev_buf, sizeof(dev_buf), ++- "/sys/devices/virtual/net/%s/brif/%s/multicast_to_unicast", ++- bridge, dev); ++- system_set_sysctl(dev_buf, "1"); +++ bool hairpin = true; +++ +++ if (dev->wireless_isolate) +++ hairpin = false; +++ +++ system_bridge_set_multicast_to_unicast(dev, "1"); +++ system_bridge_set_hairpin_mode(dev, hairpin ? "1" : "0"); ++ } ++ ++ int system_bridge_addif(struct device *bridge, struct device *dev) ++@@ -563,7 +577,7 @@ int system_bridge_addif(struct device *bridge, struct device *dev) ++ ret = system_bridge_if(bridge->ifname, dev, SIOCBRADDIF, NULL); ++ ++ if (dev->wireless) ++- system_bridge_set_wireless(bridge->ifname, dev->ifname); +++ system_bridge_set_wireless(dev); ++ ++ return ret; ++ } ++diff --git a/wireless.c b/wireless.c ++index fbd6191..337f563 100644 ++--- a/wireless.c +++++ b/wireless.c ++@@ -35,12 +35,14 @@ static const struct uci_blob_param_list wdev_param = { ++ enum { ++ VIF_ATTR_DISABLED, ++ VIF_ATTR_NETWORK, +++ VIF_ATTR_ISOLATE, ++ __VIF_ATTR_MAX, ++ }; ++ ++ static const struct blobmsg_policy vif_policy[__VIF_ATTR_MAX] = { ++ [VIF_ATTR_DISABLED] = { .name = "disabled", .type = BLOBMSG_TYPE_BOOL }, ++ [VIF_ATTR_NETWORK] = { .name = "network", .type = BLOBMSG_TYPE_ARRAY }, +++ [VIF_ATTR_ISOLATE] = { .name = "isolate", .type = BLOBMSG_TYPE_BOOL }, ++ }; ++ ++ static const struct uci_blob_param_list vif_param = { ++@@ -204,8 +206,10 @@ static void wireless_interface_handle_link(struct wireless_interface *vif, bool ++ ++ if (up) { ++ struct device *dev = device_get(vif->ifname, 2); ++- if (dev) +++ if (dev) { +++ dev->wireless_isolate = vif->isolate; ++ dev->wireless = true; +++ } ++ } ++ ++ blobmsg_for_each_attr(cur, vif->network, rem) { ++@@ -700,6 +704,12 @@ void wireless_interface_create(struct wireless_device *wdev, struct blob_attr *d ++ vif->wdev = wdev; ++ vif->config = data; ++ vif->section = section; +++ vif->isolate = false; +++ +++ cur = tb[VIF_ATTR_ISOLATE]; +++ if (cur && blobmsg_get_bool(cur)) +++ vif->isolate = blobmsg_get_bool(cur); +++ ++ vlist_add(&wdev->interfaces, &vif->node, vif->name); ++ } ++ ++diff --git a/wireless.h b/wireless.h ++index c5dbb88..476c63e 100644 ++--- a/wireless.h +++++ b/wireless.h ++@@ -77,6 +77,7 @@ struct wireless_interface { ++ ++ const char *ifname; ++ struct blob_attr *network; +++ bool isolate; ++ }; ++ ++ struct wireless_process { ++-- ++1.7.10.4 ++ +diff --git a/package/network/config/netifd/patches/0002-bridge-Allow-setting-multicast_to_unicast-option.patch b/package/network/config/netifd/patches/0002-bridge-Allow-setting-multicast_to_unicast-option.patch +new file mode 100644 +index 0000000..d199a97 +--- /dev/null ++++ b/package/network/config/netifd/patches/0002-bridge-Allow-setting-multicast_to_unicast-option.patch +@@ -0,0 +1,159 @@ ++From 2adcda7967a652547202f18fadc2efca0f290625 Mon Sep 17 00:00:00 2001 ++From: =?UTF-8?q?Linus=20L=C3=BCssing?= ++Date: Sun, 23 Aug 2015 17:19:27 +0200 ++Subject: [PATCH 2/3] bridge: Allow setting multicast_to_unicast option ++MIME-Version: 1.0 ++Content-Type: text/plain; charset=UTF-8 ++Content-Transfer-Encoding: 8bit ++ ++With this patch the multicast_to_unicast feature can be disabled for all ++wireless interfaces via an according option on the uci bridge interface. ++ ++This patch also exports the setting information to wireless handler ++scripts. The hostapd script will need that information to determine ++whether to enable or disable ap-isolation, for instance. ++ ++Signed-off-by: Linus Lüssing ++--- ++ device.c | 9 +++++++++ ++ device.h | 3 +++ ++ scripts/netifd-wireless.sh | 1 + ++ system-linux.c | 13 +++++++++---- ++ wireless.c | 4 ++++ ++ 5 files changed, 26 insertions(+), 4 deletions(-) ++ ++diff --git a/device.c b/device.c ++index dd2823d..776829d 100644 ++--- a/device.c +++++ b/device.c ++@@ -46,6 +46,7 @@ static const struct blobmsg_policy dev_attrs[__DEV_ATTR_MAX] = { ++ [DEV_ATTR_NEIGHREACHABLETIME] = { .name = "neighreachabletime", .type = BLOBMSG_TYPE_INT32 }, ++ [DEV_ATTR_RPS] = { .name = "rps", .type = BLOBMSG_TYPE_BOOL }, ++ [DEV_ATTR_XPS] = { .name = "xps", .type = BLOBMSG_TYPE_BOOL }, +++ [DEV_ATTR_MULTICAST_TO_UNICAST] = { .name = "multicast_to_unicast", .type = BLOBMSG_TYPE_BOOL }, ++ }; ++ ++ const struct uci_blob_param_list device_attr_list = { ++@@ -169,6 +170,7 @@ device_merge_settings(struct device *dev, struct device_settings *n) ++ s->neigh4reachabletime : os->neigh4reachabletime; ++ n->neigh6reachabletime = s->flags & DEV_OPT_NEIGHREACHABLETIME ? ++ s->neigh6reachabletime : os->neigh6reachabletime; +++ n->multicast_to_unicast = s->multicast_to_unicast; ++ n->flags = s->flags | os->flags; ++ } ++ ++@@ -259,6 +261,11 @@ device_init_settings(struct device *dev, struct blob_attr **tb) ++ else ++ s->xps = default_ps; ++ +++ if ((cur = tb[DEV_ATTR_MULTICAST_TO_UNICAST])) { +++ s->multicast_to_unicast = blobmsg_get_bool(cur); +++ s->flags |= DEV_OPT_MULTICAST_TO_UNICAST; +++ } +++ ++ device_set_disabled(dev, disabled); ++ } ++ ++@@ -863,6 +870,8 @@ device_dump_status(struct blob_buf *b, struct device *dev) ++ blobmsg_add_u32(b, "neigh4reachabletime", st.neigh4reachabletime); ++ blobmsg_add_u32(b, "neigh6reachabletime", st.neigh6reachabletime); ++ } +++ if (st.flags & DEV_OPT_MULTICAST_TO_UNICAST) +++ blobmsg_add_u8(b, "multicast_to_unicast", st.multicast_to_unicast); ++ } ++ ++ s = blobmsg_open_table(b, "statistics"); ++diff --git a/device.h b/device.h ++index 47ef77b..55ef1cf 100644 ++--- a/device.h +++++ b/device.h ++@@ -40,6 +40,7 @@ enum { ++ DEV_ATTR_NEIGHREACHABLETIME, ++ DEV_ATTR_RPS, ++ DEV_ATTR_XPS, +++ DEV_ATTR_MULTICAST_TO_UNICAST, ++ __DEV_ATTR_MAX, ++ }; ++ ++@@ -80,6 +81,7 @@ enum { ++ DEV_OPT_NEIGHREACHABLETIME = (1 << 9), ++ DEV_OPT_RPS = (1 << 10), ++ DEV_OPT_XPS = (1 << 11), +++ DEV_OPT_MULTICAST_TO_UNICAST = (1 << 12), ++ }; ++ ++ /* events broadcasted to all users of a device */ ++@@ -135,6 +137,7 @@ struct device_settings { ++ unsigned int neigh6reachabletime; ++ bool rps; ++ bool xps; +++ bool multicast_to_unicast; ++ }; ++ ++ /* ++diff --git a/scripts/netifd-wireless.sh b/scripts/netifd-wireless.sh ++index f981f1b..83a8223 100644 ++--- a/scripts/netifd-wireless.sh +++++ b/scripts/netifd-wireless.sh ++@@ -274,6 +274,7 @@ for_each_interface() { ++ json_select "$_w_iface" ++ if [ -n "$_w_types" ]; then ++ json_get_var network_bridge bridge +++ json_get_var multicast_to_unicast multicast_to_unicast ++ json_select config ++ _wireless_set_brsnoop_isolation "$multicast_to_unicast" ++ json_get_var _w_type mode ++diff --git a/system-linux.c b/system-linux.c ++index ff25259..549d9c0 100644 ++--- a/system-linux.c +++++ b/system-linux.c ++@@ -556,14 +556,19 @@ static char *system_get_bridge(const char *name, char *buf, int buflen) ++ } ++ ++ static void ++-system_bridge_set_wireless(struct device *dev) +++system_bridge_set_wireless(struct device *bridge, struct device *dev) ++ { +++ bool mcast_to_ucast = true; ++ bool hairpin = true; ++ ++- if (dev->wireless_isolate) +++ if (bridge->settings.flags & DEV_OPT_MULTICAST_TO_UNICAST && +++ !bridge->settings.multicast_to_unicast) +++ mcast_to_ucast = false; +++ +++ if (!mcast_to_ucast || dev->wireless_isolate) ++ hairpin = false; ++ ++- system_bridge_set_multicast_to_unicast(dev, "1"); +++ system_bridge_set_multicast_to_unicast(dev, mcast_to_ucast ? "1" : "0"); ++ system_bridge_set_hairpin_mode(dev, hairpin ? "1" : "0"); ++ } ++ ++@@ -577,7 +582,7 @@ int system_bridge_addif(struct device *bridge, struct device *dev) ++ ret = system_bridge_if(bridge->ifname, dev, SIOCBRADDIF, NULL); ++ ++ if (dev->wireless) ++- system_bridge_set_wireless(dev); +++ system_bridge_set_wireless(bridge, dev); ++ ++ return ret; ++ } ++diff --git a/wireless.c b/wireless.c ++index 337f563..d0d2942 100644 ++--- a/wireless.c +++++ b/wireless.c ++@@ -92,6 +92,10 @@ vif_config_add_bridge(struct blob_buf *buf, struct blob_attr *networks, bool pre ++ dev->hotplug_ops->prepare(dev); ++ ++ blobmsg_add_string(buf, "bridge", dev->ifname); +++ +++ if (dev->settings.flags & DEV_OPT_MULTICAST_TO_UNICAST) +++ blobmsg_add_u8(buf, "multicast_to_unicast", +++ dev->settings.multicast_to_unicast); ++ } ++ ++ static void ++-- ++1.7.10.4 ++ +diff --git a/package/network/config/netifd/patches/0003-bridge-Allow-setting-multicast_router-option.patch b/package/network/config/netifd/patches/0003-bridge-Allow-setting-multicast_router-option.patch +new file mode 100644 +index 0000000..e4b3b9c +--- /dev/null ++++ b/package/network/config/netifd/patches/0003-bridge-Allow-setting-multicast_router-option.patch +@@ -0,0 +1,158 @@ ++From 4c467dffae6b9f540cc2242a5e872e49807f310d Mon Sep 17 00:00:00 2001 ++From: =?UTF-8?q?Linus=20L=C3=BCssing?= ++Date: Sun, 23 Aug 2015 17:19:28 +0200 ++Subject: [PATCH 3/3] bridge: Allow setting multicast_router option ++MIME-Version: 1.0 ++Content-Type: text/plain; charset=UTF-8 ++Content-Transfer-Encoding: 8bit ++ ++The multicast_router option of a bridge allows to control the forwarding ++behaviour of multicast packets independant of the listener state: ++ ++* 0: Only forward if specific listener is present ++* 1 (default): Forward if specific listener or a multicast router ++ was detected (currently only learned via query messages, no MRD ++ support yet) ++* 2: Always forward any multicast traffic on this port ++ ++Since MRD is not mandated you might end up with silent multicast routers ++(e.g. if your link has more than one multicast router; only one can ++become the selected, "noisy" querier). Here you might need a manual ++configuration option like the "multicast_router" option. ++ ++Other scenarios where this can be useful are for instance: ++* Segmentation of IGMP/MLD domains together with ebtables ++* Dedicated bridge port for monitoring/debugging purposes ++ ++Signed-off-by: Linus Lüssing ++--- ++ device.c | 12 ++++++++++++ ++ device.h | 3 +++ ++ system-linux.c | 18 ++++++++++++++++++ ++ 3 files changed, 33 insertions(+) ++ ++diff --git a/device.c b/device.c ++index 776829d..75edc07 100644 ++--- a/device.c +++++ b/device.c ++@@ -47,6 +47,7 @@ static const struct blobmsg_policy dev_attrs[__DEV_ATTR_MAX] = { ++ [DEV_ATTR_RPS] = { .name = "rps", .type = BLOBMSG_TYPE_BOOL }, ++ [DEV_ATTR_XPS] = { .name = "xps", .type = BLOBMSG_TYPE_BOOL }, ++ [DEV_ATTR_MULTICAST_TO_UNICAST] = { .name = "multicast_to_unicast", .type = BLOBMSG_TYPE_BOOL }, +++ [DEV_ATTR_MULTICAST_ROUTER] = { .name = "multicast_router", .type = BLOBMSG_TYPE_INT32 }, ++ }; ++ ++ const struct uci_blob_param_list device_attr_list = { ++@@ -171,6 +172,7 @@ device_merge_settings(struct device *dev, struct device_settings *n) ++ n->neigh6reachabletime = s->flags & DEV_OPT_NEIGHREACHABLETIME ? ++ s->neigh6reachabletime : os->neigh6reachabletime; ++ n->multicast_to_unicast = s->multicast_to_unicast; +++ n->multicast_router = s->multicast_router; ++ n->flags = s->flags | os->flags; ++ } ++ ++@@ -266,6 +268,14 @@ device_init_settings(struct device *dev, struct blob_attr **tb) ++ s->flags |= DEV_OPT_MULTICAST_TO_UNICAST; ++ } ++ +++ if ((cur = tb[DEV_ATTR_MULTICAST_ROUTER])) { +++ s->multicast_router = blobmsg_get_u32(cur); +++ if (s->multicast_router <= 2) +++ s->flags |= DEV_OPT_MULTICAST_ROUTER; +++ else +++ DPRINTF("Invalid value: %d - (Use 0: never, 1: learn, 2: always)\n", blobmsg_get_u32(cur)); +++ } +++ ++ device_set_disabled(dev, disabled); ++ } ++ ++@@ -872,6 +882,8 @@ device_dump_status(struct blob_buf *b, struct device *dev) ++ } ++ 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_ROUTER) +++ blobmsg_add_u32(b, "multicast_router", st.multicast_router); ++ } ++ ++ s = blobmsg_open_table(b, "statistics"); ++diff --git a/device.h b/device.h ++index 55ef1cf..a487466 100644 ++--- a/device.h +++++ b/device.h ++@@ -41,6 +41,7 @@ enum { ++ DEV_ATTR_RPS, ++ DEV_ATTR_XPS, ++ DEV_ATTR_MULTICAST_TO_UNICAST, +++ DEV_ATTR_MULTICAST_ROUTER, ++ __DEV_ATTR_MAX, ++ }; ++ ++@@ -82,6 +83,7 @@ enum { ++ DEV_OPT_RPS = (1 << 10), ++ DEV_OPT_XPS = (1 << 11), ++ DEV_OPT_MULTICAST_TO_UNICAST = (1 << 12), +++ DEV_OPT_MULTICAST_ROUTER = (1 << 13), ++ }; ++ ++ /* events broadcasted to all users of a device */ ++@@ -138,6 +140,7 @@ struct device_settings { ++ bool rps; ++ bool xps; ++ bool multicast_to_unicast; +++ unsigned int multicast_router; ++ }; ++ ++ /* ++diff --git a/system-linux.c b/system-linux.c ++index 549d9c0..9061f25 100644 ++--- a/system-linux.c +++++ b/system-linux.c ++@@ -320,6 +320,13 @@ static void system_bridge_set_hairpin_mode(struct device *dev, const char *val) ++ system_set_dev_sysctl("/sys/class/net/%s/brport/hairpin_mode", dev->ifname, val); ++ } ++ +++static void system_bridge_set_multicast_router(struct device *dev, const char *val, bool bridge) +++{ +++ system_set_dev_sysctl(bridge ? "/sys/class/net/%s/bridge/multicast_router" : +++ "/sys/class/net/%s/brport/multicast_router", +++ dev->ifname, val); +++} +++ ++ static int system_get_sysctl(const char *path, char *buf, const size_t buf_sz) ++ { ++ int fd = -1, ret = -1; ++@@ -574,6 +581,7 @@ system_bridge_set_wireless(struct device *bridge, struct device *dev) ++ ++ int system_bridge_addif(struct device *bridge, struct device *dev) ++ { +++ char buf[64]; ++ char *oldbr; ++ int ret = 0; ++ ++@@ -584,6 +592,11 @@ int system_bridge_addif(struct device *bridge, struct device *dev) ++ if (dev->wireless) ++ system_bridge_set_wireless(bridge, dev); ++ +++ if (dev->settings.flags & DEV_OPT_MULTICAST_ROUTER) { +++ snprintf(buf, sizeof(buf), "%i", dev->settings.multicast_router); +++ system_bridge_set_multicast_router(dev, buf, false); +++ } +++ ++ return ret; ++ } ++ ++@@ -834,6 +847,11 @@ int system_bridge_addbr(struct device *bridge, struct bridge_config *cfg) ++ system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/hash_max", ++ bridge->ifname, buf); ++ +++ if (bridge->settings.flags & DEV_OPT_MULTICAST_ROUTER) { +++ snprintf(buf, sizeof(buf), "%i", bridge->settings.multicast_router); +++ system_bridge_set_multicast_router(bridge, buf, true); +++ } +++ ++ args[0] = BRCTL_SET_BRIDGE_PRIORITY; ++ args[1] = cfg->priority; ++ system_bridge_if(bridge->ifname, NULL, SIOCDEVPRIVATE, &args); ++-- ++1.7.10.4 ++ diff --git a/patches/openwrt/0026-Revert-kernel-disable-multicast-to-unicast-translation-for-ipv6-neighbor-solicitation-17625.patch b/patches/openwrt/0026-Revert-kernel-disable-multicast-to-unicast-translation-for-ipv6-neighbor-solicitation-17625.patch new file mode 100644 index 00000000..58c60a60 --- /dev/null +++ b/patches/openwrt/0026-Revert-kernel-disable-multicast-to-unicast-translation-for-ipv6-neighbor-solicitation-17625.patch @@ -0,0 +1,181 @@ +From: Linus Lüssing +Date: Sat, 5 Sep 2015 02:57:34 +0200 +Subject: Revert "kernel: disable multicast-to-unicast translation for ipv6 neighbor solicitation (#17625)" + +This reverts commit a080e8e1943156168913d0353a2e99d1151102aa. + +It did not fix the problem but just hid some symptom. The real issue was +that IGMP/MLD report suppression was not considered for the +multicast-to-unicast feature. A recent netifd which isolates IGMP/MLD +reports between STAs by utilizing AP-isolation and bridge-hairpinning +should have fixed this. + +It is perfectly fine to apply multicast-to-unicast to IPv6 Neighbor +Solicitations, too (once that feature is configured correctly). + +Signed-off-by: Linus Lüssing +Signed-off-by: Felix Fietkau + +git-svn-id: svn://svn.openwrt.org/openwrt/trunk@46720 3c298f89-4303-0410-b956-a3cf2f4a3e73 + +diff --git a/target/linux/generic/patches-3.18/645-bridge_multicast_to_unicast.patch b/target/linux/generic/patches-3.18/645-bridge_multicast_to_unicast.patch +index 00ad14f..8c436e0 100644 +--- a/target/linux/generic/patches-3.18/645-bridge_multicast_to_unicast.patch ++++ b/target/linux/generic/patches-3.18/645-bridge_multicast_to_unicast.patch +@@ -87,19 +87,16 @@ + { + struct br_ip br_group; + +-@@ -736,7 +758,10 @@ static int br_ip6_multicast_add_group(st ++@@ -736,7 +758,7 @@ static int br_ip6_multicast_add_group(st + br_group.proto = htons(ETH_P_IPV6); + br_group.vid = vid; + + - return br_multicast_add_group(br, port, &br_group); +-+ if (ipv6_addr_is_solict_mult(group)) +-+ src = NULL; +-+ + + return br_multicast_add_group(br, port, &br_group, src); + } + #endif + +-@@ -965,6 +990,7 @@ static int br_ip4_multicast_igmp3_report ++@@ -965,6 +987,7 @@ static int br_ip4_multicast_igmp3_report + struct sk_buff *skb, + u16 vid) + { +@@ -107,7 +104,7 @@ + struct igmpv3_report *ih; + struct igmpv3_grec *grec; + int i; +-@@ -1008,7 +1034,7 @@ static int br_ip4_multicast_igmp3_report ++@@ -1008,7 +1031,7 @@ static int br_ip4_multicast_igmp3_report + continue; + } + +@@ -116,7 +113,7 @@ + if (err) + break; + } +-@@ -1022,6 +1048,7 @@ static int br_ip6_multicast_mld2_report( ++@@ -1022,6 +1045,7 @@ static int br_ip6_multicast_mld2_report( + struct sk_buff *skb, + u16 vid) + { +@@ -124,7 +121,7 @@ + struct icmp6hdr *icmp6h; + struct mld2_grec *grec; + int i; +-@@ -1070,7 +1097,7 @@ static int br_ip6_multicast_mld2_report( ++@@ -1070,7 +1094,7 @@ static int br_ip6_multicast_mld2_report( + } + + err = br_ip6_multicast_add_group(br, port, &grec->grec_mca, +@@ -133,7 +130,7 @@ + if (err) + break; + } +-@@ -1406,7 +1433,8 @@ br_multicast_leave_group(struct net_brid ++@@ -1406,7 +1430,8 @@ br_multicast_leave_group(struct net_brid + struct net_bridge_port *port, + struct br_ip *group, + struct bridge_mcast_other_query *other_query, +@@ -143,7 +140,7 @@ + { + struct net_bridge_mdb_htable *mdb; + struct net_bridge_mdb_entry *mp; +-@@ -1456,7 +1484,7 @@ br_multicast_leave_group(struct net_brid ++@@ -1456,7 +1481,7 @@ br_multicast_leave_group(struct net_brid + for (pp = &mp->ports; + (p = mlock_dereference(*pp, br)) != NULL; + pp = &p->next) { +@@ -152,7 +149,7 @@ + continue; + + rcu_assign_pointer(*pp, p->next); +-@@ -1490,7 +1518,7 @@ br_multicast_leave_group(struct net_brid ++@@ -1490,7 +1515,7 @@ br_multicast_leave_group(struct net_brid + for (p = mlock_dereference(mp->ports, br); + p != NULL; + p = mlock_dereference(p->next, br)) { +@@ -161,7 +158,7 @@ + continue; + + if (!hlist_unhashed(&p->mglist) && +-@@ -1508,8 +1536,8 @@ out: ++@@ -1508,8 +1533,8 @@ out: + + static void br_ip4_multicast_leave_group(struct net_bridge *br, + struct net_bridge_port *port, +@@ -172,7 +169,7 @@ + { + struct br_ip br_group; + struct bridge_mcast_own_query *own_query; +-@@ -1524,14 +1552,14 @@ static void br_ip4_multicast_leave_group ++@@ -1524,14 +1549,14 @@ static void br_ip4_multicast_leave_group + br_group.vid = vid; + + br_multicast_leave_group(br, port, &br_group, &br->ip4_other_query, +@@ -189,7 +186,7 @@ + { + struct br_ip br_group; + struct bridge_mcast_own_query *own_query; +-@@ -1546,7 +1574,7 @@ static void br_ip6_multicast_leave_group ++@@ -1546,7 +1571,7 @@ static void br_ip6_multicast_leave_group + br_group.vid = vid; + + br_multicast_leave_group(br, port, &br_group, &br->ip6_other_query, +@@ -198,7 +195,7 @@ + } + #endif + +-@@ -1555,6 +1583,7 @@ static int br_multicast_ipv4_rcv(struct ++@@ -1555,6 +1580,7 @@ static int br_multicast_ipv4_rcv(struct + struct sk_buff *skb, + u16 vid) + { +@@ -206,7 +203,7 @@ + struct sk_buff *skb2 = skb; + const struct iphdr *iph; + struct igmphdr *ih; +-@@ -1628,7 +1657,7 @@ static int br_multicast_ipv4_rcv(struct ++@@ -1628,7 +1654,7 @@ static int br_multicast_ipv4_rcv(struct + case IGMP_HOST_MEMBERSHIP_REPORT: + case IGMPV2_HOST_MEMBERSHIP_REPORT: + BR_INPUT_SKB_CB(skb)->mrouters_only = 1; +@@ -215,7 +212,7 @@ + break; + case IGMPV3_HOST_MEMBERSHIP_REPORT: + err = br_ip4_multicast_igmp3_report(br, port, skb2, vid); +-@@ -1637,7 +1666,7 @@ static int br_multicast_ipv4_rcv(struct ++@@ -1637,7 +1663,7 @@ static int br_multicast_ipv4_rcv(struct + err = br_ip4_multicast_query(br, port, skb2, vid); + break; + case IGMP_HOST_LEAVE_MESSAGE: +@@ -224,7 +221,7 @@ + break; + } + +-@@ -1655,6 +1684,7 @@ static int br_multicast_ipv6_rcv(struct ++@@ -1655,6 +1681,7 @@ static int br_multicast_ipv6_rcv(struct + struct sk_buff *skb, + u16 vid) + { +@@ -232,7 +229,7 @@ + struct sk_buff *skb2; + const struct ipv6hdr *ip6h; + u8 icmp6_type; +-@@ -1764,7 +1794,8 @@ static int br_multicast_ipv6_rcv(struct ++@@ -1764,7 +1791,8 @@ static int br_multicast_ipv6_rcv(struct + } + mld = (struct mld_msg *)skb_transport_header(skb2); + BR_INPUT_SKB_CB(skb)->mrouters_only = 1; +@@ -242,7 +239,7 @@ + break; + } + case ICMPV6_MLD2_REPORT: +-@@ -1781,7 +1812,7 @@ static int br_multicast_ipv6_rcv(struct ++@@ -1781,7 +1809,7 @@ static int br_multicast_ipv6_rcv(struct + goto out; + } + mld = (struct mld_msg *)skb_transport_header(skb2); diff --git a/patches/openwrt/0027-kernel-bridge-multicast-to-unicast-assign-src-after-pskb_may_pull.patch b/patches/openwrt/0027-kernel-bridge-multicast-to-unicast-assign-src-after-pskb_may_pull.patch new file mode 100644 index 00000000..dfe5e4e6 --- /dev/null +++ b/patches/openwrt/0027-kernel-bridge-multicast-to-unicast-assign-src-after-pskb_may_pull.patch @@ -0,0 +1,173 @@ +From: Linus Lüssing +Date: Sat, 5 Sep 2015 03:11:34 +0200 +Subject: kernel: bridge, multicast-to-unicast: assign src after pskb_may_pull() + +A call to pskb_may_pull() might reallocate skb->data. Therefore we +should only assign the src-pointer after any potential reallocations. + +Signed-off-by: Linus Lüssing +Signed-off-by: Felix Fietkau + +git-svn-id: svn://svn.openwrt.org/openwrt/trunk@46721 3c298f89-4303-0410-b956-a3cf2f4a3e73 + +diff --git a/target/linux/generic/patches-3.18/645-bridge_multicast_to_unicast.patch b/target/linux/generic/patches-3.18/645-bridge_multicast_to_unicast.patch +index 8c436e0..6707b03 100644 +--- a/target/linux/generic/patches-3.18/645-bridge_multicast_to_unicast.patch ++++ b/target/linux/generic/patches-3.18/645-bridge_multicast_to_unicast.patch +@@ -100,37 +100,40 @@ + struct sk_buff *skb, + u16 vid) + { +-+ const unsigned char *src = eth_hdr(skb)->h_source; +++ const unsigned char *src; + struct igmpv3_report *ih; + struct igmpv3_grec *grec; + int i; +-@@ -1008,7 +1031,7 @@ static int br_ip4_multicast_igmp3_report ++@@ -1008,7 +1031,8 @@ static int br_ip4_multicast_igmp3_report + continue; + } + + - err = br_ip4_multicast_add_group(br, port, group, vid); +++ src = eth_hdr(skb)->h_source; + + err = br_ip4_multicast_add_group(br, port, group, vid, src); + if (err) + break; + } +-@@ -1022,6 +1045,7 @@ static int br_ip6_multicast_mld2_report( ++@@ -1022,6 +1046,7 @@ static int br_ip6_multicast_mld2_report( + struct sk_buff *skb, + u16 vid) + { +-+ const unsigned char *src = eth_hdr(skb)->h_source; +++ const unsigned char *src; + struct icmp6hdr *icmp6h; + struct mld2_grec *grec; + int i; +-@@ -1070,7 +1094,7 @@ static int br_ip6_multicast_mld2_report( ++@@ -1069,8 +1094,9 @@ static int br_ip6_multicast_mld2_report( ++ continue; + } + +++ src = eth_hdr(skb)->h_source; + err = br_ip6_multicast_add_group(br, port, &grec->grec_mca, + - vid); + + vid, src); + if (err) + break; + } +-@@ -1406,7 +1430,8 @@ br_multicast_leave_group(struct net_brid ++@@ -1406,7 +1432,8 @@ br_multicast_leave_group(struct net_brid + struct net_bridge_port *port, + struct br_ip *group, + struct bridge_mcast_other_query *other_query, +@@ -140,7 +143,7 @@ + { + struct net_bridge_mdb_htable *mdb; + struct net_bridge_mdb_entry *mp; +-@@ -1456,7 +1481,7 @@ br_multicast_leave_group(struct net_brid ++@@ -1456,7 +1483,7 @@ br_multicast_leave_group(struct net_brid + for (pp = &mp->ports; + (p = mlock_dereference(*pp, br)) != NULL; + pp = &p->next) { +@@ -149,7 +152,7 @@ + continue; + + rcu_assign_pointer(*pp, p->next); +-@@ -1490,7 +1515,7 @@ br_multicast_leave_group(struct net_brid ++@@ -1490,7 +1517,7 @@ br_multicast_leave_group(struct net_brid + for (p = mlock_dereference(mp->ports, br); + p != NULL; + p = mlock_dereference(p->next, br)) { +@@ -158,7 +161,7 @@ + continue; + + if (!hlist_unhashed(&p->mglist) && +-@@ -1508,8 +1533,8 @@ out: ++@@ -1508,8 +1535,8 @@ out: + + static void br_ip4_multicast_leave_group(struct net_bridge *br, + struct net_bridge_port *port, +@@ -169,7 +172,7 @@ + { + struct br_ip br_group; + struct bridge_mcast_own_query *own_query; +-@@ -1524,14 +1549,14 @@ static void br_ip4_multicast_leave_group ++@@ -1524,14 +1551,14 @@ static void br_ip4_multicast_leave_group + br_group.vid = vid; + + br_multicast_leave_group(br, port, &br_group, &br->ip4_other_query, +@@ -186,7 +189,7 @@ + { + struct br_ip br_group; + struct bridge_mcast_own_query *own_query; +-@@ -1546,7 +1571,7 @@ static void br_ip6_multicast_leave_group ++@@ -1546,7 +1573,7 @@ static void br_ip6_multicast_leave_group + br_group.vid = vid; + + br_multicast_leave_group(br, port, &br_group, &br->ip6_other_query, +@@ -195,41 +198,43 @@ + } + #endif + +-@@ -1555,6 +1580,7 @@ static int br_multicast_ipv4_rcv(struct ++@@ -1555,6 +1582,7 @@ static int br_multicast_ipv4_rcv(struct + struct sk_buff *skb, + u16 vid) + { +-+ const unsigned char *src = eth_hdr(skb)->h_source; +++ const unsigned char *src; + struct sk_buff *skb2 = skb; + const struct iphdr *iph; + struct igmphdr *ih; +-@@ -1628,7 +1654,7 @@ static int br_multicast_ipv4_rcv(struct ++@@ -1628,7 +1656,8 @@ static int br_multicast_ipv4_rcv(struct + case IGMP_HOST_MEMBERSHIP_REPORT: + case IGMPV2_HOST_MEMBERSHIP_REPORT: + BR_INPUT_SKB_CB(skb)->mrouters_only = 1; + - err = br_ip4_multicast_add_group(br, port, ih->group, vid); +++ src = eth_hdr(skb)->h_source; + + 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, skb2, vid); +-@@ -1637,7 +1663,7 @@ static int br_multicast_ipv4_rcv(struct ++@@ -1637,7 +1666,8 @@ static int br_multicast_ipv4_rcv(struct + err = br_ip4_multicast_query(br, port, skb2, vid); + break; + case IGMP_HOST_LEAVE_MESSAGE: + - br_ip4_multicast_leave_group(br, port, ih->group, vid); +++ src = eth_hdr(skb)->h_source; + + br_ip4_multicast_leave_group(br, port, ih->group, vid, src); + break; + } + +-@@ -1655,6 +1681,7 @@ static int br_multicast_ipv6_rcv(struct ++@@ -1655,6 +1685,7 @@ static int br_multicast_ipv6_rcv(struct + struct sk_buff *skb, + u16 vid) + { +-+ const unsigned char *src = eth_hdr(skb)->h_source; +++ const unsigned char *src; + struct sk_buff *skb2; + const struct ipv6hdr *ip6h; + u8 icmp6_type; +-@@ -1764,7 +1791,8 @@ static int br_multicast_ipv6_rcv(struct ++@@ -1764,7 +1795,8 @@ static int br_multicast_ipv6_rcv(struct + } + mld = (struct mld_msg *)skb_transport_header(skb2); + BR_INPUT_SKB_CB(skb)->mrouters_only = 1; +@@ -239,11 +244,12 @@ + break; + } + case ICMPV6_MLD2_REPORT: +-@@ -1781,7 +1809,7 @@ static int br_multicast_ipv6_rcv(struct ++@@ -1781,7 +1813,8 @@ static int br_multicast_ipv6_rcv(struct + goto out; + } + mld = (struct mld_msg *)skb_transport_header(skb2); + - br_ip6_multicast_leave_group(br, port, &mld->mld_mca, vid); +++ src = eth_hdr(skb)->h_source; + + br_ip6_multicast_leave_group(br, port, &mld->mld_mca, vid, src); + } + } diff --git a/patches/openwrt/0028-kernel-bridge-multicast-to-unicast-fix-echoes-on-STA.patch b/patches/openwrt/0028-kernel-bridge-multicast-to-unicast-fix-echoes-on-STA.patch new file mode 100644 index 00000000..37edd667 --- /dev/null +++ b/patches/openwrt/0028-kernel-bridge-multicast-to-unicast-fix-echoes-on-STA.patch @@ -0,0 +1,70 @@ +From: Linus Lüssing +Date: Sat, 5 Sep 2015 03:18:28 +0200 +Subject: kernel: bridge, multicast-to-unicast: fix echoes on STA + +Currently, multicast packets from an STA are sent to any according +multicast listener directly through the bridge multicast-to-unicast +feature. Unfortunately, so far this includes the originating STA, too, +resulting in multicast packets being echo'ed back to the originating STA +if it itself is a multicast listener for that group. + +This behaviour breaks IPv6 duplicate address detection: An IPv6 Neighbor +Solicitation for IPv6 Duplicate Address Detection is being echo'ed back, +resulting in the host falsely detecting an address collision, which +makes the node unable to claim an IPv6 address and use IPv6 in general. + +Mac80211 unfortunately only prevents the echoes for us for multicast +frames. For the multicast frames cast to a unicast destination we'll +need to take care of excluding the originator ourselves. + +Signed-off-by: Linus Lüssing + +git-svn-id: svn://svn.openwrt.org/openwrt/trunk@46765 3c298f89-4303-0410-b956-a3cf2f4a3e73 + +diff --git a/target/linux/generic/patches-3.18/645-bridge_multicast_to_unicast.patch b/target/linux/generic/patches-3.18/645-bridge_multicast_to_unicast.patch +index 6707b03..399f5c6 100644 +--- a/target/linux/generic/patches-3.18/645-bridge_multicast_to_unicast.patch ++++ b/target/linux/generic/patches-3.18/645-bridge_multicast_to_unicast.patch +@@ -297,7 +297,7 @@ + rcu_assign_pointer(*pp, p); + --- a/net/bridge/br_forward.c + +++ b/net/bridge/br_forward.c +-@@ -168,6 +168,29 @@ out: ++@@ -168,6 +168,34 @@ out: + return p; + } + +@@ -308,10 +308,15 @@ + + struct sk_buff *skb)) + +{ + + struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev; +++ const unsigned char *src = eth_hdr(skb)->h_source; + + + + if (!should_deliver(p, skb)) + + return prev; + + +++ /* Even with hairpin, no soliloquies - prevent breaking IPv6 DAD */ +++ if (skb->dev == p->dev && ether_addr_equal(src, addr)) +++ return prev; +++ + + skb = skb_copy(skb, GFP_ATOMIC); + + if (!skb) { + + dev->stats.tx_dropped++; +@@ -327,7 +332,7 @@ + /* called under bridge lock */ + static void br_flood(struct net_bridge *br, struct sk_buff *skb, + struct sk_buff *skb0, +-@@ -232,6 +255,7 @@ static void br_multicast_flood(struct ne ++@@ -232,6 +260,7 @@ static void br_multicast_flood(struct ne + struct net_bridge_port *prev = NULL; + struct net_bridge_port_group *p; + struct hlist_node *rp; +@@ -335,7 +340,7 @@ + + rp = rcu_dereference(hlist_first_rcu(&br->router_list)); + p = mdst ? rcu_dereference(mdst->ports) : NULL; +-@@ -242,10 +266,19 @@ static void br_multicast_flood(struct ne ++@@ -242,10 +271,19 @@ static void br_multicast_flood(struct ne + rport = rp ? hlist_entry(rp, struct net_bridge_port, rlist) : + NULL; +