* 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
*/
#include <linux/jiffies.h>
+#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
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))
ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
{
struct ieee80211_sub_if_data *sdata = rx->sdata;
+ struct ieee80211_local *local = rx->local;
struct net_device *dev = sdata->dev;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
__le16 fc = hdr->frame_control;
dev->stats.rx_packets++;
dev->stats.rx_bytes += rx->skb->len;
+ if (ieee80211_is_data(hdr->frame_control) &&
+ !is_multicast_ether_addr(hdr->addr1) &&
+ local->hw.conf.dynamic_ps_timeout > 0 && local->ps_sdata) {
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
+ }
+
ieee80211_deliver_skb(rx);
return RX_QUEUED;
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:
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:
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);
}
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:
- return RX_CONTINUE;
+ case MESH_PLINK_CATEGORY:
+ case MESH_PATH_SEL_CATEGORY:
+ if (ieee80211_vif_is_mesh(&sdata->vif))
+ return ieee80211_mesh_rx_mgmt(sdata, rx->skb);
+ break;
}
- rx->sta->rx_packets++;
+ /*
+ * 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);
+ }
+
+ handled:
+ if (rx->sta)
+ rx->sta->rx_packets++;
dev_kfree_skb(rx->skb);
return RX_QUEUED;
}
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)
rate_idx = 0; /* TODO: HT rates */
else
rate_idx = status->rate_idx;
- rx->sta = ieee80211_ibss_add_sta(sdata, bssid, hdr->addr2,
- BIT(rate_idx));
+ rx->sta = ieee80211_ibss_add_sta(sdata, bssid,
+ hdr->addr2, BIT(rate_idx), GFP_ATOMIC);
}
break;
case NL80211_IFTYPE_MESH_POINT:
sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
continue;
- rx.sta = sta_info_get(sdata, hdr->addr2);
-
- rx.flags |= IEEE80211_RX_RA_MATCH;
- prepares = prepare_for_handlers(sdata, &rx, hdr);
-
- if (!prepares)
- continue;
-
- if (status->flag & RX_FLAG_MMIC_ERROR) {
- rx.sdata = sdata;
- if (rx.flags & IEEE80211_RX_RA_MATCH)
- ieee80211_rx_michael_mic_report(hdr,
- &rx);
- continue;
- }
-
/*
* frame is destined for this interface, but if it's
* not also for the previous one we handle that after
continue;
}
+ rx.sta = sta_info_get_bss(prev, hdr->addr2);
+
+ rx.flags |= IEEE80211_RX_RA_MATCH;
+ prepares = prepare_for_handlers(prev, &rx, hdr);
+
+ if (!prepares)
+ goto next;
+
+ if (status->flag & RX_FLAG_MMIC_ERROR) {
+ rx.sdata = prev;
+ if (rx.flags & IEEE80211_RX_RA_MATCH)
+ ieee80211_rx_michael_mic_report(hdr,
+ &rx);
+ goto next;
+ }
+
/*
* frame was destined for the previous interface
* so invoke RX handlers for it
"multicast frame for %s\n",
wiphy_name(local->hw.wiphy),
prev->name);
- continue;
+ goto next;
}
ieee80211_invoke_rx_handlers(prev, &rx, skb_new, rate);
+next:
prev = sdata;
}
+
+ if (prev) {
+ rx.sta = sta_info_get_bss(prev, hdr->addr2);
+
+ rx.flags |= IEEE80211_RX_RA_MATCH;
+ prepares = prepare_for_handlers(prev, &rx, hdr);
+
+ if (!prepares)
+ prev = NULL;
+ }
}
if (prev)
ieee80211_invoke_rx_handlers(prev, &rx, skb, rate);