aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/ieee80211.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2008-02-25 10:27:49 -0500
committerJohn W. Linville <linville@tuxdriver.com>2008-03-06 15:30:47 -0500
commit44213b5e13c907bf4aa2e73941944f90184c8772 (patch)
treedb680fc6b4913b072a8f85cf0fac622aea2edb5d /net/mac80211/ieee80211.c
parent693b1bbcc47b3fd436068f294147357f90cd1296 (diff)
mac80211: remove STA entries when taking down interface
When we take down an interface, we need to remove the STA info items that belong to it because otherwise we might invoke a sta_notify() callback in the driver when we later delete the STA entries, but in that case the driver will already have removed its knowledge of the interface they belonged to leading to confusion. Also, we could invoke the set_tim() callback after the driver removed its knowledge of the interface, which can lead to a crash if it requests a beacon with a then-invalid vif pointer! A side effect of this patch is that, because it was easier, it disallows changing the WDS peer while an interface is up. Should that actually be necessary, it can be added back, but the WDS peer STA entry may not be added while the interface is UP so for now I've simplified the WDS peer's STA entry lifetime management. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/ieee80211.c')
-rw-r--r--net/mac80211/ieee80211.c99
1 files changed, 42 insertions, 57 deletions
diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c
index 22cba82a0c6f..484b063a3538 100644
--- a/net/mac80211/ieee80211.c
+++ b/net/mac80211/ieee80211.c
@@ -183,6 +183,7 @@ static int ieee80211_open(struct net_device *dev)
183 struct ieee80211_if_init_conf conf; 183 struct ieee80211_if_init_conf conf;
184 int res; 184 int res;
185 bool need_hw_reconfig = 0; 185 bool need_hw_reconfig = 0;
186 struct sta_info *sta;
186 187
187 sdata = IEEE80211_DEV_TO_SUB_IF(dev); 188 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
188 189
@@ -256,6 +257,20 @@ static int ieee80211_open(struct net_device *dev)
256 case IEEE80211_IF_TYPE_WDS: 257 case IEEE80211_IF_TYPE_WDS:
257 if (is_zero_ether_addr(sdata->u.wds.remote_addr)) 258 if (is_zero_ether_addr(sdata->u.wds.remote_addr))
258 return -ENOLINK; 259 return -ENOLINK;
260
261 /* Create STA entry for the WDS peer */
262 sta = sta_info_alloc(sdata, sdata->u.wds.remote_addr,
263 GFP_KERNEL);
264 if (!sta)
265 return -ENOMEM;
266
267 sta->flags |= WLAN_STA_AUTHORIZED;
268
269 res = sta_info_insert(sta);
270 if (res) {
271 sta_info_destroy(sta);
272 return res;
273 }
259 break; 274 break;
260 case IEEE80211_IF_TYPE_VLAN: 275 case IEEE80211_IF_TYPE_VLAN:
261 if (!sdata->u.vlan.ap) 276 if (!sdata->u.vlan.ap)
@@ -367,14 +382,20 @@ static int ieee80211_open(struct net_device *dev)
367 382
368static int ieee80211_stop(struct net_device *dev) 383static int ieee80211_stop(struct net_device *dev)
369{ 384{
370 struct ieee80211_sub_if_data *sdata; 385 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
371 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); 386 struct ieee80211_local *local = sdata->local;
372 struct ieee80211_if_init_conf conf; 387 struct ieee80211_if_init_conf conf;
373 struct sta_info *sta; 388 struct sta_info *sta;
374 int i; 389 int i;
375 390
376 sdata = IEEE80211_DEV_TO_SUB_IF(dev); 391 /*
392 * Stop TX on this interface first.
393 */
394 netif_stop_queue(dev);
377 395
396 /*
397 * Now delete all active aggregation sessions.
398 */
378 rcu_read_lock(); 399 rcu_read_lock();
379 400
380 list_for_each_entry_rcu(sta, &local->sta_list, list) { 401 list_for_each_entry_rcu(sta, &local->sta_list, list) {
@@ -388,7 +409,24 @@ static int ieee80211_stop(struct net_device *dev)
388 409
389 rcu_read_unlock(); 410 rcu_read_unlock();
390 411
391 netif_stop_queue(dev); 412 /*
413 * Remove all stations associated with this interface.
414 *
415 * This must be done before calling ops->remove_interface()
416 * because otherwise we can later invoke ops->sta_notify()
417 * whenever the STAs are removed, and that invalidates driver
418 * assumptions about always getting a vif pointer that is valid
419 * (because if we remove a STA after ops->remove_interface()
420 * the driver will have removed the vif info already!)
421 *
422 * We could relax this and only unlink the stations from the
423 * hash table and list but keep them on a per-sdata list that
424 * will be inserted back again when the interface is brought
425 * up again, but I don't currently see a use case for that,
426 * except with WDS which gets a STA entry created when it is
427 * brought up.
428 */
429 sta_info_flush(local, sdata);
392 430
393 /* 431 /*
394 * Don't count this interface for promisc/allmulti while it 432 * Don't count this interface for promisc/allmulti while it
@@ -453,8 +491,6 @@ static int ieee80211_stop(struct net_device *dev)
453 netif_tx_unlock_bh(local->mdev); 491 netif_tx_unlock_bh(local->mdev);
454 break; 492 break;
455 case IEEE80211_IF_TYPE_MESH_POINT: 493 case IEEE80211_IF_TYPE_MESH_POINT:
456 sta_info_flush(local, sdata);
457 /* fall through */
458 case IEEE80211_IF_TYPE_STA: 494 case IEEE80211_IF_TYPE_STA:
459 case IEEE80211_IF_TYPE_IBSS: 495 case IEEE80211_IF_TYPE_IBSS:
460 sdata->u.sta.state = IEEE80211_DISABLED; 496 sdata->u.sta.state = IEEE80211_DISABLED;
@@ -892,57 +928,6 @@ void ieee80211_if_setup(struct net_device *dev)
892 dev->destructor = ieee80211_if_free; 928 dev->destructor = ieee80211_if_free;
893} 929}
894 930
895/* WDS specialties */
896
897int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr)
898{
899 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
900 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
901 struct sta_info *sta;
902 int err;
903 DECLARE_MAC_BUF(mac);
904
905 might_sleep();
906
907 if (compare_ether_addr(remote_addr, sdata->u.wds.remote_addr) == 0)
908 return 0;
909
910 /* Create STA entry for the new peer */
911 sta = sta_info_alloc(sdata, remote_addr, GFP_KERNEL);
912 if (!sta)
913 return -ENOMEM;
914
915 sta->flags |= WLAN_STA_AUTHORIZED;
916 err = sta_info_insert(sta);
917 if (err) {
918 sta_info_destroy(sta);
919 return err;
920 }
921
922 rcu_read_lock();
923
924 /* Remove STA entry for the old peer */
925 sta = sta_info_get(local, sdata->u.wds.remote_addr);
926 if (sta)
927 sta_info_unlink(&sta);
928 else
929 printk(KERN_DEBUG "%s: could not find STA entry for WDS link "
930 "peer %s\n",
931 dev->name, print_mac(mac, sdata->u.wds.remote_addr));
932
933 /* Update WDS link data */
934 memcpy(&sdata->u.wds.remote_addr, remote_addr, ETH_ALEN);
935
936 rcu_read_unlock();
937
938 if (sta) {
939 synchronize_rcu();
940 sta_info_destroy(sta);
941 }
942
943 return 0;
944}
945
946/* everything else */ 931/* everything else */
947 932
948static int __ieee80211_if_config(struct net_device *dev, 933static int __ieee80211_if_config(struct net_device *dev,