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@c0d3.blue>
bool iface_config;
bool default_config;
bool wireless;
bool iface_config;
bool default_config;
bool wireless;
struct interface *config_iface;
struct interface *config_iface;
+_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
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
json_get_keys _w_ifaces interfaces
json_select interfaces
for _w_iface in $_w_ifaces; do
if [ -n "$_w_types" ]; then
json_get_var network_bridge bridge
json_select config
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 "
json_get_var _w_type mode
json_select ..
_w_types=" $_w_types "
system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/dad_transmits", dev->ifname, val);
}
system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/dad_transmits", 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;
static int system_get_sysctl(const char *path, char *buf, const size_t buf_sz)
{
int fd = -1, ret = -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)
}
int system_bridge_addif(struct device *bridge, struct device *dev)
ret = system_bridge_if(bridge->ifname, dev, SIOCBRADDIF, NULL);
if (dev->wireless)
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);
enum {
VIF_ATTR_DISABLED,
VIF_ATTR_NETWORK,
enum {
VIF_ATTR_DISABLED,
VIF_ATTR_NETWORK,
__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_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 = {
};
static const struct uci_blob_param_list vif_param = {
if (up) {
struct device *dev = device_get(vif->ifname, 2);
if (up) {
struct device *dev = device_get(vif->ifname, 2);
+ if (dev) {
+ dev->wireless_isolate = vif->isolate;
}
blobmsg_for_each_attr(cur, vif->network, rem) {
}
blobmsg_for_each_attr(cur, vif->network, rem) {
vif->wdev = wdev;
vif->config = data;
vif->section = section;
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);
}
vlist_add(&wdev->interfaces, &vif->node, vif->name);
}
const char *ifname;
struct blob_attr *network;
const char *ifname;
struct blob_attr *network;
};
struct wireless_process {
};
struct wireless_process {