net/ieee80211 -> drivers/net/ipw2x00/libipw_* rename

The old ieee80211 code only remains as a support library for the ipw2100
and ipw2200 drivers.  So, move the code and rename it appropriately to
reflects it's true purpose and status.

Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
John W. Linville
2008-11-11 16:45:38 -05:00
parent 0795cd29b6
commit beb2a7f331
11 changed files with 51 additions and 53 deletions

View File

@@ -8,7 +8,7 @@ config IPW2100
select WIRELESS_EXT
select FW_LOADER
select LIB80211
select IEEE80211
select LIBIPW
---help---
A driver for the Intel PRO/Wireless 2100 Network
Connection 802.11b wireless network adapter.
@@ -67,7 +67,7 @@ config IPW2200
select WIRELESS_EXT
select FW_LOADER
select LIB80211
select IEEE80211
select LIBIPW
---help---
A driver for the Intel PRO/Wireless 2200BG and 2915ABG Network
Connection adapters.
@@ -148,3 +148,44 @@ config IPW2200_DEBUG
If you are not sure, say N here.
config LIBIPW
tristate
select WIRELESS_EXT
select CRYPTO
select CRYPTO_ARC4
select CRYPTO_ECB
select CRYPTO_AES
select CRYPTO_MICHAEL_MIC
select CRYPTO_ECB
select CRC32
select LIB80211
select LIB80211_CRYPT_WEP
select LIB80211_CRYPT_TKIP
select LIB80211_CRYPT_CCMP
---help---
This option enables the hardware independent IEEE 802.11
networking stack. This component is deprecated in favor of the
mac80211 component.
config LIBIPW_DEBUG
bool "Full debugging output for the LIBIPW component"
depends on LIBIPW
---help---
This option will enable debug tracing output for the
libipw component.
This will result in the kernel module being ~70k larger. You
can control which debug output is sent to the kernel log by
setting the value in
/proc/net/ieee80211/debug_level
For example:
% echo 0x00000FFO > /proc/net/ieee80211/debug_level
For a list of values you can assign to debug_level, you
can look at the bit mask values in <net/ieee80211.h>
If you are not trying to debug or develop the libipw
component, you most likely want to say N here.

View File

@@ -4,3 +4,11 @@
obj-$(CONFIG_IPW2100) += ipw2100.o
obj-$(CONFIG_IPW2200) += ipw2200.o
obj-$(CONFIG_LIBIPW) += libipw.o
libipw-objs := \
libipw_module.o \
libipw_tx.o \
libipw_rx.o \
libipw_wx.o \
libipw_geo.o

View File

@@ -0,0 +1,195 @@
/******************************************************************************
Copyright(c) 2005 Intel Corporation. All rights reserved.
This program is free software; you can redistribute it and/or modify it
under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59
Temple Place - Suite 330, Boston, MA 02111-1307, USA.
The full GNU General Public License is included in this distribution in the
file called LICENSE.
Contact Information:
James P. Ketrenos <ipw2100-admin@linux.intel.com>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
******************************************************************************/
#include <linux/compiler.h>
#include <linux/errno.h>
#include <linux/if_arp.h>
#include <linux/in6.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/tcp.h>
#include <linux/types.h>
#include <linux/wireless.h>
#include <linux/etherdevice.h>
#include <asm/uaccess.h>
#include <net/ieee80211.h>
int ieee80211_is_valid_channel(struct ieee80211_device *ieee, u8 channel)
{
int i;
/* Driver needs to initialize the geography map before using
* these helper functions */
if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0)
return 0;
if (ieee->freq_band & IEEE80211_24GHZ_BAND)
for (i = 0; i < ieee->geo.bg_channels; i++)
/* NOTE: If G mode is currently supported but
* this is a B only channel, we don't see it
* as valid. */
if ((ieee->geo.bg[i].channel == channel) &&
!(ieee->geo.bg[i].flags & IEEE80211_CH_INVALID) &&
(!(ieee->mode & IEEE_G) ||
!(ieee->geo.bg[i].flags & IEEE80211_CH_B_ONLY)))
return IEEE80211_24GHZ_BAND;
if (ieee->freq_band & IEEE80211_52GHZ_BAND)
for (i = 0; i < ieee->geo.a_channels; i++)
if ((ieee->geo.a[i].channel == channel) &&
!(ieee->geo.a[i].flags & IEEE80211_CH_INVALID))
return IEEE80211_52GHZ_BAND;
return 0;
}
int ieee80211_channel_to_index(struct ieee80211_device *ieee, u8 channel)
{
int i;
/* Driver needs to initialize the geography map before using
* these helper functions */
if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0)
return -1;
if (ieee->freq_band & IEEE80211_24GHZ_BAND)
for (i = 0; i < ieee->geo.bg_channels; i++)
if (ieee->geo.bg[i].channel == channel)
return i;
if (ieee->freq_band & IEEE80211_52GHZ_BAND)
for (i = 0; i < ieee->geo.a_channels; i++)
if (ieee->geo.a[i].channel == channel)
return i;
return -1;
}
u32 ieee80211_channel_to_freq(struct ieee80211_device * ieee, u8 channel)
{
const struct ieee80211_channel * ch;
/* Driver needs to initialize the geography map before using
* these helper functions */
if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0)
return 0;
ch = ieee80211_get_channel(ieee, channel);
if (!ch->channel)
return 0;
return ch->freq;
}
u8 ieee80211_freq_to_channel(struct ieee80211_device * ieee, u32 freq)
{
int i;
/* Driver needs to initialize the geography map before using
* these helper functions */
if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0)
return 0;
freq /= 100000;
if (ieee->freq_band & IEEE80211_24GHZ_BAND)
for (i = 0; i < ieee->geo.bg_channels; i++)
if (ieee->geo.bg[i].freq == freq)
return ieee->geo.bg[i].channel;
if (ieee->freq_band & IEEE80211_52GHZ_BAND)
for (i = 0; i < ieee->geo.a_channels; i++)
if (ieee->geo.a[i].freq == freq)
return ieee->geo.a[i].channel;
return 0;
}
int ieee80211_set_geo(struct ieee80211_device *ieee,
const struct ieee80211_geo *geo)
{
memcpy(ieee->geo.name, geo->name, 3);
ieee->geo.name[3] = '\0';
ieee->geo.bg_channels = geo->bg_channels;
ieee->geo.a_channels = geo->a_channels;
memcpy(ieee->geo.bg, geo->bg, geo->bg_channels *
sizeof(struct ieee80211_channel));
memcpy(ieee->geo.a, geo->a, ieee->geo.a_channels *
sizeof(struct ieee80211_channel));
return 0;
}
const struct ieee80211_geo *ieee80211_get_geo(struct ieee80211_device *ieee)
{
return &ieee->geo;
}
u8 ieee80211_get_channel_flags(struct ieee80211_device * ieee, u8 channel)
{
int index = ieee80211_channel_to_index(ieee, channel);
if (index == -1)
return IEEE80211_CH_INVALID;
if (channel <= IEEE80211_24GHZ_CHANNELS)
return ieee->geo.bg[index].flags;
return ieee->geo.a[index].flags;
}
static const struct ieee80211_channel bad_channel = {
.channel = 0,
.flags = IEEE80211_CH_INVALID,
.max_power = 0,
};
const struct ieee80211_channel *ieee80211_get_channel(struct ieee80211_device
*ieee, u8 channel)
{
int index = ieee80211_channel_to_index(ieee, channel);
if (index == -1)
return &bad_channel;
if (channel <= IEEE80211_24GHZ_CHANNELS)
return &ieee->geo.bg[index];
return &ieee->geo.a[index];
}
EXPORT_SYMBOL(ieee80211_get_channel);
EXPORT_SYMBOL(ieee80211_get_channel_flags);
EXPORT_SYMBOL(ieee80211_is_valid_channel);
EXPORT_SYMBOL(ieee80211_freq_to_channel);
EXPORT_SYMBOL(ieee80211_channel_to_freq);
EXPORT_SYMBOL(ieee80211_channel_to_index);
EXPORT_SYMBOL(ieee80211_set_geo);
EXPORT_SYMBOL(ieee80211_get_geo);

