summaryrefslogtreecommitdiffstats
path: root/net/mac80211/status.c
diff options
context:
space:
mode:
authorJohannes Berg2015-06-01 22:54:13 +0200
committerJohannes Berg2015-06-02 13:07:55 +0200
commitb2eb0ee6d0220b47a1f901e4f1c56dd594509d8e (patch)
treeccc9c12165a9d44b3fccbbd3193cd38d555a69dc /net/mac80211/status.c
parentmac80211: move TX PN to public part of key struct (diff)
downloadkernel-qcow2-linux-b2eb0ee6d0220b47a1f901e4f1c56dd594509d8e.tar.gz
kernel-qcow2-linux-b2eb0ee6d0220b47a1f901e4f1c56dd594509d8e.tar.xz
kernel-qcow2-linux-b2eb0ee6d0220b47a1f901e4f1c56dd594509d8e.zip
mac80211: copy nl80211 mgmt TX SKB for status
When we return the TX status for an nl80211 mgmt TX SKB, we should also return the original frame with the status to allow userspace to match up the submission (it could also use the cookie but both ways are permissible.) As TX SKBs could be encrypted, at least in the case of ANQP while associated with the AP, copy the original SKB, store it with an ACK frame ID and restructure the status path to use that to return status with the original SKB. Otherwise, userspace (in particular wpa_supplicant) will get confused. Reported-by: Matti Gottlieb <matti.gottlieb@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/status.c')
-rw-r--r--net/mac80211/status.c109
1 files changed, 68 insertions, 41 deletions
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 461594966b65..56b73e012757 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -429,6 +429,66 @@ static void ieee80211_tdls_td_tx_handle(struct ieee80211_local *local,
}
}
+static struct ieee80211_sub_if_data *
+ieee80211_sdata_from_skb(struct ieee80211_local *local, struct sk_buff *skb)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ if (skb->dev) {
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (!sdata->dev)
+ continue;
+
+ if (skb->dev == sdata->dev)
+ return sdata;
+ }
+
+ return NULL;
+ }
+
+ return rcu_dereference(local->p2p_sdata);
+}
+
+static void ieee80211_report_ack_skb(struct ieee80211_local *local,
+ struct ieee80211_tx_info *info,
+ bool acked, bool dropped)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->ack_status_lock, flags);
+ skb = idr_find(&local->ack_status_frames, info->ack_frame_id);
+ if (skb)
+ idr_remove(&local->ack_status_frames, info->ack_frame_id);
+ spin_unlock_irqrestore(&local->ack_status_lock, flags);
+
+ if (!skb)
+ return;
+
+ if (dropped) {
+ dev_kfree_skb_any(skb);
+ return;
+ }
+
+ if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
+ struct ieee80211_sub_if_data *sdata;
+
+ rcu_read_lock();
+ sdata = ieee80211_sdata_from_skb(local, skb);
+ if (sdata)
+ cfg80211_mgmt_tx_status(&sdata->wdev,
+ (unsigned long)skb,
+ skb->data, skb->len,
+ acked, GFP_ATOMIC);
+ rcu_read_unlock();
+
+ dev_kfree_skb_any(skb);
+ } else {
+ /* consumes skb */
+ skb_complete_wifi_ack(skb, acked);
+ }
+}
+
static void ieee80211_report_used_skb(struct ieee80211_local *local,
struct sk_buff *skb, bool dropped)
{
@@ -440,27 +500,14 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
acked = false;
if (info->flags & (IEEE80211_TX_INTFL_NL80211_FRAME_TX |
- IEEE80211_TX_INTFL_MLME_CONN_TX)) {
- struct ieee80211_sub_if_data *sdata = NULL;
- struct ieee80211_sub_if_data *iter_sdata;
+ IEEE80211_TX_INTFL_MLME_CONN_TX) &&
+ !info->ack_frame_id) {
+ struct ieee80211_sub_if_data *sdata;
u64 cookie = (unsigned long)skb;
rcu_read_lock();
- if (skb->dev) {
- list_for_each_entry_rcu(iter_sdata, &local->interfaces,
- list) {
- if (!iter_sdata->dev)
- continue;
-
- if (skb->dev == iter_sdata->dev) {
- sdata = iter_sdata;
- break;
- }
- }
- } else {
- sdata = rcu_dereference(local->p2p_sdata);
- }
+ sdata = ieee80211_sdata_from_skb(local, skb);
if (!sdata) {
skb->dev = NULL;
@@ -483,33 +530,13 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
cfg80211_probe_status(sdata->dev, hdr->addr1,
cookie, acked, GFP_ATOMIC);
} else {
- cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data,
- skb->len, acked, GFP_ATOMIC);
+ /* we assign ack frame ID for the others */
+ WARN_ON(1);
}
rcu_read_unlock();
- }
-
- if (unlikely(info->ack_frame_id)) {
- struct sk_buff *ack_skb;
- unsigned long flags;
-
- spin_lock_irqsave(&local->ack_status_lock, flags);
- ack_skb = idr_find(&local->ack_status_frames,
- info->ack_frame_id);
- if (ack_skb)
- idr_remove(&local->ack_status_frames,
- info->ack_frame_id);
- spin_unlock_irqrestore(&local->ack_status_lock, flags);
-
- if (ack_skb) {
- if (!dropped) {
- /* consumes ack_skb */
- skb_complete_wifi_ack(ack_skb, acked);
- } else {
- dev_kfree_skb_any(ack_skb);
- }
- }
+ } else if (info->ack_frame_id) {
+ ieee80211_report_ack_skb(local, info, acked, dropped);
}
}