diff options
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/cfg.c | 12 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 6 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 35 | ||||
-rw-r--r-- | net/mac80211/rx.c | 42 | ||||
-rw-r--r-- | net/mac80211/status.c | 7 |
5 files changed, 88 insertions, 14 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index e1731b7..b7116ef 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1,7 +1,7 @@ /* * mac80211 configuration hooks for cfg80211 * - * Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> * * This file is GPLv2 as found in COPYING. */ @@ -1448,6 +1448,15 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, return ieee80211_wk_cancel_remain_on_channel(sdata, cookie); } +static int ieee80211_action(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + const u8 *buf, size_t len, u64 *cookie) +{ + return ieee80211_mgd_action(IEEE80211_DEV_TO_SUB_IF(dev), chan, + channel_type, buf, len, cookie); +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -1496,4 +1505,5 @@ struct cfg80211_ops mac80211_config_ops = { .set_bitrate_mask = ieee80211_set_bitrate_mask, .remain_on_channel = ieee80211_remain_on_channel, .cancel_remain_on_channel = ieee80211_cancel_remain_on_channel, + .action = ieee80211_action, }; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 9dd98b6..241533e 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2,7 +2,7 @@ * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> - * Copyright 2007-2008 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 @@ -966,6 +966,10 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, struct cfg80211_disassoc_request *req, void *cookie); +int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + const u8 *buf, size_t len, u64 *cookie); ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); void ieee80211_send_pspoll(struct ieee80211_local *local, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index bfc4a50..41812a1 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2084,3 +2084,38 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, return 0; } + +int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + const u8 *buf, size_t len, u64 *cookie) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct sk_buff *skb; + + /* Check that we are on the requested channel for transmission */ + if ((chan != local->tmp_channel || + channel_type != local->tmp_channel_type) && + (chan != local->oper_channel || + channel_type != local->oper_channel_type)) + return -EBUSY; + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + len); + if (!skb) + return -ENOMEM; + skb_reserve(skb, local->hw.extra_tx_headroom); + + memcpy(skb_put(skb, len), buf, len); + + if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED)) + IEEE80211_SKB_CB(skb)->flags |= + IEEE80211_TX_INTFL_DONT_ENCRYPT; + IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_NL80211_FRAME_TX | + IEEE80211_TX_CTL_REQ_TX_STATUS; + skb->dev = sdata->dev; + ieee80211_tx_skb(sdata, skb); + + *cookie = (unsigned long) skb; + return 0; +} diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index a177472..a6080d8 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1856,28 +1856,25 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) 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) + /* drop too small frames */ + if (len < IEEE80211_MIN_ACTION_SIZE) return RX_DROP_UNUSABLE; - if (!(rx->flags & IEEE80211_RX_RA_MATCH)) + if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) return RX_DROP_UNUSABLE; - if (ieee80211_drop_unencrypted(rx, mgmt->frame_control)) + if (!(rx->flags & IEEE80211_RX_RA_MATCH)) return RX_DROP_UNUSABLE; - /* drop too small frames */ - if (len < IEEE80211_MIN_ACTION_SIZE) + if (ieee80211_drop_unencrypted(rx, mgmt->frame_control)) return RX_DROP_UNUSABLE; - /* return action frames that have *only* category */ - if (len < IEEE80211_MIN_ACTION_SIZE + 1) - goto return_frame; - switch (mgmt->u.action.category) { case WLAN_CATEGORY_BACK: /* @@ -1891,6 +1888,10 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) sdata->vif.type != NL80211_IFTYPE_AP) 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: if (len < (IEEE80211_MIN_ACTION_SIZE + @@ -1919,6 +1920,10 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) if (sdata->vif.type != NL80211_IFTYPE_STATION) 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 + @@ -1954,7 +1959,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) } break; } - return_frame: + /* * For AP mode, hostapd is responsible for handling any action * frames that we didn't handle, including returning unknown @@ -1966,6 +1971,20 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) 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; @@ -1985,7 +2004,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) } handled: - rx->sta->rx_packets++; + if (rx->sta) + rx->sta->rx_packets++; dev_kfree_skb(rx->skb); return RX_QUEUED; } diff --git a/net/mac80211/status.c b/net/mac80211/status.c index ded9873..56d5b9a 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.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 2008-2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2008-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 @@ -288,6 +288,11 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) msecs_to_jiffies(10)); } + if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) + cfg80211_action_tx_status( + skb->dev, (unsigned long) skb, skb->data, skb->len, + !!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC); + /* this was a transmitted frame, but now we want to reuse it */ skb_orphan(skb); |