nl80211/mac80211: allow adding TDLS peers as stations
authorArik Nemtsov <arik@wizery.com>
Wed, 28 Sep 2011 11:12:53 +0000 (14:12 +0300)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 30 Sep 2011 19:57:08 +0000 (15:57 -0400)
When adding a TDLS peer STA, mark it with a new flag in both nl80211 and
mac80211. Before adding a peer, make sure the wiphy supports TDLS and
our operating mode is appropriate (managed).

In addition, make sure all peers are removed on disassociation.

A TDLS peer is first added just before link setup is initiated. In later
setup stages we have more info about peer supported rates, capabilities,
etc. This info is reported via nl80211_set_station().

Signed-off-by: Arik Nemtsov <arik@wizery.com>
Cc: Kalyan C Gaddam <chakkal@iit.edu>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
include/linux/nl80211.h
net/mac80211/cfg.c
net/mac80211/mlme.c
net/mac80211/sta_info.h
net/wireless/nl80211.c

index a5ab23df5b1789a0fbc47437873964e27832ea52..9d797f253d8edbe1b4968a9a0698ae7be84229cb 100644 (file)
@@ -1434,6 +1434,7 @@ enum nl80211_iftype {
  * @NL80211_STA_FLAG_WME: station is WME/QoS capable
  * @NL80211_STA_FLAG_MFP: station uses management frame protection
  * @NL80211_STA_FLAG_AUTHENTICATED: station is authenticated
+ * @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer
  * @NL80211_STA_FLAG_MAX: highest station flag number currently defined
  * @__NL80211_STA_FLAG_AFTER_LAST: internal use
  */
@@ -1444,6 +1445,7 @@ enum nl80211_sta_flags {
        NL80211_STA_FLAG_WME,
        NL80211_STA_FLAG_MFP,
        NL80211_STA_FLAG_AUTHENTICATED,
+       NL80211_STA_FLAG_TDLS_PEER,
 
        /* keep last */
        __NL80211_STA_FLAG_AFTER_LAST,
index 1d17677a0ec185d7e19ae1cb464bdaed90a3f5f8..119a573af14bb3ffedd28f34a543f5ad314ed2e5 100644 (file)
@@ -713,6 +713,12 @@ static void sta_apply_parameters(struct ieee80211_local *local,
                if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
                        sta->flags |= WLAN_STA_AUTH;
        }
+
+       if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
+               sta->flags &= ~WLAN_STA_TDLS_PEER;
+               if (set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+                       sta->flags |= WLAN_STA_TDLS_PEER;
+       }
        spin_unlock_irqrestore(&sta->flaglock, flags);
 
        if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) {
@@ -813,6 +819,12 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
 
        sta_apply_parameters(local, sta, params);
 
+       /* Only TDLS-supporting stations can add TDLS peers */
+       if ((sta->flags & WLAN_STA_TDLS_PEER) &&
+           !((wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
+             sdata->vif.type == NL80211_IFTYPE_STATION))
+               return -ENOTSUPP;
+
        rate_control_rate_init(sta);
 
        layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
@@ -865,6 +877,14 @@ static int ieee80211_change_station(struct wiphy *wiphy,
                return -ENOENT;
        }
 
+       /* The TDLS bit cannot be toggled after the STA was added */
+       if ((params->sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
+           !!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) !=
+           !!test_sta_flags(sta, WLAN_STA_TDLS_PEER)) {
+               rcu_read_unlock();
+               return -EINVAL;
+       }
+
        if (params->vlan && params->vlan != sta->sdata->dev) {
                vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
 
index cd37a4e3c0d7765f350b4792ed46acf2908c99cf..b98c43a7f1917844afb849e711ff1970778cd15c 100644 (file)
@@ -1137,8 +1137,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT;
        ieee80211_bss_info_change_notify(sdata, changed);
 
+       /* remove AP and TDLS peers */
        if (remove_sta)
-               sta_info_destroy_addr(sdata, bssid);
+               sta_info_flush(local, sdata);
 
        del_timer_sync(&sdata->u.mgd.conn_mon_timer);
        del_timer_sync(&sdata->u.mgd.bcn_mon_timer);
@@ -2738,7 +2739,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
                                       req->reason_code, cookie,
                                       !req->local_state_change);
        if (assoc_bss)
-               sta_info_destroy_addr(sdata, bssid);
+               sta_info_flush(sdata->local, sdata);
 
        mutex_lock(&sdata->local->mtx);
        ieee80211_recalc_idle(sdata->local);
@@ -2778,7 +2779,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
        ieee80211_send_deauth_disassoc(sdata, req->bss->bssid,
                        IEEE80211_STYPE_DISASSOC, req->reason_code,
                        cookie, !req->local_state_change);
-       sta_info_destroy_addr(sdata, bssid);
+       sta_info_flush(sdata->local, sdata);
 
        mutex_lock(&sdata->local->mtx);
        ieee80211_recalc_idle(sdata->local);
index 56a3d38a2cd19479520f875d080d53127a37a73d..b6bd4e9d87224c5271fc63e45b714f7e9265cef0 100644 (file)
@@ -45,6 +45,7 @@
  *     station in power-save mode, reply when the driver unblocks.
  * @WLAN_STA_PS_DRIVER_BUF: Station has frames pending in driver internal
  *     buffers. Automatically cleared on station wake-up.
+ * @WLAN_STA_TDLS_PEER: station is a TDLS peer.
  */
 enum ieee80211_sta_info_flags {
        WLAN_STA_AUTH           = 1<<0,
@@ -61,6 +62,7 @@ enum ieee80211_sta_info_flags {
        WLAN_STA_PS_DRIVER      = 1<<12,
        WLAN_STA_PSPOLL         = 1<<13,
        WLAN_STA_PS_DRIVER_BUF  = 1<<14,
+       WLAN_STA_TDLS_PEER      = 1<<15,
 };
 
 #define STA_TID_NUM 16
index 25a37fc951e3c2814a5d59becfc8a7b0899b048f..edf655aeea0015513f7fc79290b3f5cecd02bc81 100644 (file)
@@ -2530,18 +2530,25 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_STATION:
-               /* disallow everything but AUTHORIZED flag */
+               /* disallow things sta doesn't support */
                if (params.plink_action)
                        err = -EINVAL;
                if (params.vlan)
                        err = -EINVAL;
-               if (params.supported_rates)
+               if (params.supported_rates &&
+                   !(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
                        err = -EINVAL;
                if (params.ht_capa)
                        err = -EINVAL;
                if (params.listen_interval >= 0)
                        err = -EINVAL;
-               if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
+               if (params.sta_flags_mask &
+                               ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
+                                 BIT(NL80211_STA_FLAG_TDLS_PEER)))
+                       err = -EINVAL;
+               /* can't change the TDLS bit */
+               if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
+                   (params.sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER)))
                        err = -EINVAL;
                break;
        case NL80211_IFTYPE_MESH_POINT:
@@ -2662,7 +2669,18 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
        if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
            dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
            dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
-           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION)
+               return -EINVAL;
+
+       /*
+        * Only managed stations can add TDLS peers, and only when the
+        * wiphy supports external TDLS setup.
+        */
+       if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_STATION &&
+           !((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
+             (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
+             (rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)))
                return -EINVAL;
 
        err = get_vlan(info, rdev, &params.vlan);