aboutsummaryrefslogtreecommitdiff
path: root/net/mac80211/cfg.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-10-26 17:53:44 +0200
committerJohannes Berg <johannes.berg@intel.com>2013-01-03 13:01:40 +0100
commitd582cffbcd04eae0bd8a83b05648bfd54bfd21c9 (patch)
treed6832e35977669df448c196db95ecf6b4e399c2b /net/mac80211/cfg.c
parentdfa674da1807bc8fb782fc38a7d48c8ea3168eb7 (diff)
nl80211/mac80211: support full station state in AP mode
Today, stations are added already associated. That is inefficient if, for example, the driver has no room for stations any more because then the station will go through the entire auth/assoc handshake, only to be kicked out afterwards. To address this a bit better, at least with drivers using the new station state callback, allow hostapd to add stations in unauthenticated mode, just after receiving the AUTH frame, before even replying. Thus if there's no more space at that point, it can send a negative auth frame back. It still needs to handle later state transition errors though, of course. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/cfg.c')
-rw-r--r--net/mac80211/cfg.c115
1 files changed, 71 insertions, 44 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 3e7d557fd48..f4d12c71928 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -510,6 +510,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
BIT(NL80211_STA_FLAG_WME) |
BIT(NL80211_STA_FLAG_MFP) |
BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_ASSOCIATED) |
BIT(NL80211_STA_FLAG_TDLS_PEER);
if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
@@ -521,6 +522,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP);
if (test_sta_flag(sta, WLAN_STA_AUTH))
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+ if (test_sta_flag(sta, WLAN_STA_ASSOC))
+ sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
}
@@ -1077,6 +1080,58 @@ static void ieee80211_send_layer2_update(struct sta_info *sta)
netif_rx_ni(skb);
}
+static int sta_apply_auth_flags(struct ieee80211_local *local,
+ struct sta_info *sta,
+ u32 mask, u32 set)
+{
+ int ret;
+
+ if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
+ set & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
+ !test_sta_flag(sta, WLAN_STA_AUTH)) {
+ ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
+ if (ret)
+ return ret;
+ }
+
+ if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
+ set & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
+ !test_sta_flag(sta, WLAN_STA_ASSOC)) {
+ ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+ if (ret)
+ return ret;
+ }
+
+ if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
+ if (set & BIT(NL80211_STA_FLAG_AUTHORIZED))
+ ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
+ else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+ ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+ else
+ ret = 0;
+ if (ret)
+ return ret;
+ }
+
+ if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
+ !(set & BIT(NL80211_STA_FLAG_ASSOCIATED)) &&
+ test_sta_flag(sta, WLAN_STA_ASSOC)) {
+ ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
+ if (ret)
+ return ret;
+ }
+
+ if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
+ !(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) &&
+ test_sta_flag(sta, WLAN_STA_AUTH)) {
+ ret = sta_info_move_state(sta, IEEE80211_STA_NONE);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static int sta_apply_parameters(struct ieee80211_local *local,
struct sta_info *sta,
struct station_parameters *params)
@@ -1094,52 +1149,20 @@ static int sta_apply_parameters(struct ieee80211_local *local,
mask = params->sta_flags_mask;
set = params->sta_flags_set;
- /*
- * In mesh mode, we can clear AUTHENTICATED flag but must
- * also make ASSOCIATED follow appropriately for the driver
- * API. See also below, after AUTHORIZED changes.
- */
- if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) {
- /* cfg80211 should not allow this in non-mesh modes */
- if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif)))
- return -EINVAL;
-
- if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
- !test_sta_flag(sta, WLAN_STA_AUTH)) {
- ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
- if (ret)
- return ret;
- ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
- if (ret)
- return ret;
- }
- }
-
- if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
- if (set & BIT(NL80211_STA_FLAG_AUTHORIZED))
- ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
- else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
- ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
- if (ret)
- return ret;
- }
-
- if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) {
- /* cfg80211 should not allow this in non-mesh modes */
- if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif)))
- return -EINVAL;
-
- if (!(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) &&
- test_sta_flag(sta, WLAN_STA_AUTH)) {
- ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
- if (ret)
- return ret;
- ret = sta_info_move_state(sta, IEEE80211_STA_NONE);
- if (ret)
- return ret;
- }
+ if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ /*
+ * In mesh mode, ASSOCIATED isn't part of the nl80211
+ * API but must follow AUTHENTICATED for driver state.
+ */
+ if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED))
+ mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+ if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
+ set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
}
+ ret = sta_apply_auth_flags(local, sta, mask, set);
+ if (ret)
+ return ret;
if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) {
if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE))
@@ -1273,6 +1296,10 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
if (!sta)
return -ENOMEM;
+ /*
+ * defaults -- if userspace wants something else we'll
+ * change it accordingly in sta_apply_parameters()
+ */
sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);