summaryrefslogtreecommitdiff
path: root/net/wireless
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-11-19 00:56:30 +0100
committerJohn W. Linville <linville@tuxdriver.com>2009-11-19 11:08:54 -0500
commitad4bb6f8883a13bb0f65b194dae36c62a02ac779 (patch)
treeb395936203ac891b9a537c26b4602f727c4387d0 /net/wireless
parent9bc383de37090ba7ca3ff32a12c9d809dc5867f0 (diff)
downloadkernel-common-ad4bb6f8883a13bb0f65b194dae36c62a02ac779.tar.gz
kernel-common-ad4bb6f8883a13bb0f65b194dae36c62a02ac779.tar.bz2
kernel-common-ad4bb6f8883a13bb0f65b194dae36c62a02ac779.zip
cfg80211: disallow bridging managed/adhoc interfaces
A number of people have tried to add a wireless interface (in managed mode) to a bridge and then complained that it doesn't work. It cannot work, however, because in 802.11 networks all packets need to be acknowledged and as such need to be sent to the right address. Promiscuous doesn't help here. The wireless address format used for these links has only space for three addresses, the * transmitter, which must be equal to the sender (origin) * receiver (on the wireless medium), which is the AP in the case of managed mode * the recipient (destination), which is on the APs local network segment In an IBSS, it is similar, but the receiver and recipient must match and the third address is used as the BSSID. To avoid such mistakes in the future, disallow adding a wireless interface to a bridge. Felix has recently added a four-address mode to the AP and client side that can be used (after negotiating that it is possible, which must happen out-of-band by setting up both sides) for bridging, so allow that case. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Acked-by: Stephen Hemminger <shemminger@vyatta.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/core.c4
-rw-r--r--net/wireless/nl80211.c12
-rw-r--r--net/wireless/util.c31
3 files changed, 43 insertions, 4 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c
index e2cc6e7522dd..fc5e9b508607 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -697,6 +697,10 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
#endif
if (!dev->ethtool_ops)
dev->ethtool_ops = &cfg80211_ethtool_ops;
+
+ if ((wdev->iftype == NL80211_IFTYPE_STATION ||
+ wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
+ dev->priv_flags |= IFF_DONT_BRIDGE;
break;
case NETDEV_GOING_DOWN:
switch (wdev->iftype) {
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index b7b0f67b0c61..149539ade15e 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -969,10 +969,14 @@ static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
}
static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
- u8 use_4addr, enum nl80211_iftype iftype)
+ struct net_device *netdev, u8 use_4addr,
+ enum nl80211_iftype iftype)
{
- if (!use_4addr)
+ if (!use_4addr) {
+ if (netdev && netdev->br_port)
+ return -EBUSY;
return 0;
+ }
switch (iftype) {
case NL80211_IFTYPE_AP_VLAN:
@@ -1033,7 +1037,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_4ADDR]) {
params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
change = true;
- err = nl80211_valid_4addr(rdev, params.use_4addr, ntype);
+ err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype);
if (err)
goto unlock;
} else {
@@ -1111,7 +1115,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_4ADDR]) {
params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
- err = nl80211_valid_4addr(rdev, params.use_4addr, type);
+ err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
if (err)
goto unlock;
}
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 17a7a4cfc617..59361fdcb5d0 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -658,6 +658,11 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
!(rdev->wiphy.interface_modes & (1 << ntype)))
return -EOPNOTSUPP;
+ /* if it's part of a bridge, reject changing type to station/ibss */
+ if (dev->br_port && (ntype == NL80211_IFTYPE_ADHOC ||
+ ntype == NL80211_IFTYPE_STATION))
+ return -EBUSY;
+
if (ntype != otype) {
dev->ieee80211_ptr->use_4addr = false;
@@ -687,5 +692,31 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
if (!err && params && params->use_4addr != -1)
dev->ieee80211_ptr->use_4addr = params->use_4addr;
+ if (!err) {
+ dev->priv_flags &= ~IFF_DONT_BRIDGE;
+ switch (ntype) {
+ case NL80211_IFTYPE_STATION:
+ if (dev->ieee80211_ptr->use_4addr)
+ break;
+ /* fall through */
+ case NL80211_IFTYPE_ADHOC:
+ dev->priv_flags |= IFF_DONT_BRIDGE;
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_MESH_POINT:
+ /* bridging OK */
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ /* monitor can't bridge anyway */
+ break;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case __NL80211_IFTYPE_AFTER_LAST:
+ /* not happening */
+ break;
+ }
+ }
+
return err;
}