cfg80211: disallow bridging managed/adhoc interfaces
authorJohannes Berg <johannes@sipsolutions.net>
Wed, 18 Nov 2009 23:56:30 +0000 (00:56 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 19 Nov 2009 16:08:54 +0000 (11:08 -0500)
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>
include/linux/if.h
net/bridge/br_if.c
net/wireless/core.c
net/wireless/nl80211.c
net/wireless/util.c

index 3b2a46bf8f8d95a7c03d54db8f68fe1156e9d857..3a9f410a296b35291150d27e900fb73d27d0c3d6 100644 (file)
@@ -70,6 +70,7 @@
 #define IFF_XMIT_DST_RELEASE 0x400     /* dev_hard_start_xmit() is allowed to
                                         * release skb->dst
                                         */
+#define IFF_DONT_BRIDGE 0x800          /* disallow bridging this ether dev */
 
 #define IF_GET_IFACE   0x0001          /* for querying only */
 #define IF_GET_PROTO   0x0002
index a6f74b2b95711838e15650a538ca16195012641d..a2cbe61f6e653842215699ced523d7a0c6b6dfc0 100644 (file)
@@ -390,6 +390,10 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
        if (dev->br_port != NULL)
                return -EBUSY;
 
+       /* No bridging devices that dislike that (e.g. wireless) */
+       if (dev->priv_flags & IFF_DONT_BRIDGE)
+               return -EOPNOTSUPP;
+
        p = new_nbp(br, dev);
        if (IS_ERR(p))
                return PTR_ERR(p);
index e2cc6e7522dd29b00f317c24f1bdcedf36fb05d7..fc5e9b5086070131f64fb7adf4f1f9a024d55d2c 100644 (file)
@@ -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) {
index b7b0f67b0c611940fe1ae20bd5a8e56c95c98339..149539ade15e42f5ac137f13146d7cdc72d2a152 100644 (file)
@@ -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;
        }
index 17a7a4cfc617a5ff013c7f61d8cfe9d69710646b..59361fdcb5d03898b8d16ce5c7fbe637954a8cbf 100644 (file)
@@ -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;
 }