|
@@ -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
|
|
@@ -1855,23 +1855,28 @@ 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;
|
|
|
int len = rx->skb->len;
|
|
|
|
|
|
if (!ieee80211_is_action(mgmt->frame_control))
|
|
|
return RX_CONTINUE;
|
|
|
|
|
|
if (!rx->sta)
|
|
|
- return RX_DROP_MONITOR;
|
|
|
+ return RX_DROP_UNUSABLE;
|
|
|
|
|
|
if (!(rx->flags & IEEE80211_RX_RA_MATCH))
|
|
|
- return RX_DROP_MONITOR;
|
|
|
+ return RX_DROP_UNUSABLE;
|
|
|
|
|
|
if (ieee80211_drop_unencrypted(rx, mgmt->frame_control))
|
|
|
- return RX_DROP_MONITOR;
|
|
|
+ return RX_DROP_UNUSABLE;
|
|
|
|
|
|
- /* all categories we currently handle have action_code */
|
|
|
+ /* drop too small frames */
|
|
|
+ if (len < IEEE80211_MIN_ACTION_SIZE)
|
|
|
+ return RX_DROP_UNUSABLE;
|
|
|
+
|
|
|
+ /* return action frames that have *only* category */
|
|
|
if (len < IEEE80211_MIN_ACTION_SIZE + 1)
|
|
|
- return RX_DROP_MONITOR;
|
|
|
+ goto return_frame;
|
|
|
|
|
|
switch (mgmt->u.action.category) {
|
|
|
case WLAN_CATEGORY_BACK:
|
|
@@ -1884,7 +1889,7 @@ 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;
|
|
|
|
|
|
switch (mgmt->u.action.u.addba_req.action_code) {
|
|
|
case WLAN_ACTION_ADDBA_REQ:
|
|
@@ -1892,45 +1897,45 @@ 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;
|
|
|
|
|
|
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,29 +1943,48 @@ 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_frame:
|
|
|
+ /*
|
|
|
+ * 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;
|
|
|
|
|
|
- return RX_CONTINUE;
|
|
|
+ /* 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);
|
|
|
}
|
|
|
|
|
|
+ handled:
|
|
|
rx->sta->rx_packets++;
|
|
|
dev_kfree_skb(rx->skb);
|
|
|
return RX_QUEUED;
|