summaryrefslogtreecommitdiff
path: root/net/mac80211/iface.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/iface.c')
-rw-r--r--net/mac80211/iface.c32
1 files changed, 31 insertions, 1 deletions
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 7a3dbde..87fc012 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -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);