View File

@@ -0,0 +1,293 @@
/*******************************************************************************
Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
Portions of this file are based on the WEP enablement code provided by the
Host AP project hostap-drivers v0.1.3
Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
<j@w1.fi>
Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
This program is free software; you can redistribute it and/or modify it
under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59
Temple Place - Suite 330, Boston, MA 02111-1307, USA.
The full GNU General Public License is included in this distribution in the
file called LICENSE.
Contact Information:
James P. Ketrenos <ipw2100-admin@linux.intel.com>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
#include <linux/compiler.h>
#include <linux/errno.h>
#include <linux/if_arp.h>
#include <linux/in6.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/tcp.h>
#include <linux/types.h>
#include <linux/wireless.h>
#include <linux/etherdevice.h>
#include <asm/uaccess.h>
#include <net/net_namespace.h>
#include <net/arp.h>
#include <net/ieee80211.h>
#define DRV_DESCRIPTION "802.11 data/management/control stack"
#define DRV_NAME "ieee80211"
#define DRV_VERSION IEEE80211_VERSION
#define DRV_COPYRIGHT "Copyright (C) 2004-2005 Intel Corporation <jketreno@linux.intel.com>"
MODULE_VERSION(DRV_VERSION);
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_AUTHOR(DRV_COPYRIGHT);
MODULE_LICENSE("GPL");
static int ieee80211_networks_allocate(struct ieee80211_device *ieee)
{
if (ieee->networks)
return 0;
ieee->networks =
kzalloc(MAX_NETWORK_COUNT * sizeof(struct ieee80211_network),
GFP_KERNEL);
if (!ieee->networks) {
printk(KERN_WARNING "%s: Out of memory allocating beacons\n",
ieee->dev->name);
return -ENOMEM;
}
return 0;
}
void ieee80211_network_reset(struct ieee80211_network *network)
{
if (!network)
return;
if (network->ibss_dfs) {
kfree(network->ibss_dfs);
network->ibss_dfs = NULL;
}
}
static inline void ieee80211_networks_free(struct ieee80211_device *ieee)
{
int i;
if (!ieee->networks)
return;
for (i = 0; i < MAX_NETWORK_COUNT; i++)
if (ieee->networks[i].ibss_dfs)
kfree(ieee->networks[i].ibss_dfs);
kfree(ieee->networks);
ieee->networks = NULL;
}
static void ieee80211_networks_initialize(struct ieee80211_device *ieee)
{
int i;
INIT_LIST_HEAD(&ieee->network_free_list);
INIT_LIST_HEAD(&ieee->network_list);
for (i = 0; i < MAX_NETWORK_COUNT; i++)
list_add_tail(&ieee->networks[i].list,
&ieee->network_free_list);
}
static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
{
if ((new_mtu < 68) || (new_mtu > IEEE80211_DATA_LEN))
return -EINVAL;
dev->mtu = new_mtu;
return 0;
}
static struct net_device_stats *ieee80211_generic_get_stats(
struct net_device *dev)
{
struct ieee80211_device *ieee = netdev_priv(dev);
return &ieee->stats;
}
struct net_device *alloc_ieee80211(int sizeof_priv)
{
struct ieee80211_device *ieee;
struct net_device *dev;
int err;
IEEE80211_DEBUG_INFO("Initializing...\n");
dev = alloc_etherdev(sizeof(struct ieee80211_device) + sizeof_priv);
if (!dev) {
IEEE80211_ERROR("Unable to allocate network device.\n");
goto failed;
}
ieee = netdev_priv(dev);
dev->hard_start_xmit = ieee80211_xmit;
dev->change_mtu = ieee80211_change_mtu;
/* Drivers are free to override this if the generic implementation
* does not meet their needs. */
dev->get_stats = ieee80211_generic_get_stats;
ieee->dev = dev;
err = ieee80211_networks_allocate(ieee);
if (err) {
IEEE80211_ERROR("Unable to allocate beacon storage: %d\n", err);
goto failed_free_netdev;
}
ieee80211_networks_initialize(ieee);
/* Default fragmentation threshold is maximum payload size */
ieee->fts = DEFAULT_FTS;
ieee->rts = DEFAULT_FTS;
ieee->scan_age = DEFAULT_MAX_SCAN_AGE;
ieee->open_wep = 1;
/* Default to enabling full open WEP with host based encrypt/decrypt */
ieee->host_encrypt = 1;
ieee->host_decrypt = 1;
ieee->host_mc_decrypt = 1;
/* Host fragementation in Open mode. Default is enabled.
* Note: host fragmentation is always enabled if host encryption
* is enabled. For cards can do hardware encryption, they must do
* hardware fragmentation as well. So we don't need a variable
* like host_enc_frag. */
ieee->host_open_frag = 1;
ieee->ieee802_1x = 1; /* Default to supporting 802.1x */
spin_lock_init(&ieee->lock);
lib80211_crypt_info_init(&ieee->crypt_info, dev->name, &ieee->lock);
ieee->wpa_enabled = 0;
ieee->drop_unencrypted = 0;
ieee->privacy_invoked = 0;
return dev;
failed_free_netdev:
free_netdev(dev);
failed:
return NULL;
}
void free_ieee80211(struct net_device *dev)
{
struct ieee80211_device *ieee = netdev_priv(dev);
lib80211_crypt_info_free(&ieee->crypt_info);
ieee80211_networks_free(ieee);
free_netdev(dev);
}
#ifdef CONFIG_IEEE80211_DEBUG
static int debug = 0;
u32 ieee80211_debug_level = 0;
EXPORT_SYMBOL_GPL(ieee80211_debug_level);
static struct proc_dir_entry *ieee80211_proc = NULL;
static int show_debug_level(char *page, char **start, off_t offset,
int count, int *eof, void *data)
{
return snprintf(page, count, "0x%08X\n", ieee80211_debug_level);
}
static int store_debug_level(struct file *file, const char __user * buffer,
unsigned long count, void *data)
{
char buf[] = "0x00000000\n";
unsigned long len = min((unsigned long)sizeof(buf) - 1, count);
unsigned long val;
if (copy_from_user(buf, buffer, len))
return count;
buf[len] = 0;
if (sscanf(buf, "%li", &val) != 1)
printk(KERN_INFO DRV_NAME
": %s is not in hex or decimal form.\n", buf);
else
ieee80211_debug_level = val;
return strnlen(buf, len);
}
#endif /* CONFIG_IEEE80211_DEBUG */
static int __init ieee80211_init(void)
{
#ifdef CONFIG_IEEE80211_DEBUG
struct proc_dir_entry *e;
ieee80211_debug_level = debug;
ieee80211_proc = proc_mkdir(DRV_NAME, init_net.proc_net);
if (ieee80211_proc == NULL) {
IEEE80211_ERROR("Unable to create " DRV_NAME
" proc directory\n");
return -EIO;
}
e = create_proc_entry("debug_level", S_IFREG | S_IRUGO | S_IWUSR,
ieee80211_proc);
if (!e) {
remove_proc_entry(DRV_NAME, init_net.proc_net);
ieee80211_proc = NULL;
return -EIO;
}
e->read_proc = show_debug_level;
e->write_proc = store_debug_level;
e->data = NULL;
#endif /* CONFIG_IEEE80211_DEBUG */
printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
return 0;
}
static void __exit ieee80211_exit(void)
{
#ifdef CONFIG_IEEE80211_DEBUG
if (ieee80211_proc) {
remove_proc_entry("debug_level", ieee80211_proc);
remove_proc_entry(DRV_NAME, init_net.proc_net);
ieee80211_proc = NULL;
}
#endif /* CONFIG_IEEE80211_DEBUG */
}
#ifdef CONFIG_IEEE80211_DEBUG
#include <linux/moduleparam.h>
module_param(debug, int, 0444);
MODULE_PARM_DESC(debug, "debug output mask");
#endif /* CONFIG_IEEE80211_DEBUG */
module_exit(ieee80211_exit);
module_init(ieee80211_init);
EXPORT_SYMBOL(alloc_ieee80211);
EXPORT_SYMBOL(free_ieee80211);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,546 @@
/******************************************************************************
Copyright(c) 2003 - 2005 Intel Corporation. All rights reserved.
This program is free software; you can redistribute it and/or modify it
under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59
Temple Place - Suite 330, Boston, MA 02111-1307, USA.
The full GNU General Public License is included in this distribution in the
file called LICENSE.
Contact Information:
James P. Ketrenos <ipw2100-admin@linux.intel.com>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
******************************************************************************/
#include <linux/compiler.h>
#include <linux/errno.h>
#include <linux/if_arp.h>
#include <linux/in6.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/tcp.h>
#include <linux/types.h>
#include <linux/wireless.h>
#include <linux/etherdevice.h>
#include <asm/uaccess.h>
#include <net/ieee80211.h>
/*
802.11 Data Frame
,-------------------------------------------------------------------.
Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 |
|------|------|---------|---------|---------|------|---------|------|
Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | Frame | fcs |
| | tion | (BSSID) | | | ence | data | |
`--------------------------------------------------| |------'
Total: 28 non-data bytes `----.----'
|
.- 'Frame data' expands, if WEP enabled, to <----------'
|
V
,-----------------------.
Bytes | 4 | 0-2296 | 4 |
|-----|-----------|-----|
Desc. | IV | Encrypted | ICV |
| | Packet | |
`-----| |-----'
`-----.-----'
|
.- 'Encrypted Packet' expands to
|
V
,---------------------------------------------------.
Bytes | 1 | 1 | 1 | 3 | 2 | 0-2304 |
|------|------|---------|----------|------|---------|
Desc. | SNAP | SNAP | Control |Eth Tunnel| Type | IP |
| DSAP | SSAP | | | | Packet |
| 0xAA | 0xAA |0x03 (UI)|0x00-00-F8| | |
`----------------------------------------------------
Total: 8 non-data bytes
802.3 Ethernet Data Frame
,-----------------------------------------.
Bytes | 6 | 6 | 2 | Variable | 4 |
|-------|-------|------|-----------|------|
Desc. | Dest. | Source| Type | IP Packet | fcs |
| MAC | MAC | | | |
`-----------------------------------------'
Total: 18 non-data bytes
In the event that fragmentation is required, the incoming payload is split into
N parts of size ieee->fts. The first fragment contains the SNAP header and the
remaining packets are just data.
If encryption is enabled, each fragment payload size is reduced by enough space
to add the prefix and postfix (IV and ICV totalling 8 bytes in the case of WEP)
So if you have 1500 bytes of payload with ieee->fts set to 500 without
encryption it will take 3 frames. With WEP it will take 4 frames as the
payload of each frame is reduced to 492 bytes.
* SKB visualization
*
* ,- skb->data
* |
* | ETHERNET HEADER ,-<-- PAYLOAD
* | | 14 bytes from skb->data
* | 2 bytes for Type --> ,T. | (sizeof ethhdr)
* | | | |
* |,-Dest.--. ,--Src.---. | | |
* | 6 bytes| | 6 bytes | | | |
* v | | | | | |
* 0 | v 1 | v | v 2
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
* ^ | ^ | ^ |
* | | | | | |
* | | | | `T' <---- 2 bytes for Type
* | | | |
* | | '---SNAP--' <-------- 6 bytes for SNAP
* | |
* `-IV--' <-------------------- 4 bytes for IV (WEP)
*
* SNAP HEADER
*
*/
static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 };
static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 };
static int ieee80211_copy_snap(u8 * data, __be16 h_proto)
{
struct ieee80211_snap_hdr *snap;
u8 *oui;
snap = (struct ieee80211_snap_hdr *)data;
snap->dsap = 0xaa;
snap->ssap = 0xaa;
snap->ctrl = 0x03;
if (h_proto == htons(ETH_P_AARP) || h_proto == htons(ETH_P_IPX))
oui = P802_1H_OUI;
else
oui = RFC1042_OUI;
snap->oui[0] = oui[0];
snap->oui[1] = oui[1];
snap->oui[2] = oui[2];
memcpy(data + SNAP_SIZE, &h_proto, sizeof(u16));
return SNAP_SIZE + sizeof(u16);
}
static int ieee80211_encrypt_fragment(struct ieee80211_device *ieee,
struct sk_buff *frag, int hdr_len)
{
struct lib80211_crypt_data *crypt =
ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx];
int res;
if (crypt == NULL)
return -1;
/* To encrypt, frame format is:
* IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */
atomic_inc(&crypt->refcnt);
res = 0;
if (crypt->ops && crypt->ops->encrypt_mpdu)
res = crypt->ops->encrypt_mpdu(frag, hdr_len, crypt->priv);
atomic_dec(&crypt->refcnt);
if (res < 0) {
printk(KERN_INFO "%s: Encryption failed: len=%d.\n",
ieee->dev->name, frag->len);
ieee->ieee_stats.tx_discards++;
return -1;
}
return 0;
}
void ieee80211_txb_free(struct ieee80211_txb *txb)
{
int i;
if (unlikely(!txb))
return;
for (i = 0; i < txb->nr_frags; i++)
if (txb->fragments[i])
dev_kfree_skb_any(txb->fragments[i]);
kfree(txb);
}
static struct ieee80211_txb *ieee80211_alloc_txb(int nr_frags, int txb_size,
int headroom, gfp_t gfp_mask)
{
struct ieee80211_txb *txb;
int i;
txb = kmalloc(sizeof(struct ieee80211_txb) + (sizeof(u8 *) * nr_frags),
gfp_mask);
if (!txb)
return NULL;
memset(txb, 0, sizeof(struct ieee80211_txb));
txb->nr_frags = nr_frags;
txb->frag_size = txb_size;
for (i = 0; i < nr_frags; i++) {
txb->fragments[i] = __dev_alloc_skb(txb_size + headroom,
gfp_mask);
if (unlikely(!txb->fragments[i])) {
i--;
break;
}
skb_reserve(txb->fragments[i], headroom);
}
if (unlikely(i != nr_frags)) {
while (i >= 0)
dev_kfree_skb_any(txb->fragments[i--]);
kfree(txb);
return NULL;
}
return txb;
}
static int ieee80211_classify(struct sk_buff *skb)
{
struct ethhdr *eth;
struct iphdr *ip;
eth = (struct ethhdr *)skb->data;
if (eth->h_proto != htons(ETH_P_IP))
return 0;
ip = ip_hdr(skb);
switch (ip->tos & 0xfc) {
case 0x20:
return 2;
case 0x40:
return 1;
case 0x60:
return 3;
case 0x80:
return 4;
case 0xa0:
return 5;
case 0xc0:
return 6;
case 0xe0:
return 7;
default:
return 0;
}
}
/* Incoming skb is converted to a txb which consists of
* a block of 802.11 fragment packets (stored as skbs) */
int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ieee80211_device *ieee = netdev_priv(dev);
struct ieee80211_txb *txb = NULL;
struct ieee80211_hdr_3addrqos *frag_hdr;
int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size,
rts_required;
unsigned long flags;
struct net_device_stats *stats = &ieee->stats;
int encrypt, host_encrypt, host_encrypt_msdu, host_build_iv;
__be16 ether_type;
int bytes, fc, hdr_len;
struct sk_buff *skb_frag;
struct ieee80211_hdr_3addrqos header = {/* Ensure zero initialized */
.duration_id = 0,
.seq_ctl = 0,
.qos_ctl = 0
};
u8 dest[ETH_ALEN], src[ETH_ALEN];
struct lib80211_crypt_data *crypt;
int priority = skb->priority;
int snapped = 0;
if (ieee->is_queue_full && (*ieee->is_queue_full) (dev, priority))
return NETDEV_TX_BUSY;
spin_lock_irqsave(&ieee->lock, flags);
/* If there is no driver handler to take the TXB, dont' bother
* creating it... */
if (!ieee->hard_start_xmit) {
printk(KERN_WARNING "%s: No xmit handler.\n", ieee->dev->name);
goto success;
}
if (unlikely(skb->len < SNAP_SIZE + sizeof(u16))) {
printk(KERN_WARNING "%s: skb too small (%d).\n",
ieee->dev->name, skb->len);
goto success;
}
ether_type = ((struct ethhdr *)skb->data)->h_proto;
crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx];
encrypt = !(ether_type == htons(ETH_P_PAE) && ieee->ieee802_1x) &&
ieee->sec.encrypt;
host_encrypt = ieee->host_encrypt && encrypt && crypt;
host_encrypt_msdu = ieee->host_encrypt_msdu && encrypt && crypt;
host_build_iv = ieee->host_build_iv && encrypt && crypt;
if (!encrypt && ieee->ieee802_1x &&
ieee->drop_unencrypted && ether_type != htons(ETH_P_PAE)) {
stats->tx_dropped++;
goto success;
}
/* Save source and destination addresses */
skb_copy_from_linear_data(skb, dest, ETH_ALEN);
skb_copy_from_linear_data_offset(skb, ETH_ALEN, src, ETH_ALEN);
if (host_encrypt || host_build_iv)
fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA |
IEEE80211_FCTL_PROTECTED;
else
fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA;
if (ieee->iw_mode == IW_MODE_INFRA) {
fc |= IEEE80211_FCTL_TODS;
/* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */
memcpy(header.addr1, ieee->bssid, ETH_ALEN);
memcpy(header.addr2, src, ETH_ALEN);
memcpy(header.addr3, dest, ETH_ALEN);
} else if (ieee->iw_mode == IW_MODE_ADHOC) {
/* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */
memcpy(header.addr1, dest, ETH_ALEN);
memcpy(header.addr2, src, ETH_ALEN);
memcpy(header.addr3, ieee->bssid, ETH_ALEN);
}
hdr_len = IEEE80211_3ADDR_LEN;
if (ieee->is_qos_active && ieee->is_qos_active(dev, skb)) {
fc |= IEEE80211_STYPE_QOS_DATA;
hdr_len += 2;
skb->priority = ieee80211_classify(skb);
header.qos_ctl |= cpu_to_le16(skb->priority & IEEE80211_QCTL_TID);
}
header.frame_ctl = cpu_to_le16(fc);
/* Advance the SKB to the start of the payload */
skb_pull(skb, sizeof(struct ethhdr));
/* Determine total amount of storage required for TXB packets */
bytes = skb->len + SNAP_SIZE + sizeof(u16);
/* Encrypt msdu first on the whole data packet. */
if ((host_encrypt || host_encrypt_msdu) &&
crypt && crypt->ops && crypt->ops->encrypt_msdu) {
int res = 0;
int len = bytes + hdr_len + crypt->ops->extra_msdu_prefix_len +
crypt->ops->extra_msdu_postfix_len;
struct sk_buff *skb_new = dev_alloc_skb(len);
if (unlikely(!skb_new))
goto failed;
skb_reserve(skb_new, crypt->ops->extra_msdu_prefix_len);
memcpy(skb_put(skb_new, hdr_len), &header, hdr_len);
snapped = 1;
ieee80211_copy_snap(skb_put(skb_new, SNAP_SIZE + sizeof(u16)),
ether_type);
skb_copy_from_linear_data(skb, skb_put(skb_new, skb->len), skb->len);
res = crypt->ops->encrypt_msdu(skb_new, hdr_len, crypt->priv);
if (res < 0) {
IEEE80211_ERROR("msdu encryption failed\n");
dev_kfree_skb_any(skb_new);
goto failed;
}
dev_kfree_skb_any(skb);
skb = skb_new;
bytes += crypt->ops->extra_msdu_prefix_len +
crypt->ops->extra_msdu_postfix_len;
skb_pull(skb, hdr_len);
}
if (host_encrypt || ieee->host_open_frag) {
/* Determine fragmentation size based on destination (multicast
* and broadcast are not fragmented) */
if (is_multicast_ether_addr(dest) ||
is_broadcast_ether_addr(dest))
frag_size = MAX_FRAG_THRESHOLD;
else
frag_size = ieee->fts;
/* Determine amount of payload per fragment. Regardless of if
* this stack is providing the full 802.11 header, one will
* eventually be affixed to this fragment -- so we must account
* for it when determining the amount of payload space. */
bytes_per_frag = frag_size - hdr_len;
if (ieee->config &
(CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
bytes_per_frag -= IEEE80211_FCS_LEN;
/* Each fragment may need to have room for encryptiong
* pre/postfix */
if (host_encrypt)
bytes_per_frag -= crypt->ops->extra_mpdu_prefix_len +
crypt->ops->extra_mpdu_postfix_len;
/* Number of fragments is the total
* bytes_per_frag / payload_per_fragment */
nr_frags = bytes / bytes_per_frag;
bytes_last_frag = bytes % bytes_per_frag;
if (bytes_last_frag)
nr_frags++;
else
bytes_last_frag = bytes_per_frag;
} else {
nr_frags = 1;
bytes_per_frag = bytes_last_frag = bytes;
frag_size = bytes + hdr_len;
}
rts_required = (frag_size > ieee->rts
&& ieee->config & CFG_IEEE80211_RTS);
if (rts_required)
nr_frags++;
/* When we allocate the TXB we allocate enough space for the reserve
* and full fragment bytes (bytes_per_frag doesn't include prefix,
* postfix, header, FCS, etc.) */
txb = ieee80211_alloc_txb(nr_frags, frag_size,
ieee->tx_headroom, GFP_ATOMIC);
if (unlikely(!txb)) {
printk(KERN_WARNING "%s: Could not allocate TXB\n",
ieee->dev->name);
goto failed;
}
txb->encrypted = encrypt;
if (host_encrypt)
txb->payload_size = frag_size * (nr_frags - 1) +
bytes_last_frag;
else
txb->payload_size = bytes;
if (rts_required) {
skb_frag = txb->fragments[0];
frag_hdr =
(struct ieee80211_hdr_3addrqos *)skb_put(skb_frag, hdr_len);
/*
* Set header frame_ctl to the RTS.
*/
header.frame_ctl =
cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS);
memcpy(frag_hdr, &header, hdr_len);
/*
* Restore header frame_ctl to the original data setting.
*/
header.frame_ctl = cpu_to_le16(fc);
if (ieee->config &
(CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
skb_put(skb_frag, 4);
txb->rts_included = 1;
i = 1;
} else
i = 0;
for (; i < nr_frags; i++) {
skb_frag = txb->fragments[i];
if (host_encrypt || host_build_iv)
skb_reserve(skb_frag,
crypt->ops->extra_mpdu_prefix_len);
frag_hdr =
(struct ieee80211_hdr_3addrqos *)skb_put(skb_frag, hdr_len);
memcpy(frag_hdr, &header, hdr_len);
/* If this is not the last fragment, then add the MOREFRAGS
* bit to the frame control */
if (i != nr_frags - 1) {
frag_hdr->frame_ctl =
cpu_to_le16(fc | IEEE80211_FCTL_MOREFRAGS);
bytes = bytes_per_frag;
} else {
/* The last fragment takes the remaining length */
bytes = bytes_last_frag;
}
if (i == 0 && !snapped) {
ieee80211_copy_snap(skb_put
(skb_frag, SNAP_SIZE + sizeof(u16)),
ether_type);
bytes -= SNAP_SIZE + sizeof(u16);
}
skb_copy_from_linear_data(skb, skb_put(skb_frag, bytes), bytes);
/* Advance the SKB... */
skb_pull(skb, bytes);
/* Encryption routine will move the header forward in order
* to insert the IV between the header and the payload */
if (host_encrypt)
ieee80211_encrypt_fragment(ieee, skb_frag, hdr_len);
else if (host_build_iv) {
atomic_inc(&crypt->refcnt);
if (crypt->ops->build_iv)
crypt->ops->build_iv(skb_frag, hdr_len,
ieee->sec.keys[ieee->sec.active_key],
ieee->sec.key_sizes[ieee->sec.active_key],
crypt->priv);
atomic_dec(&crypt->refcnt);
}
if (ieee->config &
(CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
skb_put(skb_frag, 4);
}
success:
spin_unlock_irqrestore(&ieee->lock, flags);
dev_kfree_skb_any(skb);
if (txb) {
int ret = (*ieee->hard_start_xmit) (txb, dev, priority);
if (ret == 0) {
stats->tx_packets++;
stats->tx_bytes += txb->payload_size;
return 0;
}
ieee80211_txb_free(txb);
}
return 0;
failed:
spin_unlock_irqrestore(&ieee->lock, flags);
netif_stop_queue(dev);
stats->tx_errors++;
return 1;
}
EXPORT_SYMBOL(ieee80211_txb_free);

View File

@@ -0,0 +1,760 @@
/******************************************************************************
Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
Portions of this file are based on the WEP enablement code provided by the
Host AP project hostap-drivers v0.1.3
Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
<j@w1.fi>
Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
This program is free software; you can redistribute it and/or modify it
under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59
Temple Place - Suite 330, Boston, MA 02111-1307, USA.
The full GNU General Public License is included in this distribution in the
file called LICENSE.
Contact Information:
James P. Ketrenos <ipw2100-admin@linux.intel.com>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
******************************************************************************/
#include <linux/kmod.h>
#include <linux/module.h>
#include <linux/jiffies.h>
#include <net/lib80211.h>
#include <net/ieee80211.h>
#include <linux/wireless.h>
static const char *ieee80211_modes[] = {
"?", "a", "b", "ab", "g", "ag", "bg", "abg"
};
#define MAX_CUSTOM_LEN 64
static char *ieee80211_translate_scan(struct ieee80211_device *ieee,
char *start, char *stop,
struct ieee80211_network *network,
struct iw_request_info *info)
{
char custom[MAX_CUSTOM_LEN];
char *p;
struct iw_event iwe;
int i, j;
char *current_val; /* For rates */
u8 rate;
/* First entry *MUST* be the AP MAC address */
iwe.cmd = SIOCGIWAP;
iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
/* Remaining entries will be displayed in the order we provide them */
/* Add the ESSID */
iwe.cmd = SIOCGIWESSID;
iwe.u.data.flags = 1;
iwe.u.data.length = min(network->ssid_len, (u8) 32);
start = iwe_stream_add_point(info, start, stop,
&iwe, network->ssid);
/* Add the protocol name */
iwe.cmd = SIOCGIWNAME;
snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
ieee80211_modes[network->mode]);
start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
/* Add mode */
iwe.cmd = SIOCGIWMODE;
if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
if (network->capability & WLAN_CAPABILITY_ESS)
iwe.u.mode = IW_MODE_MASTER;
else
iwe.u.mode = IW_MODE_ADHOC;
start = iwe_stream_add_event(info, start, stop,
&iwe, IW_EV_UINT_LEN);
}
/* Add channel and frequency */
/* Note : userspace automatically computes channel using iwrange */
iwe.cmd = SIOCGIWFREQ;
iwe.u.freq.m = ieee80211_channel_to_freq(ieee, network->channel);
iwe.u.freq.e = 6;
iwe.u.freq.i = 0;
start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
/* Add encryption capability */
iwe.cmd = SIOCGIWENCODE;
if (network->capability & WLAN_CAPABILITY_PRIVACY)
iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
else
iwe.u.data.flags = IW_ENCODE_DISABLED;
iwe.u.data.length = 0;
start = iwe_stream_add_point(info, start, stop,
&iwe, network->ssid);
/* Add basic and extended rates */
/* Rate : stuffing multiple values in a single event require a bit
* more of magic - Jean II */
current_val = start + iwe_stream_lcp_len(info);
iwe.cmd = SIOCGIWRATE;
/* Those two flags are ignored... */
iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
for (i = 0, j = 0; i < network->rates_len;) {
if (j < network->rates_ex_len &&
((network->rates_ex[j] & 0x7F) <
(network->rates[i] & 0x7F)))
rate = network->rates_ex[j++] & 0x7F;
else
rate = network->rates[i++] & 0x7F;
/* Bit rate given in 500 kb/s units (+ 0x80) */
iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
/* Add new value to event */
current_val = iwe_stream_add_value(info, start, current_val,
stop, &iwe, IW_EV_PARAM_LEN);
}
for (; j < network->rates_ex_len; j++) {
rate = network->rates_ex[j] & 0x7F;
/* Bit rate given in 500 kb/s units (+ 0x80) */
iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
/* Add new value to event */
current_val = iwe_stream_add_value(info, start, current_val,
stop, &iwe, IW_EV_PARAM_LEN);
}
/* Check if we added any rate */
if ((current_val - start) > iwe_stream_lcp_len(info))
start = current_val;
/* Add quality statistics */
iwe.cmd = IWEVQUAL;
iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
IW_QUAL_NOISE_UPDATED;
if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) {
iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
IW_QUAL_LEVEL_INVALID;
iwe.u.qual.qual = 0;
} else {
if (ieee->perfect_rssi == ieee->worst_rssi)
iwe.u.qual.qual = 100;
else
iwe.u.qual.qual =
(100 *
(ieee->perfect_rssi - ieee->worst_rssi) *
(ieee->perfect_rssi - ieee->worst_rssi) -
(ieee->perfect_rssi - network->stats.rssi) *
(15 * (ieee->perfect_rssi - ieee->worst_rssi) +
62 * (ieee->perfect_rssi -
network->stats.rssi))) /
((ieee->perfect_rssi -
ieee->worst_rssi) * (ieee->perfect_rssi -
ieee->worst_rssi));
if (iwe.u.qual.qual > 100)
iwe.u.qual.qual = 100;
else if (iwe.u.qual.qual < 1)
iwe.u.qual.qual = 0;
}
if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) {
iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
iwe.u.qual.noise = 0;
} else {
iwe.u.qual.noise = network->stats.noise;
}
if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL)) {
iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
iwe.u.qual.level = 0;
} else {
iwe.u.qual.level = network->stats.signal;
}
start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
iwe.cmd = IWEVCUSTOM;
p = custom;
iwe.u.data.length = p - custom;
if (iwe.u.data.length)
start = iwe_stream_add_point(info, start, stop, &iwe, custom);
memset(&iwe, 0, sizeof(iwe));
if (network->wpa_ie_len) {
char buf[MAX_WPA_IE_LEN];
memcpy(buf, network->wpa_ie, network->wpa_ie_len);
iwe.cmd = IWEVGENIE;
iwe.u.data.length = network->wpa_ie_len;
start = iwe_stream_add_point(info, start, stop, &iwe, buf);
}
memset(&iwe, 0, sizeof(iwe));
if (network->rsn_ie_len) {
char buf[MAX_WPA_IE_LEN];
memcpy(buf, network->rsn_ie, network->rsn_ie_len);
iwe.cmd = IWEVGENIE;
iwe.u.data.length = network->rsn_ie_len;
start = iwe_stream_add_point(info, start, stop, &iwe, buf);
}
/* Add EXTRA: Age to display seconds since last beacon/probe response
* for given network. */
iwe.cmd = IWEVCUSTOM;
p = custom;
p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
" Last beacon: %dms ago",
jiffies_to_msecs(jiffies - network->last_scanned));
iwe.u.data.length = p - custom;
if (iwe.u.data.length)
start = iwe_stream_add_point(info, start, stop, &iwe, custom);
/* Add spectrum management information */
iwe.cmd = -1;
p = custom;
p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
if (ieee80211_get_channel_flags(ieee, network->channel) &
IEEE80211_CH_INVALID) {
iwe.cmd = IWEVCUSTOM;
p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
}
if (ieee80211_get_channel_flags(ieee, network->channel) &
IEEE80211_CH_RADAR_DETECT) {
iwe.cmd = IWEVCUSTOM;
p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
}
if (iwe.cmd == IWEVCUSTOM) {
iwe.u.data.length = p - custom;
start = iwe_stream_add_point(info, start, stop, &iwe, custom);
}
return start;
}
#define SCAN_ITEM_SIZE 128
int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct ieee80211_network *network;
unsigned long flags;
int err = 0;
char *ev = extra;
char *stop = ev + wrqu->data.length;
int i = 0;
DECLARE_SSID_BUF(ssid);
IEEE80211_DEBUG_WX("Getting scan\n");
spin_lock_irqsave(&ieee->lock, flags);
list_for_each_entry(network, &ieee->network_list, list) {
i++;
if (stop - ev < SCAN_ITEM_SIZE) {
err = -E2BIG;
break;
}
if (ieee->scan_age == 0 ||
time_after(network->last_scanned + ieee->scan_age, jiffies))
ev = ieee80211_translate_scan(ieee, ev, stop, network,
info);
else
IEEE80211_DEBUG_SCAN("Not showing network '%s ("
"%pM)' due to age (%dms).\n",
print_ssid(ssid, network->ssid,
network->ssid_len),
network->bssid,
jiffies_to_msecs(jiffies -
network->
last_scanned));
}
spin_unlock_irqrestore(&ieee->lock, flags);
wrqu->data.length = ev - extra;
wrqu->data.flags = 0;
IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
return err;
}
int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
struct iw_request_info *info,
union iwreq_data *wrqu, char *keybuf)
{
struct iw_point *erq = &(wrqu->encoding);
struct net_device *dev = ieee->dev;
struct ieee80211_security sec = {
.flags = 0
};
int i, key, key_provided, len;
struct lib80211_crypt_data **crypt;
int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv;
DECLARE_SSID_BUF(ssid);
IEEE80211_DEBUG_WX("SET_ENCODE\n");
key = erq->flags & IW_ENCODE_INDEX;
if (key) {
if (key > WEP_KEYS)
return -EINVAL;
key--;
key_provided = 1;
} else {
key_provided = 0;
key = ieee->crypt_info.tx_keyidx;
}
IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
"provided" : "default");
crypt = &ieee->crypt_info.crypt[key];
if (erq->flags & IW_ENCODE_DISABLED) {
if (key_provided && *crypt) {
IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
key);
lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
} else
IEEE80211_DEBUG_WX("Disabling encryption.\n");
/* Check all the keys to see if any are still configured,
* and if no key index was provided, de-init them all */
for (i = 0; i < WEP_KEYS; i++) {
if (ieee->crypt_info.crypt[i] != NULL) {
if (key_provided)
break;
lib80211_crypt_delayed_deinit(&ieee->crypt_info,
&ieee->crypt_info.crypt[i]);
}
}
if (i == WEP_KEYS) {
sec.enabled = 0;
sec.encrypt = 0;
sec.level = SEC_LEVEL_0;
sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
}
goto done;
}
sec.enabled = 1;
sec.encrypt = 1;
sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
if (*crypt != NULL && (*crypt)->ops != NULL &&
strcmp((*crypt)->ops->name, "WEP") != 0) {
/* changing to use WEP; deinit previously used algorithm
* on this key */
lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
}
if (*crypt == NULL && host_crypto) {
struct lib80211_crypt_data *new_crypt;
/* take WEP into use */
new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
GFP_KERNEL);
if (new_crypt == NULL)
return -ENOMEM;
new_crypt->ops = lib80211_get_crypto_ops("WEP");
if (!new_crypt->ops) {
request_module("lib80211_crypt_wep");
new_crypt->ops = lib80211_get_crypto_ops("WEP");
}
if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
new_crypt->priv = new_crypt->ops->init(key);
if (!new_crypt->ops || !new_crypt->priv) {
kfree(new_crypt);
new_crypt = NULL;
printk(KERN_WARNING "%s: could not initialize WEP: "
"load module lib80211_crypt_wep\n", dev->name);
return -EOPNOTSUPP;
}
*crypt = new_crypt;
}
/* If a new key was provided, set it up */
if (erq->length > 0) {
#ifdef CONFIG_IEEE80211_DEBUG
DECLARE_SSID_BUF(ssid);
#endif
len = erq->length <= 5 ? 5 : 13;
memcpy(sec.keys[key], keybuf, erq->length);
if (len > erq->length)
memset(sec.keys[key] + erq->length, 0,
len - erq->length);
IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
key, print_ssid(ssid, sec.keys[key], len),
erq->length, len);
sec.key_sizes[key] = len;
if (*crypt)
(*crypt)->ops->set_key(sec.keys[key], len, NULL,
(*crypt)->priv);
sec.flags |= (1 << key);
/* This ensures a key will be activated if no key is
* explicitly set */
if (key == sec.active_key)
sec.flags |= SEC_ACTIVE_KEY;
} else {
if (host_crypto) {
len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
NULL, (*crypt)->priv);
if (len == 0) {
/* Set a default key of all 0 */
IEEE80211_DEBUG_WX("Setting key %d to all "
"zero.\n", key);
memset(sec.keys[key], 0, 13);
(*crypt)->ops->set_key(sec.keys[key], 13, NULL,
(*crypt)->priv);
sec.key_sizes[key] = 13;
sec.flags |= (1 << key);
}
}
/* No key data - just set the default TX key index */
if (key_provided) {
IEEE80211_DEBUG_WX("Setting key %d to default Tx "
"key.\n", key);
ieee->crypt_info.tx_keyidx = key;
sec.active_key = key;
sec.flags |= SEC_ACTIVE_KEY;
}
}
if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
WLAN_AUTH_SHARED_KEY;
sec.flags |= SEC_AUTH_MODE;
IEEE80211_DEBUG_WX("Auth: %s\n",
sec.auth_mode == WLAN_AUTH_OPEN ?
"OPEN" : "SHARED KEY");
}
/* For now we just support WEP, so only set that security level...
* TODO: When WPA is added this is one place that needs to change */
sec.flags |= SEC_LEVEL;
sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
sec.encode_alg[key] = SEC_ALG_WEP;
done:
if (ieee->set_security)
ieee->set_security(dev, &sec);
/* Do not reset port if card is in Managed mode since resetting will
* generate new IEEE 802.11 authentication which may end up in looping
* with IEEE 802.1X. If your hardware requires a reset after WEP
* configuration (for example... Prism2), implement the reset_port in
* the callbacks structures used to initialize the 802.11 stack. */
if (ieee->reset_on_keychange &&
ieee->iw_mode != IW_MODE_INFRA &&
ieee->reset_port && ieee->reset_port(dev)) {
printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
return -EINVAL;
}
return 0;
}
int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
struct iw_request_info *info,
union iwreq_data *wrqu, char *keybuf)
{
struct iw_point *erq = &(wrqu->encoding);
int len, key;
struct lib80211_crypt_data *crypt;
struct ieee80211_security *sec = &ieee->sec;
IEEE80211_DEBUG_WX("GET_ENCODE\n");
key = erq->flags & IW_ENCODE_INDEX;
if (key) {
if (key > WEP_KEYS)
return -EINVAL;
key--;
} else
key = ieee->crypt_info.tx_keyidx;
crypt = ieee->crypt_info.crypt[key];
erq->flags = key + 1;
if (!sec->enabled) {
erq->length = 0;
erq->flags |= IW_ENCODE_DISABLED;
return 0;
}
len = sec->key_sizes[key];
memcpy(keybuf, sec->keys[key], len);
erq->length = len;
erq->flags |= IW_ENCODE_ENABLED;
if (ieee->open_wep)
erq->flags |= IW_ENCODE_OPEN;
else
erq->flags |= IW_ENCODE_RESTRICTED;
return 0;
}
int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct net_device *dev = ieee->dev;
struct iw_point *encoding = &wrqu->encoding;
struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
int i, idx, ret = 0;
int group_key = 0;
const char *alg, *module;
struct lib80211_crypto_ops *ops;
struct lib80211_crypt_data **crypt;
struct ieee80211_security sec = {
.flags = 0,
};
idx = encoding->flags & IW_ENCODE_INDEX;
if (idx) {
if (idx < 1 || idx > WEP_KEYS)
return -EINVAL;
idx--;
} else
idx = ieee->crypt_info.tx_keyidx;
if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
crypt = &ieee->crypt_info.crypt[idx];
group_key = 1;
} else {
/* some Cisco APs use idx>0 for unicast in dynamic WEP */
if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
return -EINVAL;
if (ieee->iw_mode == IW_MODE_INFRA)
crypt = &ieee->crypt_info.crypt[idx];
else
return -EINVAL;
}
sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
if ((encoding->flags & IW_ENCODE_DISABLED) ||
ext->alg == IW_ENCODE_ALG_NONE) {
if (*crypt)
lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
for (i = 0; i < WEP_KEYS; i++)
if (ieee->crypt_info.crypt[i] != NULL)
break;
if (i == WEP_KEYS) {
sec.enabled = 0;
sec.encrypt = 0;
sec.level = SEC_LEVEL_0;
sec.flags |= SEC_LEVEL;
}
goto done;
}
sec.enabled = 1;
sec.encrypt = 1;
if (group_key ? !ieee->host_mc_decrypt :
!(ieee->host_encrypt || ieee->host_decrypt ||
ieee->host_encrypt_msdu))
goto skip_host_crypt;
switch (ext->alg) {
case IW_ENCODE_ALG_WEP:
alg = "WEP";
module = "lib80211_crypt_wep";
break;
case IW_ENCODE_ALG_TKIP:
alg = "TKIP";
module = "lib80211_crypt_tkip";
break;
case IW_ENCODE_ALG_CCMP:
alg = "CCMP";
module = "lib80211_crypt_ccmp";
break;
default:
IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
dev->name, ext->alg);
ret = -EINVAL;
goto done;
}
ops = lib80211_get_crypto_ops(alg);
if (ops == NULL) {
request_module(module);
ops = lib80211_get_crypto_ops(alg);
}
if (ops == NULL) {
IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
dev->name, ext->alg);
ret = -EINVAL;
goto done;
}
if (*crypt == NULL || (*crypt)->ops != ops) {
struct lib80211_crypt_data *new_crypt;
lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
if (new_crypt == NULL) {
ret = -ENOMEM;
goto done;
}
new_crypt->ops = ops;
if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
new_crypt->priv = new_crypt->ops->init(idx);
if (new_crypt->priv == NULL) {
kfree(new_crypt);
ret = -EINVAL;
goto done;
}
*crypt = new_crypt;
}
if (ext->key_len > 0 && (*crypt)->ops->set_key &&
(*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
(*crypt)->priv) < 0) {
IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
ret = -EINVAL;
goto done;
}
skip_host_crypt:
if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
ieee->crypt_info.tx_keyidx = idx;
sec.active_key = idx;
sec.flags |= SEC_ACTIVE_KEY;
}
if (ext->alg != IW_ENCODE_ALG_NONE) {
memcpy(sec.keys[idx], ext->key, ext->key_len);
sec.key_sizes[idx] = ext->key_len;
sec.flags |= (1 << idx);
if (ext->alg == IW_ENCODE_ALG_WEP) {
sec.encode_alg[idx] = SEC_ALG_WEP;
sec.flags |= SEC_LEVEL;
sec.level = SEC_LEVEL_1;
} else if (ext->alg == IW_ENCODE_ALG_TKIP) {
sec.encode_alg[idx] = SEC_ALG_TKIP;
sec.flags |= SEC_LEVEL;
sec.level = SEC_LEVEL_2;
} else if (ext->alg == IW_ENCODE_ALG_CCMP) {
sec.encode_alg[idx] = SEC_ALG_CCMP;
sec.flags |= SEC_LEVEL;
sec.level = SEC_LEVEL_3;
}
/* Don't set sec level for group keys. */
if (group_key)
sec.flags &= ~SEC_LEVEL;
}
done:
if (ieee->set_security)
ieee->set_security(ieee->dev, &sec);
/*
* Do not reset port if card is in Managed mode since resetting will
* generate new IEEE 802.11 authentication which may end up in looping
* with IEEE 802.1X. If your hardware requires a reset after WEP
* configuration (for example... Prism2), implement the reset_port in
* the callbacks structures used to initialize the 802.11 stack.
*/
if (ieee->reset_on_keychange &&
ieee->iw_mode != IW_MODE_INFRA &&
ieee->reset_port && ieee->reset_port(dev)) {
IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
return -EINVAL;
}
return ret;
}
int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct iw_point *encoding = &wrqu->encoding;
struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
struct ieee80211_security *sec = &ieee->sec;
int idx, max_key_len;
max_key_len = encoding->length - sizeof(*ext);
if (max_key_len < 0)
return -EINVAL;
idx = encoding->flags & IW_ENCODE_INDEX;
if (idx) {
if (idx < 1 || idx > WEP_KEYS)
return -EINVAL;
idx--;
} else
idx = ieee->crypt_info.tx_keyidx;
if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
ext->alg != IW_ENCODE_ALG_WEP)
if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
return -EINVAL;
encoding->flags = idx + 1;
memset(ext, 0, sizeof(*ext));
if (!sec->enabled) {
ext->alg = IW_ENCODE_ALG_NONE;
ext->key_len = 0;
encoding->flags |= IW_ENCODE_DISABLED;
} else {
if (sec->encode_alg[idx] == SEC_ALG_WEP)
ext->alg = IW_ENCODE_ALG_WEP;
else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
ext->alg = IW_ENCODE_ALG_TKIP;
else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
ext->alg = IW_ENCODE_ALG_CCMP;
else
return -EINVAL;
ext->key_len = sec->key_sizes[idx];
memcpy(ext->key, sec->keys[idx], ext->key_len);
encoding->flags |= IW_ENCODE_ENABLED;
if (ext->key_len &&
(ext->alg == IW_ENCODE_ALG_TKIP ||
ext->alg == IW_ENCODE_ALG_CCMP))
ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
}
return 0;
}
EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
EXPORT_SYMBOL(ieee80211_wx_get_scan);
EXPORT_SYMBOL(ieee80211_wx_set_encode);
EXPORT_SYMBOL(ieee80211_wx_get_encode);