diff options
author | Guy Eilam <guy@wizery.com> | 2011-08-17 14:18:15 +0200 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-08-26 16:47:55 +0200 |
commit | 2a33bee2753bf28411de8822e3e3c7501966eb1b (patch) | |
tree | dac6419b65bdd79da56c6855bbf4a439f424e05c /net/mac80211/sta_info.h | |
parent | mac80211: refactor sta_info_insert_rcu to 3 main stages (diff) | |
download | linux-2a33bee2753bf28411de8822e3e3c7501966eb1b.tar.xz linux-2a33bee2753bf28411de8822e3e3c7501966eb1b.zip |
mac80211: fix race condition between assoc_done and first EAP packet
When associating to an AP, the station might miss the first EAP
packet that the AP sends due to a race condition between the association
success procedure and the rx flow in mac80211.
In such cases, the packet might fall in ieee80211_rx_h_check due to
the fact that the relevant rx->sta wasn't allocated yet.
Allocation of the relevant station info struct before actually
sending the association request and setting it with a new
dummy_sta flag solve this problem.
The station will accept only EAP packets from the AP while it
is in the pre-association/dummy state.
This dummy station entry is not seen by normal sta_info_get()
calls, only by sta_info_get_bss_rx().
The driver is not notified for the first insertion of the
dummy station. The driver is notified only after the association
is complete and the dummy flag is removed from the station entry.
That way, all the rest of the code flow should be untouched by
this change.
Signed-off-by: Guy Eilam <guy@wizery.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/sta_info.h')
-rw-r--r-- | net/mac80211/sta_info.h | 30 |
1 files changed, 29 insertions, 1 deletions
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 28beb78e601e..e9eb565506da 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -238,10 +238,12 @@ struct sta_ampdu_mlme { * @plink_timer: peer link watch timer * @plink_timer_was_running: used by suspend/resume to restore timers * @debugfs: debug filesystem info - * @sta: station information we share with the driver * @dead: set to true when sta is unlinked * @uploaded: set to true when sta is uploaded to the driver * @lost_packets: number of consecutive lost packets + * @dummy: indicate a dummy station created for receiving + * EAP frames before association + * @sta: station information we share with the driver */ struct sta_info { /* General information, mostly static */ @@ -336,6 +338,9 @@ struct sta_info { unsigned int lost_packets; + /* should be right in front of sta to be in the same cache line */ + bool dummy; + /* keep last! */ struct ieee80211_sta sta; }; @@ -436,9 +441,15 @@ rcu_dereference_protected_tid_tx(struct sta_info *sta, int tid) struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, const u8 *addr); +struct sta_info *sta_info_get_rx(struct ieee80211_sub_if_data *sdata, + const u8 *addr); + struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, const u8 *addr); +struct sta_info *sta_info_get_bss_rx(struct ieee80211_sub_if_data *sdata, + const u8 *addr); + static inline void for_each_sta_info_type_check(struct ieee80211_local *local, const u8 *addr, @@ -459,6 +470,22 @@ void for_each_sta_info_type_check(struct ieee80211_local *local, _sta = nxt, \ nxt = _sta ? rcu_dereference(_sta->hnext) : NULL \ ) \ + /* run code only if address matches and it's not a dummy sta */ \ + if (memcmp(_sta->sta.addr, (_addr), ETH_ALEN) == 0 && \ + !_sta->dummy) + +#define for_each_sta_info_rx(local, _addr, _sta, nxt) \ + for ( /* initialise loop */ \ + _sta = rcu_dereference(local->sta_hash[STA_HASH(_addr)]),\ + nxt = _sta ? rcu_dereference(_sta->hnext) : NULL; \ + /* typecheck */ \ + for_each_sta_info_type_check(local, (_addr), _sta, nxt),\ + /* continue condition */ \ + _sta; \ + /* advance loop */ \ + _sta = nxt, \ + nxt = _sta ? rcu_dereference(_sta->hnext) : NULL \ + ) \ /* compare address and run code only if it matches */ \ if (memcmp(_sta->sta.addr, (_addr), ETH_ALEN) == 0) @@ -484,6 +511,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, int sta_info_insert(struct sta_info *sta); int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU); int sta_info_insert_atomic(struct sta_info *sta); +int sta_info_reinsert(struct sta_info *sta); int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata, const u8 *addr); |