aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2008-02-21 08:09:30 -0500
committerJohn W. Linville <linville@tuxdriver.com>2008-02-29 15:41:34 -0500
commit43ba7e958f2ca05e4e9171a15402288419289d71 (patch)
tree15b7a04a7db402dd286f83cc56c21b336189da09 /net
parentd46e144b65bf053b25d134ec9f52a38e63e04bb4 (diff)
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> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/cfg.c11
-rw-r--r--net/mac80211/ieee80211.c4
-rw-r--r--net/mac80211/ieee80211_sta.c6
-rw-r--r--net/mac80211/sta_info.c38
-rw-r--r--net/mac80211/sta_info.h4
5 files changed, 36 insertions, 27 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index a083cc78855e..b0c41a0cee79 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -562,13 +562,6 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
562 if (!netif_running(dev)) 562 if (!netif_running(dev))
563 return -ENETDOWN; 563 return -ENETDOWN;
564 564
565 /* XXX: get sta belonging to dev */
566 sta = sta_info_get(local, mac);
567 if (sta) {
568 sta_info_put(sta);
569 return -EEXIST;
570 }
571
572 if (params->vlan) { 565 if (params->vlan) {
573 sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); 566 sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
574 567
@@ -579,8 +572,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
579 sdata = IEEE80211_DEV_TO_SUB_IF(dev); 572 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
580 573
581 sta = sta_info_add(local, dev, mac, GFP_KERNEL); 574 sta = sta_info_add(local, dev, mac, GFP_KERNEL);
582 if (!sta) 575 if (IS_ERR(sta))
583 return -ENOMEM; 576 return PTR_ERR(sta);
584 577
585 sta->dev = sdata->dev; 578 sta->dev = sdata->dev;
586 if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN || 579 if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN ||
diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c
index a00858dbab18..f82ebdd53d48 100644
--- a/net/mac80211/ieee80211.c
+++ b/net/mac80211/ieee80211.c
@@ -838,8 +838,8 @@ int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr)
838 838
839 /* Create STA entry for the new peer */ 839 /* Create STA entry for the new peer */
840 sta = sta_info_add(local, dev, remote_addr, GFP_KERNEL); 840 sta = sta_info_add(local, dev, remote_addr, GFP_KERNEL);
841 if (!sta) 841 if (IS_ERR(sta))
842 return -ENOMEM; 842 return PTR_ERR(sta);
843 843
844 sta->flags |= WLAN_STA_AUTHORIZED; 844 sta->flags |= WLAN_STA_AUTHORIZED;
845 845
diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c
index 8d620baba4fd..64476d9e8d77 100644
--- a/net/mac80211/ieee80211_sta.c
+++ b/net/mac80211/ieee80211_sta.c
@@ -1807,9 +1807,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
1807 if (!sta) { 1807 if (!sta) {
1808 struct ieee80211_sta_bss *bss; 1808 struct ieee80211_sta_bss *bss;
1809 sta = sta_info_add(local, dev, ifsta->bssid, GFP_KERNEL); 1809 sta = sta_info_add(local, dev, ifsta->bssid, GFP_KERNEL);
1810 if (!sta) { 1810 if (IS_ERR(sta)) {
1811 printk(KERN_DEBUG "%s: failed to add STA entry for the" 1811 printk(KERN_DEBUG "%s: failed to add STA entry for the"
1812 " AP\n", dev->name); 1812 " AP (error %ld)\n", dev->name, PTR_ERR(sta));
1813 return; 1813 return;
1814 } 1814 }
1815 bss = ieee80211_rx_bss_get(dev, ifsta->bssid, 1815 bss = ieee80211_rx_bss_get(dev, ifsta->bssid,
@@ -3820,7 +3820,7 @@ struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev,
3820 wiphy_name(local->hw.wiphy), print_mac(mac, addr), dev->name); 3820 wiphy_name(local->hw.wiphy), print_mac(mac, addr), dev->name);
3821 3821
3822 sta = sta_info_add(local, dev, addr, GFP_ATOMIC); 3822 sta = sta_info_add(local, dev, addr, GFP_ATOMIC);
3823 if (!sta) 3823 if (IS_ERR(sta))
3824 return NULL; 3824 return NULL;
3825 3825
3826 sta->flags |= WLAN_STA_AUTHORIZED; 3826 sta->flags |= WLAN_STA_AUTHORIZED;
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index b31a627ff97f..c6c0df4bbd2c 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -55,19 +55,29 @@ static int sta_info_hash_del(struct ieee80211_local *local,
55 return -ENOENT; 55 return -ENOENT;
56} 56}
57 57
58struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr) 58/* must hold local->sta_lock */
59static struct sta_info *__sta_info_find(struct ieee80211_local *local,
60 u8 *addr)
59{ 61{
60 struct sta_info *sta; 62 struct sta_info *sta;
61 63
62 read_lock_bh(&local->sta_lock);
63 sta = local->sta_hash[STA_HASH(addr)]; 64 sta = local->sta_hash[STA_HASH(addr)];
64 while (sta) { 65 while (sta) {
65 if (memcmp(sta->addr, addr, ETH_ALEN) == 0) { 66 if (compare_ether_addr(sta->addr, addr) == 0)
66 __sta_info_get(sta);
67 break; 67 break;
68 }
69 sta = sta->hnext; 68 sta = sta->hnext;
70 } 69 }
70 return sta;
71}
72
73struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr)
74{
75 struct sta_info *sta;
76
77 read_lock_bh(&local->sta_lock);
78 sta = __sta_info_find(local, addr);
79 if (sta)
80 __sta_info_get(sta);
71 read_unlock_bh(&local->sta_lock); 81 read_unlock_bh(&local->sta_lock);
72 82
73 return sta; 83 return sta;
@@ -110,8 +120,8 @@ void sta_info_put(struct sta_info *sta)
110EXPORT_SYMBOL(sta_info_put); 120EXPORT_SYMBOL(sta_info_put);
111 121
112 122
113struct sta_info * sta_info_add(struct ieee80211_local *local, 123struct sta_info *sta_info_add(struct ieee80211_local *local,
114 struct net_device *dev, u8 *addr, gfp_t gfp) 124 struct net_device *dev, u8 *addr, gfp_t gfp)
115{ 125{
116 struct sta_info *sta; 126 struct sta_info *sta;
117 int i; 127 int i;
@@ -119,7 +129,7 @@ struct sta_info * sta_info_add(struct ieee80211_local *local,
119 129
120 sta = kzalloc(sizeof(*sta), gfp); 130 sta = kzalloc(sizeof(*sta), gfp);
121 if (!sta) 131 if (!sta)
122 return NULL; 132 return ERR_PTR(-ENOMEM);
123 133
124 kref_init(&sta->kref); 134 kref_init(&sta->kref);
125 135
@@ -128,7 +138,7 @@ struct sta_info * sta_info_add(struct ieee80211_local *local,
128 if (!sta->rate_ctrl_priv) { 138 if (!sta->rate_ctrl_priv) {
129 rate_control_put(sta->rate_ctrl); 139 rate_control_put(sta->rate_ctrl);
130 kfree(sta); 140 kfree(sta);
131 return NULL; 141 return ERR_PTR(-ENOMEM);
132 } 142 }
133 143
134 memcpy(sta->addr, addr, ETH_ALEN); 144 memcpy(sta->addr, addr, ETH_ALEN);
@@ -158,9 +168,15 @@ struct sta_info * sta_info_add(struct ieee80211_local *local,
158 } 168 }
159 skb_queue_head_init(&sta->ps_tx_buf); 169 skb_queue_head_init(&sta->ps_tx_buf);
160 skb_queue_head_init(&sta->tx_filtered); 170 skb_queue_head_init(&sta->tx_filtered);
161 __sta_info_get(sta); /* sta used by caller, decremented by
162 * sta_info_put() */
163 write_lock_bh(&local->sta_lock); 171 write_lock_bh(&local->sta_lock);
172 /* mark sta as used (by caller) */
173 __sta_info_get(sta);
174 /* check if STA exists already */
175 if (__sta_info_find(local, addr)) {
176 write_unlock_bh(&local->sta_lock);
177 sta_info_put(sta);
178 return ERR_PTR(-EEXIST);
179 }
164 list_add(&sta->list, &local->sta_list); 180 list_add(&sta->list, &local->sta_list);
165 local->num_sta++; 181 local->num_sta++;
166 sta_info_hash_add(local, sta); 182 sta_info_hash_add(local, sta);
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index f7e65fa3f9ed..7b5be309fe0a 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -239,8 +239,8 @@ static inline void __sta_info_get(struct sta_info *sta)
239 239
240struct sta_info * sta_info_get(struct ieee80211_local *local, u8 *addr); 240struct sta_info * sta_info_get(struct ieee80211_local *local, u8 *addr);
241void sta_info_put(struct sta_info *sta); 241void sta_info_put(struct sta_info *sta);
242struct sta_info * sta_info_add(struct ieee80211_local *local, 242struct sta_info *sta_info_add(struct ieee80211_local *local,
243 struct net_device *dev, u8 *addr, gfp_t gfp); 243 struct net_device *dev, u8 *addr, gfp_t gfp);
244void sta_info_remove(struct sta_info *sta); 244void sta_info_remove(struct sta_info *sta);
245void sta_info_free(struct sta_info *sta); 245void sta_info_free(struct sta_info *sta);
246void sta_info_init(struct ieee80211_local *local); 246void sta_info_init(struct ieee80211_local *local);