diff options
author | Johannes Berg <johannes.berg@intel.com> | 2017-04-13 08:23:49 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-04-27 03:10:38 -0400 |
commit | 87cfeaa5e5a1834c2f55ade5da5a117991305269 (patch) | |
tree | 8aea2b106cabb277ed9edd98f4b84217aed12c67 | |
parent | e0411f1eb549a7993c9821c05f1787c0bd1523b4 (diff) |
mac80211: fix MU-MIMO follow-MAC mode
commit 9e478066eae41211c92a8f63cc69aafc391bd6ab upstream.
There are two bugs in the follow-MAC code:
* it treats the radiotap header as the 802.11 header
(therefore it can't possibly work)
* it doesn't verify that the skb data it accesses is actually
present in the header, which is mitigated by the first point
Fix this by moving all of this out into a separate function.
This function copies the data it needs using skb_copy_bits()
to make sure it can be accessed if it's paged, and offsets
that by the possibly present vendor radiotap header.
This also makes all those conditions more readable.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | net/mac80211/rx.c | 65 |
1 files changed, 47 insertions, 18 deletions
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index e2bbad0e494c..acaaf616da71 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c | |||
@@ -208,6 +208,51 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local, | |||
208 | return len; | 208 | return len; |
209 | } | 209 | } |
210 | 210 | ||
211 | static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata, | ||
212 | struct sk_buff *skb, | ||
213 | int rtap_vendor_space) | ||
214 | { | ||
215 | struct { | ||
216 | struct ieee80211_hdr_3addr hdr; | ||
217 | u8 category; | ||
218 | u8 action_code; | ||
219 | } __packed action; | ||
220 | |||
221 | if (!sdata) | ||
222 | return; | ||
223 | |||
224 | BUILD_BUG_ON(sizeof(action) != IEEE80211_MIN_ACTION_SIZE + 1); | ||
225 | |||
226 | if (skb->len < rtap_vendor_space + sizeof(action) + | ||
227 | VHT_MUMIMO_GROUPS_DATA_LEN) | ||
228 | return; | ||
229 | |||
230 | if (!is_valid_ether_addr(sdata->u.mntr.mu_follow_addr)) | ||
231 | return; | ||
232 | |||
233 | skb_copy_bits(skb, rtap_vendor_space, &action, sizeof(action)); | ||
234 | |||
235 | if (!ieee80211_is_action(action.hdr.frame_control)) | ||
236 | return; | ||
237 | |||
238 | if (action.category != WLAN_CATEGORY_VHT) | ||
239 | return; | ||
240 | |||
241 | if (action.action_code != WLAN_VHT_ACTION_GROUPID_MGMT) | ||
242 | return; | ||
243 | |||
244 | if (!ether_addr_equal(action.hdr.addr1, sdata->u.mntr.mu_follow_addr)) | ||
245 | return; | ||
246 | |||
247 | skb = skb_copy(skb, GFP_ATOMIC); | ||
248 | if (!skb) | ||
249 | return; | ||
250 | |||
251 | skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME; | ||
252 | skb_queue_tail(&sdata->skb_queue, skb); | ||
253 | ieee80211_queue_work(&sdata->local->hw, &sdata->work); | ||
254 | } | ||
255 | |||
211 | /* | 256 | /* |
212 | * ieee80211_add_rx_radiotap_header - add radiotap header | 257 | * ieee80211_add_rx_radiotap_header - add radiotap header |
213 | * | 258 | * |
@@ -515,7 +560,6 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, | |||
515 | struct net_device *prev_dev = NULL; | 560 | struct net_device *prev_dev = NULL; |
516 | int present_fcs_len = 0; | 561 | int present_fcs_len = 0; |
517 | unsigned int rtap_vendor_space = 0; | 562 | unsigned int rtap_vendor_space = 0; |
518 | struct ieee80211_mgmt *mgmt; | ||
519 | struct ieee80211_sub_if_data *monitor_sdata = | 563 | struct ieee80211_sub_if_data *monitor_sdata = |
520 | rcu_dereference(local->monitor_sdata); | 564 | rcu_dereference(local->monitor_sdata); |
521 | 565 | ||
@@ -553,6 +597,8 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, | |||
553 | return remove_monitor_info(local, origskb, rtap_vendor_space); | 597 | return remove_monitor_info(local, origskb, rtap_vendor_space); |
554 | } | 598 | } |
555 | 599 | ||
600 | ieee80211_handle_mu_mimo_mon(monitor_sdata, origskb, rtap_vendor_space); | ||
601 | |||
556 | /* room for the radiotap header based on driver features */ | 602 | /* room for the radiotap header based on driver features */ |
557 | rt_hdrlen = ieee80211_rx_radiotap_hdrlen(local, status, origskb); | 603 | rt_hdrlen = ieee80211_rx_radiotap_hdrlen(local, status, origskb); |
558 | needed_headroom = rt_hdrlen - rtap_vendor_space; | 604 | needed_headroom = rt_hdrlen - rtap_vendor_space; |
@@ -618,23 +664,6 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, | |||
618 | ieee80211_rx_stats(sdata->dev, skb->len); | 664 | ieee80211_rx_stats(sdata->dev, skb->len); |
619 | } | 665 | } |
620 | 666 | ||
621 | mgmt = (void *)skb->data; | ||
622 | if (monitor_sdata && | ||
623 | skb->len >= IEEE80211_MIN_ACTION_SIZE + 1 + VHT_MUMIMO_GROUPS_DATA_LEN && | ||
624 | ieee80211_is_action(mgmt->frame_control) && | ||
625 | mgmt->u.action.category == WLAN_CATEGORY_VHT && | ||
626 | mgmt->u.action.u.vht_group_notif.action_code == WLAN_VHT_ACTION_GROUPID_MGMT && | ||
627 | is_valid_ether_addr(monitor_sdata->u.mntr.mu_follow_addr) && | ||
628 | ether_addr_equal(mgmt->da, monitor_sdata->u.mntr.mu_follow_addr)) { | ||
629 | struct sk_buff *mu_skb = skb_copy(skb, GFP_ATOMIC); | ||
630 | |||
631 | if (mu_skb) { | ||
632 | mu_skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME; | ||
633 | skb_queue_tail(&monitor_sdata->skb_queue, mu_skb); | ||
634 | ieee80211_queue_work(&local->hw, &monitor_sdata->work); | ||
635 | } | ||
636 | } | ||
637 | |||
638 | if (prev_dev) { | 667 | if (prev_dev) { |
639 | skb->dev = prev_dev; | 668 | skb->dev = prev_dev; |
640 | netif_receive_skb(skb); | 669 | netif_receive_skb(skb); |