Skip to content

Commit

Permalink
mac80211: fix race condition between assoc_done and first EAP packet
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Guy Eilam authored and John W. Linville committed Aug 26, 2011
1 parent 8c71df7 commit 2a33bee
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 60 deletions.
59 changes: 53 additions & 6 deletions net/mac80211/mlme.c
Original file line number Diff line number Diff line change
Expand Up @@ -1482,10 +1482,14 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,

ifmgd->aid = aid;

sta = sta_info_alloc(sdata, cbss->bssid, GFP_KERNEL);
if (!sta) {
printk(KERN_DEBUG "%s: failed to alloc STA entry for"
" the AP\n", sdata->name);
mutex_lock(&sdata->local->sta_mtx);
/*
* station info was already allocated and inserted before
* the association and should be available to us
*/
sta = sta_info_get_rx(sdata, cbss->bssid);
if (WARN_ON(!sta)) {
mutex_unlock(&sdata->local->sta_mtx);
return false;
}

Expand Down Expand Up @@ -1556,7 +1560,8 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
if (elems.wmm_param)
set_sta_flags(sta, WLAN_STA_WME);

err = sta_info_insert(sta);
/* sta_info_reinsert will also unlock the mutex lock */
err = sta_info_reinsert(sta);
sta = NULL;
if (err) {
printk(KERN_DEBUG "%s: failed to insert STA entry for"
Expand Down Expand Up @@ -2429,16 +2434,44 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
return 0;
}

/* create and insert a dummy station entry */
static int ieee80211_pre_assoc(struct ieee80211_sub_if_data *sdata,
u8 *bssid) {
struct sta_info *sta;
int err;

sta = sta_info_alloc(sdata, bssid, GFP_KERNEL);
if (!sta) {
printk(KERN_DEBUG "%s: failed to alloc STA entry for"
" the AP\n", sdata->name);
return -ENOMEM;
}

sta->dummy = true;

err = sta_info_insert(sta);
sta = NULL;
if (err) {
printk(KERN_DEBUG "%s: failed to insert Dummy STA entry for"
" the AP (error %d)\n", sdata->name, err);
return err;
}

return 0;
}

static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
struct sk_buff *skb)
{
struct ieee80211_local *local = wk->sdata->local;
struct ieee80211_mgmt *mgmt;
struct ieee80211_rx_status *rx_status;
struct ieee802_11_elems elems;
struct cfg80211_bss *cbss = wk->assoc.bss;
u16 status;

if (!skb) {
sta_info_destroy_addr(wk->sdata, cbss->bssid);
cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta);
goto destroy;
}
Expand Down Expand Up @@ -2468,12 +2501,16 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
if (!ieee80211_assoc_success(wk, mgmt, skb->len)) {
mutex_unlock(&wk->sdata->u.mgd.mtx);
/* oops -- internal error -- send timeout for now */
sta_info_destroy_addr(wk->sdata, cbss->bssid);
cfg80211_send_assoc_timeout(wk->sdata->dev,
wk->filter_ta);
return WORK_DONE_DESTROY;
}

mutex_unlock(&wk->sdata->u.mgd.mtx);
} else {
/* assoc failed - destroy the dummy station entry */
sta_info_destroy_addr(wk->sdata, cbss->bssid);
}

cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len);
Expand All @@ -2492,7 +2529,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_bss *bss = (void *)req->bss->priv;
struct ieee80211_work *wk;
const u8 *ssid;
int i;
int i, err;

mutex_lock(&ifmgd->mtx);
if (ifmgd->associated) {
Expand All @@ -2517,6 +2554,16 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
if (!wk)
return -ENOMEM;

/*
* create a dummy station info entry in order
* to start accepting incoming EAPOL packets from the station
*/
err = ieee80211_pre_assoc(sdata, req->bss->bssid);
if (err) {
kfree(wk);
return err;
}

ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;

Expand Down
21 changes: 17 additions & 4 deletions net/mac80211/rx.c
Original file line number Diff line number Diff line change
Expand Up @@ -850,8 +850,21 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
ieee80211_is_pspoll(hdr->frame_control)) &&
rx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
rx->sdata->vif.type != NL80211_IFTYPE_WDS &&
(!rx->sta || !test_sta_flags(rx->sta, WLAN_STA_ASSOC))))
(!rx->sta || !test_sta_flags(rx->sta, WLAN_STA_ASSOC)))) {
if (rx->sta && rx->sta->dummy &&
ieee80211_is_data_present(hdr->frame_control)) {
u16 ethertype;
u8 *payload;

payload = rx->skb->data +
ieee80211_hdrlen(hdr->frame_control);
ethertype = (payload[6] << 8) | payload[7];
if (cpu_to_be16(ethertype) ==
rx->sdata->control_port_protocol)
return RX_CONTINUE;
}
return RX_DROP_MONITOR;
}

return RX_CONTINUE;
}
Expand Down Expand Up @@ -2808,7 +2821,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
if (ieee80211_is_data(fc)) {
prev_sta = NULL;

for_each_sta_info(local, hdr->addr2, sta, tmp) {
for_each_sta_info_rx(local, hdr->addr2, sta, tmp) {
if (!prev_sta) {
prev_sta = sta;
continue;
Expand Down Expand Up @@ -2852,15 +2865,15 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
continue;
}

rx.sta = sta_info_get_bss(prev, hdr->addr2);
rx.sta = sta_info_get_bss_rx(prev, hdr->addr2);
rx.sdata = prev;
ieee80211_prepare_and_rx_handle(&rx, skb, false);

prev = sdata;
}

if (prev) {
rx.sta = sta_info_get_bss(prev, hdr->addr2);
rx.sta = sta_info_get_bss_rx(prev, hdr->addr2);
rx.sdata = prev;

if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
Expand Down
Loading

0 comments on commit 2a33bee

Please sign in to comment.