From: Johannes Berg <johannes@sipsolutions.net>
To: John Linville <linville@tuxdriver.com>
Cc: Luis Carlos Cobo <luisca@cozybit.com>,
linux-wireless <linux-wireless@vger.kernel.org>
Subject: [PATCH v3] mac80211: atomically check whether STA exists already
Date: Thu, 21 Feb 2008 14:09:30 +0100 [thread overview]
Message-ID: <1203599370.4427.3.camel@johannes.berg> (raw)
In-Reply-To: <1203588582.17534.141.camel@johannes.berg> (sfid-20080221_100953_696094_F22A8795)
Subject: mac80211: atomically check whether STA exists already
When a STA structure is added, it is often checked whether it
already exists before adding it. This, however, isn't done
atomically so there is a race condition that could lead to two
STA structures being added with the same MAC address. This
patch changes sta_info_add() to return an ERR_PTR in case
of failure and adds the failure mode -EEXIST when the STA
already exists.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Luis Carlos Cobo <luisca@cozybit.com>
---
Luis, I don't think the check for max # of STA structs is important
enough to verify, it is only done for IBSS and it can't really
happen that we add too many because we process incoming packets in
order. If you get problems with mesh there feel free to add a strict
check for the maximum number within the locked section in
sta_info_add().
v2 fixes a small %d vs. %ld warning.
v3 fixes the crash when a STA isn't found, sorry
net/mac80211/cfg.c | 11 ++---------
net/mac80211/ieee80211.c | 4 ++--
net/mac80211/ieee80211_sta.c | 6 +++---
net/mac80211/sta_info.c | 38 +++++++++++++++++++++++++++-----------
net/mac80211/sta_info.h | 4 ++--
5 files changed, 36 insertions(+), 27 deletions(-)
--- everything.orig/net/mac80211/sta_info.c 2008-02-21 11:04:18.000000000 +0100
+++ everything/net/mac80211/sta_info.c 2008-02-21 14:03:49.000000000 +0100
@@ -55,19 +55,29 @@ static int sta_info_hash_del(struct ieee
return -ENOENT;
}
-struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr)
+/* must hold local->sta_lock */
+static struct sta_info *__sta_info_find(struct ieee80211_local *local,
+ u8 *addr)
{
struct sta_info *sta;
- read_lock_bh(&local->sta_lock);
sta = local->sta_hash[STA_HASH(addr)];
while (sta) {
- if (memcmp(sta->addr, addr, ETH_ALEN) == 0) {
- __sta_info_get(sta);
+ if (compare_ether_addr(sta->addr, addr) == 0)
break;
- }
sta = sta->hnext;
}
+ return sta;
+}
+
+struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr)
+{
+ struct sta_info *sta;
+
+ read_lock_bh(&local->sta_lock);
+ sta = __sta_info_find(local, addr);
+ if (sta)
+ __sta_info_get(sta);
read_unlock_bh(&local->sta_lock);
return sta;
@@ -110,8 +120,8 @@ void sta_info_put(struct sta_info *sta)
EXPORT_SYMBOL(sta_info_put);
-struct sta_info * sta_info_add(struct ieee80211_local *local,
- struct net_device *dev, u8 *addr, gfp_t gfp)
+struct sta_info *sta_info_add(struct ieee80211_local *local,
+ struct net_device *dev, u8 *addr, gfp_t gfp)
{
struct sta_info *sta;
int i;
@@ -119,7 +129,7 @@ struct sta_info * sta_info_add(struct ie
sta = kzalloc(sizeof(*sta), gfp);
if (!sta)
- return NULL;
+ return ERR_PTR(-ENOMEM);
kref_init(&sta->kref);
@@ -128,7 +138,7 @@ struct sta_info * sta_info_add(struct ie
if (!sta->rate_ctrl_priv) {
rate_control_put(sta->rate_ctrl);
kfree(sta);
- return NULL;
+ return ERR_PTR(-ENOMEM);
}
memcpy(sta->addr, addr, ETH_ALEN);
@@ -158,9 +168,15 @@ struct sta_info * sta_info_add(struct ie
}
skb_queue_head_init(&sta->ps_tx_buf);
skb_queue_head_init(&sta->tx_filtered);
- __sta_info_get(sta); /* sta used by caller, decremented by
- * sta_info_put() */
write_lock_bh(&local->sta_lock);
+ /* mark sta as used (by caller) */
+ __sta_info_get(sta);
+ /* check if STA exists already */
+ if (__sta_info_find(local, addr)) {
+ write_unlock_bh(&local->sta_lock);
+ sta_info_put(sta);
+ return ERR_PTR(-EEXIST);
+ }
list_add(&sta->list, &local->sta_list);
local->num_sta++;
sta_info_hash_add(local, sta);
--- everything.orig/net/mac80211/sta_info.h 2008-02-21 11:08:39.000000000 +0100
+++ everything/net/mac80211/sta_info.h 2008-02-21 11:08:46.000000000 +0100
@@ -239,8 +239,8 @@ static inline void __sta_info_get(struct
struct sta_info * sta_info_get(struct ieee80211_local *local, u8 *addr);
void sta_info_put(struct sta_info *sta);
-struct sta_info * sta_info_add(struct ieee80211_local *local,
- struct net_device *dev, u8 *addr, gfp_t gfp);
+struct sta_info *sta_info_add(struct ieee80211_local *local,
+ struct net_device *dev, u8 *addr, gfp_t gfp);
void sta_info_remove(struct sta_info *sta);
void sta_info_free(struct sta_info *sta);
void sta_info_init(struct ieee80211_local *local);
--- everything.orig/net/mac80211/cfg.c 2008-02-21 11:04:29.000000000 +0100
+++ everything/net/mac80211/cfg.c 2008-02-21 11:08:46.000000000 +0100
@@ -568,13 +568,6 @@ static int ieee80211_add_station(struct
if (!netif_running(dev))
return -ENETDOWN;
- /* XXX: get sta belonging to dev */
- sta = sta_info_get(local, mac);
- if (sta) {
- sta_info_put(sta);
- return -EEXIST;
- }
-
if (params->vlan) {
sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
@@ -585,8 +578,8 @@ static int ieee80211_add_station(struct
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
sta = sta_info_add(local, dev, mac, GFP_KERNEL);
- if (!sta)
- return -ENOMEM;
+ if (IS_ERR(sta))
+ return PTR_ERR(sta);
sta->dev = sdata->dev;
if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN ||
--- everything.orig/net/mac80211/ieee80211.c 2008-02-21 11:08:39.000000000 +0100
+++ everything/net/mac80211/ieee80211.c 2008-02-21 14:03:37.000000000 +0100
@@ -838,8 +838,8 @@ int ieee80211_if_update_wds(struct net_d
/* Create STA entry for the new peer */
sta = sta_info_add(local, dev, remote_addr, GFP_KERNEL);
- if (!sta)
- return -ENOMEM;
+ if (IS_ERR(sta))
+ return PTR_ERR(sta);
sta->flags |= WLAN_STA_AUTHORIZED;
--- everything.orig/net/mac80211/ieee80211_sta.c 2008-02-21 11:04:32.000000000 +0100
+++ everything/net/mac80211/ieee80211_sta.c 2008-02-21 11:08:46.000000000 +0100
@@ -1806,9 +1806,9 @@ static void ieee80211_rx_mgmt_assoc_resp
if (!sta) {
struct ieee80211_sta_bss *bss;
sta = sta_info_add(local, dev, ifsta->bssid, GFP_KERNEL);
- if (!sta) {
+ if (IS_ERR(sta)) {
printk(KERN_DEBUG "%s: failed to add STA entry for the"
- " AP\n", dev->name);
+ " AP (error %ld)\n", dev->name, PTR_ERR(sta));
return;
}
bss = ieee80211_rx_bss_get(dev, ifsta->bssid,
@@ -3819,7 +3819,7 @@ struct sta_info * ieee80211_ibss_add_sta
wiphy_name(local->hw.wiphy), print_mac(mac, addr), dev->name);
sta = sta_info_add(local, dev, addr, GFP_ATOMIC);
- if (!sta)
+ if (IS_ERR(sta))
return NULL;
sta->flags |= WLAN_STA_AUTHORIZED;
prev parent reply other threads:[~2008-02-21 19:22 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-02-20 23:24 [PATCH] mac80211: atomically check whether STA exists already Johannes Berg
2008-02-21 10:09 ` [PATCH v2] " Johannes Berg
2008-02-21 13:09 ` Johannes Berg [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1203599370.4427.3.camel@johannes.berg \
--to=johannes@sipsolutions.net \
--cc=linux-wireless@vger.kernel.org \
--cc=linville@tuxdriver.com \
--cc=luisca@cozybit.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox