diff options
Diffstat (limited to 'net/mac80211/rx.c')
-rw-r--r-- | net/mac80211/rx.c | 140 |
1 files changed, 99 insertions, 41 deletions
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index c9755f3d986..b5c48de81d8 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2,7 +2,7 @@ * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> - * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -1397,6 +1397,21 @@ ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) ieee80211_is_data(fc) && (rx->key || rx->sdata->drop_unencrypted))) return -EACCES; + + return 0; +} + +static int +ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; + __le16 fc = hdr->frame_control; + int res; + + res = ieee80211_drop_unencrypted(rx, fc); + if (unlikely(res)) + return res; + if (rx->sta && test_sta_flags(rx->sta, WLAN_STA_MFP)) { if (unlikely(ieee80211_is_unicast_robust_mgmt_frame(rx->skb) && rx->key)) @@ -1855,23 +1870,25 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) struct ieee80211_local *local = rx->local; struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data; + struct sk_buff *nskb; + struct ieee80211_rx_status *status; int len = rx->skb->len; if (!ieee80211_is_action(mgmt->frame_control)) return RX_CONTINUE; - if (!rx->sta) - return RX_DROP_MONITOR; + /* drop too small frames */ + if (len < IEEE80211_MIN_ACTION_SIZE) + return RX_DROP_UNUSABLE; - if (!(rx->flags & IEEE80211_RX_RA_MATCH)) - return RX_DROP_MONITOR; + if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) + return RX_DROP_UNUSABLE; - if (ieee80211_drop_unencrypted(rx, mgmt->frame_control)) - return RX_DROP_MONITOR; + if (!(rx->flags & IEEE80211_RX_RA_MATCH)) + return RX_DROP_UNUSABLE; - /* all categories we currently handle have action_code */ - if (len < IEEE80211_MIN_ACTION_SIZE + 1) - return RX_DROP_MONITOR; + if (ieee80211_drop_unencrypted_mgmt(rx)) + return RX_DROP_UNUSABLE; switch (mgmt->u.action.category) { case WLAN_CATEGORY_BACK: @@ -1884,7 +1901,11 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) if (sdata->vif.type != NL80211_IFTYPE_STATION && sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_AP) - return RX_DROP_MONITOR; + break; + + /* verify action_code is present */ + if (len < IEEE80211_MIN_ACTION_SIZE + 1) + break; switch (mgmt->u.action.u.addba_req.action_code) { case WLAN_ACTION_ADDBA_REQ: @@ -1892,45 +1913,49 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) sizeof(mgmt->u.action.u.addba_req))) return RX_DROP_MONITOR; ieee80211_process_addba_request(local, rx->sta, mgmt, len); - break; + goto handled; case WLAN_ACTION_ADDBA_RESP: if (len < (IEEE80211_MIN_ACTION_SIZE + sizeof(mgmt->u.action.u.addba_resp))) - return RX_DROP_MONITOR; + break; ieee80211_process_addba_resp(local, rx->sta, mgmt, len); - break; + goto handled; case WLAN_ACTION_DELBA: if (len < (IEEE80211_MIN_ACTION_SIZE + sizeof(mgmt->u.action.u.delba))) - return RX_DROP_MONITOR; + break; ieee80211_process_delba(sdata, rx->sta, mgmt, len); - break; + goto handled; } break; case WLAN_CATEGORY_SPECTRUM_MGMT: if (local->hw.conf.channel->band != IEEE80211_BAND_5GHZ) - return RX_DROP_MONITOR; + break; if (sdata->vif.type != NL80211_IFTYPE_STATION) - return RX_DROP_MONITOR; + break; + + /* verify action_code is present */ + if (len < IEEE80211_MIN_ACTION_SIZE + 1) + break; switch (mgmt->u.action.u.measurement.action_code) { case WLAN_ACTION_SPCT_MSR_REQ: if (len < (IEEE80211_MIN_ACTION_SIZE + sizeof(mgmt->u.action.u.measurement))) - return RX_DROP_MONITOR; + break; ieee80211_process_measurement_req(sdata, mgmt, len); - break; + goto handled; case WLAN_ACTION_SPCT_CHL_SWITCH: if (len < (IEEE80211_MIN_ACTION_SIZE + sizeof(mgmt->u.action.u.chan_switch))) - return RX_DROP_MONITOR; + break; if (sdata->vif.type != NL80211_IFTYPE_STATION) - return RX_DROP_MONITOR; + break; if (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN)) - return RX_DROP_MONITOR; + break; return ieee80211_sta_rx_mgmt(sdata, rx->skb); } @@ -1938,30 +1963,64 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) case WLAN_CATEGORY_SA_QUERY: if (len < (IEEE80211_MIN_ACTION_SIZE + sizeof(mgmt->u.action.u.sa_query))) - return RX_DROP_MONITOR; + break; + switch (mgmt->u.action.u.sa_query.action) { case WLAN_ACTION_SA_QUERY_REQUEST: if (sdata->vif.type != NL80211_IFTYPE_STATION) - return RX_DROP_MONITOR; + break; ieee80211_process_sa_query_req(sdata, mgmt, len); - break; - case WLAN_ACTION_SA_QUERY_RESPONSE: - /* - * SA Query response is currently only used in AP mode - * and it is processed in user space. - */ - return RX_CONTINUE; + goto handled; } break; - default: - /* do not process rejected action frames */ - if (mgmt->u.action.category & 0x80) - return RX_DROP_MONITOR; + } - return RX_CONTINUE; + /* + * For AP mode, hostapd is responsible for handling any action + * frames that we didn't handle, including returning unknown + * ones. For all other modes we will return them to the sender, + * setting the 0x80 bit in the action category, as required by + * 802.11-2007 7.3.1.11. + */ + if (sdata->vif.type == NL80211_IFTYPE_AP || + sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + return RX_DROP_MONITOR; + + /* + * Getting here means the kernel doesn't know how to handle + * it, but maybe userspace does ... include returned frames + * so userspace can register for those to know whether ones + * it transmitted were processed or returned. + */ + status = IEEE80211_SKB_RXCB(rx->skb); + + if (sdata->vif.type == NL80211_IFTYPE_STATION && + cfg80211_rx_action(rx->sdata->dev, status->freq, + rx->skb->data, rx->skb->len, + GFP_ATOMIC)) + goto handled; + + /* do not return rejected action frames */ + if (mgmt->u.action.category & 0x80) + return RX_DROP_UNUSABLE; + + nskb = skb_copy_expand(rx->skb, local->hw.extra_tx_headroom, 0, + GFP_ATOMIC); + if (nskb) { + struct ieee80211_mgmt *mgmt = (void *)nskb->data; + + mgmt->u.action.category |= 0x80; + memcpy(mgmt->da, mgmt->sa, ETH_ALEN); + memcpy(mgmt->sa, rx->sdata->vif.addr, ETH_ALEN); + + memset(nskb->cb, 0, sizeof(nskb->cb)); + + ieee80211_tx_skb(rx->sdata, nskb); } - rx->sta->rx_packets++; + handled: + if (rx->sta) + rx->sta->rx_packets++; dev_kfree_skb(rx->skb); return RX_QUEUED; } @@ -1970,14 +2029,13 @@ static ieee80211_rx_result debug_noinline ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) { struct ieee80211_sub_if_data *sdata = rx->sdata; - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data; ieee80211_rx_result rxs; if (!(rx->flags & IEEE80211_RX_RA_MATCH)) return RX_DROP_MONITOR; - if (ieee80211_drop_unencrypted(rx, mgmt->frame_control)) - return RX_DROP_MONITOR; + if (ieee80211_drop_unencrypted_mgmt(rx)) + return RX_DROP_UNUSABLE; rxs = ieee80211_work_rx_mgmt(rx->sdata, rx->skb); if (rxs != RX_CONTINUE) |