diff options
author | Johannes Berg <johannes.berg@intel.com> | 2012-10-26 11:53:44 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-01-03 07:01:40 -0500 |
commit | d582cffbcd04eae0bd8a83b05648bfd54bfd21c9 (patch) | |
tree | d6832e35977669df448c196db95ecf6b4e399c2b | |
parent | dfa674da1807bc8fb782fc38a7d48c8ea3168eb7 (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>
-rw-r--r-- | include/uapi/linux/nl80211.h | 16 | ||||
-rw-r--r-- | net/mac80211/cfg.c | 115 | ||||
-rw-r--r-- | net/mac80211/main.c | 3 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 24 |
4 files changed, 113 insertions, 45 deletions
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index e3e19f8b16f2..547017100a30 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h | |||
@@ -1697,6 +1697,9 @@ enum nl80211_iftype { | |||
1697 | * flag can't be changed, it is only valid while adding a station, and | 1697 | * flag can't be changed, it is only valid while adding a station, and |
1698 | * attempts to change it will silently be ignored (rather than rejected | 1698 | * attempts to change it will silently be ignored (rather than rejected |
1699 | * as errors.) | 1699 | * as errors.) |
1700 | * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers | ||
1701 | * that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a | ||
1702 | * previously added station into associated state | ||
1700 | * @NL80211_STA_FLAG_MAX: highest station flag number currently defined | 1703 | * @NL80211_STA_FLAG_MAX: highest station flag number currently defined |
1701 | * @__NL80211_STA_FLAG_AFTER_LAST: internal use | 1704 | * @__NL80211_STA_FLAG_AFTER_LAST: internal use |
1702 | */ | 1705 | */ |
@@ -1708,6 +1711,7 @@ enum nl80211_sta_flags { | |||
1708 | NL80211_STA_FLAG_MFP, | 1711 | NL80211_STA_FLAG_MFP, |
1709 | NL80211_STA_FLAG_AUTHENTICATED, | 1712 | NL80211_STA_FLAG_AUTHENTICATED, |
1710 | NL80211_STA_FLAG_TDLS_PEER, | 1713 | NL80211_STA_FLAG_TDLS_PEER, |
1714 | NL80211_STA_FLAG_ASSOCIATED, | ||
1711 | 1715 | ||
1712 | /* keep last */ | 1716 | /* keep last */ |
1713 | __NL80211_STA_FLAG_AFTER_LAST, | 1717 | __NL80211_STA_FLAG_AFTER_LAST, |
@@ -3140,6 +3144,17 @@ enum nl80211_ap_sme_features { | |||
3140 | * setting | 3144 | * setting |
3141 | * @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic | 3145 | * @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic |
3142 | * powersave | 3146 | * powersave |
3147 | * @NL80211_FEATURE_FULL_AP_CLIENT_STATE: The driver supports full state | ||
3148 | * transitions for AP clients. Without this flag (and if the driver | ||
3149 | * doesn't have the AP SME in the device) the driver supports adding | ||
3150 | * stations only when they're associated and adds them in associated | ||
3151 | * state (to later be transitioned into authorized), with this flag | ||
3152 | * they should be added before even sending the authentication reply | ||
3153 | * and then transitioned into authenticated, associated and authorized | ||
3154 | * states using station flags. | ||
3155 | * Note that even for drivers that support this, the default is to add | ||
3156 | * stations in authenticated/associated state, so to add unauthenticated | ||
3157 | * stations the authenticated/associated bits have to be set in the mask. | ||
3143 | */ | 3158 | */ |
3144 | enum nl80211_feature_flags { | 3159 | enum nl80211_feature_flags { |
3145 | NL80211_FEATURE_SK_TX_STATUS = 1 << 0, | 3160 | NL80211_FEATURE_SK_TX_STATUS = 1 << 0, |
@@ -3155,6 +3170,7 @@ enum nl80211_feature_flags { | |||
3155 | NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10, | 3170 | NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10, |
3156 | NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11, | 3171 | NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11, |
3157 | NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12, | 3172 | NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12, |
3173 | NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 13, | ||
3158 | }; | 3174 | }; |
3159 | 3175 | ||
3160 | /** | 3176 | /** |
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 3e7d557fd481..f4d12c71928d 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) | |||
510 | BIT(NL80211_STA_FLAG_WME) | | 510 | BIT(NL80211_STA_FLAG_WME) | |
511 | BIT(NL80211_STA_FLAG_MFP) | | 511 | BIT(NL80211_STA_FLAG_MFP) | |
512 | BIT(NL80211_STA_FLAG_AUTHENTICATED) | | 512 | BIT(NL80211_STA_FLAG_AUTHENTICATED) | |
513 | BIT(NL80211_STA_FLAG_ASSOCIATED) | | ||
513 | BIT(NL80211_STA_FLAG_TDLS_PEER); | 514 | BIT(NL80211_STA_FLAG_TDLS_PEER); |
514 | if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) | 515 | if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) |
515 | sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED); | 516 | 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) | |||
521 | sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP); | 522 | sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP); |
522 | if (test_sta_flag(sta, WLAN_STA_AUTH)) | 523 | if (test_sta_flag(sta, WLAN_STA_AUTH)) |
523 | sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED); | 524 | sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED); |
525 | if (test_sta_flag(sta, WLAN_STA_ASSOC)) | ||
526 | sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED); | ||
524 | if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) | 527 | if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) |
525 | sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); | 528 | sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); |
526 | } | 529 | } |
@@ -1077,6 +1080,58 @@ static void ieee80211_send_layer2_update(struct sta_info *sta) | |||
1077 | netif_rx_ni(skb); | 1080 | netif_rx_ni(skb); |
1078 | } | 1081 | } |
1079 | 1082 | ||
1083 | static int sta_apply_auth_flags(struct ieee80211_local *local, | ||
1084 | struct sta_info *sta, | ||
1085 | u32 mask, u32 set) | ||
1086 | { | ||
1087 | int ret; | ||
1088 | |||
1089 | if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) && | ||
1090 | set & BIT(NL80211_STA_FLAG_AUTHENTICATED) && | ||
1091 | !test_sta_flag(sta, WLAN_STA_AUTH)) { | ||
1092 | ret = sta_info_move_state(sta, IEEE80211_STA_AUTH); | ||
1093 | if (ret) | ||
1094 | return ret; | ||
1095 | } | ||
1096 | |||
1097 | if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) && | ||
1098 | set & BIT(NL80211_STA_FLAG_ASSOCIATED) && | ||
1099 | !test_sta_flag(sta, WLAN_STA_ASSOC)) { | ||
1100 | ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); | ||
1101 | if (ret) | ||
1102 | return ret; | ||
1103 | } | ||
1104 | |||
1105 | if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { | ||
1106 | if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) | ||
1107 | ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); | ||
1108 | else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) | ||
1109 | ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); | ||
1110 | else | ||
1111 | ret = 0; | ||
1112 | if (ret) | ||
1113 | return ret; | ||
1114 | } | ||
1115 | |||
1116 | if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) && | ||
1117 | !(set & BIT(NL80211_STA_FLAG_ASSOCIATED)) && | ||
1118 | test_sta_flag(sta, WLAN_STA_ASSOC)) { | ||
1119 | ret = sta_info_move_state(sta, IEEE80211_STA_AUTH); | ||
1120 | if (ret) | ||
1121 | return ret; | ||
1122 | } | ||
1123 | |||
1124 | if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) && | ||
1125 | !(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) && | ||
1126 | test_sta_flag(sta, WLAN_STA_AUTH)) { | ||
1127 | ret = sta_info_move_state(sta, IEEE80211_STA_NONE); | ||
1128 | if (ret) | ||
1129 | return ret; | ||
1130 | } | ||
1131 | |||
1132 | return 0; | ||
1133 | } | ||
1134 | |||
1080 | static int sta_apply_parameters(struct ieee80211_local *local, | 1135 | static int sta_apply_parameters(struct ieee80211_local *local, |
1081 | struct sta_info *sta, | 1136 | struct sta_info *sta, |
1082 | struct station_parameters *params) | 1137 | struct station_parameters *params) |
@@ -1094,52 +1149,20 @@ static int sta_apply_parameters(struct ieee80211_local *local, | |||
1094 | mask = params->sta_flags_mask; | 1149 | mask = params->sta_flags_mask; |
1095 | set = params->sta_flags_set; | 1150 | set = params->sta_flags_set; |
1096 | 1151 | ||
1097 | /* | 1152 | if (ieee80211_vif_is_mesh(&sdata->vif)) { |
1098 | * In mesh mode, we can clear AUTHENTICATED flag but must | 1153 | /* |
1099 | * also make ASSOCIATED follow appropriately for the driver | 1154 | * In mesh mode, ASSOCIATED isn't part of the nl80211 |
1100 | * API. See also below, after AUTHORIZED changes. | 1155 | * API but must follow AUTHENTICATED for driver state. |
1101 | */ | 1156 | */ |
1102 | if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { | 1157 | if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) |
1103 | /* cfg80211 should not allow this in non-mesh modes */ | 1158 | mask |= BIT(NL80211_STA_FLAG_ASSOCIATED); |
1104 | if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif))) | 1159 | if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) |
1105 | return -EINVAL; | 1160 | set |= BIT(NL80211_STA_FLAG_ASSOCIATED); |
1106 | |||
1107 | if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED) && | ||
1108 | !test_sta_flag(sta, WLAN_STA_AUTH)) { | ||
1109 | ret = sta_info_move_state(sta, IEEE80211_STA_AUTH); | ||
1110 | if (ret) | ||
1111 | return ret; | ||
1112 | ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); | ||
1113 | if (ret) | ||
1114 | return ret; | ||
1115 | } | ||
1116 | } | ||
1117 | |||
1118 | if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { | ||
1119 | if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) | ||
1120 | ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); | ||
1121 | else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) | ||
1122 | ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); | ||
1123 | if (ret) | ||
1124 | return ret; | ||
1125 | } | ||
1126 | |||
1127 | if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { | ||
1128 | /* cfg80211 should not allow this in non-mesh modes */ | ||
1129 | if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif))) | ||
1130 | return -EINVAL; | ||
1131 | |||
1132 | if (!(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) && | ||
1133 | test_sta_flag(sta, WLAN_STA_AUTH)) { | ||
1134 | ret = sta_info_move_state(sta, IEEE80211_STA_AUTH); | ||
1135 | if (ret) | ||
1136 | return ret; | ||
1137 | ret = sta_info_move_state(sta, IEEE80211_STA_NONE); | ||
1138 | if (ret) | ||
1139 | return ret; | ||
1140 | } | ||
1141 | } | 1161 | } |
1142 | 1162 | ||
1163 | ret = sta_apply_auth_flags(local, sta, mask, set); | ||
1164 | if (ret) | ||
1165 | return ret; | ||
1143 | 1166 | ||
1144 | if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) { | 1167 | if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) { |
1145 | if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) | 1168 | if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) |
@@ -1273,6 +1296,10 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, | |||
1273 | if (!sta) | 1296 | if (!sta) |
1274 | return -ENOMEM; | 1297 | return -ENOMEM; |
1275 | 1298 | ||
1299 | /* | ||
1300 | * defaults -- if userspace wants something else we'll | ||
1301 | * change it accordingly in sta_apply_parameters() | ||
1302 | */ | ||
1276 | sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); | 1303 | sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); |
1277 | sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); | 1304 | sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); |
1278 | 1305 | ||
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index e6514f240fce..39cfe8f10ad2 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -541,7 +541,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
541 | wiphy->features |= NL80211_FEATURE_SK_TX_STATUS | | 541 | wiphy->features |= NL80211_FEATURE_SK_TX_STATUS | |
542 | NL80211_FEATURE_SAE | | 542 | NL80211_FEATURE_SAE | |
543 | NL80211_FEATURE_HT_IBSS | | 543 | NL80211_FEATURE_HT_IBSS | |
544 | NL80211_FEATURE_VIF_TXPOWER; | 544 | NL80211_FEATURE_VIF_TXPOWER | |
545 | NL80211_FEATURE_FULL_AP_CLIENT_STATE; | ||
545 | 546 | ||
546 | if (!ops->hw_scan) | 547 | if (!ops->hw_scan) |
547 | wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN | | 548 | wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN | |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b3cf7cc0d4a1..087f68ba6d7a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -3231,11 +3231,21 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | |||
3231 | /* accept only the listed bits */ | 3231 | /* accept only the listed bits */ |
3232 | if (params.sta_flags_mask & | 3232 | if (params.sta_flags_mask & |
3233 | ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | | 3233 | ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | |
3234 | BIT(NL80211_STA_FLAG_AUTHENTICATED) | | ||
3235 | BIT(NL80211_STA_FLAG_ASSOCIATED) | | ||
3234 | BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | | 3236 | BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | |
3235 | BIT(NL80211_STA_FLAG_WME) | | 3237 | BIT(NL80211_STA_FLAG_WME) | |
3236 | BIT(NL80211_STA_FLAG_MFP))) | 3238 | BIT(NL80211_STA_FLAG_MFP))) |
3237 | return -EINVAL; | 3239 | return -EINVAL; |
3238 | 3240 | ||
3241 | /* but authenticated/associated only if driver handles it */ | ||
3242 | if (!(rdev->wiphy.features & | ||
3243 | NL80211_FEATURE_FULL_AP_CLIENT_STATE) && | ||
3244 | params.sta_flags_mask & | ||
3245 | (BIT(NL80211_STA_FLAG_AUTHENTICATED) | | ||
3246 | BIT(NL80211_STA_FLAG_ASSOCIATED))) | ||
3247 | return -EINVAL; | ||
3248 | |||
3239 | /* must be last in here for error handling */ | 3249 | /* must be last in here for error handling */ |
3240 | params.vlan = get_vlan(info, rdev); | 3250 | params.vlan = get_vlan(info, rdev); |
3241 | if (IS_ERR(params.vlan)) | 3251 | if (IS_ERR(params.vlan)) |
@@ -3393,17 +3403,31 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
3393 | /* but don't bother the driver with it */ | 3403 | /* but don't bother the driver with it */ |
3394 | params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); | 3404 | params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); |
3395 | 3405 | ||
3406 | /* allow authenticated/associated only if driver handles it */ | ||
3407 | if (!(rdev->wiphy.features & | ||
3408 | NL80211_FEATURE_FULL_AP_CLIENT_STATE) && | ||
3409 | params.sta_flags_mask & | ||
3410 | (BIT(NL80211_STA_FLAG_AUTHENTICATED) | | ||
3411 | BIT(NL80211_STA_FLAG_ASSOCIATED))) | ||
3412 | return -EINVAL; | ||
3413 | |||
3396 | /* must be last in here for error handling */ | 3414 | /* must be last in here for error handling */ |
3397 | params.vlan = get_vlan(info, rdev); | 3415 | params.vlan = get_vlan(info, rdev); |
3398 | if (IS_ERR(params.vlan)) | 3416 | if (IS_ERR(params.vlan)) |
3399 | return PTR_ERR(params.vlan); | 3417 | return PTR_ERR(params.vlan); |
3400 | break; | 3418 | break; |
3401 | case NL80211_IFTYPE_MESH_POINT: | 3419 | case NL80211_IFTYPE_MESH_POINT: |
3420 | /* associated is disallowed */ | ||
3421 | if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) | ||
3422 | return -EINVAL; | ||
3402 | /* TDLS peers cannot be added */ | 3423 | /* TDLS peers cannot be added */ |
3403 | if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) | 3424 | if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) |
3404 | return -EINVAL; | 3425 | return -EINVAL; |
3405 | break; | 3426 | break; |
3406 | case NL80211_IFTYPE_STATION: | 3427 | case NL80211_IFTYPE_STATION: |
3428 | /* associated is disallowed */ | ||
3429 | if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) | ||
3430 | return -EINVAL; | ||
3407 | /* Only TDLS peers can be added */ | 3431 | /* Only TDLS peers can be added */ |
3408 | if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) | 3432 | if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) |
3409 | return -EINVAL; | 3433 | return -EINVAL; |