Commit 84040805 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville

mac80211: reject unhandled action frames

802.11-2007 7.3.1.11 mandates that we need to
reject action frames we don't handle by setting
the 0x80 bit in the category and returning them
to the sender, so do that. In AP mode, hostapd
is responsible for this.

Additionally, drop completely malformed action
frames or ones that should've been encrypted as
unusable, userspace shouldn't see those.
Signed-off-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarJouni Malinen <jouni.malinen@atheros.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent de1ebdce
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2005-2006, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> * 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 * 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 * 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) ...@@ -1855,23 +1855,28 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
struct ieee80211_local *local = rx->local; struct ieee80211_local *local = rx->local;
struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_sub_if_data *sdata = rx->sdata;
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data; struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
struct sk_buff *nskb;
int len = rx->skb->len; int len = rx->skb->len;
if (!ieee80211_is_action(mgmt->frame_control)) if (!ieee80211_is_action(mgmt->frame_control))
return RX_CONTINUE; return RX_CONTINUE;
if (!rx->sta) if (!rx->sta)
return RX_DROP_MONITOR; return RX_DROP_UNUSABLE;
if (!(rx->flags & IEEE80211_RX_RA_MATCH)) if (!(rx->flags & IEEE80211_RX_RA_MATCH))
return RX_DROP_MONITOR; return RX_DROP_UNUSABLE;
if (ieee80211_drop_unencrypted(rx, mgmt->frame_control)) 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) if (len < IEEE80211_MIN_ACTION_SIZE + 1)
return RX_DROP_MONITOR; goto return_frame;
switch (mgmt->u.action.category) { switch (mgmt->u.action.category) {
case WLAN_CATEGORY_BACK: case WLAN_CATEGORY_BACK:
...@@ -1884,7 +1889,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) ...@@ -1884,7 +1889,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
if (sdata->vif.type != NL80211_IFTYPE_STATION && if (sdata->vif.type != NL80211_IFTYPE_STATION &&
sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
sdata->vif.type != NL80211_IFTYPE_AP) sdata->vif.type != NL80211_IFTYPE_AP)
return RX_DROP_MONITOR; break;
switch (mgmt->u.action.u.addba_req.action_code) { switch (mgmt->u.action.u.addba_req.action_code) {
case WLAN_ACTION_ADDBA_REQ: case WLAN_ACTION_ADDBA_REQ:
...@@ -1892,45 +1897,45 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) ...@@ -1892,45 +1897,45 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
sizeof(mgmt->u.action.u.addba_req))) sizeof(mgmt->u.action.u.addba_req)))
return RX_DROP_MONITOR; return RX_DROP_MONITOR;
ieee80211_process_addba_request(local, rx->sta, mgmt, len); ieee80211_process_addba_request(local, rx->sta, mgmt, len);
break; goto handled;
case WLAN_ACTION_ADDBA_RESP: case WLAN_ACTION_ADDBA_RESP:
if (len < (IEEE80211_MIN_ACTION_SIZE + if (len < (IEEE80211_MIN_ACTION_SIZE +
sizeof(mgmt->u.action.u.addba_resp))) sizeof(mgmt->u.action.u.addba_resp)))
return RX_DROP_MONITOR;
ieee80211_process_addba_resp(local, rx->sta, mgmt, len);
break; break;
ieee80211_process_addba_resp(local, rx->sta, mgmt, len);
goto handled;
case WLAN_ACTION_DELBA: case WLAN_ACTION_DELBA:
if (len < (IEEE80211_MIN_ACTION_SIZE + if (len < (IEEE80211_MIN_ACTION_SIZE +
sizeof(mgmt->u.action.u.delba))) sizeof(mgmt->u.action.u.delba)))
return RX_DROP_MONITOR;
ieee80211_process_delba(sdata, rx->sta, mgmt, len);
break; break;
ieee80211_process_delba(sdata, rx->sta, mgmt, len);
goto handled;
} }
break; break;
case WLAN_CATEGORY_SPECTRUM_MGMT: case WLAN_CATEGORY_SPECTRUM_MGMT:
if (local->hw.conf.channel->band != IEEE80211_BAND_5GHZ) if (local->hw.conf.channel->band != IEEE80211_BAND_5GHZ)
return RX_DROP_MONITOR; break;
if (sdata->vif.type != NL80211_IFTYPE_STATION) if (sdata->vif.type != NL80211_IFTYPE_STATION)
return RX_DROP_MONITOR; break;
switch (mgmt->u.action.u.measurement.action_code) { switch (mgmt->u.action.u.measurement.action_code) {
case WLAN_ACTION_SPCT_MSR_REQ: case WLAN_ACTION_SPCT_MSR_REQ:
if (len < (IEEE80211_MIN_ACTION_SIZE + if (len < (IEEE80211_MIN_ACTION_SIZE +
sizeof(mgmt->u.action.u.measurement))) sizeof(mgmt->u.action.u.measurement)))
return RX_DROP_MONITOR;
ieee80211_process_measurement_req(sdata, mgmt, len);
break; break;
ieee80211_process_measurement_req(sdata, mgmt, len);
goto handled;
case WLAN_ACTION_SPCT_CHL_SWITCH: case WLAN_ACTION_SPCT_CHL_SWITCH:
if (len < (IEEE80211_MIN_ACTION_SIZE + if (len < (IEEE80211_MIN_ACTION_SIZE +
sizeof(mgmt->u.action.u.chan_switch))) sizeof(mgmt->u.action.u.chan_switch)))
return RX_DROP_MONITOR; break;
if (sdata->vif.type != NL80211_IFTYPE_STATION) if (sdata->vif.type != NL80211_IFTYPE_STATION)
return RX_DROP_MONITOR; break;
if (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN)) if (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN))
return RX_DROP_MONITOR; break;
return ieee80211_sta_rx_mgmt(sdata, rx->skb); return ieee80211_sta_rx_mgmt(sdata, rx->skb);
} }
...@@ -1938,29 +1943,48 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) ...@@ -1938,29 +1943,48 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
case WLAN_CATEGORY_SA_QUERY: case WLAN_CATEGORY_SA_QUERY:
if (len < (IEEE80211_MIN_ACTION_SIZE + if (len < (IEEE80211_MIN_ACTION_SIZE +
sizeof(mgmt->u.action.u.sa_query))) sizeof(mgmt->u.action.u.sa_query)))
return RX_DROP_MONITOR; break;
switch (mgmt->u.action.u.sa_query.action) { switch (mgmt->u.action.u.sa_query.action) {
case WLAN_ACTION_SA_QUERY_REQUEST: case WLAN_ACTION_SA_QUERY_REQUEST:
if (sdata->vif.type != NL80211_IFTYPE_STATION) if (sdata->vif.type != NL80211_IFTYPE_STATION)
return RX_DROP_MONITOR; break;
ieee80211_process_sa_query_req(sdata, mgmt, len); ieee80211_process_sa_query_req(sdata, mgmt, len);
goto handled;
}
break; break;
case WLAN_ACTION_SA_QUERY_RESPONSE: }
return_frame:
/* /*
* SA Query response is currently only used in AP mode * For AP mode, hostapd is responsible for handling any action
* and it is processed in user space. * 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.
*/ */
return RX_CONTINUE; if (sdata->vif.type == NL80211_IFTYPE_AP ||
} sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
break;
default:
/* do not process rejected action frames */
if (mgmt->u.action.category & 0x80)
return RX_DROP_MONITOR; 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++; rx->sta->rx_packets++;
dev_kfree_skb(rx->skb); dev_kfree_skb(rx->skb);
return RX_QUEUED; return RX_QUEUED;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment