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 +