aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/mac80211/rx.c80
1 files changed, 70 insertions, 10 deletions
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 308e502a80eb..50c0803a63ba 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2609,7 +2609,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
int prepares;
struct ieee80211_sub_if_data *prev = NULL;
struct sk_buff *skb_new;
- struct sta_info *sta, *tmp;
+ struct sta_info *sta, *tmp, *prev_sta;
bool found_sta = false;
int err = 0;
@@ -2640,22 +2640,74 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
ieee80211_verify_alignment(&rx);
if (ieee80211_is_data(fc)) {
+ prev_sta = NULL;
for_each_sta_info(local, hdr->addr2, sta, tmp) {
- rx.sta = sta;
found_sta = true;
- rx.sdata = sta->sdata;
+ if (!prev_sta) {
+ prev_sta = sta;
+ continue;
+ }
+
+ rx.sta = prev_sta;
+ rx.sdata = prev_sta->sdata;
rx.flags |= IEEE80211_RX_RA_MATCH;
prepares = prepare_for_handlers(rx.sdata, &rx, hdr);
- if (prepares) {
- if (status->flag & RX_FLAG_MMIC_ERROR) {
- if (rx.flags & IEEE80211_RX_RA_MATCH)
- ieee80211_rx_michael_mic_report(hdr, &rx);
- } else
- prev = rx.sdata;
+ if (!prepares)
+ goto next_sta;
+
+ if (status->flag & RX_FLAG_MMIC_ERROR) {
+ if (rx.flags & IEEE80211_RX_RA_MATCH)
+ ieee80211_rx_michael_mic_report(hdr, &rx);
+ goto next_sta;
+ }
+
+ /*
+ * frame was destined for the previous interface
+ * so invoke RX handlers for it
+ */
+ skb_new = skb_copy(skb, GFP_ATOMIC);
+ if (!skb_new) {
+ if (net_ratelimit())
+ wiphy_debug(local->hw.wiphy,
+ "failed to copy multicast"
+ " frame for %s\n",
+ prev_sta->sdata->name);
+ goto next_sta;
+ }
+ ieee80211_invoke_rx_handlers(prev_sta->sdata, &rx,
+ skb_new);
+next_sta:
+ prev_sta = sta;
+ } /* for all STA info */
+
+ if (prev_sta) {
+ rx.sta = prev_sta;
+ rx.sdata = prev_sta->sdata;
+
+ rx.flags |= IEEE80211_RX_RA_MATCH;
+ prepares = prepare_for_handlers(rx.sdata, &rx, hdr);
+ if (!prepares)
+ prev_sta = NULL;
+
+ if (prev_sta && status->flag & RX_FLAG_MMIC_ERROR) {
+ if (rx.flags & IEEE80211_RX_RA_MATCH)
+ ieee80211_rx_michael_mic_report(hdr, &rx);
+ prev_sta = NULL;
}
}
- }
+
+
+ if (prev_sta) {
+ ieee80211_invoke_rx_handlers(prev_sta->sdata, &rx, skb);
+ return;
+ } else {
+ if (found_sta) {
+ dev_kfree_skb(skb);
+ return;
+ }
+ }
+ } /* if data frame */
if (!found_sta) {
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
if (!ieee80211_sdata_running(sdata))
@@ -2718,6 +2770,14 @@ next:
if (!prepares)
prev = NULL;
+
+ if (prev && status->flag & RX_FLAG_MMIC_ERROR) {
+ rx.sdata = prev;
+ if (rx.flags & IEEE80211_RX_RA_MATCH)
+ ieee80211_rx_michael_mic_report(hdr,
+ &rx);
+ prev = NULL;
+ }
}
}
if (prev)