[OpenWrt-Devel] [PATCH netifd 1/2] bridge: Fix multicast_to_unicast feature by hairpin+isolate

Linus Lüssing linus.luessing at c0d3.blue
Thu Jun 18 11:22:59 EDT 2015


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 <linus.luessing at c0d3.blue>
---
 device.c       |    9 +++++++++
 device.h       |    4 ++++
 system-linux.c |   31 +++++++++++++++++++++++++------
 wireless.c     |   12 +++++++++++-
 wireless.h     |    1 +
 5 files changed, 50 insertions(+), 7 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 3001f10..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;
 };
 
 /*
@@ -167,6 +170,7 @@ struct device {
 	bool iface_config;
 	bool default_config;
 	bool wireless;
+	bool wireless_isolate;
 
 	struct interface *config_iface;
 
diff --git a/system-linux.c b/system-linux.c
index 6dc9acd..9e21ab0 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,16 +555,25 @@ 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 mcast_to_ucast = true;
+	bool hairpin = true;
+
+	if (dev->settings.flags & DEV_OPT_MULTICAST_TO_UNICAST &&
+	    !dev->settings.multicast_to_unicast)
+		mcast_to_ucast = false;
+
+	if (!mcast_to_ucast || dev->wireless_isolate)
+		hairpin = false;
+
+	system_bridge_set_multicast_to_unicast(dev, mcast_to_ucast ? "1" : "0");
+	system_bridge_set_hairpin_mode(dev, hairpin ? "1" : "0");
 }
 
 int system_bridge_addif(struct device *bridge, struct device *dev)
 {
+	char buf[64];
 	char *oldbr;
 	int ret = 0;
 
@@ -563,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(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
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel


More information about the openwrt-devel mailing list