summaryrefslogtreecommitdiff
path: root/net/mac80211
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-11 19:40:14 -0700
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-11 19:40:14 -0700
commit038a5008b2f395c85e6e71d6ddf3c684e7c405b0 (patch)
tree4735eab577e97e5a22c3141e3f60071c8065585e /net/mac80211
parentdd6d1844af33acb4edd0a40b1770d091a22c94be (diff)
parent266918303226cceac7eca38ced30f15f277bd89c (diff)
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
* 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6: (867 commits) [SKY2]: status polling loop (post merge) [NET]: Fix NAPI completion handling in some drivers. [TCP]: Limit processing lost_retrans loop to work-to-do cases [TCP]: Fix lost_retrans loop vs fastpath problems [TCP]: No need to re-count fackets_out/sacked_out at RTO [TCP]: Extract tcp_match_queue_to_sack from sacktag code [TCP]: Kill almost unused variable pcount from sacktag [TCP]: Fix mark_head_lost to ignore R-bit when trying to mark L [TCP]: Add bytes_acked (ABC) clearing to FRTO too [IPv6]: Update setsockopt(IPV6_MULTICAST_IF) to support RFC 3493, try2 [NETFILTER]: x_tables: add missing ip6t_modulename aliases [NETFILTER]: nf_conntrack_tcp: fix connection reopening [QETH]: fix qeth_main.c [NETLINK]: fib_frontend build fixes [IPv6]: Export userland ND options through netlink (RDNSS support) [9P]: build fix with !CONFIG_SYSCTL [NET]: Fix dev_put() and dev_hold() comments [NET]: make netlink user -> kernel interface synchronious [NET]: unify netlink kernel socket recognition [NET]: cleanup 3rd argument in netlink_sendskb ... Fix up conflicts manually in Documentation/feature-removal-schedule.txt and my new least favourite crap, the "mod_devicetable" support in the files include/linux/mod_devicetable.h and scripts/mod/file2alias.c. (The latter files seem to be explicitly _designed_ to get conflicts when different subsystems work with them - that have an absolutely horrid lack of subsystem separation!) Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/Makefile9
-rw-r--r--net/mac80211/aes_ccm.c5
-rw-r--r--net/mac80211/cfg.c106
-rw-r--r--net/mac80211/cfg.h (renamed from net/mac80211/ieee80211_cfg.h)6
-rw-r--r--net/mac80211/debugfs.c15
-rw-r--r--net/mac80211/debugfs_key.c66
-rw-r--r--net/mac80211/debugfs_netdev.c38
-rw-r--r--net/mac80211/debugfs_sta.c8
-rw-r--r--net/mac80211/event.c43
-rw-r--r--net/mac80211/hostapd_ioctl.h100
-rw-r--r--net/mac80211/ieee80211.c4710
-rw-r--r--net/mac80211/ieee80211_cfg.c66
-rw-r--r--net/mac80211/ieee80211_common.h8
-rw-r--r--net/mac80211/ieee80211_i.h218
-rw-r--r--net/mac80211/ieee80211_iface.c164
-rw-r--r--net/mac80211/ieee80211_ioctl.c726
-rw-r--r--net/mac80211/ieee80211_key.h49
-rw-r--r--net/mac80211/ieee80211_led.c67
-rw-r--r--net/mac80211/ieee80211_led.h6
-rw-r--r--net/mac80211/ieee80211_rate.c41
-rw-r--r--net/mac80211/ieee80211_rate.h8
-rw-r--r--net/mac80211/ieee80211_sta.c428
-rw-r--r--net/mac80211/key.c279
-rw-r--r--net/mac80211/rc80211_simple.c13
-rw-r--r--net/mac80211/regdomain.c6
-rw-r--r--net/mac80211/rx.c1588
-rw-r--r--net/mac80211/sta_info.c221
-rw-r--r--net/mac80211/sta_info.h26
-rw-r--r--net/mac80211/tkip.c44
-rw-r--r--net/mac80211/tkip.h3
-rw-r--r--net/mac80211/tx.c1903
-rw-r--r--net/mac80211/util.c486
-rw-r--r--net/mac80211/wep.c124
-rw-r--r--net/mac80211/wep.h14
-rw-r--r--net/mac80211/wme.c84
-rw-r--r--net/mac80211/wme.h9
-rw-r--r--net/mac80211/wpa.c196
-rw-r--r--net/mac80211/wpa.h8
38 files changed, 5795 insertions, 6096 deletions
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index a9c2d0787d4a..219cd9f9341f 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_MAC80211) += mac80211.o rc80211_simple.o
mac80211-objs-$(CONFIG_MAC80211_LEDS) += ieee80211_led.o
mac80211-objs-$(CONFIG_MAC80211_DEBUGFS) += debugfs.o debugfs_sta.o debugfs_netdev.o debugfs_key.o
+mac80211-objs-$(CONFIG_NET_SCHED) += wme.o
mac80211-objs := \
ieee80211.o \
@@ -16,6 +17,10 @@ mac80211-objs := \
regdomain.o \
tkip.o \
aes_ccm.o \
- wme.o \
- ieee80211_cfg.o \
+ cfg.o \
+ rx.o \
+ tx.o \
+ key.o \
+ util.o \
+ event.o \
$(mac80211-objs-y)
diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c
index e55569bee7d0..bf7ba128b963 100644
--- a/net/mac80211/aes_ccm.c
+++ b/net/mac80211/aes_ccm.c
@@ -7,6 +7,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/crypto.h>
#include <linux/err.h>
@@ -63,7 +64,7 @@ void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
s_0 = scratch + AES_BLOCK_LEN;
e = scratch + 2 * AES_BLOCK_LEN;
- num_blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN;
+ num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
last_len = data_len % AES_BLOCK_LEN;
aes_ccm_prepare(tfm, b_0, aad, b, s_0, b);
@@ -102,7 +103,7 @@ int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
s_0 = scratch + AES_BLOCK_LEN;
a = scratch + 2 * AES_BLOCK_LEN;
- num_blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN;
+ num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
last_len = data_len % AES_BLOCK_LEN;
aes_ccm_prepare(tfm, b_0, aad, b, s_0, a);
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
new file mode 100644
index 000000000000..9e2bc1fd0237
--- /dev/null
+++ b/net/mac80211/cfg.c
@@ -0,0 +1,106 @@
+/*
+ * mac80211 configuration hooks for cfg80211
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This file is GPLv2 as found in COPYING.
+ */
+
+#include <linux/nl80211.h>
+#include <linux/rtnetlink.h>
+#include <net/net_namespace.h>
+#include <net/cfg80211.h>
+#include "ieee80211_i.h"
+#include "cfg.h"
+
+static enum ieee80211_if_types
+nl80211_type_to_mac80211_type(enum nl80211_iftype type)
+{
+ switch (type) {
+ case NL80211_IFTYPE_UNSPECIFIED:
+ return IEEE80211_IF_TYPE_STA;
+ case NL80211_IFTYPE_ADHOC:
+ return IEEE80211_IF_TYPE_IBSS;
+ case NL80211_IFTYPE_STATION:
+ return IEEE80211_IF_TYPE_STA;
+ case NL80211_IFTYPE_MONITOR:
+ return IEEE80211_IF_TYPE_MNTR;
+ default:
+ return IEEE80211_IF_TYPE_INVALID;
+ }
+}
+
+static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
+ enum nl80211_iftype type)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ enum ieee80211_if_types itype;
+
+ if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED))
+ return -ENODEV;
+
+ itype = nl80211_type_to_mac80211_type(type);
+ if (itype == IEEE80211_IF_TYPE_INVALID)
+ return -EINVAL;
+
+ return ieee80211_if_add(local->mdev, name, NULL, itype);
+}
+
+static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct net_device *dev;
+ char *name;
+
+ if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED))
+ return -ENODEV;
+
+ /* we're under RTNL */
+ dev = __dev_get_by_index(&init_net, ifindex);
+ if (!dev)
+ return 0;
+
+ name = dev->name;
+
+ return ieee80211_if_remove(local->mdev, name, -1);
+}
+
+static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex,
+ enum nl80211_iftype type)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct net_device *dev;
+ enum ieee80211_if_types itype;
+ struct ieee80211_sub_if_data *sdata;
+
+ if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED))
+ return -ENODEV;
+
+ /* we're under RTNL */
+ dev = __dev_get_by_index(&init_net, ifindex);
+ if (!dev)
+ return -ENODEV;
+
+ if (netif_running(dev))
+ return -EBUSY;
+
+ itype = nl80211_type_to_mac80211_type(type);
+ if (itype == IEEE80211_IF_TYPE_INVALID)
+ return -EINVAL;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (sdata->type == IEEE80211_IF_TYPE_VLAN)
+ return -EOPNOTSUPP;
+
+ ieee80211_if_reinit(dev);
+ ieee80211_if_set_type(dev, itype);
+
+ return 0;
+}
+
+struct cfg80211_ops mac80211_config_ops = {
+ .add_virtual_intf = ieee80211_add_iface,
+ .del_virtual_intf = ieee80211_del_iface,
+ .change_virtual_intf = ieee80211_change_iface,
+};
diff --git a/net/mac80211/ieee80211_cfg.h b/net/mac80211/cfg.h
index 85ed2c924878..7d7879f5b00b 100644
--- a/net/mac80211/ieee80211_cfg.h
+++ b/net/mac80211/cfg.h
@@ -1,9 +1,9 @@
/*
* mac80211 configuration hooks for cfg80211
*/
-#ifndef __IEEE80211_CFG_H
-#define __IEEE80211_CFG_H
+#ifndef __CFG_H
+#define __CFG_H
extern struct cfg80211_ops mac80211_config_ops;
-#endif /* __IEEE80211_CFG_H */
+#endif /* __CFG_H */
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 476c8486f789..60514b2c97b9 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -28,8 +28,6 @@ static const char *ieee80211_mode_str(int mode)
return "IEEE 802.11b";
case MODE_IEEE80211G:
return "IEEE 802.11g";
- case MODE_ATHEROS_TURBO:
- return "Atheros Turbo (5 GHz)";
default:
return "UNKNOWN";
}
@@ -86,16 +84,12 @@ DEBUGFS_READONLY_FILE(channel, 20, "%d",
local->hw.conf.channel);
DEBUGFS_READONLY_FILE(frequency, 20, "%d",
local->hw.conf.freq);
-DEBUGFS_READONLY_FILE(radar_detect, 20, "%d",
- local->hw.conf.radar_detect);
DEBUGFS_READONLY_FILE(antenna_sel_tx, 20, "%d",
local->hw.conf.antenna_sel_tx);
DEBUGFS_READONLY_FILE(antenna_sel_rx, 20, "%d",
local->hw.conf.antenna_sel_rx);
DEBUGFS_READONLY_FILE(bridge_packets, 20, "%d",
local->bridge_packets);
-DEBUGFS_READONLY_FILE(key_tx_rx_threshold, 20, "%d",
- local->key_tx_rx_threshold);
DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d",
local->rts_threshold);
DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d",
@@ -110,9 +104,6 @@ DEBUGFS_READONLY_FILE(mode, 20, "%s",
ieee80211_mode_str(local->hw.conf.phymode));
DEBUGFS_READONLY_FILE(wep_iv, 20, "%#06x",
local->wep_iv & 0xffffff);
-DEBUGFS_READONLY_FILE(tx_power_reduction, 20, "%d.%d dBm",
- local->hw.conf.tx_power_reduction / 10,
- local->hw.conf.tx_power_reduction % 10);
DEBUGFS_READONLY_FILE(rate_ctrl_alg, 100, "%s",
local->rate_ctrl ? local->rate_ctrl->ops->name : "<unset>");
@@ -305,11 +296,9 @@ void debugfs_hw_add(struct ieee80211_local *local)
DEBUGFS_ADD(channel);
DEBUGFS_ADD(frequency);
- DEBUGFS_ADD(radar_detect);
DEBUGFS_ADD(antenna_sel_tx);
DEBUGFS_ADD(antenna_sel_rx);
DEBUGFS_ADD(bridge_packets);
- DEBUGFS_ADD(key_tx_rx_threshold);
DEBUGFS_ADD(rts_threshold);
DEBUGFS_ADD(fragmentation_threshold);
DEBUGFS_ADD(short_retry_limit);
@@ -317,7 +306,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
DEBUGFS_ADD(total_ps_buffered);
DEBUGFS_ADD(mode);
DEBUGFS_ADD(wep_iv);
- DEBUGFS_ADD(tx_power_reduction);
DEBUGFS_ADD(modes);
statsd = debugfs_create_dir("statistics", phyd);
@@ -370,11 +358,9 @@ void debugfs_hw_del(struct ieee80211_local *local)
{
DEBUGFS_DEL(channel);
DEBUGFS_DEL(frequency);
- DEBUGFS_DEL(radar_detect);
DEBUGFS_DEL(antenna_sel_tx);
DEBUGFS_DEL(antenna_sel_rx);
DEBUGFS_DEL(bridge_packets);
- DEBUGFS_DEL(key_tx_rx_threshold);
DEBUGFS_DEL(rts_threshold);
DEBUGFS_DEL(fragmentation_threshold);
DEBUGFS_DEL(short_retry_limit);
@@ -382,7 +368,6 @@ void debugfs_hw_del(struct ieee80211_local *local)
DEBUGFS_DEL(total_ps_buffered);
DEBUGFS_DEL(mode);
DEBUGFS_DEL(wep_iv);
- DEBUGFS_DEL(tx_power_reduction);
DEBUGFS_DEL(modes);
DEBUGFS_STATS_DEL(transmitted_fragment_count);
diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c
index 7d56dc9e7326..c881524c8725 100644
--- a/net/mac80211/debugfs_key.c
+++ b/net/mac80211/debugfs_key.c
@@ -14,17 +14,18 @@
#include "debugfs.h"
#include "debugfs_key.h"
-#define KEY_READ(name, buflen, format_string) \
+#define KEY_READ(name, prop, buflen, format_string) \
static ssize_t key_##name##_read(struct file *file, \
char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
char buf[buflen]; \
struct ieee80211_key *key = file->private_data; \
- int res = scnprintf(buf, buflen, format_string, key->name); \
+ int res = scnprintf(buf, buflen, format_string, key->prop); \
return simple_read_from_buffer(userbuf, count, ppos, buf, res); \
}
-#define KEY_READ_D(name) KEY_READ(name, 20, "%d\n")
+#define KEY_READ_D(name) KEY_READ(name, name, 20, "%d\n")
+#define KEY_READ_X(name) KEY_READ(name, name, 20, "0x%x\n")
#define KEY_OPS(name) \
static const struct file_operations key_ ##name## _ops = { \
@@ -36,11 +37,27 @@ static const struct file_operations key_ ##name## _ops = { \
KEY_READ_##format(name) \
KEY_OPS(name)
-KEY_FILE(keylen, D);
-KEY_FILE(force_sw_encrypt, D);
-KEY_FILE(keyidx, D);
-KEY_FILE(hw_key_idx, D);
+#define KEY_CONF_READ(name, buflen, format_string) \
+ KEY_READ(conf_##name, conf.name, buflen, format_string)
+#define KEY_CONF_READ_D(name) KEY_CONF_READ(name, 20, "%d\n")
+
+#define KEY_CONF_OPS(name) \
+static const struct file_operations key_ ##name## _ops = { \
+ .read = key_conf_##name##_read, \
+ .open = mac80211_open_file_generic, \
+}
+
+#define KEY_CONF_FILE(name, format) \
+ KEY_CONF_READ_##format(name) \
+ KEY_CONF_OPS(name)
+
+KEY_CONF_FILE(keylen, D);
+KEY_CONF_FILE(keyidx, D);
+KEY_CONF_FILE(hw_key_idx, D);
+KEY_FILE(flags, X);
KEY_FILE(tx_rx_count, D);
+KEY_READ(ifindex, sdata->dev->ifindex, 20, "%d\n");
+KEY_OPS(ifindex);
static ssize_t key_algorithm_read(struct file *file,
char __user *userbuf,
@@ -49,7 +66,7 @@ static ssize_t key_algorithm_read(struct file *file,
char *alg;
struct ieee80211_key *key = file->private_data;
- switch (key->alg) {
+ switch (key->conf.alg) {
case ALG_WEP:
alg = "WEP\n";
break;
@@ -74,17 +91,20 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf,
int len;
struct ieee80211_key *key = file->private_data;
- switch (key->alg) {
+ switch (key->conf.alg) {
case ALG_WEP:
len = scnprintf(buf, sizeof(buf), "\n");
+ break;
case ALG_TKIP:
len = scnprintf(buf, sizeof(buf), "%08x %04x\n",
key->u.tkip.iv32,
key->u.tkip.iv16);
+ break;
case ALG_CCMP:
tpn = key->u.ccmp.tx_pn;
len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n",
tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], tpn[5]);
+ break;
default:
return 0;
}
@@ -100,9 +120,10 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,
int i, len;
const u8 *rpn;
- switch (key->alg) {
+ switch (key->conf.alg) {
case ALG_WEP:
len = scnprintf(buf, sizeof(buf), "\n");
+ break;
case ALG_TKIP:
for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
p += scnprintf(p, sizeof(buf)+buf-p,
@@ -110,6 +131,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,
key->u.tkip.iv32_rx[i],
key->u.tkip.iv16_rx[i]);
len = p - buf;
+ break;
case ALG_CCMP:
for (i = 0; i < NUM_RX_DATA_QUEUES; i++) {
rpn = key->u.ccmp.rx_pn[i];
@@ -119,6 +141,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,
rpn[3], rpn[4], rpn[5]);
}
len = p - buf;
+ break;
default:
return 0;
}
@@ -133,7 +156,7 @@ static ssize_t key_replays_read(struct file *file, char __user *userbuf,
char buf[20];
int len;
- if (key->alg != ALG_CCMP)
+ if (key->conf.alg != ALG_CCMP)
return 0;
len = scnprintf(buf, sizeof(buf), "%u\n", key->u.ccmp.replays);
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
@@ -144,12 +167,12 @@ static ssize_t key_key_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct ieee80211_key *key = file->private_data;
- int i, res, bufsize = 2*key->keylen+2;
+ int i, res, bufsize = 2 * key->conf.keylen + 2;
char *buf = kmalloc(bufsize, GFP_KERNEL);
char *p = buf;
- for (i = 0; i < key->keylen; i++)
- p += scnprintf(p, bufsize+buf-p, "%02x", key->key[i]);
+ for (i = 0; i < key->conf.keylen; i++)
+ p += scnprintf(p, bufsize + buf - p, "%02x", key->conf.key[i]);
p += scnprintf(p, bufsize+buf-p, "\n");
res = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
kfree(buf);
@@ -164,12 +187,14 @@ KEY_OPS(key);
void ieee80211_debugfs_key_add(struct ieee80211_local *local,
struct ieee80211_key *key)
{
+ static int keycount;
char buf[20];
if (!local->debugfs.keys)
return;
- sprintf(buf, "%d", key->keyidx);
+ sprintf(buf, "%d", keycount);
+ keycount++;
key->debugfs.dir = debugfs_create_dir(buf,
local->debugfs.keys);
@@ -177,7 +202,7 @@ void ieee80211_debugfs_key_add(struct ieee80211_local *local,
return;
DEBUGFS_ADD(keylen);
- DEBUGFS_ADD(force_sw_encrypt);
+ DEBUGFS_ADD(flags);
DEBUGFS_ADD(keyidx);
DEBUGFS_ADD(hw_key_idx);
DEBUGFS_ADD(tx_rx_count);
@@ -186,6 +211,7 @@ void ieee80211_debugfs_key_add(struct ieee80211_local *local,
DEBUGFS_ADD(rx_spec);
DEBUGFS_ADD(replays);
DEBUGFS_ADD(key);
+ DEBUGFS_ADD(ifindex);
};
#define DEBUGFS_DEL(name) \
@@ -197,7 +223,7 @@ void ieee80211_debugfs_key_remove(struct ieee80211_key *key)
return;
DEBUGFS_DEL(keylen);
- DEBUGFS_DEL(force_sw_encrypt);
+ DEBUGFS_DEL(flags);
DEBUGFS_DEL(keyidx);
DEBUGFS_DEL(hw_key_idx);
DEBUGFS_DEL(tx_rx_count);
@@ -206,6 +232,7 @@ void ieee80211_debugfs_key_remove(struct ieee80211_key *key)
DEBUGFS_DEL(rx_spec);
DEBUGFS_DEL(replays);
DEBUGFS_DEL(key);
+ DEBUGFS_DEL(ifindex);
debugfs_remove(key->debugfs.stalink);
key->debugfs.stalink = NULL;
@@ -219,7 +246,7 @@ void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata)
if (!sdata->debugfsdir)
return;
- sprintf(buf, "../keys/%d", sdata->default_key->keyidx);
+ sprintf(buf, "../keys/%d", sdata->default_key->conf.keyidx);
sdata->debugfs.default_key =
debugfs_create_symlink("default_key", sdata->debugfsdir, buf);
}
@@ -235,11 +262,12 @@ void ieee80211_debugfs_key_sta_link(struct ieee80211_key *key,
struct sta_info *sta)
{
char buf[50];
+ DECLARE_MAC_BUF(mac);
if (!key->debugfs.dir)
return;
- sprintf(buf, "../sta/" MAC_FMT, MAC_ARG(sta->addr));
+ sprintf(buf, "../../stations/%s", print_mac(mac, sta->addr));
key->debugfs.stalink =
debugfs_create_symlink("station", key->debugfs.dir, buf);
}
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 095be91829ca..f0e6ab7eb624 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -66,7 +66,8 @@ static ssize_t ieee80211_if_fmt_##name( \
const struct ieee80211_sub_if_data *sdata, char *buf, \
int buflen) \
{ \
- return scnprintf(buf, buflen, MAC_FMT "\n", MAC_ARG(sdata->field));\
+ DECLARE_MAC_BUF(mac); \
+ return scnprintf(buf, buflen, "%s\n", print_mac(mac, sdata->field));\
}
#define __IEEE80211_IF_FILE(name) \
@@ -112,13 +113,13 @@ static ssize_t ieee80211_if_fmt_flags(
const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
{
return scnprintf(buf, buflen, "%s%s%s%s%s%s%s\n",
- sdata->u.sta.ssid_set ? "SSID\n" : "",
- sdata->u.sta.bssid_set ? "BSSID\n" : "",
- sdata->u.sta.prev_bssid_set ? "prev BSSID\n" : "",
- sdata->u.sta.authenticated ? "AUTH\n" : "",
- sdata->u.sta.associated ? "ASSOC\n" : "",
- sdata->u.sta.probereq_poll ? "PROBEREQ POLL\n" : "",
- sdata->use_protection ? "CTS prot\n" : "");
+ sdata->u.sta.flags & IEEE80211_STA_SSID_SET ? "SSID\n" : "",
+ sdata->u.sta.flags & IEEE80211_STA_BSSID_SET ? "BSSID\n" : "",
+ sdata->u.sta.flags & IEEE80211_STA_PREV_BSSID_SET ? "prev BSSID\n" : "",
+ sdata->u.sta.flags & IEEE80211_STA_AUTHENTICATED ? "AUTH\n" : "",
+ sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED ? "ASSOC\n" : "",
+ sdata->u.sta.flags & IEEE80211_STA_PROBEREQ_POLL ? "PROBEREQ POLL\n" : "",
+ sdata->flags & IEEE80211_SDATA_USE_PROTECTION ? "CTS prot\n" : "");
}
__IEEE80211_IF_FILE(flags);
@@ -161,23 +162,6 @@ __IEEE80211_IF_FILE(beacon_tail_len);
/* WDS attributes */
IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC);
-/* VLAN attributes */
-IEEE80211_IF_FILE(vlan_id, u.vlan.id, DEC);
-
-/* MONITOR attributes */
-static ssize_t ieee80211_if_fmt_mode(
- const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
-{
- struct ieee80211_local *local = sdata->local;
-
- return scnprintf(buf, buflen, "%s\n",
- ((local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER) ||
- local->open_count == local->monitors) ?
- "hard" : "soft");
-}
-__IEEE80211_IF_FILE(mode);
-
-
#define DEBUGFS_ADD(name, type)\
sdata->debugfs.type.name = debugfs_create_file(#name, 0444,\
sdata->debugfsdir, sdata, &name##_ops);
@@ -236,12 +220,10 @@ static void add_vlan_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(drop_unencrypted, vlan);
DEBUGFS_ADD(eapol, vlan);
DEBUGFS_ADD(ieee8021_x, vlan);
- DEBUGFS_ADD(vlan_id, vlan);
}
static void add_monitor_files(struct ieee80211_sub_if_data *sdata)
{
- DEBUGFS_ADD(mode, monitor);
}
static void add_files(struct ieee80211_sub_if_data *sdata)
@@ -331,12 +313,10 @@ static void del_vlan_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_DEL(drop_unencrypted, vlan);
DEBUGFS_DEL(eapol, vlan);
DEBUGFS_DEL(ieee8021_x, vlan);
- DEBUGFS_DEL(vlan_id, vlan);
}
static void del_monitor_files(struct ieee80211_sub_if_data *sdata)
{
- DEBUGFS_DEL(mode, monitor);
}
static void del_files(struct ieee80211_sub_if_data *sdata, int type)
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index da34ea70276f..8f5944c53d4e 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -60,9 +60,7 @@ static const struct file_operations sta_ ##name## _ops = { \
STA_OPS(name)
STA_FILE(aid, aid, D);
-STA_FILE(key_idx_compression, key_idx_compression, D);
STA_FILE(dev, dev->name, S);
-STA_FILE(vlan_id, vlan_id, D);
STA_FILE(rx_packets, rx_packets, LU);
STA_FILE(tx_packets, tx_packets, LU);
STA_FILE(rx_bytes, rx_bytes, LU);
@@ -204,15 +202,15 @@ STA_OPS(wme_tx_queue);
void ieee80211_sta_debugfs_add(struct sta_info *sta)
{
- char buf[3*6];
struct dentry *stations_dir = sta->local->debugfs.stations;
+ DECLARE_MAC_BUF(mac);
if (!stations_dir)
return;
- sprintf(buf, MAC_FMT, MAC_ARG(sta->addr));
+ print_mac(mac, sta->addr);
- sta->debugfs.dir = debugfs_create_dir(buf, stations_dir);
+ sta->debugfs.dir = debugfs_create_dir(mac, stations_dir);
if (!sta->debugfs.dir)
return;
diff --git a/net/mac80211/event.c b/net/mac80211/event.c
new file mode 100644
index 000000000000..2280f40b4560
--- /dev/null
+++ b/net/mac80211/event.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2007 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
+ * published by the Free Software Foundation.
+ *
+ * mac80211 - events
+ */
+
+#include <linux/netdevice.h>
+#include <net/iw_handler.h>
+#include "ieee80211_i.h"
+
+/*
+ * indicate a failed Michael MIC to userspace; the passed packet
+ * (in the variable hdr) must be long enough to extract the TKIP
+ * fields like TSC
+ */
+void mac80211_ev_michael_mic_failure(struct net_device *dev, int keyidx,
+ struct ieee80211_hdr *hdr)
+{
+ union iwreq_data wrqu;
+ char *buf = kmalloc(128, GFP_ATOMIC);
+ DECLARE_MAC_BUF(mac);
+
+ if (buf) {
+ /* TODO: needed parameters: count, key type, TSC */
+ sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
+ "keyid=%d %scast addr=%s)",
+ keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni",
+ print_mac(mac, hdr->addr2));
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = strlen(buf);
+ wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
+ kfree(buf);
+ }
+
+ /*
+ * TODO: re-add support for sending MIC failure indication
+ * with all info via nl80211
+ */
+}
diff --git a/net/mac80211/hostapd_ioctl.h b/net/mac80211/hostapd_ioctl.h
deleted file mode 100644
index 52da513f060a..000000000000
--- a/net/mac80211/hostapd_ioctl.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Host AP (software wireless LAN access point) user space daemon for
- * Host AP kernel driver
- * Copyright 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
- * Copyright 2002-2004, Instant802 Networks, Inc.
- * Copyright 2005, Devicescape Software, Inc.
- *
- * 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.
- */
-
-#ifndef HOSTAPD_IOCTL_H
-#define HOSTAPD_IOCTL_H
-
-#ifdef __KERNEL__
-#include <linux/types.h>
-#endif /* __KERNEL__ */
-
-#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0)
-#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1)
-#define PRISM2_IOCTL_HOSTAPD (SIOCIWFIRSTPRIV + 3)
-
-/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes:
- * This table is no longer added to, the whole sub-ioctl
- * mess shall be deleted completely. */
-enum {
- PRISM2_PARAM_IEEE_802_1X = 23,
-
- /* Instant802 additions */
- PRISM2_PARAM_CTS_PROTECT_ERP_FRAMES = 1001,
- PRISM2_PARAM_PREAMBLE = 1003,
- PRISM2_PARAM_SHORT_SLOT_TIME = 1006,
- PRISM2_PARAM_NEXT_MODE = 1008,
- PRISM2_PARAM_RADIO_ENABLED = 1010,
- PRISM2_PARAM_ANTENNA_MODE = 1013,
- PRISM2_PARAM_STAT_TIME = 1016,
- PRISM2_PARAM_STA_ANTENNA_SEL = 1017,
- PRISM2_PARAM_TX_POWER_REDUCTION = 1022,
- PRISM2_PARAM_KEY_TX_RX_THRESHOLD = 1024,
- PRISM2_PARAM_DEFAULT_WEP_ONLY = 1026,
- PRISM2_PARAM_WIFI_WME_NOACK_TEST = 1033,
- PRISM2_PARAM_SCAN_FLAGS = 1035,
- PRISM2_PARAM_HW_MODES = 1036,
- PRISM2_PARAM_CREATE_IBSS = 1037,
- PRISM2_PARAM_WMM_ENABLED = 1038,
- PRISM2_PARAM_MIXED_CELL = 1039,
- PRISM2_PARAM_RADAR_DETECT = 1043,
- PRISM2_PARAM_SPECTRUM_MGMT = 1044,
-};
-
-enum {
- IEEE80211_KEY_MGMT_NONE = 0,
- IEEE80211_KEY_MGMT_IEEE8021X = 1,
- IEEE80211_KEY_MGMT_WPA_PSK = 2,
- IEEE80211_KEY_MGMT_WPA_EAP = 3,
-};
-
-
-/* Data structures used for get_hw_features ioctl */
-struct hostapd_ioctl_hw_modes_hdr {
- int mode;
- int num_channels;
- int num_rates;
-};
-
-struct ieee80211_channel_data {
- short chan; /* channel number (IEEE 802.11) */
- short freq; /* frequency in MHz */
- int flag; /* flag for hostapd use (IEEE80211_CHAN_*) */
-};
-
-struct ieee80211_rate_data {
- int rate; /* rate in 100 kbps */
- int flags; /* IEEE80211_RATE_ flags */
-};
-
-
-/* ADD_IF, REMOVE_IF, and UPDATE_IF 'type' argument */
-enum {
- HOSTAP_IF_WDS = 1, HOSTAP_IF_VLAN = 2, HOSTAP_IF_BSS = 3,
- HOSTAP_IF_STA = 4
-};
-
-struct hostapd_if_wds {
- u8 remote_addr[ETH_ALEN];
-};
-
-struct hostapd_if_vlan {
- u8 id;
-};
-
-struct hostapd_if_bss {
- u8 bssid[ETH_ALEN];
-};
-
-struct hostapd_if_sta {
-};
-
-#endif /* HOSTAPD_IOCTL_H */
diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c
index ff2172ffd861..f484ca7ade9c 100644
--- a/net/mac80211/ieee80211.c
+++ b/net/mac80211/ieee80211.c
@@ -20,42 +20,19 @@
#include <linux/if_arp.h>
#include <linux/wireless.h>
#include <linux/rtnetlink.h>
-#include <net/iw_handler.h>
-#include <linux/compiler.h>
#include <linux/bitmap.h>
+#include <net/net_namespace.h>
#include <net/cfg80211.h>
-#include <asm/unaligned.h>
-#include "ieee80211_common.h"
#include "ieee80211_i.h"
#include "ieee80211_rate.h"
#include "wep.h"
-#include "wpa.h"
-#include "tkip.h"
#include "wme.h"
#include "aes_ccm.h"
#include "ieee80211_led.h"
-#include "ieee80211_cfg.h"
+#include "cfg.h"
#include "debugfs.h"
#include "debugfs_netdev.h"
-#include "debugfs_key.h"
-
-/* privid for wiphys to determine whether they belong to us or not */
-void *mac80211_wiphy_privid = &mac80211_wiphy_privid;
-
-/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
-/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
-static const unsigned char rfc1042_header[] =
- { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
-
-/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
-static const unsigned char bridge_tunnel_header[] =
- { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
-
-/* No encapsulation header if EtherType < 0x600 (=length) */
-static const unsigned char eapol_header[] =
- { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x88, 0x8e };
-
/*
* For seeing transmitted packets on monitor interfaces
@@ -67,2217 +44,408 @@ struct ieee80211_tx_status_rtap_hdr {
u8 data_retries;
} __attribute__ ((packed));
+/* common interface routines */
-static inline void ieee80211_include_sequence(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_hdr *hdr)
-{
- /* Set the sequence number for this frame. */
- hdr->seq_ctrl = cpu_to_le16(sdata->sequence);
-
- /* Increase the sequence number. */
- sdata->sequence = (sdata->sequence + 0x10) & IEEE80211_SCTL_SEQ;
-}
-
-struct ieee80211_key_conf *
-ieee80211_key_data2conf(struct ieee80211_local *local,
- const struct ieee80211_key *data)
+static int header_parse_80211(const struct sk_buff *skb, unsigned char *haddr)
{
- struct ieee80211_key_conf *conf;
-
- conf = kmalloc(sizeof(*conf) + data->keylen, GFP_ATOMIC);
- if (!conf)
- return NULL;
-
- conf->hw_key_idx = data->hw_key_idx;
- conf->alg = data->alg;
- conf->keylen = data->keylen;
- conf->flags = 0;
- if (data->force_sw_encrypt)
- conf->flags |= IEEE80211_KEY_FORCE_SW_ENCRYPT;
- conf->keyidx = data->keyidx;
- if (data->default_tx_key)
- conf->flags |= IEEE80211_KEY_DEFAULT_TX_KEY;
- if (local->default_wep_only)
- conf->flags |= IEEE80211_KEY_DEFAULT_WEP_ONLY;
- memcpy(conf->key, data->key, data->keylen);
-
- return conf;
+ memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */
+ return ETH_ALEN;
}
-struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
- int idx, size_t key_len, gfp_t flags)
+/* must be called under mdev tx lock */
+static void ieee80211_configure_filter(struct ieee80211_local *local)
{
- struct ieee80211_key *key;
+ unsigned int changed_flags;
+ unsigned int new_flags = 0;
- key = kzalloc(sizeof(struct ieee80211_key) + key_len, flags);
- if (!key)
- return NULL;
- kref_init(&key->kref);
- return key;
-}
+ if (atomic_read(&local->iff_promiscs))
+ new_flags |= FIF_PROMISC_IN_BSS;
-static void ieee80211_key_release(struct kref *kref)
-{
- struct ieee80211_key *key;
+ if (atomic_read(&local->iff_allmultis))
+ new_flags |= FIF_ALLMULTI;
- key = container_of(kref, struct ieee80211_key, kref);
- if (key->alg == ALG_CCMP)
- ieee80211_aes_key_free(key->u.ccmp.tfm);
- ieee80211_debugfs_key_remove(key);
- kfree(key);
-}
+ if (local->monitors)
+ new_flags |= FIF_CONTROL |
+ FIF_OTHER_BSS |
+ FIF_BCN_PRBRESP_PROMISC;
-void ieee80211_key_free(struct ieee80211_key *key)
-{
- if (key)
- kref_put(&key->kref, ieee80211_key_release);
-}
+ changed_flags = local->filter_flags ^ new_flags;
-static int rate_list_match(const int *rate_list, int rate)
-{
- int i;
+ /* be a bit nasty */
+ new_flags |= (1<<31);
- if (!rate_list)
- return 0;
+ local->ops->configure_filter(local_to_hw(local),
+ changed_flags, &new_flags,
+ local->mdev->mc_count,
+ local->mdev->mc_list);
- for (i = 0; rate_list[i] >= 0; i++)
- if (rate_list[i] == rate)
- return 1;
+ WARN_ON(new_flags & (1<<31));
- return 0;
+ local->filter_flags = new_flags & ~(1<<31);
}
+/* master interface */
-void ieee80211_prepare_rates(struct ieee80211_local *local,
- struct ieee80211_hw_mode *mode)
+static int ieee80211_master_open(struct net_device *dev)
{
- int i;
-
- for (i = 0; i < mode->num_rates; i++) {
- struct ieee80211_rate *rate = &mode->rates[i];
-
- rate->flags &= ~(IEEE80211_RATE_SUPPORTED |
- IEEE80211_RATE_BASIC);
-
- if (local->supp_rates[mode->mode]) {
- if (!rate_list_match(local->supp_rates[mode->mode],
- rate->rate))
- continue;
- }
-
- rate->flags |= IEEE80211_RATE_SUPPORTED;
-
- /* Use configured basic rate set if it is available. If not,
- * use defaults that are sane for most cases. */
- if (local->basic_rates[mode->mode]) {
- if (rate_list_match(local->basic_rates[mode->mode],
- rate->rate))
- rate->flags |= IEEE80211_RATE_BASIC;
- } else switch (mode->mode) {
- case MODE_IEEE80211A:
- if (rate->rate == 60 || rate->rate == 120 ||
- rate->rate == 240)
- rate->flags |= IEEE80211_RATE_BASIC;
- break;
- case MODE_IEEE80211B:
- if (rate->rate == 10 || rate->rate == 20)
- rate->flags |= IEEE80211_RATE_BASIC;
- break;
- case MODE_ATHEROS_TURBO:
- if (rate->rate == 120 || rate->rate == 240 ||
- rate->rate == 480)
- rate->flags |= IEEE80211_RATE_BASIC;
- break;
- case MODE_IEEE80211G:
- if (rate->rate == 10 || rate->rate == 20 ||
- rate->rate == 55 || rate->rate == 110)
- rate->flags |= IEEE80211_RATE_BASIC;
- break;
- }
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_sub_if_data *sdata;
+ int res = -EOPNOTSUPP;
- /* Set ERP and MANDATORY flags based on phymode */
- switch (mode->mode) {
- case MODE_IEEE80211A:
- if (rate->rate == 60 || rate->rate == 120 ||
- rate->rate == 240)
- rate->flags |= IEEE80211_RATE_MANDATORY;
- break;
- case MODE_IEEE80211B:
- if (rate->rate == 10)
- rate->flags |= IEEE80211_RATE_MANDATORY;
- break;
- case MODE_ATHEROS_TURBO:
- break;
- case MODE_IEEE80211G:
- if (rate->rate == 10 || rate->rate == 20 ||
- rate->rate == 55 || rate->rate == 110 ||
- rate->rate == 60 || rate->rate == 120 ||
- rate->rate == 240)
- rate->flags |= IEEE80211_RATE_MANDATORY;
+ /* we hold the RTNL here so can safely walk the list */
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (sdata->dev != dev && netif_running(sdata->dev)) {
+ res = 0;
break;
}
- if (ieee80211_is_erp_rate(mode->mode, rate->rate))
- rate->flags |= IEEE80211_RATE_ERP;
}
+ return res;
}
-
-static void ieee80211_key_threshold_notify(struct net_device *dev,
- struct ieee80211_key *key,
- struct sta_info *sta)
+static int ieee80211_master_stop(struct net_device *dev)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct sk_buff *skb;
- struct ieee80211_msg_key_notification *msg;
-
- /* if no one will get it anyway, don't even allocate it.
- * unlikely because this is only relevant for APs
- * where the device must be open... */
- if (unlikely(!local->apdev))
- return;
-
- skb = dev_alloc_skb(sizeof(struct ieee80211_frame_info) +
- sizeof(struct ieee80211_msg_key_notification));
- if (!skb)
- return;
-
- skb_reserve(skb, sizeof(struct ieee80211_frame_info));
- msg = (struct ieee80211_msg_key_notification *)
- skb_put(skb, sizeof(struct ieee80211_msg_key_notification));
- msg->tx_rx_count = key->tx_rx_count;
- memcpy(msg->ifname, dev->name, IFNAMSIZ);
- if (sta)
- memcpy(msg->addr, sta->addr, ETH_ALEN);
- else
- memset(msg->addr, 0xff, ETH_ALEN);
-
- key->tx_rx_count = 0;
-
- ieee80211_rx_mgmt(local, skb, NULL,
- ieee80211_msg_key_threshold_notification);
-}
-
-
-static u8 * ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len)
-{
- u16 fc;
-
- if (len < 24)
- return NULL;
-
- fc = le16_to_cpu(hdr->frame_control);
-
- switch (fc & IEEE80211_FCTL_FTYPE) {
- case IEEE80211_FTYPE_DATA:
- switch (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
- case IEEE80211_FCTL_TODS:
- return hdr->addr1;
- case (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
- return NULL;
- case IEEE80211_FCTL_FROMDS:
- return hdr->addr2;
- case 0:
- return hdr->addr3;
- }
- break;
- case IEEE80211_FTYPE_MGMT:
- return hdr->addr3;
- case IEEE80211_FTYPE_CTL:
- if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)
- return hdr->addr1;
- else
- return NULL;
- }
-
- return NULL;
-}
-
-int ieee80211_get_hdrlen(u16 fc)
-{
- int hdrlen = 24;
-
- switch (fc & IEEE80211_FCTL_FTYPE) {
- case IEEE80211_FTYPE_DATA:
- if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS))
- hdrlen = 30; /* Addr4 */
- /*
- * The QoS Control field is two bytes and its presence is
- * indicated by the IEEE80211_STYPE_QOS_DATA bit. Add 2 to
- * hdrlen if that bit is set.
- * This works by masking out the bit and shifting it to
- * bit position 1 so the result has the value 0 or 2.
- */
- hdrlen += (fc & IEEE80211_STYPE_QOS_DATA)
- >> (ilog2(IEEE80211_STYPE_QOS_DATA)-1);
- break;
- case IEEE80211_FTYPE_CTL:
- /*
- * ACK and CTS are 10 bytes, all others 16. To see how
- * to get this condition consider
- * subtype mask: 0b0000000011110000 (0x00F0)
- * ACK subtype: 0b0000000011010000 (0x00D0)
- * CTS subtype: 0b0000000011000000 (0x00C0)
- * bits that matter: ^^^ (0x00E0)
- * value of those: 0b0000000011000000 (0x00C0)
- */
- if ((fc & 0xE0) == 0xC0)
- hdrlen = 10;
- else
- hdrlen = 16;
- break;
- }
-
- return hdrlen;
-}
-EXPORT_SYMBOL(ieee80211_get_hdrlen);
-
-int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
-{
- const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) skb->data;
- int hdrlen;
-
- if (unlikely(skb->len < 10))
- return 0;
- hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control));
- if (unlikely(hdrlen > skb->len))
- return 0;
- return hdrlen;
-}
-EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
-
-static int ieee80211_get_radiotap_len(struct sk_buff *skb)
-{
- struct ieee80211_radiotap_header *hdr =
- (struct ieee80211_radiotap_header *) skb->data;
-
- return le16_to_cpu(hdr->it_len);
-}
-
-#ifdef CONFIG_MAC80211_LOWTX_FRAME_DUMP
-static void ieee80211_dump_frame(const char *ifname, const char *title,
- const struct sk_buff *skb)
-{
- const struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- u16 fc;
- int hdrlen;
-
- printk(KERN_DEBUG "%s: %s (len=%d)", ifname, title, skb->len);
- if (skb->len < 4) {
- printk("\n");
- return;
- }
-
- fc = le16_to_cpu(hdr->frame_control);
- hdrlen = ieee80211_get_hdrlen(fc);
- if (hdrlen > skb->len)
- hdrlen = skb->len;
- if (hdrlen >= 4)
- printk(" FC=0x%04x DUR=0x%04x",
- fc, le16_to_cpu(hdr->duration_id));
- if (hdrlen >= 10)
- printk(" A1=" MAC_FMT, MAC_ARG(hdr->addr1));
- if (hdrlen >= 16)
- printk(" A2=" MAC_FMT, MAC_ARG(hdr->addr2));
- if (hdrlen >= 24)
- printk(" A3=" MAC_FMT, MAC_ARG(hdr->addr3));
- if (hdrlen >= 30)
- printk(" A4=" MAC_FMT, MAC_ARG(hdr->addr4));
- printk("\n");
-}
-#else /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */
-static inline void ieee80211_dump_frame(const char *ifname, const char *title,
- struct sk_buff *skb)
-{
-}
-#endif /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */
-
-
-static int ieee80211_is_eapol(const struct sk_buff *skb)
-{
- const struct ieee80211_hdr *hdr;
- u16 fc;
- int hdrlen;
-
- if (unlikely(skb->len < 10))
- return 0;
-
- hdr = (const struct ieee80211_hdr *) skb->data;
- fc = le16_to_cpu(hdr->frame_control);
-
- if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
- return 0;
-
- hdrlen = ieee80211_get_hdrlen(fc);
+ struct ieee80211_sub_if_data *sdata;
- if (unlikely(skb->len >= hdrlen + sizeof(eapol_header) &&
- memcmp(skb->data + hdrlen, eapol_header,
- sizeof(eapol_header)) == 0))
- return 1;
+ /* we hold the RTNL here so can safely walk the list */
+ list_for_each_entry(sdata, &local->interfaces, list)
+ if (sdata->dev != dev && netif_running(sdata->dev))
+ dev_close(sdata->dev);
return 0;
}
-
-static ieee80211_txrx_result
-ieee80211_tx_h_rate_ctrl(struct ieee80211_txrx_data *tx)
-{
- struct rate_control_extra extra;
-
- memset(&extra, 0, sizeof(extra));
- extra.mode = tx->u.tx.mode;
- extra.mgmt_data = tx->sdata &&
- tx->sdata->type == IEEE80211_IF_TYPE_MGMT;
- extra.ethertype = tx->ethertype;
-
- tx->u.tx.rate = rate_control_get_rate(tx->local, tx->dev, tx->skb,
- &extra);
- if (unlikely(extra.probe != NULL)) {
- tx->u.tx.control->flags |= IEEE80211_TXCTL_RATE_CTRL_PROBE;
- tx->u.tx.probe_last_frag = 1;
- tx->u.tx.control->alt_retry_rate = tx->u.tx.rate->val;
- tx->u.tx.rate = extra.probe;
- } else {
- tx->u.tx.control->alt_retry_rate = -1;
- }
- if (!tx->u.tx.rate)
- return TXRX_DROP;
- if (tx->u.tx.mode->mode == MODE_IEEE80211G &&
- tx->sdata->use_protection && tx->fragmented &&
- extra.nonerp) {
- tx->u.tx.last_frag_rate = tx->u.tx.rate;
- tx->u.tx.probe_last_frag = extra.probe ? 1 : 0;
-
- tx->u.tx.rate = extra.nonerp;
- tx->u.tx.control->rate = extra.nonerp;
- tx->u.tx.control->flags &= ~IEEE80211_TXCTL_RATE_CTRL_PROBE;
- } else {
- tx->u.tx.last_frag_rate = tx->u.tx.rate;
- tx->u.tx.control->rate = tx->u.tx.rate;
- }
- tx->u.tx.control->tx_rate = tx->u.tx.rate->val;
- if ((tx->u.tx.rate->flags & IEEE80211_RATE_PREAMBLE2) &&
- tx->local->short_preamble &&
- (!tx->sta || (tx->sta->flags & WLAN_STA_SHORT_PREAMBLE))) {
- tx->u.tx.short_preamble = 1;
- tx->u.tx.control->tx_rate = tx->u.tx.rate->val2;
- }
-
- return TXRX_CONTINUE;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_tx_h_select_key(struct ieee80211_txrx_data *tx)
+static void ieee80211_master_set_multicast_list(struct net_device *dev)
{
- if (tx->sta)
- tx->u.tx.control->key_idx = tx->sta->key_idx_compression;
- else
- tx->u.tx.control->key_idx = HW_KEY_IDX_INVALID;
-
- if (unlikely(tx->u.tx.control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT))
- tx->key = NULL;
- else if (tx->sta && tx->sta->key)
- tx->key = tx->sta->key;
- else if (tx->sdata->default_key)
- tx->key = tx->sdata->default_key;
- else if (tx->sdata->drop_unencrypted &&
- !(tx->sdata->eapol && ieee80211_is_eapol(tx->skb))) {
- I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted);
- return TXRX_DROP;
- } else
- tx->key = NULL;
-
- if (tx->key) {
- tx->key->tx_rx_count++;
- if (unlikely(tx->local->key_tx_rx_threshold &&
- tx->key->tx_rx_count >
- tx->local->key_tx_rx_threshold)) {
- ieee80211_key_threshold_notify(tx->dev, tx->key,
- tx->sta);
- }
- }
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- return TXRX_CONTINUE;
+ ieee80211_configure_filter(local);
}
+/* regular interfaces */
-static ieee80211_txrx_result
-ieee80211_tx_h_fragment(struct ieee80211_txrx_data *tx)
+static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
- size_t hdrlen, per_fragm, num_fragm, payload_len, left;
- struct sk_buff **frags, *first, *frag;
- int i;
- u16 seq;
- u8 *pos;
- int frag_threshold = tx->local->fragmentation_threshold;
-
- if (!tx->fragmented)
- return TXRX_CONTINUE;
-
- first = tx->skb;
-
- hdrlen = ieee80211_get_hdrlen(tx->fc);
- payload_len = first->len - hdrlen;
- per_fragm = frag_threshold - hdrlen - FCS_LEN;
- num_fragm = (payload_len + per_fragm - 1) / per_fragm;
-
- frags = kzalloc(num_fragm * sizeof(struct sk_buff *), GFP_ATOMIC);
- if (!frags)
- goto fail;
-
- hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
- seq = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ;
- pos = first->data + hdrlen + per_fragm;
- left = payload_len - per_fragm;
- for (i = 0; i < num_fragm - 1; i++) {
- struct ieee80211_hdr *fhdr;
- size_t copylen;
-
- if (left <= 0)
- goto fail;
-
- /* reserve enough extra head and tail room for possible
- * encryption */
- frag = frags[i] =
- dev_alloc_skb(tx->local->tx_headroom +
- frag_threshold +
- IEEE80211_ENCRYPT_HEADROOM +
- IEEE80211_ENCRYPT_TAILROOM);
- if (!frag)
- goto fail;
- /* Make sure that all fragments use the same priority so
- * that they end up using the same TX queue */
- frag->priority = first->priority;
- skb_reserve(frag, tx->local->tx_headroom +
- IEEE80211_ENCRYPT_HEADROOM);
- fhdr = (struct ieee80211_hdr *) skb_put(frag, hdrlen);
- memcpy(fhdr, first->data, hdrlen);
- if (i == num_fragm - 2)
- fhdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREFRAGS);
- fhdr->seq_ctrl = cpu_to_le16(seq | ((i + 1) & IEEE80211_SCTL_FRAG));
- copylen = left > per_fragm ? per_fragm : left;
- memcpy(skb_put(frag, copylen), pos, copylen);
-
- pos += copylen;
- left -= copylen;
- }
- skb_trim(first, hdrlen + per_fragm);
-
- tx->u.tx.num_extra_frag = num_fragm - 1;
- tx->u.tx.extra_frag = frags;
-
- return TXRX_CONTINUE;
-
- fail:
- printk(KERN_DEBUG "%s: failed to fragment frame\n", tx->dev->name);
- if (frags) {
- for (i = 0; i < num_fragm - 1; i++)
- if (frags[i])
- dev_kfree_skb(frags[i]);
- kfree(frags);
+ /* FIX: what would be proper limits for MTU?
+ * This interface uses 802.3 frames. */
+ if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6) {
+ printk(KERN_WARNING "%s: invalid MTU %d\n",
+ dev->name, new_mtu);
+ return -EINVAL;
}
- I802_DEBUG_INC(tx->local->tx_handlers_drop_fragment);
- return TXRX_DROP;
-}
-
-static int wep_encrypt_skb(struct ieee80211_txrx_data *tx, struct sk_buff *skb)
-{
- if (tx->key->force_sw_encrypt) {
- if (ieee80211_wep_encrypt(tx->local, skb, tx->key))
- return -1;
- } else {
- tx->u.tx.control->key_idx = tx->key->hw_key_idx;
- if (tx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) {
- if (ieee80211_wep_add_iv(tx->local, skb, tx->key) ==
- NULL)
- return -1;
- }
- }
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ printk(KERN_DEBUG "%s: setting MTU %d\n", dev->name, new_mtu);
+#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
+ dev->mtu = new_mtu;
return 0;
}
-
-void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
-
- hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
- if (tx->u.tx.extra_frag) {
- struct ieee80211_hdr *fhdr;
- int i;
- for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
- fhdr = (struct ieee80211_hdr *)
- tx->u.tx.extra_frag[i]->data;
- fhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
- }
- }
-}
-
-
-static ieee80211_txrx_result
-ieee80211_tx_h_wep_encrypt(struct ieee80211_txrx_data *tx)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
- u16 fc;
-
- fc = le16_to_cpu(hdr->frame_control);
-
- if (!tx->key || tx->key->alg != ALG_WEP ||
- ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
- ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
- (fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH)))
- return TXRX_CONTINUE;
-
- tx->u.tx.control->iv_len = WEP_IV_LEN;
- tx->u.tx.control->icv_len = WEP_ICV_LEN;
- ieee80211_tx_set_iswep(tx);
-
- if (wep_encrypt_skb(tx, tx->skb) < 0) {
- I802_DEBUG_INC(tx->local->tx_handlers_drop_wep);
- return TXRX_DROP;
- }
-
- if (tx->u.tx.extra_frag) {
- int i;
- for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
- if (wep_encrypt_skb(tx, tx->u.tx.extra_frag[i]) < 0) {
- I802_DEBUG_INC(tx->local->
- tx_handlers_drop_wep);
- return TXRX_DROP;
- }
- }
- }
-
- return TXRX_CONTINUE;
-}
-
-
-static int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
- int rate, int erp, int short_preamble)
-{
- int dur;
-
- /* calculate duration (in microseconds, rounded up to next higher
- * integer if it includes a fractional microsecond) to send frame of
- * len bytes (does not include FCS) at the given rate. Duration will
- * also include SIFS.
- *
- * rate is in 100 kbps, so divident is multiplied by 10 in the
- * DIV_ROUND_UP() operations.
- */
-
- if (local->hw.conf.phymode == MODE_IEEE80211A || erp ||
- local->hw.conf.phymode == MODE_ATHEROS_TURBO) {
- /*
- * OFDM:
- *
- * N_DBPS = DATARATE x 4
- * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS)
- * (16 = SIGNAL time, 6 = tail bits)
- * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext
- *
- * T_SYM = 4 usec
- * 802.11a - 17.5.2: aSIFSTime = 16 usec
- * 802.11g - 19.8.4: aSIFSTime = 10 usec +
- * signal ext = 6 usec
- */
- /* FIX: Atheros Turbo may have different (shorter) duration? */
- dur = 16; /* SIFS + signal ext */
- dur += 16; /* 17.3.2.3: T_PREAMBLE = 16 usec */
- dur += 4; /* 17.3.2.3: T_SIGNAL = 4 usec */
- dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10,
- 4 * rate); /* T_SYM x N_SYM */
- } else {
- /*
- * 802.11b or 802.11g with 802.11b compatibility:
- * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime +
- * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0.
- *
- * 802.11 (DS): 15.3.3, 802.11b: 18.3.4
- * aSIFSTime = 10 usec
- * aPreambleLength = 144 usec or 72 usec with short preamble
- * aPLCPHeaderLength = 48 usec or 24 usec with short preamble
- */
- dur = 10; /* aSIFSTime = 10 usec */
- dur += short_preamble ? (72 + 24) : (144 + 48);
-
- dur += DIV_ROUND_UP(8 * (len + 4) * 10, rate);
- }
-
- return dur;
-}
-
-
-/* Exported duration function for driver use */
-__le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
- size_t frame_len, int rate)
-{
- struct ieee80211_local *local = hw_to_local(hw);
- u16 dur;
- int erp;
-
- erp = ieee80211_is_erp_rate(hw->conf.phymode, rate);
- dur = ieee80211_frame_duration(local, frame_len, rate,
- erp, local->short_preamble);
-
- return cpu_to_le16(dur);
-}
-EXPORT_SYMBOL(ieee80211_generic_frame_duration);
-
-
-static u16 ieee80211_duration(struct ieee80211_txrx_data *tx, int group_addr,
- int next_frag_len)
-{
- int rate, mrate, erp, dur, i;
- struct ieee80211_rate *txrate = tx->u.tx.rate;
- struct ieee80211_local *local = tx->local;
- struct ieee80211_hw_mode *mode = tx->u.tx.mode;
-
- erp = txrate->flags & IEEE80211_RATE_ERP;
-
- /*
- * data and mgmt (except PS Poll):
- * - during CFP: 32768
- * - during contention period:
- * if addr1 is group address: 0
- * if more fragments = 0 and addr1 is individual address: time to
- * transmit one ACK plus SIFS
- * if more fragments = 1 and addr1 is individual address: time to
- * transmit next fragment plus 2 x ACK plus 3 x SIFS
- *
- * IEEE 802.11, 9.6:
- * - control response frame (CTS or ACK) shall be transmitted using the
- * same rate as the immediately previous frame in the frame exchange
- * sequence, if this rate belongs to the PHY mandatory rates, or else
- * at the highest possible rate belonging to the PHY rates in the
- * BSSBasicRateSet
- */
-
- if ((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) {
- /* TODO: These control frames are not currently sent by
- * 80211.o, but should they be implemented, this function
- * needs to be updated to support duration field calculation.
- *
- * RTS: time needed to transmit pending data/mgmt frame plus
- * one CTS frame plus one ACK frame plus 3 x SIFS
- * CTS: duration of immediately previous RTS minus time
- * required to transmit CTS and its SIFS
- * ACK: 0 if immediately previous directed data/mgmt had
- * more=0, with more=1 duration in ACK frame is duration
- * from previous frame minus time needed to transmit ACK
- * and its SIFS
- * PS Poll: BIT(15) | BIT(14) | aid
- */
- return 0;
- }
-
- /* data/mgmt */
- if (0 /* FIX: data/mgmt during CFP */)
- return 32768;
-
- if (group_addr) /* Group address as the destination - no ACK */
- return 0;
-
- /* Individual destination address:
- * IEEE 802.11, Ch. 9.6 (after IEEE 802.11g changes)
- * CTS and ACK frames shall be transmitted using the highest rate in
- * basic rate set that is less than or equal to the rate of the
- * immediately previous frame and that is using the same modulation
- * (CCK or OFDM). If no basic rate set matches with these requirements,
- * the highest mandatory rate of the PHY that is less than or equal to
- * the rate of the previous frame is used.
- * Mandatory rates for IEEE 802.11g PHY: 1, 2, 5.5, 11, 6, 12, 24 Mbps
- */
- rate = -1;
- mrate = 10; /* use 1 Mbps if everything fails */
- for (i = 0; i < mode->num_rates; i++) {
- struct ieee80211_rate *r = &mode->rates[i];
- if (r->rate > txrate->rate)
- break;
-
- if (IEEE80211_RATE_MODULATION(txrate->flags) !=
- IEEE80211_RATE_MODULATION(r->flags))
- continue;
-
- if (r->flags & IEEE80211_RATE_BASIC)
- rate = r->rate;
- else if (r->flags & IEEE80211_RATE_MANDATORY)
- mrate = r->rate;
- }
- if (rate == -1) {
- /* No matching basic rate found; use highest suitable mandatory
- * PHY rate */
- rate = mrate;
- }
-
- /* Time needed to transmit ACK
- * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up
- * to closest integer */
-
- dur = ieee80211_frame_duration(local, 10, rate, erp,
- local->short_preamble);
-
- if (next_frag_len) {
- /* Frame is fragmented: duration increases with time needed to
- * transmit next fragment plus ACK and 2 x SIFS. */
- dur *= 2; /* ACK + SIFS */
- /* next fragment */
- dur += ieee80211_frame_duration(local, next_frag_len,
- txrate->rate, erp,
- local->short_preamble);
- }
-
- return dur;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
+static inline int identical_mac_addr_allowed(int type1, int type2)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
- u16 dur;
- struct ieee80211_tx_control *control = tx->u.tx.control;
- struct ieee80211_hw_mode *mode = tx->u.tx.mode;
-
- if (!is_multicast_ether_addr(hdr->addr1)) {
- if (tx->skb->len + FCS_LEN > tx->local->rts_threshold &&
- tx->local->rts_threshold < IEEE80211_MAX_RTS_THRESHOLD) {
- control->flags |= IEEE80211_TXCTL_USE_RTS_CTS;
- control->retry_limit =
- tx->local->long_retry_limit;
- } else {
- control->retry_limit =
- tx->local->short_retry_limit;
- }
- } else {
- control->retry_limit = 1;
- }
-
- if (tx->fragmented) {
- /* Do not use multiple retry rates when sending fragmented
- * frames.
- * TODO: The last fragment could still use multiple retry
- * rates. */
- control->alt_retry_rate = -1;
- }
-
- /* Use CTS protection for unicast frames sent using extended rates if
- * there are associated non-ERP stations and RTS/CTS is not configured
- * for the frame. */
- if (mode->mode == MODE_IEEE80211G &&
- (tx->u.tx.rate->flags & IEEE80211_RATE_ERP) &&
- tx->u.tx.unicast && tx->sdata->use_protection &&
- !(control->flags & IEEE80211_TXCTL_USE_RTS_CTS))
- control->flags |= IEEE80211_TXCTL_USE_CTS_PROTECT;
-
- /* Setup duration field for the first fragment of the frame. Duration
- * for remaining fragments will be updated when they are being sent
- * to low-level driver in ieee80211_tx(). */
- dur = ieee80211_duration(tx, is_multicast_ether_addr(hdr->addr1),
- tx->fragmented ? tx->u.tx.extra_frag[0]->len :
- 0);
- hdr->duration_id = cpu_to_le16(dur);
-
- if ((control->flags & IEEE80211_TXCTL_USE_RTS_CTS) ||
- (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)) {
- struct ieee80211_rate *rate;
-
- /* Do not use multiple retry rates when using RTS/CTS */
- control->alt_retry_rate = -1;
-
- /* Use min(data rate, max base rate) as CTS/RTS rate */
- rate = tx->u.tx.rate;
- while (rate > mode->rates &&
- !(rate->flags & IEEE80211_RATE_BASIC))
- rate--;
-
- control->rts_cts_rate = rate->val;
- control->rts_rate = rate;
- }
-
- if (tx->sta) {
- tx->sta->tx_packets++;
- tx->sta->tx_fragments++;
- tx->sta->tx_bytes += tx->skb->len;
- if (tx->u.tx.extra_frag) {
- int i;
- tx->sta->tx_fragments += tx->u.tx.num_extra_frag;
- for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
- tx->sta->tx_bytes +=
- tx->u.tx.extra_frag[i]->len;
- }
- }
- }
-
- return TXRX_CONTINUE;
+ return (type1 == IEEE80211_IF_TYPE_MNTR ||
+ type2 == IEEE80211_IF_TYPE_MNTR ||
+ (type1 == IEEE80211_IF_TYPE_AP &&
+ type2 == IEEE80211_IF_TYPE_WDS) ||
+ (type1 == IEEE80211_IF_TYPE_WDS &&
+ (type2 == IEEE80211_IF_TYPE_WDS ||
+ type2 == IEEE80211_IF_TYPE_AP)) ||
+ (type1 == IEEE80211_IF_TYPE_AP &&
+ type2 == IEEE80211_IF_TYPE_VLAN) ||
+ (type1 == IEEE80211_IF_TYPE_VLAN &&
+ (type2 == IEEE80211_IF_TYPE_AP ||
+ type2 == IEEE80211_IF_TYPE_VLAN)));
}
-
-static ieee80211_txrx_result
-ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx)
+static int ieee80211_open(struct net_device *dev)
{
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- struct sk_buff *skb = tx->skb;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
- u32 sta_flags;
-
- if (unlikely(tx->local->sta_scanning != 0) &&
- ((tx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
- (tx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PROBE_REQ))
- return TXRX_DROP;
+ struct ieee80211_sub_if_data *sdata, *nsdata;
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_if_init_conf conf;
+ int res;
- if (tx->u.tx.ps_buffered)
- return TXRX_CONTINUE;
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- sta_flags = tx->sta ? tx->sta->flags : 0;
+ /* we hold the RTNL here so can safely walk the list */
+ list_for_each_entry(nsdata, &local->interfaces, list) {
+ struct net_device *ndev = nsdata->dev;
- if (likely(tx->u.tx.unicast)) {
- if (unlikely(!(sta_flags & WLAN_STA_ASSOC) &&
- tx->sdata->type != IEEE80211_IF_TYPE_IBSS &&
- (tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: dropped data frame to not "
- "associated station " MAC_FMT "\n",
- tx->dev->name, MAC_ARG(hdr->addr1));
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
- I802_DEBUG_INC(tx->local->tx_handlers_drop_not_assoc);
- return TXRX_DROP;
- }
- } else {
- if (unlikely((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
- tx->local->num_sta == 0 &&
- !tx->local->allow_broadcast_always &&
- tx->sdata->type != IEEE80211_IF_TYPE_IBSS)) {
+ if (ndev != dev && ndev != local->mdev && netif_running(ndev) &&
+ compare_ether_addr(dev->dev_addr, ndev->dev_addr) == 0) {
/*
- * No associated STAs - no need to send multicast
- * frames.
+ * check whether it may have the same address
*/
- return TXRX_DROP;
- }
- return TXRX_CONTINUE;
- }
-
- if (unlikely(!tx->u.tx.mgmt_interface && tx->sdata->ieee802_1x &&
- !(sta_flags & WLAN_STA_AUTHORIZED))) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: dropped frame to " MAC_FMT
- " (unauthorized port)\n", tx->dev->name,
- MAC_ARG(hdr->addr1));
-#endif
- I802_DEBUG_INC(tx->local->tx_handlers_drop_unauth_port);
- return TXRX_DROP;
- }
-
- return TXRX_CONTINUE;
-}
-
-static ieee80211_txrx_result
-ieee80211_tx_h_sequence(struct ieee80211_txrx_data *tx)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
+ if (!identical_mac_addr_allowed(sdata->type,
+ nsdata->type))
+ return -ENOTUNIQ;
- if (ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control)) >= 24)
- ieee80211_include_sequence(tx->sdata, hdr);
-
- return TXRX_CONTINUE;
-}
-
-/* This function is called whenever the AP is about to exceed the maximum limit
- * of buffered frames for power saving STAs. This situation should not really
- * happen often during normal operation, so dropping the oldest buffered packet
- * from each queue should be OK to make some room for new frames. */
-static void purge_old_ps_buffers(struct ieee80211_local *local)
-{
- int total = 0, purged = 0;
- struct sk_buff *skb;
- struct ieee80211_sub_if_data *sdata;
- struct sta_info *sta;
-
- read_lock(&local->sub_if_lock);
- list_for_each_entry(sdata, &local->sub_if_list, list) {
- struct ieee80211_if_ap *ap;
- if (sdata->dev == local->mdev ||
- sdata->type != IEEE80211_IF_TYPE_AP)
- continue;
- ap = &sdata->u.ap;
- skb = skb_dequeue(&ap->ps_bc_buf);
- if (skb) {
- purged++;
- dev_kfree_skb(skb);
+ /*
+ * can only add VLANs to enabled APs
+ */
+ if (sdata->type == IEEE80211_IF_TYPE_VLAN &&
+ nsdata->type == IEEE80211_IF_TYPE_AP &&
+ netif_running(nsdata->dev))
+ sdata->u.vlan.ap = nsdata;
}
- total += skb_queue_len(&ap->ps_bc_buf);
}
- read_unlock(&local->sub_if_lock);
- spin_lock_bh(&local->sta_lock);
- list_for_each_entry(sta, &local->sta_list, list) {
- skb = skb_dequeue(&sta->ps_tx_buf);
- if (skb) {
- purged++;
- dev_kfree_skb(skb);
- }
- total += skb_queue_len(&sta->ps_tx_buf);
+ switch (sdata->type) {
+ case IEEE80211_IF_TYPE_WDS:
+ if (is_zero_ether_addr(sdata->u.wds.remote_addr))
+ return -ENOLINK;
+ break;
+ case IEEE80211_IF_TYPE_VLAN:
+ if (!sdata->u.vlan.ap)
+ return -ENOLINK;
+ break;
+ case IEEE80211_IF_TYPE_AP:
+ case IEEE80211_IF_TYPE_STA:
+ case IEEE80211_IF_TYPE_MNTR:
+ case IEEE80211_IF_TYPE_IBSS:
+ /* no special treatment */
+ break;
+ case IEEE80211_IF_TYPE_INVALID:
+ /* cannot happen */
+ WARN_ON(1);
+ break;
}
- spin_unlock_bh(&local->sta_lock);
-
- local->total_ps_buffered = total;
- printk(KERN_DEBUG "%s: PS buffers full - purged %d frames\n",
- local->mdev->name, purged);
-}
-
-static inline ieee80211_txrx_result
-ieee80211_tx_h_multicast_ps_buf(struct ieee80211_txrx_data *tx)
-{
- /* broadcast/multicast frame */
- /* If any of the associated stations is in power save mode,
- * the frame is buffered to be sent after DTIM beacon frame */
- if ((tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING) &&
- tx->sdata->type != IEEE80211_IF_TYPE_WDS &&
- tx->sdata->bss && atomic_read(&tx->sdata->bss->num_sta_ps) &&
- !(tx->fc & IEEE80211_FCTL_ORDER)) {
- if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
- purge_old_ps_buffers(tx->local);
- if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >=
- AP_MAX_BC_BUFFER) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: BC TX buffer full - "
- "dropping the oldest frame\n",
- tx->dev->name);
- }
- dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf));
- } else
- tx->local->total_ps_buffered++;
- skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb);
- return TXRX_QUEUED;
+ if (local->open_count == 0) {
+ res = 0;
+ if (local->ops->start)
+ res = local->ops->start(local_to_hw(local));
+ if (res)
+ return res;
}
- return TXRX_CONTINUE;
-}
-
+ switch (sdata->type) {
+ case IEEE80211_IF_TYPE_VLAN:
+ list_add(&sdata->u.vlan.list, &sdata->u.vlan.ap->u.ap.vlans);
+ /* no need to tell driver */
+ break;
+ case IEEE80211_IF_TYPE_MNTR:
+ /* must be before the call to ieee80211_configure_filter */
+ local->monitors++;
+ if (local->monitors == 1) {
+ netif_tx_lock_bh(local->mdev);
+ ieee80211_configure_filter(local);
+ netif_tx_unlock_bh(local->mdev);
-static inline ieee80211_txrx_result
-ieee80211_tx_h_unicast_ps_buf(struct ieee80211_txrx_data *tx)
-{
- struct sta_info *sta = tx->sta;
-
- if (unlikely(!sta ||
- ((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT &&
- (tx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP)))
- return TXRX_CONTINUE;
-
- if (unlikely((sta->flags & WLAN_STA_PS) && !sta->pspoll)) {
- struct ieee80211_tx_packet_data *pkt_data;
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "STA " MAC_FMT " aid %d: PS buffer (entries "
- "before %d)\n",
- MAC_ARG(sta->addr), sta->aid,
- skb_queue_len(&sta->ps_tx_buf));
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
- sta->flags |= WLAN_STA_TIM;
- if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
- purge_old_ps_buffers(tx->local);
- if (skb_queue_len(&sta->ps_tx_buf) >= STA_MAX_TX_BUFFER) {
- struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf);
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: STA " MAC_FMT " TX "
- "buffer full - dropping oldest frame\n",
- tx->dev->name, MAC_ARG(sta->addr));
- }
- dev_kfree_skb(old);
- } else
- tx->local->total_ps_buffered++;
- /* Queue frame to be sent after STA sends an PS Poll frame */
- if (skb_queue_empty(&sta->ps_tx_buf)) {
- if (tx->local->ops->set_tim)
- tx->local->ops->set_tim(local_to_hw(tx->local),
- sta->aid, 1);
- if (tx->sdata->bss)
- bss_tim_set(tx->local, tx->sdata->bss, sta->aid);
+ local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP;
+ ieee80211_hw_config(local);
}
- pkt_data = (struct ieee80211_tx_packet_data *)tx->skb->cb;
- pkt_data->jiffies = jiffies;
- skb_queue_tail(&sta->ps_tx_buf, tx->skb);
- return TXRX_QUEUED;
- }
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- else if (unlikely(sta->flags & WLAN_STA_PS)) {
- printk(KERN_DEBUG "%s: STA " MAC_FMT " in PS mode, but pspoll "
- "set -> send frame\n", tx->dev->name,
- MAC_ARG(sta->addr));
- }
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
- sta->pspoll = 0;
-
- return TXRX_CONTINUE;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_tx_h_ps_buf(struct ieee80211_txrx_data *tx)
-{
- if (unlikely(tx->u.tx.ps_buffered))
- return TXRX_CONTINUE;
-
- if (tx->u.tx.unicast)
- return ieee80211_tx_h_unicast_ps_buf(tx);
- else
- return ieee80211_tx_h_multicast_ps_buf(tx);
-}
-
-
-/*
- * deal with packet injection down monitor interface
- * with Radiotap Header -- only called for monitor mode interface
- */
-
-static ieee80211_txrx_result
-__ieee80211_parse_tx_radiotap(
- struct ieee80211_txrx_data *tx,
- struct sk_buff *skb, struct ieee80211_tx_control *control)
-{
- /*
- * this is the moment to interpret and discard the radiotap header that
- * must be at the start of the packet injected in Monitor mode
- *
- * Need to take some care with endian-ness since radiotap
- * args are little-endian
- */
-
- struct ieee80211_radiotap_iterator iterator;
- struct ieee80211_radiotap_header *rthdr =
- (struct ieee80211_radiotap_header *) skb->data;
- struct ieee80211_hw_mode *mode = tx->local->hw.conf.mode;
- int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
-
- /*
- * default control situation for all injected packets
- * FIXME: this does not suit all usage cases, expand to allow control
- */
-
- control->retry_limit = 1; /* no retry */
- control->key_idx = -1; /* no encryption key */
- control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS |
- IEEE80211_TXCTL_USE_CTS_PROTECT);
- control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT |
- IEEE80211_TXCTL_NO_ACK;
- control->antenna_sel_tx = 0; /* default to default antenna */
-
- /*
- * for every radiotap entry that is present
- * (ieee80211_radiotap_iterator_next returns -ENOENT when no more
- * entries present, or -EINVAL on error)
- */
-
- while (!ret) {
- int i, target_rate;
-
- ret = ieee80211_radiotap_iterator_next(&iterator);
-
- if (ret)
- continue;
-
- /* see if this argument is something we can use */
- switch (iterator.this_arg_index) {
- /*
- * You must take care when dereferencing iterator.this_arg
- * for multibyte types... the pointer is not aligned. Use
- * get_unaligned((type *)iterator.this_arg) to dereference
- * iterator.this_arg for type "type" safely on all arches.
- */
- case IEEE80211_RADIOTAP_RATE:
- /*
- * radiotap rate u8 is in 500kbps units eg, 0x02=1Mbps
- * ieee80211 rate int is in 100kbps units eg, 0x0a=1Mbps
- */
- target_rate = (*iterator.this_arg) * 5;
- for (i = 0; i < mode->num_rates; i++) {
- struct ieee80211_rate *r = &mode->rates[i];
-
- if (r->rate > target_rate)
- continue;
-
- control->rate = r;
-
- if (r->flags & IEEE80211_RATE_PREAMBLE2)
- control->tx_rate = r->val2;
- else
- control->tx_rate = r->val;
-
- /* end on exact match */
- if (r->rate == target_rate)
- i = mode->num_rates;
- }
- break;
-
- case IEEE80211_RADIOTAP_ANTENNA:
- /*
- * radiotap uses 0 for 1st ant, mac80211 is 1 for
- * 1st ant
- */
- control->antenna_sel_tx = (*iterator.this_arg) + 1;
- break;
-
- case IEEE80211_RADIOTAP_DBM_TX_POWER:
- control->power_level = *iterator.this_arg;
- break;
+ break;
+ case IEEE80211_IF_TYPE_STA:
+ case IEEE80211_IF_TYPE_IBSS:
+ sdata->u.sta.flags &= ~IEEE80211_STA_PREV_BSSID_SET;
+ /* fall through */
+ default:
+ conf.if_id = dev->ifindex;
+ conf.type = sdata->type;
+ conf.mac_addr = dev->dev_addr;
+ res = local->ops->add_interface(local_to_hw(local), &conf);
+ if (res && !local->open_count && local->ops->stop)
+ local->ops->stop(local_to_hw(local));
+ if (res)
+ return res;
- case IEEE80211_RADIOTAP_FLAGS:
- if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FCS) {
- /*
- * this indicates that the skb we have been
- * handed has the 32-bit FCS CRC at the end...
- * we should react to that by snipping it off
- * because it will be recomputed and added
- * on transmission
- */
- if (skb->len < (iterator.max_length + FCS_LEN))
- return TXRX_DROP;
+ ieee80211_if_config(dev);
+ ieee80211_reset_erp_info(dev);
+ ieee80211_enable_keys(sdata);
- skb_trim(skb, skb->len - FCS_LEN);
- }
- break;
+ if (sdata->type == IEEE80211_IF_TYPE_STA &&
+ !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME))
+ netif_carrier_off(dev);
+ else
+ netif_carrier_on(dev);
+ }
- default:
- break;
- }
+ if (local->open_count == 0) {
+ res = dev_open(local->mdev);
+ WARN_ON(res);
+ tasklet_enable(&local->tx_pending_tasklet);
+ tasklet_enable(&local->tasklet);
}
- if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */
- return TXRX_DROP;
+ local->open_count++;
- /*
- * remove the radiotap header
- * iterator->max_length was sanity-checked against
- * skb->len by iterator init
- */
- skb_pull(skb, iterator.max_length);
+ netif_start_queue(dev);
- return TXRX_CONTINUE;
+ return 0;
}
-
-static ieee80211_txrx_result inline
-__ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
- struct sk_buff *skb,
- struct net_device *dev,
- struct ieee80211_tx_control *control)
+static int ieee80211_stop(struct net_device *dev)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_sub_if_data *sdata;
- ieee80211_txrx_result res = TXRX_CONTINUE;
-
- int hdrlen;
-
- memset(tx, 0, sizeof(*tx));
- tx->skb = skb;
- tx->dev = dev; /* use original interface */
- tx->local = local;
- tx->sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- tx->sta = sta_info_get(local, hdr->addr1);
- tx->fc = le16_to_cpu(hdr->frame_control);
-
- /*
- * set defaults for things that can be set by
- * injected radiotap headers
- */
- control->power_level = local->hw.conf.power_level;
- control->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
- if (local->sta_antenna_sel != STA_ANTENNA_SEL_AUTO && tx->sta)
- control->antenna_sel_tx = tx->sta->antenna_sel_tx;
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_if_init_conf conf;
- /* process and remove the injection radiotap header */
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (unlikely(sdata->type == IEEE80211_IF_TYPE_MNTR)) {
- if (__ieee80211_parse_tx_radiotap(tx, skb, control) ==
- TXRX_DROP) {
- return TXRX_DROP;
- }
- /*
- * we removed the radiotap header after this point,
- * we filled control with what we could use
- * set to the actual ieee header now
- */
- hdr = (struct ieee80211_hdr *) skb->data;
- res = TXRX_QUEUED; /* indication it was monitor packet */
- }
-
- tx->u.tx.control = control;
- tx->u.tx.unicast = !is_multicast_ether_addr(hdr->addr1);
- if (is_multicast_ether_addr(hdr->addr1))
- control->flags |= IEEE80211_TXCTL_NO_ACK;
- else
- control->flags &= ~IEEE80211_TXCTL_NO_ACK;
- tx->fragmented = local->fragmentation_threshold <
- IEEE80211_MAX_FRAG_THRESHOLD && tx->u.tx.unicast &&
- skb->len + FCS_LEN > local->fragmentation_threshold &&
- (!local->ops->set_frag_threshold);
- if (!tx->sta)
- control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK;
- else if (tx->sta->clear_dst_mask) {
- control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK;
- tx->sta->clear_dst_mask = 0;
- }
- hdrlen = ieee80211_get_hdrlen(tx->fc);
- if (skb->len > hdrlen + sizeof(rfc1042_header) + 2) {
- u8 *pos = &skb->data[hdrlen + sizeof(rfc1042_header)];
- tx->ethertype = (pos[0] << 8) | pos[1];
- }
- control->flags |= IEEE80211_TXCTL_FIRST_FRAGMENT;
- return res;
-}
+ netif_stop_queue(dev);
-static int inline is_ieee80211_device(struct net_device *dev,
- struct net_device *master)
-{
- return (wdev_priv(dev->ieee80211_ptr) ==
- wdev_priv(master->ieee80211_ptr));
-}
+ dev_mc_unsync(local->mdev, dev);
-/* Device in tx->dev has a reference added; use dev_put(tx->dev) when
- * finished with it. */
-static int inline ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
- struct sk_buff *skb,
- struct net_device *mdev,
- struct ieee80211_tx_control *control)
-{
- struct ieee80211_tx_packet_data *pkt_data;
- struct net_device *dev;
+ /* down all dependent devices, that is VLANs */
+ if (sdata->type == IEEE80211_IF_TYPE_AP) {
+ struct ieee80211_sub_if_data *vlan, *tmp;
- pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
- dev = dev_get_by_index(pkt_data->ifindex);
- if (unlikely(dev && !is_ieee80211_device(dev, mdev))) {
- dev_put(dev);
- dev = NULL;
+ list_for_each_entry_safe(vlan, tmp, &sdata->u.ap.vlans,
+ u.vlan.list)
+ dev_close(vlan->dev);
+ WARN_ON(!list_empty(&sdata->u.ap.vlans));
}
- if (unlikely(!dev))
- return -ENODEV;
- __ieee80211_tx_prepare(tx, skb, dev, control);
- return 0;
-}
-
-static inline int __ieee80211_queue_stopped(const struct ieee80211_local *local,
- int queue)
-{
- return test_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
-}
-
-static inline int __ieee80211_queue_pending(const struct ieee80211_local *local,
- int queue)
-{
- return test_bit(IEEE80211_LINK_STATE_PENDING, &local->state[queue]);
-}
-
-#define IEEE80211_TX_OK 0
-#define IEEE80211_TX_AGAIN 1
-#define IEEE80211_TX_FRAG_AGAIN 2
-static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
- struct ieee80211_txrx_data *tx)
-{
- struct ieee80211_tx_control *control = tx->u.tx.control;
- int ret, i;
+ local->open_count--;
- if (!ieee80211_qdisc_installed(local->mdev) &&
- __ieee80211_queue_stopped(local, 0)) {
- netif_stop_queue(local->mdev);
- return IEEE80211_TX_AGAIN;
- }
- if (skb) {
- ieee80211_dump_frame(local->mdev->name, "TX to low-level driver", skb);
- ret = local->ops->tx(local_to_hw(local), skb, control);
- if (ret)
- return IEEE80211_TX_AGAIN;
- local->mdev->trans_start = jiffies;
- ieee80211_led_tx(local, 1);
- }
- if (tx->u.tx.extra_frag) {
- control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS |
- IEEE80211_TXCTL_USE_CTS_PROTECT |
- IEEE80211_TXCTL_CLEAR_DST_MASK |
- IEEE80211_TXCTL_FIRST_FRAGMENT);
- for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
- if (!tx->u.tx.extra_frag[i])
- continue;
- if (__ieee80211_queue_stopped(local, control->queue))
- return IEEE80211_TX_FRAG_AGAIN;
- if (i == tx->u.tx.num_extra_frag) {
- control->tx_rate = tx->u.tx.last_frag_hwrate;
- control->rate = tx->u.tx.last_frag_rate;
- if (tx->u.tx.probe_last_frag)
- control->flags |=
- IEEE80211_TXCTL_RATE_CTRL_PROBE;
- else
- control->flags &=
- ~IEEE80211_TXCTL_RATE_CTRL_PROBE;
- }
+ switch (sdata->type) {
+ case IEEE80211_IF_TYPE_VLAN:
+ list_del(&sdata->u.vlan.list);
+ sdata->u.vlan.ap = NULL;
+ /* no need to tell driver */
+ break;
+ case IEEE80211_IF_TYPE_MNTR:
+ local->monitors--;
+ if (local->monitors == 0) {
+ netif_tx_lock_bh(local->mdev);
+ ieee80211_configure_filter(local);
+ netif_tx_unlock_bh(local->mdev);
- ieee80211_dump_frame(local->mdev->name,
- "TX to low-level driver",
- tx->u.tx.extra_frag[i]);
- ret = local->ops->tx(local_to_hw(local),
- tx->u.tx.extra_frag[i],
- control);
- if (ret)
- return IEEE80211_TX_FRAG_AGAIN;
- local->mdev->trans_start = jiffies;
- ieee80211_led_tx(local, 1);
- tx->u.tx.extra_frag[i] = NULL;
+ local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP;
+ ieee80211_hw_config(local);
}
- kfree(tx->u.tx.extra_frag);
- tx->u.tx.extra_frag = NULL;
- }
- return IEEE80211_TX_OK;
-}
-
-static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
- struct ieee80211_tx_control *control, int mgmt)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct sta_info *sta;
- ieee80211_tx_handler *handler;
- struct ieee80211_txrx_data tx;
- ieee80211_txrx_result res = TXRX_DROP, res_prepare;
- int ret, i;
-
- WARN_ON(__ieee80211_queue_pending(local, control->queue));
-
- if (unlikely(skb->len < 10)) {
- dev_kfree_skb(skb);
- return 0;
- }
-
- res_prepare = __ieee80211_tx_prepare(&tx, skb, dev, control);
-
- if (res_prepare == TXRX_DROP) {
- dev_kfree_skb(skb);
- return 0;
- }
-
- sta = tx.sta;
- tx.u.tx.mgmt_interface = mgmt;
- tx.u.tx.mode = local->hw.conf.mode;
+ break;
+ case IEEE80211_IF_TYPE_STA:
+ case IEEE80211_IF_TYPE_IBSS:
+ sdata->u.sta.state = IEEE80211_DISABLED;
+ del_timer_sync(&sdata->u.sta.timer);
+ /*
+ * When we get here, the interface is marked down.
+ * Call synchronize_rcu() to wait for the RX path
+ * should it be using the interface and enqueuing
+ * frames at this very time on another CPU.
+ */
+ synchronize_rcu();
+ skb_queue_purge(&sdata->u.sta.skb_queue);
- if (res_prepare == TXRX_QUEUED) { /* if it was an injected packet */
- res = TXRX_CONTINUE;
- } else {
- for (handler = local->tx_handlers; *handler != NULL;
- handler++) {
- res = (*handler)(&tx);
- if (res != TXRX_CONTINUE)
- break;
+ if (!local->ops->hw_scan &&
+ local->scan_dev == sdata->dev) {
+ local->sta_scanning = 0;
+ cancel_delayed_work(&local->scan_work);
}
+ flush_workqueue(local->hw.workqueue);
+ /* fall through */
+ default:
+ conf.if_id = dev->ifindex;
+ conf.type = sdata->type;
+ conf.mac_addr = dev->dev_addr;
+ /* disable all keys for as long as this netdev is down */
+ ieee80211_disable_keys(sdata);
+ local->ops->remove_interface(local_to_hw(local), &conf);
}
- skb = tx.skb; /* handlers are allowed to change skb */
-
- if (sta)
- sta_info_put(sta);
-
- if (unlikely(res == TXRX_DROP)) {
- I802_DEBUG_INC(local->tx_handlers_drop);
- goto drop;
- }
-
- if (unlikely(res == TXRX_QUEUED)) {
- I802_DEBUG_INC(local->tx_handlers_queued);
- return 0;
- }
-
- if (tx.u.tx.extra_frag) {
- for (i = 0; i < tx.u.tx.num_extra_frag; i++) {
- int next_len, dur;
- struct ieee80211_hdr *hdr =
- (struct ieee80211_hdr *)
- tx.u.tx.extra_frag[i]->data;
+ if (local->open_count == 0) {
+ if (netif_running(local->mdev))
+ dev_close(local->mdev);
- if (i + 1 < tx.u.tx.num_extra_frag) {
- next_len = tx.u.tx.extra_frag[i + 1]->len;
- } else {
- next_len = 0;
- tx.u.tx.rate = tx.u.tx.last_frag_rate;
- tx.u.tx.last_frag_hwrate = tx.u.tx.rate->val;
- }
- dur = ieee80211_duration(&tx, 0, next_len);
- hdr->duration_id = cpu_to_le16(dur);
- }
- }
+ if (local->ops->stop)
+ local->ops->stop(local_to_hw(local));
-retry:
- ret = __ieee80211_tx(local, skb, &tx);
- if (ret) {
- struct ieee80211_tx_stored_packet *store =
- &local->pending_packet[control->queue];
-
- if (ret == IEEE80211_TX_FRAG_AGAIN)
- skb = NULL;
- set_bit(IEEE80211_LINK_STATE_PENDING,
- &local->state[control->queue]);
- smp_mb();
- /* When the driver gets out of buffers during sending of
- * fragments and calls ieee80211_stop_queue, there is
- * a small window between IEEE80211_LINK_STATE_XOFF and
- * IEEE80211_LINK_STATE_PENDING flags are set. If a buffer
- * gets available in that window (i.e. driver calls
- * ieee80211_wake_queue), we would end up with ieee80211_tx
- * called with IEEE80211_LINK_STATE_PENDING. Prevent this by
- * continuing transmitting here when that situation is
- * possible to have happened. */
- if (!__ieee80211_queue_stopped(local, control->queue)) {
- clear_bit(IEEE80211_LINK_STATE_PENDING,
- &local->state[control->queue]);
- goto retry;
- }
- memcpy(&store->control, control,
- sizeof(struct ieee80211_tx_control));
- store->skb = skb;
- store->extra_frag = tx.u.tx.extra_frag;
- store->num_extra_frag = tx.u.tx.num_extra_frag;
- store->last_frag_hwrate = tx.u.tx.last_frag_hwrate;
- store->last_frag_rate = tx.u.tx.last_frag_rate;
- store->last_frag_rate_ctrl_probe = tx.u.tx.probe_last_frag;
+ tasklet_disable(&local->tx_pending_tasklet);
+ tasklet_disable(&local->tasklet);
}
- return 0;
- drop:
- if (skb)
- dev_kfree_skb(skb);
- for (i = 0; i < tx.u.tx.num_extra_frag; i++)
- if (tx.u.tx.extra_frag[i])
- dev_kfree_skb(tx.u.tx.extra_frag[i]);
- kfree(tx.u.tx.extra_frag);
return 0;
}
-static void ieee80211_tx_pending(unsigned long data)
-{
- struct ieee80211_local *local = (struct ieee80211_local *)data;
- struct net_device *dev = local->mdev;
- struct ieee80211_tx_stored_packet *store;
- struct ieee80211_txrx_data tx;
- int i, ret, reschedule = 0;
-
- netif_tx_lock_bh(dev);
- for (i = 0; i < local->hw.queues; i++) {
- if (__ieee80211_queue_stopped(local, i))
- continue;
- if (!__ieee80211_queue_pending(local, i)) {
- reschedule = 1;
- continue;
- }
- store = &local->pending_packet[i];
- tx.u.tx.control = &store->control;
- tx.u.tx.extra_frag = store->extra_frag;
- tx.u.tx.num_extra_frag = store->num_extra_frag;
- tx.u.tx.last_frag_hwrate = store->last_frag_hwrate;
- tx.u.tx.last_frag_rate = store->last_frag_rate;
- tx.u.tx.probe_last_frag = store->last_frag_rate_ctrl_probe;
- ret = __ieee80211_tx(local, store->skb, &tx);
- if (ret) {
- if (ret == IEEE80211_TX_FRAG_AGAIN)
- store->skb = NULL;
- } else {
- clear_bit(IEEE80211_LINK_STATE_PENDING,
- &local->state[i]);
- reschedule = 1;
- }
- }
- netif_tx_unlock_bh(dev);
- if (reschedule) {
- if (!ieee80211_qdisc_installed(dev)) {
- if (!__ieee80211_queue_stopped(local, 0))
- netif_wake_queue(dev);
- } else
- netif_schedule(dev);
- }
-}
-
-static void ieee80211_clear_tx_pending(struct ieee80211_local *local)
-{
- int i, j;
- struct ieee80211_tx_stored_packet *store;
-
- for (i = 0; i < local->hw.queues; i++) {
- if (!__ieee80211_queue_pending(local, i))
- continue;
- store = &local->pending_packet[i];
- kfree_skb(store->skb);
- for (j = 0; j < store->num_extra_frag; j++)
- kfree_skb(store->extra_frag[j]);
- kfree(store->extra_frag);
- clear_bit(IEEE80211_LINK_STATE_PENDING, &local->state[i]);
- }
-}
-
-static int ieee80211_master_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
+static void ieee80211_set_multicast_list(struct net_device *dev)
{
- struct ieee80211_tx_control control;
- struct ieee80211_tx_packet_data *pkt_data;
- struct net_device *odev = NULL;
- struct ieee80211_sub_if_data *osdata;
- int headroom;
- int ret;
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ int allmulti, promisc, sdata_allmulti, sdata_promisc;
- /*
- * copy control out of the skb so other people can use skb->cb
- */
- pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
- memset(&control, 0, sizeof(struct ieee80211_tx_control));
+ allmulti = !!(dev->flags & IFF_ALLMULTI);
+ promisc = !!(dev->flags & IFF_PROMISC);
+ sdata_allmulti = sdata->flags & IEEE80211_SDATA_ALLMULTI;
+ sdata_promisc = sdata->flags & IEEE80211_SDATA_PROMISC;
- if (pkt_data->ifindex)
- odev = dev_get_by_index(pkt_data->ifindex);
- if (unlikely(odev && !is_ieee80211_device(odev, dev))) {
- dev_put(odev);
- odev = NULL;
- }
- if (unlikely(!odev)) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Discarded packet with nonexistent "
- "originating device\n", dev->name);
-#endif
- dev_kfree_skb(skb);
- return 0;
+ if (allmulti != sdata_allmulti) {
+ if (dev->flags & IFF_ALLMULTI)
+ atomic_inc(&local->iff_allmultis);
+ else
+ atomic_dec(&local->iff_allmultis);
+ sdata->flags ^= IEEE80211_SDATA_ALLMULTI;
}
- osdata = IEEE80211_DEV_TO_SUB_IF(odev);
- headroom = osdata->local->tx_headroom + IEEE80211_ENCRYPT_HEADROOM;
- if (skb_headroom(skb) < headroom) {
- if (pskb_expand_head(skb, headroom, 0, GFP_ATOMIC)) {
- dev_kfree_skb(skb);
- dev_put(odev);
- return 0;
- }
+ if (promisc != sdata_promisc) {
+ if (dev->flags & IFF_PROMISC)
+ atomic_inc(&local->iff_promiscs);
+ else
+ atomic_dec(&local->iff_promiscs);
+ sdata->flags ^= IEEE80211_SDATA_PROMISC;
}
- control.ifindex = odev->ifindex;
- control.type = osdata->type;
- if (pkt_data->req_tx_status)
- control.flags |= IEEE80211_TXCTL_REQ_TX_STATUS;
- if (pkt_data->do_not_encrypt)
- control.flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
- if (pkt_data->requeue)
- control.flags |= IEEE80211_TXCTL_REQUEUE;
- control.queue = pkt_data->queue;
-
- ret = ieee80211_tx(odev, skb, &control,
- control.type == IEEE80211_IF_TYPE_MGMT);
- dev_put(odev);
-
- return ret;
+ dev_mc_sync(local->mdev, dev);
}
+static const struct header_ops ieee80211_header_ops = {
+ .create = eth_header,
+ .parse = header_parse_80211,
+ .rebuild = eth_rebuild_header,
+ .cache = eth_header_cache,
+ .cache_update = eth_header_cache_update,
+};
-int ieee80211_monitor_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
+/* Must not be called for mdev */
+void ieee80211_if_setup(struct net_device *dev)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_tx_packet_data *pkt_data;
- struct ieee80211_radiotap_header *prthdr =
- (struct ieee80211_radiotap_header *)skb->data;
- u16 len;
-
- /*
- * there must be a radiotap header at the
- * start in this case
- */
- if (unlikely(prthdr->it_version)) {
- /* only version 0 is supported */
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
- }
-
- skb->dev = local->mdev;
-
- pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
- memset(pkt_data, 0, sizeof(*pkt_data));
- pkt_data->ifindex = dev->ifindex;
- pkt_data->mgmt_iface = 0;
- pkt_data->do_not_encrypt = 1;
-
- /* above needed because we set skb device to master */
-
- /*
- * fix up the pointers accounting for the radiotap
- * header still being in there. We are being given
- * a precooked IEEE80211 header so no need for
- * normal processing
- */
- len = le16_to_cpu(get_unaligned(&prthdr->it_len));
- skb_set_mac_header(skb, len);
- skb_set_network_header(skb, len + sizeof(struct ieee80211_hdr));
- skb_set_transport_header(skb, len + sizeof(struct ieee80211_hdr));
-
- /*
- * pass the radiotap header up to
- * the next stage intact
- */
- dev_queue_xmit(skb);
-
- return NETDEV_TX_OK;
+ ether_setup(dev);
+ dev->header_ops = &ieee80211_header_ops;
+ dev->hard_start_xmit = ieee80211_subif_start_xmit;
+ dev->wireless_handlers = &ieee80211_iw_handler_def;
+ dev->set_multicast_list = ieee80211_set_multicast_list;
+ dev->change_mtu = ieee80211_change_mtu;
+ dev->open = ieee80211_open;
+ dev->stop = ieee80211_stop;
+ dev->destructor = ieee80211_if_free;
}
+/* WDS specialties */
-/**
- * ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type
- * subinterfaces (wlan#, WDS, and VLAN interfaces)
- * @skb: packet to be sent
- * @dev: incoming interface
- *
- * Returns: 0 on success (and frees skb in this case) or 1 on failure (skb will
- * not be freed, and caller is responsible for either retrying later or freeing
- * skb).
- *
- * This function takes in an Ethernet header and encapsulates it with suitable
- * IEEE 802.11 header based on which interface the packet is coming in. The
- * encapsulated packet will then be passed to master interface, wlan#.11, for
- * transmission (through low-level driver).
- */
-int ieee80211_subif_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
+int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_tx_packet_data *pkt_data;
- struct ieee80211_sub_if_data *sdata;
- int ret = 1, head_need;
- u16 ethertype, hdrlen, fc;
- struct ieee80211_hdr hdr;
- const u8 *encaps_data;
- int encaps_len, skip_header_bytes;
- int nh_pos, h_pos, no_encrypt = 0;
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct sta_info *sta;
+ DECLARE_MAC_BUF(mac);
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (unlikely(skb->len < ETH_HLEN)) {
- printk(KERN_DEBUG "%s: short skb (len=%d)\n",
- dev->name, skb->len);
- ret = 0;
- goto fail;
- }
+ if (compare_ether_addr(remote_addr, sdata->u.wds.remote_addr) == 0)
+ return 0;
- nh_pos = skb_network_header(skb) - skb->data;
- h_pos = skb_transport_header(skb) - skb->data;
-
- /* convert Ethernet header to proper 802.11 header (based on
- * operation mode) */
- ethertype = (skb->data[12] << 8) | skb->data[13];
- /* TODO: handling for 802.1x authorized/unauthorized port */
- fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA;
-
- if (likely(sdata->type == IEEE80211_IF_TYPE_AP ||
- sdata->type == IEEE80211_IF_TYPE_VLAN)) {
- fc |= IEEE80211_FCTL_FROMDS;
- /* DA BSSID SA */
- memcpy(hdr.addr1, skb->data, ETH_ALEN);
- memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
- memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
- hdrlen = 24;
- } else if (sdata->type == IEEE80211_IF_TYPE_WDS) {
- fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS;
- /* RA TA DA SA */
- memcpy(hdr.addr1, sdata->u.wds.remote_addr, ETH_ALEN);
- memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
- memcpy(hdr.addr3, skb->data, ETH_ALEN);
- memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
- hdrlen = 30;
- } else if (sdata->type == IEEE80211_IF_TYPE_STA) {
- fc |= IEEE80211_FCTL_TODS;
- /* BSSID SA DA */
- memcpy(hdr.addr1, sdata->u.sta.bssid, ETH_ALEN);
- memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
- memcpy(hdr.addr3, skb->data, ETH_ALEN);
- hdrlen = 24;
- } else if (sdata->type == IEEE80211_IF_TYPE_IBSS) {
- /* DA SA BSSID */
- memcpy(hdr.addr1, skb->data, ETH_ALEN);
- memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
- memcpy(hdr.addr3, sdata->u.sta.bssid, ETH_ALEN);
- hdrlen = 24;
- } else {
- ret = 0;
- goto fail;
- }
+ /* Create STA entry for the new peer */
+ sta = sta_info_add(local, dev, remote_addr, GFP_KERNEL);
+ if (!sta)
+ return -ENOMEM;
+ sta_info_put(sta);
- /* receiver is QoS enabled, use a QoS type frame */
- sta = sta_info_get(local, hdr.addr1);
+ /* Remove STA entry for the old peer */
+ sta = sta_info_get(local, sdata->u.wds.remote_addr);
if (sta) {
- if (sta->flags & WLAN_STA_WME) {
- fc |= IEEE80211_STYPE_QOS_DATA;
- hdrlen += 2;
- }
+ sta_info_free(sta);
sta_info_put(sta);
- }
-
- hdr.frame_control = cpu_to_le16(fc);
- hdr.duration_id = 0;
- hdr.seq_ctrl = 0;
-
- skip_header_bytes = ETH_HLEN;
- if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) {
- encaps_data = bridge_tunnel_header;
- encaps_len = sizeof(bridge_tunnel_header);
- skip_header_bytes -= 2;
- } else if (ethertype >= 0x600) {
- encaps_data = rfc1042_header;
- encaps_len = sizeof(rfc1042_header);
- skip_header_bytes -= 2;
} else {
- encaps_data = NULL;
- encaps_len = 0;
- }
-
- skb_pull(skb, skip_header_bytes);
- nh_pos -= skip_header_bytes;
- h_pos -= skip_header_bytes;
-
- /* TODO: implement support for fragments so that there is no need to
- * reallocate and copy payload; it might be enough to support one
- * extra fragment that would be copied in the beginning of the frame
- * data.. anyway, it would be nice to include this into skb structure
- * somehow
- *
- * There are few options for this:
- * use skb->cb as an extra space for 802.11 header
- * allocate new buffer if not enough headroom
- * make sure that there is enough headroom in every skb by increasing
- * build in headroom in __dev_alloc_skb() (linux/skbuff.h) and
- * alloc_skb() (net/core/skbuff.c)
- */
- head_need = hdrlen + encaps_len + local->tx_headroom;
- head_need -= skb_headroom(skb);
-
- /* We are going to modify skb data, so make a copy of it if happens to
- * be cloned. This could happen, e.g., with Linux bridge code passing
- * us broadcast frames. */
-
- if (head_need > 0 || skb_cloned(skb)) {
-#if 0
- printk(KERN_DEBUG "%s: need to reallocate buffer for %d bytes "
- "of headroom\n", dev->name, head_need);
-#endif
-
- if (skb_cloned(skb))
- I802_DEBUG_INC(local->tx_expand_skb_head_cloned);
- else
- I802_DEBUG_INC(local->tx_expand_skb_head);
- /* Since we have to reallocate the buffer, make sure that there
- * is enough room for possible WEP IV/ICV and TKIP (8 bytes
- * before payload and 12 after). */
- if (pskb_expand_head(skb, (head_need > 0 ? head_need + 8 : 8),
- 12, GFP_ATOMIC)) {
- printk(KERN_DEBUG "%s: failed to reallocate TX buffer"
- "\n", dev->name);
- goto fail;
- }
- }
-
- if (encaps_data) {
- memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len);
- nh_pos += encaps_len;
- h_pos += encaps_len;
- }
- memcpy(skb_push(skb, hdrlen), &hdr, hdrlen);
- nh_pos += hdrlen;
- h_pos += hdrlen;
-
- pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
- memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
- pkt_data->ifindex = dev->ifindex;
- pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT);
- pkt_data->do_not_encrypt = no_encrypt;
-
- skb->dev = local->mdev;
- sdata->stats.tx_packets++;
- sdata->stats.tx_bytes += skb->len;
-
- /* Update skb pointers to various headers since this modified frame
- * is going to go through Linux networking code that may potentially
- * need things like pointer to IP header. */
- skb_set_mac_header(skb, 0);
- skb_set_network_header(skb, nh_pos);
- skb_set_transport_header(skb, h_pos);
-
- dev->trans_start = jiffies;
- dev_queue_xmit(skb);
-
- return 0;
-
- fail:
- if (!ret)
- dev_kfree_skb(skb);
-
- return ret;
-}
-
-
-/*
- * This is the transmit routine for the 802.11 type interfaces
- * called by upper layers of the linux networking
- * stack when it has a frame to transmit
- */
-static int
-ieee80211_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct ieee80211_sub_if_data *sdata;
- struct ieee80211_tx_packet_data *pkt_data;
- struct ieee80211_hdr *hdr;
- u16 fc;
-
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- if (skb->len < 10) {
- dev_kfree_skb(skb);
- return 0;
- }
-
- if (skb_headroom(skb) < sdata->local->tx_headroom) {
- if (pskb_expand_head(skb, sdata->local->tx_headroom,
- 0, GFP_ATOMIC)) {
- dev_kfree_skb(skb);
- return 0;
- }
- }
-
- hdr = (struct ieee80211_hdr *) skb->data;
- fc = le16_to_cpu(hdr->frame_control);
-
- pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
- memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
- pkt_data->ifindex = sdata->dev->ifindex;
- pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT);
-
- skb->priority = 20; /* use hardcoded priority for mgmt TX queue */
- skb->dev = sdata->local->mdev;
-
- /*
- * We're using the protocol field of the the frame control header
- * to request TX callback for hostapd. BIT(1) is checked.
- */
- if ((fc & BIT(1)) == BIT(1)) {
- pkt_data->req_tx_status = 1;
- fc &= ~BIT(1);
- hdr->frame_control = cpu_to_le16(fc);
+ printk(KERN_DEBUG "%s: could not find STA entry for WDS link "
+ "peer %s\n",
+ dev->name, print_mac(mac, sdata->u.wds.remote_addr));
}
- pkt_data->do_not_encrypt = !(fc & IEEE80211_FCTL_PROTECTED);
-
- sdata->stats.tx_packets++;
- sdata->stats.tx_bytes += skb->len;
-
- dev_queue_xmit(skb);
+ /* Update WDS link data */
+ memcpy(&sdata->u.wds.remote_addr, remote_addr, ETH_ALEN);
return 0;
}
-
-static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
- struct ieee80211_if_ap *bss,
- struct sk_buff *skb)
-{
- u8 *pos, *tim;
- int aid0 = 0;
- int i, have_bits = 0, n1, n2;
-
- /* Generate bitmap for TIM only if there are any STAs in power save
- * mode. */
- spin_lock_bh(&local->sta_lock);
- if (atomic_read(&bss->num_sta_ps) > 0)
- /* in the hope that this is faster than
- * checking byte-for-byte */
- have_bits = !bitmap_empty((unsigned long*)bss->tim,
- IEEE80211_MAX_AID+1);
-
- if (bss->dtim_count == 0)
- bss->dtim_count = bss->dtim_period - 1;
- else
- bss->dtim_count--;
-
- tim = pos = (u8 *) skb_put(skb, 6);
- *pos++ = WLAN_EID_TIM;
- *pos++ = 4;
- *pos++ = bss->dtim_count;
- *pos++ = bss->dtim_period;
-
- if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf))
- aid0 = 1;
-
- if (have_bits) {
- /* Find largest even number N1 so that bits numbered 1 through
- * (N1 x 8) - 1 in the bitmap are 0 and number N2 so that bits
- * (N2 + 1) x 8 through 2007 are 0. */
- n1 = 0;
- for (i = 0; i < IEEE80211_MAX_TIM_LEN; i++) {
- if (bss->tim[i]) {
- n1 = i & 0xfe;
- break;
- }
- }
- n2 = n1;
- for (i = IEEE80211_MAX_TIM_LEN - 1; i >= n1; i--) {
- if (bss->tim[i]) {
- n2 = i;
- break;
- }
- }
-
- /* Bitmap control */
- *pos++ = n1 | aid0;
- /* Part Virt Bitmap */
- memcpy(pos, bss->tim + n1, n2 - n1 + 1);
-
- tim[1] = n2 - n1 + 4;
- skb_put(skb, n2 - n1);
- } else {
- *pos++ = aid0; /* Bitmap control */
- *pos++ = 0; /* Part Virt Bitmap */
- }
- spin_unlock_bh(&local->sta_lock);
-}
-
-
-struct sk_buff * ieee80211_beacon_get(struct ieee80211_hw *hw, int if_id,
- struct ieee80211_tx_control *control)
-{
- struct ieee80211_local *local = hw_to_local(hw);
- struct sk_buff *skb;
- struct net_device *bdev;
- struct ieee80211_sub_if_data *sdata = NULL;
- struct ieee80211_if_ap *ap = NULL;
- struct ieee80211_rate *rate;
- struct rate_control_extra extra;
- u8 *b_head, *b_tail;
- int bh_len, bt_len;
-
- bdev = dev_get_by_index(if_id);
- if (bdev) {
- sdata = IEEE80211_DEV_TO_SUB_IF(bdev);
- ap = &sdata->u.ap;
- dev_put(bdev);
- }
-
- if (!ap || sdata->type != IEEE80211_IF_TYPE_AP ||
- !ap->beacon_head) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- if (net_ratelimit())
- printk(KERN_DEBUG "no beacon data avail for idx=%d "
- "(%s)\n", if_id, bdev ? bdev->name : "N/A");
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
- return NULL;
- }
-
- /* Assume we are generating the normal beacon locally */
- b_head = ap->beacon_head;
- b_tail = ap->beacon_tail;
- bh_len = ap->beacon_head_len;
- bt_len = ap->beacon_tail_len;
-
- skb = dev_alloc_skb(local->tx_headroom +
- bh_len + bt_len + 256 /* maximum TIM len */);
- if (!skb)
- return NULL;
-
- skb_reserve(skb, local->tx_headroom);
- memcpy(skb_put(skb, bh_len), b_head, bh_len);
-
- ieee80211_include_sequence(sdata, (struct ieee80211_hdr *)skb->data);
-
- ieee80211_beacon_add_tim(local, ap, skb);
-
- if (b_tail) {
- memcpy(skb_put(skb, bt_len), b_tail, bt_len);
- }
-
- if (control) {
- memset(&extra, 0, sizeof(extra));
- extra.mode = local->oper_hw_mode;
-
- rate = rate_control_get_rate(local, local->mdev, skb, &extra);
- if (!rate) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: ieee80211_beacon_get: no rate "
- "found\n", local->mdev->name);
- }
- dev_kfree_skb(skb);
- return NULL;
- }
-
- control->tx_rate = (local->short_preamble &&
- (rate->flags & IEEE80211_RATE_PREAMBLE2)) ?
- rate->val2 : rate->val;
- control->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
- control->power_level = local->hw.conf.power_level;
- control->flags |= IEEE80211_TXCTL_NO_ACK;
- control->retry_limit = 1;
- control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK;
- }
-
- ap->num_beacons++;
- return skb;
-}
-EXPORT_SYMBOL(ieee80211_beacon_get);
-
-__le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
- size_t frame_len,
- const struct ieee80211_tx_control *frame_txctl)
-{
- struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_rate *rate;
- int short_preamble = local->short_preamble;
- int erp;
- u16 dur;
-
- rate = frame_txctl->rts_rate;
- erp = !!(rate->flags & IEEE80211_RATE_ERP);
-
- /* CTS duration */
- dur = ieee80211_frame_duration(local, 10, rate->rate,
- erp, short_preamble);
- /* Data frame duration */
- dur += ieee80211_frame_duration(local, frame_len, rate->rate,
- erp, short_preamble);
- /* ACK duration */
- dur += ieee80211_frame_duration(local, 10, rate->rate,
- erp, short_preamble);
-
- return cpu_to_le16(dur);
-}
-EXPORT_SYMBOL(ieee80211_rts_duration);
-
-
-__le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
- size_t frame_len,
- const struct ieee80211_tx_control *frame_txctl)
-{
- struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_rate *rate;
- int short_preamble = local->short_preamble;
- int erp;
- u16 dur;
-
- rate = frame_txctl->rts_rate;
- erp = !!(rate->flags & IEEE80211_RATE_ERP);
-
- /* Data frame duration */
- dur = ieee80211_frame_duration(local, frame_len, rate->rate,
- erp, short_preamble);
- if (!(frame_txctl->flags & IEEE80211_TXCTL_NO_ACK)) {
- /* ACK duration */
- dur += ieee80211_frame_duration(local, 10, rate->rate,
- erp, short_preamble);
- }
-
- return cpu_to_le16(dur);
-}
-EXPORT_SYMBOL(ieee80211_ctstoself_duration);
-
-void ieee80211_rts_get(struct ieee80211_hw *hw,
- const void *frame, size_t frame_len,
- const struct ieee80211_tx_control *frame_txctl,
- struct ieee80211_rts *rts)
-{
- const struct ieee80211_hdr *hdr = frame;
- u16 fctl;
-
- fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS;
- rts->frame_control = cpu_to_le16(fctl);
- rts->duration = ieee80211_rts_duration(hw, frame_len, frame_txctl);
- memcpy(rts->ra, hdr->addr1, sizeof(rts->ra));
- memcpy(rts->ta, hdr->addr2, sizeof(rts->ta));
-}
-EXPORT_SYMBOL(ieee80211_rts_get);
-
-void ieee80211_ctstoself_get(struct ieee80211_hw *hw,
- const void *frame, size_t frame_len,
- const struct ieee80211_tx_control *frame_txctl,
- struct ieee80211_cts *cts)
-{
- const struct ieee80211_hdr *hdr = frame;
- u16 fctl;
-
- fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS;
- cts->frame_control = cpu_to_le16(fctl);
- cts->duration = ieee80211_ctstoself_duration(hw, frame_len, frame_txctl);
- memcpy(cts->ra, hdr->addr1, sizeof(cts->ra));
-}
-EXPORT_SYMBOL(ieee80211_ctstoself_get);
-
-struct sk_buff *
-ieee80211_get_buffered_bc(struct ieee80211_hw *hw, int if_id,
- struct ieee80211_tx_control *control)
-{
- struct ieee80211_local *local = hw_to_local(hw);
- struct sk_buff *skb;
- struct sta_info *sta;
- ieee80211_tx_handler *handler;
- struct ieee80211_txrx_data tx;
- ieee80211_txrx_result res = TXRX_DROP;
- struct net_device *bdev;
- struct ieee80211_sub_if_data *sdata;
- struct ieee80211_if_ap *bss = NULL;
-
- bdev = dev_get_by_index(if_id);
- if (bdev) {
- sdata = IEEE80211_DEV_TO_SUB_IF(bdev);
- bss = &sdata->u.ap;
- dev_put(bdev);
- }
- if (!bss || sdata->type != IEEE80211_IF_TYPE_AP || !bss->beacon_head)
- return NULL;
-
- if (bss->dtim_count != 0)
- return NULL; /* send buffered bc/mc only after DTIM beacon */
- memset(control, 0, sizeof(*control));
- while (1) {
- skb = skb_dequeue(&bss->ps_bc_buf);
- if (!skb)
- return NULL;
- local->total_ps_buffered--;
-
- if (!skb_queue_empty(&bss->ps_bc_buf) && skb->len >= 2) {
- struct ieee80211_hdr *hdr =
- (struct ieee80211_hdr *) skb->data;
- /* more buffered multicast/broadcast frames ==> set
- * MoreData flag in IEEE 802.11 header to inform PS
- * STAs */
- hdr->frame_control |=
- cpu_to_le16(IEEE80211_FCTL_MOREDATA);
- }
-
- if (ieee80211_tx_prepare(&tx, skb, local->mdev, control) == 0)
- break;
- dev_kfree_skb_any(skb);
- }
- sta = tx.sta;
- tx.u.tx.ps_buffered = 1;
-
- for (handler = local->tx_handlers; *handler != NULL; handler++) {
- res = (*handler)(&tx);
- if (res == TXRX_DROP || res == TXRX_QUEUED)
- break;
- }
- dev_put(tx.dev);
- skb = tx.skb; /* handlers are allowed to change skb */
-
- if (res == TXRX_DROP) {
- I802_DEBUG_INC(local->tx_handlers_drop);
- dev_kfree_skb(skb);
- skb = NULL;
- } else if (res == TXRX_QUEUED) {
- I802_DEBUG_INC(local->tx_handlers_queued);
- skb = NULL;
- }
-
- if (sta)
- sta_info_put(sta);
-
- return skb;
-}
-EXPORT_SYMBOL(ieee80211_get_buffered_bc);
+/* everything else */
static int __ieee80211_if_config(struct net_device *dev,
struct sk_buff *beacon,
@@ -2286,7 +454,6 @@ static int __ieee80211_if_config(struct net_device *dev,
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_if_conf conf;
- static u8 scan_bssid[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
if (!local->ops->config_interface || !netif_running(dev))
return 0;
@@ -2295,20 +462,12 @@ static int __ieee80211_if_config(struct net_device *dev,
conf.type = sdata->type;
if (sdata->type == IEEE80211_IF_TYPE_STA ||
sdata->type == IEEE80211_IF_TYPE_IBSS) {
- if (local->sta_scanning &&
- local->scan_dev == dev)
- conf.bssid = scan_bssid;
- else
- conf.bssid = sdata->u.sta.bssid;
+ conf.bssid = sdata->u.sta.bssid;
conf.ssid = sdata->u.sta.ssid;
conf.ssid_len = sdata->u.sta.ssid_len;
- conf.generic_elem = sdata->u.sta.extra_ie;
- conf.generic_elem_len = sdata->u.sta.extra_ie_len;
} else if (sdata->type == IEEE80211_IF_TYPE_AP) {
conf.ssid = sdata->u.ap.ssid;
conf.ssid_len = sdata->u.ap.ssid_len;
- conf.generic_elem = sdata->u.ap.generic_elem;
- conf.generic_elem_len = sdata->u.ap.generic_elem_len;
conf.beacon = beacon;
conf.beacon_control = control;
}
@@ -2351,7 +510,12 @@ int ieee80211_hw_config(struct ieee80211_local *local)
local->hw.conf.channel = chan->chan;
local->hw.conf.channel_val = chan->val;
- local->hw.conf.power_level = chan->power_level;
+ if (!local->hw.conf.power_level) {
+ local->hw.conf.power_level = chan->power_level;
+ } else {
+ local->hw.conf.power_level = min(chan->power_level,
+ local->hw.conf.power_level);
+ }
local->hw.conf.freq = chan->freq;
local->hw.conf.phymode = mode->mode;
local->hw.conf.antenna_max = chan->antenna_max;
@@ -2364,1971 +528,33 @@ int ieee80211_hw_config(struct ieee80211_local *local)
local->hw.conf.phymode);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
- if (local->ops->config)
+ if (local->open_count)
ret = local->ops->config(local_to_hw(local), &local->hw.conf);
return ret;
}
-
-static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
-{
- /* FIX: what would be proper limits for MTU?
- * This interface uses 802.3 frames. */
- if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6) {
- printk(KERN_WARNING "%s: invalid MTU %d\n",
- dev->name, new_mtu);
- return -EINVAL;
- }
-
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: setting MTU %d\n", dev->name, new_mtu);
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
- dev->mtu = new_mtu;
- return 0;
-}
-
-
-static int ieee80211_change_mtu_apdev(struct net_device *dev, int new_mtu)
-{
- /* FIX: what would be proper limits for MTU?
- * This interface uses 802.11 frames. */
- if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN) {
- printk(KERN_WARNING "%s: invalid MTU %d\n",
- dev->name, new_mtu);
- return -EINVAL;
- }
-
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: setting MTU %d\n", dev->name, new_mtu);
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
- dev->mtu = new_mtu;
- return 0;
-}
-
-enum netif_tx_lock_class {
- TX_LOCK_NORMAL,
- TX_LOCK_MASTER,
-};
-
-static inline void netif_tx_lock_nested(struct net_device *dev, int subclass)
-{
- spin_lock_nested(&dev->_xmit_lock, subclass);
- dev->xmit_lock_owner = smp_processor_id();
-}
-
-static void ieee80211_set_multicast_list(struct net_device *dev)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- unsigned short flags;
-
- netif_tx_lock_nested(local->mdev, TX_LOCK_MASTER);
- if (((dev->flags & IFF_ALLMULTI) != 0) ^ (sdata->allmulti != 0)) {
- if (sdata->allmulti) {
- sdata->allmulti = 0;
- local->iff_allmultis--;
- } else {
- sdata->allmulti = 1;
- local->iff_allmultis++;
- }
- }
- if (((dev->flags & IFF_PROMISC) != 0) ^ (sdata->promisc != 0)) {
- if (sdata->promisc) {
- sdata->promisc = 0;
- local->iff_promiscs--;
- } else {
- sdata->promisc = 1;
- local->iff_promiscs++;
- }
- }
- if (dev->mc_count != sdata->mc_count) {
- local->mc_count = local->mc_count - sdata->mc_count +
- dev->mc_count;
- sdata->mc_count = dev->mc_count;
- }
- if (local->ops->set_multicast_list) {
- flags = local->mdev->flags;
- if (local->iff_allmultis)
- flags |= IFF_ALLMULTI;
- if (local->iff_promiscs)
- flags |= IFF_PROMISC;
- read_lock(&local->sub_if_lock);
- local->ops->set_multicast_list(local_to_hw(local), flags,
- local->mc_count);
- read_unlock(&local->sub_if_lock);
- }
- netif_tx_unlock(local->mdev);
-}
-
-struct dev_mc_list *ieee80211_get_mc_list_item(struct ieee80211_hw *hw,
- struct dev_mc_list *prev,
- void **ptr)
-{
- struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_sub_if_data *sdata = *ptr;
- struct dev_mc_list *mc;
-
- if (!prev) {
- WARN_ON(sdata);
- sdata = NULL;
- }
- if (!prev || !prev->next) {
- if (sdata)
- sdata = list_entry(sdata->list.next,
- struct ieee80211_sub_if_data, list);
- else
- sdata = list_entry(local->sub_if_list.next,
- struct ieee80211_sub_if_data, list);
- if (&sdata->list != &local->sub_if_list)
- mc = sdata->dev->mc_list;
- else
- mc = NULL;
- } else
- mc = prev->next;
-
- *ptr = sdata;
- return mc;
-}
-EXPORT_SYMBOL(ieee80211_get_mc_list_item);
-
-static struct net_device_stats *ieee80211_get_stats(struct net_device *dev)
-{
- struct ieee80211_sub_if_data *sdata;
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- return &(sdata->stats);
-}
-
-static void ieee80211_if_shutdown(struct net_device *dev)
+void ieee80211_erp_info_change_notify(struct net_device *dev, u8 changes)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- ASSERT_RTNL();
- switch (sdata->type) {
- case IEEE80211_IF_TYPE_STA:
- case IEEE80211_IF_TYPE_IBSS:
- sdata->u.sta.state = IEEE80211_DISABLED;
- del_timer_sync(&sdata->u.sta.timer);
- skb_queue_purge(&sdata->u.sta.skb_queue);
- if (!local->ops->hw_scan &&
- local->scan_dev == sdata->dev) {
- local->sta_scanning = 0;
- cancel_delayed_work(&local->scan_work);
- }
- flush_workqueue(local->hw.workqueue);
- break;
- }
-}
-
-static inline int identical_mac_addr_allowed(int type1, int type2)
-{
- return (type1 == IEEE80211_IF_TYPE_MNTR ||
- type2 == IEEE80211_IF_TYPE_MNTR ||
- (type1 == IEEE80211_IF_TYPE_AP &&
- type2 == IEEE80211_IF_TYPE_WDS) ||
- (type1 == IEEE80211_IF_TYPE_WDS &&
- (type2 == IEEE80211_IF_TYPE_WDS ||
- type2 == IEEE80211_IF_TYPE_AP)) ||
- (type1 == IEEE80211_IF_TYPE_AP &&
- type2 == IEEE80211_IF_TYPE_VLAN) ||
- (type1 == IEEE80211_IF_TYPE_VLAN &&
- (type2 == IEEE80211_IF_TYPE_AP ||
- type2 == IEEE80211_IF_TYPE_VLAN)));
-}
-
-static int ieee80211_master_open(struct net_device *dev)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata;
- int res = -EOPNOTSUPP;
-
- read_lock(&local->sub_if_lock);
- list_for_each_entry(sdata, &local->sub_if_list, list) {
- if (sdata->dev != dev && netif_running(sdata->dev)) {
- res = 0;
- break;
- }
- }
- read_unlock(&local->sub_if_lock);
- return res;
-}
-
-static int ieee80211_master_stop(struct net_device *dev)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata;
-
- read_lock(&local->sub_if_lock);
- list_for_each_entry(sdata, &local->sub_if_list, list)
- if (sdata->dev != dev && netif_running(sdata->dev))
- dev_close(sdata->dev);
- read_unlock(&local->sub_if_lock);
-
- return 0;
-}
-
-static int ieee80211_mgmt_open(struct net_device *dev)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-
- if (!netif_running(local->mdev))
- return -EOPNOTSUPP;
- return 0;
-}
-
-static int ieee80211_mgmt_stop(struct net_device *dev)
-{
- return 0;
-}
-
-/* Check if running monitor interfaces should go to a "soft monitor" mode
- * and switch them if necessary. */
-static inline void ieee80211_start_soft_monitor(struct ieee80211_local *local)
-{
- struct ieee80211_if_init_conf conf;
-
- if (local->open_count && local->open_count == local->monitors &&
- !(local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER) &&
- local->ops->remove_interface) {
- conf.if_id = -1;
- conf.type = IEEE80211_IF_TYPE_MNTR;
- conf.mac_addr = NULL;
- local->ops->remove_interface(local_to_hw(local), &conf);
- }
-}
-
-/* Check if running monitor interfaces should go to a "hard monitor" mode
- * and switch them if necessary. */
-static void ieee80211_start_hard_monitor(struct ieee80211_local *local)
-{
- struct ieee80211_if_init_conf conf;
-
- if (local->open_count && local->open_count == local->monitors &&
- !(local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER)) {
- conf.if_id = -1;
- conf.type = IEEE80211_IF_TYPE_MNTR;
- conf.mac_addr = NULL;
- local->ops->add_interface(local_to_hw(local), &conf);
- }
-}
-
-static int ieee80211_open(struct net_device *dev)
-{
- struct ieee80211_sub_if_data *sdata, *nsdata;
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_if_init_conf conf;
- int res;
-
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- read_lock(&local->sub_if_lock);
- list_for_each_entry(nsdata, &local->sub_if_list, list) {
- struct net_device *ndev = nsdata->dev;
-
- if (ndev != dev && ndev != local->mdev && netif_running(ndev) &&
- compare_ether_addr(dev->dev_addr, ndev->dev_addr) == 0 &&
- !identical_mac_addr_allowed(sdata->type, nsdata->type)) {
- read_unlock(&local->sub_if_lock);
- return -ENOTUNIQ;
- }
- }
- read_unlock(&local->sub_if_lock);
-
- if (sdata->type == IEEE80211_IF_TYPE_WDS &&
- is_zero_ether_addr(sdata->u.wds.remote_addr))
- return -ENOLINK;
-
- if (sdata->type == IEEE80211_IF_TYPE_MNTR && local->open_count &&
- !(local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER)) {
- /* run the interface in a "soft monitor" mode */
- local->monitors++;
- local->open_count++;
- local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP;
- return 0;
- }
- ieee80211_start_soft_monitor(local);
-
- conf.if_id = dev->ifindex;
- conf.type = sdata->type;
- conf.mac_addr = dev->dev_addr;
- res = local->ops->add_interface(local_to_hw(local), &conf);
- if (res) {
- if (sdata->type == IEEE80211_IF_TYPE_MNTR)
- ieee80211_start_hard_monitor(local);
- return res;
- }
-
- if (local->open_count == 0) {
- res = 0;
- tasklet_enable(&local->tx_pending_tasklet);
- tasklet_enable(&local->tasklet);
- if (local->ops->open)
- res = local->ops->open(local_to_hw(local));
- if (res == 0) {
- res = dev_open(local->mdev);
- if (res) {
- if (local->ops->stop)
- local->ops->stop(local_to_hw(local));
- } else {
- res = ieee80211_hw_config(local);
- if (res && local->ops->stop)
- local->ops->stop(local_to_hw(local));
- else if (!res && local->apdev)
- dev_open(local->apdev);
- }
- }
- if (res) {
- if (local->ops->remove_interface)
- local->ops->remove_interface(local_to_hw(local),
- &conf);
- return res;
- }
- }
- local->open_count++;
-
- if (sdata->type == IEEE80211_IF_TYPE_MNTR) {
- local->monitors++;
- local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP;
- } else
- ieee80211_if_config(dev);
-
- if (sdata->type == IEEE80211_IF_TYPE_STA &&
- !local->user_space_mlme)
- netif_carrier_off(dev);
- else
- netif_carrier_on(dev);
-
- netif_start_queue(dev);
- return 0;
-}
-
-
-static int ieee80211_stop(struct net_device *dev)
-{
- struct ieee80211_sub_if_data *sdata;
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- if (sdata->type == IEEE80211_IF_TYPE_MNTR &&
- local->open_count > 1 &&
- !(local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER)) {
- /* remove "soft monitor" interface */
- local->open_count--;
- local->monitors--;
- if (!local->monitors)
- local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP;
- return 0;
- }
-
- netif_stop_queue(dev);
- ieee80211_if_shutdown(dev);
-
- if (sdata->type == IEEE80211_IF_TYPE_MNTR) {
- local->monitors--;
- if (!local->monitors)
- local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP;
- }
-
- local->open_count--;
- if (local->open_count == 0) {
- if (netif_running(local->mdev))
- dev_close(local->mdev);
- if (local->apdev)
- dev_close(local->apdev);
- if (local->ops->stop)
- local->ops->stop(local_to_hw(local));
- tasklet_disable(&local->tx_pending_tasklet);
- tasklet_disable(&local->tasklet);
- }
- if (local->ops->remove_interface) {
- struct ieee80211_if_init_conf conf;
-
- conf.if_id = dev->ifindex;
- conf.type = sdata->type;
- conf.mac_addr = dev->dev_addr;
- local->ops->remove_interface(local_to_hw(local), &conf);
- }
-
- ieee80211_start_hard_monitor(local);
-
- return 0;
-}
-
-
-static int header_parse_80211(struct sk_buff *skb, unsigned char *haddr)
-{
- memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */
- return ETH_ALEN;
-}
-
-static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
-{
- return compare_ether_addr(raddr, addr) == 0 ||
- is_broadcast_ether_addr(raddr);
+ if (local->ops->erp_ie_changed)
+ local->ops->erp_ie_changed(local_to_hw(local), changes,
+ !!(sdata->flags & IEEE80211_SDATA_USE_PROTECTION),
+ !(sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE));
}
-
-static ieee80211_txrx_result
-ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
+void ieee80211_reset_erp_info(struct net_device *dev)
{
- struct net_device *dev = rx->dev;
- struct ieee80211_local *local = rx->local;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
- u16 fc, hdrlen, ethertype;
- u8 *payload;
- u8 dst[ETH_ALEN];
- u8 src[ETH_ALEN];
- struct sk_buff *skb = rx->skb, *skb2;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- fc = rx->fc;
- if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA))
- return TXRX_CONTINUE;
-
- if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
- return TXRX_DROP;
-
- hdrlen = ieee80211_get_hdrlen(fc);
-
- /* convert IEEE 802.11 header + possible LLC headers into Ethernet
- * header
- * IEEE 802.11 address fields:
- * ToDS FromDS Addr1 Addr2 Addr3 Addr4
- * 0 0 DA SA BSSID n/a
- * 0 1 DA BSSID SA n/a
- * 1 0 BSSID SA DA n/a
- * 1 1 RA TA DA SA
- */
-
- switch (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
- case IEEE80211_FCTL_TODS:
- /* BSSID SA DA */
- memcpy(dst, hdr->addr3, ETH_ALEN);
- memcpy(src, hdr->addr2, ETH_ALEN);
-
- if (unlikely(sdata->type != IEEE80211_IF_TYPE_AP &&
- sdata->type != IEEE80211_IF_TYPE_VLAN)) {
- printk(KERN_DEBUG "%s: dropped ToDS frame (BSSID="
- MAC_FMT " SA=" MAC_FMT " DA=" MAC_FMT ")\n",
- dev->name, MAC_ARG(hdr->addr1),
- MAC_ARG(hdr->addr2), MAC_ARG(hdr->addr3));
- return TXRX_DROP;
- }
- break;
- case (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
- /* RA TA DA SA */
- memcpy(dst, hdr->addr3, ETH_ALEN);
- memcpy(src, hdr->addr4, ETH_ALEN);
-
- if (unlikely(sdata->type != IEEE80211_IF_TYPE_WDS)) {
- printk(KERN_DEBUG "%s: dropped FromDS&ToDS frame (RA="
- MAC_FMT " TA=" MAC_FMT " DA=" MAC_FMT " SA="
- MAC_FMT ")\n",
- rx->dev->name, MAC_ARG(hdr->addr1),
- MAC_ARG(hdr->addr2), MAC_ARG(hdr->addr3),
- MAC_ARG(hdr->addr4));
- return TXRX_DROP;
- }
- break;
- case IEEE80211_FCTL_FROMDS:
- /* DA BSSID SA */
- memcpy(dst, hdr->addr1, ETH_ALEN);
- memcpy(src, hdr->addr3, ETH_ALEN);
-
- if (sdata->type != IEEE80211_IF_TYPE_STA) {
- return TXRX_DROP;
- }
- break;
- case 0:
- /* DA SA BSSID */
- memcpy(dst, hdr->addr1, ETH_ALEN);
- memcpy(src, hdr->addr2, ETH_ALEN);
-
- if (sdata->type != IEEE80211_IF_TYPE_IBSS) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: dropped IBSS frame (DA="
- MAC_FMT " SA=" MAC_FMT " BSSID=" MAC_FMT
- ")\n",
- dev->name, MAC_ARG(hdr->addr1),
- MAC_ARG(hdr->addr2),
- MAC_ARG(hdr->addr3));
- }
- return TXRX_DROP;
- }
- break;
- }
-
- payload = skb->data + hdrlen;
-
- if (unlikely(skb->len - hdrlen < 8)) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: RX too short data frame "
- "payload\n", dev->name);
- }
- return TXRX_DROP;
- }
-
- ethertype = (payload[6] << 8) | payload[7];
-
- if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
- ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
- compare_ether_addr(payload, bridge_tunnel_header) == 0)) {
- /* remove RFC1042 or Bridge-Tunnel encapsulation and
- * replace EtherType */
- skb_pull(skb, hdrlen + 6);
- memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
- memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
- } else {
- struct ethhdr *ehdr;
- __be16 len;
- skb_pull(skb, hdrlen);
- len = htons(skb->len);
- ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
- memcpy(ehdr->h_dest, dst, ETH_ALEN);
- memcpy(ehdr->h_source, src, ETH_ALEN);
- ehdr->h_proto = len;
- }
- skb->dev = dev;
-
- skb2 = NULL;
-
- sdata->stats.rx_packets++;
- sdata->stats.rx_bytes += skb->len;
-
- if (local->bridge_packets && (sdata->type == IEEE80211_IF_TYPE_AP
- || sdata->type == IEEE80211_IF_TYPE_VLAN) && rx->u.rx.ra_match) {
- if (is_multicast_ether_addr(skb->data)) {
- /* send multicast frames both to higher layers in
- * local net stack and back to the wireless media */
- skb2 = skb_copy(skb, GFP_ATOMIC);
- if (!skb2)
- printk(KERN_DEBUG "%s: failed to clone "
- "multicast frame\n", dev->name);
- } else {
- struct sta_info *dsta;
- dsta = sta_info_get(local, skb->data);
- if (dsta && !dsta->dev) {
- printk(KERN_DEBUG "Station with null dev "
- "structure!\n");
- } else if (dsta && dsta->dev == dev) {
- /* Destination station is associated to this
- * AP, so send the frame directly to it and
- * do not pass the frame to local net stack.
- */
- skb2 = skb;
- skb = NULL;
- }
- if (dsta)
- sta_info_put(dsta);
- }
- }
-
- if (skb) {
- /* deliver to local stack */
- skb->protocol = eth_type_trans(skb, dev);
- memset(skb->cb, 0, sizeof(skb->cb));
- netif_rx(skb);
- }
-
- if (skb2) {
- /* send to wireless media */
- skb2->protocol = __constant_htons(ETH_P_802_3);
- skb_set_network_header(skb2, 0);
- skb_set_mac_header(skb2, 0);
- dev_queue_xmit(skb2);
- }
-
- return TXRX_QUEUED;
-}
-
-
-static struct ieee80211_rate *
-ieee80211_get_rate(struct ieee80211_local *local, int phymode, int hw_rate)
-{
- struct ieee80211_hw_mode *mode;
- int r;
-
- list_for_each_entry(mode, &local->modes_list, list) {
- if (mode->mode != phymode)
- continue;
- for (r = 0; r < mode->num_rates; r++) {
- struct ieee80211_rate *rate = &mode->rates[r];
- if (rate->val == hw_rate ||
- (rate->flags & IEEE80211_RATE_PREAMBLE2 &&
- rate->val2 == hw_rate))
- return rate;
- }
- }
-
- return NULL;
-}
-
-static void
-ieee80211_fill_frame_info(struct ieee80211_local *local,
- struct ieee80211_frame_info *fi,
- struct ieee80211_rx_status *status)
-{
- if (status) {
- struct timespec ts;
- struct ieee80211_rate *rate;
-
- jiffies_to_timespec(jiffies, &ts);
- fi->hosttime = cpu_to_be64((u64) ts.tv_sec * 1000000 +
- ts.tv_nsec / 1000);
- fi->mactime = cpu_to_be64(status->mactime);
- switch (status->phymode) {
- case MODE_IEEE80211A:
- fi->phytype = htonl(ieee80211_phytype_ofdm_dot11_a);
- break;
- case MODE_IEEE80211B:
- fi->phytype = htonl(ieee80211_phytype_dsss_dot11_b);
- break;
- case MODE_IEEE80211G:
- fi->phytype = htonl(ieee80211_phytype_pbcc_dot11_g);
- break;
- case MODE_ATHEROS_TURBO:
- fi->phytype =
- htonl(ieee80211_phytype_dsss_dot11_turbo);
- break;
- default:
- fi->phytype = htonl(0xAAAAAAAA);
- break;
- }
- fi->channel = htonl(status->channel);
- rate = ieee80211_get_rate(local, status->phymode,
- status->rate);
- if (rate) {
- fi->datarate = htonl(rate->rate);
- if (rate->flags & IEEE80211_RATE_PREAMBLE2) {
- if (status->rate == rate->val)
- fi->preamble = htonl(2); /* long */
- else if (status->rate == rate->val2)
- fi->preamble = htonl(1); /* short */
- } else
- fi->preamble = htonl(0);
- } else {
- fi->datarate = htonl(0);
- fi->preamble = htonl(0);
- }
-
- fi->antenna = htonl(status->antenna);
- fi->priority = htonl(0xffffffff); /* no clue */
- fi->ssi_type = htonl(ieee80211_ssi_raw);
- fi->ssi_signal = htonl(status->ssi);
- fi->ssi_noise = 0x00000000;
- fi->encoding = 0;
- } else {
- /* clear everything because we really don't know.
- * the msg_type field isn't present on monitor frames
- * so we don't know whether it will be present or not,
- * but it's ok to not clear it since it'll be assigned
- * anyway */
- memset(fi, 0, sizeof(*fi) - sizeof(fi->msg_type));
-
- fi->ssi_type = htonl(ieee80211_ssi_none);
- }
- fi->version = htonl(IEEE80211_FI_VERSION);
- fi->length = cpu_to_be32(sizeof(*fi) - sizeof(fi->msg_type));
-}
-
-/* this routine is actually not just for this, but also
- * for pushing fake 'management' frames into userspace.
- * it shall be replaced by a netlink-based system. */
-void
-ieee80211_rx_mgmt(struct ieee80211_local *local, struct sk_buff *skb,
- struct ieee80211_rx_status *status, u32 msg_type)
-{
- struct ieee80211_frame_info *fi;
- const size_t hlen = sizeof(struct ieee80211_frame_info);
- struct ieee80211_sub_if_data *sdata;
-
- skb->dev = local->apdev;
-
- sdata = IEEE80211_DEV_TO_SUB_IF(local->apdev);
-
- if (skb_headroom(skb) < hlen) {
- I802_DEBUG_INC(local->rx_expand_skb_head);
- if (pskb_expand_head(skb, hlen, 0, GFP_ATOMIC)) {
- dev_kfree_skb(skb);
- return;
- }
- }
-
- fi = (struct ieee80211_frame_info *) skb_push(skb, hlen);
-
- ieee80211_fill_frame_info(local, fi, status);
- fi->msg_type = htonl(msg_type);
-
- sdata->stats.rx_packets++;
- sdata->stats.rx_bytes += skb->len;
-
- skb_set_mac_header(skb, 0);
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- skb->pkt_type = PACKET_OTHERHOST;
- skb->protocol = htons(ETH_P_802_2);
- memset(skb->cb, 0, sizeof(skb->cb));
- netif_rx(skb);
-}
-
-static void
-ieee80211_rx_monitor(struct net_device *dev, struct sk_buff *skb,
- struct ieee80211_rx_status *status)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata;
- struct ieee80211_rate *rate;
- struct ieee80211_rtap_hdr {
- struct ieee80211_radiotap_header hdr;
- u8 flags;
- u8 rate;
- __le16 chan_freq;
- __le16 chan_flags;
- u8 antsignal;
- } __attribute__ ((packed)) *rthdr;
-
- skb->dev = dev;
-
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- if (status->flag & RX_FLAG_RADIOTAP)
- goto out;
-
- if (skb_headroom(skb) < sizeof(*rthdr)) {
- I802_DEBUG_INC(local->rx_expand_skb_head);
- if (pskb_expand_head(skb, sizeof(*rthdr), 0, GFP_ATOMIC)) {
- dev_kfree_skb(skb);
- return;
- }
- }
-
- rthdr = (struct ieee80211_rtap_hdr *) skb_push(skb, sizeof(*rthdr));
- memset(rthdr, 0, sizeof(*rthdr));
- rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr));
- rthdr->hdr.it_present =
- cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
- (1 << IEEE80211_RADIOTAP_RATE) |
- (1 << IEEE80211_RADIOTAP_CHANNEL) |
- (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL));
- rthdr->flags = local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS ?
- IEEE80211_RADIOTAP_F_FCS : 0;
- rate = ieee80211_get_rate(local, status->phymode, status->rate);
- if (rate)
- rthdr->rate = rate->rate / 5;
- rthdr->chan_freq = cpu_to_le16(status->freq);
- rthdr->chan_flags =
- status->phymode == MODE_IEEE80211A ?
- cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ) :
- cpu_to_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ);
- rthdr->antsignal = status->ssi;
-
- out:
- sdata->stats.rx_packets++;
- sdata->stats.rx_bytes += skb->len;
-
- skb_set_mac_header(skb, 0);
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- skb->pkt_type = PACKET_OTHERHOST;
- skb->protocol = htons(ETH_P_802_2);
- memset(skb->cb, 0, sizeof(skb->cb));
- netif_rx(skb);
-}
-
-int ieee80211_radar_status(struct ieee80211_hw *hw, int channel,
- int radar, int radar_type)
-{
- struct sk_buff *skb;
- struct ieee80211_radar_info *msg;
- struct ieee80211_local *local = hw_to_local(hw);
-
- if (!local->apdev)
- return 0;
-
- skb = dev_alloc_skb(sizeof(struct ieee80211_frame_info) +
- sizeof(struct ieee80211_radar_info));
-
- if (!skb)
- return -ENOMEM;
- skb_reserve(skb, sizeof(struct ieee80211_frame_info));
-
- msg = (struct ieee80211_radar_info *)
- skb_put(skb, sizeof(struct ieee80211_radar_info));
- msg->channel = channel;
- msg->radar = radar;
- msg->radar_type = radar_type;
-
- ieee80211_rx_mgmt(local, skb, NULL, ieee80211_msg_radar);
- return 0;
-}
-EXPORT_SYMBOL(ieee80211_radar_status);
-
-
-static void ap_sta_ps_start(struct net_device *dev, struct sta_info *sta)
-{
- struct ieee80211_sub_if_data *sdata;
- sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
-
- if (sdata->bss)
- atomic_inc(&sdata->bss->num_sta_ps);
- sta->flags |= WLAN_STA_PS;
- sta->pspoll = 0;
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "%s: STA " MAC_FMT " aid %d enters power "
- "save mode\n", dev->name, MAC_ARG(sta->addr), sta->aid);
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
-}
-
-
-static int ap_sta_ps_end(struct net_device *dev, struct sta_info *sta)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct sk_buff *skb;
- int sent = 0;
- struct ieee80211_sub_if_data *sdata;
- struct ieee80211_tx_packet_data *pkt_data;
-
- sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
- if (sdata->bss)
- atomic_dec(&sdata->bss->num_sta_ps);
- sta->flags &= ~(WLAN_STA_PS | WLAN_STA_TIM);
- sta->pspoll = 0;
- if (!skb_queue_empty(&sta->ps_tx_buf)) {
- if (local->ops->set_tim)
- local->ops->set_tim(local_to_hw(local), sta->aid, 0);
- if (sdata->bss)
- bss_tim_clear(local, sdata->bss, sta->aid);
- }
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "%s: STA " MAC_FMT " aid %d exits power "
- "save mode\n", dev->name, MAC_ARG(sta->addr), sta->aid);
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
- /* Send all buffered frames to the station */
- while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
- pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
- sent++;
- pkt_data->requeue = 1;
- dev_queue_xmit(skb);
- }
- while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
- pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
- local->total_ps_buffered--;
- sent++;
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "%s: STA " MAC_FMT " aid %d send PS frame "
- "since STA not sleeping anymore\n", dev->name,
- MAC_ARG(sta->addr), sta->aid);
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
- pkt_data->requeue = 1;
- dev_queue_xmit(skb);
- }
-
- return sent;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx)
-{
- struct sk_buff *skb;
- int no_pending_pkts;
-
- if (likely(!rx->sta ||
- (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_CTL ||
- (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PSPOLL ||
- !rx->u.rx.ra_match))
- return TXRX_CONTINUE;
-
- skb = skb_dequeue(&rx->sta->tx_filtered);
- if (!skb) {
- skb = skb_dequeue(&rx->sta->ps_tx_buf);
- if (skb)
- rx->local->total_ps_buffered--;
- }
- no_pending_pkts = skb_queue_empty(&rx->sta->tx_filtered) &&
- skb_queue_empty(&rx->sta->ps_tx_buf);
-
- if (skb) {
- struct ieee80211_hdr *hdr =
- (struct ieee80211_hdr *) skb->data;
-
- /* tell TX path to send one frame even though the STA may
- * still remain is PS mode after this frame exchange */
- rx->sta->pspoll = 1;
-
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "STA " MAC_FMT " aid %d: PS Poll (entries "
- "after %d)\n",
- MAC_ARG(rx->sta->addr), rx->sta->aid,
- skb_queue_len(&rx->sta->ps_tx_buf));
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
-
- /* Use MoreData flag to indicate whether there are more
- * buffered frames for this STA */
- if (no_pending_pkts) {
- hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
- rx->sta->flags &= ~WLAN_STA_TIM;
- } else
- hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
-
- dev_queue_xmit(skb);
-
- if (no_pending_pkts) {
- if (rx->local->ops->set_tim)
- rx->local->ops->set_tim(local_to_hw(rx->local),
- rx->sta->aid, 0);
- if (rx->sdata->bss)
- bss_tim_clear(rx->local, rx->sdata->bss, rx->sta->aid);
- }
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- } else if (!rx->u.rx.sent_ps_buffered) {
- printk(KERN_DEBUG "%s: STA " MAC_FMT " sent PS Poll even "
- "though there is no buffered frames for it\n",
- rx->dev->name, MAC_ARG(rx->sta->addr));
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
-
- }
-
- /* Free PS Poll skb here instead of returning TXRX_DROP that would
- * count as an dropped frame. */
- dev_kfree_skb(rx->skb);
-
- return TXRX_QUEUED;
-}
-
-
-static inline struct ieee80211_fragment_entry *
-ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
- unsigned int frag, unsigned int seq, int rx_queue,
- struct sk_buff **skb)
-{
- struct ieee80211_fragment_entry *entry;
- int idx;
-
- idx = sdata->fragment_next;
- entry = &sdata->fragments[sdata->fragment_next++];
- if (sdata->fragment_next >= IEEE80211_FRAGMENT_MAX)
- sdata->fragment_next = 0;
-
- if (!skb_queue_empty(&entry->skb_list)) {
-#ifdef CONFIG_MAC80211_DEBUG
- struct ieee80211_hdr *hdr =
- (struct ieee80211_hdr *) entry->skb_list.next->data;
- printk(KERN_DEBUG "%s: RX reassembly removed oldest "
- "fragment entry (idx=%d age=%lu seq=%d last_frag=%d "
- "addr1=" MAC_FMT " addr2=" MAC_FMT "\n",
- sdata->dev->name, idx,
- jiffies - entry->first_frag_time, entry->seq,
- entry->last_frag, MAC_ARG(hdr->addr1),
- MAC_ARG(hdr->addr2));
-#endif /* CONFIG_MAC80211_DEBUG */
- __skb_queue_purge(&entry->skb_list);
- }
-
- __skb_queue_tail(&entry->skb_list, *skb); /* no need for locking */
- *skb = NULL;
- entry->first_frag_time = jiffies;
- entry->seq = seq;
- entry->rx_queue = rx_queue;
- entry->last_frag = frag;
- entry->ccmp = 0;
- entry->extra_len = 0;
-
- return entry;
-}
-
-
-static inline struct ieee80211_fragment_entry *
-ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata,
- u16 fc, unsigned int frag, unsigned int seq,
- int rx_queue, struct ieee80211_hdr *hdr)
-{
- struct ieee80211_fragment_entry *entry;
- int i, idx;
-
- idx = sdata->fragment_next;
- for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) {
- struct ieee80211_hdr *f_hdr;
- u16 f_fc;
-
- idx--;
- if (idx < 0)
- idx = IEEE80211_FRAGMENT_MAX - 1;
-
- entry = &sdata->fragments[idx];
- if (skb_queue_empty(&entry->skb_list) || entry->seq != seq ||
- entry->rx_queue != rx_queue ||
- entry->last_frag + 1 != frag)
- continue;
-
- f_hdr = (struct ieee80211_hdr *) entry->skb_list.next->data;
- f_fc = le16_to_cpu(f_hdr->frame_control);
-
- if ((fc & IEEE80211_FCTL_FTYPE) != (f_fc & IEEE80211_FCTL_FTYPE) ||
- compare_ether_addr(hdr->addr1, f_hdr->addr1) != 0 ||
- compare_ether_addr(hdr->addr2, f_hdr->addr2) != 0)
- continue;
-
- if (entry->first_frag_time + 2 * HZ < jiffies) {
- __skb_queue_purge(&entry->skb_list);
- continue;
- }
- return entry;
- }
-
- return NULL;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx)
-{
- struct ieee80211_hdr *hdr;
- u16 sc;
- unsigned int frag, seq;
- struct ieee80211_fragment_entry *entry;
- struct sk_buff *skb;
-
- hdr = (struct ieee80211_hdr *) rx->skb->data;
- sc = le16_to_cpu(hdr->seq_ctrl);
- frag = sc & IEEE80211_SCTL_FRAG;
-
- if (likely((!(rx->fc & IEEE80211_FCTL_MOREFRAGS) && frag == 0) ||
- (rx->skb)->len < 24 ||
- is_multicast_ether_addr(hdr->addr1))) {
- /* not fragmented */
- goto out;
- }
- I802_DEBUG_INC(rx->local->rx_handlers_fragments);
-
- seq = (sc & IEEE80211_SCTL_SEQ) >> 4;
-
- if (frag == 0) {
- /* This is the first fragment of a new frame. */
- entry = ieee80211_reassemble_add(rx->sdata, frag, seq,
- rx->u.rx.queue, &(rx->skb));
- if (rx->key && rx->key->alg == ALG_CCMP &&
- (rx->fc & IEEE80211_FCTL_PROTECTED)) {
- /* Store CCMP PN so that we can verify that the next
- * fragment has a sequential PN value. */
- entry->ccmp = 1;
- memcpy(entry->last_pn,
- rx->key->u.ccmp.rx_pn[rx->u.rx.queue],
- CCMP_PN_LEN);
- }
- return TXRX_QUEUED;
- }
-
- /* This is a fragment for a frame that should already be pending in
- * fragment cache. Add this fragment to the end of the pending entry.
- */
- entry = ieee80211_reassemble_find(rx->sdata, rx->fc, frag, seq,
- rx->u.rx.queue, hdr);
- if (!entry) {
- I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
- return TXRX_DROP;
- }
-
- /* Verify that MPDUs within one MSDU have sequential PN values.
- * (IEEE 802.11i, 8.3.3.4.5) */
- if (entry->ccmp) {
- int i;
- u8 pn[CCMP_PN_LEN], *rpn;
- if (!rx->key || rx->key->alg != ALG_CCMP)
- return TXRX_DROP;
- memcpy(pn, entry->last_pn, CCMP_PN_LEN);
- for (i = CCMP_PN_LEN - 1; i >= 0; i--) {
- pn[i]++;
- if (pn[i])
- break;
- }
- rpn = rx->key->u.ccmp.rx_pn[rx->u.rx.queue];
- if (memcmp(pn, rpn, CCMP_PN_LEN) != 0) {
- printk(KERN_DEBUG "%s: defrag: CCMP PN not sequential"
- " A2=" MAC_FMT " PN=%02x%02x%02x%02x%02x%02x "
- "(expected %02x%02x%02x%02x%02x%02x)\n",
- rx->dev->name, MAC_ARG(hdr->addr2),
- rpn[0], rpn[1], rpn[2], rpn[3], rpn[4], rpn[5],
- pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]);
- return TXRX_DROP;
- }
- memcpy(entry->last_pn, pn, CCMP_PN_LEN);
- }
-
- skb_pull(rx->skb, ieee80211_get_hdrlen(rx->fc));
- __skb_queue_tail(&entry->skb_list, rx->skb);
- entry->last_frag = frag;
- entry->extra_len += rx->skb->len;
- if (rx->fc & IEEE80211_FCTL_MOREFRAGS) {
- rx->skb = NULL;
- return TXRX_QUEUED;
- }
-
- rx->skb = __skb_dequeue(&entry->skb_list);
- if (skb_tailroom(rx->skb) < entry->extra_len) {
- I802_DEBUG_INC(rx->local->rx_expand_skb_head2);
- if (unlikely(pskb_expand_head(rx->skb, 0, entry->extra_len,
- GFP_ATOMIC))) {
- I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
- __skb_queue_purge(&entry->skb_list);
- return TXRX_DROP;
- }
- }
- while ((skb = __skb_dequeue(&entry->skb_list))) {
- memcpy(skb_put(rx->skb, skb->len), skb->data, skb->len);
- dev_kfree_skb(skb);
- }
-
- /* Complete frame has been reassembled - process it now */
- rx->fragmented = 1;
-
- out:
- if (rx->sta)
- rx->sta->rx_packets++;
- if (is_multicast_ether_addr(hdr->addr1))
- rx->local->dot11MulticastReceivedFrameCount++;
- else
- ieee80211_led_rx(rx->local);
- return TXRX_CONTINUE;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_rx_h_monitor(struct ieee80211_txrx_data *rx)
-{
- if (rx->sdata->type == IEEE80211_IF_TYPE_MNTR) {
- ieee80211_rx_monitor(rx->dev, rx->skb, rx->u.rx.status);
- return TXRX_QUEUED;
- }
-
- if (rx->u.rx.status->flag & RX_FLAG_RADIOTAP)
- skb_pull(rx->skb, ieee80211_get_radiotap_len(rx->skb));
-
- return TXRX_CONTINUE;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
-{
- struct ieee80211_hdr *hdr;
- int always_sta_key;
- hdr = (struct ieee80211_hdr *) rx->skb->data;
-
- /* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */
- if (rx->sta && !is_multicast_ether_addr(hdr->addr1)) {
- if (unlikely(rx->fc & IEEE80211_FCTL_RETRY &&
- rx->sta->last_seq_ctrl[rx->u.rx.queue] ==
- hdr->seq_ctrl)) {
- if (rx->u.rx.ra_match) {
- rx->local->dot11FrameDuplicateCount++;
- rx->sta->num_duplicates++;
- }
- return TXRX_DROP;
- } else
- rx->sta->last_seq_ctrl[rx->u.rx.queue] = hdr->seq_ctrl;
- }
-
- if ((rx->local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) &&
- rx->skb->len > FCS_LEN)
- skb_trim(rx->skb, rx->skb->len - FCS_LEN);
-
- if (unlikely(rx->skb->len < 16)) {
- I802_DEBUG_INC(rx->local->rx_handlers_drop_short);
- return TXRX_DROP;
- }
-
- if (!rx->u.rx.ra_match)
- rx->skb->pkt_type = PACKET_OTHERHOST;
- else if (compare_ether_addr(rx->dev->dev_addr, hdr->addr1) == 0)
- rx->skb->pkt_type = PACKET_HOST;
- else if (is_multicast_ether_addr(hdr->addr1)) {
- if (is_broadcast_ether_addr(hdr->addr1))
- rx->skb->pkt_type = PACKET_BROADCAST;
- else
- rx->skb->pkt_type = PACKET_MULTICAST;
- } else
- rx->skb->pkt_type = PACKET_OTHERHOST;
-
- /* Drop disallowed frame classes based on STA auth/assoc state;
- * IEEE 802.11, Chap 5.5.
- *
- * 80211.o does filtering only based on association state, i.e., it
- * drops Class 3 frames from not associated stations. hostapd sends
- * deauth/disassoc frames when needed. In addition, hostapd is
- * responsible for filtering on both auth and assoc states.
- */
- if (unlikely(((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA ||
- ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL &&
- (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)) &&
- rx->sdata->type != IEEE80211_IF_TYPE_IBSS &&
- (!rx->sta || !(rx->sta->flags & WLAN_STA_ASSOC)))) {
- if ((!(rx->fc & IEEE80211_FCTL_FROMDS) &&
- !(rx->fc & IEEE80211_FCTL_TODS) &&
- (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)
- || !rx->u.rx.ra_match) {
- /* Drop IBSS frames and frames for other hosts
- * silently. */
- return TXRX_DROP;
- }
-
- if (!rx->local->apdev)
- return TXRX_DROP;
-
- ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
- ieee80211_msg_sta_not_assoc);
- return TXRX_QUEUED;
- }
-
- if (rx->sdata->type == IEEE80211_IF_TYPE_STA)
- always_sta_key = 0;
- else
- always_sta_key = 1;
-
- if (rx->sta && rx->sta->key && always_sta_key) {
- rx->key = rx->sta->key;
- } else {
- if (rx->sta && rx->sta->key)
- rx->key = rx->sta->key;
- else
- rx->key = rx->sdata->default_key;
-
- if ((rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) &&
- rx->fc & IEEE80211_FCTL_PROTECTED) {
- int keyidx = ieee80211_wep_get_keyidx(rx->skb);
-
- if (keyidx >= 0 && keyidx < NUM_DEFAULT_KEYS &&
- (!rx->sta || !rx->sta->key || keyidx > 0))
- rx->key = rx->sdata->keys[keyidx];
-
- if (!rx->key) {
- if (!rx->u.rx.ra_match)
- return TXRX_DROP;
- printk(KERN_DEBUG "%s: RX WEP frame with "
- "unknown keyidx %d (A1=" MAC_FMT " A2="
- MAC_FMT " A3=" MAC_FMT ")\n",
- rx->dev->name, keyidx,
- MAC_ARG(hdr->addr1),
- MAC_ARG(hdr->addr2),
- MAC_ARG(hdr->addr3));
- if (!rx->local->apdev)
- return TXRX_DROP;
- ieee80211_rx_mgmt(
- rx->local, rx->skb, rx->u.rx.status,
- ieee80211_msg_wep_frame_unknown_key);
- return TXRX_QUEUED;
- }
- }
- }
-
- if (rx->fc & IEEE80211_FCTL_PROTECTED && rx->key && rx->u.rx.ra_match) {
- rx->key->tx_rx_count++;
- if (unlikely(rx->local->key_tx_rx_threshold &&
- rx->key->tx_rx_count >
- rx->local->key_tx_rx_threshold)) {
- ieee80211_key_threshold_notify(rx->dev, rx->key,
- rx->sta);
- }
- }
-
- return TXRX_CONTINUE;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_rx_h_sta_process(struct ieee80211_txrx_data *rx)
-{
- struct sta_info *sta = rx->sta;
- struct net_device *dev = rx->dev;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
-
- if (!sta)
- return TXRX_CONTINUE;
-
- /* Update last_rx only for IBSS packets which are for the current
- * BSSID to avoid keeping the current IBSS network alive in cases where
- * other STAs are using different BSSID. */
- if (rx->sdata->type == IEEE80211_IF_TYPE_IBSS) {
- u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len);
- if (compare_ether_addr(bssid, rx->sdata->u.sta.bssid) == 0)
- sta->last_rx = jiffies;
- } else
- if (!is_multicast_ether_addr(hdr->addr1) ||
- rx->sdata->type == IEEE80211_IF_TYPE_STA) {
- /* Update last_rx only for unicast frames in order to prevent
- * the Probe Request frames (the only broadcast frames from a
- * STA in infrastructure mode) from keeping a connection alive.
- */
- sta->last_rx = jiffies;
- }
-
- if (!rx->u.rx.ra_match)
- return TXRX_CONTINUE;
-
- sta->rx_fragments++;
- sta->rx_bytes += rx->skb->len;
- sta->last_rssi = (sta->last_rssi * 15 +
- rx->u.rx.status->ssi) / 16;
- sta->last_signal = (sta->last_signal * 15 +
- rx->u.rx.status->signal) / 16;
- sta->last_noise = (sta->last_noise * 15 +
- rx->u.rx.status->noise) / 16;
-
- if (!(rx->fc & IEEE80211_FCTL_MOREFRAGS)) {
- /* Change STA power saving mode only in the end of a frame
- * exchange sequence */
- if ((sta->flags & WLAN_STA_PS) && !(rx->fc & IEEE80211_FCTL_PM))
- rx->u.rx.sent_ps_buffered += ap_sta_ps_end(dev, sta);
- else if (!(sta->flags & WLAN_STA_PS) &&
- (rx->fc & IEEE80211_FCTL_PM))
- ap_sta_ps_start(dev, sta);
- }
-
- /* Drop data::nullfunc frames silently, since they are used only to
- * control station power saving mode. */
- if ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
- (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_NULLFUNC) {
- I802_DEBUG_INC(rx->local->rx_handlers_drop_nullfunc);
- /* Update counter and free packet here to avoid counting this
- * as a dropped packed. */
- sta->rx_packets++;
- dev_kfree_skb(rx->skb);
- return TXRX_QUEUED;
- }
-
- return TXRX_CONTINUE;
-} /* ieee80211_rx_h_sta_process */
-
-
-static ieee80211_txrx_result
-ieee80211_rx_h_wep_weak_iv_detection(struct ieee80211_txrx_data *rx)
-{
- if (!rx->sta || !(rx->fc & IEEE80211_FCTL_PROTECTED) ||
- (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
- !rx->key || rx->key->alg != ALG_WEP || !rx->u.rx.ra_match)
- return TXRX_CONTINUE;
-
- /* Check for weak IVs, if hwaccel did not remove IV from the frame */
- if ((rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) ||
- rx->key->force_sw_encrypt) {
- u8 *iv = ieee80211_wep_is_weak_iv(rx->skb, rx->key);
- if (iv) {
- rx->sta->wep_weak_iv_count++;
- }
- }
-
- return TXRX_CONTINUE;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_rx_h_wep_decrypt(struct ieee80211_txrx_data *rx)
-{
- /* If the device handles decryption totally, skip this test */
- if (rx->local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP)
- return TXRX_CONTINUE;
-
- if ((rx->key && rx->key->alg != ALG_WEP) ||
- !(rx->fc & IEEE80211_FCTL_PROTECTED) ||
- ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
- ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
- (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH)))
- return TXRX_CONTINUE;
-
- if (!rx->key) {
- printk(KERN_DEBUG "%s: RX WEP frame, but no key set\n",
- rx->dev->name);
- return TXRX_DROP;
- }
-
- if (!(rx->u.rx.status->flag & RX_FLAG_DECRYPTED) ||
- rx->key->force_sw_encrypt) {
- if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key)) {
- printk(KERN_DEBUG "%s: RX WEP frame, decrypt "
- "failed\n", rx->dev->name);
- return TXRX_DROP;
- }
- } else if (rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) {
- ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
- /* remove ICV */
- skb_trim(rx->skb, rx->skb->len - 4);
- }
-
- return TXRX_CONTINUE;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_rx_h_802_1x_pae(struct ieee80211_txrx_data *rx)
-{
- if (rx->sdata->eapol && ieee80211_is_eapol(rx->skb) &&
- rx->sdata->type != IEEE80211_IF_TYPE_STA && rx->u.rx.ra_match) {
- /* Pass both encrypted and unencrypted EAPOL frames to user
- * space for processing. */
- if (!rx->local->apdev)
- return TXRX_DROP;
- ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
- ieee80211_msg_normal);
- return TXRX_QUEUED;
- }
-
- if (unlikely(rx->sdata->ieee802_1x &&
- (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
- (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_NULLFUNC &&
- (!rx->sta || !(rx->sta->flags & WLAN_STA_AUTHORIZED)) &&
- !ieee80211_is_eapol(rx->skb))) {
-#ifdef CONFIG_MAC80211_DEBUG
- struct ieee80211_hdr *hdr =
- (struct ieee80211_hdr *) rx->skb->data;
- printk(KERN_DEBUG "%s: dropped frame from " MAC_FMT
- " (unauthorized port)\n", rx->dev->name,
- MAC_ARG(hdr->addr2));
-#endif /* CONFIG_MAC80211_DEBUG */
- return TXRX_DROP;
- }
-
- return TXRX_CONTINUE;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_rx_h_drop_unencrypted(struct ieee80211_txrx_data *rx)
-{
- /* If the device handles decryption totally, skip this test */
- if (rx->local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP)
- return TXRX_CONTINUE;
-
- /* Drop unencrypted frames if key is set. */
- if (unlikely(!(rx->fc & IEEE80211_FCTL_PROTECTED) &&
- (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
- (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_NULLFUNC &&
- (rx->key || rx->sdata->drop_unencrypted) &&
- (rx->sdata->eapol == 0 ||
- !ieee80211_is_eapol(rx->skb)))) {
- printk(KERN_DEBUG "%s: RX non-WEP frame, but expected "
- "encryption\n", rx->dev->name);
- return TXRX_DROP;
- }
- return TXRX_CONTINUE;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx)
-{
- struct ieee80211_sub_if_data *sdata;
-
- if (!rx->u.rx.ra_match)
- return TXRX_DROP;
-
- sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
- if ((sdata->type == IEEE80211_IF_TYPE_STA ||
- sdata->type == IEEE80211_IF_TYPE_IBSS) &&
- !rx->local->user_space_mlme) {
- ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status);
- } else {
- /* Management frames are sent to hostapd for processing */
- if (!rx->local->apdev)
- return TXRX_DROP;
- ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
- ieee80211_msg_normal);
- }
- return TXRX_QUEUED;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx)
-{
- struct ieee80211_local *local = rx->local;
- struct sk_buff *skb = rx->skb;
-
- if (unlikely(local->sta_scanning != 0)) {
- ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status);
- return TXRX_QUEUED;
- }
-
- if (unlikely(rx->u.rx.in_scan)) {
- /* scanning finished during invoking of handlers */
- I802_DEBUG_INC(local->rx_handlers_drop_passive_scan);
- return TXRX_DROP;
- }
-
- return TXRX_CONTINUE;
-}
-
-
-static void ieee80211_rx_michael_mic_report(struct net_device *dev,
- struct ieee80211_hdr *hdr,
- struct sta_info *sta,
- struct ieee80211_txrx_data *rx)
-{
- int keyidx, hdrlen;
-
- hdrlen = ieee80211_get_hdrlen_from_skb(rx->skb);
- if (rx->skb->len >= hdrlen + 4)
- keyidx = rx->skb->data[hdrlen + 3] >> 6;
- else
- keyidx = -1;
-
- /* TODO: verify that this is not triggered by fragmented
- * frames (hw does not verify MIC for them). */
- printk(KERN_DEBUG "%s: TKIP hwaccel reported Michael MIC "
- "failure from " MAC_FMT " to " MAC_FMT " keyidx=%d\n",
- dev->name, MAC_ARG(hdr->addr2), MAC_ARG(hdr->addr1), keyidx);
-
- if (!sta) {
- /* Some hardware versions seem to generate incorrect
- * Michael MIC reports; ignore them to avoid triggering
- * countermeasures. */
- printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
- "error for unknown address " MAC_FMT "\n",
- dev->name, MAC_ARG(hdr->addr2));
- goto ignore;
- }
-
- if (!(rx->fc & IEEE80211_FCTL_PROTECTED)) {
- printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
- "error for a frame with no ISWEP flag (src "
- MAC_FMT ")\n", dev->name, MAC_ARG(hdr->addr2));
- goto ignore;
- }
-
- if ((rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) &&
- rx->sdata->type == IEEE80211_IF_TYPE_AP) {
- keyidx = ieee80211_wep_get_keyidx(rx->skb);
- /* AP with Pairwise keys support should never receive Michael
- * MIC errors for non-zero keyidx because these are reserved
- * for group keys and only the AP is sending real multicast
- * frames in BSS. */
- if (keyidx) {
- printk(KERN_DEBUG "%s: ignored Michael MIC error for "
- "a frame with non-zero keyidx (%d) (src " MAC_FMT
- ")\n", dev->name, keyidx, MAC_ARG(hdr->addr2));
- goto ignore;
- }
- }
-
- if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
- ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
- (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH)) {
- printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
- "error for a frame that cannot be encrypted "
- "(fc=0x%04x) (src " MAC_FMT ")\n",
- dev->name, rx->fc, MAC_ARG(hdr->addr2));
- goto ignore;
- }
-
- do {
- union iwreq_data wrqu;
- char *buf = kmalloc(128, GFP_ATOMIC);
- if (!buf)
- break;
-
- /* TODO: needed parameters: count, key type, TSC */
- sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
- "keyid=%d %scast addr=" MAC_FMT ")",
- keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni",
- MAC_ARG(hdr->addr2));
- memset(&wrqu, 0, sizeof(wrqu));
- wrqu.data.length = strlen(buf);
- wireless_send_event(rx->dev, IWEVCUSTOM, &wrqu, buf);
- kfree(buf);
- } while (0);
-
- /* TODO: consider verifying the MIC error report with software
- * implementation if we get too many spurious reports from the
- * hardware. */
- if (!rx->local->apdev)
- goto ignore;
- ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
- ieee80211_msg_michael_mic_failure);
- return;
-
- ignore:
- dev_kfree_skb(rx->skb);
- rx->skb = NULL;
-}
-
-static inline ieee80211_txrx_result __ieee80211_invoke_rx_handlers(
- struct ieee80211_local *local,
- ieee80211_rx_handler *handlers,
- struct ieee80211_txrx_data *rx,
- struct sta_info *sta)
-{
- ieee80211_rx_handler *handler;
- ieee80211_txrx_result res = TXRX_DROP;
-
- for (handler = handlers; *handler != NULL; handler++) {
- res = (*handler)(rx);
- if (res != TXRX_CONTINUE) {
- if (res == TXRX_DROP) {
- I802_DEBUG_INC(local->rx_handlers_drop);
- if (sta)
- sta->rx_dropped++;
- }
- if (res == TXRX_QUEUED)
- I802_DEBUG_INC(local->rx_handlers_queued);
- break;
- }
- }
-
- if (res == TXRX_DROP) {
- dev_kfree_skb(rx->skb);
- }
- return res;
-}
-
-static inline void ieee80211_invoke_rx_handlers(struct ieee80211_local *local,
- ieee80211_rx_handler *handlers,
- struct ieee80211_txrx_data *rx,
- struct sta_info *sta)
-{
- if (__ieee80211_invoke_rx_handlers(local, handlers, rx, sta) ==
- TXRX_CONTINUE)
- dev_kfree_skb(rx->skb);
-}
-
-/*
- * This is the receive path handler. It is called by a low level driver when an
- * 802.11 MPDU is received from the hardware.
- */
-void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
- struct ieee80211_rx_status *status)
-{
- struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_sub_if_data *sdata;
- struct sta_info *sta;
- struct ieee80211_hdr *hdr;
- struct ieee80211_txrx_data rx;
- u16 type;
- int multicast;
- int radiotap_len = 0;
-
- if (status->flag & RX_FLAG_RADIOTAP) {
- radiotap_len = ieee80211_get_radiotap_len(skb);
- skb_pull(skb, radiotap_len);
- }
-
- hdr = (struct ieee80211_hdr *) skb->data;
- memset(&rx, 0, sizeof(rx));
- rx.skb = skb;
- rx.local = local;
-
- rx.u.rx.status = status;
- rx.fc = skb->len >= 2 ? le16_to_cpu(hdr->frame_control) : 0;
- type = rx.fc & IEEE80211_FCTL_FTYPE;
- if (type == IEEE80211_FTYPE_DATA || type == IEEE80211_FTYPE_MGMT)
- local->dot11ReceivedFragmentCount++;
- multicast = is_multicast_ether_addr(hdr->addr1);
-
- if (skb->len >= 16)
- sta = rx.sta = sta_info_get(local, hdr->addr2);
- else
- sta = rx.sta = NULL;
-
- if (sta) {
- rx.dev = sta->dev;
- rx.sdata = IEEE80211_DEV_TO_SUB_IF(rx.dev);
- }
-
- if ((status->flag & RX_FLAG_MMIC_ERROR)) {
- ieee80211_rx_michael_mic_report(local->mdev, hdr, sta, &rx);
- goto end;
- }
-
- if (unlikely(local->sta_scanning))
- rx.u.rx.in_scan = 1;
-
- if (__ieee80211_invoke_rx_handlers(local, local->rx_pre_handlers, &rx,
- sta) != TXRX_CONTINUE)
- goto end;
- skb = rx.skb;
-
- skb_push(skb, radiotap_len);
- if (sta && !sta->assoc_ap && !(sta->flags & WLAN_STA_WDS) &&
- !local->iff_promiscs && !multicast) {
- rx.u.rx.ra_match = 1;
- ieee80211_invoke_rx_handlers(local, local->rx_handlers, &rx,
- sta);
- } else {
- struct ieee80211_sub_if_data *prev = NULL;
- struct sk_buff *skb_new;
- u8 *bssid = ieee80211_get_bssid(hdr, skb->len - radiotap_len);
-
- read_lock(&local->sub_if_lock);
- list_for_each_entry(sdata, &local->sub_if_list, list) {
- rx.u.rx.ra_match = 1;
- switch (sdata->type) {
- case IEEE80211_IF_TYPE_STA:
- if (!bssid)
- continue;
- if (!ieee80211_bssid_match(bssid,
- sdata->u.sta.bssid)) {
- if (!rx.u.rx.in_scan)
- continue;
- rx.u.rx.ra_match = 0;
- } else if (!multicast &&
- compare_ether_addr(sdata->dev->dev_addr,
- hdr->addr1) != 0) {
- if (!sdata->promisc)
- continue;
- rx.u.rx.ra_match = 0;
- }
- break;
- case IEEE80211_IF_TYPE_IBSS:
- if (!bssid)
- continue;
- if (!ieee80211_bssid_match(bssid,
- sdata->u.sta.bssid)) {
- if (!rx.u.rx.in_scan)
- continue;
- rx.u.rx.ra_match = 0;
- } else if (!multicast &&
- compare_ether_addr(sdata->dev->dev_addr,
- hdr->addr1) != 0) {
- if (!sdata->promisc)
- continue;
- rx.u.rx.ra_match = 0;
- } else if (!sta)
- sta = rx.sta =
- ieee80211_ibss_add_sta(sdata->dev,
- skb, bssid,
- hdr->addr2);
- break;
- case IEEE80211_IF_TYPE_AP:
- if (!bssid) {
- if (compare_ether_addr(sdata->dev->dev_addr,
- hdr->addr1) != 0)
- continue;
- } else if (!ieee80211_bssid_match(bssid,
- sdata->dev->dev_addr)) {
- if (!rx.u.rx.in_scan)
- continue;
- rx.u.rx.ra_match = 0;
- }
- if (sdata->dev == local->mdev &&
- !rx.u.rx.in_scan)
- /* do not receive anything via
- * master device when not scanning */
- continue;
- break;
- case IEEE80211_IF_TYPE_WDS:
- if (bssid ||
- (rx.fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)
- continue;
- if (compare_ether_addr(sdata->u.wds.remote_addr,
- hdr->addr2) != 0)
- continue;
- break;
- }
-
- if (prev) {
- skb_new = skb_copy(skb, GFP_ATOMIC);
- if (!skb_new) {
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: failed to copy "
- "multicast frame for %s",
- local->mdev->name, prev->dev->name);
- continue;
- }
- rx.skb = skb_new;
- rx.dev = prev->dev;
- rx.sdata = prev;
- ieee80211_invoke_rx_handlers(local,
- local->rx_handlers,
- &rx, sta);
- }
- prev = sdata;
- }
- if (prev) {
- rx.skb = skb;
- rx.dev = prev->dev;
- rx.sdata = prev;
- ieee80211_invoke_rx_handlers(local, local->rx_handlers,
- &rx, sta);
- } else
- dev_kfree_skb(skb);
- read_unlock(&local->sub_if_lock);
- }
-
- end:
- if (sta)
- sta_info_put(sta);
-}
-EXPORT_SYMBOL(__ieee80211_rx);
-
-static ieee80211_txrx_result
-ieee80211_tx_h_load_stats(struct ieee80211_txrx_data *tx)
-{
- struct ieee80211_local *local = tx->local;
- struct ieee80211_hw_mode *mode = tx->u.tx.mode;
- struct sk_buff *skb = tx->skb;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- u32 load = 0, hdrtime;
-
- /* TODO: this could be part of tx_status handling, so that the number
- * of retries would be known; TX rate should in that case be stored
- * somewhere with the packet */
-
- /* Estimate total channel use caused by this frame */
-
- /* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values,
- * 1 usec = 1/8 * (1080 / 10) = 13.5 */
-
- if (mode->mode == MODE_IEEE80211A ||
- mode->mode == MODE_ATHEROS_TURBO ||
- mode->mode == MODE_ATHEROS_TURBOG ||
- (mode->mode == MODE_IEEE80211G &&
- tx->u.tx.rate->flags & IEEE80211_RATE_ERP))
- hdrtime = CHAN_UTIL_HDR_SHORT;
- else
- hdrtime = CHAN_UTIL_HDR_LONG;
-
- load = hdrtime;
- if (!is_multicast_ether_addr(hdr->addr1))
- load += hdrtime;
-
- if (tx->u.tx.control->flags & IEEE80211_TXCTL_USE_RTS_CTS)
- load += 2 * hdrtime;
- else if (tx->u.tx.control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)
- load += hdrtime;
-
- load += skb->len * tx->u.tx.rate->rate_inv;
-
- if (tx->u.tx.extra_frag) {
- int i;
- for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
- load += 2 * hdrtime;
- load += tx->u.tx.extra_frag[i]->len *
- tx->u.tx.rate->rate;
- }
- }
-
- /* Divide channel_use by 8 to avoid wrapping around the counter */
- load >>= CHAN_UTIL_SHIFT;
- local->channel_use_raw += load;
- if (tx->sta)
- tx->sta->channel_use_raw += load;
- tx->sdata->channel_use_raw += load;
-
- return TXRX_CONTINUE;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_rx_h_load_stats(struct ieee80211_txrx_data *rx)
-{
- struct ieee80211_local *local = rx->local;
- struct sk_buff *skb = rx->skb;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- u32 load = 0, hdrtime;
- struct ieee80211_rate *rate;
- struct ieee80211_hw_mode *mode = local->hw.conf.mode;
- int i;
-
- /* Estimate total channel use caused by this frame */
-
- if (unlikely(mode->num_rates < 0))
- return TXRX_CONTINUE;
-
- rate = &mode->rates[0];
- for (i = 0; i < mode->num_rates; i++) {
- if (mode->rates[i].val == rx->u.rx.status->rate) {
- rate = &mode->rates[i];
- break;
- }
- }
-
- /* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values,
- * 1 usec = 1/8 * (1080 / 10) = 13.5 */
-
- if (mode->mode == MODE_IEEE80211A ||
- mode->mode == MODE_ATHEROS_TURBO ||
- mode->mode == MODE_ATHEROS_TURBOG ||
- (mode->mode == MODE_IEEE80211G &&
- rate->flags & IEEE80211_RATE_ERP))
- hdrtime = CHAN_UTIL_HDR_SHORT;
- else
- hdrtime = CHAN_UTIL_HDR_LONG;
-
- load = hdrtime;
- if (!is_multicast_ether_addr(hdr->addr1))
- load += hdrtime;
-
- load += skb->len * rate->rate_inv;
-
- /* Divide channel_use by 8 to avoid wrapping around the counter */
- load >>= CHAN_UTIL_SHIFT;
- local->channel_use_raw += load;
- if (rx->sta)
- rx->sta->channel_use_raw += load;
- rx->u.rx.load = load;
-
- return TXRX_CONTINUE;
-}
-
-static ieee80211_txrx_result
-ieee80211_rx_h_if_stats(struct ieee80211_txrx_data *rx)
-{
- rx->sdata->channel_use_raw += rx->u.rx.load;
- return TXRX_CONTINUE;
-}
-
-static void ieee80211_stat_refresh(unsigned long data)
-{
- struct ieee80211_local *local = (struct ieee80211_local *) data;
- struct sta_info *sta;
- struct ieee80211_sub_if_data *sdata;
-
- if (!local->stat_time)
- return;
-
- /* go through all stations */
- spin_lock_bh(&local->sta_lock);
- list_for_each_entry(sta, &local->sta_list, list) {
- sta->channel_use = (sta->channel_use_raw / local->stat_time) /
- CHAN_UTIL_PER_10MS;
- sta->channel_use_raw = 0;
- }
- spin_unlock_bh(&local->sta_lock);
-
- /* go through all subinterfaces */
- read_lock(&local->sub_if_lock);
- list_for_each_entry(sdata, &local->sub_if_list, list) {
- sdata->channel_use = (sdata->channel_use_raw /
- local->stat_time) / CHAN_UTIL_PER_10MS;
- sdata->channel_use_raw = 0;
- }
- read_unlock(&local->sub_if_lock);
-
- /* hardware interface */
- local->channel_use = (local->channel_use_raw /
- local->stat_time) / CHAN_UTIL_PER_10MS;
- local->channel_use_raw = 0;
-
- local->stat_timer.expires = jiffies + HZ * local->stat_time / 100;
- add_timer(&local->stat_timer);
+ sdata->flags &= ~(IEEE80211_SDATA_USE_PROTECTION |
+ IEEE80211_SDATA_SHORT_PREAMBLE);
+ ieee80211_erp_info_change_notify(dev,
+ IEEE80211_ERP_CHANGE_PROTECTION |
+ IEEE80211_ERP_CHANGE_PREAMBLE);
}
-
-/* This is a version of the rx handler that can be called from hard irq
- * context. Post the skb on the queue and schedule the tasklet */
-void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb,
- struct ieee80211_rx_status *status)
-{
- struct ieee80211_local *local = hw_to_local(hw);
-
- BUILD_BUG_ON(sizeof(struct ieee80211_rx_status) > sizeof(skb->cb));
-
- skb->dev = local->mdev;
- /* copy status into skb->cb for use by tasklet */
- memcpy(skb->cb, status, sizeof(*status));
- skb->pkt_type = IEEE80211_RX_MSG;
- skb_queue_tail(&local->skb_queue, skb);
- tasklet_schedule(&local->tasklet);
-}
-EXPORT_SYMBOL(ieee80211_rx_irqsafe);
-
void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
struct sk_buff *skb,
struct ieee80211_tx_status *status)
@@ -4398,14 +624,13 @@ static void ieee80211_tasklet_handler(unsigned long data)
break;
default: /* should never get here! */
printk(KERN_ERR "%s: Unknown message type (%d)\n",
- local->mdev->name, skb->pkt_type);
+ wiphy_name(local->hw.wiphy), skb->pkt_type);
dev_kfree_skb(skb);
break;
}
}
}
-
/* Remove added headers (e.g., QoS control), encryption header/MIC, etc. to
* make a prepared TX frame (one that has been given to hw) to look like brand
* new IEEE 802.11 frame that is ready to go through TX processing again.
@@ -4420,10 +645,13 @@ static void ieee80211_remove_tx_extra(struct ieee80211_local *local,
pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
pkt_data->ifindex = control->ifindex;
- pkt_data->mgmt_iface = (control->type == IEEE80211_IF_TYPE_MGMT);
- pkt_data->req_tx_status = !!(control->flags & IEEE80211_TXCTL_REQ_TX_STATUS);
- pkt_data->do_not_encrypt = !!(control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT);
- pkt_data->requeue = !!(control->flags & IEEE80211_TXCTL_REQUEUE);
+ pkt_data->flags = 0;
+ if (control->flags & IEEE80211_TXCTL_REQ_TX_STATUS)
+ pkt_data->flags |= IEEE80211_TXPD_REQ_TX_STATUS;
+ if (control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT)
+ pkt_data->flags |= IEEE80211_TXPD_DO_NOT_ENCRYPT;
+ if (control->flags & IEEE80211_TXCTL_REQUEUE)
+ pkt_data->flags |= IEEE80211_TXPD_REQUEUE;
pkt_data->queue = control->queue;
hdrlen = ieee80211_get_hdrlen_from_skb(skb);
@@ -4431,7 +659,7 @@ static void ieee80211_remove_tx_extra(struct ieee80211_local *local,
if (!key)
goto no_key;
- switch (key->alg) {
+ switch (key->conf.alg) {
case ALG_WEP:
iv_len = WEP_IV_LEN;
mic_len = WEP_ICV_LEN;
@@ -4448,7 +676,8 @@ static void ieee80211_remove_tx_extra(struct ieee80211_local *local,
goto no_key;
}
- if (skb->len >= mic_len && key->force_sw_encrypt)
+ if (skb->len >= mic_len &&
+ !(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
skb_trim(skb, skb->len - mic_len);
if (skb->len >= iv_len && skb->len > hdrlen) {
memmove(skb->data + iv_len, skb->data, hdrlen);
@@ -4468,7 +697,6 @@ no_key:
}
}
-
void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
struct ieee80211_tx_status *status)
{
@@ -4476,7 +704,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_local *local = hw_to_local(hw);
u16 frag, type;
- u32 msg_type;
struct ieee80211_tx_status_rtap_hdr *rthdr;
struct ieee80211_sub_if_data *sdata;
int monitors;
@@ -4484,7 +711,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
if (!status) {
printk(KERN_ERR
"%s: ieee80211_tx_status called with NULL status\n",
- local->mdev->name);
+ wiphy_name(local->hw.wiphy));
dev_kfree_skb(skb);
return;
}
@@ -4541,7 +768,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
printk(KERN_DEBUG "%s: dropped TX "
"filtered frame queue_len=%d "
"PS=%d @%lu\n",
- local->mdev->name,
+ wiphy_name(local->hw.wiphy),
skb_queue_len(
&sta->tx_filtered),
!!(sta->flags & WLAN_STA_PS),
@@ -4591,29 +818,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
local->dot11FailedCount++;
}
- msg_type = (status->flags & IEEE80211_TX_STATUS_ACK) ?
- ieee80211_msg_tx_callback_ack : ieee80211_msg_tx_callback_fail;
-
/* this was a transmitted frame, but now we want to reuse it */
skb_orphan(skb);
- if ((status->control.flags & IEEE80211_TXCTL_REQ_TX_STATUS) &&
- local->apdev) {
- if (local->monitors) {
- skb2 = skb_clone(skb, GFP_ATOMIC);
- } else {
- skb2 = skb;
- skb = NULL;
- }
-
- if (skb2)
- /* Send frame to hostapd */
- ieee80211_rx_mgmt(local, skb2, NULL, msg_type);
-
- if (!skb)
- return;
- }
-
if (!local->monitors) {
dev_kfree_skb(skb);
return;
@@ -4648,9 +855,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
rthdr->data_retries = status->retry_count;
- read_lock(&local->sub_if_lock);
+ rcu_read_lock();
monitors = local->monitors;
- list_for_each_entry(sdata, &local->sub_if_list, list) {
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
/*
* Using the monitors counter is possibly racy, but
* if the value is wrong we simply either clone the skb
@@ -4666,7 +873,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
continue;
monitors--;
if (monitors)
- skb2 = skb_clone(skb, GFP_KERNEL);
+ skb2 = skb_clone(skb, GFP_ATOMIC);
else
skb2 = NULL;
skb->dev = sdata->dev;
@@ -4681,170 +888,12 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
}
}
out:
- read_unlock(&local->sub_if_lock);
+ rcu_read_unlock();
if (skb)
dev_kfree_skb(skb);
}
EXPORT_SYMBOL(ieee80211_tx_status);
-/* TODO: implement register/unregister functions for adding TX/RX handlers
- * into ordered list */
-
-/* rx_pre handlers don't have dev and sdata fields available in
- * ieee80211_txrx_data */
-static ieee80211_rx_handler ieee80211_rx_pre_handlers[] =
-{
- ieee80211_rx_h_parse_qos,
- ieee80211_rx_h_load_stats,
- NULL
-};
-
-static ieee80211_rx_handler ieee80211_rx_handlers[] =
-{
- ieee80211_rx_h_if_stats,
- ieee80211_rx_h_monitor,
- ieee80211_rx_h_passive_scan,
- ieee80211_rx_h_check,
- ieee80211_rx_h_sta_process,
- ieee80211_rx_h_ccmp_decrypt,
- ieee80211_rx_h_tkip_decrypt,
- ieee80211_rx_h_wep_weak_iv_detection,
- ieee80211_rx_h_wep_decrypt,
- ieee80211_rx_h_defragment,
- ieee80211_rx_h_ps_poll,
- ieee80211_rx_h_michael_mic_verify,
- /* this must be after decryption - so header is counted in MPDU mic
- * must be before pae and data, so QOS_DATA format frames
- * are not passed to user space by these functions
- */
- ieee80211_rx_h_remove_qos_control,
- ieee80211_rx_h_802_1x_pae,
- ieee80211_rx_h_drop_unencrypted,
- ieee80211_rx_h_data,
- ieee80211_rx_h_mgmt,
- NULL
-};
-
-static ieee80211_tx_handler ieee80211_tx_handlers[] =
-{
- ieee80211_tx_h_check_assoc,
- ieee80211_tx_h_sequence,
- ieee80211_tx_h_ps_buf,
- ieee80211_tx_h_select_key,
- ieee80211_tx_h_michael_mic_add,
- ieee80211_tx_h_fragment,
- ieee80211_tx_h_tkip_encrypt,
- ieee80211_tx_h_ccmp_encrypt,
- ieee80211_tx_h_wep_encrypt,
- ieee80211_tx_h_rate_ctrl,
- ieee80211_tx_h_misc,
- ieee80211_tx_h_load_stats,
- NULL
-};
-
-
-int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct sta_info *sta;
-
- if (compare_ether_addr(remote_addr, sdata->u.wds.remote_addr) == 0)
- return 0;
-
- /* Create STA entry for the new peer */
- sta = sta_info_add(local, dev, remote_addr, GFP_KERNEL);
- if (!sta)
- return -ENOMEM;
- sta_info_put(sta);
-
- /* Remove STA entry for the old peer */
- sta = sta_info_get(local, sdata->u.wds.remote_addr);
- if (sta) {
- sta_info_put(sta);
- sta_info_free(sta, 0);
- } else {
- printk(KERN_DEBUG "%s: could not find STA entry for WDS link "
- "peer " MAC_FMT "\n",
- dev->name, MAC_ARG(sdata->u.wds.remote_addr));
- }
-
- /* Update WDS link data */
- memcpy(&sdata->u.wds.remote_addr, remote_addr, ETH_ALEN);
-
- return 0;
-}
-
-/* Must not be called for mdev and apdev */
-void ieee80211_if_setup(struct net_device *dev)
-{
- ether_setup(dev);
- dev->hard_start_xmit = ieee80211_subif_start_xmit;
- dev->wireless_handlers = &ieee80211_iw_handler_def;
- dev->set_multicast_list = ieee80211_set_multicast_list;
- dev->change_mtu = ieee80211_change_mtu;
- dev->get_stats = ieee80211_get_stats;
- dev->open = ieee80211_open;
- dev->stop = ieee80211_stop;
- dev->uninit = ieee80211_if_reinit;
- dev->destructor = ieee80211_if_free;
-}
-
-void ieee80211_if_mgmt_setup(struct net_device *dev)
-{
- ether_setup(dev);
- dev->hard_start_xmit = ieee80211_mgmt_start_xmit;
- dev->change_mtu = ieee80211_change_mtu_apdev;
- dev->get_stats = ieee80211_get_stats;
- dev->open = ieee80211_mgmt_open;
- dev->stop = ieee80211_mgmt_stop;
- dev->type = ARPHRD_IEEE80211_PRISM;
- dev->hard_header_parse = header_parse_80211;
- dev->uninit = ieee80211_if_reinit;
- dev->destructor = ieee80211_if_free;
-}
-
-int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
- const char *name)
-{
- struct rate_control_ref *ref, *old;
-
- ASSERT_RTNL();
- if (local->open_count || netif_running(local->mdev) ||
- (local->apdev && netif_running(local->apdev)))
- return -EBUSY;
-
- ref = rate_control_alloc(name, local);
- if (!ref) {
- printk(KERN_WARNING "%s: Failed to select rate control "
- "algorithm\n", local->mdev->name);
- return -ENOENT;
- }
-
- old = local->rate_ctrl;
- local->rate_ctrl = ref;
- if (old) {
- rate_control_put(old);
- sta_info_flush(local, NULL);
- }
-
- printk(KERN_DEBUG "%s: Selected rate control "
- "algorithm '%s'\n", local->mdev->name,
- ref->ops->name);
-
-
- return 0;
-}
-
-static void rate_control_deinitialize(struct ieee80211_local *local)
-{
- struct rate_control_ref *ref;
-
- ref = local->rate_ctrl;
- local->rate_ctrl = NULL;
- rate_control_put(ref);
-}
-
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
const struct ieee80211_ops *ops)
{
@@ -4888,8 +937,12 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST);
BUG_ON(!ops->tx);
+ BUG_ON(!ops->start);
+ BUG_ON(!ops->stop);
BUG_ON(!ops->config);
BUG_ON(!ops->add_interface);
+ BUG_ON(!ops->remove_interface);
+ BUG_ON(!ops->configure_filter);
local->ops = ops;
/* for now, mdev needs sub_if_data :/ */
@@ -4919,17 +972,13 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
local->long_retry_limit = 4;
local->hw.conf.radio_enabled = 1;
- local->enabled_modes = (unsigned int) -1;
+ local->enabled_modes = ~0;
INIT_LIST_HEAD(&local->modes_list);
- rwlock_init(&local->sub_if_lock);
- INIT_LIST_HEAD(&local->sub_if_list);
+ INIT_LIST_HEAD(&local->interfaces);
INIT_DELAYED_WORK(&local->scan_work, ieee80211_sta_scan_work);
- init_timer(&local->stat_timer);
- local->stat_timer.function = ieee80211_stat_refresh;
- local->stat_timer.data = (unsigned long) local;
ieee80211_rx_bss_list_init(mdev);
sta_info_init(local);
@@ -4938,7 +987,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
mdev->open = ieee80211_master_open;
mdev->stop = ieee80211_master_stop;
mdev->type = ARPHRD_IEEE80211;
- mdev->hard_header_parse = header_parse_80211;
+ mdev->header_ops = &ieee80211_header_ops;
+ mdev->set_multicast_list = ieee80211_master_set_multicast_list;
sdata->type = IEEE80211_IF_TYPE_AP;
sdata->dev = mdev;
@@ -4946,7 +996,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
sdata->u.ap.force_unicast_rateidx = -1;
sdata->u.ap.max_ratectrl_rateidx = -1;
ieee80211_if_sdata_init(sdata);
- list_add_tail(&sdata->list, &local->sub_if_list);
+ /* no RCU needed since we're still during init phase */
+ list_add_tail(&sdata->list, &local->interfaces);
tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
(unsigned long)local);
@@ -5019,11 +1070,12 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
goto fail_dev;
ieee80211_debugfs_add_netdev(IEEE80211_DEV_TO_SUB_IF(local->mdev));
+ ieee80211_if_set_type(local->mdev, IEEE80211_IF_TYPE_AP);
result = ieee80211_init_rate_ctrl_alg(local, NULL);
if (result < 0) {
printk(KERN_DEBUG "%s: Failed to initialize rate control "
- "algorithm\n", local->mdev->name);
+ "algorithm\n", wiphy_name(local->hw.wiphy));
goto fail_rate;
}
@@ -5031,7 +1083,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (result < 0) {
printk(KERN_DEBUG "%s: Failed to initialize wep\n",
- local->mdev->name);
+ wiphy_name(local->hw.wiphy));
goto fail_wep;
}
@@ -5042,7 +1094,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
IEEE80211_IF_TYPE_STA);
if (result)
printk(KERN_WARNING "%s: Failed to add default virtual iface\n",
- local->mdev->name);
+ wiphy_name(local->hw.wiphy));
local->reg_state = IEEE80211_DEV_REGISTERED;
rtnl_unlock();
@@ -5105,7 +1157,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata, *tmp;
- struct list_head tmp_list;
int i;
tasklet_kill(&local->tx_pending_tasklet);
@@ -5116,20 +1167,30 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
BUG_ON(local->reg_state != IEEE80211_DEV_REGISTERED);
local->reg_state = IEEE80211_DEV_UNREGISTERED;
- if (local->apdev)
- ieee80211_if_del_mgmt(local);
- write_lock_bh(&local->sub_if_lock);
- list_replace_init(&local->sub_if_list, &tmp_list);
- write_unlock_bh(&local->sub_if_lock);
+ /*
+ * At this point, interface list manipulations are fine
+ * because the driver cannot be handing us frames any
+ * more and the tasklet is killed.
+ */
- list_for_each_entry_safe(sdata, tmp, &tmp_list, list)
+ /*
+ * First, we remove all non-master interfaces. Do this because they
+ * may have bss pointer dependency on the master, and when we free
+ * the master these would be freed as well, breaking our list
+ * iteration completely.
+ */
+ list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
+ if (sdata->dev == local->mdev)
+ continue;
+ list_del(&sdata->list);
__ieee80211_if_del(local, sdata);
+ }
- rtnl_unlock();
+ /* then, finally, remove the master interface */
+ __ieee80211_if_del(local, IEEE80211_DEV_TO_SUB_IF(local->mdev));
- if (local->stat_time)
- del_timer_sync(&local->stat_timer);
+ rtnl_unlock();
ieee80211_rx_bss_list_deinit(local->mdev);
ieee80211_clear_tx_pending(local);
@@ -5145,7 +1206,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
if (skb_queue_len(&local->skb_queue)
|| skb_queue_len(&local->skb_queue_unreliable))
printk(KERN_WARNING "%s: skb_queue not empty\n",
- local->mdev->name);
+ wiphy_name(local->hw.wiphy));
skb_queue_purge(&local->skb_queue);
skb_queue_purge(&local->skb_queue_unreliable);
@@ -5165,72 +1226,6 @@ void ieee80211_free_hw(struct ieee80211_hw *hw)
}
EXPORT_SYMBOL(ieee80211_free_hw);
-void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
-{
- struct ieee80211_local *local = hw_to_local(hw);
-
- if (test_and_clear_bit(IEEE80211_LINK_STATE_XOFF,
- &local->state[queue])) {
- if (test_bit(IEEE80211_LINK_STATE_PENDING,
- &local->state[queue]))
- tasklet_schedule(&local->tx_pending_tasklet);
- else
- if (!ieee80211_qdisc_installed(local->mdev)) {
- if (queue == 0)
- netif_wake_queue(local->mdev);
- } else
- __netif_schedule(local->mdev);
- }
-}
-EXPORT_SYMBOL(ieee80211_wake_queue);
-
-void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
-{
- struct ieee80211_local *local = hw_to_local(hw);
-
- if (!ieee80211_qdisc_installed(local->mdev) && queue == 0)
- netif_stop_queue(local->mdev);
- set_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
-}
-EXPORT_SYMBOL(ieee80211_stop_queue);
-
-void ieee80211_start_queues(struct ieee80211_hw *hw)
-{
- struct ieee80211_local *local = hw_to_local(hw);
- int i;
-
- for (i = 0; i < local->hw.queues; i++)
- clear_bit(IEEE80211_LINK_STATE_XOFF, &local->state[i]);
- if (!ieee80211_qdisc_installed(local->mdev))
- netif_start_queue(local->mdev);
-}
-EXPORT_SYMBOL(ieee80211_start_queues);
-
-void ieee80211_stop_queues(struct ieee80211_hw *hw)
-{
- int i;
-
- for (i = 0; i < hw->queues; i++)
- ieee80211_stop_queue(hw, i);
-}
-EXPORT_SYMBOL(ieee80211_stop_queues);
-
-void ieee80211_wake_queues(struct ieee80211_hw *hw)
-{
- int i;
-
- for (i = 0; i < hw->queues; i++)
- ieee80211_wake_queue(hw, i);
-}
-EXPORT_SYMBOL(ieee80211_wake_queues);
-
-struct net_device_stats *ieee80211_dev_stats(struct net_device *dev)
-{
- struct ieee80211_sub_if_data *sdata;
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- return &sdata->stats;
-}
-
static int __init ieee80211_init(void)
{
struct sk_buff *skb;
@@ -5251,7 +1246,6 @@ static int __init ieee80211_init(void)
return 0;
}
-
static void __exit ieee80211_exit(void)
{
ieee80211_wme_unregister();
diff --git a/net/mac80211/ieee80211_cfg.c b/net/mac80211/ieee80211_cfg.c
deleted file mode 100644
index 509096edb324..000000000000
--- a/net/mac80211/ieee80211_cfg.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * mac80211 configuration hooks for cfg80211
- *
- * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * This file is GPLv2 as found in COPYING.
- */
-
-#include <linux/nl80211.h>
-#include <linux/rtnetlink.h>
-#include <net/cfg80211.h>
-#include "ieee80211_i.h"
-#include "ieee80211_cfg.h"
-
-static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
- unsigned int type)
-{
- struct ieee80211_local *local = wiphy_priv(wiphy);
- int itype;
-
- if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED))
- return -ENODEV;
-
- switch (type) {
- case NL80211_IFTYPE_UNSPECIFIED:
- itype = IEEE80211_IF_TYPE_STA;
- break;
- case NL80211_IFTYPE_ADHOC:
- itype = IEEE80211_IF_TYPE_IBSS;
- break;
- case NL80211_IFTYPE_STATION:
- itype = IEEE80211_IF_TYPE_STA;
- break;
- case NL80211_IFTYPE_MONITOR:
- itype = IEEE80211_IF_TYPE_MNTR;
- break;
- default:
- return -EINVAL;
- }
-
- return ieee80211_if_add(local->mdev, name, NULL, itype);
-}
-
-static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex)
-{
- struct ieee80211_local *local = wiphy_priv(wiphy);
- struct net_device *dev;
- char *name;
-
- if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED))
- return -ENODEV;
-
- dev = dev_get_by_index(ifindex);
- if (!dev)
- return 0;
-
- name = dev->name;
- dev_put(dev);
-
- return ieee80211_if_remove(local->mdev, name, -1);
-}
-
-struct cfg80211_ops mac80211_config_ops = {
- .add_virtual_intf = ieee80211_add_iface,
- .del_virtual_intf = ieee80211_del_iface,
-};
diff --git a/net/mac80211/ieee80211_common.h b/net/mac80211/ieee80211_common.h
index 77c6afb7f6a8..c15295d43d87 100644
--- a/net/mac80211/ieee80211_common.h
+++ b/net/mac80211/ieee80211_common.h
@@ -48,13 +48,13 @@ enum ieee80211_msg_type {
ieee80211_msg_tx_callback_ack = 1,
ieee80211_msg_tx_callback_fail = 2,
/* hole at 3, was ieee80211_msg_passive_scan but unused */
- ieee80211_msg_wep_frame_unknown_key = 4,
+ /* hole at 4, was ieee80211_msg_wep_frame_unknown_key but now unused */
ieee80211_msg_michael_mic_failure = 5,
/* hole at 6, was monitor but never sent to userspace */
ieee80211_msg_sta_not_assoc = 7,
/* 8 was ieee80211_msg_set_aid_for_sta */
- ieee80211_msg_key_threshold_notification = 9,
- ieee80211_msg_radar = 11,
+ /* 9 was ieee80211_msg_key_threshold_notification */
+ /* 11 was ieee80211_msg_radar */
};
struct ieee80211_msg_key_notification {
@@ -73,8 +73,6 @@ enum ieee80211_phytype {
ieee80211_phytype_ofdm_dot11_g = 6,
ieee80211_phytype_pbcc_dot11_g = 7,
ieee80211_phytype_ofdm_dot11_a = 8,
- ieee80211_phytype_dsss_dot11_turbog = 255,
- ieee80211_phytype_dsss_dot11_turbo = 256,
};
enum ieee80211_ssi_type {
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 6f7bae7ef9c0..d34a9deca67a 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -21,6 +21,7 @@
#include <linux/workqueue.h>
#include <linux/types.h>
#include <linux/spinlock.h>
+#include <linux/etherdevice.h>
#include <net/wireless.h>
#include "ieee80211_key.h"
#include "sta_info.h"
@@ -112,6 +113,16 @@ typedef enum {
TXRX_CONTINUE, TXRX_DROP, TXRX_QUEUED
} ieee80211_txrx_result;
+/* flags used in struct ieee80211_txrx_data.flags */
+/* whether the MSDU was fragmented */
+#define IEEE80211_TXRXD_FRAGMENTED BIT(0)
+#define IEEE80211_TXRXD_TXUNICAST BIT(1)
+#define IEEE80211_TXRXD_TXPS_BUFFERED BIT(2)
+#define IEEE80211_TXRXD_TXPROBE_LAST_FRAG BIT(3)
+#define IEEE80211_TXRXD_RXIN_SCAN BIT(4)
+/* frame is destined to interface currently processed (incl. multicast frames) */
+#define IEEE80211_TXRXD_RXRA_MATCH BIT(5)
+#define IEEE80211_TXRXD_TX_INJECTED BIT(6)
struct ieee80211_txrx_data {
struct sk_buff *skb;
struct net_device *dev;
@@ -120,14 +131,10 @@ struct ieee80211_txrx_data {
struct sta_info *sta;
u16 fc, ethertype;
struct ieee80211_key *key;
- unsigned int fragmented:1; /* whether the MSDU was fragmented */
+ unsigned int flags;
union {
struct {
struct ieee80211_tx_control *control;
- unsigned int unicast:1;
- unsigned int ps_buffered:1;
- unsigned int short_preamble:1;
- unsigned int probe_last_frag:1;
struct ieee80211_hw_mode *mode;
struct ieee80211_rate *rate;
/* use this rate (if set) for last fragment; rate can
@@ -135,7 +142,6 @@ struct ieee80211_txrx_data {
* when using CTS protection with IEEE 802.11g. */
struct ieee80211_rate *last_frag_rate;
int last_frag_hwrate;
- int mgmt_interface;
/* Extra fragments (in addition to the first fragment
* in skb) */
@@ -147,23 +153,22 @@ struct ieee80211_txrx_data {
int sent_ps_buffered;
int queue;
int load;
- unsigned int in_scan:1;
- /* frame is destined to interface currently processed
- * (including multicast frames) */
- unsigned int ra_match:1;
+ u32 tkip_iv32;
+ u16 tkip_iv16;
} rx;
} u;
};
+/* flags used in struct ieee80211_tx_packet_data.flags */
+#define IEEE80211_TXPD_REQ_TX_STATUS BIT(0)
+#define IEEE80211_TXPD_DO_NOT_ENCRYPT BIT(1)
+#define IEEE80211_TXPD_REQUEUE BIT(2)
/* Stored in sk_buff->cb */
struct ieee80211_tx_packet_data {
int ifindex;
unsigned long jiffies;
- unsigned int req_tx_status:1;
- unsigned int do_not_encrypt:1;
- unsigned int requeue:1;
- unsigned int mgmt_iface:1;
- unsigned int queue:4;
+ unsigned int flags;
+ u8 queue;
};
struct ieee80211_tx_stored_packet {
@@ -174,7 +179,7 @@ struct ieee80211_tx_stored_packet {
int last_frag_rateidx;
int last_frag_hwrate;
struct ieee80211_rate *last_frag_rate;
- unsigned int last_frag_rate_ctrl_probe:1;
+ unsigned int last_frag_rate_ctrl_probe;
};
typedef ieee80211_txrx_result (*ieee80211_tx_handler)
@@ -187,10 +192,10 @@ struct ieee80211_if_ap {
u8 *beacon_head, *beacon_tail;
int beacon_head_len, beacon_tail_len;
+ struct list_head vlans;
+
u8 ssid[IEEE80211_MAX_SSID_LEN];
size_t ssid_len;
- u8 *generic_elem;
- size_t generic_elem_len;
/* yes, this looks ugly, but guarantees that we can later use
* bitmap_empty :)
@@ -210,9 +215,23 @@ struct ieee80211_if_wds {
};
struct ieee80211_if_vlan {
- u8 id;
+ struct ieee80211_sub_if_data *ap;
+ struct list_head list;
};
+/* flags used in struct ieee80211_if_sta.flags */
+#define IEEE80211_STA_SSID_SET BIT(0)
+#define IEEE80211_STA_BSSID_SET BIT(1)
+#define IEEE80211_STA_PREV_BSSID_SET BIT(2)
+#define IEEE80211_STA_AUTHENTICATED BIT(3)
+#define IEEE80211_STA_ASSOCIATED BIT(4)
+#define IEEE80211_STA_PROBEREQ_POLL BIT(5)
+#define IEEE80211_STA_CREATE_IBSS BIT(6)
+#define IEEE80211_STA_MIXED_CELL BIT(7)
+#define IEEE80211_STA_WMM_ENABLED BIT(8)
+#define IEEE80211_STA_AUTO_SSID_SEL BIT(10)
+#define IEEE80211_STA_AUTO_BSSID_SEL BIT(11)
+#define IEEE80211_STA_AUTO_CHANNEL_SEL BIT(12)
struct ieee80211_if_sta {
enum {
IEEE80211_DISABLED, IEEE80211_AUTHENTICATE,
@@ -235,25 +254,14 @@ struct ieee80211_if_sta {
int auth_tries, assoc_tries;
- unsigned int ssid_set:1;
- unsigned int bssid_set:1;
- unsigned int prev_bssid_set:1;
- unsigned int authenticated:1;
- unsigned int associated:1;
- unsigned int probereq_poll:1;
- unsigned int create_ibss:1;
- unsigned int mixed_cell:1;
- unsigned int wmm_enabled:1;
- unsigned int auto_ssid_sel:1;
- unsigned int auto_bssid_sel:1;
- unsigned int auto_channel_sel:1;
+ unsigned int flags;
#define IEEE80211_STA_REQ_SCAN 0
#define IEEE80211_STA_REQ_AUTH 1
#define IEEE80211_STA_REQ_RUN 2
unsigned long request;
struct sk_buff_head skb_queue;
- int key_mgmt;
+ int key_management_enabled;
unsigned long last_probe;
#define IEEE80211_AUTH_ALG_OPEN BIT(0)
@@ -271,21 +279,29 @@ struct ieee80211_if_sta {
};
+/* flags used in struct ieee80211_sub_if_data.flags */
+#define IEEE80211_SDATA_ALLMULTI BIT(0)
+#define IEEE80211_SDATA_PROMISC BIT(1)
+#define IEEE80211_SDATA_USE_PROTECTION BIT(2) /* CTS protect ERP frames */
+/* use short preamble with IEEE 802.11b: this flag is set when the AP or beacon
+ * generator reports that there are no present stations that cannot support short
+ * preambles */
+#define IEEE80211_SDATA_SHORT_PREAMBLE BIT(3)
+#define IEEE80211_SDATA_USERSPACE_MLME BIT(4)
struct ieee80211_sub_if_data {
struct list_head list;
- unsigned int type;
+ enum ieee80211_if_types type;
struct wireless_dev wdev;
+ /* keys */
+ struct list_head key_list;
+
struct net_device *dev;
struct ieee80211_local *local;
- int mc_count;
- unsigned int allmulti:1;
- unsigned int promisc:1;
- unsigned int use_protection:1; /* CTS protect ERP frames */
+ unsigned int flags;
- struct net_device_stats stats;
int drop_unencrypted;
int eapol; /* 0 = process EAPOL frames as normal data frames,
* 1 = send EAPOL frames through wlan#ap to hostapd
@@ -364,7 +380,6 @@ struct ieee80211_sub_if_data {
struct dentry *drop_unencrypted;
struct dentry *eapol;
struct dentry *ieee8021_x;
- struct dentry *vlan_id;
} vlan;
struct {
struct dentry *mode;
@@ -393,9 +408,9 @@ struct ieee80211_local {
struct list_head modes_list;
struct net_device *mdev; /* wmaster# - "master" 802.11 device */
- struct net_device *apdev; /* wlan#ap - management frames (hostapd) */
int open_count;
int monitors;
+ unsigned int filter_flags; /* FIF_* */
struct iw_statistics wstats;
u8 wstats_flags;
int tx_headroom; /* required headroom for hardware/radiotap */
@@ -416,10 +431,9 @@ struct ieee80211_local {
struct sk_buff_head skb_queue_unreliable;
/* Station data structures */
- spinlock_t sta_lock; /* mutex for STA data structures */
+ rwlock_t sta_lock; /* protects STA data structures */
int num_sta; /* number of stations in sta_list */
struct list_head sta_list;
- struct list_head deleted_sta_list;
struct sta_info *sta_hash[STA_HASH_SIZE];
struct timer_list sta_cleanup;
@@ -427,17 +441,11 @@ struct ieee80211_local {
struct ieee80211_tx_stored_packet pending_packet[NUM_TX_DATA_QUEUES];
struct tasklet_struct tx_pending_tasklet;
- int mc_count; /* total count of multicast entries in all interfaces */
- int iff_allmultis, iff_promiscs;
- /* number of interfaces with corresponding IFF_ flags */
+ /* number of interfaces with corresponding IFF_ flags */
+ atomic_t iff_allmultis, iff_promiscs;
struct rate_control_ref *rate_ctrl;
- int next_mode; /* MODE_IEEE80211*
- * The mode preference for next channel change. This is
- * used to select .11g vs. .11b channels (or 4.9 GHz vs.
- * .11a) when the channel number is not unique. */
-
/* Supported and basic rate filters for different modes. These are
* pointers to -1 terminated lists and rates in 100 kbps units. */
int *supp_rates[NUM_IEEE80211_MODES];
@@ -447,14 +455,10 @@ struct ieee80211_local {
int fragmentation_threshold;
int short_retry_limit; /* dot11ShortRetryLimit */
int long_retry_limit; /* dot11LongRetryLimit */
- int short_preamble; /* use short preamble with IEEE 802.11b */
struct crypto_blkcipher *wep_tx_tfm;
struct crypto_blkcipher *wep_rx_tfm;
u32 wep_iv;
- int key_tx_rx_threshold; /* number of times any key can be used in TX
- * or RX before generating a rekey
- * notification; 0 = notification disabled. */
int bridge_packets; /* bridge packets between associated stations and
* deliver multicast frames both back to wireless
@@ -464,9 +468,8 @@ struct ieee80211_local {
ieee80211_rx_handler *rx_handlers;
ieee80211_tx_handler *tx_handlers;
- rwlock_t sub_if_lock; /* Protects sub_if_list. Cannot be taken under
- * sta_bss_lock or sta_lock. */
- struct list_head sub_if_list;
+ struct list_head interfaces;
+
int sta_scanning;
int scan_channel_idx;
enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state;
@@ -500,25 +503,17 @@ struct ieee80211_local {
#ifdef CONFIG_MAC80211_LEDS
int tx_led_counter, rx_led_counter;
- struct led_trigger *tx_led, *rx_led;
- char tx_led_name[32], rx_led_name[32];
+ struct led_trigger *tx_led, *rx_led, *assoc_led;
+ char tx_led_name[32], rx_led_name[32], assoc_led_name[32];
#endif
u32 channel_use;
u32 channel_use_raw;
- u32 stat_time;
- struct timer_list stat_timer;
#ifdef CONFIG_MAC80211_DEBUGFS
struct work_struct sta_debugfs_add;
#endif
- enum {
- STA_ANTENNA_SEL_AUTO = 0,
- STA_ANTENNA_SEL_SW_CTRL = 1,
- STA_ANTENNA_SEL_SW_CTRL_DEBUG = 2
- } sta_antenna_sel;
-
#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
/* TX/RX handler statistics */
unsigned int tx_handlers_drop;
@@ -548,16 +543,9 @@ struct ieee80211_local {
#endif /* CONFIG_MAC80211_DEBUG_COUNTERS */
- int default_wep_only; /* only default WEP keys are used with this
- * interface; this is used to decide when hwaccel
- * can be used with default keys */
int total_ps_buffered; /* total number of all buffered unicast and
* multicast packets for power saving stations
*/
- int allow_broadcast_always; /* whether to allow TX of broadcast frames
- * even when there are no associated STAs
- */
-
int wifi_wme_noack_test;
unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */
@@ -566,17 +554,13 @@ struct ieee80211_local {
unsigned int hw_modes; /* bitfield of supported hardware modes;
* (1 << MODE_*) */
- int user_space_mlme;
-
#ifdef CONFIG_MAC80211_DEBUGFS
struct local_debugfsdentries {
struct dentry *channel;
struct dentry *frequency;
- struct dentry *radar_detect;
struct dentry *antenna_sel_tx;
struct dentry *antenna_sel_rx;
struct dentry *bridge_packets;
- struct dentry *key_tx_rx_threshold;
struct dentry *rts_threshold;
struct dentry *fragmentation_threshold;
struct dentry *short_retry_limit;
@@ -584,7 +568,6 @@ struct ieee80211_local {
struct dentry *total_ps_buffered;
struct dentry *mode;
struct dentry *wep_iv;
- struct dentry *tx_power_reduction;
struct dentry *modes;
struct dentry *statistics;
struct local_debugfsdentries_statsdentries {
@@ -656,38 +639,38 @@ struct sta_attribute {
ssize_t (*store)(struct sta_info *, const char *buf, size_t count);
};
-static inline void __bss_tim_set(struct ieee80211_if_ap *bss, int aid)
+static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid)
{
/*
- * This format has ben mandated by the IEEE specifications,
+ * This format has been mandated by the IEEE specifications,
* so this line may not be changed to use the __set_bit() format.
*/
- bss->tim[(aid)/8] |= 1<<((aid) % 8);
+ bss->tim[aid / 8] |= (1 << (aid % 8));
}
static inline void bss_tim_set(struct ieee80211_local *local,
- struct ieee80211_if_ap *bss, int aid)
+ struct ieee80211_if_ap *bss, u16 aid)
{
- spin_lock_bh(&local->sta_lock);
+ read_lock_bh(&local->sta_lock);
__bss_tim_set(bss, aid);
- spin_unlock_bh(&local->sta_lock);
+ read_unlock_bh(&local->sta_lock);
}
-static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, int aid)
+static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, u16 aid)
{
/*
- * This format has ben mandated by the IEEE specifications,
+ * This format has been mandated by the IEEE specifications,
* so this line may not be changed to use the __clear_bit() format.
*/
- bss->tim[(aid)/8] &= !(1<<((aid) % 8));
+ bss->tim[aid / 8] &= ~(1 << (aid % 8));
}
static inline void bss_tim_clear(struct ieee80211_local *local,
- struct ieee80211_if_ap *bss, int aid)
+ struct ieee80211_if_ap *bss, u16 aid)
{
- spin_lock_bh(&local->sta_lock);
+ read_lock_bh(&local->sta_lock);
__bss_tim_clear(bss, aid);
- spin_unlock_bh(&local->sta_lock);
+ read_unlock_bh(&local->sta_lock);
}
/**
@@ -707,35 +690,28 @@ static inline int ieee80211_is_erp_rate(int phymode, int rate)
return 0;
}
+static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
+{
+ return compare_ether_addr(raddr, addr) == 0 ||
+ is_broadcast_ether_addr(raddr);
+}
+
+
/* ieee80211.c */
int ieee80211_hw_config(struct ieee80211_local *local);
int ieee80211_if_config(struct net_device *dev);
int ieee80211_if_config_beacon(struct net_device *dev);
-struct ieee80211_key_conf *
-ieee80211_key_data2conf(struct ieee80211_local *local,
- const struct ieee80211_key *data);
-struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
- int idx, size_t key_len, gfp_t flags);
-void ieee80211_key_free(struct ieee80211_key *key);
-void ieee80211_rx_mgmt(struct ieee80211_local *local, struct sk_buff *skb,
- struct ieee80211_rx_status *status, u32 msg_type);
void ieee80211_prepare_rates(struct ieee80211_local *local,
struct ieee80211_hw_mode *mode);
void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx);
int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr);
-int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
-int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
void ieee80211_if_setup(struct net_device *dev);
-void ieee80211_if_mgmt_setup(struct net_device *dev);
-int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
- const char *name);
-struct net_device_stats *ieee80211_dev_stats(struct net_device *dev);
+struct ieee80211_rate *ieee80211_get_rate(struct ieee80211_local *local,
+ int phymode, int hwrate);
/* ieee80211_ioctl.c */
extern const struct iw_handler_def ieee80211_iw_handler_def;
-void ieee80211_update_default_wep_only(struct ieee80211_local *local);
-
/* Least common multiple of the used rates (in 100 kbps). This is used to
* calculate rate_inv values for each rate so that only integers are needed. */
@@ -783,6 +759,8 @@ struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev,
u8 *addr);
int ieee80211_sta_deauthenticate(struct net_device *dev, u16 reason);
int ieee80211_sta_disassociate(struct net_device *dev, u16 reason);
+void ieee80211_erp_info_change_notify(struct net_device *dev, u8 changes);
+void ieee80211_reset_erp_info(struct net_device *dev);
/* ieee80211_iface.c */
int ieee80211_if_add(struct net_device *dev, const char *name,
@@ -794,14 +772,32 @@ void __ieee80211_if_del(struct ieee80211_local *local,
int ieee80211_if_remove(struct net_device *dev, const char *name, int id);
void ieee80211_if_free(struct net_device *dev);
void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata);
-int ieee80211_if_add_mgmt(struct ieee80211_local *local);
-void ieee80211_if_del_mgmt(struct ieee80211_local *local);
/* regdomain.c */
void ieee80211_regdomain_init(void);
void ieee80211_set_default_regdomain(struct ieee80211_hw_mode *mode);
-/* for wiphy privid */
-extern void *mac80211_wiphy_privid;
+/* rx handling */
+extern ieee80211_rx_handler ieee80211_rx_pre_handlers[];
+extern ieee80211_rx_handler ieee80211_rx_handlers[];
+
+/* tx handling */
+extern ieee80211_tx_handler ieee80211_tx_handlers[];
+void ieee80211_clear_tx_pending(struct ieee80211_local *local);
+void ieee80211_tx_pending(unsigned long data);
+int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev);
+int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
+int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
+
+/* utility functions/constants */
+extern void *mac80211_wiphy_privid; /* for wiphy privid */
+extern const unsigned char rfc1042_header[6];
+extern const unsigned char bridge_tunnel_header[6];
+u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len);
+int ieee80211_is_eapol(const struct sk_buff *skb);
+int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
+ int rate, int erp, int short_preamble);
+void mac80211_ev_michael_mic_failure(struct net_device *dev, int keyidx,
+ struct ieee80211_hdr *hdr);
#endif /* IEEE80211_I_H */
diff --git a/net/mac80211/ieee80211_iface.c b/net/mac80211/ieee80211_iface.c
index 8532a5ccdd1e..43e505d29452 100644
--- a/net/mac80211/ieee80211_iface.c
+++ b/net/mac80211/ieee80211_iface.c
@@ -25,6 +25,8 @@ void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata)
sdata->eapol = 1;
for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
skb_queue_head_init(&sdata->fragments[i].skb_list);
+
+ INIT_LIST_HEAD(&sdata->key_list);
}
static void ieee80211_if_sdata_deinit(struct ieee80211_sub_if_data *sdata)
@@ -77,18 +79,15 @@ int ieee80211_if_add(struct net_device *dev, const char *name,
ieee80211_debugfs_add_netdev(sdata);
ieee80211_if_set_type(ndev, type);
- write_lock_bh(&local->sub_if_lock);
+ /* we're under RTNL so all this is fine */
if (unlikely(local->reg_state == IEEE80211_DEV_UNREGISTERED)) {
- write_unlock_bh(&local->sub_if_lock);
__ieee80211_if_del(local, sdata);
return -ENODEV;
}
- list_add(&sdata->list, &local->sub_if_list);
+ list_add_tail_rcu(&sdata->list, &local->interfaces);
+
if (new_dev)
*new_dev = ndev;
- write_unlock_bh(&local->sub_if_lock);
-
- ieee80211_update_default_wep_only(local);
return 0;
@@ -97,74 +96,35 @@ fail:
return ret;
}
-int ieee80211_if_add_mgmt(struct ieee80211_local *local)
-{
- struct net_device *ndev;
- struct ieee80211_sub_if_data *nsdata;
- int ret;
-
- ASSERT_RTNL();
-
- ndev = alloc_netdev(sizeof(struct ieee80211_sub_if_data), "wmgmt%d",
- ieee80211_if_mgmt_setup);
- if (!ndev)
- return -ENOMEM;
- ret = dev_alloc_name(ndev, ndev->name);
- if (ret < 0)
- goto fail;
-
- memcpy(ndev->dev_addr, local->hw.wiphy->perm_addr, ETH_ALEN);
- SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy));
-
- nsdata = IEEE80211_DEV_TO_SUB_IF(ndev);
- ndev->ieee80211_ptr = &nsdata->wdev;
- nsdata->wdev.wiphy = local->hw.wiphy;
- nsdata->type = IEEE80211_IF_TYPE_MGMT;
- nsdata->dev = ndev;
- nsdata->local = local;
- ieee80211_if_sdata_init(nsdata);
-
- ret = register_netdevice(ndev);
- if (ret)
- goto fail;
-
- ieee80211_debugfs_add_netdev(nsdata);
-
- if (local->open_count > 0)
- dev_open(ndev);
- local->apdev = ndev;
- return 0;
-
-fail:
- free_netdev(ndev);
- return ret;
-}
-
-void ieee80211_if_del_mgmt(struct ieee80211_local *local)
-{
- struct net_device *apdev;
-
- ASSERT_RTNL();
- apdev = local->apdev;
- ieee80211_debugfs_remove_netdev(IEEE80211_DEV_TO_SUB_IF(apdev));
- local->apdev = NULL;
- unregister_netdevice(apdev);
-}
-
void ieee80211_if_set_type(struct net_device *dev, int type)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
int oldtype = sdata->type;
- dev->hard_start_xmit = ieee80211_subif_start_xmit;
-
+ /*
+ * We need to call this function on the master interface
+ * which already has a hard_start_xmit routine assigned
+ * which must not be changed.
+ */
+ if (dev != sdata->local->mdev)
+ dev->hard_start_xmit = ieee80211_subif_start_xmit;
+
+ /*
+ * Called even when register_netdevice fails, it would
+ * oops if assigned before initialising the rest.
+ */
+ dev->uninit = ieee80211_if_reinit;
+
+ /* most have no BSS pointer */
+ sdata->bss = NULL;
sdata->type = type;
+
switch (type) {
case IEEE80211_IF_TYPE_WDS:
- sdata->bss = NULL;
+ /* nothing special */
break;
case IEEE80211_IF_TYPE_VLAN:
+ sdata->u.vlan.ap = NULL;
break;
case IEEE80211_IF_TYPE_AP:
sdata->u.ap.dtim_period = 2;
@@ -172,6 +132,7 @@ void ieee80211_if_set_type(struct net_device *dev, int type)
sdata->u.ap.max_ratectrl_rateidx = -1;
skb_queue_head_init(&sdata->u.ap.ps_bc_buf);
sdata->bss = &sdata->u.ap;
+ INIT_LIST_HEAD(&sdata->u.ap.vlans);
break;
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS: {
@@ -187,10 +148,10 @@ void ieee80211_if_set_type(struct net_device *dev, int type)
ifsta->capab = WLAN_CAPABILITY_ESS;
ifsta->auth_algs = IEEE80211_AUTH_ALG_OPEN |
IEEE80211_AUTH_ALG_SHARED_KEY;
- ifsta->create_ibss = 1;
- ifsta->wmm_enabled = 1;
- ifsta->auto_channel_sel = 1;
- ifsta->auto_bssid_sel = 1;
+ ifsta->flags |= IEEE80211_STA_CREATE_IBSS |
+ IEEE80211_STA_WMM_ENABLED |
+ IEEE80211_STA_AUTO_BSSID_SEL |
+ IEEE80211_STA_AUTO_CHANNEL_SEL;
msdata = IEEE80211_DEV_TO_SUB_IF(sdata->local->mdev);
sdata->bss = &msdata->u.ap;
@@ -205,7 +166,6 @@ void ieee80211_if_set_type(struct net_device *dev, int type)
dev->name, __FUNCTION__, type);
}
ieee80211_debugfs_change_if_type(sdata, oldtype);
- ieee80211_update_default_wep_only(local);
}
/* Must be called with rtnl lock held. */
@@ -214,57 +174,46 @@ void ieee80211_if_reinit(struct net_device *dev)
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct sta_info *sta;
- int i;
+ struct sk_buff *skb;
ASSERT_RTNL();
+
+ ieee80211_free_keys(sdata);
+
ieee80211_if_sdata_deinit(sdata);
- for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
- if (!sdata->keys[i])
- continue;
-#if 0
- /* The interface is down at the moment, so there is not
- * really much point in disabling the keys at this point. */
- memset(addr, 0xff, ETH_ALEN);
- if (local->ops->set_key)
- local->ops->set_key(local_to_hw(local), DISABLE_KEY, addr,
- local->keys[i], 0);
-#endif
- ieee80211_key_free(sdata->keys[i]);
- sdata->keys[i] = NULL;
- }
switch (sdata->type) {
+ case IEEE80211_IF_TYPE_INVALID:
+ /* cannot happen */
+ WARN_ON(1);
+ break;
case IEEE80211_IF_TYPE_AP: {
/* Remove all virtual interfaces that use this BSS
* as their sdata->bss */
struct ieee80211_sub_if_data *tsdata, *n;
- LIST_HEAD(tmp_list);
- write_lock_bh(&local->sub_if_lock);
- list_for_each_entry_safe(tsdata, n, &local->sub_if_list, list) {
+ list_for_each_entry_safe(tsdata, n, &local->interfaces, list) {
if (tsdata != sdata && tsdata->bss == &sdata->u.ap) {
printk(KERN_DEBUG "%s: removing virtual "
"interface %s because its BSS interface"
" is being removed\n",
sdata->dev->name, tsdata->dev->name);
- list_move_tail(&tsdata->list, &tmp_list);
+ list_del_rcu(&tsdata->list);
+ /*
+ * We have lots of time and can afford
+ * to sync for each interface
+ */
+ synchronize_rcu();
+ __ieee80211_if_del(local, tsdata);
}
}
- write_unlock_bh(&local->sub_if_lock);
-
- list_for_each_entry_safe(tsdata, n, &tmp_list, list)
- __ieee80211_if_del(local, tsdata);
kfree(sdata->u.ap.beacon_head);
kfree(sdata->u.ap.beacon_tail);
- kfree(sdata->u.ap.generic_elem);
- if (dev != local->mdev) {
- struct sk_buff *skb;
- while ((skb = skb_dequeue(&sdata->u.ap.ps_bc_buf))) {
- local->total_ps_buffered--;
- dev_kfree_skb(skb);
- }
+ while ((skb = skb_dequeue(&sdata->u.ap.ps_bc_buf))) {
+ local->total_ps_buffered--;
+ dev_kfree_skb(skb);
}
break;
@@ -272,8 +221,8 @@ void ieee80211_if_reinit(struct net_device *dev)
case IEEE80211_IF_TYPE_WDS:
sta = sta_info_get(local, sdata->u.wds.remote_addr);
if (sta) {
+ sta_info_free(sta);
sta_info_put(sta);
- sta_info_free(sta, 0);
} else {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk(KERN_DEBUG "%s: Someone had deleted my STA "
@@ -298,6 +247,9 @@ void ieee80211_if_reinit(struct net_device *dev)
case IEEE80211_IF_TYPE_MNTR:
dev->type = ARPHRD_ETHER;
break;
+ case IEEE80211_IF_TYPE_VLAN:
+ sdata->u.vlan.ap = NULL;
+ break;
}
/* remove all STAs that are bound to this virtual interface */
@@ -327,29 +279,23 @@ int ieee80211_if_remove(struct net_device *dev, const char *name, int id)
ASSERT_RTNL();
- write_lock_bh(&local->sub_if_lock);
- list_for_each_entry_safe(sdata, n, &local->sub_if_list, list) {
+ list_for_each_entry_safe(sdata, n, &local->interfaces, list) {
if ((sdata->type == id || id == -1) &&
strcmp(name, sdata->dev->name) == 0 &&
sdata->dev != local->mdev) {
- list_del(&sdata->list);
- write_unlock_bh(&local->sub_if_lock);
+ list_del_rcu(&sdata->list);
+ synchronize_rcu();
__ieee80211_if_del(local, sdata);
- ieee80211_update_default_wep_only(local);
return 0;
}
}
- write_unlock_bh(&local->sub_if_lock);
return -ENODEV;
}
void ieee80211_if_free(struct net_device *dev)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- /* local->apdev must be NULL when freeing management interface */
- BUG_ON(dev == local->apdev);
ieee80211_if_sdata_deinit(sdata);
free_netdev(dev);
}
diff --git a/net/mac80211/ieee80211_ioctl.c b/net/mac80211/ieee80211_ioctl.c
index e7904db55325..f0224c2311d2 100644
--- a/net/mac80211/ieee80211_ioctl.c
+++ b/net/mac80211/ieee80211_ioctl.c
@@ -21,77 +21,41 @@
#include <net/mac80211.h>
#include "ieee80211_i.h"
-#include "hostapd_ioctl.h"
#include "ieee80211_rate.h"
#include "wpa.h"
#include "aes_ccm.h"
-#include "debugfs_key.h"
-
-static void ieee80211_set_hw_encryption(struct net_device *dev,
- struct sta_info *sta, u8 addr[ETH_ALEN],
- struct ieee80211_key *key)
-{
- struct ieee80211_key_conf *keyconf = NULL;
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-
- /* default to sw encryption; this will be cleared by low-level
- * driver if the hw supports requested encryption */
- if (key)
- key->force_sw_encrypt = 1;
-
- if (key && local->ops->set_key &&
- (keyconf = ieee80211_key_data2conf(local, key))) {
- if (local->ops->set_key(local_to_hw(local), SET_KEY, addr,
- keyconf, sta ? sta->aid : 0)) {
- key->force_sw_encrypt = 1;
- key->hw_key_idx = HW_KEY_IDX_INVALID;
- } else {
- key->force_sw_encrypt =
- !!(keyconf->flags & IEEE80211_KEY_FORCE_SW_ENCRYPT);
- key->hw_key_idx =
- keyconf->hw_key_idx;
-
- }
- }
- kfree(keyconf);
-}
static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
- int idx, int alg, int set_tx_key,
- const u8 *_key, size_t key_len)
+ int idx, int alg, int remove,
+ int set_tx_key, const u8 *_key,
+ size_t key_len)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
int ret = 0;
struct sta_info *sta;
- struct ieee80211_key *key, *old_key;
- int try_hwaccel = 1;
- struct ieee80211_key_conf *keyconf;
+ struct ieee80211_key *key;
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ if (idx < 0 || idx >= NUM_DEFAULT_KEYS) {
+ printk(KERN_DEBUG "%s: set_encrypt - invalid idx=%d\n",
+ dev->name, idx);
+ return -EINVAL;
+ }
+
if (is_broadcast_ether_addr(sta_addr)) {
sta = NULL;
- if (idx >= NUM_DEFAULT_KEYS) {
- printk(KERN_DEBUG "%s: set_encrypt - invalid idx=%d\n",
- dev->name, idx);
- return -EINVAL;
- }
key = sdata->keys[idx];
-
- /* TODO: consider adding hwaccel support for these; at least
- * Atheros key cache should be able to handle this since AP is
- * only transmitting frames with default keys. */
- /* FIX: hw key cache can be used when only one virtual
- * STA is associated with each AP. If more than one STA
- * is associated to the same AP, software encryption
- * must be used. This should be done automatically
- * based on configured station devices. For the time
- * being, this can be only set at compile time. */
} else {
set_tx_key = 0;
- if (idx != 0) {
+ /*
+ * According to the standard, the key index of a pairwise
+ * key must be zero. However, some AP are broken when it
+ * comes to WEP key indices, so we work around this.
+ */
+ if (idx != 0 && alg != ALG_WEP) {
printk(KERN_DEBUG "%s: set_encrypt - non-zero idx for "
"individual key\n", dev->name);
return -EINVAL;
@@ -100,9 +64,10 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
sta = sta_info_get(local, sta_addr);
if (!sta) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ DECLARE_MAC_BUF(mac);
printk(KERN_DEBUG "%s: set_encrypt - unknown addr "
- MAC_FMT "\n",
- dev->name, MAC_ARG(sta_addr));
+ "%s\n",
+ dev->name, print_mac(mac, sta_addr));
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
return -ENOENT;
@@ -111,144 +76,25 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
key = sta->key;
}
- /* FIX:
- * Cannot configure default hwaccel keys with WEP algorithm, if
- * any of the virtual interfaces is using static WEP
- * configuration because hwaccel would otherwise try to decrypt
- * these frames.
- *
- * For now, just disable WEP hwaccel for broadcast when there is
- * possibility of conflict with default keys. This can maybe later be
- * optimized by using non-default keys (at least with Atheros ar521x).
- */
- if (!sta && alg == ALG_WEP && !local->default_wep_only &&
- sdata->type != IEEE80211_IF_TYPE_IBSS &&
- sdata->type != IEEE80211_IF_TYPE_AP) {
- try_hwaccel = 0;
- }
-
- if (local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP) {
- /* Software encryption cannot be used with devices that hide
- * encryption from the host system, so always try to use
- * hardware acceleration with such devices. */
- try_hwaccel = 1;
- }
-
- if ((local->hw.flags & IEEE80211_HW_NO_TKIP_WMM_HWACCEL) &&
- alg == ALG_TKIP) {
- if (sta && (sta->flags & WLAN_STA_WME)) {
- /* Hardware does not support hwaccel with TKIP when using WMM.
- */
- try_hwaccel = 0;
- }
- else if (sdata->type == IEEE80211_IF_TYPE_STA) {
- sta = sta_info_get(local, sdata->u.sta.bssid);
- if (sta) {
- if (sta->flags & WLAN_STA_WME) {
- try_hwaccel = 0;
- }
- sta_info_put(sta);
- sta = NULL;
- }
- }
- }
-
- if (alg == ALG_NONE) {
- keyconf = NULL;
- if (try_hwaccel && key &&
- key->hw_key_idx != HW_KEY_IDX_INVALID &&
- local->ops->set_key &&
- (keyconf = ieee80211_key_data2conf(local, key)) != NULL &&
- local->ops->set_key(local_to_hw(local), DISABLE_KEY,
- sta_addr, keyconf, sta ? sta->aid : 0)) {
- printk(KERN_DEBUG "%s: set_encrypt - low-level disable"
- " failed\n", dev->name);
- ret = -EINVAL;
- }
- kfree(keyconf);
-
- if (set_tx_key || sdata->default_key == key) {
- ieee80211_debugfs_key_remove_default(sdata);
- sdata->default_key = NULL;
- }
- ieee80211_debugfs_key_remove(key);
- if (sta)
- sta->key = NULL;
- else
- sdata->keys[idx] = NULL;
+ if (remove) {
ieee80211_key_free(key);
key = NULL;
} else {
- old_key = key;
- key = ieee80211_key_alloc(sta ? NULL : sdata, idx, key_len,
- GFP_KERNEL);
+ /*
+ * Automatically frees any old key if present.
+ */
+ key = ieee80211_key_alloc(sdata, sta, alg, idx, key_len, _key);
if (!key) {
ret = -ENOMEM;
goto err_out;
}
-
- /* default to sw encryption; low-level driver sets these if the
- * requested encryption is supported */
- key->hw_key_idx = HW_KEY_IDX_INVALID;
- key->force_sw_encrypt = 1;
-
- key->alg = alg;
- key->keyidx = idx;
- key->keylen = key_len;
- memcpy(key->key, _key, key_len);
- if (set_tx_key)
- key->default_tx_key = 1;
-
- if (alg == ALG_CCMP) {
- /* Initialize AES key state here as an optimization
- * so that it does not need to be initialized for every
- * packet. */
- key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(
- key->key);
- if (!key->u.ccmp.tfm) {
- ret = -ENOMEM;
- goto err_free;
- }
- }
-
- if (set_tx_key || sdata->default_key == old_key) {
- ieee80211_debugfs_key_remove_default(sdata);
- sdata->default_key = NULL;
- }
- ieee80211_debugfs_key_remove(old_key);
- if (sta)
- sta->key = key;
- else
- sdata->keys[idx] = key;
- ieee80211_key_free(old_key);
- ieee80211_debugfs_key_add(local, key);
- if (sta)
- ieee80211_debugfs_key_sta_link(key, sta);
-
- if (try_hwaccel &&
- (alg == ALG_WEP || alg == ALG_TKIP || alg == ALG_CCMP))
- ieee80211_set_hw_encryption(dev, sta, sta_addr, key);
}
- if (set_tx_key || (!sta && !sdata->default_key && key)) {
- sdata->default_key = key;
- if (key)
- ieee80211_debugfs_key_add_default(sdata);
-
- if (local->ops->set_key_idx &&
- local->ops->set_key_idx(local_to_hw(local), idx))
- printk(KERN_DEBUG "%s: failed to set TX key idx for "
- "low-level driver\n", dev->name);
- }
+ if (set_tx_key || (!sta && !sdata->default_key && key))
+ ieee80211_set_default_key(sdata, idx);
- if (sta)
- sta_info_put(sta);
-
- return 0;
-
-err_free:
- ieee80211_key_free(key);
-err_out:
+ ret = 0;
+ err_out:
if (sta)
sta_info_put(sta);
return ret;
@@ -259,44 +105,25 @@ static int ieee80211_ioctl_siwgenie(struct net_device *dev,
struct iw_point *data, char *extra)
{
struct ieee80211_sub_if_data *sdata;
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- if (local->user_space_mlme)
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)
return -EOPNOTSUPP;
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type == IEEE80211_IF_TYPE_STA ||
sdata->type == IEEE80211_IF_TYPE_IBSS) {
int ret = ieee80211_sta_set_extra_ie(dev, extra, data->length);
if (ret)
return ret;
- sdata->u.sta.auto_bssid_sel = 0;
+ sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
ieee80211_sta_req_auth(dev, &sdata->u.sta);
return 0;
}
- if (sdata->type == IEEE80211_IF_TYPE_AP) {
- kfree(sdata->u.ap.generic_elem);
- sdata->u.ap.generic_elem = kmalloc(data->length, GFP_KERNEL);
- if (!sdata->u.ap.generic_elem)
- return -ENOMEM;
- memcpy(sdata->u.ap.generic_elem, extra, data->length);
- sdata->u.ap.generic_elem_len = data->length;
- return ieee80211_if_config(dev);
- }
return -EOPNOTSUPP;
}
-static int ieee80211_ioctl_set_radio_enabled(struct net_device *dev,
- int val)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_conf *conf = &local->hw.conf;
-
- conf->radio_enabled = val;
- return ieee80211_hw_config(wdev_priv(dev->ieee80211_ptr));
-}
-
static int ieee80211_ioctl_giwname(struct net_device *dev,
struct iw_request_info *info,
char *name, char *extra)
@@ -313,9 +140,6 @@ static int ieee80211_ioctl_giwname(struct net_device *dev,
case MODE_IEEE80211G:
strcpy(name, "IEEE 802.11g");
break;
- case MODE_ATHEROS_TURBO:
- strcpy(name, "5GHz Turbo");
- break;
default:
strcpy(name, "IEEE 802.11");
break;
@@ -480,11 +304,6 @@ int ieee80211_set_channel(struct ieee80211_local *local, int channel, int freq)
struct ieee80211_channel *chan = &mode->channels[c];
if (chan->flag & IEEE80211_CHAN_W_SCAN &&
((chan->chan == channel) || (chan->freq == freq))) {
- /* Use next_mode as the mode preference to
- * resolve non-unique channel numbers. */
- if (set && mode->mode != local->next_mode)
- continue;
-
local->oper_channel = chan;
local->oper_hw_mode = mode;
set++;
@@ -512,13 +331,14 @@ static int ieee80211_ioctl_siwfreq(struct net_device *dev,
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type == IEEE80211_IF_TYPE_STA)
- sdata->u.sta.auto_channel_sel = 0;
+ sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_CHANNEL_SEL;
/* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */
if (freq->e == 0) {
if (freq->m < 0) {
if (sdata->type == IEEE80211_IF_TYPE_STA)
- sdata->u.sta.auto_channel_sel = 1;
+ sdata->u.sta.flags |=
+ IEEE80211_STA_AUTO_CHANNEL_SEL;
return 0;
} else
return ieee80211_set_channel(local, freq->m, -1);
@@ -554,7 +374,6 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *ssid)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata;
size_t len = data->length;
@@ -566,14 +385,17 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev,
if (sdata->type == IEEE80211_IF_TYPE_STA ||
sdata->type == IEEE80211_IF_TYPE_IBSS) {
int ret;
- if (local->user_space_mlme) {
+ if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) {
if (len > IEEE80211_MAX_SSID_LEN)
return -EINVAL;
memcpy(sdata->u.sta.ssid, ssid, len);
sdata->u.sta.ssid_len = len;
return 0;
}
- sdata->u.sta.auto_ssid_sel = !data->flags;
+ if (data->flags)
+ sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_SSID_SEL;
+ else
+ sdata->u.sta.flags |= IEEE80211_STA_AUTO_SSID_SEL;
ret = ieee80211_sta_set_ssid(dev, ssid, len);
if (ret)
return ret;
@@ -628,25 +450,24 @@ static int ieee80211_ioctl_siwap(struct net_device *dev,
struct iw_request_info *info,
struct sockaddr *ap_addr, char *extra)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type == IEEE80211_IF_TYPE_STA ||
sdata->type == IEEE80211_IF_TYPE_IBSS) {
int ret;
- if (local->user_space_mlme) {
+ if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) {
memcpy(sdata->u.sta.bssid, (u8 *) &ap_addr->sa_data,
ETH_ALEN);
return 0;
}
- if (is_zero_ether_addr((u8 *) &ap_addr->sa_data)) {
- sdata->u.sta.auto_bssid_sel = 1;
- sdata->u.sta.auto_channel_sel = 1;
- } else if (is_broadcast_ether_addr((u8 *) &ap_addr->sa_data))
- sdata->u.sta.auto_bssid_sel = 1;
+ if (is_zero_ether_addr((u8 *) &ap_addr->sa_data))
+ sdata->u.sta.flags |= IEEE80211_STA_AUTO_BSSID_SEL |
+ IEEE80211_STA_AUTO_CHANNEL_SEL;
+ else if (is_broadcast_ether_addr((u8 *) &ap_addr->sa_data))
+ sdata->u.sta.flags |= IEEE80211_STA_AUTO_BSSID_SEL;
else
- sdata->u.sta.auto_bssid_sel = 0;
+ sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
ret = ieee80211_sta_set_bssid(dev, (u8 *) &ap_addr->sa_data);
if (ret)
return ret;
@@ -762,9 +583,6 @@ static int ieee80211_ioctl_siwrate(struct net_device *dev,
struct ieee80211_rate *rates = &mode->rates[i];
int this_rate = rates->rate;
- if (mode->mode == MODE_ATHEROS_TURBO ||
- mode->mode == MODE_ATHEROS_TURBOG)
- this_rate *= 2;
if (target_rate == this_rate) {
sdata->bss->max_ratectrl_rateidx = i;
if (rate->fixed)
@@ -798,6 +616,52 @@ static int ieee80211_ioctl_giwrate(struct net_device *dev,
return 0;
}
+static int ieee80211_ioctl_siwtxpower(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ bool need_reconfig = 0;
+
+ if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
+ return -EINVAL;
+ if (data->txpower.flags & IW_TXPOW_RANGE)
+ return -EINVAL;
+ if (!data->txpower.fixed)
+ return -EINVAL;
+
+ if (local->hw.conf.power_level != data->txpower.value) {
+ local->hw.conf.power_level = data->txpower.value;
+ need_reconfig = 1;
+ }
+ if (local->hw.conf.radio_enabled != !(data->txpower.disabled)) {
+ local->hw.conf.radio_enabled = !(data->txpower.disabled);
+ need_reconfig = 1;
+ }
+ if (need_reconfig) {
+ ieee80211_hw_config(local);
+ /* The return value of hw_config is not of big interest here,
+ * as it doesn't say that it failed because of _this_ config
+ * change or something else. Ignore it. */
+ }
+
+ return 0;
+}
+
+static int ieee80211_ioctl_giwtxpower(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+
+ data->txpower.fixed = 1;
+ data->txpower.disabled = !(local->hw.conf.radio_enabled);
+ data->txpower.value = local->hw.conf.power_level;
+ data->txpower.flags = IW_TXPOW_DBM;
+
+ return 0;
+}
+
static int ieee80211_ioctl_siwrts(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *rts, char *extra)
@@ -930,335 +794,6 @@ static int ieee80211_ioctl_giwretry(struct net_device *dev,
return 0;
}
-static void ieee80211_key_enable_hwaccel(struct ieee80211_local *local,
- struct ieee80211_key *key)
-{
- struct ieee80211_key_conf *keyconf;
- u8 addr[ETH_ALEN];
-
- if (!key || key->alg != ALG_WEP || !key->force_sw_encrypt ||
- (local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP))
- return;
-
- memset(addr, 0xff, ETH_ALEN);
- keyconf = ieee80211_key_data2conf(local, key);
- if (keyconf && local->ops->set_key &&
- local->ops->set_key(local_to_hw(local),
- SET_KEY, addr, keyconf, 0) == 0) {
- key->force_sw_encrypt =
- !!(keyconf->flags & IEEE80211_KEY_FORCE_SW_ENCRYPT);
- key->hw_key_idx = keyconf->hw_key_idx;
- }
- kfree(keyconf);
-}
-
-
-static void ieee80211_key_disable_hwaccel(struct ieee80211_local *local,
- struct ieee80211_key *key)
-{
- struct ieee80211_key_conf *keyconf;
- u8 addr[ETH_ALEN];
-
- if (!key || key->alg != ALG_WEP || key->force_sw_encrypt ||
- (local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP))
- return;
-
- memset(addr, 0xff, ETH_ALEN);
- keyconf = ieee80211_key_data2conf(local, key);
- if (keyconf && local->ops->set_key)
- local->ops->set_key(local_to_hw(local), DISABLE_KEY,
- addr, keyconf, 0);
- kfree(keyconf);
- key->force_sw_encrypt = 1;
-}
-
-
-static int ieee80211_ioctl_default_wep_only(struct ieee80211_local *local,
- int value)
-{
- int i;
- struct ieee80211_sub_if_data *sdata;
-
- local->default_wep_only = value;
- read_lock(&local->sub_if_lock);
- list_for_each_entry(sdata, &local->sub_if_list, list)
- for (i = 0; i < NUM_DEFAULT_KEYS; i++)
- if (value)
- ieee80211_key_enable_hwaccel(local,
- sdata->keys[i]);
- else
- ieee80211_key_disable_hwaccel(local,
- sdata->keys[i]);
- read_unlock(&local->sub_if_lock);
-
- return 0;
-}
-
-
-void ieee80211_update_default_wep_only(struct ieee80211_local *local)
-{
- int i = 0;
- struct ieee80211_sub_if_data *sdata;
-
- read_lock(&local->sub_if_lock);
- list_for_each_entry(sdata, &local->sub_if_list, list) {
-
- if (sdata->dev == local->mdev)
- continue;
-
- /* If there is an AP interface then depend on userspace to
- set default_wep_only correctly. */
- if (sdata->type == IEEE80211_IF_TYPE_AP) {
- read_unlock(&local->sub_if_lock);
- return;
- }
-
- i++;
- }
-
- read_unlock(&local->sub_if_lock);
-
- if (i <= 1)
- ieee80211_ioctl_default_wep_only(local, 1);
- else
- ieee80211_ioctl_default_wep_only(local, 0);
-}
-
-
-static int ieee80211_ioctl_prism2_param(struct net_device *dev,
- struct iw_request_info *info,
- void *wrqu, char *extra)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata;
- int *i = (int *) extra;
- int param = *i;
- int value = *(i + 1);
- int ret = 0;
-
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- switch (param) {
- case PRISM2_PARAM_IEEE_802_1X:
- if (local->ops->set_ieee8021x)
- ret = local->ops->set_ieee8021x(local_to_hw(local),
- value);
- if (ret)
- printk(KERN_DEBUG "%s: failed to set IEEE 802.1X (%d) "
- "for low-level driver\n", dev->name, value);
- else
- sdata->ieee802_1x = value;
- break;
-
- case PRISM2_PARAM_CTS_PROTECT_ERP_FRAMES:
- if (sdata->type != IEEE80211_IF_TYPE_AP)
- ret = -ENOENT;
- else
- sdata->use_protection = value;
- break;
-
- case PRISM2_PARAM_PREAMBLE:
- local->short_preamble = value;
- break;
-
- case PRISM2_PARAM_STAT_TIME:
- if (!local->stat_time && value) {
- local->stat_timer.expires = jiffies + HZ * value / 100;
- add_timer(&local->stat_timer);
- } else if (local->stat_time && !value) {
- del_timer_sync(&local->stat_timer);
- }
- local->stat_time = value;
- break;
- case PRISM2_PARAM_SHORT_SLOT_TIME:
- if (value)
- local->hw.conf.flags |= IEEE80211_CONF_SHORT_SLOT_TIME;
- else
- local->hw.conf.flags &= ~IEEE80211_CONF_SHORT_SLOT_TIME;
- if (ieee80211_hw_config(local))
- ret = -EINVAL;
- break;
-
- case PRISM2_PARAM_NEXT_MODE:
- local->next_mode = value;
- break;
-
- case PRISM2_PARAM_RADIO_ENABLED:
- ret = ieee80211_ioctl_set_radio_enabled(dev, value);
- break;
-
- case PRISM2_PARAM_ANTENNA_MODE:
- local->hw.conf.antenna_mode = value;
- if (ieee80211_hw_config(local))
- ret = -EINVAL;
- break;
-
- case PRISM2_PARAM_STA_ANTENNA_SEL:
- local->sta_antenna_sel = value;
- break;
-
- case PRISM2_PARAM_TX_POWER_REDUCTION:
- if (value < 0)
- ret = -EINVAL;
- else
- local->hw.conf.tx_power_reduction = value;
- break;
-
- case PRISM2_PARAM_KEY_TX_RX_THRESHOLD:
- local->key_tx_rx_threshold = value;
- break;
-
- case PRISM2_PARAM_DEFAULT_WEP_ONLY:
- ret = ieee80211_ioctl_default_wep_only(local, value);
- break;
-
- case PRISM2_PARAM_WIFI_WME_NOACK_TEST:
- local->wifi_wme_noack_test = value;
- break;
-
- case PRISM2_PARAM_SCAN_FLAGS:
- local->scan_flags = value;
- break;
-
- case PRISM2_PARAM_MIXED_CELL:
- if (sdata->type != IEEE80211_IF_TYPE_STA &&
- sdata->type != IEEE80211_IF_TYPE_IBSS)
- ret = -EINVAL;
- else
- sdata->u.sta.mixed_cell = !!value;
- break;
-
- case PRISM2_PARAM_HW_MODES:
- local->enabled_modes = value;
- break;
-
- case PRISM2_PARAM_CREATE_IBSS:
- if (sdata->type != IEEE80211_IF_TYPE_IBSS)
- ret = -EINVAL;
- else
- sdata->u.sta.create_ibss = !!value;
- break;
- case PRISM2_PARAM_WMM_ENABLED:
- if (sdata->type != IEEE80211_IF_TYPE_STA &&
- sdata->type != IEEE80211_IF_TYPE_IBSS)
- ret = -EINVAL;
- else
- sdata->u.sta.wmm_enabled = !!value;
- break;
- case PRISM2_PARAM_RADAR_DETECT:
- local->hw.conf.radar_detect = value;
- break;
- case PRISM2_PARAM_SPECTRUM_MGMT:
- local->hw.conf.spect_mgmt = value;
- break;
- default:
- ret = -EOPNOTSUPP;
- break;
- }
-
- return ret;
-}
-
-
-static int ieee80211_ioctl_get_prism2_param(struct net_device *dev,
- struct iw_request_info *info,
- void *wrqu, char *extra)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata;
- int *param = (int *) extra;
- int ret = 0;
-
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- switch (*param) {
- case PRISM2_PARAM_IEEE_802_1X:
- *param = sdata->ieee802_1x;
- break;
-
- case PRISM2_PARAM_CTS_PROTECT_ERP_FRAMES:
- *param = sdata->use_protection;
- break;
-
- case PRISM2_PARAM_PREAMBLE:
- *param = local->short_preamble;
- break;
-
- case PRISM2_PARAM_STAT_TIME:
- *param = local->stat_time;
- break;
- case PRISM2_PARAM_SHORT_SLOT_TIME:
- *param = !!(local->hw.conf.flags & IEEE80211_CONF_SHORT_SLOT_TIME);
- break;
-
- case PRISM2_PARAM_NEXT_MODE:
- *param = local->next_mode;
- break;
-
- case PRISM2_PARAM_ANTENNA_MODE:
- *param = local->hw.conf.antenna_mode;
- break;
-
- case PRISM2_PARAM_STA_ANTENNA_SEL:
- *param = local->sta_antenna_sel;
- break;
-
- case PRISM2_PARAM_TX_POWER_REDUCTION:
- *param = local->hw.conf.tx_power_reduction;
- break;
-
- case PRISM2_PARAM_KEY_TX_RX_THRESHOLD:
- *param = local->key_tx_rx_threshold;
- break;
-
- case PRISM2_PARAM_DEFAULT_WEP_ONLY:
- *param = local->default_wep_only;
- break;
-
- case PRISM2_PARAM_WIFI_WME_NOACK_TEST:
- *param = local->wifi_wme_noack_test;
- break;
-
- case PRISM2_PARAM_SCAN_FLAGS:
- *param = local->scan_flags;
- break;
-
- case PRISM2_PARAM_HW_MODES:
- *param = local->enabled_modes;
- break;
-
- case PRISM2_PARAM_CREATE_IBSS:
- if (sdata->type != IEEE80211_IF_TYPE_IBSS)
- ret = -EINVAL;
- else
- *param = !!sdata->u.sta.create_ibss;
- break;
-
- case PRISM2_PARAM_MIXED_CELL:
- if (sdata->type != IEEE80211_IF_TYPE_STA &&
- sdata->type != IEEE80211_IF_TYPE_IBSS)
- ret = -EINVAL;
- else
- *param = !!sdata->u.sta.mixed_cell;
- break;
- case PRISM2_PARAM_WMM_ENABLED:
- if (sdata->type != IEEE80211_IF_TYPE_STA &&
- sdata->type != IEEE80211_IF_TYPE_IBSS)
- ret = -EINVAL;
- else
- *param = !!sdata->u.sta.wmm_enabled;
- break;
- default:
- ret = -EOPNOTSUPP;
- break;
- }
-
- return ret;
-}
-
static int ieee80211_ioctl_siwmlme(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *extra)
@@ -1291,6 +826,7 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev,
struct ieee80211_sub_if_data *sdata;
int idx, i, alg = ALG_WEP;
u8 bcaddr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ int remove = 0;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -1309,21 +845,16 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev,
idx--;
if (erq->flags & IW_ENCODE_DISABLED)
- alg = ALG_NONE;
+ remove = 1;
else if (erq->length == 0) {
/* No key data - just set the default TX key index */
- if (sdata->default_key != sdata->keys[idx]) {
- ieee80211_debugfs_key_remove_default(sdata);
- sdata->default_key = sdata->keys[idx];
- if (sdata->default_key)
- ieee80211_debugfs_key_add_default(sdata);
- }
+ ieee80211_set_default_key(sdata, idx);
return 0;
}
return ieee80211_set_encryption(
dev, bcaddr,
- idx, alg,
+ idx, alg, remove,
!sdata->default_key,
keybuf, erq->length);
}
@@ -1362,9 +893,9 @@ static int ieee80211_ioctl_giwencode(struct net_device *dev,
return 0;
}
- memcpy(key, sdata->keys[idx]->key,
- min((int)erq->length, sdata->keys[idx]->keylen));
- erq->length = sdata->keys[idx]->keylen;
+ memcpy(key, sdata->keys[idx]->conf.key,
+ min_t(int, erq->length, sdata->keys[idx]->conf.keylen));
+ erq->length = sdata->keys[idx]->conf.keylen;
erq->flags |= IW_ENCODE_ENABLED;
return 0;
@@ -1390,22 +921,12 @@ static int ieee80211_ioctl_siwauth(struct net_device *dev,
ret = -EINVAL;
else {
/*
- * TODO: sdata->u.sta.key_mgmt does not match with WE18
- * value completely; could consider modifying this to
- * be closer to WE18. For now, this value is not really
- * used for anything else than Privacy matching, so the
- * current code here should be more or less OK.
+ * Key management was set by wpa_supplicant,
+ * we only need this to associate to a network
+ * that has privacy enabled regardless of not
+ * having a key.
*/
- if (data->value & IW_AUTH_KEY_MGMT_802_1X) {
- sdata->u.sta.key_mgmt =
- IEEE80211_KEY_MGMT_WPA_EAP;
- } else if (data->value & IW_AUTH_KEY_MGMT_PSK) {
- sdata->u.sta.key_mgmt =
- IEEE80211_KEY_MGMT_WPA_PSK;
- } else {
- sdata->u.sta.key_mgmt =
- IEEE80211_KEY_MGMT_NONE;
- }
+ sdata->u.sta.key_management_enabled = !!data->value;
}
break;
case IW_AUTH_80211_AUTH_ALG:
@@ -1484,11 +1005,11 @@ static int ieee80211_ioctl_siwencodeext(struct net_device *dev,
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
- int alg, idx, i;
+ int uninitialized_var(alg), idx, i, remove = 0;
switch (ext->alg) {
case IW_ENCODE_ALG_NONE:
- alg = ALG_NONE;
+ remove = 1;
break;
case IW_ENCODE_ALG_WEP:
alg = ALG_WEP;
@@ -1504,7 +1025,7 @@ static int ieee80211_ioctl_siwencodeext(struct net_device *dev,
}
if (erq->flags & IW_ENCODE_DISABLED)
- alg = ALG_NONE;
+ remove = 1;
idx = erq->flags & IW_ENCODE_INDEX;
if (idx < 1 || idx > 4) {
@@ -1523,20 +1044,13 @@ static int ieee80211_ioctl_siwencodeext(struct net_device *dev,
idx--;
return ieee80211_set_encryption(dev, ext->addr.sa_data, idx, alg,
+ remove,
ext->ext_flags &
IW_ENCODE_EXT_SET_TX_KEY,
ext->key, ext->key_len);
}
-static const struct iw_priv_args ieee80211_ioctl_priv[] = {
- { PRISM2_IOCTL_PRISM2_PARAM,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "param" },
- { PRISM2_IOCTL_GET_PRISM2_PARAM,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_param" },
-};
-
/* Structures to export the Wireless Handlers */
static const iw_handler ieee80211_handler[] =
@@ -1557,10 +1071,10 @@ static const iw_handler ieee80211_handler[] =
(iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */
(iw_handler) NULL /* not used */, /* SIOCSIWSTATS */
(iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */
- iw_handler_set_spy, /* SIOCSIWSPY */
- iw_handler_get_spy, /* SIOCGIWSPY */
- iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
- iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
+ (iw_handler) NULL, /* SIOCSIWSPY */
+ (iw_handler) NULL, /* SIOCGIWSPY */
+ (iw_handler) NULL, /* SIOCSIWTHRSPY */
+ (iw_handler) NULL, /* SIOCGIWTHRSPY */
(iw_handler) ieee80211_ioctl_siwap, /* SIOCSIWAP */
(iw_handler) ieee80211_ioctl_giwap, /* SIOCGIWAP */
(iw_handler) ieee80211_ioctl_siwmlme, /* SIOCSIWMLME */
@@ -1579,8 +1093,8 @@ static const iw_handler ieee80211_handler[] =
(iw_handler) ieee80211_ioctl_giwrts, /* SIOCGIWRTS */
(iw_handler) ieee80211_ioctl_siwfrag, /* SIOCSIWFRAG */
(iw_handler) ieee80211_ioctl_giwfrag, /* SIOCGIWFRAG */
- (iw_handler) NULL, /* SIOCSIWTXPOW */
- (iw_handler) NULL, /* SIOCGIWTXPOW */
+ (iw_handler) ieee80211_ioctl_siwtxpower, /* SIOCSIWTXPOW */
+ (iw_handler) ieee80211_ioctl_giwtxpower, /* SIOCGIWTXPOW */
(iw_handler) ieee80211_ioctl_siwretry, /* SIOCSIWRETRY */
(iw_handler) ieee80211_ioctl_giwretry, /* SIOCGIWRETRY */
(iw_handler) ieee80211_ioctl_siwencode, /* SIOCSIWENCODE */
@@ -1599,19 +1113,9 @@ static const iw_handler ieee80211_handler[] =
(iw_handler) NULL, /* -- hole -- */
};
-static const iw_handler ieee80211_private_handler[] =
-{ /* SIOCIWFIRSTPRIV + */
- (iw_handler) ieee80211_ioctl_prism2_param, /* 0 */
- (iw_handler) ieee80211_ioctl_get_prism2_param, /* 1 */
-};
-
const struct iw_handler_def ieee80211_iw_handler_def =
{
.num_standard = ARRAY_SIZE(ieee80211_handler),
- .num_private = ARRAY_SIZE(ieee80211_private_handler),
- .num_private_args = ARRAY_SIZE(ieee80211_ioctl_priv),
.standard = (iw_handler *) ieee80211_handler,
- .private = (iw_handler *) ieee80211_private_handler,
- .private_args = (struct iw_priv_args *) ieee80211_ioctl_priv,
.get_wireless_stats = ieee80211_get_wireless_stats,
};
diff --git a/net/mac80211/ieee80211_key.h b/net/mac80211/ieee80211_key.h
index c33384912782..fc770e98d47b 100644
--- a/net/mac80211/ieee80211_key.h
+++ b/net/mac80211/ieee80211_key.h
@@ -11,7 +11,7 @@
#define IEEE80211_KEY_H
#include <linux/types.h>
-#include <linux/kref.h>
+#include <linux/list.h>
#include <linux/crypto.h>
#include <net/mac80211.h>
@@ -41,11 +41,21 @@
#define NUM_RX_DATA_QUEUES 17
+struct ieee80211_local;
+struct ieee80211_sub_if_data;
+struct sta_info;
+
+#define KEY_FLAG_UPLOADED_TO_HARDWARE (1<<0)
+
struct ieee80211_key {
- struct kref kref;
+ struct ieee80211_local *local;
+ struct ieee80211_sub_if_data *sdata;
+ struct sta_info *sta;
+
+ struct list_head list;
+
+ unsigned int flags;
- int hw_key_idx; /* filled and used by low-level driver */
- ieee80211_key_alg alg;
union {
struct {
/* last used TSC */
@@ -73,22 +83,16 @@ struct ieee80211_key {
u8 rx_crypto_buf[6 * AES_BLOCK_LEN];
} ccmp;
} u;
- int tx_rx_count; /* number of times this key has been used */
- int keylen;
- /* if the low level driver can provide hardware acceleration it should
- * clear this flag */
- unsigned int force_sw_encrypt:1;
- unsigned int default_tx_key:1; /* This key is the new default TX key
- * (used only for broadcast keys). */
- s8 keyidx; /* WEP key index */
+ /* number of times this key has been used */
+ int tx_rx_count;
#ifdef CONFIG_MAC80211_DEBUGFS
struct {
struct dentry *stalink;
struct dentry *dir;
struct dentry *keylen;
- struct dentry *force_sw_encrypt;
+ struct dentry *flags;
struct dentry *keyidx;
struct dentry *hw_key_idx;
struct dentry *tx_rx_count;
@@ -97,10 +101,27 @@ struct ieee80211_key {
struct dentry *rx_spec;
struct dentry *replays;
struct dentry *key;
+ struct dentry *ifindex;
} debugfs;
#endif
- u8 key[0];
+ /*
+ * key config, must be last because it contains key
+ * material as variable length member
+ */
+ struct ieee80211_key_conf conf;
};
+struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ enum ieee80211_key_alg alg,
+ int idx,
+ size_t key_len,
+ const u8 *key_data);
+void ieee80211_key_free(struct ieee80211_key *key);
+void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx);
+void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata);
+void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata);
+void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata);
+
#endif /* IEEE80211_KEY_H */
diff --git a/net/mac80211/ieee80211_led.c b/net/mac80211/ieee80211_led.c
index 719d75b20707..4cf89af9d100 100644
--- a/net/mac80211/ieee80211_led.c
+++ b/net/mac80211/ieee80211_led.c
@@ -33,33 +33,58 @@ void ieee80211_led_tx(struct ieee80211_local *local, int q)
led_trigger_event(local->tx_led, LED_FULL);
}
+void ieee80211_led_assoc(struct ieee80211_local *local, bool associated)
+{
+ if (unlikely(!local->assoc_led))
+ return;
+ if (associated)
+ led_trigger_event(local->assoc_led, LED_FULL);
+ else
+ led_trigger_event(local->assoc_led, LED_OFF);
+}
+
void ieee80211_led_init(struct ieee80211_local *local)
{
local->rx_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
- if (!local->rx_led)
- return;
- snprintf(local->rx_led_name, sizeof(local->rx_led_name),
- "%srx", wiphy_name(local->hw.wiphy));
- local->rx_led->name = local->rx_led_name;
- if (led_trigger_register(local->rx_led)) {
- kfree(local->rx_led);
- local->rx_led = NULL;
+ if (local->rx_led) {
+ snprintf(local->rx_led_name, sizeof(local->rx_led_name),
+ "%srx", wiphy_name(local->hw.wiphy));
+ local->rx_led->name = local->rx_led_name;
+ if (led_trigger_register(local->rx_led)) {
+ kfree(local->rx_led);
+ local->rx_led = NULL;
+ }
}
local->tx_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
- if (!local->tx_led)
- return;
- snprintf(local->tx_led_name, sizeof(local->tx_led_name),
- "%stx", wiphy_name(local->hw.wiphy));
- local->tx_led->name = local->tx_led_name;
- if (led_trigger_register(local->tx_led)) {
- kfree(local->tx_led);
- local->tx_led = NULL;
+ if (local->tx_led) {
+ snprintf(local->tx_led_name, sizeof(local->tx_led_name),
+ "%stx", wiphy_name(local->hw.wiphy));
+ local->tx_led->name = local->tx_led_name;
+ if (led_trigger_register(local->tx_led)) {
+ kfree(local->tx_led);
+ local->tx_led = NULL;
+ }
+ }
+
+ local->assoc_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
+ if (local->assoc_led) {
+ snprintf(local->assoc_led_name, sizeof(local->assoc_led_name),
+ "%sassoc", wiphy_name(local->hw.wiphy));
+ local->assoc_led->name = local->assoc_led_name;
+ if (led_trigger_register(local->assoc_led)) {
+ kfree(local->assoc_led);
+ local->assoc_led = NULL;
+ }
}
}
void ieee80211_led_exit(struct ieee80211_local *local)
{
+ if (local->assoc_led) {
+ led_trigger_unregister(local->assoc_led);
+ kfree(local->assoc_led);
+ }
if (local->tx_led) {
led_trigger_unregister(local->tx_led);
kfree(local->tx_led);
@@ -70,6 +95,16 @@ void ieee80211_led_exit(struct ieee80211_local *local)
}
}
+char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+
+ if (local->assoc_led)
+ return local->assoc_led_name;
+ return NULL;
+}
+EXPORT_SYMBOL(__ieee80211_get_assoc_led_name);
+
char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
diff --git a/net/mac80211/ieee80211_led.h b/net/mac80211/ieee80211_led.h
index 5c8ab8263878..0feb22619835 100644
--- a/net/mac80211/ieee80211_led.h
+++ b/net/mac80211/ieee80211_led.h
@@ -14,6 +14,8 @@
#ifdef CONFIG_MAC80211_LEDS
extern void ieee80211_led_rx(struct ieee80211_local *local);
extern void ieee80211_led_tx(struct ieee80211_local *local, int q);
+extern void ieee80211_led_assoc(struct ieee80211_local *local,
+ bool associated);
extern void ieee80211_led_init(struct ieee80211_local *local);
extern void ieee80211_led_exit(struct ieee80211_local *local);
#else
@@ -23,6 +25,10 @@ static inline void ieee80211_led_rx(struct ieee80211_local *local)
static inline void ieee80211_led_tx(struct ieee80211_local *local, int q)
{
}
+static inline void ieee80211_led_assoc(struct ieee80211_local *local,
+ bool associated)
+{
+}
static inline void ieee80211_led_init(struct ieee80211_local *local)
{
}
diff --git a/net/mac80211/ieee80211_rate.c b/net/mac80211/ieee80211_rate.c
index 2118de04fc35..93abb8fff141 100644
--- a/net/mac80211/ieee80211_rate.c
+++ b/net/mac80211/ieee80211_rate.c
@@ -9,6 +9,7 @@
*/
#include <linux/kernel.h>
+#include <linux/rtnetlink.h>
#include "ieee80211_rate.h"
#include "ieee80211_i.h"
@@ -137,3 +138,43 @@ void rate_control_put(struct rate_control_ref *ref)
{
kref_put(&ref->kref, rate_control_release);
}
+
+int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
+ const char *name)
+{
+ struct rate_control_ref *ref, *old;
+
+ ASSERT_RTNL();
+ if (local->open_count || netif_running(local->mdev))
+ return -EBUSY;
+
+ ref = rate_control_alloc(name, local);
+ if (!ref) {
+ printk(KERN_WARNING "%s: Failed to select rate control "
+ "algorithm\n", wiphy_name(local->hw.wiphy));
+ return -ENOENT;
+ }
+
+ old = local->rate_ctrl;
+ local->rate_ctrl = ref;
+ if (old) {
+ rate_control_put(old);
+ sta_info_flush(local, NULL);
+ }
+
+ printk(KERN_DEBUG "%s: Selected rate control "
+ "algorithm '%s'\n", wiphy_name(local->hw.wiphy),
+ ref->ops->name);
+
+
+ return 0;
+}
+
+void rate_control_deinitialize(struct ieee80211_local *local)
+{
+ struct rate_control_ref *ref;
+
+ ref = local->rate_ctrl;
+ local->rate_ctrl = NULL;
+ rate_control_put(ref);
+}
diff --git a/net/mac80211/ieee80211_rate.h b/net/mac80211/ieee80211_rate.h
index f021a028d9d0..7cd1ebab4f83 100644
--- a/net/mac80211/ieee80211_rate.h
+++ b/net/mac80211/ieee80211_rate.h
@@ -30,8 +30,6 @@ struct rate_control_extra {
/* parameters from the caller to rate_control_get_rate(): */
struct ieee80211_hw_mode *mode;
- int mgmt_data; /* this is data frame that is used for management
- * (e.g., IEEE 802.1X EAPOL) */
u16 ethertype;
};
@@ -141,4 +139,10 @@ static inline void rate_control_remove_sta_debugfs(struct sta_info *sta)
#endif
}
+
+/* functions for rate control related to a device */
+int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
+ const char *name);
+void rate_control_deinitialize(struct ieee80211_local *local);
+
#endif /* IEEE80211_RATE_H */
diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c
index 0d99b685df5f..1641e8fe44b7 100644
--- a/net/mac80211/ieee80211_sta.c
+++ b/net/mac80211/ieee80211_sta.c
@@ -31,7 +31,7 @@
#include <net/mac80211.h>
#include "ieee80211_i.h"
#include "ieee80211_rate.h"
-#include "hostapd_ioctl.h"
+#include "ieee80211_led.h"
#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
#define IEEE80211_AUTH_MAX_TRIES 3
@@ -108,11 +108,10 @@ struct ieee802_11_elems {
u8 wmm_param_len;
};
-typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
+enum ParseRes { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 };
-
-static ParseRes ieee802_11_parse_elems(u8 *start, size_t len,
- struct ieee802_11_elems *elems)
+static enum ParseRes ieee802_11_parse_elems(u8 *start, size_t len,
+ struct ieee802_11_elems *elems)
{
size_t left = len;
u8 *pos = start;
@@ -234,7 +233,6 @@ static int ecw2cw(int ecw)
return cw - 1;
}
-
static void ieee80211_sta_wmm_params(struct net_device *dev,
struct ieee80211_if_sta *ifsta,
u8 *wmm_param, size_t wmm_param_len)
@@ -318,17 +316,43 @@ static void ieee80211_handle_erp_ie(struct net_device *dev, u8 erp_value)
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
int use_protection = (erp_value & WLAN_ERP_USE_PROTECTION) != 0;
+ int preamble_mode = (erp_value & WLAN_ERP_BARKER_PREAMBLE) != 0;
+ u8 changes = 0;
+ DECLARE_MAC_BUF(mac);
- if (use_protection != sdata->use_protection) {
+ if (use_protection != !!(sdata->flags & IEEE80211_SDATA_USE_PROTECTION)) {
if (net_ratelimit()) {
printk(KERN_DEBUG "%s: CTS protection %s (BSSID="
- MAC_FMT ")\n",
+ "%s)\n",
dev->name,
use_protection ? "enabled" : "disabled",
- MAC_ARG(ifsta->bssid));
+ print_mac(mac, ifsta->bssid));
+ }
+ if (use_protection)
+ sdata->flags |= IEEE80211_SDATA_USE_PROTECTION;
+ else
+ sdata->flags &= ~IEEE80211_SDATA_USE_PROTECTION;
+ changes |= IEEE80211_ERP_CHANGE_PROTECTION;
+ }
+
+ if (preamble_mode != !(sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE)) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: switched to %s barker preamble"
+ " (BSSID=%s)\n",
+ dev->name,
+ (preamble_mode == WLAN_ERP_PREAMBLE_SHORT) ?
+ "short" : "long",
+ print_mac(mac, ifsta->bssid));
}
- sdata->use_protection = use_protection;
+ if (preamble_mode)
+ sdata->flags &= ~IEEE80211_SDATA_SHORT_PREAMBLE;
+ else
+ sdata->flags |= IEEE80211_SDATA_SHORT_PREAMBLE;
+ changes |= IEEE80211_ERP_CHANGE_PREAMBLE;
}
+
+ if (changes)
+ ieee80211_erp_info_change_notify(dev, changes);
}
@@ -344,7 +368,7 @@ static void ieee80211_sta_send_associnfo(struct net_device *dev,
return;
buf = kmalloc(50 + 2 * (ifsta->assocreq_ies_len +
- ifsta->assocresp_ies_len), GFP_ATOMIC);
+ ifsta->assocresp_ies_len), GFP_KERNEL);
if (!buf)
return;
@@ -384,19 +408,21 @@ static void ieee80211_sta_send_associnfo(struct net_device *dev,
static void ieee80211_set_associated(struct net_device *dev,
- struct ieee80211_if_sta *ifsta, int assoc)
+ struct ieee80211_if_sta *ifsta,
+ bool assoc)
{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
union iwreq_data wrqu;
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (ifsta->associated == assoc)
+ if (!!(ifsta->flags & IEEE80211_STA_ASSOCIATED) == assoc)
return;
- ifsta->associated = assoc;
-
if (assoc) {
struct ieee80211_sub_if_data *sdata;
struct ieee80211_sta_bss *bss;
+
+ ifsta->flags |= IEEE80211_STA_ASSOCIATED;
+
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type != IEEE80211_IF_TYPE_STA)
return;
@@ -409,18 +435,21 @@ static void ieee80211_set_associated(struct net_device *dev,
}
netif_carrier_on(dev);
- ifsta->prev_bssid_set = 1;
+ ifsta->flags |= IEEE80211_STA_PREV_BSSID_SET;
memcpy(ifsta->prev_bssid, sdata->u.sta.bssid, ETH_ALEN);
memcpy(wrqu.ap_addr.sa_data, sdata->u.sta.bssid, ETH_ALEN);
ieee80211_sta_send_associnfo(dev, ifsta);
} else {
+ ifsta->flags &= ~IEEE80211_STA_ASSOCIATED;
+
netif_carrier_off(dev);
- sdata->use_protection = 0;
+ ieee80211_reset_erp_info(dev);
memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
}
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
ifsta->last_probe = jiffies;
+ ieee80211_led_assoc(local, assoc);
}
static void ieee80211_set_disassoc(struct net_device *dev,
@@ -447,8 +476,8 @@ static void ieee80211_sta_tx(struct net_device *dev, struct sk_buff *skb,
pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
pkt_data->ifindex = sdata->dev->ifindex;
- pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT);
- pkt_data->do_not_encrypt = !encrypt;
+ if (!encrypt)
+ pkt_data->flags |= IEEE80211_TXPD_DO_NOT_ENCRYPT;
dev_queue_xmit(skb);
}
@@ -495,18 +524,20 @@ static void ieee80211_send_auth(struct net_device *dev,
static void ieee80211_authenticate(struct net_device *dev,
struct ieee80211_if_sta *ifsta)
{
+ DECLARE_MAC_BUF(mac);
+
ifsta->auth_tries++;
if (ifsta->auth_tries > IEEE80211_AUTH_MAX_TRIES) {
- printk(KERN_DEBUG "%s: authentication with AP " MAC_FMT
+ printk(KERN_DEBUG "%s: authentication with AP %s"
" timed out\n",
- dev->name, MAC_ARG(ifsta->bssid));
+ dev->name, print_mac(mac, ifsta->bssid));
ifsta->state = IEEE80211_DISABLED;
return;
}
ifsta->state = IEEE80211_AUTHENTICATE;
- printk(KERN_DEBUG "%s: authenticate with AP " MAC_FMT "\n",
- dev->name, MAC_ARG(ifsta->bssid));
+ printk(KERN_DEBUG "%s: authenticate with AP %s\n",
+ dev->name, print_mac(mac, ifsta->bssid));
ieee80211_send_auth(dev, ifsta, 1, NULL, 0, 0);
@@ -559,7 +590,7 @@ static void ieee80211_send_assoc(struct net_device *dev,
memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
- if (ifsta->prev_bssid_set) {
+ if (ifsta->flags & IEEE80211_STA_PREV_BSSID_SET) {
skb_put(skb, 10);
mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
IEEE80211_STYPE_REASSOC_REQ);
@@ -589,8 +620,6 @@ static void ieee80211_send_assoc(struct net_device *dev,
*pos++ = len;
for (i = 0; i < len; i++) {
int rate = mode->rates[i].rate;
- if (mode->mode == MODE_ATHEROS_TURBO)
- rate /= 2;
*pos++ = (u8) (rate / 5);
}
@@ -600,8 +629,6 @@ static void ieee80211_send_assoc(struct net_device *dev,
*pos++ = mode->num_rates - len;
for (i = len; i < mode->num_rates; i++) {
int rate = mode->rates[i].rate;
- if (mode->mode == MODE_ATHEROS_TURBO)
- rate /= 2;
*pos++ = (u8) (rate / 5);
}
}
@@ -611,7 +638,7 @@ static void ieee80211_send_assoc(struct net_device *dev,
memcpy(pos, ifsta->extra_ie, ifsta->extra_ie_len);
}
- if (wmm && ifsta->wmm_enabled) {
+ if (wmm && (ifsta->flags & IEEE80211_STA_WMM_ENABLED)) {
pos = skb_put(skb, 9);
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = 7; /* len */
@@ -626,7 +653,7 @@ static void ieee80211_send_assoc(struct net_device *dev,
kfree(ifsta->assocreq_ies);
ifsta->assocreq_ies_len = (skb->data + skb->len) - ies;
- ifsta->assocreq_ies = kmalloc(ifsta->assocreq_ies_len, GFP_ATOMIC);
+ ifsta->assocreq_ies = kmalloc(ifsta->assocreq_ies_len, GFP_KERNEL);
if (ifsta->assocreq_ies)
memcpy(ifsta->assocreq_ies, ies, ifsta->assocreq_ies_len);
@@ -698,8 +725,8 @@ static int ieee80211_privacy_mismatch(struct net_device *dev,
struct ieee80211_sta_bss *bss;
int res = 0;
- if (!ifsta || ifsta->mixed_cell ||
- ifsta->key_mgmt != IEEE80211_KEY_MGMT_NONE)
+ if (!ifsta || (ifsta->flags & IEEE80211_STA_MIXED_CELL) ||
+ ifsta->key_management_enabled)
return 0;
bss = ieee80211_rx_bss_get(dev, ifsta->bssid);
@@ -719,18 +746,20 @@ static int ieee80211_privacy_mismatch(struct net_device *dev,
static void ieee80211_associate(struct net_device *dev,
struct ieee80211_if_sta *ifsta)
{
+ DECLARE_MAC_BUF(mac);
+
ifsta->assoc_tries++;
if (ifsta->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
- printk(KERN_DEBUG "%s: association with AP " MAC_FMT
+ printk(KERN_DEBUG "%s: association with AP %s"
" timed out\n",
- dev->name, MAC_ARG(ifsta->bssid));
+ dev->name, print_mac(mac, ifsta->bssid));
ifsta->state = IEEE80211_DISABLED;
return;
}
ifsta->state = IEEE80211_ASSOCIATE;
- printk(KERN_DEBUG "%s: associate with AP " MAC_FMT "\n",
- dev->name, MAC_ARG(ifsta->bssid));
+ printk(KERN_DEBUG "%s: associate with AP %s\n",
+ dev->name, print_mac(mac, ifsta->bssid));
if (ieee80211_privacy_mismatch(dev, ifsta)) {
printk(KERN_DEBUG "%s: mismatch in privacy configuration and "
"mixed-cell disabled - abort association\n", dev->name);
@@ -750,6 +779,7 @@ static void ieee80211_associated(struct net_device *dev,
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sta_info *sta;
int disassoc;
+ DECLARE_MAC_BUF(mac);
/* TODO: start monitoring current AP signal quality and number of
* missed beacons. Scan other channels every now and then and search
@@ -760,29 +790,27 @@ static void ieee80211_associated(struct net_device *dev,
sta = sta_info_get(local, ifsta->bssid);
if (!sta) {
- printk(KERN_DEBUG "%s: No STA entry for own AP " MAC_FMT "\n",
- dev->name, MAC_ARG(ifsta->bssid));
+ printk(KERN_DEBUG "%s: No STA entry for own AP %s\n",
+ dev->name, print_mac(mac, ifsta->bssid));
disassoc = 1;
} else {
disassoc = 0;
if (time_after(jiffies,
sta->last_rx + IEEE80211_MONITORING_INTERVAL)) {
- if (ifsta->probereq_poll) {
+ if (ifsta->flags & IEEE80211_STA_PROBEREQ_POLL) {
printk(KERN_DEBUG "%s: No ProbeResp from "
- "current AP " MAC_FMT " - assume out of "
+ "current AP %s - assume out of "
"range\n",
- dev->name, MAC_ARG(ifsta->bssid));
+ dev->name, print_mac(mac, ifsta->bssid));
disassoc = 1;
- sta_info_free(sta, 0);
- ifsta->probereq_poll = 0;
- } else {
+ sta_info_free(sta);
+ } else
ieee80211_send_probe_req(dev, ifsta->bssid,
local->scan_ssid,
local->scan_ssid_len);
- ifsta->probereq_poll = 1;
- }
+ ifsta->flags ^= IEEE80211_STA_PROBEREQ_POLL;
} else {
- ifsta->probereq_poll = 0;
+ ifsta->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
if (time_after(jiffies, ifsta->last_probe +
IEEE80211_PROBE_INTERVAL)) {
ifsta->last_probe = jiffies;
@@ -862,10 +890,7 @@ static void ieee80211_send_probe_req(struct net_device *dev, u8 *dst,
pos = skb_put(skb, 1);
supp_rates[1]++;
}
- if (mode->mode == MODE_ATHEROS_TURBO)
- *pos = rate->rate / 10;
- else
- *pos = rate->rate / 5;
+ *pos = rate->rate / 5;
}
ieee80211_sta_tx(dev, skb, 0);
@@ -876,7 +901,7 @@ static int ieee80211_sta_wep_configured(struct net_device *dev)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (!sdata || !sdata->default_key ||
- sdata->default_key->alg != ALG_WEP)
+ sdata->default_key->conf.alg != ALG_WEP)
return 0;
return 1;
}
@@ -886,7 +911,7 @@ static void ieee80211_auth_completed(struct net_device *dev,
struct ieee80211_if_sta *ifsta)
{
printk(KERN_DEBUG "%s: authenticated\n", dev->name);
- ifsta->authenticated = 1;
+ ifsta->flags |= IEEE80211_STA_AUTHENTICATED;
ieee80211_associate(dev, ifsta);
}
@@ -924,37 +949,38 @@ static void ieee80211_rx_mgmt_auth(struct net_device *dev,
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
u16 auth_alg, auth_transaction, status_code;
+ DECLARE_MAC_BUF(mac);
if (ifsta->state != IEEE80211_AUTHENTICATE &&
sdata->type != IEEE80211_IF_TYPE_IBSS) {
printk(KERN_DEBUG "%s: authentication frame received from "
- MAC_FMT ", but not in authenticate state - ignored\n",
- dev->name, MAC_ARG(mgmt->sa));
+ "%s, but not in authenticate state - ignored\n",
+ dev->name, print_mac(mac, mgmt->sa));
return;
}
if (len < 24 + 6) {
printk(KERN_DEBUG "%s: too short (%zd) authentication frame "
- "received from " MAC_FMT " - ignored\n",
- dev->name, len, MAC_ARG(mgmt->sa));
+ "received from %s - ignored\n",
+ dev->name, len, print_mac(mac, mgmt->sa));
return;
}
if (sdata->type != IEEE80211_IF_TYPE_IBSS &&
memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) {
printk(KERN_DEBUG "%s: authentication frame received from "
- "unknown AP (SA=" MAC_FMT " BSSID=" MAC_FMT ") - "
- "ignored\n", dev->name, MAC_ARG(mgmt->sa),
- MAC_ARG(mgmt->bssid));
+ "unknown AP (SA=%s BSSID=%s) - "
+ "ignored\n", dev->name, print_mac(mac, mgmt->sa),
+ print_mac(mac, mgmt->bssid));
return;
}
if (sdata->type != IEEE80211_IF_TYPE_IBSS &&
memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0) {
printk(KERN_DEBUG "%s: authentication frame received from "
- "unknown BSSID (SA=" MAC_FMT " BSSID=" MAC_FMT ") - "
- "ignored\n", dev->name, MAC_ARG(mgmt->sa),
- MAC_ARG(mgmt->bssid));
+ "unknown BSSID (SA=%s BSSID=%s) - "
+ "ignored\n", dev->name, print_mac(mac, mgmt->sa),
+ print_mac(mac, mgmt->bssid));
return;
}
@@ -962,9 +988,9 @@ static void ieee80211_rx_mgmt_auth(struct net_device *dev,
auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
status_code = le16_to_cpu(mgmt->u.auth.status_code);
- printk(KERN_DEBUG "%s: RX authentication from " MAC_FMT " (alg=%d "
+ printk(KERN_DEBUG "%s: RX authentication from %s (alg=%d "
"transaction=%d status=%d)\n",
- dev->name, MAC_ARG(mgmt->sa), auth_alg,
+ dev->name, print_mac(mac, mgmt->sa), auth_alg,
auth_transaction, status_code);
if (sdata->type == IEEE80211_IF_TYPE_IBSS) {
@@ -1051,29 +1077,30 @@ static void ieee80211_rx_mgmt_deauth(struct net_device *dev,
size_t len)
{
u16 reason_code;
+ DECLARE_MAC_BUF(mac);
if (len < 24 + 2) {
printk(KERN_DEBUG "%s: too short (%zd) deauthentication frame "
- "received from " MAC_FMT " - ignored\n",
- dev->name, len, MAC_ARG(mgmt->sa));
+ "received from %s - ignored\n",
+ dev->name, len, print_mac(mac, mgmt->sa));
return;
}
if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) {
printk(KERN_DEBUG "%s: deauthentication frame received from "
- "unknown AP (SA=" MAC_FMT " BSSID=" MAC_FMT ") - "
- "ignored\n", dev->name, MAC_ARG(mgmt->sa),
- MAC_ARG(mgmt->bssid));
+ "unknown AP (SA=%s BSSID=%s) - "
+ "ignored\n", dev->name, print_mac(mac, mgmt->sa),
+ print_mac(mac, mgmt->bssid));
return;
}
reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
- printk(KERN_DEBUG "%s: RX deauthentication from " MAC_FMT
+ printk(KERN_DEBUG "%s: RX deauthentication from %s"
" (reason=%d)\n",
- dev->name, MAC_ARG(mgmt->sa), reason_code);
+ dev->name, print_mac(mac, mgmt->sa), reason_code);
- if (ifsta->authenticated) {
+ if (ifsta->flags & IEEE80211_STA_AUTHENTICATED) {
printk(KERN_DEBUG "%s: deauthenticated\n", dev->name);
}
@@ -1086,7 +1113,7 @@ static void ieee80211_rx_mgmt_deauth(struct net_device *dev,
}
ieee80211_set_disassoc(dev, ifsta, 1);
- ifsta->authenticated = 0;
+ ifsta->flags &= ~IEEE80211_STA_AUTHENTICATED;
}
@@ -1096,29 +1123,30 @@ static void ieee80211_rx_mgmt_disassoc(struct net_device *dev,
size_t len)
{
u16 reason_code;
+ DECLARE_MAC_BUF(mac);
if (len < 24 + 2) {
printk(KERN_DEBUG "%s: too short (%zd) disassociation frame "
- "received from " MAC_FMT " - ignored\n",
- dev->name, len, MAC_ARG(mgmt->sa));
+ "received from %s - ignored\n",
+ dev->name, len, print_mac(mac, mgmt->sa));
return;
}
if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) {
printk(KERN_DEBUG "%s: disassociation frame received from "
- "unknown AP (SA=" MAC_FMT " BSSID=" MAC_FMT ") - "
- "ignored\n", dev->name, MAC_ARG(mgmt->sa),
- MAC_ARG(mgmt->bssid));
+ "unknown AP (SA=%s BSSID=%s) - "
+ "ignored\n", dev->name, print_mac(mac, mgmt->sa),
+ print_mac(mac, mgmt->bssid));
return;
}
reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
- printk(KERN_DEBUG "%s: RX disassociation from " MAC_FMT
+ printk(KERN_DEBUG "%s: RX disassociation from %s"
" (reason=%d)\n",
- dev->name, MAC_ARG(mgmt->sa), reason_code);
+ dev->name, print_mac(mac, mgmt->sa), reason_code);
- if (ifsta->associated)
+ if (ifsta->flags & IEEE80211_STA_ASSOCIATED)
printk(KERN_DEBUG "%s: disassociated\n", dev->name);
if (ifsta->state == IEEE80211_ASSOCIATED) {
@@ -1145,29 +1173,30 @@ static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev,
struct ieee802_11_elems elems;
u8 *pos;
int i, j;
+ DECLARE_MAC_BUF(mac);
/* AssocResp and ReassocResp have identical structure, so process both
* of them in this function. */
if (ifsta->state != IEEE80211_ASSOCIATE) {
printk(KERN_DEBUG "%s: association frame received from "
- MAC_FMT ", but not in associate state - ignored\n",
- dev->name, MAC_ARG(mgmt->sa));
+ "%s, but not in associate state - ignored\n",
+ dev->name, print_mac(mac, mgmt->sa));
return;
}
if (len < 24 + 6) {
printk(KERN_DEBUG "%s: too short (%zd) association frame "
- "received from " MAC_FMT " - ignored\n",
- dev->name, len, MAC_ARG(mgmt->sa));
+ "received from %s - ignored\n",
+ dev->name, len, print_mac(mac, mgmt->sa));
return;
}
if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) {
printk(KERN_DEBUG "%s: association frame received from "
- "unknown AP (SA=" MAC_FMT " BSSID=" MAC_FMT ") - "
- "ignored\n", dev->name, MAC_ARG(mgmt->sa),
- MAC_ARG(mgmt->bssid));
+ "unknown AP (SA=%s BSSID=%s) - "
+ "ignored\n", dev->name, print_mac(mac, mgmt->sa),
+ print_mac(mac, mgmt->bssid));
return;
}
@@ -1179,16 +1208,18 @@ static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev,
"set\n", dev->name, aid);
aid &= ~(BIT(15) | BIT(14));
- printk(KERN_DEBUG "%s: RX %sssocResp from " MAC_FMT " (capab=0x%x "
+ printk(KERN_DEBUG "%s: RX %sssocResp from %s (capab=0x%x "
"status=%d aid=%d)\n",
- dev->name, reassoc ? "Rea" : "A", MAC_ARG(mgmt->sa),
+ dev->name, reassoc ? "Rea" : "A", print_mac(mac, mgmt->sa),
capab_info, status_code, aid);
if (status_code != WLAN_STATUS_SUCCESS) {
printk(KERN_DEBUG "%s: AP denied association (code=%d)\n",
dev->name, status_code);
- if (status_code == WLAN_STATUS_REASSOC_NO_ASSOC)
- ifsta->prev_bssid_set = 0;
+ /* if this was a reassociation, ensure we try a "full"
+ * association next time. This works around some broken APs
+ * which do not correctly reject reassociation requests. */
+ ifsta->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
return;
}
@@ -1224,7 +1255,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev,
kfree(ifsta->assocresp_ies);
ifsta->assocresp_ies_len = len - (pos - (u8 *) mgmt);
- ifsta->assocresp_ies = kmalloc(ifsta->assocresp_ies_len, GFP_ATOMIC);
+ ifsta->assocresp_ies = kmalloc(ifsta->assocresp_ies_len, GFP_KERNEL);
if (ifsta->assocresp_ies)
memcpy(ifsta->assocresp_ies, pos, ifsta->assocresp_ies_len);
@@ -1234,7 +1265,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev,
sta = sta_info_get(local, ifsta->bssid);
if (!sta) {
struct ieee80211_sta_bss *bss;
- sta = sta_info_add(local, dev, ifsta->bssid, GFP_ATOMIC);
+ sta = sta_info_add(local, dev, ifsta->bssid, GFP_KERNEL);
if (!sta) {
printk(KERN_DEBUG "%s: failed to add STA entry for the"
" AP\n", dev->name);
@@ -1250,23 +1281,18 @@ static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev,
}
sta->dev = dev;
- sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
- sta->assoc_ap = 1;
+ sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_AP;
rates = 0;
mode = local->oper_hw_mode;
for (i = 0; i < elems.supp_rates_len; i++) {
int rate = (elems.supp_rates[i] & 0x7f) * 5;
- if (mode->mode == MODE_ATHEROS_TURBO)
- rate *= 2;
for (j = 0; j < mode->num_rates; j++)
if (mode->rates[j].rate == rate)
rates |= BIT(j);
}
for (i = 0; i < elems.ext_supp_rates_len; i++) {
int rate = (elems.ext_supp_rates[i] & 0x7f) * 5;
- if (mode->mode == MODE_ATHEROS_TURBO)
- rate *= 2;
for (j = 0; j < mode->num_rates; j++)
if (mode->rates[j].rate == rate)
rates |= BIT(j);
@@ -1275,7 +1301,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev,
rate_control_rate_init(sta, local);
- if (elems.wmm_param && ifsta->wmm_enabled) {
+ if (elems.wmm_param && (ifsta->flags & IEEE80211_STA_WMM_ENABLED)) {
sta->flags |= WLAN_STA_WME;
ieee80211_sta_wmm_params(dev, ifsta, elems.wmm_param,
elems.wmm_param_len);
@@ -1418,14 +1444,16 @@ static void ieee80211_rx_bss_info(struct net_device *dev,
struct sta_info *sta;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
u64 timestamp;
+ DECLARE_MAC_BUF(mac);
+ DECLARE_MAC_BUF(mac2);
if (!beacon && memcmp(mgmt->da, dev->dev_addr, ETH_ALEN))
return; /* ignore ProbeResp to foreign address */
#if 0
- printk(KERN_DEBUG "%s: RX %s from " MAC_FMT " to " MAC_FMT "\n",
+ printk(KERN_DEBUG "%s: RX %s from %s to %s\n",
dev->name, beacon ? "Beacon" : "Probe Response",
- MAC_ARG(mgmt->sa), MAC_ARG(mgmt->da));
+ print_mac(mac, mgmt->sa), print_mac(mac2, mgmt->da));
#endif
baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
@@ -1444,10 +1472,10 @@ static void ieee80211_rx_bss_info(struct net_device *dev,
else
tsf = -1LLU;
if (time_after(jiffies, last_tsf_debug + 5 * HZ)) {
- printk(KERN_DEBUG "RX beacon SA=" MAC_FMT " BSSID="
- MAC_FMT " TSF=0x%llx BCN=0x%llx diff=%lld "
+ printk(KERN_DEBUG "RX beacon SA=%s BSSID="
+ "%s TSF=0x%llx BCN=0x%llx diff=%lld "
"@%lu\n",
- MAC_ARG(mgmt->sa), MAC_ARG(mgmt->bssid),
+ print_mac(mac, mgmt->sa), print_mac(mac2, mgmt->bssid),
(unsigned long long)tsf,
(unsigned long long)timestamp,
(unsigned long long)(tsf - timestamp),
@@ -1486,8 +1514,6 @@ static void ieee80211_rx_bss_info(struct net_device *dev,
rate = elems.ext_supp_rates
[i - elems.supp_rates_len];
own_rate = 5 * (rate & 0x7f);
- if (mode->mode == MODE_ATHEROS_TURBO)
- own_rate *= 2;
for (j = 0; j < num_rates; j++)
if (rates[j].rate == own_rate)
supp_rates |= BIT(j);
@@ -1503,9 +1529,9 @@ static void ieee80211_rx_bss_info(struct net_device *dev,
}
if (sta->supp_rates != prev_rates) {
printk(KERN_DEBUG "%s: updated supp_rates set for "
- MAC_FMT " based on beacon info (0x%x & 0x%x -> "
+ "%s based on beacon info (0x%x & 0x%x -> "
"0x%x)\n",
- dev->name, MAC_ARG(sta->addr), prev_rates,
+ dev->name, print_mac(mac, sta->addr), prev_rates,
supp_rates, sta->supp_rates);
}
sta_info_put(sta);
@@ -1672,7 +1698,7 @@ static void ieee80211_rx_mgmt_beacon(struct net_device *dev,
return;
ifsta = &sdata->u.sta;
- if (!ifsta->associated ||
+ if (!(ifsta->flags & IEEE80211_STA_ASSOCIATED) ||
memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0)
return;
@@ -1688,7 +1714,7 @@ static void ieee80211_rx_mgmt_beacon(struct net_device *dev,
if (elems.erp_info && elems.erp_info_len >= 1)
ieee80211_handle_erp_ie(dev, elems.erp_info[0]);
- if (elems.wmm_param && ifsta->wmm_enabled) {
+ if (elems.wmm_param && (ifsta->flags & IEEE80211_STA_WMM_ENABLED)) {
ieee80211_sta_wmm_params(dev, ifsta, elems.wmm_param,
elems.wmm_param_len);
}
@@ -1707,6 +1733,11 @@ static void ieee80211_rx_mgmt_probe_req(struct net_device *dev,
struct sk_buff *skb;
struct ieee80211_mgmt *resp;
u8 *pos, *end;
+ DECLARE_MAC_BUF(mac);
+#ifdef CONFIG_MAC80211_IBSS_DEBUG
+ DECLARE_MAC_BUF(mac2);
+ DECLARE_MAC_BUF(mac3);
+#endif
if (sdata->type != IEEE80211_IF_TYPE_IBSS ||
ifsta->state != IEEE80211_IBSS_JOINED ||
@@ -1719,10 +1750,10 @@ static void ieee80211_rx_mgmt_probe_req(struct net_device *dev,
tx_last_beacon = 1;
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "%s: RX ProbeReq SA=" MAC_FMT " DA=" MAC_FMT " BSSID="
- MAC_FMT " (tx_last_beacon=%d)\n",
- dev->name, MAC_ARG(mgmt->sa), MAC_ARG(mgmt->da),
- MAC_ARG(mgmt->bssid), tx_last_beacon);
+ printk(KERN_DEBUG "%s: RX ProbeReq SA=%s DA=%s BSSID="
+ "%s (tx_last_beacon=%d)\n",
+ dev->name, print_mac(mac, mgmt->sa), print_mac(mac2, mgmt->da),
+ print_mac(mac3, mgmt->bssid), tx_last_beacon);
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
if (!tx_last_beacon)
@@ -1738,8 +1769,8 @@ static void ieee80211_rx_mgmt_probe_req(struct net_device *dev,
pos + 2 + pos[1] > end) {
if (net_ratelimit()) {
printk(KERN_DEBUG "%s: Invalid SSID IE in ProbeReq "
- "from " MAC_FMT "\n",
- dev->name, MAC_ARG(mgmt->sa));
+ "from %s\n",
+ dev->name, print_mac(mac, mgmt->sa));
}
return;
}
@@ -1751,15 +1782,15 @@ static void ieee80211_rx_mgmt_probe_req(struct net_device *dev,
}
/* Reply with ProbeResp */
- skb = skb_copy(ifsta->probe_resp, GFP_ATOMIC);
+ skb = skb_copy(ifsta->probe_resp, GFP_KERNEL);
if (!skb)
return;
resp = (struct ieee80211_mgmt *) skb->data;
memcpy(resp->da, mgmt->sa, ETH_ALEN);
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "%s: Sending ProbeResp to " MAC_FMT "\n",
- dev->name, MAC_ARG(resp->da));
+ printk(KERN_DEBUG "%s: Sending ProbeResp to %s\n",
+ dev->name, print_mac(mac, resp->da));
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
ieee80211_sta_tx(dev, skb, 0);
}
@@ -1890,7 +1921,7 @@ static int ieee80211_sta_active_ibss(struct net_device *dev)
int active = 0;
struct sta_info *sta;
- spin_lock_bh(&local->sta_lock);
+ read_lock_bh(&local->sta_lock);
list_for_each_entry(sta, &local->sta_list, list) {
if (sta->dev == dev &&
time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
@@ -1899,7 +1930,7 @@ static int ieee80211_sta_active_ibss(struct net_device *dev)
break;
}
}
- spin_unlock_bh(&local->sta_lock);
+ read_unlock_bh(&local->sta_lock);
return active;
}
@@ -1909,16 +1940,25 @@ static void ieee80211_sta_expire(struct net_device *dev)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sta_info *sta, *tmp;
+ LIST_HEAD(tmp_list);
+ DECLARE_MAC_BUF(mac);
- spin_lock_bh(&local->sta_lock);
+ write_lock_bh(&local->sta_lock);
list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
if (time_after(jiffies, sta->last_rx +
IEEE80211_IBSS_INACTIVITY_LIMIT)) {
- printk(KERN_DEBUG "%s: expiring inactive STA " MAC_FMT
- "\n", dev->name, MAC_ARG(sta->addr));
- sta_info_free(sta, 1);
+ printk(KERN_DEBUG "%s: expiring inactive STA %s\n",
+ dev->name, print_mac(mac, sta->addr));
+ __sta_info_get(sta);
+ sta_info_remove(sta);
+ list_add(&sta->list, &tmp_list);
}
- spin_unlock_bh(&local->sta_lock);
+ write_unlock_bh(&local->sta_lock);
+
+ list_for_each_entry_safe(sta, tmp, &tmp_list, list) {
+ sta_info_free(sta);
+ sta_info_put(sta);
+ }
}
@@ -2047,7 +2087,8 @@ static void ieee80211_sta_reset_auth(struct net_device *dev,
printk(KERN_DEBUG "%s: Initial auth_alg=%d\n", dev->name,
ifsta->auth_alg);
ifsta->auth_transaction = -1;
- ifsta->associated = ifsta->auth_tries = ifsta->assoc_tries = 0;
+ ifsta->flags &= ~IEEE80211_STA_ASSOCIATED;
+ ifsta->auth_tries = ifsta->assoc_tries = 0;
netif_carrier_off(dev);
}
@@ -2061,8 +2102,10 @@ void ieee80211_sta_req_auth(struct net_device *dev,
if (sdata->type != IEEE80211_IF_TYPE_STA)
return;
- if ((ifsta->bssid_set || ifsta->auto_bssid_sel) &&
- (ifsta->ssid_set || ifsta->auto_ssid_sel)) {
+ if ((ifsta->flags & (IEEE80211_STA_BSSID_SET |
+ IEEE80211_STA_AUTO_BSSID_SEL)) &&
+ (ifsta->flags & (IEEE80211_STA_SSID_SET |
+ IEEE80211_STA_AUTO_SSID_SEL))) {
set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request);
queue_work(local->hw.workqueue, &ifsta->work);
}
@@ -2076,7 +2119,7 @@ static int ieee80211_sta_match_ssid(struct ieee80211_if_sta *ifsta,
if (!memcmp(ifsta->ssid, ssid, ssid_len))
return 1;
- if (ifsta->auto_bssid_sel)
+ if (ifsta->flags & IEEE80211_STA_AUTO_BSSID_SEL)
return 0;
hidden_ssid = 1;
@@ -2105,8 +2148,8 @@ static int ieee80211_sta_config_auth(struct net_device *dev,
struct ieee80211_sta_bss *bss, *selected = NULL;
int top_rssi = 0, freq;
- if (!ifsta->auto_channel_sel && !ifsta->auto_bssid_sel &&
- !ifsta->auto_ssid_sel) {
+ if (!(ifsta->flags & (IEEE80211_STA_AUTO_SSID_SEL |
+ IEEE80211_STA_AUTO_BSSID_SEL | IEEE80211_STA_AUTO_CHANNEL_SEL))) {
ifsta->state = IEEE80211_AUTHENTICATE;
ieee80211_sta_reset_auth(dev, ifsta);
return 0;
@@ -2122,14 +2165,15 @@ static int ieee80211_sta_config_auth(struct net_device *dev,
!!sdata->default_key)
continue;
- if (!ifsta->auto_channel_sel && bss->freq != freq)
+ if (!(ifsta->flags & IEEE80211_STA_AUTO_CHANNEL_SEL) &&
+ bss->freq != freq)
continue;
- if (!ifsta->auto_bssid_sel &&
+ if (!(ifsta->flags & IEEE80211_STA_AUTO_BSSID_SEL) &&
memcmp(bss->bssid, ifsta->bssid, ETH_ALEN))
continue;
- if (!ifsta->auto_ssid_sel &&
+ if (!(ifsta->flags & IEEE80211_STA_AUTO_SSID_SEL) &&
!ieee80211_sta_match_ssid(ifsta, bss->ssid, bss->ssid_len))
continue;
@@ -2144,7 +2188,7 @@ static int ieee80211_sta_config_auth(struct net_device *dev,
if (selected) {
ieee80211_set_channel(local, -1, selected->freq);
- if (!ifsta->ssid_set)
+ if (!(ifsta->flags & IEEE80211_STA_SSID_SET))
ieee80211_sta_set_ssid(dev, selected->ssid,
selected->ssid_len);
ieee80211_sta_set_bssid(dev, selected->bssid);
@@ -2154,7 +2198,7 @@ static int ieee80211_sta_config_auth(struct net_device *dev,
return 0;
} else {
if (ifsta->state != IEEE80211_AUTHENTICATE) {
- if (ifsta->auto_ssid_sel)
+ if (ifsta->flags & IEEE80211_STA_AUTO_SSID_SEL)
ieee80211_sta_start_scan(dev, NULL, 0);
else
ieee80211_sta_start_scan(dev, ifsta->ssid,
@@ -2271,8 +2315,9 @@ static int ieee80211_sta_join_ibss(struct net_device *dev,
"for IBSS beacon\n", dev->name);
break;
}
- control.tx_rate = (local->short_preamble &&
- (rate->flags & IEEE80211_RATE_PREAMBLE2)) ?
+ control.tx_rate =
+ ((sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE) &&
+ (rate->flags & IEEE80211_RATE_PREAMBLE2)) ?
rate->val2 : rate->val;
control.antenna_sel_tx = local->hw.conf.antenna_sel_tx;
control.power_level = local->hw.conf.power_level;
@@ -2303,8 +2348,6 @@ static int ieee80211_sta_join_ibss(struct net_device *dev,
mode = local->oper_hw_mode;
for (i = 0; i < bss->supp_rates_len; i++) {
int bitrate = (bss->supp_rates[i] & 0x7f) * 5;
- if (mode->mode == MODE_ATHEROS_TURBO)
- bitrate *= 2;
for (j = 0; j < mode->num_rates; j++)
if (mode->rates[j].rate == bitrate)
rates |= BIT(j);
@@ -2336,6 +2379,7 @@ static int ieee80211_sta_create_ibss(struct net_device *dev,
struct ieee80211_hw_mode *mode;
u8 bssid[ETH_ALEN], *pos;
int i;
+ DECLARE_MAC_BUF(mac);
#if 0
/* Easier testing, use fixed BSSID. */
@@ -2351,8 +2395,8 @@ static int ieee80211_sta_create_ibss(struct net_device *dev,
bssid[0] |= 0x02;
#endif
- printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID " MAC_FMT "\n",
- dev->name, MAC_ARG(bssid));
+ printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID %s\n",
+ dev->name, print_mac(mac, bssid));
bss = ieee80211_rx_bss_add(dev, bssid);
if (!bss)
@@ -2377,8 +2421,6 @@ static int ieee80211_sta_create_ibss(struct net_device *dev,
pos = bss->supp_rates;
for (i = 0; i < mode->num_rates; i++) {
int rate = mode->rates[i].rate;
- if (mode->mode == MODE_ATHEROS_TURBO)
- rate /= 2;
*pos++ = (u8) (rate / 5);
}
@@ -2394,6 +2436,8 @@ static int ieee80211_sta_find_ibss(struct net_device *dev,
int found = 0;
u8 bssid[ETH_ALEN];
int active_ibss;
+ DECLARE_MAC_BUF(mac);
+ DECLARE_MAC_BUF(mac2);
if (ifsta->ssid_len == 0)
return -EINVAL;
@@ -2410,8 +2454,8 @@ static int ieee80211_sta_find_ibss(struct net_device *dev,
|| !(bss->capability & WLAN_CAPABILITY_IBSS))
continue;
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG " bssid=" MAC_FMT " found\n",
- MAC_ARG(bss->bssid));
+ printk(KERN_DEBUG " bssid=%s found\n",
+ print_mac(mac, bss->bssid));
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
memcpy(bssid, bss->bssid, ETH_ALEN);
found = 1;
@@ -2421,14 +2465,14 @@ static int ieee80211_sta_find_ibss(struct net_device *dev,
spin_unlock_bh(&local->sta_bss_lock);
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG " sta_find_ibss: selected " MAC_FMT " current "
- MAC_FMT "\n", MAC_ARG(bssid), MAC_ARG(ifsta->bssid));
+ printk(KERN_DEBUG " sta_find_ibss: selected %s current "
+ "%s\n", print_mac(mac, bssid), print_mac(mac2, ifsta->bssid));
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
if (found && memcmp(ifsta->bssid, bssid, ETH_ALEN) != 0 &&
(bss = ieee80211_rx_bss_get(dev, bssid))) {
- printk(KERN_DEBUG "%s: Selected IBSS BSSID " MAC_FMT
+ printk(KERN_DEBUG "%s: Selected IBSS BSSID %s"
" based on configured SSID\n",
- dev->name, MAC_ARG(bssid));
+ dev->name, print_mac(mac, bssid));
return ieee80211_sta_join_ibss(dev, ifsta, bss);
}
#ifdef CONFIG_MAC80211_IBSS_DEBUG
@@ -2451,10 +2495,10 @@ static int ieee80211_sta_find_ibss(struct net_device *dev,
if (time_after(jiffies, ifsta->ibss_join_req +
IEEE80211_IBSS_JOIN_TIMEOUT)) {
- if (ifsta->create_ibss &&
+ if ((ifsta->flags & IEEE80211_STA_CREATE_IBSS) &&
local->oper_channel->flag & IEEE80211_CHAN_W_IBSS)
return ieee80211_sta_create_ibss(dev, ifsta);
- if (ifsta->create_ibss) {
+ if (ifsta->flags & IEEE80211_STA_CREATE_IBSS) {
printk(KERN_DEBUG "%s: IBSS not allowed on the"
" configured channel %d (%d MHz)\n",
dev->name, local->hw.conf.channel,
@@ -2515,13 +2559,17 @@ int ieee80211_sta_set_ssid(struct net_device *dev, char *ssid, size_t len)
ifsta = &sdata->u.sta;
if (ifsta->ssid_len != len || memcmp(ifsta->ssid, ssid, len) != 0)
- ifsta->prev_bssid_set = 0;
+ ifsta->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
memcpy(ifsta->ssid, ssid, len);
memset(ifsta->ssid + len, 0, IEEE80211_MAX_SSID_LEN - len);
ifsta->ssid_len = len;
- ifsta->ssid_set = len ? 1 : 0;
- if (sdata->type == IEEE80211_IF_TYPE_IBSS && !ifsta->bssid_set) {
+ if (len)
+ ifsta->flags |= IEEE80211_STA_SSID_SET;
+ else
+ ifsta->flags &= ~IEEE80211_STA_SSID_SET;
+ if (sdata->type == IEEE80211_IF_TYPE_IBSS &&
+ !(ifsta->flags & IEEE80211_STA_BSSID_SET)) {
ifsta->ibss_join_req = jiffies;
ifsta->state = IEEE80211_IBSS_SEARCH;
return ieee80211_sta_find_ibss(dev, ifsta);
@@ -2559,10 +2607,11 @@ int ieee80211_sta_set_bssid(struct net_device *dev, u8 *bssid)
}
}
- if (!is_valid_ether_addr(bssid))
- ifsta->bssid_set = 0;
+ if (is_valid_ether_addr(bssid))
+ ifsta->flags |= IEEE80211_STA_BSSID_SET;
else
- ifsta->bssid_set = 1;
+ ifsta->flags &= ~IEEE80211_STA_BSSID_SET;
+
return 0;
}
@@ -2613,35 +2662,41 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
printk(KERN_DEBUG "%s: failed to restore operational"
"channel after scan\n", dev->name);
- if (!(local->hw.flags & IEEE80211_HW_NO_PROBE_FILTERING) &&
- ieee80211_if_config(dev))
- printk(KERN_DEBUG "%s: failed to restore operational"
- "BSSID after scan\n", dev->name);
+
+ netif_tx_lock_bh(local->mdev);
+ local->filter_flags &= ~FIF_BCN_PRBRESP_PROMISC;
+ local->ops->configure_filter(local_to_hw(local),
+ FIF_BCN_PRBRESP_PROMISC,
+ &local->filter_flags,
+ local->mdev->mc_count,
+ local->mdev->mc_list);
+
+ netif_tx_unlock_bh(local->mdev);
memset(&wrqu, 0, sizeof(wrqu));
wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
- read_lock(&local->sub_if_lock);
- list_for_each_entry(sdata, &local->sub_if_list, list) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
/* No need to wake the master device. */
if (sdata->dev == local->mdev)
continue;
if (sdata->type == IEEE80211_IF_TYPE_STA) {
- if (sdata->u.sta.associated)
+ if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED)
ieee80211_send_nullfunc(local, sdata, 0);
ieee80211_sta_timer((unsigned long)sdata);
}
netif_wake_queue(sdata->dev);
}
- read_unlock(&local->sub_if_lock);
+ rcu_read_unlock();
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type == IEEE80211_IF_TYPE_IBSS) {
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
- if (!ifsta->bssid_set ||
+ if (!(ifsta->flags & IEEE80211_STA_BSSID_SET) ||
(!ifsta->state == IEEE80211_IBSS_JOINED &&
!ieee80211_sta_active_ibss(dev)))
ieee80211_sta_find_ibss(dev, ifsta);
@@ -2773,8 +2828,8 @@ static int ieee80211_sta_start_scan(struct net_device *dev,
local->sta_scanning = 1;
- read_lock(&local->sub_if_lock);
- list_for_each_entry(sdata, &local->sub_if_list, list) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
/* Don't stop the master interface, otherwise we can't transmit
* probes! */
@@ -2783,10 +2838,10 @@ static int ieee80211_sta_start_scan(struct net_device *dev,
netif_stop_queue(sdata->dev);
if (sdata->type == IEEE80211_IF_TYPE_STA &&
- sdata->u.sta.associated)
+ (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED))
ieee80211_send_nullfunc(local, sdata, 1);
}
- read_unlock(&local->sub_if_lock);
+ rcu_read_unlock();
if (ssid) {
local->scan_ssid_len = ssid_len;
@@ -2800,10 +2855,14 @@ static int ieee80211_sta_start_scan(struct net_device *dev,
local->scan_channel_idx = 0;
local->scan_dev = dev;
- if (!(local->hw.flags & IEEE80211_HW_NO_PROBE_FILTERING) &&
- ieee80211_if_config(dev))
- printk(KERN_DEBUG "%s: failed to set BSSID for scan\n",
- dev->name);
+ netif_tx_lock_bh(local->mdev);
+ local->filter_flags |= FIF_BCN_PRBRESP_PROMISC;
+ local->ops->configure_filter(local_to_hw(local),
+ FIF_BCN_PRBRESP_PROMISC,
+ &local->filter_flags,
+ local->mdev->mc_count,
+ local->mdev->mc_list);
+ netif_tx_unlock_bh(local->mdev);
/* TODO: start scan as soon as all nullfunc frames are ACKed */
queue_delayed_work(local->hw.workqueue, &local->scan_work,
@@ -3041,19 +3100,20 @@ struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev,
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sta_info *sta;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ DECLARE_MAC_BUF(mac);
/* TODO: Could consider removing the least recently used entry and
* allow new one to be added. */
if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) {
if (net_ratelimit()) {
printk(KERN_DEBUG "%s: No room for a new IBSS STA "
- "entry " MAC_FMT "\n", dev->name, MAC_ARG(addr));
+ "entry %s\n", dev->name, print_mac(mac, addr));
}
return NULL;
}
- printk(KERN_DEBUG "%s: Adding new IBSS station " MAC_FMT " (dev=%s)\n",
- local->mdev->name, MAC_ARG(addr), dev->name);
+ printk(KERN_DEBUG "%s: Adding new IBSS station %s (dev=%s)\n",
+ wiphy_name(local->hw.wiphy), print_mac(mac, addr), dev->name);
sta = sta_info_add(local, dev, addr, GFP_ATOMIC);
if (!sta)
@@ -3096,7 +3156,7 @@ int ieee80211_sta_disassociate(struct net_device *dev, u16 reason)
if (sdata->type != IEEE80211_IF_TYPE_STA)
return -EINVAL;
- if (!ifsta->associated)
+ if (!(ifsta->flags & IEEE80211_STA_ASSOCIATED))
return -1;
ieee80211_send_disassoc(dev, ifsta, reason);
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
new file mode 100644
index 000000000000..0b2328f7d67c
--- /dev/null
+++ b/net/mac80211/key.c
@@ -0,0 +1,279 @@
+/*
+ * 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>
+ *
+ * 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.
+ */
+
+#include <linux/if_ether.h>
+#include <linux/etherdevice.h>
+#include <linux/list.h>
+#include <linux/rcupdate.h>
+#include <net/mac80211.h>
+#include "ieee80211_i.h"
+#include "debugfs_key.h"
+#include "aes_ccm.h"
+
+
+/*
+ * Key handling basics
+ *
+ * Key handling in mac80211 is done based on per-interface (sub_if_data)
+ * keys and per-station keys. Since each station belongs to an interface,
+ * each station key also belongs to that interface.
+ *
+ * Hardware acceleration is done on a best-effort basis, for each key
+ * that is eligible the hardware is asked to enable that key but if
+ * it cannot do that they key is simply kept for software encryption.
+ * There is currently no way of knowing this except by looking into
+ * debugfs.
+ *
+ * All operations here are called under RTNL so no extra locking is
+ * required.
+ */
+
+static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+static const u8 zero_addr[ETH_ALEN];
+
+static const u8 *get_mac_for_key(struct ieee80211_key *key)
+{
+ const u8 *addr = bcast_addr;
+
+ /*
+ * If we're an AP we won't ever receive frames with a non-WEP
+ * group key so we tell the driver that by using the zero MAC
+ * address to indicate a transmit-only key.
+ */
+ if (key->conf.alg != ALG_WEP &&
+ (key->sdata->type == IEEE80211_IF_TYPE_AP ||
+ key->sdata->type == IEEE80211_IF_TYPE_VLAN))
+ addr = zero_addr;
+
+ if (key->sta)
+ addr = key->sta->addr;
+
+ return addr;
+}
+
+static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
+{
+ const u8 *addr;
+ int ret;
+ DECLARE_MAC_BUF(mac);
+
+ if (!key->local->ops->set_key)
+ return;
+
+ addr = get_mac_for_key(key);
+
+ ret = key->local->ops->set_key(local_to_hw(key->local), SET_KEY,
+ key->sdata->dev->dev_addr, addr,
+ &key->conf);
+
+ if (!ret)
+ key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE;
+
+ if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP)
+ printk(KERN_ERR "mac80211-%s: failed to set key "
+ "(%d, %s) to hardware (%d)\n",
+ wiphy_name(key->local->hw.wiphy),
+ key->conf.keyidx, print_mac(mac, addr), ret);
+}
+
+static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
+{
+ const u8 *addr;
+ int ret;
+ DECLARE_MAC_BUF(mac);
+
+ if (!key->local->ops->set_key)
+ return;
+
+ if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
+ return;
+
+ addr = get_mac_for_key(key);
+
+ ret = key->local->ops->set_key(local_to_hw(key->local), DISABLE_KEY,
+ key->sdata->dev->dev_addr, addr,
+ &key->conf);
+
+ if (ret)
+ printk(KERN_ERR "mac80211-%s: failed to remove key "
+ "(%d, %s) from hardware (%d)\n",
+ wiphy_name(key->local->hw.wiphy),
+ key->conf.keyidx, print_mac(mac, addr), ret);
+
+ key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
+}
+
+struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ enum ieee80211_key_alg alg,
+ int idx,
+ size_t key_len,
+ const u8 *key_data)
+{
+ struct ieee80211_key *key;
+
+ BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS);
+
+ key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL);
+ if (!key)
+ return NULL;
+
+ /*
+ * Default to software encryption; we'll later upload the
+ * key to the hardware if possible.
+ */
+ key->conf.flags = 0;
+ key->flags = 0;
+
+ key->conf.alg = alg;
+ key->conf.keyidx = idx;
+ key->conf.keylen = key_len;
+ memcpy(key->conf.key, key_data, key_len);
+
+ key->local = sdata->local;
+ key->sdata = sdata;
+ key->sta = sta;
+
+ if (alg == ALG_CCMP) {
+ /*
+ * Initialize AES key state here as an optimization so that
+ * it does not need to be initialized for every packet.
+ */
+ key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(key_data);
+ if (!key->u.ccmp.tfm) {
+ ieee80211_key_free(key);
+ return NULL;
+ }
+ }
+
+ ieee80211_debugfs_key_add(key->local, key);
+
+ /* remove key first */
+ if (sta)
+ ieee80211_key_free(sta->key);
+ else
+ ieee80211_key_free(sdata->keys[idx]);
+
+ if (sta) {
+ ieee80211_debugfs_key_sta_link(key, sta);
+
+ /*
+ * some hardware cannot handle TKIP with QoS, so
+ * we indicate whether QoS could be in use.
+ */
+ if (sta->flags & WLAN_STA_WME)
+ key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA;
+ } else {
+ if (sdata->type == IEEE80211_IF_TYPE_STA) {
+ struct sta_info *ap;
+
+ /* same here, the AP could be using QoS */
+ ap = sta_info_get(key->local, key->sdata->u.sta.bssid);
+ if (ap) {
+ if (ap->flags & WLAN_STA_WME)
+ key->conf.flags |=
+ IEEE80211_KEY_FLAG_WMM_STA;
+ sta_info_put(ap);
+ }
+ }
+ }
+
+ /* enable hwaccel if appropriate */
+ if (netif_running(key->sdata->dev))
+ ieee80211_key_enable_hw_accel(key);
+
+ if (sta)
+ rcu_assign_pointer(sta->key, key);
+ else
+ rcu_assign_pointer(sdata->keys[idx], key);
+
+ list_add(&key->list, &sdata->key_list);
+
+ return key;
+}
+
+void ieee80211_key_free(struct ieee80211_key *key)
+{
+ if (!key)
+ return;
+
+ if (key->sta) {
+ rcu_assign_pointer(key->sta->key, NULL);
+ } else {
+ if (key->sdata->default_key == key)
+ ieee80211_set_default_key(key->sdata, -1);
+ if (key->conf.keyidx >= 0 &&
+ key->conf.keyidx < NUM_DEFAULT_KEYS)
+ rcu_assign_pointer(key->sdata->keys[key->conf.keyidx],
+ NULL);
+ else
+ WARN_ON(1);
+ }
+
+ /* wait for all key users to complete */
+ synchronize_rcu();
+
+ /* remove from hwaccel if appropriate */
+ ieee80211_key_disable_hw_accel(key);
+
+ if (key->conf.alg == ALG_CCMP)
+ ieee80211_aes_key_free(key->u.ccmp.tfm);
+ ieee80211_debugfs_key_remove(key);
+
+ list_del(&key->list);
+
+ kfree(key);
+}
+
+void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx)
+{
+ struct ieee80211_key *key = NULL;
+
+ if (idx >= 0 && idx < NUM_DEFAULT_KEYS)
+ key = sdata->keys[idx];
+
+ if (sdata->default_key != key) {
+ ieee80211_debugfs_key_remove_default(sdata);
+
+ rcu_assign_pointer(sdata->default_key, key);
+
+ if (sdata->default_key)
+ ieee80211_debugfs_key_add_default(sdata);
+ }
+}
+
+void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_key *key, *tmp;
+
+ list_for_each_entry_safe(key, tmp, &sdata->key_list, list)
+ ieee80211_key_free(key);
+}
+
+void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_key *key;
+
+ WARN_ON(!netif_running(sdata->dev));
+ if (!netif_running(sdata->dev))
+ return;
+
+ list_for_each_entry(key, &sdata->key_list, list)
+ ieee80211_key_enable_hw_accel(key);
+}
+
+void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_key *key;
+
+ list_for_each_entry(key, &sdata->key_list, list)
+ ieee80211_key_disable_hw_accel(key);
+}
diff --git a/net/mac80211/rc80211_simple.c b/net/mac80211/rc80211_simple.c
index 17b9f46bbf2b..314b8de88862 100644
--- a/net/mac80211/rc80211_simple.c
+++ b/net/mac80211/rc80211_simple.c
@@ -147,14 +147,6 @@ static void rate_control_simple_tx_status(void *priv, struct net_device *dev,
srctrl = sta->rate_ctrl_priv;
srctrl->tx_num_xmit++;
if (status->excessive_retries) {
- sta->antenna_sel_tx = sta->antenna_sel_tx == 1 ? 2 : 1;
- sta->antenna_sel_rx = sta->antenna_sel_rx == 1 ? 2 : 1;
- if (local->sta_antenna_sel == STA_ANTENNA_SEL_SW_CTRL_DEBUG) {
- printk(KERN_DEBUG "%s: " MAC_FMT " TX antenna --> %d "
- "RX antenna --> %d (@%lu)\n",
- dev->name, MAC_ARG(hdr->addr1),
- sta->antenna_sel_tx, sta->antenna_sel_rx, jiffies);
- }
srctrl->tx_num_failures++;
sta->tx_retry_failed++;
sta->tx_num_consecutive_failures++;
@@ -209,9 +201,10 @@ static void rate_control_simple_tx_status(void *priv, struct net_device *dev,
srctrl->avg_rate_update = jiffies;
if (srctrl->tx_avg_rate_num > 0) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: STA " MAC_FMT " Average rate: "
+ DECLARE_MAC_BUF(mac);
+ printk(KERN_DEBUG "%s: STA %s Average rate: "
"%d (%d/%d)\n",
- dev->name, MAC_ARG(sta->addr),
+ dev->name, print_mac(mac, sta->addr),
srctrl->tx_avg_rate_sum /
srctrl->tx_avg_rate_num,
srctrl->tx_avg_rate_sum,
diff --git a/net/mac80211/regdomain.c b/net/mac80211/regdomain.c
index b697a2afbb4b..f42678fa62d1 100644
--- a/net/mac80211/regdomain.c
+++ b/net/mac80211/regdomain.c
@@ -82,12 +82,6 @@ static void ieee80211_unmask_channel(int mode, struct ieee80211_channel *chan)
chan->flag = 0;
- if (ieee80211_regdom == 64 &&
- (mode == MODE_ATHEROS_TURBO || mode == MODE_ATHEROS_TURBOG)) {
- /* Do not allow Turbo modes in Japan. */
- return;
- }
-
for (i = 0; channel_range[i].start_freq; i++) {
const struct ieee80211_channel_range *r = &channel_range[i];
if (r->start_freq <= chan->freq && r->end_freq >= chan->freq) {
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
new file mode 100644
index 000000000000..ece77766ea2b
--- /dev/null
+++ b/net/mac80211/rx.c
@@ -0,0 +1,1588 @@
+/*
+ * 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>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/rcupdate.h>
+#include <net/mac80211.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "ieee80211_i.h"
+#include "ieee80211_led.h"
+#include "wep.h"
+#include "wpa.h"
+#include "tkip.h"
+#include "wme.h"
+
+/*
+ * monitor mode reception
+ *
+ * This function cleans up the SKB, i.e. it removes all the stuff
+ * only useful for monitoring.
+ */
+static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
+ struct sk_buff *skb,
+ int rtap_len)
+{
+ skb_pull(skb, rtap_len);
+
+ if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) {
+ if (likely(skb->len > FCS_LEN))
+ skb_trim(skb, skb->len - FCS_LEN);
+ else {
+ /* driver bug */
+ WARN_ON(1);
+ dev_kfree_skb(skb);
+ skb = NULL;
+ }
+ }
+
+ return skb;
+}
+
+static inline int should_drop_frame(struct ieee80211_rx_status *status,
+ struct sk_buff *skb,
+ int present_fcs_len,
+ int radiotap_len)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+
+ if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
+ return 1;
+ if (unlikely(skb->len < 16 + present_fcs_len + radiotap_len))
+ return 1;
+ if ((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_FTYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_CTL))
+ return 1;
+ return 0;
+}
+
+/*
+ * This function copies a received frame to all monitor interfaces and
+ * returns a cleaned-up SKB that no longer includes the FCS nor the
+ * radiotap header the driver might have added.
+ */
+static struct sk_buff *
+ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
+ struct ieee80211_rx_status *status)
+{
+ struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_rate *rate;
+ int needed_headroom = 0;
+ struct ieee80211_rtap_hdr {
+ struct ieee80211_radiotap_header hdr;
+ u8 flags;
+ u8 rate;
+ __le16 chan_freq;
+ __le16 chan_flags;
+ u8 antsignal;
+ u8 padding_for_rxflags;
+ __le16 rx_flags;
+ } __attribute__ ((packed)) *rthdr;
+ struct sk_buff *skb, *skb2;
+ struct net_device *prev_dev = NULL;
+ int present_fcs_len = 0;
+ int rtap_len = 0;
+
+ /*
+ * First, we may need to make a copy of the skb because
+ * (1) we need to modify it for radiotap (if not present), and
+ * (2) the other RX handlers will modify the skb we got.
+ *
+ * We don't need to, of course, if we aren't going to return
+ * the SKB because it has a bad FCS/PLCP checksum.
+ */
+ if (status->flag & RX_FLAG_RADIOTAP)
+ rtap_len = ieee80211_get_radiotap_len(origskb->data);
+ else
+ needed_headroom = sizeof(*rthdr);
+
+ if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
+ present_fcs_len = FCS_LEN;
+
+ if (!local->monitors) {
+ if (should_drop_frame(status, origskb, present_fcs_len,
+ rtap_len)) {
+ dev_kfree_skb(origskb);
+ return NULL;
+ }
+
+ return remove_monitor_info(local, origskb, rtap_len);
+ }
+
+ if (should_drop_frame(status, origskb, present_fcs_len, rtap_len)) {
+ /* only need to expand headroom if necessary */
+ skb = origskb;
+ origskb = NULL;
+
+ /*
+ * This shouldn't trigger often because most devices have an
+ * RX header they pull before we get here, and that should
+ * be big enough for our radiotap information. We should
+ * probably export the length to drivers so that we can have
+ * them allocate enough headroom to start with.
+ */
+ if (skb_headroom(skb) < needed_headroom &&
+ pskb_expand_head(skb, sizeof(*rthdr), 0, GFP_ATOMIC)) {
+ dev_kfree_skb(skb);
+ return NULL;
+ }
+ } else {
+ /*
+ * Need to make a copy and possibly remove radiotap header
+ * and FCS from the original.
+ */
+ skb = skb_copy_expand(origskb, needed_headroom, 0, GFP_ATOMIC);
+
+ origskb = remove_monitor_info(local, origskb, rtap_len);
+
+ if (!skb)
+ return origskb;
+ }
+
+ /* if necessary, prepend radiotap information */
+ if (!(status->flag & RX_FLAG_RADIOTAP)) {
+ rthdr = (void *) skb_push(skb, sizeof(*rthdr));
+ memset(rthdr, 0, sizeof(*rthdr));
+ rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr));
+ rthdr->hdr.it_present =
+ cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
+ (1 << IEEE80211_RADIOTAP_RATE) |
+ (1 << IEEE80211_RADIOTAP_CHANNEL) |
+ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |
+ (1 << IEEE80211_RADIOTAP_RX_FLAGS));
+ rthdr->flags = local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS ?
+ IEEE80211_RADIOTAP_F_FCS : 0;
+
+ /* FIXME: when radiotap gets a 'bad PLCP' flag use it here */
+ rthdr->rx_flags = 0;
+ if (status->flag &
+ (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
+ rthdr->rx_flags |=
+ cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADFCS);
+
+ rate = ieee80211_get_rate(local, status->phymode,
+ status->rate);
+ if (rate)
+ rthdr->rate = rate->rate / 5;
+
+ rthdr->chan_freq = cpu_to_le16(status->freq);
+
+ if (status->phymode == MODE_IEEE80211A)
+ rthdr->chan_flags =
+ cpu_to_le16(IEEE80211_CHAN_OFDM |
+ IEEE80211_CHAN_5GHZ);
+ else
+ rthdr->chan_flags =
+ cpu_to_le16(IEEE80211_CHAN_DYN |
+ IEEE80211_CHAN_2GHZ);
+
+ rthdr->antsignal = status->ssi;
+ }
+
+ skb_set_mac_header(skb, 0);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = htons(ETH_P_802_2);
+
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (!netif_running(sdata->dev))
+ continue;
+
+ if (sdata->type != IEEE80211_IF_TYPE_MNTR)
+ continue;
+
+ if (prev_dev) {
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2) {
+ skb2->dev = prev_dev;
+ netif_rx(skb2);
+ }
+ }
+
+ prev_dev = sdata->dev;
+ sdata->dev->stats.rx_packets++;
+ sdata->dev->stats.rx_bytes += skb->len;
+ }
+
+ if (prev_dev) {
+ skb->dev = prev_dev;
+ netif_rx(skb);
+ } else
+ dev_kfree_skb(skb);
+
+ return origskb;
+}
+
+
+/* pre-rx handlers
+ *
+ * these don't have dev/sdata fields in the rx data
+ * The sta value should also not be used because it may
+ * be NULL even though a STA (in IBSS mode) will be added.
+ */
+
+static ieee80211_txrx_result
+ieee80211_rx_h_parse_qos(struct ieee80211_txrx_data *rx)
+{
+ u8 *data = rx->skb->data;
+ int tid;
+
+ /* does the frame have a qos control field? */
+ if (WLAN_FC_IS_QOS_DATA(rx->fc)) {
+ u8 *qc = data + ieee80211_get_hdrlen(rx->fc) - QOS_CONTROL_LEN;
+ /* frame has qos control */
+ tid = qc[0] & QOS_CONTROL_TID_MASK;
+ } else {
+ if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) {
+ /* Separate TID for management frames */
+ tid = NUM_RX_DATA_QUEUES - 1;
+ } else {
+ /* no qos control present */
+ tid = 0; /* 802.1d - Best Effort */
+ }
+ }
+
+ I802_DEBUG_INC(rx->local->wme_rx_queue[tid]);
+ /* only a debug counter, sta might not be assigned properly yet */
+ if (rx->sta)
+ I802_DEBUG_INC(rx->sta->wme_rx_queue[tid]);
+
+ rx->u.rx.queue = tid;
+ /* Set skb->priority to 1d tag if highest order bit of TID is not set.
+ * For now, set skb->priority to 0 for other cases. */
+ rx->skb->priority = (tid > 7) ? 0 : tid;
+
+ return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_load_stats(struct ieee80211_txrx_data *rx)
+{
+ struct ieee80211_local *local = rx->local;
+ struct sk_buff *skb = rx->skb;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ u32 load = 0, hdrtime;
+ struct ieee80211_rate *rate;
+ struct ieee80211_hw_mode *mode = local->hw.conf.mode;
+ int i;
+
+ /* Estimate total channel use caused by this frame */
+
+ if (unlikely(mode->num_rates < 0))
+ return TXRX_CONTINUE;
+
+ rate = &mode->rates[0];
+ for (i = 0; i < mode->num_rates; i++) {
+ if (mode->rates[i].val == rx->u.rx.status->rate) {
+ rate = &mode->rates[i];
+ break;
+ }
+ }
+
+ /* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values,
+ * 1 usec = 1/8 * (1080 / 10) = 13.5 */
+
+ if (mode->mode == MODE_IEEE80211A ||
+ (mode->mode == MODE_IEEE80211G &&
+ rate->flags & IEEE80211_RATE_ERP))
+ hdrtime = CHAN_UTIL_HDR_SHORT;
+ else
+ hdrtime = CHAN_UTIL_HDR_LONG;
+
+ load = hdrtime;
+ if (!is_multicast_ether_addr(hdr->addr1))
+ load += hdrtime;
+
+ load += skb->len * rate->rate_inv;
+
+ /* Divide channel_use by 8 to avoid wrapping around the counter */
+ load >>= CHAN_UTIL_SHIFT;
+ local->channel_use_raw += load;
+ rx->u.rx.load = load;
+
+ return TXRX_CONTINUE;
+}
+
+ieee80211_rx_handler ieee80211_rx_pre_handlers[] =
+{
+ ieee80211_rx_h_parse_qos,
+ ieee80211_rx_h_load_stats,
+ NULL
+};
+
+/* rx handlers */
+
+static ieee80211_txrx_result
+ieee80211_rx_h_if_stats(struct ieee80211_txrx_data *rx)
+{
+ if (rx->sta)
+ rx->sta->channel_use_raw += rx->u.rx.load;
+ rx->sdata->channel_use_raw += rx->u.rx.load;
+ return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx)
+{
+ struct ieee80211_local *local = rx->local;
+ struct sk_buff *skb = rx->skb;
+
+ if (unlikely(local->sta_scanning != 0)) {
+ ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status);
+ return TXRX_QUEUED;
+ }
+
+ if (unlikely(rx->flags & IEEE80211_TXRXD_RXIN_SCAN)) {
+ /* scanning finished during invoking of handlers */
+ I802_DEBUG_INC(local->rx_handlers_drop_passive_scan);
+ return TXRX_DROP;
+ }
+
+ return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
+{
+ struct ieee80211_hdr *hdr;
+ hdr = (struct ieee80211_hdr *) rx->skb->data;
+
+ /* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */
+ if (rx->sta && !is_multicast_ether_addr(hdr->addr1)) {
+ if (unlikely(rx->fc & IEEE80211_FCTL_RETRY &&
+ rx->sta->last_seq_ctrl[rx->u.rx.queue] ==
+ hdr->seq_ctrl)) {
+ if (rx->flags & IEEE80211_TXRXD_RXRA_MATCH) {
+ rx->local->dot11FrameDuplicateCount++;
+ rx->sta->num_duplicates++;
+ }
+ return TXRX_DROP;
+ } else
+ rx->sta->last_seq_ctrl[rx->u.rx.queue] = hdr->seq_ctrl;
+ }
+
+ if (unlikely(rx->skb->len < 16)) {
+ I802_DEBUG_INC(rx->local->rx_handlers_drop_short);
+ return TXRX_DROP;
+ }
+
+ if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
+ rx->skb->pkt_type = PACKET_OTHERHOST;
+ else if (compare_ether_addr(rx->dev->dev_addr, hdr->addr1) == 0)
+ rx->skb->pkt_type = PACKET_HOST;
+ else if (is_multicast_ether_addr(hdr->addr1)) {
+ if (is_broadcast_ether_addr(hdr->addr1))
+ rx->skb->pkt_type = PACKET_BROADCAST;
+ else
+ rx->skb->pkt_type = PACKET_MULTICAST;
+ } else
+ rx->skb->pkt_type = PACKET_OTHERHOST;
+
+ /* Drop disallowed frame classes based on STA auth/assoc state;
+ * IEEE 802.11, Chap 5.5.
+ *
+ * 80211.o does filtering only based on association state, i.e., it
+ * drops Class 3 frames from not associated stations. hostapd sends
+ * deauth/disassoc frames when needed. In addition, hostapd is
+ * responsible for filtering on both auth and assoc states.
+ */
+ if (unlikely(((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA ||
+ ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL &&
+ (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)) &&
+ rx->sdata->type != IEEE80211_IF_TYPE_IBSS &&
+ (!rx->sta || !(rx->sta->flags & WLAN_STA_ASSOC)))) {
+ if ((!(rx->fc & IEEE80211_FCTL_FROMDS) &&
+ !(rx->fc & IEEE80211_FCTL_TODS) &&
+ (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)
+ || !(rx->flags & IEEE80211_TXRXD_RXRA_MATCH)) {
+ /* Drop IBSS frames and frames for other hosts
+ * silently. */
+ return TXRX_DROP;
+ }
+
+ return TXRX_DROP;
+ }
+
+ return TXRX_CONTINUE;
+}
+
+
+static ieee80211_txrx_result
+ieee80211_rx_h_decrypt(struct ieee80211_txrx_data *rx)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
+ int keyidx;
+ int hdrlen;
+ ieee80211_txrx_result result = TXRX_DROP;
+ struct ieee80211_key *stakey = NULL;
+
+ /*
+ * Key selection 101
+ *
+ * There are three types of keys:
+ * - GTK (group keys)
+ * - PTK (pairwise keys)
+ * - STK (station-to-station pairwise keys)
+ *
+ * When selecting a key, we have to distinguish between multicast
+ * (including broadcast) and unicast frames, the latter can only
+ * use PTKs and STKs while the former always use GTKs. Unless, of
+ * course, actual WEP keys ("pre-RSNA") are used, then unicast
+ * frames can also use key indizes like GTKs. Hence, if we don't
+ * have a PTK/STK we check the key index for a WEP key.
+ *
+ * Note that in a regular BSS, multicast frames are sent by the
+ * AP only, associated stations unicast the frame to the AP first
+ * which then multicasts it on their behalf.
+ *
+ * There is also a slight problem in IBSS mode: GTKs are negotiated
+ * with each station, that is something we don't currently handle.
+ * The spec seems to expect that one negotiates the same key with
+ * every station but there's no such requirement; VLANs could be
+ * possible.
+ */
+
+ if (!(rx->fc & IEEE80211_FCTL_PROTECTED))
+ return TXRX_CONTINUE;
+
+ /*
+ * No point in finding a key and decrypting if the frame is neither
+ * addressed to us nor a multicast frame.
+ */
+ if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
+ return TXRX_CONTINUE;
+
+ if (rx->sta)
+ stakey = rcu_dereference(rx->sta->key);
+
+ if (!is_multicast_ether_addr(hdr->addr1) && stakey) {
+ rx->key = stakey;
+ } else {
+ /*
+ * The device doesn't give us the IV so we won't be
+ * able to look up the key. That's ok though, we
+ * don't need to decrypt the frame, we just won't
+ * be able to keep statistics accurate.
+ * Except for key threshold notifications, should
+ * we somehow allow the driver to tell us which key
+ * the hardware used if this flag is set?
+ */
+ if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) &&
+ (rx->u.rx.status->flag & RX_FLAG_IV_STRIPPED))
+ return TXRX_CONTINUE;
+
+ hdrlen = ieee80211_get_hdrlen(rx->fc);
+
+ if (rx->skb->len < 8 + hdrlen)
+ return TXRX_DROP; /* TODO: count this? */
+
+ /*
+ * no need to call ieee80211_wep_get_keyidx,
+ * it verifies a bunch of things we've done already
+ */
+ keyidx = rx->skb->data[hdrlen + 3] >> 6;
+
+ rx->key = rcu_dereference(rx->sdata->keys[keyidx]);
+
+ /*
+ * RSNA-protected unicast frames should always be sent with
+ * pairwise or station-to-station keys, but for WEP we allow
+ * using a key index as well.
+ */
+ if (rx->key && rx->key->conf.alg != ALG_WEP &&
+ !is_multicast_ether_addr(hdr->addr1))
+ rx->key = NULL;
+ }
+
+ if (rx->key) {
+ rx->key->tx_rx_count++;
+ /* TODO: add threshold stuff again */
+ } else {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "%s: RX protected frame,"
+ " but have no key\n", rx->dev->name);
+ return TXRX_DROP;
+ }
+
+ /* Check for weak IVs if possible */
+ if (rx->sta && rx->key->conf.alg == ALG_WEP &&
+ ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) &&
+ (!(rx->u.rx.status->flag & RX_FLAG_IV_STRIPPED) ||
+ !(rx->u.rx.status->flag & RX_FLAG_DECRYPTED)) &&
+ ieee80211_wep_is_weak_iv(rx->skb, rx->key))
+ rx->sta->wep_weak_iv_count++;
+
+ switch (rx->key->conf.alg) {
+ case ALG_WEP:
+ result = ieee80211_crypto_wep_decrypt(rx);
+ break;
+ case ALG_TKIP:
+ result = ieee80211_crypto_tkip_decrypt(rx);
+ break;
+ case ALG_CCMP:
+ result = ieee80211_crypto_ccmp_decrypt(rx);
+ break;
+ }
+
+ /* either the frame has been decrypted or will be dropped */
+ rx->u.rx.status->flag |= RX_FLAG_DECRYPTED;
+
+ return result;
+}
+
+static void ap_sta_ps_start(struct net_device *dev, struct sta_info *sta)
+{
+ struct ieee80211_sub_if_data *sdata;
+ DECLARE_MAC_BUF(mac);
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+
+ if (sdata->bss)
+ atomic_inc(&sdata->bss->num_sta_ps);
+ sta->flags |= WLAN_STA_PS;
+ sta->pspoll = 0;
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+ printk(KERN_DEBUG "%s: STA %s aid %d enters power save mode\n",
+ dev->name, print_mac(mac, sta->addr), sta->aid);
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+}
+
+static int ap_sta_ps_end(struct net_device *dev, struct sta_info *sta)
+{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct sk_buff *skb;
+ int sent = 0;
+ struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_tx_packet_data *pkt_data;
+ DECLARE_MAC_BUF(mac);
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+ if (sdata->bss)
+ atomic_dec(&sdata->bss->num_sta_ps);
+ sta->flags &= ~(WLAN_STA_PS | WLAN_STA_TIM);
+ sta->pspoll = 0;
+ if (!skb_queue_empty(&sta->ps_tx_buf)) {
+ if (local->ops->set_tim)
+ local->ops->set_tim(local_to_hw(local), sta->aid, 0);
+ if (sdata->bss)
+ bss_tim_clear(local, sdata->bss, sta->aid);
+ }
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+ printk(KERN_DEBUG "%s: STA %s aid %d exits power save mode\n",
+ dev->name, print_mac(mac, sta->addr), sta->aid);
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+ /* Send all buffered frames to the station */
+ while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
+ pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
+ sent++;
+ pkt_data->flags |= IEEE80211_TXPD_REQUEUE;
+ dev_queue_xmit(skb);
+ }
+ while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
+ pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
+ local->total_ps_buffered--;
+ sent++;
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+ printk(KERN_DEBUG "%s: STA %s aid %d send PS frame "
+ "since STA not sleeping anymore\n", dev->name,
+ print_mac(mac, sta->addr), sta->aid);
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+ pkt_data->flags |= IEEE80211_TXPD_REQUEUE;
+ dev_queue_xmit(skb);
+ }
+
+ return sent;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_sta_process(struct ieee80211_txrx_data *rx)
+{
+ struct sta_info *sta = rx->sta;
+ struct net_device *dev = rx->dev;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
+
+ if (!sta)
+ return TXRX_CONTINUE;
+
+ /* Update last_rx only for IBSS packets which are for the current
+ * BSSID to avoid keeping the current IBSS network alive in cases where
+ * other STAs are using different BSSID. */
+ if (rx->sdata->type == IEEE80211_IF_TYPE_IBSS) {
+ u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len);
+ if (compare_ether_addr(bssid, rx->sdata->u.sta.bssid) == 0)
+ sta->last_rx = jiffies;
+ } else
+ if (!is_multicast_ether_addr(hdr->addr1) ||
+ rx->sdata->type == IEEE80211_IF_TYPE_STA) {
+ /* Update last_rx only for unicast frames in order to prevent
+ * the Probe Request frames (the only broadcast frames from a
+ * STA in infrastructure mode) from keeping a connection alive.
+ */
+ sta->last_rx = jiffies;
+ }
+
+ if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
+ return TXRX_CONTINUE;
+
+ sta->rx_fragments++;
+ sta->rx_bytes += rx->skb->len;
+ sta->last_rssi = rx->u.rx.status->ssi;
+ sta->last_signal = rx->u.rx.status->signal;
+ sta->last_noise = rx->u.rx.status->noise;
+
+ if (!(rx->fc & IEEE80211_FCTL_MOREFRAGS)) {
+ /* Change STA power saving mode only in the end of a frame
+ * exchange sequence */
+ if ((sta->flags & WLAN_STA_PS) && !(rx->fc & IEEE80211_FCTL_PM))
+ rx->u.rx.sent_ps_buffered += ap_sta_ps_end(dev, sta);
+ else if (!(sta->flags & WLAN_STA_PS) &&
+ (rx->fc & IEEE80211_FCTL_PM))
+ ap_sta_ps_start(dev, sta);
+ }
+
+ /* Drop data::nullfunc frames silently, since they are used only to
+ * control station power saving mode. */
+ if ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
+ (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_NULLFUNC) {
+ I802_DEBUG_INC(rx->local->rx_handlers_drop_nullfunc);
+ /* Update counter and free packet here to avoid counting this
+ * as a dropped packed. */
+ sta->rx_packets++;
+ dev_kfree_skb(rx->skb);
+ return TXRX_QUEUED;
+ }
+
+ return TXRX_CONTINUE;
+} /* ieee80211_rx_h_sta_process */
+
+static inline struct ieee80211_fragment_entry *
+ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
+ unsigned int frag, unsigned int seq, int rx_queue,
+ struct sk_buff **skb)
+{
+ struct ieee80211_fragment_entry *entry;
+ int idx;
+
+ idx = sdata->fragment_next;
+ entry = &sdata->fragments[sdata->fragment_next++];
+ if (sdata->fragment_next >= IEEE80211_FRAGMENT_MAX)
+ sdata->fragment_next = 0;
+
+ if (!skb_queue_empty(&entry->skb_list)) {
+#ifdef CONFIG_MAC80211_DEBUG
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *) entry->skb_list.next->data;
+ DECLARE_MAC_BUF(mac);
+ DECLARE_MAC_BUF(mac2);
+ printk(KERN_DEBUG "%s: RX reassembly removed oldest "
+ "fragment entry (idx=%d age=%lu seq=%d last_frag=%d "
+ "addr1=%s addr2=%s\n",
+ sdata->dev->name, idx,
+ jiffies - entry->first_frag_time, entry->seq,
+ entry->last_frag, print_mac(mac, hdr->addr1),
+ print_mac(mac2, hdr->addr2));
+#endif /* CONFIG_MAC80211_DEBUG */
+ __skb_queue_purge(&entry->skb_list);
+ }
+
+ __skb_queue_tail(&entry->skb_list, *skb); /* no need for locking */
+ *skb = NULL;
+ entry->first_frag_time = jiffies;
+ entry->seq = seq;
+ entry->rx_queue = rx_queue;
+ entry->last_frag = frag;
+ entry->ccmp = 0;
+ entry->extra_len = 0;
+
+ return entry;
+}
+
+static inline struct ieee80211_fragment_entry *
+ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata,
+ u16 fc, unsigned int frag, unsigned int seq,
+ int rx_queue, struct ieee80211_hdr *hdr)
+{
+ struct ieee80211_fragment_entry *entry;
+ int i, idx;
+
+ idx = sdata->fragment_next;
+ for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) {
+ struct ieee80211_hdr *f_hdr;
+ u16 f_fc;
+
+ idx--;
+ if (idx < 0)
+ idx = IEEE80211_FRAGMENT_MAX - 1;
+
+ entry = &sdata->fragments[idx];
+ if (skb_queue_empty(&entry->skb_list) || entry->seq != seq ||
+ entry->rx_queue != rx_queue ||
+ entry->last_frag + 1 != frag)
+ continue;
+
+ f_hdr = (struct ieee80211_hdr *) entry->skb_list.next->data;
+ f_fc = le16_to_cpu(f_hdr->frame_control);
+
+ if ((fc & IEEE80211_FCTL_FTYPE) != (f_fc & IEEE80211_FCTL_FTYPE) ||
+ compare_ether_addr(hdr->addr1, f_hdr->addr1) != 0 ||
+ compare_ether_addr(hdr->addr2, f_hdr->addr2) != 0)
+ continue;
+
+ if (entry->first_frag_time + 2 * HZ < jiffies) {
+ __skb_queue_purge(&entry->skb_list);
+ continue;
+ }
+ return entry;
+ }
+
+ return NULL;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx)
+{
+ struct ieee80211_hdr *hdr;
+ u16 sc;
+ unsigned int frag, seq;
+ struct ieee80211_fragment_entry *entry;
+ struct sk_buff *skb;
+ DECLARE_MAC_BUF(mac);
+
+ hdr = (struct ieee80211_hdr *) rx->skb->data;
+ sc = le16_to_cpu(hdr->seq_ctrl);
+ frag = sc & IEEE80211_SCTL_FRAG;
+
+ if (likely((!(rx->fc & IEEE80211_FCTL_MOREFRAGS) && frag == 0) ||
+ (rx->skb)->len < 24 ||
+ is_multicast_ether_addr(hdr->addr1))) {
+ /* not fragmented */
+ goto out;
+ }
+ I802_DEBUG_INC(rx->local->rx_handlers_fragments);
+
+ seq = (sc & IEEE80211_SCTL_SEQ) >> 4;
+
+ if (frag == 0) {
+ /* This is the first fragment of a new frame. */
+ entry = ieee80211_reassemble_add(rx->sdata, frag, seq,
+ rx->u.rx.queue, &(rx->skb));
+ if (rx->key && rx->key->conf.alg == ALG_CCMP &&
+ (rx->fc & IEEE80211_FCTL_PROTECTED)) {
+ /* Store CCMP PN so that we can verify that the next
+ * fragment has a sequential PN value. */
+ entry->ccmp = 1;
+ memcpy(entry->last_pn,
+ rx->key->u.ccmp.rx_pn[rx->u.rx.queue],
+ CCMP_PN_LEN);
+ }
+ return TXRX_QUEUED;
+ }
+
+ /* This is a fragment for a frame that should already be pending in
+ * fragment cache. Add this fragment to the end of the pending entry.
+ */
+ entry = ieee80211_reassemble_find(rx->sdata, rx->fc, frag, seq,
+ rx->u.rx.queue, hdr);
+ if (!entry) {
+ I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
+ return TXRX_DROP;
+ }
+
+ /* Verify that MPDUs within one MSDU have sequential PN values.
+ * (IEEE 802.11i, 8.3.3.4.5) */
+ if (entry->ccmp) {
+ int i;
+ u8 pn[CCMP_PN_LEN], *rpn;
+ if (!rx->key || rx->key->conf.alg != ALG_CCMP)
+ return TXRX_DROP;
+ memcpy(pn, entry->last_pn, CCMP_PN_LEN);
+ for (i = CCMP_PN_LEN - 1; i >= 0; i--) {
+ pn[i]++;
+ if (pn[i])
+ break;
+ }
+ rpn = rx->key->u.ccmp.rx_pn[rx->u.rx.queue];
+ if (memcmp(pn, rpn, CCMP_PN_LEN) != 0) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "%s: defrag: CCMP PN not "
+ "sequential A2=%s"
+ " PN=%02x%02x%02x%02x%02x%02x "
+ "(expected %02x%02x%02x%02x%02x%02x)\n",
+ rx->dev->name, print_mac(mac, hdr->addr2),
+ rpn[0], rpn[1], rpn[2], rpn[3], rpn[4],
+ rpn[5], pn[0], pn[1], pn[2], pn[3],
+ pn[4], pn[5]);
+ return TXRX_DROP;
+ }
+ memcpy(entry->last_pn, pn, CCMP_PN_LEN);
+ }
+
+ skb_pull(rx->skb, ieee80211_get_hdrlen(rx->fc));
+ __skb_queue_tail(&entry->skb_list, rx->skb);
+ entry->last_frag = frag;
+ entry->extra_len += rx->skb->len;
+ if (rx->fc & IEEE80211_FCTL_MOREFRAGS) {
+ rx->skb = NULL;
+ return TXRX_QUEUED;
+ }
+
+ rx->skb = __skb_dequeue(&entry->skb_list);
+ if (skb_tailroom(rx->skb) < entry->extra_len) {
+ I802_DEBUG_INC(rx->local->rx_expand_skb_head2);
+ if (unlikely(pskb_expand_head(rx->skb, 0, entry->extra_len,
+ GFP_ATOMIC))) {
+ I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
+ __skb_queue_purge(&entry->skb_list);
+ return TXRX_DROP;
+ }
+ }
+ while ((skb = __skb_dequeue(&entry->skb_list))) {
+ memcpy(skb_put(rx->skb, skb->len), skb->data, skb->len);
+ dev_kfree_skb(skb);
+ }
+
+ /* Complete frame has been reassembled - process it now */
+ rx->flags |= IEEE80211_TXRXD_FRAGMENTED;
+
+ out:
+ if (rx->sta)
+ rx->sta->rx_packets++;
+ if (is_multicast_ether_addr(hdr->addr1))
+ rx->local->dot11MulticastReceivedFrameCount++;
+ else
+ ieee80211_led_rx(rx->local);
+ return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx)
+{
+ struct sk_buff *skb;
+ int no_pending_pkts;
+ DECLARE_MAC_BUF(mac);
+
+ if (likely(!rx->sta ||
+ (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_CTL ||
+ (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PSPOLL ||
+ !(rx->flags & IEEE80211_TXRXD_RXRA_MATCH)))
+ return TXRX_CONTINUE;
+
+ skb = skb_dequeue(&rx->sta->tx_filtered);
+ if (!skb) {
+ skb = skb_dequeue(&rx->sta->ps_tx_buf);
+ if (skb)
+ rx->local->total_ps_buffered--;
+ }
+ no_pending_pkts = skb_queue_empty(&rx->sta->tx_filtered) &&
+ skb_queue_empty(&rx->sta->ps_tx_buf);
+
+ if (skb) {
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *) skb->data;
+
+ /* tell TX path to send one frame even though the STA may
+ * still remain is PS mode after this frame exchange */
+ rx->sta->pspoll = 1;
+
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+ printk(KERN_DEBUG "STA %s aid %d: PS Poll (entries after %d)\n",
+ print_mac(mac, rx->sta->addr), rx->sta->aid,
+ skb_queue_len(&rx->sta->ps_tx_buf));
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+
+ /* Use MoreData flag to indicate whether there are more
+ * buffered frames for this STA */
+ if (no_pending_pkts) {
+ hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
+ rx->sta->flags &= ~WLAN_STA_TIM;
+ } else
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+
+ dev_queue_xmit(skb);
+
+ if (no_pending_pkts) {
+ if (rx->local->ops->set_tim)
+ rx->local->ops->set_tim(local_to_hw(rx->local),
+ rx->sta->aid, 0);
+ if (rx->sdata->bss)
+ bss_tim_clear(rx->local, rx->sdata->bss, rx->sta->aid);
+ }
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+ } else if (!rx->u.rx.sent_ps_buffered) {
+ printk(KERN_DEBUG "%s: STA %s sent PS Poll even "
+ "though there is no buffered frames for it\n",
+ rx->dev->name, print_mac(mac, rx->sta->addr));
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+
+ }
+
+ /* Free PS Poll skb here instead of returning TXRX_DROP that would
+ * count as an dropped frame. */
+ dev_kfree_skb(rx->skb);
+
+ return TXRX_QUEUED;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx)
+{
+ u16 fc = rx->fc;
+ u8 *data = rx->skb->data;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) data;
+
+ if (!WLAN_FC_IS_QOS_DATA(fc))
+ return TXRX_CONTINUE;
+
+ /* remove the qos control field, update frame type and meta-data */
+ memmove(data + 2, data, ieee80211_get_hdrlen(fc) - 2);
+ hdr = (struct ieee80211_hdr *) skb_pull(rx->skb, 2);
+ /* change frame type to non QOS */
+ rx->fc = fc &= ~IEEE80211_STYPE_QOS_DATA;
+ hdr->frame_control = cpu_to_le16(fc);
+
+ return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_802_1x_pae(struct ieee80211_txrx_data *rx)
+{
+ if (rx->sdata->eapol && ieee80211_is_eapol(rx->skb) &&
+ rx->sdata->type != IEEE80211_IF_TYPE_STA &&
+ (rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
+ return TXRX_CONTINUE;
+
+ if (unlikely(rx->sdata->ieee802_1x &&
+ (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
+ (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_NULLFUNC &&
+ (!rx->sta || !(rx->sta->flags & WLAN_STA_AUTHORIZED)) &&
+ !ieee80211_is_eapol(rx->skb))) {
+#ifdef CONFIG_MAC80211_DEBUG
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *) rx->skb->data;
+ DECLARE_MAC_BUF(mac);
+ printk(KERN_DEBUG "%s: dropped frame from %s"
+ " (unauthorized port)\n", rx->dev->name,
+ print_mac(mac, hdr->addr2));
+#endif /* CONFIG_MAC80211_DEBUG */
+ return TXRX_DROP;
+ }
+
+ return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_drop_unencrypted(struct ieee80211_txrx_data *rx)
+{
+ /*
+ * Pass through unencrypted frames if the hardware has
+ * decrypted them already.
+ */
+ if (rx->u.rx.status->flag & RX_FLAG_DECRYPTED)
+ return TXRX_CONTINUE;
+
+ /* Drop unencrypted frames if key is set. */
+ if (unlikely(!(rx->fc & IEEE80211_FCTL_PROTECTED) &&
+ (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
+ (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_NULLFUNC &&
+ rx->sdata->drop_unencrypted &&
+ (rx->sdata->eapol == 0 || !ieee80211_is_eapol(rx->skb)))) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "%s: RX non-WEP frame, but expected "
+ "encryption\n", rx->dev->name);
+ return TXRX_DROP;
+ }
+ return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
+{
+ struct net_device *dev = rx->dev;
+ struct ieee80211_local *local = rx->local;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
+ u16 fc, hdrlen, ethertype;
+ u8 *payload;
+ u8 dst[ETH_ALEN];
+ u8 src[ETH_ALEN];
+ struct sk_buff *skb = rx->skb, *skb2;
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ DECLARE_MAC_BUF(mac);
+ DECLARE_MAC_BUF(mac2);
+ DECLARE_MAC_BUF(mac3);
+ DECLARE_MAC_BUF(mac4);
+
+ fc = rx->fc;
+ if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA))
+ return TXRX_CONTINUE;
+
+ if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
+ return TXRX_DROP;
+
+ hdrlen = ieee80211_get_hdrlen(fc);
+
+ /* convert IEEE 802.11 header + possible LLC headers into Ethernet
+ * header
+ * IEEE 802.11 address fields:
+ * ToDS FromDS Addr1 Addr2 Addr3 Addr4
+ * 0 0 DA SA BSSID n/a
+ * 0 1 DA BSSID SA n/a
+ * 1 0 BSSID SA DA n/a
+ * 1 1 RA TA DA SA
+ */
+
+ switch (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
+ case IEEE80211_FCTL_TODS:
+ /* BSSID SA DA */
+ memcpy(dst, hdr->addr3, ETH_ALEN);
+ memcpy(src, hdr->addr2, ETH_ALEN);
+
+ if (unlikely(sdata->type != IEEE80211_IF_TYPE_AP &&
+ sdata->type != IEEE80211_IF_TYPE_VLAN)) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "%s: dropped ToDS frame "
+ "(BSSID=%s SA=%s DA=%s)\n",
+ dev->name,
+ print_mac(mac, hdr->addr1),
+ print_mac(mac2, hdr->addr2),
+ print_mac(mac3, hdr->addr3));
+ return TXRX_DROP;
+ }
+ break;
+ case (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
+ /* RA TA DA SA */
+ memcpy(dst, hdr->addr3, ETH_ALEN);
+ memcpy(src, hdr->addr4, ETH_ALEN);
+
+ if (unlikely(sdata->type != IEEE80211_IF_TYPE_WDS)) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "%s: dropped FromDS&ToDS "
+ "frame (RA=%s TA=%s DA=%s SA=%s)\n",
+ rx->dev->name,
+ print_mac(mac, hdr->addr1),
+ print_mac(mac2, hdr->addr2),
+ print_mac(mac3, hdr->addr3),
+ print_mac(mac4, hdr->addr4));
+ return TXRX_DROP;
+ }
+ break;
+ case IEEE80211_FCTL_FROMDS:
+ /* DA BSSID SA */
+ memcpy(dst, hdr->addr1, ETH_ALEN);
+ memcpy(src, hdr->addr3, ETH_ALEN);
+
+ if (sdata->type != IEEE80211_IF_TYPE_STA ||
+ (is_multicast_ether_addr(dst) &&
+ !compare_ether_addr(src, dev->dev_addr)))
+ return TXRX_DROP;
+ break;
+ case 0:
+ /* DA SA BSSID */
+ memcpy(dst, hdr->addr1, ETH_ALEN);
+ memcpy(src, hdr->addr2, ETH_ALEN);
+
+ if (sdata->type != IEEE80211_IF_TYPE_IBSS) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: dropped IBSS frame "
+ "(DA=%s SA=%s BSSID=%s)\n",
+ dev->name,
+ print_mac(mac, hdr->addr1),
+ print_mac(mac2, hdr->addr2),
+ print_mac(mac3, hdr->addr3));
+ }
+ return TXRX_DROP;
+ }
+ break;
+ }
+
+ payload = skb->data + hdrlen;
+
+ if (unlikely(skb->len - hdrlen < 8)) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: RX too short data frame "
+ "payload\n", dev->name);
+ }
+ return TXRX_DROP;
+ }
+
+ ethertype = (payload[6] << 8) | payload[7];
+
+ if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
+ ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
+ compare_ether_addr(payload, bridge_tunnel_header) == 0)) {
+ /* remove RFC1042 or Bridge-Tunnel encapsulation and
+ * replace EtherType */
+ skb_pull(skb, hdrlen + 6);
+ memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
+ memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
+ } else {
+ struct ethhdr *ehdr;
+ __be16 len;
+ skb_pull(skb, hdrlen);
+ len = htons(skb->len);
+ ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
+ memcpy(ehdr->h_dest, dst, ETH_ALEN);
+ memcpy(ehdr->h_source, src, ETH_ALEN);
+ ehdr->h_proto = len;
+ }
+ skb->dev = dev;
+
+ skb2 = NULL;
+
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += skb->len;
+
+ if (local->bridge_packets && (sdata->type == IEEE80211_IF_TYPE_AP
+ || sdata->type == IEEE80211_IF_TYPE_VLAN) &&
+ (rx->flags & IEEE80211_TXRXD_RXRA_MATCH)) {
+ if (is_multicast_ether_addr(skb->data)) {
+ /* send multicast frames both to higher layers in
+ * local net stack and back to the wireless media */
+ skb2 = skb_copy(skb, GFP_ATOMIC);
+ if (!skb2 && net_ratelimit())
+ printk(KERN_DEBUG "%s: failed to clone "
+ "multicast frame\n", dev->name);
+ } else {
+ struct sta_info *dsta;
+ dsta = sta_info_get(local, skb->data);
+ if (dsta && !dsta->dev) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "Station with null "
+ "dev structure!\n");
+ } else if (dsta && dsta->dev == dev) {
+ /* Destination station is associated to this
+ * AP, so send the frame directly to it and
+ * do not pass the frame to local net stack.
+ */
+ skb2 = skb;
+ skb = NULL;
+ }
+ if (dsta)
+ sta_info_put(dsta);
+ }
+ }
+
+ if (skb) {
+ /* deliver to local stack */
+ skb->protocol = eth_type_trans(skb, dev);
+ memset(skb->cb, 0, sizeof(skb->cb));
+ netif_rx(skb);
+ }
+
+ if (skb2) {
+ /* send to wireless media */
+ skb2->protocol = __constant_htons(ETH_P_802_3);
+ skb_set_network_header(skb2, 0);
+ skb_set_mac_header(skb2, 0);
+ dev_queue_xmit(skb2);
+ }
+
+ return TXRX_QUEUED;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
+ return TXRX_DROP;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
+ if ((sdata->type == IEEE80211_IF_TYPE_STA ||
+ sdata->type == IEEE80211_IF_TYPE_IBSS) &&
+ !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME))
+ ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status);
+ else
+ return TXRX_DROP;
+
+ return TXRX_QUEUED;
+}
+
+static inline ieee80211_txrx_result __ieee80211_invoke_rx_handlers(
+ struct ieee80211_local *local,
+ ieee80211_rx_handler *handlers,
+ struct ieee80211_txrx_data *rx,
+ struct sta_info *sta)
+{
+ ieee80211_rx_handler *handler;
+ ieee80211_txrx_result res = TXRX_DROP;
+
+ for (handler = handlers; *handler != NULL; handler++) {
+ res = (*handler)(rx);
+
+ switch (res) {
+ case TXRX_CONTINUE:
+ continue;
+ case TXRX_DROP:
+ I802_DEBUG_INC(local->rx_handlers_drop);
+ if (sta)
+ sta->rx_dropped++;
+ break;
+ case TXRX_QUEUED:
+ I802_DEBUG_INC(local->rx_handlers_queued);
+ break;
+ }
+ break;
+ }
+
+ if (res == TXRX_DROP)
+ dev_kfree_skb(rx->skb);
+ return res;
+}
+
+static inline void ieee80211_invoke_rx_handlers(struct ieee80211_local *local,
+ ieee80211_rx_handler *handlers,
+ struct ieee80211_txrx_data *rx,
+ struct sta_info *sta)
+{
+ if (__ieee80211_invoke_rx_handlers(local, handlers, rx, sta) ==
+ TXRX_CONTINUE)
+ dev_kfree_skb(rx->skb);
+}
+
+static void ieee80211_rx_michael_mic_report(struct net_device *dev,
+ struct ieee80211_hdr *hdr,
+ struct sta_info *sta,
+ struct ieee80211_txrx_data *rx)
+{
+ int keyidx, hdrlen;
+ DECLARE_MAC_BUF(mac);
+ DECLARE_MAC_BUF(mac2);
+
+ hdrlen = ieee80211_get_hdrlen_from_skb(rx->skb);
+ if (rx->skb->len >= hdrlen + 4)
+ keyidx = rx->skb->data[hdrlen + 3] >> 6;
+ else
+ keyidx = -1;
+
+ if (net_ratelimit())
+ printk(KERN_DEBUG "%s: TKIP hwaccel reported Michael MIC "
+ "failure from %s to %s keyidx=%d\n",
+ dev->name, print_mac(mac, hdr->addr2),
+ print_mac(mac2, hdr->addr1), keyidx);
+
+ if (!sta) {
+ /*
+ * Some hardware seem to generate incorrect Michael MIC
+ * reports; ignore them to avoid triggering countermeasures.
+ */
+ if (net_ratelimit())
+ printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
+ "error for unknown address %s\n",
+ dev->name, print_mac(mac, hdr->addr2));
+ goto ignore;
+ }
+
+ if (!(rx->fc & IEEE80211_FCTL_PROTECTED)) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
+ "error for a frame with no PROTECTED flag (src "
+ "%s)\n", dev->name, print_mac(mac, hdr->addr2));
+ goto ignore;
+ }
+
+ if (rx->sdata->type == IEEE80211_IF_TYPE_AP && keyidx) {
+ /*
+ * APs with pairwise keys should never receive Michael MIC
+ * errors for non-zero keyidx because these are reserved for
+ * group keys and only the AP is sending real multicast
+ * frames in the BSS.
+ */
+ if (net_ratelimit())
+ printk(KERN_DEBUG "%s: ignored Michael MIC error for "
+ "a frame with non-zero keyidx (%d)"
+ " (src %s)\n", dev->name, keyidx,
+ print_mac(mac, hdr->addr2));
+ goto ignore;
+ }
+
+ if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
+ ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
+ (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH)) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
+ "error for a frame that cannot be encrypted "
+ "(fc=0x%04x) (src %s)\n",
+ dev->name, rx->fc, print_mac(mac, hdr->addr2));
+ goto ignore;
+ }
+
+ mac80211_ev_michael_mic_failure(rx->dev, keyidx, hdr);
+ ignore:
+ dev_kfree_skb(rx->skb);
+ rx->skb = NULL;
+}
+
+ieee80211_rx_handler ieee80211_rx_handlers[] =
+{
+ ieee80211_rx_h_if_stats,
+ ieee80211_rx_h_passive_scan,
+ ieee80211_rx_h_check,
+ ieee80211_rx_h_decrypt,
+ ieee80211_rx_h_sta_process,
+ ieee80211_rx_h_defragment,
+ ieee80211_rx_h_ps_poll,
+ ieee80211_rx_h_michael_mic_verify,
+ /* this must be after decryption - so header is counted in MPDU mic
+ * must be before pae and data, so QOS_DATA format frames
+ * are not passed to user space by these functions
+ */
+ ieee80211_rx_h_remove_qos_control,
+ ieee80211_rx_h_802_1x_pae,
+ ieee80211_rx_h_drop_unencrypted,
+ ieee80211_rx_h_data,
+ ieee80211_rx_h_mgmt,
+ NULL
+};
+
+/* main receive path */
+
+static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
+ u8 *bssid, struct ieee80211_txrx_data *rx,
+ struct ieee80211_hdr *hdr)
+{
+ int multicast = is_multicast_ether_addr(hdr->addr1);
+
+ switch (sdata->type) {
+ case IEEE80211_IF_TYPE_STA:
+ if (!bssid)
+ return 0;
+ if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) {
+ if (!(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
+ return 0;
+ rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+ } else if (!multicast &&
+ compare_ether_addr(sdata->dev->dev_addr,
+ hdr->addr1) != 0) {
+ if (!(sdata->dev->flags & IFF_PROMISC))
+ return 0;
+ rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+ }
+ break;
+ case IEEE80211_IF_TYPE_IBSS:
+ if (!bssid)
+ return 0;
+ if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) {
+ if (!(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
+ return 0;
+ rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+ } else if (!multicast &&
+ compare_ether_addr(sdata->dev->dev_addr,
+ hdr->addr1) != 0) {
+ if (!(sdata->dev->flags & IFF_PROMISC))
+ return 0;
+ rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+ } else if (!rx->sta)
+ rx->sta = ieee80211_ibss_add_sta(sdata->dev, rx->skb,
+ bssid, hdr->addr2);
+ break;
+ case IEEE80211_IF_TYPE_VLAN:
+ case IEEE80211_IF_TYPE_AP:
+ if (!bssid) {
+ if (compare_ether_addr(sdata->dev->dev_addr,
+ hdr->addr1))
+ return 0;
+ } else if (!ieee80211_bssid_match(bssid,
+ sdata->dev->dev_addr)) {
+ if (!(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
+ return 0;
+ rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+ }
+ if (sdata->dev == sdata->local->mdev &&
+ !(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
+ /* do not receive anything via
+ * master device when not scanning */
+ return 0;
+ break;
+ case IEEE80211_IF_TYPE_WDS:
+ if (bssid ||
+ (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)
+ return 0;
+ if (compare_ether_addr(sdata->u.wds.remote_addr, hdr->addr2))
+ return 0;
+ break;
+ case IEEE80211_IF_TYPE_MNTR:
+ /* take everything */
+ break;
+ case IEEE80211_IF_TYPE_INVALID:
+ /* should never get here */
+ WARN_ON(1);
+ break;
+ }
+
+ return 1;
+}
+
+/*
+ * This is the receive path handler. It is called by a low level driver when an
+ * 802.11 MPDU is received from the hardware.
+ */
+void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
+ struct ieee80211_rx_status *status)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_sub_if_data *sdata;
+ struct sta_info *sta;
+ struct ieee80211_hdr *hdr;
+ struct ieee80211_txrx_data rx;
+ u16 type;
+ int prepres;
+ struct ieee80211_sub_if_data *prev = NULL;
+ struct sk_buff *skb_new;
+ u8 *bssid;
+
+ /*
+ * key references and virtual interfaces are protected using RCU
+ * and this requires that we are in a read-side RCU section during
+ * receive processing
+ */
+ rcu_read_lock();
+
+ /*
+ * Frames with failed FCS/PLCP checksum are not returned,
+ * all other frames are returned without radiotap header
+ * if it was previously present.
+ * Also, frames with less than 16 bytes are dropped.
+ */
+ skb = ieee80211_rx_monitor(local, skb, status);
+ if (!skb) {
+ rcu_read_unlock();
+ return;
+ }
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ memset(&rx, 0, sizeof(rx));
+ rx.skb = skb;
+ rx.local = local;
+
+ rx.u.rx.status = status;
+ rx.fc = le16_to_cpu(hdr->frame_control);
+ type = rx.fc & IEEE80211_FCTL_FTYPE;
+
+ if (type == IEEE80211_FTYPE_DATA || type == IEEE80211_FTYPE_MGMT)
+ local->dot11ReceivedFragmentCount++;
+
+ sta = rx.sta = sta_info_get(local, hdr->addr2);
+ if (sta) {
+ rx.dev = rx.sta->dev;
+ rx.sdata = IEEE80211_DEV_TO_SUB_IF(rx.dev);
+ }
+
+ if ((status->flag & RX_FLAG_MMIC_ERROR)) {
+ ieee80211_rx_michael_mic_report(local->mdev, hdr, sta, &rx);
+ goto end;
+ }
+
+ if (unlikely(local->sta_scanning))
+ rx.flags |= IEEE80211_TXRXD_RXIN_SCAN;
+
+ if (__ieee80211_invoke_rx_handlers(local, local->rx_pre_handlers, &rx,
+ sta) != TXRX_CONTINUE)
+ goto end;
+ skb = rx.skb;
+
+ if (sta && !(sta->flags & (WLAN_STA_WDS | WLAN_STA_ASSOC_AP)) &&
+ !atomic_read(&local->iff_promiscs) &&
+ !is_multicast_ether_addr(hdr->addr1)) {
+ rx.flags |= IEEE80211_TXRXD_RXRA_MATCH;
+ ieee80211_invoke_rx_handlers(local, local->rx_handlers, &rx,
+ rx.sta);
+ sta_info_put(sta);
+ rcu_read_unlock();
+ return;
+ }
+
+ bssid = ieee80211_get_bssid(hdr, skb->len);
+
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (!netif_running(sdata->dev))
+ continue;
+
+ if (sdata->type == IEEE80211_IF_TYPE_MNTR)
+ continue;
+
+ rx.flags |= IEEE80211_TXRXD_RXRA_MATCH;
+ prepres = prepare_for_handlers(sdata, bssid, &rx, hdr);
+ /* prepare_for_handlers can change sta */
+ sta = rx.sta;
+
+ if (!prepres)
+ continue;
+
+ /*
+ * frame is destined for this interface, but if it's not
+ * also for the previous one we handle that after the
+ * loop to avoid copying the SKB once too much
+ */
+
+ if (!prev) {
+ prev = sdata;
+ continue;
+ }
+
+ /*
+ * frame was destined for the previous interface
+ * so invoke RX handlers for it
+ */
+
+ skb_new = skb_copy(skb, GFP_ATOMIC);
+ if (!skb_new) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "%s: failed to copy "
+ "multicast frame for %s",
+ wiphy_name(local->hw.wiphy),
+ prev->dev->name);
+ continue;
+ }
+ rx.skb = skb_new;
+ rx.dev = prev->dev;
+ rx.sdata = prev;
+ ieee80211_invoke_rx_handlers(local, local->rx_handlers,
+ &rx, sta);
+ prev = sdata;
+ }
+ if (prev) {
+ rx.skb = skb;
+ rx.dev = prev->dev;
+ rx.sdata = prev;
+ ieee80211_invoke_rx_handlers(local, local->rx_handlers,
+ &rx, sta);
+ } else
+ dev_kfree_skb(skb);
+
+ end:
+ rcu_read_unlock();
+
+ if (sta)
+ sta_info_put(sta);
+}
+EXPORT_SYMBOL(__ieee80211_rx);
+
+/* This is a version of the rx handler that can be called from hard irq
+ * context. Post the skb on the queue and schedule the tasklet */
+void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb,
+ struct ieee80211_rx_status *status)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+
+ BUILD_BUG_ON(sizeof(struct ieee80211_rx_status) > sizeof(skb->cb));
+
+ skb->dev = local->mdev;
+ /* copy status into skb->cb for use by tasklet */
+ memcpy(skb->cb, status, sizeof(*status));
+ skb->pkt_type = IEEE80211_RX_MSG;
+ skb_queue_tail(&local->skb_queue, skb);
+ tasklet_schedule(&local->tasklet);
+}
+EXPORT_SYMBOL(ieee80211_rx_irqsafe);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index ab7b1f067c6e..e8491554a5dc 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -19,7 +19,6 @@
#include "ieee80211_i.h"
#include "ieee80211_rate.h"
#include "sta_info.h"
-#include "debugfs_key.h"
#include "debugfs_sta.h"
/* Caller must hold local->sta_lock */
@@ -32,38 +31,34 @@ static void sta_info_hash_add(struct ieee80211_local *local,
/* Caller must hold local->sta_lock */
-static void sta_info_hash_del(struct ieee80211_local *local,
- struct sta_info *sta)
+static int sta_info_hash_del(struct ieee80211_local *local,
+ struct sta_info *sta)
{
struct sta_info *s;
s = local->sta_hash[STA_HASH(sta->addr)];
if (!s)
- return;
- if (memcmp(s->addr, sta->addr, ETH_ALEN) == 0) {
+ return -ENOENT;
+ if (s == sta) {
local->sta_hash[STA_HASH(sta->addr)] = s->hnext;
- return;
+ return 0;
}
- while (s->hnext && memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0)
+ while (s->hnext && s->hnext != sta)
s = s->hnext;
- if (s->hnext)
- s->hnext = s->hnext->hnext;
- else
- printk(KERN_ERR "%s: could not remove STA " MAC_FMT " from "
- "hash table\n", local->mdev->name, MAC_ARG(sta->addr));
-}
+ if (s->hnext) {
+ s->hnext = sta->hnext;
+ return 0;
+ }
-static inline void __sta_info_get(struct sta_info *sta)
-{
- kref_get(&sta->kref);
+ return -ENOENT;
}
struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr)
{
struct sta_info *sta;
- spin_lock_bh(&local->sta_lock);
+ read_lock_bh(&local->sta_lock);
sta = local->sta_hash[STA_HASH(addr)];
while (sta) {
if (memcmp(sta->addr, addr, ETH_ALEN) == 0) {
@@ -72,7 +67,7 @@ struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr)
}
sta = sta->hnext;
}
- spin_unlock_bh(&local->sta_lock);
+ read_unlock_bh(&local->sta_lock);
return sta;
}
@@ -85,7 +80,7 @@ int sta_info_min_txrate_get(struct ieee80211_local *local)
int min_txrate = 9999999;
int i;
- spin_lock_bh(&local->sta_lock);
+ read_lock_bh(&local->sta_lock);
mode = local->oper_hw_mode;
for (i = 0; i < STA_HASH_SIZE; i++) {
sta = local->sta_hash[i];
@@ -95,7 +90,7 @@ int sta_info_min_txrate_get(struct ieee80211_local *local)
sta = sta->hnext;
}
}
- spin_unlock_bh(&local->sta_lock);
+ read_unlock_bh(&local->sta_lock);
if (min_txrate == 9999999)
min_txrate = 0;
@@ -122,8 +117,6 @@ static void sta_info_release(struct kref *kref)
}
rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv);
rate_control_put(sta->rate_ctrl);
- if (sta->key)
- ieee80211_debugfs_key_sta_del(sta->key, sta);
kfree(sta);
}
@@ -139,6 +132,7 @@ struct sta_info * sta_info_add(struct ieee80211_local *local,
struct net_device *dev, u8 *addr, gfp_t gfp)
{
struct sta_info *sta;
+ DECLARE_MAC_BUF(mac);
sta = kzalloc(sizeof(*sta), gfp);
if (!sta)
@@ -150,7 +144,6 @@ struct sta_info * sta_info_add(struct ieee80211_local *local,
sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl, gfp);
if (!sta->rate_ctrl_priv) {
rate_control_put(sta->rate_ctrl);
- kref_put(&sta->kref, sta_info_release);
kfree(sta);
return NULL;
}
@@ -162,63 +155,40 @@ struct sta_info * sta_info_add(struct ieee80211_local *local,
skb_queue_head_init(&sta->tx_filtered);
__sta_info_get(sta); /* sta used by caller, decremented by
* sta_info_put() */
- spin_lock_bh(&local->sta_lock);
+ write_lock_bh(&local->sta_lock);
list_add(&sta->list, &local->sta_list);
local->num_sta++;
sta_info_hash_add(local, sta);
- spin_unlock_bh(&local->sta_lock);
- if (local->ops->sta_table_notification)
- local->ops->sta_table_notification(local_to_hw(local),
- local->num_sta);
- sta->key_idx_compression = HW_KEY_IDX_INVALID;
+ if (local->ops->sta_notify)
+ local->ops->sta_notify(local_to_hw(local), dev->ifindex,
+ STA_NOTIFY_ADD, addr);
+ write_unlock_bh(&local->sta_lock);
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Added STA " MAC_FMT "\n",
- local->mdev->name, MAC_ARG(addr));
+ printk(KERN_DEBUG "%s: Added STA %s\n",
+ wiphy_name(local->hw.wiphy), print_mac(mac, addr));
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
#ifdef CONFIG_MAC80211_DEBUGFS
- if (!in_interrupt()) {
- sta->debugfs_registered = 1;
- ieee80211_sta_debugfs_add(sta);
- rate_control_add_sta_debugfs(sta);
- } else {
- /* debugfs entry adding might sleep, so schedule process
- * context task for adding entry for STAs that do not yet
- * have one. */
- queue_work(local->hw.workqueue, &local->sta_debugfs_add);
- }
+ /* debugfs entry adding might sleep, so schedule process
+ * context task for adding entry for STAs that do not yet
+ * have one. */
+ queue_work(local->hw.workqueue, &local->sta_debugfs_add);
#endif
return sta;
}
-static void finish_sta_info_free(struct ieee80211_local *local,
- struct sta_info *sta)
-{
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Removed STA " MAC_FMT "\n",
- local->mdev->name, MAC_ARG(sta->addr));
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
-
- if (sta->key) {
- ieee80211_debugfs_key_remove(sta->key);
- ieee80211_key_free(sta->key);
- sta->key = NULL;
- }
-
- rate_control_remove_sta_debugfs(sta);
- ieee80211_sta_debugfs_remove(sta);
-
- sta_info_put(sta);
-}
-
-static void sta_info_remove(struct sta_info *sta)
+/* Caller must hold local->sta_lock */
+void sta_info_remove(struct sta_info *sta)
{
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata;
- sta_info_hash_del(local, sta);
+ /* don't do anything if we've been removed already */
+ if (sta_info_hash_del(local, sta))
+ return;
+
list_del(&sta->list);
sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
if (sta->flags & WLAN_STA_PS) {
@@ -228,61 +198,45 @@ static void sta_info_remove(struct sta_info *sta)
}
local->num_sta--;
sta_info_remove_aid_ptr(sta);
+
}
-void sta_info_free(struct sta_info *sta, int locked)
+void sta_info_free(struct sta_info *sta)
{
struct sk_buff *skb;
struct ieee80211_local *local = sta->local;
+ DECLARE_MAC_BUF(mac);
- if (!locked) {
- spin_lock_bh(&local->sta_lock);
- sta_info_remove(sta);
- spin_unlock_bh(&local->sta_lock);
- } else {
- sta_info_remove(sta);
- }
- if (local->ops->sta_table_notification)
- local->ops->sta_table_notification(local_to_hw(local),
- local->num_sta);
+ might_sleep();
+
+ write_lock_bh(&local->sta_lock);
+ sta_info_remove(sta);
+ write_unlock_bh(&local->sta_lock);
while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
local->total_ps_buffered--;
- dev_kfree_skb_any(skb);
+ dev_kfree_skb(skb);
}
while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
- dev_kfree_skb_any(skb);
+ dev_kfree_skb(skb);
}
- if (sta->key) {
- if (local->ops->set_key) {
- struct ieee80211_key_conf *key;
- key = ieee80211_key_data2conf(local, sta->key);
- if (key) {
- local->ops->set_key(local_to_hw(local),
- DISABLE_KEY,
- sta->addr, key, sta->aid);
- kfree(key);
- }
- }
- } else if (sta->key_idx_compression != HW_KEY_IDX_INVALID) {
- struct ieee80211_key_conf conf;
- memset(&conf, 0, sizeof(conf));
- conf.hw_key_idx = sta->key_idx_compression;
- conf.alg = ALG_NULL;
- conf.flags |= IEEE80211_KEY_FORCE_SW_ENCRYPT;
- local->ops->set_key(local_to_hw(local), DISABLE_KEY,
- sta->addr, &conf, sta->aid);
- sta->key_idx_compression = HW_KEY_IDX_INVALID;
- }
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ printk(KERN_DEBUG "%s: Removed STA %s\n",
+ wiphy_name(local->hw.wiphy), print_mac(mac, sta->addr));
+#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
-#ifdef CONFIG_MAC80211_DEBUGFS
- if (in_atomic()) {
- list_add(&sta->list, &local->deleted_sta_list);
- queue_work(local->hw.workqueue, &local->sta_debugfs_add);
- } else
-#endif
- finish_sta_info_free(local, sta);
+ ieee80211_key_free(sta->key);
+ sta->key = NULL;
+
+ if (local->ops->sta_notify)
+ local->ops->sta_notify(local_to_hw(local), sta->dev->ifindex,
+ STA_NOTIFY_REMOVE, sta->addr);
+
+ rate_control_remove_sta_debugfs(sta);
+ ieee80211_sta_debugfs_remove(sta);
+
+ sta_info_put(sta);
}
@@ -312,6 +266,7 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
{
unsigned long flags;
struct sk_buff *skb;
+ DECLARE_MAC_BUF(mac);
if (skb_queue_empty(&sta->ps_tx_buf))
return;
@@ -330,7 +285,7 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
if (skb) {
local->total_ps_buffered--;
printk(KERN_DEBUG "Buffered frame expired (STA "
- MAC_FMT ")\n", MAC_ARG(sta->addr));
+ "%s)\n", print_mac(mac, sta->addr));
dev_kfree_skb(skb);
} else
break;
@@ -343,13 +298,13 @@ static void sta_info_cleanup(unsigned long data)
struct ieee80211_local *local = (struct ieee80211_local *) data;
struct sta_info *sta;
- spin_lock_bh(&local->sta_lock);
+ read_lock_bh(&local->sta_lock);
list_for_each_entry(sta, &local->sta_list, list) {
__sta_info_get(sta);
sta_info_cleanup_expire_buffered(local, sta);
sta_info_put(sta);
}
- spin_unlock_bh(&local->sta_lock);
+ read_unlock_bh(&local->sta_lock);
local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL;
add_timer(&local->sta_cleanup);
@@ -363,35 +318,20 @@ static void sta_info_debugfs_add_task(struct work_struct *work)
struct sta_info *sta, *tmp;
while (1) {
- spin_lock_bh(&local->sta_lock);
- if (!list_empty(&local->deleted_sta_list)) {
- sta = list_entry(local->deleted_sta_list.next,
- struct sta_info, list);
- list_del(local->deleted_sta_list.next);
- } else
- sta = NULL;
- spin_unlock_bh(&local->sta_lock);
- if (!sta)
- break;
- finish_sta_info_free(local, sta);
- }
-
- while (1) {
sta = NULL;
- spin_lock_bh(&local->sta_lock);
+ read_lock_bh(&local->sta_lock);
list_for_each_entry(tmp, &local->sta_list, list) {
- if (!tmp->debugfs_registered) {
+ if (!tmp->debugfs.dir) {
sta = tmp;
__sta_info_get(sta);
break;
}
}
- spin_unlock_bh(&local->sta_lock);
+ read_unlock_bh(&local->sta_lock);
if (!sta)
break;
- sta->debugfs_registered = 1;
ieee80211_sta_debugfs_add(sta);
rate_control_add_sta_debugfs(sta);
sta_info_put(sta);
@@ -401,9 +341,8 @@ static void sta_info_debugfs_add_task(struct work_struct *work)
void sta_info_init(struct ieee80211_local *local)
{
- spin_lock_init(&local->sta_lock);
+ rwlock_init(&local->sta_lock);
INIT_LIST_HEAD(&local->sta_list);
- INIT_LIST_HEAD(&local->deleted_sta_list);
init_timer(&local->sta_cleanup);
local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL;
@@ -423,17 +362,8 @@ int sta_info_start(struct ieee80211_local *local)
void sta_info_stop(struct ieee80211_local *local)
{
- struct sta_info *sta, *tmp;
-
del_timer(&local->sta_cleanup);
-
- list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
- /* sta_info_free must be called with 0 as the last
- * parameter to ensure all debugfs sta entries are
- * unregistered. We don't need locking at this
- * point. */
- sta_info_free(sta, 0);
- }
+ sta_info_flush(local, NULL);
}
void sta_info_remove_aid_ptr(struct sta_info *sta)
@@ -461,10 +391,19 @@ void sta_info_remove_aid_ptr(struct sta_info *sta)
void sta_info_flush(struct ieee80211_local *local, struct net_device *dev)
{
struct sta_info *sta, *tmp;
+ LIST_HEAD(tmp_list);
- spin_lock_bh(&local->sta_lock);
+ write_lock_bh(&local->sta_lock);
list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
- if (!dev || dev == sta->dev)
- sta_info_free(sta, 1);
- spin_unlock_bh(&local->sta_lock);
+ if (!dev || dev == sta->dev) {
+ __sta_info_get(sta);
+ sta_info_remove(sta);
+ list_add_tail(&sta->list, &tmp_list);
+ }
+ write_unlock_bh(&local->sta_lock);
+
+ list_for_each_entry_safe(sta, tmp, &tmp_list, list) {
+ sta_info_free(sta);
+ sta_info_put(sta);
+ }
}
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index b5591d2f60a4..8f7ebe41c024 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -26,6 +26,8 @@
* send and receive non-IEEE 802.1X frames
*/
#define WLAN_STA_SHORT_PREAMBLE BIT(7)
+/* whether this is an AP that we are associated with as a client */
+#define WLAN_STA_ASSOC_AP BIT(8)
#define WLAN_STA_WME BIT(9)
#define WLAN_STA_WDS BIT(27)
@@ -90,27 +92,11 @@ struct sta_info {
int channel_use;
int channel_use_raw;
- u8 antenna_sel_tx;
- u8 antenna_sel_rx;
-
-
- int key_idx_compression; /* key table index for compression and TX
- * filtering; used only if sta->key is not
- * set */
-
-#ifdef CONFIG_MAC80211_DEBUGFS
- int debugfs_registered;
-#endif
- int assoc_ap; /* whether this is an AP that we are
- * associated with as a client */
-
#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
unsigned int wme_rx_queue[NUM_RX_DATA_QUEUES];
unsigned int wme_tx_queue[NUM_RX_DATA_QUEUES];
#endif /* CONFIG_MAC80211_DEBUG_COUNTERS */
- int vlan_id;
-
u16 listen_interval;
#ifdef CONFIG_MAC80211_DEBUGFS
@@ -149,12 +135,18 @@ struct sta_info {
*/
#define STA_INFO_CLEANUP_INTERVAL (10 * HZ)
+static inline void __sta_info_get(struct sta_info *sta)
+{
+ kref_get(&sta->kref);
+}
+
struct sta_info * sta_info_get(struct ieee80211_local *local, u8 *addr);
int sta_info_min_txrate_get(struct ieee80211_local *local);
void sta_info_put(struct sta_info *sta);
struct sta_info * sta_info_add(struct ieee80211_local *local,
struct net_device *dev, u8 *addr, gfp_t gfp);
-void sta_info_free(struct sta_info *sta, int locked);
+void sta_info_remove(struct sta_info *sta);
+void sta_info_free(struct sta_info *sta);
void sta_info_init(struct ieee80211_local *local);
int sta_info_start(struct ieee80211_local *local);
void sta_info_stop(struct ieee80211_local *local);
diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c
index 41621720e560..3abe194e4d55 100644
--- a/net/mac80211/tkip.c
+++ b/net/mac80211/tkip.c
@@ -182,7 +182,7 @@ u8 * ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key,
*pos++ = iv0;
*pos++ = iv1;
*pos++ = iv2;
- *pos++ = (key->keyidx << 6) | (1 << 5) /* Ext IV */;
+ *pos++ = (key->conf.keyidx << 6) | (1 << 5) /* Ext IV */;
*pos++ = key->u.tkip.iv32 & 0xff;
*pos++ = (key->u.tkip.iv32 >> 8) & 0xff;
*pos++ = (key->u.tkip.iv32 >> 16) & 0xff;
@@ -194,7 +194,7 @@ u8 * ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key,
void ieee80211_tkip_gen_phase1key(struct ieee80211_key *key, u8 *ta,
u16 *phase1key)
{
- tkip_mixing_phase1(ta, &key->key[ALG_TKIP_TEMP_ENCR_KEY],
+ tkip_mixing_phase1(ta, &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY],
key->u.tkip.iv32, phase1key);
}
@@ -204,12 +204,13 @@ void ieee80211_tkip_gen_rc4key(struct ieee80211_key *key, u8 *ta,
/* Calculate per-packet key */
if (key->u.tkip.iv16 == 0 || !key->u.tkip.tx_initialized) {
/* IV16 wrapped around - perform TKIP phase 1 */
- tkip_mixing_phase1(ta, &key->key[ALG_TKIP_TEMP_ENCR_KEY],
+ tkip_mixing_phase1(ta, &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY],
key->u.tkip.iv32, key->u.tkip.p1k);
key->u.tkip.tx_initialized = 1;
}
- tkip_mixing_phase2(key->u.tkip.p1k, &key->key[ALG_TKIP_TEMP_ENCR_KEY],
+ tkip_mixing_phase2(key->u.tkip.p1k,
+ &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY],
key->u.tkip.iv16, rc4key);
}
@@ -237,7 +238,8 @@ void ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm,
int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
struct ieee80211_key *key,
u8 *payload, size_t payload_len, u8 *ta,
- int only_iv, int queue)
+ int only_iv, int queue,
+ u32 *out_iv32, u16 *out_iv16)
{
u32 iv32;
u32 iv16;
@@ -266,7 +268,7 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
if (!(keyid & (1 << 5)))
return TKIP_DECRYPT_NO_EXT_IV;
- if ((keyid >> 6) != key->keyidx)
+ if ((keyid >> 6) != key->conf.keyidx)
return TKIP_DECRYPT_INVALID_KEYIDX;
if (key->u.tkip.rx_initialized[queue] &&
@@ -274,9 +276,10 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
(iv32 == key->u.tkip.iv32_rx[queue] &&
iv16 <= key->u.tkip.iv16_rx[queue]))) {
#ifdef CONFIG_TKIP_DEBUG
+ DECLARE_MAC_BUF(mac);
printk(KERN_DEBUG "TKIP replay detected for RX frame from "
- MAC_FMT " (RX IV (%04x,%02x) <= prev. IV (%04x,%02x)\n",
- MAC_ARG(ta),
+ "%s (RX IV (%04x,%02x) <= prev. IV (%04x,%02x)\n",
+ print_mac(mac, ta),
iv32, iv16, key->u.tkip.iv32_rx[queue],
key->u.tkip.iv16_rx[queue]);
#endif /* CONFIG_TKIP_DEBUG */
@@ -293,16 +296,18 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
key->u.tkip.iv32_rx[queue] != iv32) {
key->u.tkip.rx_initialized[queue] = 1;
/* IV16 wrapped around - perform TKIP phase 1 */
- tkip_mixing_phase1(ta, &key->key[ALG_TKIP_TEMP_ENCR_KEY],
+ tkip_mixing_phase1(ta, &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY],
iv32, key->u.tkip.p1k_rx[queue]);
#ifdef CONFIG_TKIP_DEBUG
{
int i;
- printk(KERN_DEBUG "TKIP decrypt: Phase1 TA=" MAC_FMT
- " TK=", MAC_ARG(ta));
+ DECLARE_MAC_BUF(mac);
+ printk(KERN_DEBUG "TKIP decrypt: Phase1 TA=%s"
+ " TK=", print_mac(mac, ta));
for (i = 0; i < 16; i++)
printk("%02x ",
- key->key[ALG_TKIP_TEMP_ENCR_KEY + i]);
+ key->conf.key[
+ ALG_TKIP_TEMP_ENCR_KEY + i]);
printk("\n");
printk(KERN_DEBUG "TKIP decrypt: P1K=");
for (i = 0; i < 5; i++)
@@ -313,7 +318,7 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
}
tkip_mixing_phase2(key->u.tkip.p1k_rx[queue],
- &key->key[ALG_TKIP_TEMP_ENCR_KEY],
+ &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY],
iv16, rc4key);
#ifdef CONFIG_TKIP_DEBUG
{
@@ -328,11 +333,14 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
res = ieee80211_wep_decrypt_data(tfm, rc4key, 16, pos, payload_len - 12);
done:
if (res == TKIP_DECRYPT_OK) {
- /* FIX: these should be updated only after Michael MIC has been
- * verified */
- /* Record previously received IV */
- key->u.tkip.iv32_rx[queue] = iv32;
- key->u.tkip.iv16_rx[queue] = iv16;
+ /*
+ * Record previously received IV, will be copied into the
+ * key information after MIC verification. It is possible
+ * that we don't catch replays of fragments but that's ok
+ * because the Michael MIC verication will then fail.
+ */
+ *out_iv32 = iv32;
+ *out_iv16 = iv16;
}
return res;
diff --git a/net/mac80211/tkip.h b/net/mac80211/tkip.h
index a0d181a18049..73d8ef2a93b0 100644
--- a/net/mac80211/tkip.h
+++ b/net/mac80211/tkip.h
@@ -31,6 +31,7 @@ enum {
int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
struct ieee80211_key *key,
u8 *payload, size_t payload_len, u8 *ta,
- int only_iv, int queue);
+ int only_iv, int queue,
+ u32 *out_iv32, u16 *out_iv16);
#endif /* TKIP_H */
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
new file mode 100644
index 000000000000..1a531543bccb
--- /dev/null
+++ b/net/mac80211/tx.c
@@ -0,0 +1,1903 @@
+/*
+ * 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>
+ *
+ * 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.
+ *
+ *
+ * Transmit and frame generation functions.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/bitmap.h>
+#include <linux/rcupdate.h>
+#include <net/net_namespace.h>
+#include <net/ieee80211_radiotap.h>
+#include <net/cfg80211.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+
+#include "ieee80211_i.h"
+#include "ieee80211_led.h"
+#include "wep.h"
+#include "wpa.h"
+#include "wme.h"
+#include "ieee80211_rate.h"
+
+#define IEEE80211_TX_OK 0
+#define IEEE80211_TX_AGAIN 1
+#define IEEE80211_TX_FRAG_AGAIN 2
+
+/* misc utils */
+
+static inline void ieee80211_include_sequence(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_hdr *hdr)
+{
+ /* Set the sequence number for this frame. */
+ hdr->seq_ctrl = cpu_to_le16(sdata->sequence);
+
+ /* Increase the sequence number. */
+ sdata->sequence = (sdata->sequence + 0x10) & IEEE80211_SCTL_SEQ;
+}
+
+#ifdef CONFIG_MAC80211_LOWTX_FRAME_DUMP
+static void ieee80211_dump_frame(const char *ifname, const char *title,
+ const struct sk_buff *skb)
+{
+ const struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ u16 fc;
+ int hdrlen;
+ DECLARE_MAC_BUF(mac);
+
+ printk(KERN_DEBUG "%s: %s (len=%d)", ifname, title, skb->len);
+ if (skb->len < 4) {
+ printk("\n");
+ return;
+ }
+
+ fc = le16_to_cpu(hdr->frame_control);
+ hdrlen = ieee80211_get_hdrlen(fc);
+ if (hdrlen > skb->len)
+ hdrlen = skb->len;
+ if (hdrlen >= 4)
+ printk(" FC=0x%04x DUR=0x%04x",
+ fc, le16_to_cpu(hdr->duration_id));
+ if (hdrlen >= 10)
+ printk(" A1=%s", print_mac(mac, hdr->addr1));
+ if (hdrlen >= 16)
+ printk(" A2=%s", print_mac(mac, hdr->addr2));
+ if (hdrlen >= 24)
+ printk(" A3=%s", print_mac(mac, hdr->addr3));
+ if (hdrlen >= 30)
+ printk(" A4=%s", print_mac(mac, hdr->addr4));
+ printk("\n");
+}
+#else /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */
+static inline void ieee80211_dump_frame(const char *ifname, const char *title,
+ struct sk_buff *skb)
+{
+}
+#endif /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */
+
+static u16 ieee80211_duration(struct ieee80211_txrx_data *tx, int group_addr,
+ int next_frag_len)
+{
+ int rate, mrate, erp, dur, i;
+ struct ieee80211_rate *txrate = tx->u.tx.rate;
+ struct ieee80211_local *local = tx->local;
+ struct ieee80211_hw_mode *mode = tx->u.tx.mode;
+
+ erp = txrate->flags & IEEE80211_RATE_ERP;
+
+ /*
+ * data and mgmt (except PS Poll):
+ * - during CFP: 32768
+ * - during contention period:
+ * if addr1 is group address: 0
+ * if more fragments = 0 and addr1 is individual address: time to
+ * transmit one ACK plus SIFS
+ * if more fragments = 1 and addr1 is individual address: time to
+ * transmit next fragment plus 2 x ACK plus 3 x SIFS
+ *
+ * IEEE 802.11, 9.6:
+ * - control response frame (CTS or ACK) shall be transmitted using the
+ * same rate as the immediately previous frame in the frame exchange
+ * sequence, if this rate belongs to the PHY mandatory rates, or else
+ * at the highest possible rate belonging to the PHY rates in the
+ * BSSBasicRateSet
+ */
+
+ if ((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) {
+ /* TODO: These control frames are not currently sent by
+ * 80211.o, but should they be implemented, this function
+ * needs to be updated to support duration field calculation.
+ *
+ * RTS: time needed to transmit pending data/mgmt frame plus
+ * one CTS frame plus one ACK frame plus 3 x SIFS
+ * CTS: duration of immediately previous RTS minus time
+ * required to transmit CTS and its SIFS
+ * ACK: 0 if immediately previous directed data/mgmt had
+ * more=0, with more=1 duration in ACK frame is duration
+ * from previous frame minus time needed to transmit ACK
+ * and its SIFS
+ * PS Poll: BIT(15) | BIT(14) | aid
+ */
+ return 0;
+ }
+
+ /* data/mgmt */
+ if (0 /* FIX: data/mgmt during CFP */)
+ return 32768;
+
+ if (group_addr) /* Group address as the destination - no ACK */
+ return 0;
+
+ /* Individual destination address:
+ * IEEE 802.11, Ch. 9.6 (after IEEE 802.11g changes)
+ * CTS and ACK frames shall be transmitted using the highest rate in
+ * basic rate set that is less than or equal to the rate of the
+ * immediately previous frame and that is using the same modulation
+ * (CCK or OFDM). If no basic rate set matches with these requirements,
+ * the highest mandatory rate of the PHY that is less than or equal to
+ * the rate of the previous frame is used.
+ * Mandatory rates for IEEE 802.11g PHY: 1, 2, 5.5, 11, 6, 12, 24 Mbps
+ */
+ rate = -1;
+ mrate = 10; /* use 1 Mbps if everything fails */
+ for (i = 0; i < mode->num_rates; i++) {
+ struct ieee80211_rate *r = &mode->rates[i];
+ if (r->rate > txrate->rate)
+ break;
+
+ if (IEEE80211_RATE_MODULATION(txrate->flags) !=
+ IEEE80211_RATE_MODULATION(r->flags))
+ continue;
+
+ if (r->flags & IEEE80211_RATE_BASIC)
+ rate = r->rate;
+ else if (r->flags & IEEE80211_RATE_MANDATORY)
+ mrate = r->rate;
+ }
+ if (rate == -1) {
+ /* No matching basic rate found; use highest suitable mandatory
+ * PHY rate */
+ rate = mrate;
+ }
+
+ /* Time needed to transmit ACK
+ * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up
+ * to closest integer */
+
+ dur = ieee80211_frame_duration(local, 10, rate, erp,
+ tx->sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE);
+
+ if (next_frag_len) {
+ /* Frame is fragmented: duration increases with time needed to
+ * transmit next fragment plus ACK and 2 x SIFS. */
+ dur *= 2; /* ACK + SIFS */
+ /* next fragment */
+ dur += ieee80211_frame_duration(local, next_frag_len,
+ txrate->rate, erp,
+ tx->sdata->flags &
+ IEEE80211_SDATA_SHORT_PREAMBLE);
+ }
+
+ return dur;
+}
+
+static inline int __ieee80211_queue_stopped(const struct ieee80211_local *local,
+ int queue)
+{
+ return test_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
+}
+
+static inline int __ieee80211_queue_pending(const struct ieee80211_local *local,
+ int queue)
+{
+ return test_bit(IEEE80211_LINK_STATE_PENDING, &local->state[queue]);
+}
+
+static int inline is_ieee80211_device(struct net_device *dev,
+ struct net_device *master)
+{
+ return (wdev_priv(dev->ieee80211_ptr) ==
+ wdev_priv(master->ieee80211_ptr));
+}
+
+/* tx handlers */
+
+static ieee80211_txrx_result
+ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx)
+{
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ struct sk_buff *skb = tx->skb;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
+ u32 sta_flags;
+
+ if (unlikely(tx->flags & IEEE80211_TXRXD_TX_INJECTED))
+ return TXRX_CONTINUE;
+
+ if (unlikely(tx->local->sta_scanning != 0) &&
+ ((tx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
+ (tx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PROBE_REQ))
+ return TXRX_DROP;
+
+ if (tx->flags & IEEE80211_TXRXD_TXPS_BUFFERED)
+ return TXRX_CONTINUE;
+
+ sta_flags = tx->sta ? tx->sta->flags : 0;
+
+ if (likely(tx->flags & IEEE80211_TXRXD_TXUNICAST)) {
+ if (unlikely(!(sta_flags & WLAN_STA_ASSOC) &&
+ tx->sdata->type != IEEE80211_IF_TYPE_IBSS &&
+ (tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)) {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ DECLARE_MAC_BUF(mac);
+ printk(KERN_DEBUG "%s: dropped data frame to not "
+ "associated station %s\n",
+ tx->dev->name, print_mac(mac, hdr->addr1));
+#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
+ I802_DEBUG_INC(tx->local->tx_handlers_drop_not_assoc);
+ return TXRX_DROP;
+ }
+ } else {
+ if (unlikely((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
+ tx->local->num_sta == 0 &&
+ tx->sdata->type != IEEE80211_IF_TYPE_IBSS)) {
+ /*
+ * No associated STAs - no need to send multicast
+ * frames.
+ */
+ return TXRX_DROP;
+ }
+ return TXRX_CONTINUE;
+ }
+
+ if (unlikely(/* !injected && */ tx->sdata->ieee802_1x &&
+ !(sta_flags & WLAN_STA_AUTHORIZED))) {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ DECLARE_MAC_BUF(mac);
+ printk(KERN_DEBUG "%s: dropped frame to %s"
+ " (unauthorized port)\n", tx->dev->name,
+ print_mac(mac, hdr->addr1));
+#endif
+ I802_DEBUG_INC(tx->local->tx_handlers_drop_unauth_port);
+ return TXRX_DROP;
+ }
+
+ return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_tx_h_sequence(struct ieee80211_txrx_data *tx)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
+
+ if (ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control)) >= 24)
+ ieee80211_include_sequence(tx->sdata, hdr);
+
+ return TXRX_CONTINUE;
+}
+
+/* This function is called whenever the AP is about to exceed the maximum limit
+ * of buffered frames for power saving STAs. This situation should not really
+ * happen often during normal operation, so dropping the oldest buffered packet
+ * from each queue should be OK to make some room for new frames. */
+static void purge_old_ps_buffers(struct ieee80211_local *local)
+{
+ int total = 0, purged = 0;
+ struct sk_buff *skb;
+ struct ieee80211_sub_if_data *sdata;
+ struct sta_info *sta;
+
+ /*
+ * virtual interfaces are protected by RCU
+ */
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ struct ieee80211_if_ap *ap;
+ if (sdata->dev == local->mdev ||
+ sdata->type != IEEE80211_IF_TYPE_AP)
+ continue;
+ ap = &sdata->u.ap;
+ skb = skb_dequeue(&ap->ps_bc_buf);
+ if (skb) {
+ purged++;
+ dev_kfree_skb(skb);
+ }
+ total += skb_queue_len(&ap->ps_bc_buf);
+ }
+ rcu_read_unlock();
+
+ read_lock_bh(&local->sta_lock);
+ list_for_each_entry(sta, &local->sta_list, list) {
+ skb = skb_dequeue(&sta->ps_tx_buf);
+ if (skb) {
+ purged++;
+ dev_kfree_skb(skb);
+ }
+ total += skb_queue_len(&sta->ps_tx_buf);
+ }
+ read_unlock_bh(&local->sta_lock);
+
+ local->total_ps_buffered = total;
+ printk(KERN_DEBUG "%s: PS buffers full - purged %d frames\n",
+ wiphy_name(local->hw.wiphy), purged);
+}
+
+static inline ieee80211_txrx_result
+ieee80211_tx_h_multicast_ps_buf(struct ieee80211_txrx_data *tx)
+{
+ /* broadcast/multicast frame */
+ /* If any of the associated stations is in power save mode,
+ * the frame is buffered to be sent after DTIM beacon frame */
+ if ((tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING) &&
+ tx->sdata->type != IEEE80211_IF_TYPE_WDS &&
+ tx->sdata->bss && atomic_read(&tx->sdata->bss->num_sta_ps) &&
+ !(tx->fc & IEEE80211_FCTL_ORDER)) {
+ if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
+ purge_old_ps_buffers(tx->local);
+ if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >=
+ AP_MAX_BC_BUFFER) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: BC TX buffer full - "
+ "dropping the oldest frame\n",
+ tx->dev->name);
+ }
+ dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf));
+ } else
+ tx->local->total_ps_buffered++;
+ skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb);
+ return TXRX_QUEUED;
+ }
+
+ return TXRX_CONTINUE;
+}
+
+static inline ieee80211_txrx_result
+ieee80211_tx_h_unicast_ps_buf(struct ieee80211_txrx_data *tx)
+{
+ struct sta_info *sta = tx->sta;
+ DECLARE_MAC_BUF(mac);
+
+ if (unlikely(!sta ||
+ ((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT &&
+ (tx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP)))
+ return TXRX_CONTINUE;
+
+ if (unlikely((sta->flags & WLAN_STA_PS) && !sta->pspoll)) {
+ struct ieee80211_tx_packet_data *pkt_data;
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+ printk(KERN_DEBUG "STA %s aid %d: PS buffer (entries "
+ "before %d)\n",
+ print_mac(mac, sta->addr), sta->aid,
+ skb_queue_len(&sta->ps_tx_buf));
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+ sta->flags |= WLAN_STA_TIM;
+ if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
+ purge_old_ps_buffers(tx->local);
+ if (skb_queue_len(&sta->ps_tx_buf) >= STA_MAX_TX_BUFFER) {
+ struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf);
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: STA %s TX "
+ "buffer full - dropping oldest frame\n",
+ tx->dev->name, print_mac(mac, sta->addr));
+ }
+ dev_kfree_skb(old);
+ } else
+ tx->local->total_ps_buffered++;
+ /* Queue frame to be sent after STA sends an PS Poll frame */
+ if (skb_queue_empty(&sta->ps_tx_buf)) {
+ if (tx->local->ops->set_tim)
+ tx->local->ops->set_tim(local_to_hw(tx->local),
+ sta->aid, 1);
+ if (tx->sdata->bss)
+ bss_tim_set(tx->local, tx->sdata->bss, sta->aid);
+ }
+ pkt_data = (struct ieee80211_tx_packet_data *)tx->skb->cb;
+ pkt_data->jiffies = jiffies;
+ skb_queue_tail(&sta->ps_tx_buf, tx->skb);
+ return TXRX_QUEUED;
+ }
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+ else if (unlikely(sta->flags & WLAN_STA_PS)) {
+ printk(KERN_DEBUG "%s: STA %s in PS mode, but pspoll "
+ "set -> send frame\n", tx->dev->name,
+ print_mac(mac, sta->addr));
+ }
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+ sta->pspoll = 0;
+
+ return TXRX_CONTINUE;
+}
+
+
+static ieee80211_txrx_result
+ieee80211_tx_h_ps_buf(struct ieee80211_txrx_data *tx)
+{
+ if (unlikely(tx->flags & IEEE80211_TXRXD_TXPS_BUFFERED))
+ return TXRX_CONTINUE;
+
+ if (tx->flags & IEEE80211_TXRXD_TXUNICAST)
+ return ieee80211_tx_h_unicast_ps_buf(tx);
+ else
+ return ieee80211_tx_h_multicast_ps_buf(tx);
+}
+
+
+
+
+static ieee80211_txrx_result
+ieee80211_tx_h_select_key(struct ieee80211_txrx_data *tx)
+{
+ struct ieee80211_key *key;
+
+ if (unlikely(tx->u.tx.control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT))
+ tx->key = NULL;
+ else if (tx->sta && (key = rcu_dereference(tx->sta->key)))
+ tx->key = key;
+ else if ((key = rcu_dereference(tx->sdata->default_key)))
+ tx->key = key;
+ else if (tx->sdata->drop_unencrypted &&
+ !(tx->sdata->eapol && ieee80211_is_eapol(tx->skb))) {
+ I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted);
+ return TXRX_DROP;
+ } else {
+ tx->key = NULL;
+ tx->u.tx.control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
+ }
+
+ if (tx->key) {
+ tx->key->tx_rx_count++;
+ /* TODO: add threshold stuff again */
+ }
+
+ return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_tx_h_fragment(struct ieee80211_txrx_data *tx)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
+ size_t hdrlen, per_fragm, num_fragm, payload_len, left;
+ struct sk_buff **frags, *first, *frag;
+ int i;
+ u16 seq;
+ u8 *pos;
+ int frag_threshold = tx->local->fragmentation_threshold;
+
+ if (!(tx->flags & IEEE80211_TXRXD_FRAGMENTED))
+ return TXRX_CONTINUE;
+
+ first = tx->skb;
+
+ hdrlen = ieee80211_get_hdrlen(tx->fc);
+ payload_len = first->len - hdrlen;
+ per_fragm = frag_threshold - hdrlen - FCS_LEN;
+ num_fragm = DIV_ROUND_UP(payload_len, per_fragm);
+
+ frags = kzalloc(num_fragm * sizeof(struct sk_buff *), GFP_ATOMIC);
+ if (!frags)
+ goto fail;
+
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
+ seq = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ;
+ pos = first->data + hdrlen + per_fragm;
+ left = payload_len - per_fragm;
+ for (i = 0; i < num_fragm - 1; i++) {
+ struct ieee80211_hdr *fhdr;
+ size_t copylen;
+
+ if (left <= 0)
+ goto fail;
+
+ /* reserve enough extra head and tail room for possible
+ * encryption */
+ frag = frags[i] =
+ dev_alloc_skb(tx->local->tx_headroom +
+ frag_threshold +
+ IEEE80211_ENCRYPT_HEADROOM +
+ IEEE80211_ENCRYPT_TAILROOM);
+ if (!frag)
+ goto fail;
+ /* Make sure that all fragments use the same priority so
+ * that they end up using the same TX queue */
+ frag->priority = first->priority;
+ skb_reserve(frag, tx->local->tx_headroom +
+ IEEE80211_ENCRYPT_HEADROOM);
+ fhdr = (struct ieee80211_hdr *) skb_put(frag, hdrlen);
+ memcpy(fhdr, first->data, hdrlen);
+ if (i == num_fragm - 2)
+ fhdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREFRAGS);
+ fhdr->seq_ctrl = cpu_to_le16(seq | ((i + 1) & IEEE80211_SCTL_FRAG));
+ copylen = left > per_fragm ? per_fragm : left;
+ memcpy(skb_put(frag, copylen), pos, copylen);
+
+ pos += copylen;
+ left -= copylen;
+ }
+ skb_trim(first, hdrlen + per_fragm);
+
+ tx->u.tx.num_extra_frag = num_fragm - 1;
+ tx->u.tx.extra_frag = frags;
+
+ return TXRX_CONTINUE;
+
+ fail:
+ printk(KERN_DEBUG "%s: failed to fragment frame\n", tx->dev->name);
+ if (frags) {
+ for (i = 0; i < num_fragm - 1; i++)
+ if (frags[i])
+ dev_kfree_skb(frags[i]);
+ kfree(frags);
+ }
+ I802_DEBUG_INC(tx->local->tx_handlers_drop_fragment);
+ return TXRX_DROP;
+}
+
+static ieee80211_txrx_result
+ieee80211_tx_h_encrypt(struct ieee80211_txrx_data *tx)
+{
+ if (!tx->key)
+ return TXRX_CONTINUE;
+
+ switch (tx->key->conf.alg) {
+ case ALG_WEP:
+ return ieee80211_crypto_wep_encrypt(tx);
+ case ALG_TKIP:
+ return ieee80211_crypto_tkip_encrypt(tx);
+ case ALG_CCMP:
+ return ieee80211_crypto_ccmp_encrypt(tx);
+ }
+
+ /* not reached */
+ WARN_ON(1);
+ return TXRX_DROP;
+}
+
+static ieee80211_txrx_result
+ieee80211_tx_h_rate_ctrl(struct ieee80211_txrx_data *tx)
+{
+ struct rate_control_extra extra;
+
+ if (likely(!tx->u.tx.rate)) {
+ memset(&extra, 0, sizeof(extra));
+ extra.mode = tx->u.tx.mode;
+ extra.ethertype = tx->ethertype;
+
+ tx->u.tx.rate = rate_control_get_rate(tx->local, tx->dev,
+ tx->skb, &extra);
+ if (unlikely(extra.probe != NULL)) {
+ tx->u.tx.control->flags |=
+ IEEE80211_TXCTL_RATE_CTRL_PROBE;
+ tx->flags |= IEEE80211_TXRXD_TXPROBE_LAST_FRAG;
+ tx->u.tx.control->alt_retry_rate = tx->u.tx.rate->val;
+ tx->u.tx.rate = extra.probe;
+ } else
+ tx->u.tx.control->alt_retry_rate = -1;
+
+ if (!tx->u.tx.rate)
+ return TXRX_DROP;
+ } else
+ tx->u.tx.control->alt_retry_rate = -1;
+
+ if (tx->u.tx.mode->mode == MODE_IEEE80211G &&
+ (tx->sdata->flags & IEEE80211_SDATA_USE_PROTECTION) &&
+ (tx->flags & IEEE80211_TXRXD_FRAGMENTED) && extra.nonerp) {
+ tx->u.tx.last_frag_rate = tx->u.tx.rate;
+ if (extra.probe)
+ tx->flags &= ~IEEE80211_TXRXD_TXPROBE_LAST_FRAG;
+ else
+ tx->flags |= IEEE80211_TXRXD_TXPROBE_LAST_FRAG;
+ tx->u.tx.rate = extra.nonerp;
+ tx->u.tx.control->rate = extra.nonerp;
+ tx->u.tx.control->flags &= ~IEEE80211_TXCTL_RATE_CTRL_PROBE;
+ } else {
+ tx->u.tx.last_frag_rate = tx->u.tx.rate;
+ tx->u.tx.control->rate = tx->u.tx.rate;
+ }
+ tx->u.tx.control->tx_rate = tx->u.tx.rate->val;
+
+ return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
+ u16 fc = le16_to_cpu(hdr->frame_control);
+ u16 dur;
+ struct ieee80211_tx_control *control = tx->u.tx.control;
+ struct ieee80211_hw_mode *mode = tx->u.tx.mode;
+
+ if (!control->retry_limit) {
+ if (!is_multicast_ether_addr(hdr->addr1)) {
+ if (tx->skb->len + FCS_LEN > tx->local->rts_threshold
+ && tx->local->rts_threshold <
+ IEEE80211_MAX_RTS_THRESHOLD) {
+ control->flags |=
+ IEEE80211_TXCTL_USE_RTS_CTS;
+ control->flags |=
+ IEEE80211_TXCTL_LONG_RETRY_LIMIT;
+ control->retry_limit =
+ tx->local->long_retry_limit;
+ } else {
+ control->retry_limit =
+ tx->local->short_retry_limit;
+ }
+ } else {
+ control->retry_limit = 1;
+ }
+ }
+
+ if (tx->flags & IEEE80211_TXRXD_FRAGMENTED) {
+ /* Do not use multiple retry rates when sending fragmented
+ * frames.
+ * TODO: The last fragment could still use multiple retry
+ * rates. */
+ control->alt_retry_rate = -1;
+ }
+
+ /* Use CTS protection for unicast frames sent using extended rates if
+ * there are associated non-ERP stations and RTS/CTS is not configured
+ * for the frame. */
+ if (mode->mode == MODE_IEEE80211G &&
+ (tx->u.tx.rate->flags & IEEE80211_RATE_ERP) &&
+ (tx->flags & IEEE80211_TXRXD_TXUNICAST) &&
+ (tx->sdata->flags & IEEE80211_SDATA_USE_PROTECTION) &&
+ !(control->flags & IEEE80211_TXCTL_USE_RTS_CTS))
+ control->flags |= IEEE80211_TXCTL_USE_CTS_PROTECT;
+
+ /* Transmit data frames using short preambles if the driver supports
+ * short preambles at the selected rate and short preambles are
+ * available on the network at the current point in time. */
+ if (((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) &&
+ (tx->u.tx.rate->flags & IEEE80211_RATE_PREAMBLE2) &&
+ (tx->sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE) &&
+ (!tx->sta || (tx->sta->flags & WLAN_STA_SHORT_PREAMBLE))) {
+ tx->u.tx.control->tx_rate = tx->u.tx.rate->val2;
+ }
+
+ /* Setup duration field for the first fragment of the frame. Duration
+ * for remaining fragments will be updated when they are being sent
+ * to low-level driver in ieee80211_tx(). */
+ dur = ieee80211_duration(tx, is_multicast_ether_addr(hdr->addr1),
+ (tx->flags & IEEE80211_TXRXD_FRAGMENTED) ?
+ tx->u.tx.extra_frag[0]->len : 0);
+ hdr->duration_id = cpu_to_le16(dur);
+
+ if ((control->flags & IEEE80211_TXCTL_USE_RTS_CTS) ||
+ (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)) {
+ struct ieee80211_rate *rate;
+
+ /* Do not use multiple retry rates when using RTS/CTS */
+ control->alt_retry_rate = -1;
+
+ /* Use min(data rate, max base rate) as CTS/RTS rate */
+ rate = tx->u.tx.rate;
+ while (rate > mode->rates &&
+ !(rate->flags & IEEE80211_RATE_BASIC))
+ rate--;
+
+ control->rts_cts_rate = rate->val;
+ control->rts_rate = rate;
+ }
+
+ if (tx->sta) {
+ tx->sta->tx_packets++;
+ tx->sta->tx_fragments++;
+ tx->sta->tx_bytes += tx->skb->len;
+ if (tx->u.tx.extra_frag) {
+ int i;
+ tx->sta->tx_fragments += tx->u.tx.num_extra_frag;
+ for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
+ tx->sta->tx_bytes +=
+ tx->u.tx.extra_frag[i]->len;
+ }
+ }
+ }
+
+ /*
+ * Tell hardware to not encrypt when we had sw crypto.
+ * Because we use the same flag to internally indicate that
+ * no (software) encryption should be done, we have to set it
+ * after all crypto handlers.
+ */
+ if (tx->key && !(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
+ tx->u.tx.control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
+
+ return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_tx_h_load_stats(struct ieee80211_txrx_data *tx)
+{
+ struct ieee80211_local *local = tx->local;
+ struct ieee80211_hw_mode *mode = tx->u.tx.mode;
+ struct sk_buff *skb = tx->skb;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ u32 load = 0, hdrtime;
+
+ /* TODO: this could be part of tx_status handling, so that the number
+ * of retries would be known; TX rate should in that case be stored
+ * somewhere with the packet */
+
+ /* Estimate total channel use caused by this frame */
+
+ /* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values,
+ * 1 usec = 1/8 * (1080 / 10) = 13.5 */
+
+ if (mode->mode == MODE_IEEE80211A ||
+ (mode->mode == MODE_IEEE80211G &&
+ tx->u.tx.rate->flags & IEEE80211_RATE_ERP))
+ hdrtime = CHAN_UTIL_HDR_SHORT;
+ else
+ hdrtime = CHAN_UTIL_HDR_LONG;
+
+ load = hdrtime;
+ if (!is_multicast_ether_addr(hdr->addr1))
+ load += hdrtime;
+
+ if (tx->u.tx.control->flags & IEEE80211_TXCTL_USE_RTS_CTS)
+ load += 2 * hdrtime;
+ else if (tx->u.tx.control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)
+ load += hdrtime;
+
+ load += skb->len * tx->u.tx.rate->rate_inv;
+
+ if (tx->u.tx.extra_frag) {
+ int i;
+ for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
+ load += 2 * hdrtime;
+ load += tx->u.tx.extra_frag[i]->len *
+ tx->u.tx.rate->rate;
+ }
+ }
+
+ /* Divide channel_use by 8 to avoid wrapping around the counter */
+ load >>= CHAN_UTIL_SHIFT;
+ local->channel_use_raw += load;
+ if (tx->sta)
+ tx->sta->channel_use_raw += load;
+ tx->sdata->channel_use_raw += load;
+
+ return TXRX_CONTINUE;
+}
+
+/* TODO: implement register/unregister functions for adding TX/RX handlers
+ * into ordered list */
+
+ieee80211_tx_handler ieee80211_tx_handlers[] =
+{
+ ieee80211_tx_h_check_assoc,
+ ieee80211_tx_h_sequence,
+ ieee80211_tx_h_ps_buf,
+ ieee80211_tx_h_select_key,
+ ieee80211_tx_h_michael_mic_add,
+ ieee80211_tx_h_fragment,
+ ieee80211_tx_h_encrypt,
+ ieee80211_tx_h_rate_ctrl,
+ ieee80211_tx_h_misc,
+ ieee80211_tx_h_load_stats,
+ NULL
+};
+
+/* actual transmit path */
+
+/*
+ * deal with packet injection down monitor interface
+ * with Radiotap Header -- only called for monitor mode interface
+ */
+static ieee80211_txrx_result
+__ieee80211_parse_tx_radiotap(struct ieee80211_txrx_data *tx,
+ struct sk_buff *skb)
+{
+ /*
+ * this is the moment to interpret and discard the radiotap header that
+ * must be at the start of the packet injected in Monitor mode
+ *
+ * Need to take some care with endian-ness since radiotap
+ * args are little-endian
+ */
+
+ struct ieee80211_radiotap_iterator iterator;
+ struct ieee80211_radiotap_header *rthdr =
+ (struct ieee80211_radiotap_header *) skb->data;
+ struct ieee80211_hw_mode *mode = tx->local->hw.conf.mode;
+ int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
+ struct ieee80211_tx_control *control = tx->u.tx.control;
+
+ control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
+ tx->flags |= IEEE80211_TXRXD_TX_INJECTED;
+ tx->flags &= ~IEEE80211_TXRXD_FRAGMENTED;
+
+ /*
+ * for every radiotap entry that is present
+ * (ieee80211_radiotap_iterator_next returns -ENOENT when no more
+ * entries present, or -EINVAL on error)
+ */
+
+ while (!ret) {
+ int i, target_rate;
+
+ ret = ieee80211_radiotap_iterator_next(&iterator);
+
+ if (ret)
+ continue;
+
+ /* see if this argument is something we can use */
+ switch (iterator.this_arg_index) {
+ /*
+ * You must take care when dereferencing iterator.this_arg
+ * for multibyte types... the pointer is not aligned. Use
+ * get_unaligned((type *)iterator.this_arg) to dereference
+ * iterator.this_arg for type "type" safely on all arches.
+ */
+ case IEEE80211_RADIOTAP_RATE:
+ /*
+ * radiotap rate u8 is in 500kbps units eg, 0x02=1Mbps
+ * ieee80211 rate int is in 100kbps units eg, 0x0a=1Mbps
+ */
+ target_rate = (*iterator.this_arg) * 5;
+ for (i = 0; i < mode->num_rates; i++) {
+ struct ieee80211_rate *r = &mode->rates[i];
+
+ if (r->rate == target_rate) {
+ tx->u.tx.rate = r;
+ break;
+ }
+ }
+ break;
+
+ case IEEE80211_RADIOTAP_ANTENNA:
+ /*
+ * radiotap uses 0 for 1st ant, mac80211 is 1 for
+ * 1st ant
+ */
+ control->antenna_sel_tx = (*iterator.this_arg) + 1;
+ break;
+
+ case IEEE80211_RADIOTAP_DBM_TX_POWER:
+ control->power_level = *iterator.this_arg;
+ break;
+
+ case IEEE80211_RADIOTAP_FLAGS:
+ if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FCS) {
+ /*
+ * this indicates that the skb we have been
+ * handed has the 32-bit FCS CRC at the end...
+ * we should react to that by snipping it off
+ * because it will be recomputed and added
+ * on transmission
+ */
+ if (skb->len < (iterator.max_length + FCS_LEN))
+ return TXRX_DROP;
+
+ skb_trim(skb, skb->len - FCS_LEN);
+ }
+ if (*iterator.this_arg & IEEE80211_RADIOTAP_F_WEP)
+ control->flags &=
+ ~IEEE80211_TXCTL_DO_NOT_ENCRYPT;
+ if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FRAG)
+ tx->flags |= IEEE80211_TXRXD_FRAGMENTED;
+ break;
+
+ /*
+ * Please update the file
+ * Documentation/networking/mac80211-injection.txt
+ * when parsing new fields here.
+ */
+
+ default:
+ break;
+ }
+ }
+
+ if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */
+ return TXRX_DROP;
+
+ /*
+ * remove the radiotap header
+ * iterator->max_length was sanity-checked against
+ * skb->len by iterator init
+ */
+ skb_pull(skb, iterator.max_length);
+
+ return TXRX_CONTINUE;
+}
+
+/*
+ * initialises @tx
+ */
+static ieee80211_txrx_result
+__ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
+ struct sk_buff *skb,
+ struct net_device *dev,
+ struct ieee80211_tx_control *control)
+{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_hdr *hdr;
+ struct ieee80211_sub_if_data *sdata;
+ ieee80211_txrx_result res = TXRX_CONTINUE;
+
+ int hdrlen;
+
+ memset(tx, 0, sizeof(*tx));
+ tx->skb = skb;
+ tx->dev = dev; /* use original interface */
+ tx->local = local;
+ tx->sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ tx->u.tx.control = control;
+ /*
+ * Set this flag (used below to indicate "automatic fragmentation"),
+ * it will be cleared/left by radiotap as desired.
+ */
+ tx->flags |= IEEE80211_TXRXD_FRAGMENTED;
+
+ /* process and remove the injection radiotap header */
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ if (unlikely(sdata->type == IEEE80211_IF_TYPE_MNTR)) {
+ if (__ieee80211_parse_tx_radiotap(tx, skb) == TXRX_DROP)
+ return TXRX_DROP;
+
+ /*
+ * __ieee80211_parse_tx_radiotap has now removed
+ * the radiotap header that was present and pre-filled
+ * 'tx' with tx control information.
+ */
+ }
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+
+ tx->sta = sta_info_get(local, hdr->addr1);
+ tx->fc = le16_to_cpu(hdr->frame_control);
+
+ if (is_multicast_ether_addr(hdr->addr1)) {
+ tx->flags &= ~IEEE80211_TXRXD_TXUNICAST;
+ control->flags |= IEEE80211_TXCTL_NO_ACK;
+ } else {
+ tx->flags |= IEEE80211_TXRXD_TXUNICAST;
+ control->flags &= ~IEEE80211_TXCTL_NO_ACK;
+ }
+
+ if (tx->flags & IEEE80211_TXRXD_FRAGMENTED) {
+ if ((tx->flags & IEEE80211_TXRXD_TXUNICAST) &&
+ skb->len + FCS_LEN > local->fragmentation_threshold &&
+ !local->ops->set_frag_threshold)
+ tx->flags |= IEEE80211_TXRXD_FRAGMENTED;
+ else
+ tx->flags &= ~IEEE80211_TXRXD_FRAGMENTED;
+ }
+
+ if (!tx->sta)
+ control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK;
+ else if (tx->sta->clear_dst_mask) {
+ control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK;
+ tx->sta->clear_dst_mask = 0;
+ }
+
+ hdrlen = ieee80211_get_hdrlen(tx->fc);
+ if (skb->len > hdrlen + sizeof(rfc1042_header) + 2) {
+ u8 *pos = &skb->data[hdrlen + sizeof(rfc1042_header)];
+ tx->ethertype = (pos[0] << 8) | pos[1];
+ }
+ control->flags |= IEEE80211_TXCTL_FIRST_FRAGMENT;
+
+ return res;
+}
+
+/* Device in tx->dev has a reference added; use dev_put(tx->dev) when
+ * finished with it.
+ *
+ * NB: @tx is uninitialised when passed in here
+ */
+static int ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
+ struct sk_buff *skb,
+ struct net_device *mdev,
+ struct ieee80211_tx_control *control)
+{
+ struct ieee80211_tx_packet_data *pkt_data;
+ struct net_device *dev;
+
+ pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
+ dev = dev_get_by_index(&init_net, pkt_data->ifindex);
+ if (unlikely(dev && !is_ieee80211_device(dev, mdev))) {
+ dev_put(dev);
+ dev = NULL;
+ }
+ if (unlikely(!dev))
+ return -ENODEV;
+ /* initialises tx with control */
+ __ieee80211_tx_prepare(tx, skb, dev, control);
+ return 0;
+}
+
+static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
+ struct ieee80211_txrx_data *tx)
+{
+ struct ieee80211_tx_control *control = tx->u.tx.control;
+ int ret, i;
+
+ if (!ieee80211_qdisc_installed(local->mdev) &&
+ __ieee80211_queue_stopped(local, 0)) {
+ netif_stop_queue(local->mdev);
+ return IEEE80211_TX_AGAIN;
+ }
+ if (skb) {
+ ieee80211_dump_frame(wiphy_name(local->hw.wiphy),
+ "TX to low-level driver", skb);
+ ret = local->ops->tx(local_to_hw(local), skb, control);
+ if (ret)
+ return IEEE80211_TX_AGAIN;
+ local->mdev->trans_start = jiffies;
+ ieee80211_led_tx(local, 1);
+ }
+ if (tx->u.tx.extra_frag) {
+ control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS |
+ IEEE80211_TXCTL_USE_CTS_PROTECT |
+ IEEE80211_TXCTL_CLEAR_DST_MASK |
+ IEEE80211_TXCTL_FIRST_FRAGMENT);
+ for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
+ if (!tx->u.tx.extra_frag[i])
+ continue;
+ if (__ieee80211_queue_stopped(local, control->queue))
+ return IEEE80211_TX_FRAG_AGAIN;
+ if (i == tx->u.tx.num_extra_frag) {
+ control->tx_rate = tx->u.tx.last_frag_hwrate;
+ control->rate = tx->u.tx.last_frag_rate;
+ if (tx->flags & IEEE80211_TXRXD_TXPROBE_LAST_FRAG)
+ control->flags |=
+ IEEE80211_TXCTL_RATE_CTRL_PROBE;
+ else
+ control->flags &=
+ ~IEEE80211_TXCTL_RATE_CTRL_PROBE;
+ }
+
+ ieee80211_dump_frame(wiphy_name(local->hw.wiphy),
+ "TX to low-level driver",
+ tx->u.tx.extra_frag[i]);
+ ret = local->ops->tx(local_to_hw(local),
+ tx->u.tx.extra_frag[i],
+ control);
+ if (ret)
+ return IEEE80211_TX_FRAG_AGAIN;
+ local->mdev->trans_start = jiffies;
+ ieee80211_led_tx(local, 1);
+ tx->u.tx.extra_frag[i] = NULL;
+ }
+ kfree(tx->u.tx.extra_frag);
+ tx->u.tx.extra_frag = NULL;
+ }
+ return IEEE80211_TX_OK;
+}
+
+static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
+ struct ieee80211_tx_control *control)
+{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct sta_info *sta;
+ ieee80211_tx_handler *handler;
+ struct ieee80211_txrx_data tx;
+ ieee80211_txrx_result res = TXRX_DROP, res_prepare;
+ int ret, i;
+
+ WARN_ON(__ieee80211_queue_pending(local, control->queue));
+
+ if (unlikely(skb->len < 10)) {
+ dev_kfree_skb(skb);
+ return 0;
+ }
+
+ /* initialises tx */
+ res_prepare = __ieee80211_tx_prepare(&tx, skb, dev, control);
+
+ if (res_prepare == TXRX_DROP) {
+ dev_kfree_skb(skb);
+ return 0;
+ }
+
+ /*
+ * key references are protected using RCU and this requires that
+ * we are in a read-site RCU section during receive processing
+ */
+ rcu_read_lock();
+
+ sta = tx.sta;
+ tx.u.tx.mode = local->hw.conf.mode;
+
+ for (handler = local->tx_handlers; *handler != NULL;
+ handler++) {
+ res = (*handler)(&tx);
+ if (res != TXRX_CONTINUE)
+ break;
+ }
+
+ skb = tx.skb; /* handlers are allowed to change skb */
+
+ if (sta)
+ sta_info_put(sta);
+
+ if (unlikely(res == TXRX_DROP)) {
+ I802_DEBUG_INC(local->tx_handlers_drop);
+ goto drop;
+ }
+
+ if (unlikely(res == TXRX_QUEUED)) {
+ I802_DEBUG_INC(local->tx_handlers_queued);
+ rcu_read_unlock();
+ return 0;
+ }
+
+ if (tx.u.tx.extra_frag) {
+ for (i = 0; i < tx.u.tx.num_extra_frag; i++) {
+ int next_len, dur;
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *)
+ tx.u.tx.extra_frag[i]->data;
+
+ if (i + 1 < tx.u.tx.num_extra_frag) {
+ next_len = tx.u.tx.extra_frag[i + 1]->len;
+ } else {
+ next_len = 0;
+ tx.u.tx.rate = tx.u.tx.last_frag_rate;
+ tx.u.tx.last_frag_hwrate = tx.u.tx.rate->val;
+ }
+ dur = ieee80211_duration(&tx, 0, next_len);
+ hdr->duration_id = cpu_to_le16(dur);
+ }
+ }
+
+retry:
+ ret = __ieee80211_tx(local, skb, &tx);
+ if (ret) {
+ struct ieee80211_tx_stored_packet *store =
+ &local->pending_packet[control->queue];
+
+ if (ret == IEEE80211_TX_FRAG_AGAIN)
+ skb = NULL;
+ set_bit(IEEE80211_LINK_STATE_PENDING,
+ &local->state[control->queue]);
+ smp_mb();
+ /* When the driver gets out of buffers during sending of
+ * fragments and calls ieee80211_stop_queue, there is
+ * a small window between IEEE80211_LINK_STATE_XOFF and
+ * IEEE80211_LINK_STATE_PENDING flags are set. If a buffer
+ * gets available in that window (i.e. driver calls
+ * ieee80211_wake_queue), we would end up with ieee80211_tx
+ * called with IEEE80211_LINK_STATE_PENDING. Prevent this by
+ * continuing transmitting here when that situation is
+ * possible to have happened. */
+ if (!__ieee80211_queue_stopped(local, control->queue)) {
+ clear_bit(IEEE80211_LINK_STATE_PENDING,
+ &local->state[control->queue]);
+ goto retry;
+ }
+ memcpy(&store->control, control,
+ sizeof(struct ieee80211_tx_control));
+ store->skb = skb;
+ store->extra_frag = tx.u.tx.extra_frag;
+ store->num_extra_frag = tx.u.tx.num_extra_frag;
+ store->last_frag_hwrate = tx.u.tx.last_frag_hwrate;
+ store->last_frag_rate = tx.u.tx.last_frag_rate;
+ store->last_frag_rate_ctrl_probe =
+ !!(tx.flags & IEEE80211_TXRXD_TXPROBE_LAST_FRAG);
+ }
+ rcu_read_unlock();
+ return 0;
+
+ drop:
+ if (skb)
+ dev_kfree_skb(skb);
+ for (i = 0; i < tx.u.tx.num_extra_frag; i++)
+ if (tx.u.tx.extra_frag[i])
+ dev_kfree_skb(tx.u.tx.extra_frag[i]);
+ kfree(tx.u.tx.extra_frag);
+ rcu_read_unlock();
+ return 0;
+}
+
+/* device xmit handlers */
+
+int ieee80211_master_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct ieee80211_tx_control control;
+ struct ieee80211_tx_packet_data *pkt_data;
+ struct net_device *odev = NULL;
+ struct ieee80211_sub_if_data *osdata;
+ int headroom;
+ int ret;
+
+ /*
+ * copy control out of the skb so other people can use skb->cb
+ */
+ pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
+ memset(&control, 0, sizeof(struct ieee80211_tx_control));
+
+ if (pkt_data->ifindex)
+ odev = dev_get_by_index(&init_net, pkt_data->ifindex);
+ if (unlikely(odev && !is_ieee80211_device(odev, dev))) {
+ dev_put(odev);
+ odev = NULL;
+ }
+ if (unlikely(!odev)) {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ printk(KERN_DEBUG "%s: Discarded packet with nonexistent "
+ "originating device\n", dev->name);
+#endif
+ dev_kfree_skb(skb);
+ return 0;
+ }
+ osdata = IEEE80211_DEV_TO_SUB_IF(odev);
+
+ headroom = osdata->local->tx_headroom + IEEE80211_ENCRYPT_HEADROOM;
+ if (skb_headroom(skb) < headroom) {
+ if (pskb_expand_head(skb, headroom, 0, GFP_ATOMIC)) {
+ dev_kfree_skb(skb);
+ dev_put(odev);
+ return 0;
+ }
+ }
+
+ control.ifindex = odev->ifindex;
+ control.type = osdata->type;
+ if (pkt_data->flags & IEEE80211_TXPD_REQ_TX_STATUS)
+ control.flags |= IEEE80211_TXCTL_REQ_TX_STATUS;
+ if (pkt_data->flags & IEEE80211_TXPD_DO_NOT_ENCRYPT)
+ control.flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
+ if (pkt_data->flags & IEEE80211_TXPD_REQUEUE)
+ control.flags |= IEEE80211_TXCTL_REQUEUE;
+ control.queue = pkt_data->queue;
+
+ ret = ieee80211_tx(odev, skb, &control);
+ dev_put(odev);
+
+ return ret;
+}
+
+int ieee80211_monitor_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_tx_packet_data *pkt_data;
+ struct ieee80211_radiotap_header *prthdr =
+ (struct ieee80211_radiotap_header *)skb->data;
+ u16 len_rthdr;
+
+ /* check for not even having the fixed radiotap header part */
+ if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header)))
+ goto fail; /* too short to be possibly valid */
+
+ /* is it a header version we can trust to find length from? */
+ if (unlikely(prthdr->it_version))
+ goto fail; /* only version 0 is supported */
+
+ /* then there must be a radiotap header with a length we can use */
+ len_rthdr = ieee80211_get_radiotap_len(skb->data);
+
+ /* does the skb contain enough to deliver on the alleged length? */
+ if (unlikely(skb->len < len_rthdr))
+ goto fail; /* skb too short for claimed rt header extent */
+
+ skb->dev = local->mdev;
+
+ pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
+ memset(pkt_data, 0, sizeof(*pkt_data));
+ /* needed because we set skb device to master */
+ pkt_data->ifindex = dev->ifindex;
+
+ pkt_data->flags |= IEEE80211_TXPD_DO_NOT_ENCRYPT;
+
+ /*
+ * fix up the pointers accounting for the radiotap
+ * header still being in there. We are being given
+ * a precooked IEEE80211 header so no need for
+ * normal processing
+ */
+ skb_set_mac_header(skb, len_rthdr);
+ /*
+ * these are just fixed to the end of the rt area since we
+ * don't have any better information and at this point, nobody cares
+ */
+ skb_set_network_header(skb, len_rthdr);
+ skb_set_transport_header(skb, len_rthdr);
+
+ /* pass the radiotap header up to the next stage intact */
+ dev_queue_xmit(skb);
+ return NETDEV_TX_OK;
+
+fail:
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK; /* meaning, we dealt with the skb */
+}
+
+/**
+ * ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type
+ * subinterfaces (wlan#, WDS, and VLAN interfaces)
+ * @skb: packet to be sent
+ * @dev: incoming interface
+ *
+ * Returns: 0 on success (and frees skb in this case) or 1 on failure (skb will
+ * not be freed, and caller is responsible for either retrying later or freeing
+ * skb).
+ *
+ * This function takes in an Ethernet header and encapsulates it with suitable
+ * IEEE 802.11 header based on which interface the packet is coming in. The
+ * encapsulated packet will then be passed to master interface, wlan#.11, for
+ * transmission (through low-level driver).
+ */
+int ieee80211_subif_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_tx_packet_data *pkt_data;
+ struct ieee80211_sub_if_data *sdata;
+ int ret = 1, head_need;
+ u16 ethertype, hdrlen, fc;
+ struct ieee80211_hdr hdr;
+ const u8 *encaps_data;
+ int encaps_len, skip_header_bytes;
+ int nh_pos, h_pos;
+ struct sta_info *sta;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ if (unlikely(skb->len < ETH_HLEN)) {
+ printk(KERN_DEBUG "%s: short skb (len=%d)\n",
+ dev->name, skb->len);
+ ret = 0;
+ goto fail;
+ }
+
+ nh_pos = skb_network_header(skb) - skb->data;
+ h_pos = skb_transport_header(skb) - skb->data;
+
+ /* convert Ethernet header to proper 802.11 header (based on
+ * operation mode) */
+ ethertype = (skb->data[12] << 8) | skb->data[13];
+ /* TODO: handling for 802.1x authorized/unauthorized port */
+ fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA;
+
+ switch (sdata->type) {
+ case IEEE80211_IF_TYPE_AP:
+ case IEEE80211_IF_TYPE_VLAN:
+ fc |= IEEE80211_FCTL_FROMDS;
+ /* DA BSSID SA */
+ memcpy(hdr.addr1, skb->data, ETH_ALEN);
+ memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
+ memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
+ hdrlen = 24;
+ break;
+ case IEEE80211_IF_TYPE_WDS:
+ fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS;
+ /* RA TA DA SA */
+ memcpy(hdr.addr1, sdata->u.wds.remote_addr, ETH_ALEN);
+ memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
+ memcpy(hdr.addr3, skb->data, ETH_ALEN);
+ memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
+ hdrlen = 30;
+ break;
+ case IEEE80211_IF_TYPE_STA:
+ fc |= IEEE80211_FCTL_TODS;
+ /* BSSID SA DA */
+ memcpy(hdr.addr1, sdata->u.sta.bssid, ETH_ALEN);
+ memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+ memcpy(hdr.addr3, skb->data, ETH_ALEN);
+ hdrlen = 24;
+ break;
+ case IEEE80211_IF_TYPE_IBSS:
+ /* DA SA BSSID */
+ memcpy(hdr.addr1, skb->data, ETH_ALEN);
+ memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+ memcpy(hdr.addr3, sdata->u.sta.bssid, ETH_ALEN);
+ hdrlen = 24;
+ break;
+ default:
+ ret = 0;
+ goto fail;
+ }
+
+ /* receiver is QoS enabled, use a QoS type frame */
+ sta = sta_info_get(local, hdr.addr1);
+ if (sta) {
+ if (sta->flags & WLAN_STA_WME) {
+ fc |= IEEE80211_STYPE_QOS_DATA;
+ hdrlen += 2;
+ }
+ sta_info_put(sta);
+ }
+
+ hdr.frame_control = cpu_to_le16(fc);
+ hdr.duration_id = 0;
+ hdr.seq_ctrl = 0;
+
+ skip_header_bytes = ETH_HLEN;
+ if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) {
+ encaps_data = bridge_tunnel_header;
+ encaps_len = sizeof(bridge_tunnel_header);
+ skip_header_bytes -= 2;
+ } else if (ethertype >= 0x600) {
+ encaps_data = rfc1042_header;
+ encaps_len = sizeof(rfc1042_header);
+ skip_header_bytes -= 2;
+ } else {
+ encaps_data = NULL;
+ encaps_len = 0;
+ }
+
+ skb_pull(skb, skip_header_bytes);
+ nh_pos -= skip_header_bytes;
+ h_pos -= skip_header_bytes;
+
+ /* TODO: implement support for fragments so that there is no need to
+ * reallocate and copy payload; it might be enough to support one
+ * extra fragment that would be copied in the beginning of the frame
+ * data.. anyway, it would be nice to include this into skb structure
+ * somehow
+ *
+ * There are few options for this:
+ * use skb->cb as an extra space for 802.11 header
+ * allocate new buffer if not enough headroom
+ * make sure that there is enough headroom in every skb by increasing
+ * build in headroom in __dev_alloc_skb() (linux/skbuff.h) and
+ * alloc_skb() (net/core/skbuff.c)
+ */
+ head_need = hdrlen + encaps_len + local->tx_headroom;
+ head_need -= skb_headroom(skb);
+
+ /* We are going to modify skb data, so make a copy of it if happens to
+ * be cloned. This could happen, e.g., with Linux bridge code passing
+ * us broadcast frames. */
+
+ if (head_need > 0 || skb_cloned(skb)) {
+#if 0
+ printk(KERN_DEBUG "%s: need to reallocate buffer for %d bytes "
+ "of headroom\n", dev->name, head_need);
+#endif
+
+ if (skb_cloned(skb))
+ I802_DEBUG_INC(local->tx_expand_skb_head_cloned);
+ else
+ I802_DEBUG_INC(local->tx_expand_skb_head);
+ /* Since we have to reallocate the buffer, make sure that there
+ * is enough room for possible WEP IV/ICV and TKIP (8 bytes
+ * before payload and 12 after). */
+ if (pskb_expand_head(skb, (head_need > 0 ? head_need + 8 : 8),
+ 12, GFP_ATOMIC)) {
+ printk(KERN_DEBUG "%s: failed to reallocate TX buffer"
+ "\n", dev->name);
+ goto fail;
+ }
+ }
+
+ if (encaps_data) {
+ memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len);
+ nh_pos += encaps_len;
+ h_pos += encaps_len;
+ }
+
+ if (fc & IEEE80211_STYPE_QOS_DATA) {
+ __le16 *qos_control;
+
+ qos_control = (__le16*) skb_push(skb, 2);
+ memcpy(skb_push(skb, hdrlen - 2), &hdr, hdrlen - 2);
+ /*
+ * Maybe we could actually set some fields here, for now just
+ * initialise to zero to indicate no special operation.
+ */
+ *qos_control = 0;
+ } else
+ memcpy(skb_push(skb, hdrlen), &hdr, hdrlen);
+
+ nh_pos += hdrlen;
+ h_pos += hdrlen;
+
+ pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
+ memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
+ pkt_data->ifindex = dev->ifindex;
+
+ skb->dev = local->mdev;
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+
+ /* Update skb pointers to various headers since this modified frame
+ * is going to go through Linux networking code that may potentially
+ * need things like pointer to IP header. */
+ skb_set_mac_header(skb, 0);
+ skb_set_network_header(skb, nh_pos);
+ skb_set_transport_header(skb, h_pos);
+
+ dev->trans_start = jiffies;
+ dev_queue_xmit(skb);
+
+ return 0;
+
+ fail:
+ if (!ret)
+ dev_kfree_skb(skb);
+
+ return ret;
+}
+
+/*
+ * This is the transmit routine for the 802.11 type interfaces
+ * called by upper layers of the linux networking
+ * stack when it has a frame to transmit
+ */
+int ieee80211_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_tx_packet_data *pkt_data;
+ struct ieee80211_hdr *hdr;
+ u16 fc;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (skb->len < 10) {
+ dev_kfree_skb(skb);
+ return 0;
+ }
+
+ if (skb_headroom(skb) < sdata->local->tx_headroom) {
+ if (pskb_expand_head(skb, sdata->local->tx_headroom,
+ 0, GFP_ATOMIC)) {
+ dev_kfree_skb(skb);
+ return 0;
+ }
+ }
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ fc = le16_to_cpu(hdr->frame_control);
+
+ pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
+ memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
+ pkt_data->ifindex = sdata->dev->ifindex;
+
+ skb->priority = 20; /* use hardcoded priority for mgmt TX queue */
+ skb->dev = sdata->local->mdev;
+
+ /*
+ * We're using the protocol field of the the frame control header
+ * to request TX callback for hostapd. BIT(1) is checked.
+ */
+ if ((fc & BIT(1)) == BIT(1)) {
+ pkt_data->flags |= IEEE80211_TXPD_REQ_TX_STATUS;
+ fc &= ~BIT(1);
+ hdr->frame_control = cpu_to_le16(fc);
+ }
+
+ if (!(fc & IEEE80211_FCTL_PROTECTED))
+ pkt_data->flags |= IEEE80211_TXPD_DO_NOT_ENCRYPT;
+
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+
+ dev_queue_xmit(skb);
+
+ return 0;
+}
+
+/* helper functions for pending packets for when queues are stopped */
+
+void ieee80211_clear_tx_pending(struct ieee80211_local *local)
+{
+ int i, j;
+ struct ieee80211_tx_stored_packet *store;
+
+ for (i = 0; i < local->hw.queues; i++) {
+ if (!__ieee80211_queue_pending(local, i))
+ continue;
+ store = &local->pending_packet[i];
+ kfree_skb(store->skb);
+ for (j = 0; j < store->num_extra_frag; j++)
+ kfree_skb(store->extra_frag[j]);
+ kfree(store->extra_frag);
+ clear_bit(IEEE80211_LINK_STATE_PENDING, &local->state[i]);
+ }
+}
+
+void ieee80211_tx_pending(unsigned long data)
+{
+ struct ieee80211_local *local = (struct ieee80211_local *)data;
+ struct net_device *dev = local->mdev;
+ struct ieee80211_tx_stored_packet *store;
+ struct ieee80211_txrx_data tx;
+ int i, ret, reschedule = 0;
+
+ netif_tx_lock_bh(dev);
+ for (i = 0; i < local->hw.queues; i++) {
+ if (__ieee80211_queue_stopped(local, i))
+ continue;
+ if (!__ieee80211_queue_pending(local, i)) {
+ reschedule = 1;
+ continue;
+ }
+ store = &local->pending_packet[i];
+ tx.u.tx.control = &store->control;
+ tx.u.tx.extra_frag = store->extra_frag;
+ tx.u.tx.num_extra_frag = store->num_extra_frag;
+ tx.u.tx.last_frag_hwrate = store->last_frag_hwrate;
+ tx.u.tx.last_frag_rate = store->last_frag_rate;
+ tx.flags = 0;
+ if (store->last_frag_rate_ctrl_probe)
+ tx.flags |= IEEE80211_TXRXD_TXPROBE_LAST_FRAG;
+ ret = __ieee80211_tx(local, store->skb, &tx);
+ if (ret) {
+ if (ret == IEEE80211_TX_FRAG_AGAIN)
+ store->skb = NULL;
+ } else {
+ clear_bit(IEEE80211_LINK_STATE_PENDING,
+ &local->state[i]);
+ reschedule = 1;
+ }
+ }
+ netif_tx_unlock_bh(dev);
+ if (reschedule) {
+ if (!ieee80211_qdisc_installed(dev)) {
+ if (!__ieee80211_queue_stopped(local, 0))
+ netif_wake_queue(dev);
+ } else
+ netif_schedule(dev);
+ }
+}
+
+/* functions for drivers to get certain frames */
+
+static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
+ struct ieee80211_if_ap *bss,
+ struct sk_buff *skb)
+{
+ u8 *pos, *tim;
+ int aid0 = 0;
+ int i, have_bits = 0, n1, n2;
+
+ /* Generate bitmap for TIM only if there are any STAs in power save
+ * mode. */
+ read_lock_bh(&local->sta_lock);
+ if (atomic_read(&bss->num_sta_ps) > 0)
+ /* in the hope that this is faster than
+ * checking byte-for-byte */
+ have_bits = !bitmap_empty((unsigned long*)bss->tim,
+ IEEE80211_MAX_AID+1);
+
+ if (bss->dtim_count == 0)
+ bss->dtim_count = bss->dtim_period - 1;
+ else
+ bss->dtim_count--;
+
+ tim = pos = (u8 *) skb_put(skb, 6);
+ *pos++ = WLAN_EID_TIM;
+ *pos++ = 4;
+ *pos++ = bss->dtim_count;
+ *pos++ = bss->dtim_period;
+
+ if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf))
+ aid0 = 1;
+
+ if (have_bits) {
+ /* Find largest even number N1 so that bits numbered 1 through
+ * (N1 x 8) - 1 in the bitmap are 0 and number N2 so that bits
+ * (N2 + 1) x 8 through 2007 are 0. */
+ n1 = 0;
+ for (i = 0; i < IEEE80211_MAX_TIM_LEN; i++) {
+ if (bss->tim[i]) {
+ n1 = i & 0xfe;
+ break;
+ }
+ }
+ n2 = n1;
+ for (i = IEEE80211_MAX_TIM_LEN - 1; i >= n1; i--) {
+ if (bss->tim[i]) {
+ n2 = i;
+ break;
+ }
+ }
+
+ /* Bitmap control */
+ *pos++ = n1 | aid0;
+ /* Part Virt Bitmap */
+ memcpy(pos, bss->tim + n1, n2 - n1 + 1);
+
+ tim[1] = n2 - n1 + 4;
+ skb_put(skb, n2 - n1);
+ } else {
+ *pos++ = aid0; /* Bitmap control */
+ *pos++ = 0; /* Part Virt Bitmap */
+ }
+ read_unlock_bh(&local->sta_lock);
+}
+
+struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, int if_id,
+ struct ieee80211_tx_control *control)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct sk_buff *skb;
+ struct net_device *bdev;
+ struct ieee80211_sub_if_data *sdata = NULL;
+ struct ieee80211_if_ap *ap = NULL;
+ struct ieee80211_rate *rate;
+ struct rate_control_extra extra;
+ u8 *b_head, *b_tail;
+ int bh_len, bt_len;
+
+ bdev = dev_get_by_index(&init_net, if_id);
+ if (bdev) {
+ sdata = IEEE80211_DEV_TO_SUB_IF(bdev);
+ ap = &sdata->u.ap;
+ dev_put(bdev);
+ }
+
+ if (!ap || sdata->type != IEEE80211_IF_TYPE_AP ||
+ !ap->beacon_head) {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ if (net_ratelimit())
+ printk(KERN_DEBUG "no beacon data avail for idx=%d "
+ "(%s)\n", if_id, bdev ? bdev->name : "N/A");
+#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
+ return NULL;
+ }
+
+ /* Assume we are generating the normal beacon locally */
+ b_head = ap->beacon_head;
+ b_tail = ap->beacon_tail;
+ bh_len = ap->beacon_head_len;
+ bt_len = ap->beacon_tail_len;
+
+ skb = dev_alloc_skb(local->tx_headroom +
+ bh_len + bt_len + 256 /* maximum TIM len */);
+ if (!skb)
+ return NULL;
+
+ skb_reserve(skb, local->tx_headroom);
+ memcpy(skb_put(skb, bh_len), b_head, bh_len);
+
+ ieee80211_include_sequence(sdata, (struct ieee80211_hdr *)skb->data);
+
+ ieee80211_beacon_add_tim(local, ap, skb);
+
+ if (b_tail) {
+ memcpy(skb_put(skb, bt_len), b_tail, bt_len);
+ }
+
+ if (control) {
+ memset(&extra, 0, sizeof(extra));
+ extra.mode = local->oper_hw_mode;
+
+ rate = rate_control_get_rate(local, local->mdev, skb, &extra);
+ if (!rate) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: ieee80211_beacon_get: no rate "
+ "found\n", wiphy_name(local->hw.wiphy));
+ }
+ dev_kfree_skb(skb);
+ return NULL;
+ }
+
+ control->tx_rate =
+ ((sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE) &&
+ (rate->flags & IEEE80211_RATE_PREAMBLE2)) ?
+ rate->val2 : rate->val;
+ control->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
+ control->power_level = local->hw.conf.power_level;
+ control->flags |= IEEE80211_TXCTL_NO_ACK;
+ control->retry_limit = 1;
+ control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK;
+ }
+
+ ap->num_beacons++;
+ return skb;
+}
+EXPORT_SYMBOL(ieee80211_beacon_get);
+
+void ieee80211_rts_get(struct ieee80211_hw *hw, int if_id,
+ const void *frame, size_t frame_len,
+ const struct ieee80211_tx_control *frame_txctl,
+ struct ieee80211_rts *rts)
+{
+ const struct ieee80211_hdr *hdr = frame;
+ u16 fctl;
+
+ fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS;
+ rts->frame_control = cpu_to_le16(fctl);
+ rts->duration = ieee80211_rts_duration(hw, if_id, frame_len, frame_txctl);
+ memcpy(rts->ra, hdr->addr1, sizeof(rts->ra));
+ memcpy(rts->ta, hdr->addr2, sizeof(rts->ta));
+}
+EXPORT_SYMBOL(ieee80211_rts_get);
+
+void ieee80211_ctstoself_get(struct ieee80211_hw *hw, int if_id,
+ const void *frame, size_t frame_len,
+ const struct ieee80211_tx_control *frame_txctl,
+ struct ieee80211_cts *cts)
+{
+ const struct ieee80211_hdr *hdr = frame;
+ u16 fctl;
+
+ fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS;
+ cts->frame_control = cpu_to_le16(fctl);
+ cts->duration = ieee80211_ctstoself_duration(hw, if_id, frame_len, frame_txctl);
+ memcpy(cts->ra, hdr->addr1, sizeof(cts->ra));
+}
+EXPORT_SYMBOL(ieee80211_ctstoself_get);
+
+struct sk_buff *
+ieee80211_get_buffered_bc(struct ieee80211_hw *hw, int if_id,
+ struct ieee80211_tx_control *control)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct sk_buff *skb;
+ struct sta_info *sta;
+ ieee80211_tx_handler *handler;
+ struct ieee80211_txrx_data tx;
+ ieee80211_txrx_result res = TXRX_DROP;
+ struct net_device *bdev;
+ struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_if_ap *bss = NULL;
+
+ bdev = dev_get_by_index(&init_net, if_id);
+ if (bdev) {
+ sdata = IEEE80211_DEV_TO_SUB_IF(bdev);
+ bss = &sdata->u.ap;
+ dev_put(bdev);
+ }
+ if (!bss || sdata->type != IEEE80211_IF_TYPE_AP || !bss->beacon_head)
+ return NULL;
+
+ if (bss->dtim_count != 0)
+ return NULL; /* send buffered bc/mc only after DTIM beacon */
+ memset(control, 0, sizeof(*control));
+ while (1) {
+ skb = skb_dequeue(&bss->ps_bc_buf);
+ if (!skb)
+ return NULL;
+ local->total_ps_buffered--;
+
+ if (!skb_queue_empty(&bss->ps_bc_buf) && skb->len >= 2) {
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *) skb->data;
+ /* more buffered multicast/broadcast frames ==> set
+ * MoreData flag in IEEE 802.11 header to inform PS
+ * STAs */
+ hdr->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ }
+
+ if (!ieee80211_tx_prepare(&tx, skb, local->mdev, control))
+ break;
+ dev_kfree_skb_any(skb);
+ }
+ sta = tx.sta;
+ tx.flags |= IEEE80211_TXRXD_TXPS_BUFFERED;
+ tx.u.tx.mode = local->hw.conf.mode;
+
+ for (handler = local->tx_handlers; *handler != NULL; handler++) {
+ res = (*handler)(&tx);
+ if (res == TXRX_DROP || res == TXRX_QUEUED)
+ break;
+ }
+ dev_put(tx.dev);
+ skb = tx.skb; /* handlers are allowed to change skb */
+
+ if (res == TXRX_DROP) {
+ I802_DEBUG_INC(local->tx_handlers_drop);
+ dev_kfree_skb(skb);
+ skb = NULL;
+ } else if (res == TXRX_QUEUED) {
+ I802_DEBUG_INC(local->tx_handlers_queued);
+ skb = NULL;
+ }
+
+ if (sta)
+ sta_info_put(sta);
+
+ return skb;
+}
+EXPORT_SYMBOL(ieee80211_get_buffered_bc);
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
new file mode 100644
index 000000000000..5a0564e1dbd6
--- /dev/null
+++ b/net/mac80211/util.c
@@ -0,0 +1,486 @@
+/*
+ * 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>
+ *
+ * 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.
+ *
+ * utilities for mac80211
+ */
+
+#include <net/mac80211.h>
+#include <linux/netdevice.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+#include <linux/bitmap.h>
+#include <net/net_namespace.h>
+#include <net/cfg80211.h>
+
+#include "ieee80211_i.h"
+#include "ieee80211_rate.h"
+#include "wme.h"
+
+/* privid for wiphys to determine whether they belong to us or not */
+void *mac80211_wiphy_privid = &mac80211_wiphy_privid;
+
+/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
+/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
+const unsigned char rfc1042_header[] =
+ { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
+const unsigned char bridge_tunnel_header[] =
+ { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
+
+/* No encapsulation header if EtherType < 0x600 (=length) */
+static const unsigned char eapol_header[] =
+ { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x88, 0x8e };
+
+
+static int rate_list_match(const int *rate_list, int rate)
+{
+ int i;
+
+ if (!rate_list)
+ return 0;
+
+ for (i = 0; rate_list[i] >= 0; i++)
+ if (rate_list[i] == rate)
+ return 1;
+
+ return 0;
+}
+
+void ieee80211_prepare_rates(struct ieee80211_local *local,
+ struct ieee80211_hw_mode *mode)
+{
+ int i;
+
+ for (i = 0; i < mode->num_rates; i++) {
+ struct ieee80211_rate *rate = &mode->rates[i];
+
+ rate->flags &= ~(IEEE80211_RATE_SUPPORTED |
+ IEEE80211_RATE_BASIC);
+
+ if (local->supp_rates[mode->mode]) {
+ if (!rate_list_match(local->supp_rates[mode->mode],
+ rate->rate))
+ continue;
+ }
+
+ rate->flags |= IEEE80211_RATE_SUPPORTED;
+
+ /* Use configured basic rate set if it is available. If not,
+ * use defaults that are sane for most cases. */
+ if (local->basic_rates[mode->mode]) {
+ if (rate_list_match(local->basic_rates[mode->mode],
+ rate->rate))
+ rate->flags |= IEEE80211_RATE_BASIC;
+ } else switch (mode->mode) {
+ case MODE_IEEE80211A:
+ if (rate->rate == 60 || rate->rate == 120 ||
+ rate->rate == 240)
+ rate->flags |= IEEE80211_RATE_BASIC;
+ break;
+ case MODE_IEEE80211B:
+ if (rate->rate == 10 || rate->rate == 20)
+ rate->flags |= IEEE80211_RATE_BASIC;
+ break;
+ case MODE_IEEE80211G:
+ if (rate->rate == 10 || rate->rate == 20 ||
+ rate->rate == 55 || rate->rate == 110)
+ rate->flags |= IEEE80211_RATE_BASIC;
+ break;
+ case NUM_IEEE80211_MODES:
+ /* not useful */
+ break;
+ }
+
+ /* Set ERP and MANDATORY flags based on phymode */
+ switch (mode->mode) {
+ case MODE_IEEE80211A:
+ if (rate->rate == 60 || rate->rate == 120 ||
+ rate->rate == 240)
+ rate->flags |= IEEE80211_RATE_MANDATORY;
+ break;
+ case MODE_IEEE80211B:
+ if (rate->rate == 10)
+ rate->flags |= IEEE80211_RATE_MANDATORY;
+ break;
+ case MODE_IEEE80211G:
+ if (rate->rate == 10 || rate->rate == 20 ||
+ rate->rate == 55 || rate->rate == 110 ||
+ rate->rate == 60 || rate->rate == 120 ||
+ rate->rate == 240)
+ rate->flags |= IEEE80211_RATE_MANDATORY;
+ break;
+ case NUM_IEEE80211_MODES:
+ /* not useful */
+ break;
+ }
+ if (ieee80211_is_erp_rate(mode->mode, rate->rate))
+ rate->flags |= IEEE80211_RATE_ERP;
+ }
+}
+
+u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len)
+{
+ u16 fc;
+
+ if (len < 24)
+ return NULL;
+
+ fc = le16_to_cpu(hdr->frame_control);
+
+ switch (fc & IEEE80211_FCTL_FTYPE) {
+ case IEEE80211_FTYPE_DATA:
+ switch (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
+ case IEEE80211_FCTL_TODS:
+ return hdr->addr1;
+ case (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
+ return NULL;
+ case IEEE80211_FCTL_FROMDS:
+ return hdr->addr2;
+ case 0:
+ return hdr->addr3;
+ }
+ break;
+ case IEEE80211_FTYPE_MGMT:
+ return hdr->addr3;
+ case IEEE80211_FTYPE_CTL:
+ if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)
+ return hdr->addr1;
+ else
+ return NULL;
+ }
+
+ return NULL;
+}
+
+int ieee80211_get_hdrlen(u16 fc)
+{
+ int hdrlen = 24;
+
+ switch (fc & IEEE80211_FCTL_FTYPE) {
+ case IEEE80211_FTYPE_DATA:
+ if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS))
+ hdrlen = 30; /* Addr4 */
+ /*
+ * The QoS Control field is two bytes and its presence is
+ * indicated by the IEEE80211_STYPE_QOS_DATA bit. Add 2 to
+ * hdrlen if that bit is set.
+ * This works by masking out the bit and shifting it to
+ * bit position 1 so the result has the value 0 or 2.
+ */
+ hdrlen += (fc & IEEE80211_STYPE_QOS_DATA)
+ >> (ilog2(IEEE80211_STYPE_QOS_DATA)-1);
+ break;
+ case IEEE80211_FTYPE_CTL:
+ /*
+ * ACK and CTS are 10 bytes, all others 16. To see how
+ * to get this condition consider
+ * subtype mask: 0b0000000011110000 (0x00F0)
+ * ACK subtype: 0b0000000011010000 (0x00D0)
+ * CTS subtype: 0b0000000011000000 (0x00C0)
+ * bits that matter: ^^^ (0x00E0)
+ * value of those: 0b0000000011000000 (0x00C0)
+ */
+ if ((fc & 0xE0) == 0xC0)
+ hdrlen = 10;
+ else
+ hdrlen = 16;
+ break;
+ }
+
+ return hdrlen;
+}
+EXPORT_SYMBOL(ieee80211_get_hdrlen);
+
+int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
+{
+ const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) skb->data;
+ int hdrlen;
+
+ if (unlikely(skb->len < 10))
+ return 0;
+ hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control));
+ if (unlikely(hdrlen > skb->len))
+ return 0;
+ return hdrlen;
+}
+EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
+
+int ieee80211_is_eapol(const struct sk_buff *skb)
+{
+ const struct ieee80211_hdr *hdr;
+ u16 fc;
+ int hdrlen;
+
+ if (unlikely(skb->len < 10))
+ return 0;
+
+ hdr = (const struct ieee80211_hdr *) skb->data;
+ fc = le16_to_cpu(hdr->frame_control);
+
+ if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
+ return 0;
+
+ hdrlen = ieee80211_get_hdrlen(fc);
+
+ if (unlikely(skb->len >= hdrlen + sizeof(eapol_header) &&
+ memcmp(skb->data + hdrlen, eapol_header,
+ sizeof(eapol_header)) == 0))
+ return 1;
+
+ return 0;
+}
+
+void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
+
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+ if (tx->u.tx.extra_frag) {
+ struct ieee80211_hdr *fhdr;
+ int i;
+ for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
+ fhdr = (struct ieee80211_hdr *)
+ tx->u.tx.extra_frag[i]->data;
+ fhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+ }
+ }
+}
+
+int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
+ int rate, int erp, int short_preamble)
+{
+ int dur;
+
+ /* calculate duration (in microseconds, rounded up to next higher
+ * integer if it includes a fractional microsecond) to send frame of
+ * len bytes (does not include FCS) at the given rate. Duration will
+ * also include SIFS.
+ *
+ * rate is in 100 kbps, so divident is multiplied by 10 in the
+ * DIV_ROUND_UP() operations.
+ */
+
+ if (local->hw.conf.phymode == MODE_IEEE80211A || erp) {
+ /*
+ * OFDM:
+ *
+ * N_DBPS = DATARATE x 4
+ * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS)
+ * (16 = SIGNAL time, 6 = tail bits)
+ * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext
+ *
+ * T_SYM = 4 usec
+ * 802.11a - 17.5.2: aSIFSTime = 16 usec
+ * 802.11g - 19.8.4: aSIFSTime = 10 usec +
+ * signal ext = 6 usec
+ */
+ dur = 16; /* SIFS + signal ext */
+ dur += 16; /* 17.3.2.3: T_PREAMBLE = 16 usec */
+ dur += 4; /* 17.3.2.3: T_SIGNAL = 4 usec */
+ dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10,
+ 4 * rate); /* T_SYM x N_SYM */
+ } else {
+ /*
+ * 802.11b or 802.11g with 802.11b compatibility:
+ * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime +
+ * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0.
+ *
+ * 802.11 (DS): 15.3.3, 802.11b: 18.3.4
+ * aSIFSTime = 10 usec
+ * aPreambleLength = 144 usec or 72 usec with short preamble
+ * aPLCPHeaderLength = 48 usec or 24 usec with short preamble
+ */
+ dur = 10; /* aSIFSTime = 10 usec */
+ dur += short_preamble ? (72 + 24) : (144 + 48);
+
+ dur += DIV_ROUND_UP(8 * (len + 4) * 10, rate);
+ }
+
+ return dur;
+}
+
+/* Exported duration function for driver use */
+__le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, int if_id,
+ size_t frame_len, int rate)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct net_device *bdev = dev_get_by_index(&init_net, if_id);
+ struct ieee80211_sub_if_data *sdata;
+ u16 dur;
+ int erp;
+
+ if (unlikely(!bdev))
+ return 0;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(bdev);
+ erp = ieee80211_is_erp_rate(hw->conf.phymode, rate);
+ dur = ieee80211_frame_duration(local, frame_len, rate,
+ erp, sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE);
+
+ dev_put(bdev);
+ return cpu_to_le16(dur);
+}
+EXPORT_SYMBOL(ieee80211_generic_frame_duration);
+
+__le16 ieee80211_rts_duration(struct ieee80211_hw *hw, int if_id,
+ size_t frame_len,
+ const struct ieee80211_tx_control *frame_txctl)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_rate *rate;
+ struct net_device *bdev = dev_get_by_index(&init_net, if_id);
+ struct ieee80211_sub_if_data *sdata;
+ int short_preamble;
+ int erp;
+ u16 dur;
+
+ if (unlikely(!bdev))
+ return 0;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(bdev);
+ short_preamble = sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE;
+
+ rate = frame_txctl->rts_rate;
+ erp = !!(rate->flags & IEEE80211_RATE_ERP);
+
+ /* CTS duration */
+ dur = ieee80211_frame_duration(local, 10, rate->rate,
+ erp, short_preamble);
+ /* Data frame duration */
+ dur += ieee80211_frame_duration(local, frame_len, rate->rate,
+ erp, short_preamble);
+ /* ACK duration */
+ dur += ieee80211_frame_duration(local, 10, rate->rate,
+ erp, short_preamble);
+
+ dev_put(bdev);
+ return cpu_to_le16(dur);
+}
+EXPORT_SYMBOL(ieee80211_rts_duration);
+
+__le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, int if_id,
+ size_t frame_len,
+ const struct ieee80211_tx_control *frame_txctl)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_rate *rate;
+ struct net_device *bdev = dev_get_by_index(&init_net, if_id);
+ struct ieee80211_sub_if_data *sdata;
+ int short_preamble;
+ int erp;
+ u16 dur;
+
+ if (unlikely(!bdev))
+ return 0;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(bdev);
+ short_preamble = sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE;
+
+ rate = frame_txctl->rts_rate;
+ erp = !!(rate->flags & IEEE80211_RATE_ERP);
+
+ /* Data frame duration */
+ dur = ieee80211_frame_duration(local, frame_len, rate->rate,
+ erp, short_preamble);
+ if (!(frame_txctl->flags & IEEE80211_TXCTL_NO_ACK)) {
+ /* ACK duration */
+ dur += ieee80211_frame_duration(local, 10, rate->rate,
+ erp, short_preamble);
+ }
+
+ dev_put(bdev);
+ return cpu_to_le16(dur);
+}
+EXPORT_SYMBOL(ieee80211_ctstoself_duration);
+
+struct ieee80211_rate *
+ieee80211_get_rate(struct ieee80211_local *local, int phymode, int hw_rate)
+{
+ struct ieee80211_hw_mode *mode;
+ int r;
+
+ list_for_each_entry(mode, &local->modes_list, list) {
+ if (mode->mode != phymode)
+ continue;
+ for (r = 0; r < mode->num_rates; r++) {
+ struct ieee80211_rate *rate = &mode->rates[r];
+ if (rate->val == hw_rate ||
+ (rate->flags & IEEE80211_RATE_PREAMBLE2 &&
+ rate->val2 == hw_rate))
+ return rate;
+ }
+ }
+
+ return NULL;
+}
+
+void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+
+ if (test_and_clear_bit(IEEE80211_LINK_STATE_XOFF,
+ &local->state[queue])) {
+ if (test_bit(IEEE80211_LINK_STATE_PENDING,
+ &local->state[queue]))
+ tasklet_schedule(&local->tx_pending_tasklet);
+ else
+ if (!ieee80211_qdisc_installed(local->mdev)) {
+ if (queue == 0)
+ netif_wake_queue(local->mdev);
+ } else
+ __netif_schedule(local->mdev);
+ }
+}
+EXPORT_SYMBOL(ieee80211_wake_queue);
+
+void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+
+ if (!ieee80211_qdisc_installed(local->mdev) && queue == 0)
+ netif_stop_queue(local->mdev);
+ set_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
+}
+EXPORT_SYMBOL(ieee80211_stop_queue);
+
+void ieee80211_start_queues(struct ieee80211_hw *hw)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ int i;
+
+ for (i = 0; i < local->hw.queues; i++)
+ clear_bit(IEEE80211_LINK_STATE_XOFF, &local->state[i]);
+ if (!ieee80211_qdisc_installed(local->mdev))
+ netif_start_queue(local->mdev);
+}
+EXPORT_SYMBOL(ieee80211_start_queues);
+
+void ieee80211_stop_queues(struct ieee80211_hw *hw)
+{
+ int i;
+
+ for (i = 0; i < hw->queues; i++)
+ ieee80211_stop_queue(hw, i);
+}
+EXPORT_SYMBOL(ieee80211_stop_queues);
+
+void ieee80211_wake_queues(struct ieee80211_hw *hw)
+{
+ int i;
+
+ for (i = 0; i < hw->queues; i++)
+ ieee80211_wake_queue(hw, i);
+}
+EXPORT_SYMBOL(ieee80211_wake_queues);
diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c
index 1ad3d75281cc..6675261e958f 100644
--- a/net/mac80211/wep.c
+++ b/net/mac80211/wep.c
@@ -63,11 +63,11 @@ static inline int ieee80211_wep_weak_iv(u32 iv, int keylen)
}
-void ieee80211_wep_get_iv(struct ieee80211_local *local,
- struct ieee80211_key *key, u8 *iv)
+static void ieee80211_wep_get_iv(struct ieee80211_local *local,
+ struct ieee80211_key *key, u8 *iv)
{
local->wep_iv++;
- if (ieee80211_wep_weak_iv(local->wep_iv, key->keylen))
+ if (ieee80211_wep_weak_iv(local->wep_iv, key->conf.keylen))
local->wep_iv += 0x0100;
if (!iv)
@@ -76,13 +76,13 @@ void ieee80211_wep_get_iv(struct ieee80211_local *local,
*iv++ = (local->wep_iv >> 16) & 0xff;
*iv++ = (local->wep_iv >> 8) & 0xff;
*iv++ = local->wep_iv & 0xff;
- *iv++ = key->keyidx << 6;
+ *iv++ = key->conf.keyidx << 6;
}
-u8 * ieee80211_wep_add_iv(struct ieee80211_local *local,
- struct sk_buff *skb,
- struct ieee80211_key *key)
+static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local,
+ struct sk_buff *skb,
+ struct ieee80211_key *key)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
u16 fc;
@@ -109,9 +109,9 @@ u8 * ieee80211_wep_add_iv(struct ieee80211_local *local,
}
-void ieee80211_wep_remove_iv(struct ieee80211_local *local,
- struct sk_buff *skb,
- struct ieee80211_key *key)
+static void ieee80211_wep_remove_iv(struct ieee80211_local *local,
+ struct sk_buff *skb,
+ struct ieee80211_key *key)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
u16 fc;
@@ -159,10 +159,10 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb,
u8 *rc4key, *iv;
size_t len;
- if (!key || key->alg != ALG_WEP)
+ if (!key || key->conf.alg != ALG_WEP)
return -1;
- klen = 3 + key->keylen;
+ klen = 3 + key->conf.keylen;
rc4key = kmalloc(klen, GFP_ATOMIC);
if (!rc4key)
return -1;
@@ -179,7 +179,7 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb,
memcpy(rc4key, iv, 3);
/* Copy rest of the WEP key (the secret part) */
- memcpy(rc4key + 3, key->key, key->keylen);
+ memcpy(rc4key + 3, key->conf.key, key->conf.keylen);
/* Add room for ICV */
skb_put(skb, WEP_ICV_LEN);
@@ -251,10 +251,10 @@ int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb,
keyidx = skb->data[hdrlen + 3] >> 6;
- if (!key || keyidx != key->keyidx || key->alg != ALG_WEP)
+ if (!key || keyidx != key->conf.keyidx || key->conf.alg != ALG_WEP)
return -1;
- klen = 3 + key->keylen;
+ klen = 3 + key->conf.keylen;
rc4key = kmalloc(klen, GFP_ATOMIC);
if (!rc4key)
@@ -264,7 +264,7 @@ int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb,
memcpy(rc4key, skb->data + hdrlen, 3);
/* Copy rest of the WEP key (the secret part) */
- memcpy(rc4key + 3, key->key, key->keylen);
+ memcpy(rc4key + 3, key->conf.key, key->conf.keylen);
if (ieee80211_wep_decrypt_data(local->wep_rx_tfm, rc4key, klen,
skb->data + hdrlen + WEP_IV_LEN,
@@ -286,43 +286,99 @@ int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb,
}
-int ieee80211_wep_get_keyidx(struct sk_buff *skb)
+u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
u16 fc;
int hdrlen;
+ u8 *ivpos;
+ u32 iv;
fc = le16_to_cpu(hdr->frame_control);
if (!(fc & IEEE80211_FCTL_PROTECTED))
- return -1;
+ return NULL;
hdrlen = ieee80211_get_hdrlen(fc);
+ ivpos = skb->data + hdrlen;
+ iv = (ivpos[0] << 16) | (ivpos[1] << 8) | ivpos[2];
- if (skb->len < 8 + hdrlen)
- return -1;
+ if (ieee80211_wep_weak_iv(iv, key->conf.keylen))
+ return ivpos;
- return skb->data[hdrlen + 3] >> 6;
+ return NULL;
}
+ieee80211_txrx_result
+ieee80211_crypto_wep_decrypt(struct ieee80211_txrx_data *rx)
+{
+ if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
+ ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
+ (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH))
+ return TXRX_CONTINUE;
+
+ if (!(rx->u.rx.status->flag & RX_FLAG_DECRYPTED)) {
+ if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key)) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "%s: RX WEP frame, decrypt "
+ "failed\n", rx->dev->name);
+ return TXRX_DROP;
+ }
+ } else if (!(rx->u.rx.status->flag & RX_FLAG_IV_STRIPPED)) {
+ ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
+ /* remove ICV */
+ skb_trim(rx->skb, rx->skb->len - 4);
+ }
-u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key)
+ return TXRX_CONTINUE;
+}
+
+static int wep_encrypt_skb(struct ieee80211_txrx_data *tx, struct sk_buff *skb)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ if (!(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) {
+ if (ieee80211_wep_encrypt(tx->local, skb, tx->key))
+ return -1;
+ } else {
+ tx->u.tx.control->key_idx = tx->key->conf.hw_key_idx;
+ if (tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) {
+ if (!ieee80211_wep_add_iv(tx->local, skb, tx->key))
+ return -1;
+ }
+ }
+ return 0;
+}
+
+ieee80211_txrx_result
+ieee80211_crypto_wep_encrypt(struct ieee80211_txrx_data *tx)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
u16 fc;
- int hdrlen;
- u8 *ivpos;
- u32 iv;
fc = le16_to_cpu(hdr->frame_control);
- if (!(fc & IEEE80211_FCTL_PROTECTED))
- return NULL;
- hdrlen = ieee80211_get_hdrlen(fc);
- ivpos = skb->data + hdrlen;
- iv = (ivpos[0] << 16) | (ivpos[1] << 8) | ivpos[2];
+ if (((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
+ ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
+ (fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH)))
+ return TXRX_CONTINUE;
- if (ieee80211_wep_weak_iv(iv, key->keylen))
- return ivpos;
+ tx->u.tx.control->iv_len = WEP_IV_LEN;
+ tx->u.tx.control->icv_len = WEP_ICV_LEN;
+ ieee80211_tx_set_iswep(tx);
- return NULL;
+ if (wep_encrypt_skb(tx, tx->skb) < 0) {
+ I802_DEBUG_INC(tx->local->tx_handlers_drop_wep);
+ return TXRX_DROP;
+ }
+
+ if (tx->u.tx.extra_frag) {
+ int i;
+ for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
+ if (wep_encrypt_skb(tx, tx->u.tx.extra_frag[i]) < 0) {
+ I802_DEBUG_INC(tx->local->
+ tx_handlers_drop_wep);
+ return TXRX_DROP;
+ }
+ }
+ }
+
+ return TXRX_CONTINUE;
}
diff --git a/net/mac80211/wep.h b/net/mac80211/wep.h
index bfe29e8e10aa..785fbb4e0dd7 100644
--- a/net/mac80211/wep.h
+++ b/net/mac80211/wep.h
@@ -18,14 +18,6 @@
int ieee80211_wep_init(struct ieee80211_local *local);
void ieee80211_wep_free(struct ieee80211_local *local);
-void ieee80211_wep_get_iv(struct ieee80211_local *local,
- struct ieee80211_key *key, u8 *iv);
-u8 * ieee80211_wep_add_iv(struct ieee80211_local *local,
- struct sk_buff *skb,
- struct ieee80211_key *key);
-void ieee80211_wep_remove_iv(struct ieee80211_local *local,
- struct sk_buff *skb,
- struct ieee80211_key *key);
void ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key,
size_t klen, u8 *data, size_t data_len);
int ieee80211_wep_decrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key,
@@ -34,7 +26,11 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb,
struct ieee80211_key *key);
int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb,
struct ieee80211_key *key);
-int ieee80211_wep_get_keyidx(struct sk_buff *skb);
u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key);
+ieee80211_txrx_result
+ieee80211_crypto_wep_decrypt(struct ieee80211_txrx_data *rx);
+ieee80211_txrx_result
+ieee80211_crypto_wep_encrypt(struct ieee80211_txrx_data *tx);
+
#endif /* WEP_H */
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 7ab82b376e1b..5b8a157975a3 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -18,70 +18,6 @@
#include "ieee80211_i.h"
#include "wme.h"
-static inline int WLAN_FC_IS_QOS_DATA(u16 fc)
-{
- return (fc & 0x8C) == 0x88;
-}
-
-
-ieee80211_txrx_result
-ieee80211_rx_h_parse_qos(struct ieee80211_txrx_data *rx)
-{
- u8 *data = rx->skb->data;
- int tid;
-
- /* does the frame have a qos control field? */
- if (WLAN_FC_IS_QOS_DATA(rx->fc)) {
- u8 *qc = data + ieee80211_get_hdrlen(rx->fc) - QOS_CONTROL_LEN;
- /* frame has qos control */
- tid = qc[0] & QOS_CONTROL_TID_MASK;
- } else {
- if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) {
- /* Separate TID for management frames */
- tid = NUM_RX_DATA_QUEUES - 1;
- } else {
- /* no qos control present */
- tid = 0; /* 802.1d - Best Effort */
- }
- }
-#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
- I802_DEBUG_INC(rx->local->wme_rx_queue[tid]);
- if (rx->sta) {
- I802_DEBUG_INC(rx->sta->wme_rx_queue[tid]);
- }
-#endif /* CONFIG_MAC80211_DEBUG_COUNTERS */
-
- rx->u.rx.queue = tid;
- /* Set skb->priority to 1d tag if highest order bit of TID is not set.
- * For now, set skb->priority to 0 for other cases. */
- rx->skb->priority = (tid > 7) ? 0 : tid;
-
- return TXRX_CONTINUE;
-}
-
-
-ieee80211_txrx_result
-ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx)
-{
- u16 fc = rx->fc;
- u8 *data = rx->skb->data;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) data;
-
- if (!WLAN_FC_IS_QOS_DATA(fc))
- return TXRX_CONTINUE;
-
- /* remove the qos control field, update frame type and meta-data */
- memmove(data + 2, data, ieee80211_get_hdrlen(fc) - 2);
- hdr = (struct ieee80211_hdr *) skb_pull(rx->skb, 2);
- /* change frame type to non QOS */
- rx->fc = fc &= ~IEEE80211_STYPE_QOS_DATA;
- hdr->frame_control = cpu_to_le16(fc);
-
- return TXRX_CONTINUE;
-}
-
-
-#ifdef CONFIG_NET_SCHED
/* maximum number of hardware queues we support. */
#define TC_80211_MAX_QUEUES 8
@@ -158,8 +94,6 @@ static inline int wme_downgrade_ac(struct sk_buff *skb)
static inline int classify80211(struct sk_buff *skb, struct Qdisc *qd)
{
struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr);
- struct ieee80211_tx_packet_data *pkt_data =
- (struct ieee80211_tx_packet_data *) skb->cb;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
unsigned short fc = le16_to_cpu(hdr->frame_control);
int qos;
@@ -172,12 +106,8 @@ static inline int classify80211(struct sk_buff *skb, struct Qdisc *qd)
return IEEE80211_TX_QUEUE_DATA0;
}
- if (unlikely(pkt_data->mgmt_iface)) {
- /* Data frames from hostapd (mainly, EAPOL) use AC_VO
- * and they will include QoS control fields if
- * the target STA is using WME. */
- skb->priority = 7;
- return ieee802_1d_to_ac[skb->priority];
+ if (0 /* injected */) {
+ /* use AC from radiotap */
}
/* is this a QoS frame? */
@@ -189,14 +119,13 @@ static inline int classify80211(struct sk_buff *skb, struct Qdisc *qd)
}
/* use the data classifier to determine what 802.1d tag the
- * data frame has */
+ * data frame has */
skb->priority = classify_1d(skb, qd);
- /* incase we are a client verify acm is not set for this ac */
+ /* in case we are a client verify acm is not set for this ac */
while (unlikely(local->wmm_acm & BIT(skb->priority))) {
if (wme_downgrade_ac(skb)) {
- /* No AC with lower priority has acm=0,
- * drop packet. */
+ /* No AC with lower priority has acm=0, drop packet. */
return -1;
}
}
@@ -217,7 +146,7 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd)
struct Qdisc *qdisc;
int err, queue;
- if (pkt_data->requeue) {
+ if (pkt_data->flags & IEEE80211_TXPD_REQUEUE) {
skb_queue_tail(&q->requeued[pkt_data->queue], skb);
qd->q.qlen++;
return 0;
@@ -675,4 +604,3 @@ void ieee80211_wme_unregister(void)
{
unregister_qdisc(&wme_qdisc_ops);
}
-#endif /* CONFIG_NET_SCHED */
diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h
index f0bff10f0e08..76c713a6450c 100644
--- a/net/mac80211/wme.h
+++ b/net/mac80211/wme.h
@@ -24,11 +24,10 @@
#define QOS_CONTROL_TAG1D_MASK 0x07
-ieee80211_txrx_result
-ieee80211_rx_h_parse_qos(struct ieee80211_txrx_data *rx);
-
-ieee80211_txrx_result
-ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx);
+static inline int WLAN_FC_IS_QOS_DATA(u16 fc)
+{
+ return (fc & 0x8C) == 0x88;
+}
#ifdef CONFIG_NET_SCHED
void ieee80211_install_qdisc(struct net_device *dev);
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 783af32c6911..6695efba57ec 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -11,10 +11,8 @@
#include <linux/slab.h>
#include <linux/skbuff.h>
#include <linux/compiler.h>
-#include <net/iw_handler.h>
-
#include <net/mac80211.h>
-#include "ieee80211_common.h"
+
#include "ieee80211_i.h"
#include "michael.h"
#include "tkip.h"
@@ -84,16 +82,16 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_txrx_data *tx)
fc = tx->fc;
- if (!tx->key || tx->key->alg != ALG_TKIP || skb->len < 24 ||
+ if (!tx->key || tx->key->conf.alg != ALG_TKIP || skb->len < 24 ||
!WLAN_FC_DATA_PRESENT(fc))
return TXRX_CONTINUE;
if (ieee80211_get_hdr_info(skb, &sa, &da, &qos_tid, &data, &data_len))
return TXRX_DROP;
- if (!tx->key->force_sw_encrypt &&
- !tx->fragmented &&
- !(tx->local->hw.flags & IEEE80211_HW_TKIP_INCLUDE_MMIC) &&
+ if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
+ !(tx->flags & IEEE80211_TXRXD_FRAGMENTED) &&
+ !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) &&
!wpa_test) {
/* hwaccel - with no need for preallocated room for Michael MIC
*/
@@ -116,8 +114,8 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_txrx_data *tx)
#else
authenticator = 1;
#endif
- key = &tx->key->key[authenticator ? ALG_TKIP_TEMP_AUTH_TX_MIC_KEY :
- ALG_TKIP_TEMP_AUTH_RX_MIC_KEY];
+ key = &tx->key->conf.key[authenticator ? ALG_TKIP_TEMP_AUTH_TX_MIC_KEY :
+ ALG_TKIP_TEMP_AUTH_RX_MIC_KEY];
mic = skb_put(skb, MICHAEL_MIC_LEN);
michael_mic(key, da, sa, qos_tid & 0x0f, data, data_len, mic);
@@ -134,31 +132,20 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_txrx_data *rx)
u8 mic[MICHAEL_MIC_LEN];
struct sk_buff *skb = rx->skb;
int authenticator = 1, wpa_test = 0;
+ DECLARE_MAC_BUF(mac);
fc = rx->fc;
- /* If device handles decryption totally, skip this check */
- if ((rx->local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP) ||
- (rx->local->hw.flags & IEEE80211_HW_DEVICE_STRIPS_MIC))
+ /*
+ * No way to verify the MIC if the hardware stripped it
+ */
+ if (rx->u.rx.status->flag & RX_FLAG_MMIC_STRIPPED)
return TXRX_CONTINUE;
- if (!rx->key || rx->key->alg != ALG_TKIP ||
+ if (!rx->key || rx->key->conf.alg != ALG_TKIP ||
!(rx->fc & IEEE80211_FCTL_PROTECTED) || !WLAN_FC_DATA_PRESENT(fc))
return TXRX_CONTINUE;
- if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) &&
- !rx->key->force_sw_encrypt) {
- if (rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) {
- if (skb->len < MICHAEL_MIC_LEN)
- return TXRX_DROP;
- }
- /* Need to verify Michael MIC sometimes in software even when
- * hwaccel is used. Atheros ar5212: fragmented frames and QoS
- * frames. */
- if (!rx->fragmented && !wpa_test)
- goto remove_mic;
- }
-
if (ieee80211_get_hdr_info(skb, &sa, &da, &qos_tid, &data, &data_len)
|| data_len < MICHAEL_MIC_LEN)
return TXRX_DROP;
@@ -170,49 +157,28 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_txrx_data *rx)
#else
authenticator = 1;
#endif
- key = &rx->key->key[authenticator ? ALG_TKIP_TEMP_AUTH_RX_MIC_KEY :
- ALG_TKIP_TEMP_AUTH_TX_MIC_KEY];
+ key = &rx->key->conf.key[authenticator ? ALG_TKIP_TEMP_AUTH_RX_MIC_KEY :
+ ALG_TKIP_TEMP_AUTH_TX_MIC_KEY];
michael_mic(key, da, sa, qos_tid & 0x0f, data, data_len, mic);
if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0 || wpa_test) {
- if (!rx->u.rx.ra_match)
+ if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
return TXRX_DROP;
printk(KERN_DEBUG "%s: invalid Michael MIC in data frame from "
- MAC_FMT "\n", rx->dev->name, MAC_ARG(sa));
-
- do {
- struct ieee80211_hdr *hdr;
- union iwreq_data wrqu;
- char *buf = kmalloc(128, GFP_ATOMIC);
- if (!buf)
- break;
-
- /* TODO: needed parameters: count, key type, TSC */
- hdr = (struct ieee80211_hdr *) skb->data;
- sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
- "keyid=%d %scast addr=" MAC_FMT ")",
- rx->key->keyidx,
- hdr->addr1[0] & 0x01 ? "broad" : "uni",
- MAC_ARG(hdr->addr2));
- memset(&wrqu, 0, sizeof(wrqu));
- wrqu.data.length = strlen(buf);
- wireless_send_event(rx->dev, IWEVCUSTOM, &wrqu, buf);
- kfree(buf);
- } while (0);
-
- if (!rx->local->apdev)
- return TXRX_DROP;
-
- ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
- ieee80211_msg_michael_mic_failure);
+ "%s\n", rx->dev->name, print_mac(mac, sa));
- return TXRX_QUEUED;
+ mac80211_ev_michael_mic_failure(rx->dev, rx->key->conf.keyidx,
+ (void *) skb->data);
+ return TXRX_DROP;
}
- remove_mic:
/* remove Michael MIC from payload */
skb_trim(skb, skb->len - MICHAEL_MIC_LEN);
+ /* update IV in key information to be able to detect replays */
+ rx->key->u.tkip.iv32_rx[rx->u.rx.queue] = rx->u.rx.tkip_iv32;
+ rx->key->u.tkip.iv16_rx[rx->u.rx.queue] = rx->u.rx.tkip_iv16;
+
return TXRX_CONTINUE;
}
@@ -230,7 +196,11 @@ static int tkip_encrypt_skb(struct ieee80211_txrx_data *tx,
hdrlen = ieee80211_get_hdrlen(fc);
len = skb->len - hdrlen;
- tailneed = !tx->key->force_sw_encrypt ? 0 : TKIP_ICV_LEN;
+ if (tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)
+ tailneed = 0;
+ else
+ tailneed = TKIP_ICV_LEN;
+
if ((skb_headroom(skb) < TKIP_IV_LEN ||
skb_tailroom(skb) < tailneed)) {
I802_DEBUG_INC(tx->local->tx_expand_skb_head);
@@ -248,8 +218,7 @@ static int tkip_encrypt_skb(struct ieee80211_txrx_data *tx,
if (key->u.tkip.iv16 == 0)
key->u.tkip.iv32++;
- if (!tx->key->force_sw_encrypt) {
- u32 flags = tx->local->hw.flags;
+ if (tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
hdr = (struct ieee80211_hdr *)skb->data;
/* hwaccel - with preallocated room for IV */
@@ -259,23 +228,7 @@ static int tkip_encrypt_skb(struct ieee80211_txrx_data *tx,
0x7f),
(u8) key->u.tkip.iv16);
- if (flags & IEEE80211_HW_TKIP_REQ_PHASE2_KEY)
- ieee80211_tkip_gen_rc4key(key, hdr->addr2,
- tx->u.tx.control->tkip_key);
- else if (flags & IEEE80211_HW_TKIP_REQ_PHASE1_KEY) {
- if (key->u.tkip.iv16 == 0 ||
- !key->u.tkip.tx_initialized) {
- ieee80211_tkip_gen_phase1key(key, hdr->addr2,
- (u16 *)tx->u.tx.control->tkip_key);
- key->u.tkip.tx_initialized = 1;
- tx->u.tx.control->flags |=
- IEEE80211_TXCTL_TKIP_NEW_PHASE1_KEY;
- } else
- tx->u.tx.control->flags &=
- ~IEEE80211_TXCTL_TKIP_NEW_PHASE1_KEY;
- }
-
- tx->u.tx.control->key_idx = tx->key->hw_key_idx;
+ tx->u.tx.control->key_idx = tx->key->conf.hw_key_idx;
return 0;
}
@@ -290,28 +243,27 @@ static int tkip_encrypt_skb(struct ieee80211_txrx_data *tx,
ieee80211_txrx_result
-ieee80211_tx_h_tkip_encrypt(struct ieee80211_txrx_data *tx)
+ieee80211_crypto_tkip_encrypt(struct ieee80211_txrx_data *tx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
u16 fc;
- struct ieee80211_key *key = tx->key;
struct sk_buff *skb = tx->skb;
int wpa_test = 0, test = 0;
fc = le16_to_cpu(hdr->frame_control);
- if (!key || key->alg != ALG_TKIP || !WLAN_FC_DATA_PRESENT(fc))
+ if (!WLAN_FC_DATA_PRESENT(fc))
return TXRX_CONTINUE;
tx->u.tx.control->icv_len = TKIP_ICV_LEN;
tx->u.tx.control->iv_len = TKIP_IV_LEN;
ieee80211_tx_set_iswep(tx);
- if (!tx->key->force_sw_encrypt &&
- !(tx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) &&
+ if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
+ !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
!wpa_test) {
/* hwaccel - with no need for preallocated room for IV/ICV */
- tx->u.tx.control->key_idx = tx->key->hw_key_idx;
+ tx->u.tx.control->key_idx = tx->key->conf.hw_key_idx;
return TXRX_CONTINUE;
}
@@ -332,30 +284,31 @@ ieee80211_tx_h_tkip_encrypt(struct ieee80211_txrx_data *tx)
ieee80211_txrx_result
-ieee80211_rx_h_tkip_decrypt(struct ieee80211_txrx_data *rx)
+ieee80211_crypto_tkip_decrypt(struct ieee80211_txrx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
u16 fc;
int hdrlen, res, hwaccel = 0, wpa_test = 0;
struct ieee80211_key *key = rx->key;
struct sk_buff *skb = rx->skb;
+ DECLARE_MAC_BUF(mac);
fc = le16_to_cpu(hdr->frame_control);
hdrlen = ieee80211_get_hdrlen(fc);
- if (!rx->key || rx->key->alg != ALG_TKIP ||
- !(rx->fc & IEEE80211_FCTL_PROTECTED) ||
- (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)
+ if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)
return TXRX_CONTINUE;
if (!rx->sta || skb->len - hdrlen < 12)
return TXRX_DROP;
- if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) &&
- !rx->key->force_sw_encrypt) {
- if (!(rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV)) {
- /* Hardware takes care of all processing, including
- * replay protection, so no need to continue here. */
+ if (rx->u.rx.status->flag & RX_FLAG_DECRYPTED) {
+ if (rx->u.rx.status->flag & RX_FLAG_IV_STRIPPED) {
+ /*
+ * Hardware took care of all processing, including
+ * replay protection, and stripped the ICV/IV so
+ * we cannot do any checks here.
+ */
return TXRX_CONTINUE;
}
@@ -366,11 +319,13 @@ ieee80211_rx_h_tkip_decrypt(struct ieee80211_txrx_data *rx)
res = ieee80211_tkip_decrypt_data(rx->local->wep_rx_tfm,
key, skb->data + hdrlen,
skb->len - hdrlen, rx->sta->addr,
- hwaccel, rx->u.rx.queue);
+ hwaccel, rx->u.rx.queue,
+ &rx->u.rx.tkip_iv32,
+ &rx->u.rx.tkip_iv16);
if (res != TKIP_DECRYPT_OK || wpa_test) {
printk(KERN_DEBUG "%s: TKIP decrypt failed for RX frame from "
- MAC_FMT " (res=%d)\n",
- rx->dev->name, MAC_ARG(rx->sta->addr), res);
+ "%s (res=%d)\n",
+ rx->dev->name, print_mac(mac, rx->sta->addr), res);
return TXRX_DROP;
}
@@ -496,7 +451,10 @@ static int ccmp_encrypt_skb(struct ieee80211_txrx_data *tx,
hdrlen = ieee80211_get_hdrlen(fc);
len = skb->len - hdrlen;
- tailneed = !key->force_sw_encrypt ? 0 : CCMP_MIC_LEN;
+ if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)
+ tailneed = 0;
+ else
+ tailneed = CCMP_MIC_LEN;
if ((skb_headroom(skb) < CCMP_HDR_LEN ||
skb_tailroom(skb) < tailneed)) {
@@ -520,11 +478,11 @@ static int ccmp_encrypt_skb(struct ieee80211_txrx_data *tx,
break;
}
- ccmp_pn2hdr(pos, pn, key->keyidx);
+ ccmp_pn2hdr(pos, pn, key->conf.keyidx);
- if (!key->force_sw_encrypt) {
+ if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
/* hwaccel - with preallocated room for CCMP header */
- tx->u.tx.control->key_idx = key->hw_key_idx;
+ tx->u.tx.control->key_idx = key->conf.hw_key_idx;
return 0;
}
@@ -538,28 +496,27 @@ static int ccmp_encrypt_skb(struct ieee80211_txrx_data *tx,
ieee80211_txrx_result
-ieee80211_tx_h_ccmp_encrypt(struct ieee80211_txrx_data *tx)
+ieee80211_crypto_ccmp_encrypt(struct ieee80211_txrx_data *tx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
- struct ieee80211_key *key = tx->key;
u16 fc;
struct sk_buff *skb = tx->skb;
int test = 0;
fc = le16_to_cpu(hdr->frame_control);
- if (!key || key->alg != ALG_CCMP || !WLAN_FC_DATA_PRESENT(fc))
+ if (!WLAN_FC_DATA_PRESENT(fc))
return TXRX_CONTINUE;
tx->u.tx.control->icv_len = CCMP_MIC_LEN;
tx->u.tx.control->iv_len = CCMP_HDR_LEN;
ieee80211_tx_set_iswep(tx);
- if (!tx->key->force_sw_encrypt &&
- !(tx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV)) {
+ if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
+ !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
/* hwaccel - with no need for preallocated room for CCMP "
* header or MIC fields */
- tx->u.tx.control->key_idx = tx->key->hw_key_idx;
+ tx->u.tx.control->key_idx = tx->key->conf.hw_key_idx;
return TXRX_CONTINUE;
}
@@ -568,7 +525,6 @@ ieee80211_tx_h_ccmp_encrypt(struct ieee80211_txrx_data *tx)
if (tx->u.tx.extra_frag) {
int i;
-
for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
if (ccmp_encrypt_skb(tx, tx->u.tx.extra_frag[i], test)
< 0)
@@ -581,7 +537,7 @@ ieee80211_tx_h_ccmp_encrypt(struct ieee80211_txrx_data *tx)
ieee80211_txrx_result
-ieee80211_rx_h_ccmp_decrypt(struct ieee80211_txrx_data *rx)
+ieee80211_crypto_ccmp_decrypt(struct ieee80211_txrx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
u16 fc;
@@ -590,13 +546,12 @@ ieee80211_rx_h_ccmp_decrypt(struct ieee80211_txrx_data *rx)
struct sk_buff *skb = rx->skb;
u8 pn[CCMP_PN_LEN];
int data_len;
+ DECLARE_MAC_BUF(mac);
fc = le16_to_cpu(hdr->frame_control);
hdrlen = ieee80211_get_hdrlen(fc);
- if (!key || key->alg != ALG_CCMP ||
- !(rx->fc & IEEE80211_FCTL_PROTECTED) ||
- (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)
+ if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)
return TXRX_CONTINUE;
data_len = skb->len - hdrlen - CCMP_HDR_LEN - CCMP_MIC_LEN;
@@ -604,8 +559,7 @@ ieee80211_rx_h_ccmp_decrypt(struct ieee80211_txrx_data *rx)
return TXRX_DROP;
if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) &&
- !key->force_sw_encrypt &&
- !(rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV))
+ (rx->u.rx.status->flag & RX_FLAG_IV_STRIPPED))
return TXRX_CONTINUE;
(void) ccmp_hdr2pn(pn, skb->data + hdrlen);
@@ -613,10 +567,11 @@ ieee80211_rx_h_ccmp_decrypt(struct ieee80211_txrx_data *rx)
if (memcmp(pn, key->u.ccmp.rx_pn[rx->u.rx.queue], CCMP_PN_LEN) <= 0) {
#ifdef CONFIG_MAC80211_DEBUG
u8 *ppn = key->u.ccmp.rx_pn[rx->u.rx.queue];
+
printk(KERN_DEBUG "%s: CCMP replay detected for RX frame from "
- MAC_FMT " (RX PN %02x%02x%02x%02x%02x%02x <= prev. PN "
+ "%s (RX PN %02x%02x%02x%02x%02x%02x <= prev. PN "
"%02x%02x%02x%02x%02x%02x)\n", rx->dev->name,
- MAC_ARG(rx->sta->addr),
+ print_mac(mac, rx->sta->addr),
pn[0], pn[1], pn[2], pn[3], pn[4], pn[5],
ppn[0], ppn[1], ppn[2], ppn[3], ppn[4], ppn[5]);
#endif /* CONFIG_MAC80211_DEBUG */
@@ -624,10 +579,8 @@ ieee80211_rx_h_ccmp_decrypt(struct ieee80211_txrx_data *rx)
return TXRX_DROP;
}
- if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) &&
- !key->force_sw_encrypt) {
- /* hwaccel has already decrypted frame and verified MIC */
- } else {
+ if (!(rx->u.rx.status->flag & RX_FLAG_DECRYPTED)) {
+ /* hardware didn't decrypt/verify MIC */
u8 *scratch, *b_0, *aad;
scratch = key->u.ccmp.rx_crypto_buf;
@@ -642,8 +595,8 @@ ieee80211_rx_h_ccmp_decrypt(struct ieee80211_txrx_data *rx)
skb->data + skb->len - CCMP_MIC_LEN,
skb->data + hdrlen + CCMP_HDR_LEN)) {
printk(KERN_DEBUG "%s: CCMP decrypt failed for RX "
- "frame from " MAC_FMT "\n", rx->dev->name,
- MAC_ARG(rx->sta->addr));
+ "frame from %s\n", rx->dev->name,
+ print_mac(mac, rx->sta->addr));
return TXRX_DROP;
}
}
@@ -657,4 +610,3 @@ ieee80211_rx_h_ccmp_decrypt(struct ieee80211_txrx_data *rx)
return TXRX_CONTINUE;
}
-
diff --git a/net/mac80211/wpa.h b/net/mac80211/wpa.h
index da3b9594f9c3..49d80cf0cd75 100644
--- a/net/mac80211/wpa.h
+++ b/net/mac80211/wpa.h
@@ -19,13 +19,13 @@ ieee80211_txrx_result
ieee80211_rx_h_michael_mic_verify(struct ieee80211_txrx_data *rx);
ieee80211_txrx_result
-ieee80211_tx_h_tkip_encrypt(struct ieee80211_txrx_data *tx);
+ieee80211_crypto_tkip_encrypt(struct ieee80211_txrx_data *tx);
ieee80211_txrx_result
-ieee80211_rx_h_tkip_decrypt(struct ieee80211_txrx_data *rx);
+ieee80211_crypto_tkip_decrypt(struct ieee80211_txrx_data *rx);
ieee80211_txrx_result
-ieee80211_tx_h_ccmp_encrypt(struct ieee80211_txrx_data *tx);
+ieee80211_crypto_ccmp_encrypt(struct ieee80211_txrx_data *tx);
ieee80211_txrx_result
-ieee80211_rx_h_ccmp_decrypt(struct ieee80211_txrx_data *rx);
+ieee80211_crypto_ccmp_decrypt(struct ieee80211_txrx_data *rx);
#endif /* WPA_H */