diff options
Diffstat (limited to 'net/mac80211/iface.c')
-rw-r--r-- | net/mac80211/iface.c | 174 |
1 files changed, 147 insertions, 27 deletions
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 1afa9ec81fe8..910729fc18cd 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -339,7 +339,6 @@ static int ieee80211_stop(struct net_device *dev) | |||
339 | { | 339 | { |
340 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 340 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
341 | struct ieee80211_local *local = sdata->local; | 341 | struct ieee80211_local *local = sdata->local; |
342 | struct sta_info *sta; | ||
343 | unsigned long flags; | 342 | unsigned long flags; |
344 | struct sk_buff *skb, *tmp; | 343 | struct sk_buff *skb, *tmp; |
345 | u32 hw_reconf_flags = 0; | 344 | u32 hw_reconf_flags = 0; |
@@ -356,18 +355,6 @@ static int ieee80211_stop(struct net_device *dev) | |||
356 | ieee80211_work_purge(sdata); | 355 | ieee80211_work_purge(sdata); |
357 | 356 | ||
358 | /* | 357 | /* |
359 | * Now delete all active aggregation sessions. | ||
360 | */ | ||
361 | rcu_read_lock(); | ||
362 | |||
363 | list_for_each_entry_rcu(sta, &local->sta_list, list) { | ||
364 | if (sta->sdata == sdata) | ||
365 | ieee80211_sta_tear_down_BA_sessions(sta); | ||
366 | } | ||
367 | |||
368 | rcu_read_unlock(); | ||
369 | |||
370 | /* | ||
371 | * Remove all stations associated with this interface. | 358 | * Remove all stations associated with this interface. |
372 | * | 359 | * |
373 | * This must be done before calling ops->remove_interface() | 360 | * This must be done before calling ops->remove_interface() |
@@ -473,27 +460,14 @@ static int ieee80211_stop(struct net_device *dev) | |||
473 | * whether the interface is running, which, at this point, | 460 | * whether the interface is running, which, at this point, |
474 | * it no longer is. | 461 | * it no longer is. |
475 | */ | 462 | */ |
476 | cancel_work_sync(&sdata->u.mgd.work); | ||
477 | cancel_work_sync(&sdata->u.mgd.chswitch_work); | 463 | cancel_work_sync(&sdata->u.mgd.chswitch_work); |
478 | cancel_work_sync(&sdata->u.mgd.monitor_work); | 464 | cancel_work_sync(&sdata->u.mgd.monitor_work); |
479 | cancel_work_sync(&sdata->u.mgd.beacon_connection_loss_work); | 465 | cancel_work_sync(&sdata->u.mgd.beacon_connection_loss_work); |
480 | 466 | ||
481 | /* | ||
482 | * When we get here, the interface is marked down. | ||
483 | * Call synchronize_rcu() to wait for the RX path | ||
484 | * should it be using the interface and enqueuing | ||
485 | * frames at this very time on another CPU. | ||
486 | */ | ||
487 | synchronize_rcu(); | ||
488 | skb_queue_purge(&sdata->u.mgd.skb_queue); | ||
489 | /* fall through */ | 467 | /* fall through */ |
490 | case NL80211_IFTYPE_ADHOC: | 468 | case NL80211_IFTYPE_ADHOC: |
491 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { | 469 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) |
492 | del_timer_sync(&sdata->u.ibss.timer); | 470 | del_timer_sync(&sdata->u.ibss.timer); |
493 | cancel_work_sync(&sdata->u.ibss.work); | ||
494 | synchronize_rcu(); | ||
495 | skb_queue_purge(&sdata->u.ibss.skb_queue); | ||
496 | } | ||
497 | /* fall through */ | 471 | /* fall through */ |
498 | case NL80211_IFTYPE_MESH_POINT: | 472 | case NL80211_IFTYPE_MESH_POINT: |
499 | if (ieee80211_vif_is_mesh(&sdata->vif)) { | 473 | if (ieee80211_vif_is_mesh(&sdata->vif)) { |
@@ -508,6 +482,16 @@ static int ieee80211_stop(struct net_device *dev) | |||
508 | } | 482 | } |
509 | /* fall through */ | 483 | /* fall through */ |
510 | default: | 484 | default: |
485 | flush_work(&sdata->work); | ||
486 | /* | ||
487 | * When we get here, the interface is marked down. | ||
488 | * Call synchronize_rcu() to wait for the RX path | ||
489 | * should it be using the interface and enqueuing | ||
490 | * frames at this very time on another CPU. | ||
491 | */ | ||
492 | synchronize_rcu(); | ||
493 | skb_queue_purge(&sdata->skb_queue); | ||
494 | |||
511 | if (local->scan_sdata == sdata) | 495 | if (local->scan_sdata == sdata) |
512 | ieee80211_scan_cancel(local); | 496 | ieee80211_scan_cancel(local); |
513 | 497 | ||
@@ -717,6 +701,136 @@ static void ieee80211_if_setup(struct net_device *dev) | |||
717 | dev->destructor = free_netdev; | 701 | dev->destructor = free_netdev; |
718 | } | 702 | } |
719 | 703 | ||
704 | static void ieee80211_iface_work(struct work_struct *work) | ||
705 | { | ||
706 | struct ieee80211_sub_if_data *sdata = | ||
707 | container_of(work, struct ieee80211_sub_if_data, work); | ||
708 | struct ieee80211_local *local = sdata->local; | ||
709 | struct sk_buff *skb; | ||
710 | struct sta_info *sta; | ||
711 | struct ieee80211_ra_tid *ra_tid; | ||
712 | |||
713 | if (!ieee80211_sdata_running(sdata)) | ||
714 | return; | ||
715 | |||
716 | if (local->scanning) | ||
717 | return; | ||
718 | |||
719 | /* | ||
720 | * ieee80211_queue_work() should have picked up most cases, | ||
721 | * here we'll pick the rest. | ||
722 | */ | ||
723 | if (WARN(local->suspended, | ||
724 | "interface work scheduled while going to suspend\n")) | ||
725 | return; | ||
726 | |||
727 | /* first process frames */ | ||
728 | while ((skb = skb_dequeue(&sdata->skb_queue))) { | ||
729 | struct ieee80211_mgmt *mgmt = (void *)skb->data; | ||
730 | |||
731 | if (skb->pkt_type == IEEE80211_SDATA_QUEUE_AGG_START) { | ||
732 | ra_tid = (void *)&skb->cb; | ||
733 | ieee80211_start_tx_ba_cb(&sdata->vif, ra_tid->ra, | ||
734 | ra_tid->tid); | ||
735 | } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_AGG_STOP) { | ||
736 | ra_tid = (void *)&skb->cb; | ||
737 | ieee80211_stop_tx_ba_cb(&sdata->vif, ra_tid->ra, | ||
738 | ra_tid->tid); | ||
739 | } else if (ieee80211_is_action(mgmt->frame_control) && | ||
740 | mgmt->u.action.category == WLAN_CATEGORY_BACK) { | ||
741 | int len = skb->len; | ||
742 | |||
743 | mutex_lock(&local->sta_mtx); | ||
744 | sta = sta_info_get(sdata, mgmt->sa); | ||
745 | if (sta) { | ||
746 | switch (mgmt->u.action.u.addba_req.action_code) { | ||
747 | case WLAN_ACTION_ADDBA_REQ: | ||
748 | ieee80211_process_addba_request( | ||
749 | local, sta, mgmt, len); | ||
750 | break; | ||
751 | case WLAN_ACTION_ADDBA_RESP: | ||
752 | ieee80211_process_addba_resp(local, sta, | ||
753 | mgmt, len); | ||
754 | break; | ||
755 | case WLAN_ACTION_DELBA: | ||
756 | ieee80211_process_delba(sdata, sta, | ||
757 | mgmt, len); | ||
758 | break; | ||
759 | default: | ||
760 | WARN_ON(1); | ||
761 | break; | ||
762 | } | ||
763 | } | ||
764 | mutex_unlock(&local->sta_mtx); | ||
765 | } else if (ieee80211_is_data_qos(mgmt->frame_control)) { | ||
766 | struct ieee80211_hdr *hdr = (void *)mgmt; | ||
767 | /* | ||
768 | * So the frame isn't mgmt, but frame_control | ||
769 | * is at the right place anyway, of course, so | ||
770 | * the if statement is correct. | ||
771 | * | ||
772 | * Warn if we have other data frame types here, | ||
773 | * they must not get here. | ||
774 | */ | ||
775 | WARN_ON(hdr->frame_control & | ||
776 | cpu_to_le16(IEEE80211_STYPE_NULLFUNC)); | ||
777 | WARN_ON(!(hdr->seq_ctrl & | ||
778 | cpu_to_le16(IEEE80211_SCTL_FRAG))); | ||
779 | /* | ||
780 | * This was a fragment of a frame, received while | ||
781 | * a block-ack session was active. That cannot be | ||
782 | * right, so terminate the session. | ||
783 | */ | ||
784 | mutex_lock(&local->sta_mtx); | ||
785 | sta = sta_info_get(sdata, mgmt->sa); | ||
786 | if (sta) { | ||
787 | u16 tid = *ieee80211_get_qos_ctl(hdr) & | ||
788 | IEEE80211_QOS_CTL_TID_MASK; | ||
789 | |||
790 | __ieee80211_stop_rx_ba_session( | ||
791 | sta, tid, WLAN_BACK_RECIPIENT, | ||
792 | WLAN_REASON_QSTA_REQUIRE_SETUP); | ||
793 | } | ||
794 | mutex_unlock(&local->sta_mtx); | ||
795 | } else switch (sdata->vif.type) { | ||
796 | case NL80211_IFTYPE_STATION: | ||
797 | ieee80211_sta_rx_queued_mgmt(sdata, skb); | ||
798 | break; | ||
799 | case NL80211_IFTYPE_ADHOC: | ||
800 | ieee80211_ibss_rx_queued_mgmt(sdata, skb); | ||
801 | break; | ||
802 | case NL80211_IFTYPE_MESH_POINT: | ||
803 | if (!ieee80211_vif_is_mesh(&sdata->vif)) | ||
804 | break; | ||
805 | ieee80211_mesh_rx_queued_mgmt(sdata, skb); | ||
806 | break; | ||
807 | default: | ||
808 | WARN(1, "frame for unexpected interface type"); | ||
809 | break; | ||
810 | } | ||
811 | |||
812 | kfree_skb(skb); | ||
813 | } | ||
814 | |||
815 | /* then other type-dependent work */ | ||
816 | switch (sdata->vif.type) { | ||
817 | case NL80211_IFTYPE_STATION: | ||
818 | ieee80211_sta_work(sdata); | ||
819 | break; | ||
820 | case NL80211_IFTYPE_ADHOC: | ||
821 | ieee80211_ibss_work(sdata); | ||
822 | break; | ||
823 | case NL80211_IFTYPE_MESH_POINT: | ||
824 | if (!ieee80211_vif_is_mesh(&sdata->vif)) | ||
825 | break; | ||
826 | ieee80211_mesh_work(sdata); | ||
827 | break; | ||
828 | default: | ||
829 | break; | ||
830 | } | ||
831 | } | ||
832 | |||
833 | |||
720 | /* | 834 | /* |
721 | * Helper function to initialise an interface to a specific type. | 835 | * Helper function to initialise an interface to a specific type. |
722 | */ | 836 | */ |
@@ -734,6 +848,9 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, | |||
734 | /* only monitor differs */ | 848 | /* only monitor differs */ |
735 | sdata->dev->type = ARPHRD_ETHER; | 849 | sdata->dev->type = ARPHRD_ETHER; |
736 | 850 | ||
851 | skb_queue_head_init(&sdata->skb_queue); | ||
852 | INIT_WORK(&sdata->work, ieee80211_iface_work); | ||
853 | |||
737 | switch (type) { | 854 | switch (type) { |
738 | case NL80211_IFTYPE_AP: | 855 | case NL80211_IFTYPE_AP: |
739 | skb_queue_head_init(&sdata->u.ap.ps_bc_buf); | 856 | skb_queue_head_init(&sdata->u.ap.ps_bc_buf); |
@@ -959,6 +1076,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, | |||
959 | sdata->wdev.wiphy = local->hw.wiphy; | 1076 | sdata->wdev.wiphy = local->hw.wiphy; |
960 | sdata->local = local; | 1077 | sdata->local = local; |
961 | sdata->dev = ndev; | 1078 | sdata->dev = ndev; |
1079 | #ifdef CONFIG_INET | ||
1080 | sdata->arp_filter_state = true; | ||
1081 | #endif | ||
962 | 1082 | ||
963 | for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) | 1083 | for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) |
964 | skb_queue_head_init(&sdata->fragments[i].skb_list); | 1084 | skb_queue_head_init(&sdata->fragments[i].skb_list); |