aboutsummaryrefslogtreecommitdiff
path: root/net/wireless
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-03-02 07:55:08 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2010-03-02 07:55:08 -0800
commit6d6b89bd2e316b78d668f761d380837b81fa71ef (patch)
tree7e63c58611fc6181153526abbdafdd846ed1a19d /net/wireless
parent13dda80e48439b446d0bc9bab34b91484bc8f533 (diff)
parent2507c05ff55fbf38326b08ed27eaed233bc75042 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6: (1341 commits) virtio_net: remove forgotten assignment be2net: fix tx completion polling sis190: fix cable detect via link status poll net: fix protocol sk_buff field bridge: Fix build error when IGMP_SNOOPING is not enabled bnx2x: Tx barriers and locks scm: Only support SCM_RIGHTS on unix domain sockets. vhost-net: restart tx poll on sk_sndbuf full vhost: fix get_user_pages_fast error handling vhost: initialize log eventfd context pointer vhost: logging thinko fix wireless: convert to use netdev_for_each_mc_addr ethtool: do not set some flags, if others failed ipoib: returned back addrlen check for mc addresses netlink: Adding inode field to /proc/net/netlink axnet_cs: add new id bridge: Make IGMP snooping depend upon BRIDGE. bridge: Add multicast count/interval sysfs entries bridge: Add hash elasticity/max sysfs entries bridge: Add multicast_snooping sysfs toggle ... Trivial conflicts in Documentation/feature-removal-schedule.txt
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/.gitignore1
-rw-r--r--net/wireless/Kconfig13
-rw-r--r--net/wireless/Makefile6
-rw-r--r--net/wireless/chan.c41
-rw-r--r--net/wireless/core.c59
-rw-r--r--net/wireless/core.h20
-rw-r--r--net/wireless/db.txt17
-rw-r--r--net/wireless/genregdb.awk118
-rw-r--r--net/wireless/lib80211_crypt_ccmp.c2
-rw-r--r--net/wireless/lib80211_crypt_tkip.c23
-rw-r--r--net/wireless/mlme.c214
-rw-r--r--net/wireless/nl80211.c866
-rw-r--r--net/wireless/nl80211.h23
-rw-r--r--net/wireless/radiotap.c305
-rw-r--r--net/wireless/reg.c687
-rw-r--r--net/wireless/reg.h29
-rw-r--r--net/wireless/regdb.h7
-rw-r--r--net/wireless/scan.c158
-rw-r--r--net/wireless/sme.c41
-rw-r--r--net/wireless/sysfs.c20
-rw-r--r--net/wireless/util.c137
-rw-r--r--net/wireless/wext-compat.c49
-rw-r--r--net/wireless/wext-proc.c4
23 files changed, 2380 insertions, 460 deletions
diff --git a/net/wireless/.gitignore b/net/wireless/.gitignore
new file mode 100644
index 00000000000..c33451b896d
--- /dev/null
+++ b/net/wireless/.gitignore
@@ -0,0 +1 @@
+regdb.c
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index 90e93a5701a..d0ee29063e5 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -94,20 +94,21 @@ config CFG80211_DEBUGFS
If unsure, say N.
-config WIRELESS_OLD_REGULATORY
- bool "Old wireless static regulatory definitions"
+config CFG80211_INTERNAL_REGDB
+ bool "use statically compiled regulatory rules database" if EMBEDDED
default n
depends on CFG80211
---help---
- This option enables the old static regulatory information
- and uses it within the new framework. This option is available
- for historical reasons and it is advised to leave it off.
+ This option generates an internal data structure representing
+ the wireless regulatory rules described in net/wireless/db.txt
+ and includes code to query that database. This is an alternative
+ to using CRDA for defining regulatory rules for the kernel.
For details see:
http://wireless.kernel.org/en/developers/Regulatory
- Say N and if you say Y, please tell us why. The default is N.
+ Most distributions have a CRDA package. So if unsure, say N.
config CFG80211_WEXT
bool "cfg80211 wireless extensions compatibility"
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index f07c8dc7aab..e77e508126f 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -13,5 +13,11 @@ cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o
cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o
+cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o
ccflags-y += -D__CHECK_ENDIAN__
+
+$(obj)/regdb.c: $(src)/db.txt $(src)/genregdb.awk
+ @$(AWK) -f $(srctree)/$(src)/genregdb.awk < $< > $@
+
+clean-files := regdb.c
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index a46ac6c9b36..bf1737fc9a7 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -41,44 +41,57 @@ rdev_fixed_channel(struct cfg80211_registered_device *rdev,
return result;
}
-int rdev_set_freq(struct cfg80211_registered_device *rdev,
- struct wireless_dev *for_wdev,
+struct ieee80211_channel *
+rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
int freq, enum nl80211_channel_type channel_type)
{
struct ieee80211_channel *chan;
struct ieee80211_sta_ht_cap *ht_cap;
- int result;
-
- if (rdev_fixed_channel(rdev, for_wdev))
- return -EBUSY;
-
- if (!rdev->ops->set_channel)
- return -EOPNOTSUPP;
chan = ieee80211_get_channel(&rdev->wiphy, freq);
/* Primary channel not allowed */
if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
- return -EINVAL;
+ return NULL;
if (channel_type == NL80211_CHAN_HT40MINUS &&
chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
- return -EINVAL;
+ return NULL;
else if (channel_type == NL80211_CHAN_HT40PLUS &&
chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
- return -EINVAL;
+ return NULL;
ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
if (channel_type != NL80211_CHAN_NO_HT) {
if (!ht_cap->ht_supported)
- return -EINVAL;
+ return NULL;
if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
- return -EINVAL;
+ return NULL;
}
+ return chan;
+}
+
+int rdev_set_freq(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *for_wdev,
+ int freq, enum nl80211_channel_type channel_type)
+{
+ struct ieee80211_channel *chan;
+ int result;
+
+ if (rdev_fixed_channel(rdev, for_wdev))
+ return -EBUSY;
+
+ if (!rdev->ops->set_channel)
+ return -EOPNOTSUPP;
+
+ chan = rdev_freq_to_chan(rdev, freq, channel_type);
+ if (!chan)
+ return -EINVAL;
+
result = rdev->ops->set_channel(&rdev->wiphy, chan, channel_type);
if (result)
return result;
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 92b81244248..7fdb9409ad2 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -1,7 +1,7 @@
/*
* This is the linux wireless configuration interface.
*
- * Copyright 2006-2009 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
*/
#include <linux/if.h>
@@ -31,15 +31,10 @@ MODULE_AUTHOR("Johannes Berg");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("wireless configuration support");
-/* RCU might be appropriate here since we usually
- * only read the list, and that can happen quite
- * often because we need to do it for each command */
+/* RCU-protected (and cfg80211_mutex for writers) */
LIST_HEAD(cfg80211_rdev_list);
int cfg80211_rdev_list_generation;
-/*
- * This is used to protect the cfg80211_rdev_list
- */
DEFINE_MUTEX(cfg80211_mutex);
/* for debugfs */
@@ -402,6 +397,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
rdev->wiphy.retry_long = 4;
rdev->wiphy.frag_threshold = (u32) -1;
rdev->wiphy.rts_threshold = (u32) -1;
+ rdev->wiphy.coverage_class = 0;
return &rdev->wiphy;
}
@@ -417,6 +413,18 @@ int wiphy_register(struct wiphy *wiphy)
int i;
u16 ifmodes = wiphy->interface_modes;
+ if (WARN_ON(wiphy->addresses && !wiphy->n_addresses))
+ return -EINVAL;
+
+ if (WARN_ON(wiphy->addresses &&
+ !is_zero_ether_addr(wiphy->perm_addr) &&
+ memcmp(wiphy->perm_addr, wiphy->addresses[0].addr,
+ ETH_ALEN)))
+ return -EINVAL;
+
+ if (wiphy->addresses)
+ memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN);
+
/* sanity check ifmodes */
WARN_ON(!ifmodes);
ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1;
@@ -476,7 +484,7 @@ int wiphy_register(struct wiphy *wiphy)
/* set up regulatory info */
wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE);
- list_add(&rdev->list, &cfg80211_rdev_list);
+ list_add_rcu(&rdev->list, &cfg80211_rdev_list);
cfg80211_rdev_list_generation++;
mutex_unlock(&cfg80211_mutex);
@@ -553,7 +561,8 @@ void wiphy_unregister(struct wiphy *wiphy)
* it impossible to find from userspace.
*/
debugfs_remove_recursive(rdev->wiphy.debugfsdir);
- list_del(&rdev->list);
+ list_del_rcu(&rdev->list);
+ synchronize_rcu();
/*
* Try to grab rdev->mtx. If a command is still in progress,
@@ -668,8 +677,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work);
INIT_LIST_HEAD(&wdev->event_list);
spin_lock_init(&wdev->event_lock);
+ INIT_LIST_HEAD(&wdev->action_registrations);
+ spin_lock_init(&wdev->action_registrations_lock);
+
mutex_lock(&rdev->devlist_mtx);
- list_add(&wdev->list, &rdev->netdev_list);
+ list_add_rcu(&wdev->list, &rdev->netdev_list);
rdev->devlist_generation++;
/* can only change netns with wiphy */
dev->features |= NETIF_F_NETNS_LOCAL;
@@ -686,19 +698,21 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
wdev->wext.default_key = -1;
wdev->wext.default_mgmt_key = -1;
wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
+#endif
+
if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT)
- wdev->wext.ps = true;
+ wdev->ps = true;
else
- wdev->wext.ps = false;
- wdev->wext.ps_timeout = 100;
+ wdev->ps = false;
+ wdev->ps_timeout = 100;
if (rdev->ops->set_power_mgmt)
if (rdev->ops->set_power_mgmt(wdev->wiphy, dev,
- wdev->wext.ps,
- wdev->wext.ps_timeout)) {
+ wdev->ps,
+ wdev->ps_timeout)) {
/* assume this means it's off */
- wdev->wext.ps = false;
+ wdev->ps = false;
}
-#endif
+
if (!dev->ethtool_ops)
dev->ethtool_ops = &cfg80211_ethtool_ops;
@@ -781,13 +795,22 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
*/
if (!list_empty(&wdev->list)) {
sysfs_remove_link(&dev->dev.kobj, "phy80211");
- list_del_init(&wdev->list);
+ list_del_rcu(&wdev->list);
rdev->devlist_generation++;
+ cfg80211_mlme_purge_actions(wdev);
#ifdef CONFIG_CFG80211_WEXT
kfree(wdev->wext.keys);
#endif
}
mutex_unlock(&rdev->devlist_mtx);
+ /*
+ * synchronise (so that we won't find this netdev
+ * from other code any more) and then clear the list
+ * head so that the above code can safely check for
+ * !list_empty() to avoid double-cleanup.
+ */
+ synchronize_rcu();
+ INIT_LIST_HEAD(&wdev->list);
break;
case NETDEV_PRE_UP:
if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype)))
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 4ef3efc9410..d52da913145 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -1,7 +1,7 @@
/*
* Wireless configuration interface internals.
*
- * Copyright 2006-2009 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
*/
#ifndef __NET_WIRELESS_CORE_H
#define __NET_WIRELESS_CORE_H
@@ -48,6 +48,7 @@ struct cfg80211_registered_device {
/* associate netdev list */
struct mutex devlist_mtx;
+ /* protected by devlist_mtx or RCU */
struct list_head netdev_list;
int devlist_generation;
int opencount; /* also protected by devlist_mtx */
@@ -111,7 +112,8 @@ struct cfg80211_internal_bss {
unsigned long ts;
struct kref ref;
atomic_t hold;
- bool ies_allocated;
+ bool beacon_ies_allocated;
+ bool proberesp_ies_allocated;
/* must be last because of priv member */
struct cfg80211_bss pub;
@@ -327,6 +329,15 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
const u8 *resp_ie, size_t resp_ie_len,
u16 status, bool wextev,
struct cfg80211_bss *bss);
+int cfg80211_mlme_register_action(struct wireless_dev *wdev, u32 snd_pid,
+ const u8 *match_data, int match_len);
+void cfg80211_mlme_unregister_actions(struct wireless_dev *wdev, u32 nlpid);
+void cfg80211_mlme_purge_actions(struct wireless_dev *wdev);
+int cfg80211_mlme_action(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type,
+ const u8 *buf, size_t len, u64 *cookie);
/* SME */
int __cfg80211_connect(struct cfg80211_registered_device *rdev,
@@ -374,10 +385,15 @@ void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
struct ieee80211_channel *
rdev_fixed_channel(struct cfg80211_registered_device *rdev,
struct wireless_dev *for_wdev);
+struct ieee80211_channel *
+rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
+ int freq, enum nl80211_channel_type channel_type);
int rdev_set_freq(struct cfg80211_registered_device *rdev,
struct wireless_dev *for_wdev,
int freq, enum nl80211_channel_type channel_type);
+u16 cfg80211_calculate_bitrate(struct rate_info *rate);
+
#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS
#define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond)
#else
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
new file mode 100644
index 00000000000..a2fc3a09ccd
--- /dev/null
+++ b/net/wireless/db.txt
@@ -0,0 +1,17 @@
+#
+# This file is a placeholder to prevent accidental build breakage if someone
+# enables CONFIG_CFG80211_INTERNAL_REGDB. Almost no one actually needs to
+# enable that build option.
+#
+# You should be using CRDA instead. It is even better if you use the CRDA
+# package provided by your distribution, since they will probably keep it
+# up-to-date on your behalf.
+#
+# If you _really_ intend to use CONFIG_CFG80211_INTERNAL_REGDB then you will
+# need to replace this file with one containing appropriately formatted
+# regulatory rules that cover the regulatory domains you will be using. Your
+# best option is to extract the db.txt file from the wireless-regdb git
+# repository:
+#
+# git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-regdb.git
+#
diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk
new file mode 100644
index 00000000000..3cc9e69880a
--- /dev/null
+++ b/net/wireless/genregdb.awk
@@ -0,0 +1,118 @@
+#!/usr/bin/awk -f
+#
+# genregdb.awk -- generate regdb.c from db.txt
+#
+# Actually, it reads from stdin (presumed to be db.txt) and writes
+# to stdout (presumed to be regdb.c), but close enough...
+#
+# Copyright 2009 John W. Linville <linville@tuxdriver.com>
+#
+# 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
+# published by the Free Software Foundation.
+#
+
+BEGIN {
+ active = 0
+ rules = 0;
+ print "/*"
+ print " * DO NOT EDIT -- file generated from data in db.txt"
+ print " */"
+ print ""
+ print "#include <linux/nl80211.h>"
+ print "#include <net/cfg80211.h>"
+ print ""
+ regdb = "const struct ieee80211_regdomain *reg_regdb[] = {\n"
+}
+
+/^[ \t]*#/ {
+ # Ignore
+}
+
+!active && /^[ \t]*$/ {
+ # Ignore
+}
+
+!active && /country/ {
+ country=$2
+ sub(/:/, "", country)
+ printf "static const struct ieee80211_regdomain regdom_%s = {\n", country
+ printf "\t.alpha2 = \"%s\",\n", country
+ printf "\t.reg_rules = {\n"
+ active = 1
+ regdb = regdb "\t&regdom_" country ",\n"
+}
+
+active && /^[ \t]*\(/ {
+ start = $1
+ sub(/\(/, "", start)
+ end = $3
+ bw = $5
+ sub(/\),/, "", bw)
+ gain = $6
+ sub(/\(/, "", gain)
+ sub(/,/, "", gain)
+ power = $7
+ sub(/\)/, "", power)
+ sub(/,/, "", power)
+ # power might be in mW...
+ units = $8
+ sub(/\)/, "", units)
+ sub(/,/, "", units)
+ if (units == "mW") {
+ if (power == 100) {
+ power = 20
+ } else if (power == 200) {
+ power = 23
+ } else if (power == 500) {
+ power = 27
+ } else if (power == 1000) {
+ power = 30
+ } else {
+ print "Unknown power value in database!"
+ }
+ }
+ flagstr = ""
+ for (i=8; i<=NF; i++)
+ flagstr = flagstr $i
+ split(flagstr, flagarray, ",")
+ flags = ""
+ for (arg in flagarray) {
+ if (flagarray[arg] == "NO-OFDM") {
+ flags = flags "\n\t\t\tNL80211_RRF_NO_OFDM | "
+ } else if (flagarray[arg] == "NO-CCK") {
+ flags = flags "\n\t\t\tNL80211_RRF_NO_CCK | "
+ } else if (flagarray[arg] == "NO-INDOOR") {
+ flags = flags "\n\t\t\tNL80211_RRF_NO_INDOOR | "
+ } else if (flagarray[arg] == "NO-OUTDOOR") {
+ flags = flags "\n\t\t\tNL80211_RRF_NO_OUTDOOR | "
+ } else if (flagarray[arg] == "DFS") {
+ flags = flags "\n\t\t\tNL80211_RRF_DFS | "
+ } else if (flagarray[arg] == "PTP-ONLY") {
+ flags = flags "\n\t\t\tNL80211_RRF_PTP_ONLY | "
+ } else if (flagarray[arg] == "PTMP-ONLY") {
+ flags = flags "\n\t\t\tNL80211_RRF_PTMP_ONLY | "
+ } else if (flagarray[arg] == "PASSIVE-SCAN") {
+ flags = flags "\n\t\t\tNL80211_RRF_PASSIVE_SCAN | "
+ } else if (flagarray[arg] == "NO-IBSS") {
+ flags = flags "\n\t\t\tNL80211_RRF_NO_IBSS | "
+ }
+ }
+ flags = flags "0"
+ printf "\t\tREG_RULE(%d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, flags
+ rules++
+}
+
+active && /^[ \t]*$/ {
+ active = 0
+ printf "\t},\n"
+ printf "\t.n_reg_rules = %d\n", rules
+ printf "};\n\n"
+ rules = 0;
+}
+
+END {
+ print regdb "};"
+ print ""
+ print "int reg_regdb_size = ARRAY_SIZE(reg_regdb);"
+}
diff --git a/net/wireless/lib80211_crypt_ccmp.c b/net/wireless/lib80211_crypt_ccmp.c
index 2301dc1edc4..b7fa31d5fd1 100644
--- a/net/wireless/lib80211_crypt_ccmp.c
+++ b/net/wireless/lib80211_crypt_ccmp.c
@@ -237,7 +237,6 @@ static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
return -1;
pos = skb->data + hdr_len + CCMP_HDR_LEN;
- mic = skb_put(skb, CCMP_MIC_LEN);
hdr = (struct ieee80211_hdr *)skb->data;
ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0);
@@ -257,6 +256,7 @@ static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
pos += len;
}
+ mic = skb_put(skb, CCMP_MIC_LEN);
for (i = 0; i < CCMP_MIC_LEN; i++)
mic[i] = b[i] ^ s0[i];
diff --git a/net/wireless/lib80211_crypt_tkip.c b/net/wireless/lib80211_crypt_tkip.c
index c36287399d7..8cbdb32ff31 100644
--- a/net/wireless/lib80211_crypt_tkip.c
+++ b/net/wireless/lib80211_crypt_tkip.c
@@ -36,6 +36,8 @@ MODULE_AUTHOR("Jouni Malinen");
MODULE_DESCRIPTION("lib80211 crypt: TKIP");
MODULE_LICENSE("GPL");
+#define TKIP_HDR_LEN 8
+
struct lib80211_tkip_data {
#define TKIP_KEY_LEN 32
u8 key[TKIP_KEY_LEN];
@@ -314,13 +316,12 @@ static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len,
u8 * rc4key, int keylen, void *priv)
{
struct lib80211_tkip_data *tkey = priv;
- int len;
u8 *pos;
struct ieee80211_hdr *hdr;
hdr = (struct ieee80211_hdr *)skb->data;
- if (skb_headroom(skb) < 8 || skb->len < hdr_len)
+ if (skb_headroom(skb) < TKIP_HDR_LEN || skb->len < hdr_len)
return -1;
if (rc4key == NULL || keylen < 16)
@@ -333,9 +334,8 @@ static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len,
}
tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16);
- len = skb->len - hdr_len;
- pos = skb_push(skb, 8);
- memmove(pos, pos + 8, hdr_len);
+ pos = skb_push(skb, TKIP_HDR_LEN);
+ memmove(pos, pos + TKIP_HDR_LEN, hdr_len);
pos += hdr_len;
*pos++ = *rc4key;
@@ -353,7 +353,7 @@ static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len,
tkey->tx_iv32++;
}
- return 8;
+ return TKIP_HDR_LEN;
}
static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
@@ -384,9 +384,8 @@ static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
if ((lib80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0)
return -1;
- icv = skb_put(skb, 4);
-
crc = ~crc32_le(~0, pos, len);
+ icv = skb_put(skb, 4);
icv[0] = crc;
icv[1] = crc >> 8;
icv[2] = crc >> 16;
@@ -434,7 +433,7 @@ static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
return -1;
}
- if (skb->len < hdr_len + 8 + 4)
+ if (skb->len < hdr_len + TKIP_HDR_LEN + 4)
return -1;
pos = skb->data + hdr_len;
@@ -462,7 +461,7 @@ static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
}
iv16 = (pos[0] << 8) | pos[2];
iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24);
- pos += 8;
+ pos += TKIP_HDR_LEN;
if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) {
#ifdef CONFIG_LIB80211_DEBUG
@@ -523,8 +522,8 @@ static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
tkey->rx_iv16_new = iv16;
/* Remove IV and ICV */
- memmove(skb->data + 8, skb->data, hdr_len);
- skb_pull(skb, 8);
+ memmove(skb->data + TKIP_HDR_LEN, skb->data, hdr_len);
+ skb_pull(skb, TKIP_HDR_LEN);
skb_trim(skb, skb->len - 4);
return keyidx;
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 82e6002c8d6..62bc8855e12 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -148,22 +148,23 @@ void __cfg80211_send_deauth(struct net_device *dev,
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
const u8 *bssid = mgmt->bssid;
int i;
+ bool found = false;
ASSERT_WDEV_LOCK(wdev);
- nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
-
if (wdev->current_bss &&
memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
cfg80211_unhold_bss(wdev->current_bss);
cfg80211_put_bss(&wdev->current_bss->pub);
wdev->current_bss = NULL;
+ found = true;
} else for (i = 0; i < MAX_AUTH_BSSES; i++) {
if (wdev->auth_bsses[i] &&
memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
cfg80211_unhold_bss(wdev->auth_bsses[i]);
cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
wdev->auth_bsses[i] = NULL;
+ found = true;
break;
}
if (wdev->authtry_bsses[i] &&
@@ -171,10 +172,16 @@ void __cfg80211_send_deauth(struct net_device *dev,
cfg80211_unhold_bss(wdev->authtry_bsses[i]);
cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
wdev->authtry_bsses[i] = NULL;
+ found = true;
break;
}
}
+ if (!found)
+ return;
+
+ nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
+
if (wdev->sme_state == CFG80211_SME_CONNECTED) {
u16 reason_code;
bool from_ap;
@@ -684,3 +691,206 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
}
}
}
+
+void cfg80211_ready_on_channel(struct net_device *dev, u64 cookie,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type,
+ unsigned int duration, gfp_t gfp)
+{
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+ nl80211_send_remain_on_channel(rdev, dev, cookie, chan, channel_type,
+ duration, gfp);
+}
+EXPORT_SYMBOL(cfg80211_ready_on_channel);
+
+void cfg80211_remain_on_channel_expired(struct net_device *dev,
+ u64 cookie,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type,
+ gfp_t gfp)
+{
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+ nl80211_send_remain_on_channel_cancel(rdev, dev, cookie, chan,
+ channel_type, gfp);
+}
+EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
+
+void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
+ struct station_info *sinfo, gfp_t gfp)
+{
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+ nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp);
+}
+EXPORT_SYMBOL(cfg80211_new_sta);
+
+struct cfg80211_action_registration {
+ struct list_head list;
+
+ u32 nlpid;
+
+ int match_len;
+
+ u8 match[];
+};
+
+int cfg80211_mlme_register_action(struct wireless_dev *wdev, u32 snd_pid,
+ const u8 *match_data, int match_len)
+{
+ struct cfg80211_action_registration *reg, *nreg;
+ int err = 0;
+
+ nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL);
+ if (!nreg)
+ return -ENOMEM;
+
+ spin_lock_bh(&wdev->action_registrations_lock);
+
+ list_for_each_entry(reg, &wdev->action_registrations, list) {
+ int mlen = min(match_len, reg->match_len);
+
+ if (memcmp(reg->match, match_data, mlen) == 0) {
+ err = -EALREADY;
+ break;
+ }
+ }
+
+ if (err) {
+ kfree(nreg);
+ goto out;
+ }
+
+ memcpy(nreg->match, match_data, match_len);
+ nreg->match_len = match_len;
+ nreg->nlpid = snd_pid;
+ list_add(&nreg->list, &wdev->action_registrations);
+
+ out:
+ spin_unlock_bh(&wdev->action_registrations_lock);
+ return err;
+}
+
+void cfg80211_mlme_unregister_actions(struct wireless_dev *wdev, u32 nlpid)
+{
+ struct cfg80211_action_registration *reg, *tmp;
+
+ spin_lock_bh(&wdev->action_registrations_lock);
+
+ list_for_each_entry_safe(reg, tmp, &wdev->action_registrations, list) {
+ if (reg->nlpid == nlpid) {
+ list_del(&reg->list);
+ kfree(reg);
+ }
+ }
+
+ spin_unlock_bh(&wdev->action_registrations_lock);
+}
+
+void cfg80211_mlme_purge_actions(struct wireless_dev *wdev)
+{
+ struct cfg80211_action_registration *reg, *tmp;
+
+ spin_lock_bh(&wdev->action_registrations_lock);
+
+ list_for_each_entry_safe(reg, tmp, &wdev->action_registrations, list) {
+ list_del(&reg->list);
+ kfree(reg);
+ }
+
+ spin_unlock_bh(&wdev->action_registrations_lock);
+}
+
+int cfg80211_mlme_action(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type,
+ const u8 *buf, size_t len, u64 *cookie)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ const struct ieee80211_mgmt *mgmt;
+
+ if (rdev->ops->action == NULL)
+ return -EOPNOTSUPP;
+ if (len < 24 + 1)
+ return -EINVAL;
+
+ mgmt = (const struct ieee80211_mgmt *) buf;
+ if (!ieee80211_is_action(mgmt->frame_control))
+ return -EINVAL;
+ if (mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) {
+ /* Verify that we are associated with the destination AP */
+ if (!wdev->current_bss ||
+ memcmp(wdev->current_bss->pub.bssid, mgmt->bssid,
+ ETH_ALEN) != 0 ||
+ memcmp(wdev->current_bss->pub.bssid, mgmt->da,
+ ETH_ALEN) != 0)
+ return -ENOTCONN;
+ }
+
+ if (memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0)
+ return -EINVAL;
+
+ /* Transmit the Action frame as requested by user space */
+ return rdev->ops->action(&rdev->wiphy, dev, chan, channel_type,
+ buf, len, cookie);
+}
+
+bool cfg80211_rx_action(struct net_device *dev, int freq, const u8 *buf,
+ size_t len, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ struct cfg80211_action_registration *reg;
+ const u8 *action_data;
+ int action_data_len;
+ bool result = false;
+
+ /* frame length - min size excluding category */
+ action_data_len = len - (IEEE80211_MIN_ACTION_SIZE - 1);
+
+ /* action data starts with category */
+ action_data = buf + IEEE80211_MIN_ACTION_SIZE - 1;
+
+ spin_lock_bh(&wdev->action_registrations_lock);
+
+ list_for_each_entry(reg, &wdev->action_registrations, list) {
+ if (reg->match_len > action_data_len)
+ continue;
+
+ if (memcmp(reg->match, action_data, reg->match_len))
+ continue;
+
+ /* found match! */
+
+ /* Indicate the received Action frame to user space */
+ if (nl80211_send_action(rdev, dev, reg->nlpid, freq,
+ buf, len, gfp))
+ continue;
+
+ result = true;
+ break;
+ }
+
+ spin_unlock_bh(&wdev->action_registrations_lock);
+
+ return result;
+}
+EXPORT_SYMBOL(cfg80211_rx_action);
+
+void cfg80211_action_tx_status(struct net_device *dev, u64 cookie,
+ const u8 *buf, size_t len, bool ack, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+ /* Indicate TX status of the Action frame to user space */
+ nl80211_send_action_tx_status(rdev, dev, cookie, buf, len, ack, gfp);
+}
+EXPORT_SYMBOL(cfg80211_action_tx_status);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index a6028433e3a..e447db04cf7 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1,7 +1,7 @@
/*
* This is the new netlink-based wireless configuration interface.
*
- * Copyright 2006-2009 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
*/
#include <linux/if.h>
@@ -58,7 +58,7 @@ static int get_rdev_dev_by_info_ifindex(struct genl_info *info,
}
/* policy for the attributes */
-static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
+static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
.len = 20-1 },
@@ -69,6 +69,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
[NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
[NL80211_ATTR_WIPHY_FRA