aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
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
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')
-rw-r--r--net/mac80211/ieee80211.c99
-rw-r--r--net/mac80211/ieee80211_i.h1
-rw-r--r--net/mac80211/ieee80211_iface.c22
-rw-r--r--net/mac80211/ieee80211_ioctl.c18
-rw-r--r--net/mac80211/sta_info.c14
-rw-r--r--net/mac80211/sta_info.h2
6 files changed, 73 insertions, 83 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,
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 8e440c5706dd..7f10ff5d4a0b 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -858,7 +858,6 @@ int ieee80211_hw_config(struct ieee80211_local *local);
858int ieee80211_if_config(struct net_device *dev); 858int ieee80211_if_config(struct net_device *dev);
859int ieee80211_if_config_beacon(struct net_device *dev); 859int ieee80211_if_config_beacon(struct net_device *dev);
860void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx); 860void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx);
861int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr);
862void ieee80211_if_setup(struct net_device *dev); 861void ieee80211_if_setup(struct net_device *dev);
863int ieee80211_hw_config_ht(struct ieee80211_local *local, int enable_ht, 862int ieee80211_hw_config_ht(struct ieee80211_local *local, int enable_ht,
864 struct ieee80211_ht_info *req_ht_cap, 863 struct ieee80211_ht_info *req_ht_cap,
diff --git a/net/mac80211/ieee80211_iface.c b/net/mac80211/ieee80211_iface.c
index 98b22736e883..80954a512185 100644
--- a/net/mac80211/ieee80211_iface.c
+++ b/net/mac80211/ieee80211_iface.c
@@ -187,8 +187,8 @@ void ieee80211_if_reinit(struct net_device *dev)
187{ 187{
188 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); 188 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
189 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 189 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
190 struct sta_info *sta;
191 struct sk_buff *skb; 190 struct sk_buff *skb;
191 int flushed;
192 192
193 ASSERT_RTNL(); 193 ASSERT_RTNL();
194 194
@@ -240,21 +240,7 @@ void ieee80211_if_reinit(struct net_device *dev)
240 break; 240 break;
241 } 241 }
242 case IEEE80211_IF_TYPE_WDS: 242 case IEEE80211_IF_TYPE_WDS:
243 rcu_read_lock(); 243 /* nothing to do */
244 sta = sta_info_get(local, sdata->u.wds.remote_addr);
245 if (sta) {
246 sta_info_unlink(&sta);
247 } else {
248#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
249 printk(KERN_DEBUG "%s: Someone had deleted my STA "
250 "entry for the WDS link\n", dev->name);
251#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
252 }
253 rcu_read_unlock();
254 if (sta) {
255 synchronize_rcu();
256 sta_info_destroy(sta);
257 }
258 break; 244 break;
259 case IEEE80211_IF_TYPE_MESH_POINT: 245 case IEEE80211_IF_TYPE_MESH_POINT:
260 case IEEE80211_IF_TYPE_STA: 246 case IEEE80211_IF_TYPE_STA:
@@ -279,8 +265,8 @@ void ieee80211_if_reinit(struct net_device *dev)
279 break; 265 break;
280 } 266 }
281 267
282 /* remove all STAs that are bound to this virtual interface */ 268 flushed = sta_info_flush(local, sdata);
283 sta_info_flush(local, sdata); 269 WARN_ON(flushed);
284 270
285 memset(&sdata->u, 0, sizeof(sdata->u)); 271 memset(&sdata->u, 0, sizeof(sdata->u));
286 ieee80211_if_sdata_init(sdata); 272 ieee80211_if_sdata_init(sdata);
diff --git a/net/mac80211/ieee80211_ioctl.c b/net/mac80211/ieee80211_ioctl.c
index 5147152b9268..1d91575a0fe9 100644
--- a/net/mac80211/ieee80211_ioctl.c
+++ b/net/mac80211/ieee80211_ioctl.c
@@ -468,10 +468,20 @@ static int ieee80211_ioctl_siwap(struct net_device *dev,
468 ieee80211_sta_req_auth(dev, &sdata->u.sta); 468 ieee80211_sta_req_auth(dev, &sdata->u.sta);
469 return 0; 469 return 0;
470 } else if (sdata->vif.type == IEEE80211_IF_TYPE_WDS) { 470 } else if (sdata->vif.type == IEEE80211_IF_TYPE_WDS) {
471 if (memcmp(sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data, 471 /*
472 ETH_ALEN) == 0) 472 * If it is necessary to update the WDS peer address
473 return 0; 473 * while the interface is running, then we need to do
474 return ieee80211_if_update_wds(dev, (u8 *) &ap_addr->sa_data); 474 * more work here, namely if it is running we need to
475 * add a new and remove the old STA entry, this is
476 * normally handled by _open() and _stop().
477 */
478 if (netif_running(dev))
479 return -EBUSY;
480
481 memcpy(&sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data,
482 ETH_ALEN);
483
484 return 0;
475 } 485 }
476 486
477 return -EOPNOTSUPP; 487 return -EOPNOTSUPP;
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index a230a9597398..a767042ec4fd 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -258,6 +258,8 @@ int sta_info_insert(struct sta_info *sta)
258 unsigned long flags; 258 unsigned long flags;
259 DECLARE_MAC_BUF(mac); 259 DECLARE_MAC_BUF(mac);
260 260
261 WARN_ON(!netif_running(sdata->dev));
262
261 spin_lock_irqsave(&local->sta_lock, flags); 263 spin_lock_irqsave(&local->sta_lock, flags);
262 /* check if STA exists already */ 264 /* check if STA exists already */
263 if (__sta_info_find(local, sta->addr)) { 265 if (__sta_info_find(local, sta->addr)) {
@@ -608,14 +610,18 @@ void sta_info_stop(struct ieee80211_local *local)
608 610
609/** 611/**
610 * sta_info_flush - flush matching STA entries from the STA table 612 * sta_info_flush - flush matching STA entries from the STA table
613 *
614 * Returns the number of removed STA entries.
615 *
611 * @local: local interface data 616 * @local: local interface data
612 * @sdata: matching rule for the net device (sta->dev) or %NULL to match all STAs 617 * @sdata: matching rule for the net device (sta->dev) or %NULL to match all STAs
613 */ 618 */
614void sta_info_flush(struct ieee80211_local *local, 619int sta_info_flush(struct ieee80211_local *local,
615 struct ieee80211_sub_if_data *sdata) 620 struct ieee80211_sub_if_data *sdata)
616{ 621{
617 struct sta_info *sta, *tmp; 622 struct sta_info *sta, *tmp;
618 LIST_HEAD(tmp_list); 623 LIST_HEAD(tmp_list);
624 int ret = 0;
619 unsigned long flags; 625 unsigned long flags;
620 626
621 might_sleep(); 627 might_sleep();
@@ -624,8 +630,10 @@ void sta_info_flush(struct ieee80211_local *local,
624 list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { 630 list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
625 if (!sdata || sdata == sta->sdata) { 631 if (!sdata || sdata == sta->sdata) {
626 __sta_info_unlink(&sta); 632 __sta_info_unlink(&sta);
627 if (sta) 633 if (sta) {
628 list_add_tail(&sta->list, &tmp_list); 634 list_add_tail(&sta->list, &tmp_list);
635 ret++;
636 }
629 } 637 }
630 } 638 }
631 spin_unlock_irqrestore(&local->sta_lock, flags); 639 spin_unlock_irqrestore(&local->sta_lock, flags);
@@ -634,4 +642,6 @@ void sta_info_flush(struct ieee80211_local *local,
634 642
635 list_for_each_entry_safe(sta, tmp, &tmp_list, list) 643 list_for_each_entry_safe(sta, tmp, &tmp_list, list)
636 sta_info_destroy(sta); 644 sta_info_destroy(sta);
645
646 return ret;
637} 647}
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index fb5411d01f82..547bfc3b2431 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -339,7 +339,7 @@ void sta_info_clear_tim_bit(struct sta_info *sta);
339void sta_info_init(struct ieee80211_local *local); 339void sta_info_init(struct ieee80211_local *local);
340int sta_info_start(struct ieee80211_local *local); 340int sta_info_start(struct ieee80211_local *local);
341void sta_info_stop(struct ieee80211_local *local); 341void sta_info_stop(struct ieee80211_local *local);
342void sta_info_flush(struct ieee80211_local *local, 342int sta_info_flush(struct ieee80211_local *local,
343 struct ieee80211_sub_if_data *sdata); 343 struct ieee80211_sub_if_data *sdata);
344 344
345#endif /* STA_INFO_H */ 345#endif /* STA_INFO_H */