mac80211: move blockack stop due to fragmentation
[pandora-kernel.git] / net / mac80211 / iface.c
index 7a3dbde..87fc012 100644 (file)
@@ -707,6 +707,7 @@ static void ieee80211_iface_work(struct work_struct *work)
                container_of(work, struct ieee80211_sub_if_data, work);
        struct ieee80211_local *local = sdata->local;
        struct sk_buff *skb;
+       struct sta_info *sta;
 
        if (!ieee80211_sdata_running(sdata))
                return;
@@ -729,7 +730,6 @@ static void ieee80211_iface_work(struct work_struct *work)
                if (ieee80211_is_action(mgmt->frame_control) &&
                    mgmt->u.action.category == WLAN_CATEGORY_BACK) {
                        int len = skb->len;
-                       struct sta_info *sta;
 
                        rcu_read_lock();
                        sta = sta_info_get(sdata, mgmt->sa);
@@ -753,6 +753,36 @@ static void ieee80211_iface_work(struct work_struct *work)
                                }
                        }
                        rcu_read_unlock();
+               } else if (ieee80211_is_data_qos(mgmt->frame_control)) {
+                       struct ieee80211_hdr *hdr = (void *)mgmt;
+                       /*
+                        * So the frame isn't mgmt, but frame_control
+                        * is at the right place anyway, of course, so
+                        * the if statement is correct.
+                        *
+                        * Warn if we have other data frame types here,
+                        * they must not get here.
+                        */
+                       WARN_ON(hdr->frame_control &
+                                       cpu_to_le16(IEEE80211_STYPE_NULLFUNC));
+                       WARN_ON(!(hdr->seq_ctrl &
+                                       cpu_to_le16(IEEE80211_SCTL_FRAG)));
+                       /*
+                        * This was a fragment of a frame, received while
+                        * a block-ack session was active. That cannot be
+                        * right, so terminate the session.
+                        */
+                       rcu_read_lock();
+                       sta = sta_info_get(sdata, mgmt->sa);
+                       if (sta) {
+                               u16 tid = *ieee80211_get_qos_ctl(hdr) &
+                                               IEEE80211_QOS_CTL_TID_MASK;
+
+                               __ieee80211_stop_rx_ba_session(
+                                       sta, tid, WLAN_BACK_RECIPIENT,
+                                       WLAN_REASON_QSTA_REQUIRE_SETUP);
+                       }
+                       rcu_read_unlock();
                } else switch (sdata->vif.type) {
                case NL80211_IFTYPE_STATION:
                        ieee80211_sta_rx_queued_mgmt(sdata, skb);