diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2008-02-21 08:09:30 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-02-29 15:41:34 -0500 |
commit | 43ba7e958f2ca05e4e9171a15402288419289d71 (patch) | |
tree | 15b7a04a7db402dd286f83cc56c21b336189da09 /net | |
parent | d46e144b65bf053b25d134ec9f52a38e63e04bb4 (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.c | 11 | ||||
-rw-r--r-- | net/mac80211/ieee80211.c | 4 | ||||
-rw-r--r-- | net/mac80211/ieee80211_sta.c | 6 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 38 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 4 |
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 | ||
58 | struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr) | 58 | /* must hold local->sta_lock */ |
59 | static 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 | |||
73 | struct 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) | |||
110 | EXPORT_SYMBOL(sta_info_put); | 120 | EXPORT_SYMBOL(sta_info_put); |
111 | 121 | ||
112 | 122 | ||
113 | struct sta_info * sta_info_add(struct ieee80211_local *local, | 123 | struct 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 | ||
240 | struct sta_info * sta_info_get(struct ieee80211_local *local, u8 *addr); | 240 | struct sta_info * sta_info_get(struct ieee80211_local *local, u8 *addr); |
241 | void sta_info_put(struct sta_info *sta); | 241 | void sta_info_put(struct sta_info *sta); |
242 | struct sta_info * sta_info_add(struct ieee80211_local *local, | 242 | struct 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); |
244 | void sta_info_remove(struct sta_info *sta); | 244 | void sta_info_remove(struct sta_info *sta); |
245 | void sta_info_free(struct sta_info *sta); | 245 | void sta_info_free(struct sta_info *sta); |
246 | void sta_info_init(struct ieee80211_local *local); | 246 | void sta_info_init(struct ieee80211_local *local); |