Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,185 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Intel Centrino wireless drivers
#
config IPW2100
tristate "Intel PRO/Wireless 2100 Network Connection"
depends on PCI && CFG80211
select WIRELESS_EXT
select WEXT_PRIV
select FW_LOADER
select LIBIPW
help
A driver for the Intel PRO/Wireless 2100 Network
Connection 802.11b wireless network adapter.
See <file:Documentation/networking/device_drivers/wifi/intel/ipw2100.rst>
for information on the capabilities currently enabled in this driver
and for tips for debugging issues and problems.
In order to use this driver, you will need a firmware image for it.
You can obtain the firmware from
<http://ipw2100.sf.net/>. Once you have the firmware image, you
will need to place it in /lib/firmware.
You will also very likely need the Wireless Tools in order to
configure your card:
<https://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
It is recommended that you compile this driver as a module (M)
rather than built-in (Y). This driver requires firmware at device
initialization time, and when built-in this typically happens
before the filesystem is accessible (hence firmware will be
unavailable and initialization will fail). If you do choose to build
this driver into your kernel image, you can avoid this problem by
including the firmware and a firmware loader in an initramfs.
config IPW2100_MONITOR
bool "Enable promiscuous mode"
depends on IPW2100
help
Enables promiscuous/monitor mode support for the ipw2100 driver.
With this feature compiled into the driver, you can switch to
promiscuous mode via the Wireless Tool's Monitor mode. While in this
mode, no packets can be sent.
config IPW2100_DEBUG
bool "Enable full debugging output in IPW2100 module."
depends on IPW2100
help
This option will enable debug tracing output for the IPW2100.
This will result in the kernel module being ~60k larger. You can
control which debug output is sent to the kernel log by setting the
value in
/sys/bus/pci/drivers/ipw2100/debug_level
This entry will only exist if this option is enabled.
If you are not trying to debug or develop the IPW2100 driver, you
most likely want to say N here.
config IPW2200
tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection"
depends on PCI && CFG80211
select WIRELESS_EXT
select WEXT_PRIV
select FW_LOADER
select LIBIPW
help
A driver for the Intel PRO/Wireless 2200BG and 2915ABG Network
Connection adapters.
See <file:Documentation/networking/device_drivers/wifi/intel/ipw2200.rst>
for information on the capabilities currently enabled in this
driver and for tips for debugging issues and problems.
In order to use this driver, you will need a firmware image for it.
You can obtain the firmware from
<http://ipw2200.sf.net/>. See the above referenced README.ipw2200
for information on where to install the firmware images.
You will also very likely need the Wireless Tools in order to
configure your card:
<https://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
It is recommended that you compile this driver as a module (M)
rather than built-in (Y). This driver requires firmware at device
initialization time, and when built-in this typically happens
before the filesystem is accessible (hence firmware will be
unavailable and initialization will fail). If you do choose to build
this driver into your kernel image, you can avoid this problem by
including the firmware and a firmware loader in an initramfs.
config IPW2200_MONITOR
bool "Enable promiscuous mode"
depends on IPW2200
help
Enables promiscuous/monitor mode support for the ipw2200 driver.
With this feature compiled into the driver, you can switch to
promiscuous mode via the Wireless Tool's Monitor mode. While in this
mode, no packets can be sent.
config IPW2200_RADIOTAP
bool "Enable radiotap format 802.11 raw packet support"
depends on IPW2200_MONITOR
config IPW2200_PROMISCUOUS
bool "Enable creation of a RF radiotap promiscuous interface"
depends on IPW2200_MONITOR
select IPW2200_RADIOTAP
help
Enables the creation of a second interface prefixed 'rtap'.
This second interface will provide every received in radiotap
format.
This is useful for performing wireless network analysis while
maintaining an active association.
Example usage:
% modprobe ipw2200 rtap_iface=1
% ifconfig rtap0 up
% tethereal -i rtap0
If you do not specify 'rtap_iface=1' as a module parameter then
the rtap interface will not be created and you will need to turn
it on via sysfs:
% echo 1 > /sys/bus/pci/drivers/ipw2200/*/rtap_iface
config IPW2200_QOS
bool "Enable QoS support"
depends on IPW2200
config IPW2200_DEBUG
bool "Enable full debugging output in IPW2200 module."
depends on IPW2200
help
This option will enable low level debug tracing output for IPW2200.
Note, normal debug code is already compiled in. This low level
debug option enables debug on hot paths (e.g Tx, Rx, ISR) and
will result in the kernel module being ~70 larger. Most users
will typically not need this high verbosity debug information.
If you are not sure, say N here.
config LIBIPW
tristate
depends on PCI && CFG80211
select WIRELESS_EXT
select CRYPTO
select CRYPTO_LIB_ARC4
select CRC32
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 ieee80211.h
If you are not trying to debug or develop the libipw
component, you most likely want to say N here.
@@ -0,0 +1,20 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the Intel Centrino wireless drivers
#
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 \
libipw_spy.o \
libipw_crypto.o \
libipw_crypto_ccmp.o \
libipw_crypto_tkip.o \
libipw_crypto_wep.o
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Intel Pro/Wireless 2100, 2200BG, 2915ABG network connection driver
*
* Copyright 2012 Stanislav Yakovlev <stas.yakovlev@gmail.com>
*/
#ifndef __IPW_H__
#define __IPW_H__
#include <linux/ieee80211.h>
static const u32 ipw_cipher_suites[] = {
WLAN_CIPHER_SUITE_WEP40,
WLAN_CIPHER_SUITE_WEP104,
WLAN_CIPHER_SUITE_TKIP,
WLAN_CIPHER_SUITE_CCMP,
};
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,246 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* libipw -- common bits for IPW drivers
*
* Copyright(c) 2008 John W. Linville <linville@tuxdriver.com>
*
* Portions copied from old ieee80211 component, w/ original copyright
* notices below:
*
* Host AP crypto routines
*
* Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
* Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com>
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/ieee80211.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/string.h>
#include "libipw.h"
struct libipw_crypto_alg {
struct list_head list;
const struct libipw_crypto_ops *ops;
};
static LIST_HEAD(libipw_crypto_algs);
static DEFINE_SPINLOCK(libipw_crypto_lock);
static void libipw_crypt_deinit_entries(struct libipw_crypt_info *info,
int force);
static void libipw_crypt_quiescing(struct libipw_crypt_info *info);
static void libipw_crypt_deinit_handler(struct timer_list *t);
int libipw_crypt_info_init(struct libipw_crypt_info *info, char *name,
spinlock_t *lock)
{
memset(info, 0, sizeof(*info));
info->name = name;
info->lock = lock;
INIT_LIST_HEAD(&info->crypt_deinit_list);
timer_setup(&info->crypt_deinit_timer, libipw_crypt_deinit_handler,
0);
return 0;
}
EXPORT_SYMBOL(libipw_crypt_info_init);
void libipw_crypt_info_free(struct libipw_crypt_info *info)
{
int i;
libipw_crypt_quiescing(info);
timer_delete_sync(&info->crypt_deinit_timer);
libipw_crypt_deinit_entries(info, 1);
for (i = 0; i < NUM_WEP_KEYS; i++) {
struct libipw_crypt_data *crypt = info->crypt[i];
if (crypt) {
if (crypt->ops) {
crypt->ops->deinit(crypt->priv);
module_put(crypt->ops->owner);
}
kfree(crypt);
info->crypt[i] = NULL;
}
}
}
EXPORT_SYMBOL(libipw_crypt_info_free);
static void libipw_crypt_deinit_entries(struct libipw_crypt_info *info,
int force)
{
struct libipw_crypt_data *entry, *next;
unsigned long flags;
spin_lock_irqsave(info->lock, flags);
list_for_each_entry_safe(entry, next, &info->crypt_deinit_list, list) {
if (atomic_read(&entry->refcnt) != 0 && !force)
continue;
list_del(&entry->list);
if (entry->ops) {
entry->ops->deinit(entry->priv);
module_put(entry->ops->owner);
}
kfree(entry);
}
spin_unlock_irqrestore(info->lock, flags);
}
/* After this, crypt_deinit_list won't accept new members */
static void libipw_crypt_quiescing(struct libipw_crypt_info *info)
{
unsigned long flags;
spin_lock_irqsave(info->lock, flags);
info->crypt_quiesced = 1;
spin_unlock_irqrestore(info->lock, flags);
}
static void libipw_crypt_deinit_handler(struct timer_list *t)
{
struct libipw_crypt_info *info = timer_container_of(info, t,
crypt_deinit_timer);
unsigned long flags;
libipw_crypt_deinit_entries(info, 0);
spin_lock_irqsave(info->lock, flags);
if (!list_empty(&info->crypt_deinit_list) && !info->crypt_quiesced) {
printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
"deletion list\n", info->name);
info->crypt_deinit_timer.expires = jiffies + HZ;
add_timer(&info->crypt_deinit_timer);
}
spin_unlock_irqrestore(info->lock, flags);
}
void libipw_crypt_delayed_deinit(struct libipw_crypt_info *info,
struct libipw_crypt_data **crypt)
{
struct libipw_crypt_data *tmp;
unsigned long flags;
if (*crypt == NULL)
return;
tmp = *crypt;
*crypt = NULL;
/* must not run ops->deinit() while there may be pending encrypt or
* decrypt operations. Use a list of delayed deinits to avoid needing
* locking. */
spin_lock_irqsave(info->lock, flags);
if (!info->crypt_quiesced) {
list_add(&tmp->list, &info->crypt_deinit_list);
if (!timer_pending(&info->crypt_deinit_timer)) {
info->crypt_deinit_timer.expires = jiffies + HZ;
add_timer(&info->crypt_deinit_timer);
}
}
spin_unlock_irqrestore(info->lock, flags);
}
EXPORT_SYMBOL(libipw_crypt_delayed_deinit);
int libipw_register_crypto_ops(const struct libipw_crypto_ops *ops)
{
unsigned long flags;
struct libipw_crypto_alg *alg;
alg = kzalloc_obj(*alg);
if (alg == NULL)
return -ENOMEM;
alg->ops = ops;
spin_lock_irqsave(&libipw_crypto_lock, flags);
list_add(&alg->list, &libipw_crypto_algs);
spin_unlock_irqrestore(&libipw_crypto_lock, flags);
printk(KERN_DEBUG "libipw_crypt: registered algorithm '%s'\n",
ops->name);
return 0;
}
EXPORT_SYMBOL(libipw_register_crypto_ops);
int libipw_unregister_crypto_ops(const struct libipw_crypto_ops *ops)
{
struct libipw_crypto_alg *alg;
unsigned long flags;
spin_lock_irqsave(&libipw_crypto_lock, flags);
list_for_each_entry(alg, &libipw_crypto_algs, list) {
if (alg->ops == ops)
goto found;
}
spin_unlock_irqrestore(&libipw_crypto_lock, flags);
return -EINVAL;
found:
printk(KERN_DEBUG "libipw_crypt: unregistered algorithm '%s'\n",
ops->name);
list_del(&alg->list);
spin_unlock_irqrestore(&libipw_crypto_lock, flags);
kfree(alg);
return 0;
}
EXPORT_SYMBOL(libipw_unregister_crypto_ops);
const struct libipw_crypto_ops *libipw_get_crypto_ops(const char *name)
{
struct libipw_crypto_alg *alg;
unsigned long flags;
spin_lock_irqsave(&libipw_crypto_lock, flags);
list_for_each_entry(alg, &libipw_crypto_algs, list) {
if (strcmp(alg->ops->name, name) == 0)
goto found;
}
spin_unlock_irqrestore(&libipw_crypto_lock, flags);
return NULL;
found:
spin_unlock_irqrestore(&libipw_crypto_lock, flags);
return alg->ops;
}
EXPORT_SYMBOL(libipw_get_crypto_ops);
static void *libipw_crypt_null_init(int keyidx)
{
return (void *)1;
}
static void libipw_crypt_null_deinit(void *priv)
{
}
static const struct libipw_crypto_ops libipw_crypt_null = {
.name = "NULL",
.init = libipw_crypt_null_init,
.deinit = libipw_crypt_null_deinit,
.owner = THIS_MODULE,
};
int __init libipw_crypto_init(void)
{
return libipw_register_crypto_ops(&libipw_crypt_null);
}
void libipw_crypto_exit(void)
{
libipw_unregister_crypto_ops(&libipw_crypt_null);
BUG_ON(!list_empty(&libipw_crypto_algs));
}
@@ -0,0 +1,438 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* libipw crypt: host-based CCMP encryption implementation for libipw
*
* Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
* Copyright (c) 2008, John W. Linville <linville@tuxdriver.com>
*/
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <asm/string.h>
#include <linux/wireless.h>
#include <linux/ieee80211.h>
#include <linux/crypto.h>
#include <crypto/aead.h>
#include "libipw.h"
#define AES_BLOCK_LEN 16
#define CCMP_HDR_LEN 8
#define CCMP_MIC_LEN 8
#define CCMP_TK_LEN 16
#define CCMP_PN_LEN 6
struct libipw_ccmp_data {
u8 key[CCMP_TK_LEN];
int key_set;
u8 tx_pn[CCMP_PN_LEN];
u8 rx_pn[CCMP_PN_LEN];
u32 dot11RSNAStatsCCMPFormatErrors;
u32 dot11RSNAStatsCCMPReplays;
u32 dot11RSNAStatsCCMPDecryptErrors;
int key_idx;
struct crypto_aead *tfm;
/* scratch buffers for virt_to_page() (crypto API) */
u8 tx_aad[2 * AES_BLOCK_LEN];
u8 rx_aad[2 * AES_BLOCK_LEN];
};
static void *libipw_ccmp_init(int key_idx)
{
struct libipw_ccmp_data *priv;
priv = kzalloc_obj(*priv, GFP_ATOMIC);
if (priv == NULL)
goto fail;
priv->key_idx = key_idx;
priv->tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(priv->tfm)) {
priv->tfm = NULL;
goto fail;
}
return priv;
fail:
if (priv) {
if (priv->tfm)
crypto_free_aead(priv->tfm);
kfree(priv);
}
return NULL;
}
static void libipw_ccmp_deinit(void *priv)
{
struct libipw_ccmp_data *_priv = priv;
if (_priv && _priv->tfm)
crypto_free_aead(_priv->tfm);
kfree(priv);
}
static int ccmp_init_iv_and_aad(const struct ieee80211_hdr *hdr,
const u8 *pn, u8 *iv, u8 *aad)
{
u8 *pos, qc = 0;
size_t aad_len;
int a4_included, qc_included;
a4_included = ieee80211_has_a4(hdr->frame_control);
qc_included = ieee80211_is_data_qos(hdr->frame_control);
aad_len = 22;
if (a4_included)
aad_len += 6;
if (qc_included) {
pos = (u8 *) & hdr->addr4;
if (a4_included)
pos += 6;
qc = *pos & 0x0f;
aad_len += 2;
}
/* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC
* mode authentication are not allowed to collide, yet both are derived
* from the same vector. We only set L := 1 here to indicate that the
* data size can be represented in (L+1) bytes. The CCM layer will take
* care of storing the data length in the top (L+1) bytes and setting
* and clearing the other bits as is required to derive the two IVs.
*/
iv[0] = 0x1;
/* Nonce: QC | A2 | PN */
iv[1] = qc;
memcpy(iv + 2, hdr->addr2, ETH_ALEN);
memcpy(iv + 8, pn, CCMP_PN_LEN);
/* AAD:
* FC with bits 4..6 and 11..13 masked to zero; 14 is always one
* A1 | A2 | A3
* SC with bits 4..15 (seq#) masked to zero
* A4 (if present)
* QC (if present)
*/
pos = (u8 *) hdr;
aad[0] = pos[0] & 0x8f;
aad[1] = pos[1] & 0xc7;
memcpy(aad + 2, &hdr->addrs, 3 * ETH_ALEN);
pos = (u8 *) & hdr->seq_ctrl;
aad[20] = pos[0] & 0x0f;
aad[21] = 0; /* all bits masked */
memset(aad + 22, 0, 8);
if (a4_included)
memcpy(aad + 22, hdr->addr4, ETH_ALEN);
if (qc_included) {
aad[a4_included ? 28 : 22] = qc;
/* rest of QC masked */
}
return aad_len;
}
static int libipw_ccmp_hdr(struct sk_buff *skb, int hdr_len,
u8 *aeskey, int keylen, void *priv)
{
struct libipw_ccmp_data *key = priv;
int i;
u8 *pos;
if (skb_headroom(skb) < CCMP_HDR_LEN || skb->len < hdr_len)
return -1;
if (aeskey != NULL && keylen >= CCMP_TK_LEN)
memcpy(aeskey, key->key, CCMP_TK_LEN);
pos = skb_push(skb, CCMP_HDR_LEN);
memmove(pos, pos + CCMP_HDR_LEN, hdr_len);
pos += hdr_len;
i = CCMP_PN_LEN - 1;
while (i >= 0) {
key->tx_pn[i]++;
if (key->tx_pn[i] != 0)
break;
i--;
}
*pos++ = key->tx_pn[5];
*pos++ = key->tx_pn[4];
*pos++ = 0;
*pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */ ;
*pos++ = key->tx_pn[3];
*pos++ = key->tx_pn[2];
*pos++ = key->tx_pn[1];
*pos++ = key->tx_pn[0];
return CCMP_HDR_LEN;
}
static int libipw_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
{
struct libipw_ccmp_data *key = priv;
struct ieee80211_hdr *hdr;
struct aead_request *req;
struct scatterlist sg[2];
u8 *aad = key->tx_aad;
u8 iv[AES_BLOCK_LEN];
int len, data_len, aad_len;
int ret;
if (skb_tailroom(skb) < CCMP_MIC_LEN || skb->len < hdr_len)
return -1;
data_len = skb->len - hdr_len;
len = libipw_ccmp_hdr(skb, hdr_len, NULL, 0, priv);
if (len < 0)
return -1;
req = aead_request_alloc(key->tfm, GFP_ATOMIC);
if (!req)
return -ENOMEM;
hdr = (struct ieee80211_hdr *)skb->data;
aad_len = ccmp_init_iv_and_aad(hdr, key->tx_pn, iv, aad);
skb_put(skb, CCMP_MIC_LEN);
sg_init_table(sg, 2);
sg_set_buf(&sg[0], aad, aad_len);
sg_set_buf(&sg[1], skb->data + hdr_len + CCMP_HDR_LEN,
data_len + CCMP_MIC_LEN);
aead_request_set_callback(req, 0, NULL, NULL);
aead_request_set_ad(req, aad_len);
aead_request_set_crypt(req, sg, sg, data_len, iv);
ret = crypto_aead_encrypt(req);
aead_request_free(req);
return ret;
}
/*
* deal with seq counter wrapping correctly.
* refer to timer_after() for jiffies wrapping handling
*/
static inline int ccmp_replay_check(u8 *pn_n, u8 *pn_o)
{
u32 iv32_n, iv16_n;
u32 iv32_o, iv16_o;
iv32_n = (pn_n[0] << 24) | (pn_n[1] << 16) | (pn_n[2] << 8) | pn_n[3];
iv16_n = (pn_n[4] << 8) | pn_n[5];
iv32_o = (pn_o[0] << 24) | (pn_o[1] << 16) | (pn_o[2] << 8) | pn_o[3];
iv16_o = (pn_o[4] << 8) | pn_o[5];
if ((s32)iv32_n - (s32)iv32_o < 0 ||
(iv32_n == iv32_o && iv16_n <= iv16_o))
return 1;
return 0;
}
static int libipw_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
{
struct libipw_ccmp_data *key = priv;
u8 keyidx, *pos;
struct ieee80211_hdr *hdr;
struct aead_request *req;
struct scatterlist sg[2];
u8 *aad = key->rx_aad;
u8 iv[AES_BLOCK_LEN];
u8 pn[6];
int aad_len, ret;
size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN;
if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) {
key->dot11RSNAStatsCCMPFormatErrors++;
return -1;
}
hdr = (struct ieee80211_hdr *)skb->data;
pos = skb->data + hdr_len;
keyidx = pos[3];
if (!(keyidx & (1 << 5))) {
net_dbg_ratelimited("CCMP: received packet without ExtIV flag from %pM\n",
hdr->addr2);
key->dot11RSNAStatsCCMPFormatErrors++;
return -2;
}
keyidx >>= 6;
if (key->key_idx != keyidx) {
net_dbg_ratelimited("CCMP: RX tkey->key_idx=%d frame keyidx=%d\n",
key->key_idx, keyidx);
return -6;
}
if (!key->key_set) {
net_dbg_ratelimited("CCMP: received packet from %pM with keyid=%d that does not have a configured key\n",
hdr->addr2, keyidx);
return -3;
}
pn[0] = pos[7];
pn[1] = pos[6];
pn[2] = pos[5];
pn[3] = pos[4];
pn[4] = pos[1];
pn[5] = pos[0];
pos += 8;
if (ccmp_replay_check(pn, key->rx_pn)) {
#ifdef CONFIG_LIBIPW_DEBUG
net_dbg_ratelimited("CCMP: replay detected: STA=%pM previous PN %02x%02x%02x%02x%02x%02x received PN %02x%02x%02x%02x%02x%02x\n",
hdr->addr2,
key->rx_pn[0], key->rx_pn[1], key->rx_pn[2],
key->rx_pn[3], key->rx_pn[4], key->rx_pn[5],
pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]);
#endif
key->dot11RSNAStatsCCMPReplays++;
return -4;
}
req = aead_request_alloc(key->tfm, GFP_ATOMIC);
if (!req)
return -ENOMEM;
aad_len = ccmp_init_iv_and_aad(hdr, pn, iv, aad);
sg_init_table(sg, 2);
sg_set_buf(&sg[0], aad, aad_len);
sg_set_buf(&sg[1], pos, data_len);
aead_request_set_callback(req, 0, NULL, NULL);
aead_request_set_ad(req, aad_len);
aead_request_set_crypt(req, sg, sg, data_len, iv);
ret = crypto_aead_decrypt(req);
aead_request_free(req);
if (ret) {
net_dbg_ratelimited("CCMP: decrypt failed: STA=%pM (%d)\n",
hdr->addr2, ret);
key->dot11RSNAStatsCCMPDecryptErrors++;
return -5;
}
memcpy(key->rx_pn, pn, CCMP_PN_LEN);
/* Remove hdr and MIC */
memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len);
skb_pull(skb, CCMP_HDR_LEN);
skb_trim(skb, skb->len - CCMP_MIC_LEN);
return keyidx;
}
static int libipw_ccmp_set_key(void *key, int len, u8 * seq, void *priv)
{
struct libipw_ccmp_data *data = priv;
int keyidx;
struct crypto_aead *tfm = data->tfm;
keyidx = data->key_idx;
memset(data, 0, sizeof(*data));
data->key_idx = keyidx;
data->tfm = tfm;
if (len == CCMP_TK_LEN) {
memcpy(data->key, key, CCMP_TK_LEN);
data->key_set = 1;
if (seq) {
data->rx_pn[0] = seq[5];
data->rx_pn[1] = seq[4];
data->rx_pn[2] = seq[3];
data->rx_pn[3] = seq[2];
data->rx_pn[4] = seq[1];
data->rx_pn[5] = seq[0];
}
if (crypto_aead_setauthsize(data->tfm, CCMP_MIC_LEN) ||
crypto_aead_setkey(data->tfm, data->key, CCMP_TK_LEN))
return -1;
} else if (len == 0)
data->key_set = 0;
else
return -1;
return 0;
}
static int libipw_ccmp_get_key(void *key, int len, u8 * seq, void *priv)
{
struct libipw_ccmp_data *data = priv;
if (len < CCMP_TK_LEN)
return -1;
if (!data->key_set)
return 0;
memcpy(key, data->key, CCMP_TK_LEN);
if (seq) {
seq[0] = data->tx_pn[5];
seq[1] = data->tx_pn[4];
seq[2] = data->tx_pn[3];
seq[3] = data->tx_pn[2];
seq[4] = data->tx_pn[1];
seq[5] = data->tx_pn[0];
}
return CCMP_TK_LEN;
}
static void libipw_ccmp_print_stats(struct seq_file *m, void *priv)
{
struct libipw_ccmp_data *ccmp = priv;
seq_printf(m,
"key[%d] alg=CCMP key_set=%d "
"tx_pn=%02x%02x%02x%02x%02x%02x "
"rx_pn=%02x%02x%02x%02x%02x%02x "
"format_errors=%d replays=%d decrypt_errors=%d\n",
ccmp->key_idx, ccmp->key_set,
ccmp->tx_pn[0], ccmp->tx_pn[1], ccmp->tx_pn[2],
ccmp->tx_pn[3], ccmp->tx_pn[4], ccmp->tx_pn[5],
ccmp->rx_pn[0], ccmp->rx_pn[1], ccmp->rx_pn[2],
ccmp->rx_pn[3], ccmp->rx_pn[4], ccmp->rx_pn[5],
ccmp->dot11RSNAStatsCCMPFormatErrors,
ccmp->dot11RSNAStatsCCMPReplays,
ccmp->dot11RSNAStatsCCMPDecryptErrors);
}
static const struct libipw_crypto_ops libipw_crypt_ccmp = {
.name = "CCMP",
.init = libipw_ccmp_init,
.deinit = libipw_ccmp_deinit,
.encrypt_mpdu = libipw_ccmp_encrypt,
.decrypt_mpdu = libipw_ccmp_decrypt,
.encrypt_msdu = NULL,
.decrypt_msdu = NULL,
.set_key = libipw_ccmp_set_key,
.get_key = libipw_ccmp_get_key,
.print_stats = libipw_ccmp_print_stats,
.extra_mpdu_prefix_len = CCMP_HDR_LEN,
.extra_mpdu_postfix_len = CCMP_MIC_LEN,
.owner = THIS_MODULE,
};
int __init libipw_crypto_ccmp_init(void)
{
return libipw_register_crypto_ops(&libipw_crypt_ccmp);
}
void libipw_crypto_ccmp_exit(void)
{
libipw_unregister_crypto_ops(&libipw_crypt_ccmp);
}
@@ -0,0 +1,618 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* libipw crypt: host-based TKIP encryption implementation for libipw
*
* Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
* Copyright (c) 2008, John W. Linville <linville@tuxdriver.com>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/err.h>
#include <linux/fips.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/scatterlist.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/mm.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <asm/string.h>
#include <linux/wireless.h>
#include <linux/ieee80211.h>
#include <net/iw_handler.h>
#include <crypto/arc4.h>
#include <linux/crc32.h>
#include "libipw.h"
#define TKIP_HDR_LEN 8
struct libipw_tkip_data {
#define TKIP_KEY_LEN 32
u8 key[TKIP_KEY_LEN];
int key_set;
u32 tx_iv32;
u16 tx_iv16;
u16 tx_ttak[5];
int tx_phase1_done;
u32 rx_iv32;
u16 rx_iv16;
u16 rx_ttak[5];
int rx_phase1_done;
u32 rx_iv32_new;
u16 rx_iv16_new;
u32 dot11RSNAStatsTKIPReplays;
u32 dot11RSNAStatsTKIPICVErrors;
u32 dot11RSNAStatsTKIPLocalMICFailures;
int key_idx;
struct arc4_ctx rx_ctx_arc4;
struct arc4_ctx tx_ctx_arc4;
unsigned long flags;
};
static unsigned long libipw_tkip_set_flags(unsigned long flags, void *priv)
{
struct libipw_tkip_data *_priv = priv;
unsigned long old_flags = _priv->flags;
_priv->flags = flags;
return old_flags;
}
static unsigned long libipw_tkip_get_flags(void *priv)
{
struct libipw_tkip_data *_priv = priv;
return _priv->flags;
}
static void *libipw_tkip_init(int key_idx)
{
struct libipw_tkip_data *priv;
if (fips_enabled)
return NULL;
priv = kzalloc_obj(*priv, GFP_ATOMIC);
if (priv == NULL)
return priv;
priv->key_idx = key_idx;
return priv;
}
static void libipw_tkip_deinit(void *priv)
{
kfree_sensitive(priv);
}
static inline u16 RotR1(u16 val)
{
return (val >> 1) | (val << 15);
}
static inline u8 Lo8(u16 val)
{
return val & 0xff;
}
static inline u8 Hi8(u16 val)
{
return val >> 8;
}
static inline u16 Lo16(u32 val)
{
return val & 0xffff;
}
static inline u16 Hi16(u32 val)
{
return val >> 16;
}
static inline u16 Mk16(u8 hi, u8 lo)
{
return lo | (((u16) hi) << 8);
}
static inline u16 Mk16_le(__le16 * v)
{
return le16_to_cpu(*v);
}
static const u16 Sbox[256] = {
0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
};
static inline u16 _S_(u16 v)
{
u16 t = Sbox[Hi8(v)];
return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
}
#define PHASE1_LOOP_COUNT 8
static void tkip_mixing_phase1(u16 * TTAK, const u8 * TK, const u8 * TA,
u32 IV32)
{
int i, j;
/* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
TTAK[0] = Lo16(IV32);
TTAK[1] = Hi16(IV32);
TTAK[2] = Mk16(TA[1], TA[0]);
TTAK[3] = Mk16(TA[3], TA[2]);
TTAK[4] = Mk16(TA[5], TA[4]);
for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
j = 2 * (i & 1);
TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
}
}
static void tkip_mixing_phase2(u8 * WEPSeed, const u8 * TK, const u16 * TTAK,
u16 IV16)
{
/* Make temporary area overlap WEP seed so that the final copy can be
* avoided on little endian hosts. */
u16 *PPK = (u16 *) & WEPSeed[4];
/* Step 1 - make copy of TTAK and bring in TSC */
PPK[0] = TTAK[0];
PPK[1] = TTAK[1];
PPK[2] = TTAK[2];
PPK[3] = TTAK[3];
PPK[4] = TTAK[4];
PPK[5] = TTAK[4] + IV16;
/* Step 2 - 96-bit bijective mixing using S-box */
PPK[0] += _S_(PPK[5] ^ Mk16_le((__le16 *) & TK[0]));
PPK[1] += _S_(PPK[0] ^ Mk16_le((__le16 *) & TK[2]));
PPK[2] += _S_(PPK[1] ^ Mk16_le((__le16 *) & TK[4]));
PPK[3] += _S_(PPK[2] ^ Mk16_le((__le16 *) & TK[6]));
PPK[4] += _S_(PPK[3] ^ Mk16_le((__le16 *) & TK[8]));
PPK[5] += _S_(PPK[4] ^ Mk16_le((__le16 *) & TK[10]));
PPK[0] += RotR1(PPK[5] ^ Mk16_le((__le16 *) & TK[12]));
PPK[1] += RotR1(PPK[0] ^ Mk16_le((__le16 *) & TK[14]));
PPK[2] += RotR1(PPK[1]);
PPK[3] += RotR1(PPK[2]);
PPK[4] += RotR1(PPK[3]);
PPK[5] += RotR1(PPK[4]);
/* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
* WEPSeed[0..2] is transmitted as WEP IV */
WEPSeed[0] = Hi8(IV16);
WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
WEPSeed[2] = Lo8(IV16);
WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((__le16 *) & TK[0])) >> 1);
#ifdef __BIG_ENDIAN
{
int i;
for (i = 0; i < 6; i++)
PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8);
}
#endif
}
static int libipw_tkip_hdr(struct sk_buff *skb, int hdr_len,
u8 * rc4key, int keylen, void *priv)
{
struct libipw_tkip_data *tkey = priv;
u8 *pos;
struct ieee80211_hdr *hdr;
hdr = (struct ieee80211_hdr *)skb->data;
if (skb_headroom(skb) < TKIP_HDR_LEN || skb->len < hdr_len)
return -1;
if (rc4key == NULL || keylen < 16)
return -1;
if (!tkey->tx_phase1_done) {
tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2,
tkey->tx_iv32);
tkey->tx_phase1_done = 1;
}
tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16);
pos = skb_push(skb, TKIP_HDR_LEN);
memmove(pos, pos + TKIP_HDR_LEN, hdr_len);
pos += hdr_len;
*pos++ = *rc4key;
*pos++ = *(rc4key + 1);
*pos++ = *(rc4key + 2);
*pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */ ;
*pos++ = tkey->tx_iv32 & 0xff;
*pos++ = (tkey->tx_iv32 >> 8) & 0xff;
*pos++ = (tkey->tx_iv32 >> 16) & 0xff;
*pos++ = (tkey->tx_iv32 >> 24) & 0xff;
tkey->tx_iv16++;
if (tkey->tx_iv16 == 0) {
tkey->tx_phase1_done = 0;
tkey->tx_iv32++;
}
return TKIP_HDR_LEN;
}
static int libipw_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
{
struct libipw_tkip_data *tkey = priv;
int len;
u8 rc4key[16], *pos, *icv;
u32 crc;
if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
net_dbg_ratelimited("TKIP countermeasures: dropped TX packet to %pM\n",
hdr->addr1);
return -1;
}
if (skb_tailroom(skb) < 4 || skb->len < hdr_len)
return -1;
len = skb->len - hdr_len;
pos = skb->data + hdr_len;
if ((libipw_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0)
return -1;
crc = ~crc32_le(~0, pos, len);
icv = skb_put(skb, 4);
icv[0] = crc;
icv[1] = crc >> 8;
icv[2] = crc >> 16;
icv[3] = crc >> 24;
arc4_setkey(&tkey->tx_ctx_arc4, rc4key, 16);
arc4_crypt(&tkey->tx_ctx_arc4, pos, pos, len + 4);
return 0;
}
/*
* deal with seq counter wrapping correctly.
* refer to timer_after() for jiffies wrapping handling
*/
static inline int tkip_replay_check(u32 iv32_n, u16 iv16_n,
u32 iv32_o, u16 iv16_o)
{
if ((s32)iv32_n - (s32)iv32_o < 0 ||
(iv32_n == iv32_o && iv16_n <= iv16_o))
return 1;
return 0;
}
static int libipw_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
{
struct libipw_tkip_data *tkey = priv;
u8 rc4key[16];
u8 keyidx, *pos;
u32 iv32;
u16 iv16;
struct ieee80211_hdr *hdr;
u8 icv[4];
u32 crc;
int plen;
hdr = (struct ieee80211_hdr *)skb->data;
if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
net_dbg_ratelimited("TKIP countermeasures: dropped received packet from %pM\n",
hdr->addr2);
return -1;
}
if (skb->len < hdr_len + TKIP_HDR_LEN + 4)
return -1;
pos = skb->data + hdr_len;
keyidx = pos[3];
if (!(keyidx & (1 << 5))) {
net_dbg_ratelimited("TKIP: received packet without ExtIV flag from %pM\n",
hdr->addr2);
return -2;
}
keyidx >>= 6;
if (tkey->key_idx != keyidx) {
net_dbg_ratelimited("TKIP: RX tkey->key_idx=%d frame keyidx=%d\n",
tkey->key_idx, keyidx);
return -6;
}
if (!tkey->key_set) {
net_dbg_ratelimited("TKIP: received packet from %pM with keyid=%d that does not have a configured key\n",
hdr->addr2, keyidx);
return -3;
}
iv16 = (pos[0] << 8) | pos[2];
iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24);
pos += TKIP_HDR_LEN;
if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) {
#ifdef CONFIG_LIBIPW_DEBUG
net_dbg_ratelimited("TKIP: replay detected: STA=%pM previous TSC %08x%04x received TSC %08x%04x\n",
hdr->addr2, tkey->rx_iv32, tkey->rx_iv16,
iv32, iv16);
#endif
tkey->dot11RSNAStatsTKIPReplays++;
return -4;
}
if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) {
tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32);
tkey->rx_phase1_done = 1;
}
tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16);
plen = skb->len - hdr_len - 12;
arc4_setkey(&tkey->rx_ctx_arc4, rc4key, 16);
arc4_crypt(&tkey->rx_ctx_arc4, pos, pos, plen + 4);
crc = ~crc32_le(~0, pos, plen);
icv[0] = crc;
icv[1] = crc >> 8;
icv[2] = crc >> 16;
icv[3] = crc >> 24;
if (memcmp(icv, pos + plen, 4) != 0) {
if (iv32 != tkey->rx_iv32) {
/* Previously cached Phase1 result was already lost, so
* it needs to be recalculated for the next packet. */
tkey->rx_phase1_done = 0;
}
#ifdef CONFIG_LIBIPW_DEBUG
net_dbg_ratelimited("TKIP: ICV error detected: STA=%pM\n",
hdr->addr2);
#endif
tkey->dot11RSNAStatsTKIPICVErrors++;
return -5;
}
/* Update real counters only after Michael MIC verification has
* completed */
tkey->rx_iv32_new = iv32;
tkey->rx_iv16_new = iv16;
/* Remove IV and ICV */
memmove(skb->data + TKIP_HDR_LEN, skb->data, hdr_len);
skb_pull(skb, TKIP_HDR_LEN);
skb_trim(skb, skb->len - 4);
return keyidx;
}
static int libipw_michael_mic_add(struct sk_buff *skb, int hdr_len,
void *priv)
{
struct libipw_tkip_data *tkey = priv;
u8 *pos;
if (skb_tailroom(skb) < 8 || skb->len < hdr_len) {
printk(KERN_DEBUG "Invalid packet for Michael MIC add "
"(tailroom=%d hdr_len=%d skb->len=%d)\n",
skb_tailroom(skb), hdr_len, skb->len);
return -1;
}
pos = skb_put(skb, 8);
michael_mic(&tkey->key[16], (struct ieee80211_hdr *)skb->data,
skb->data + hdr_len, skb->len - 8 - hdr_len, pos);
return 0;
}
static void libipw_michael_mic_failure(struct net_device *dev,
struct ieee80211_hdr *hdr,
int keyidx)
{
union iwreq_data wrqu;
struct iw_michaelmicfailure ev;
/* TODO: needed parameters: count, keyid, key type, TSC */
memset(&ev, 0, sizeof(ev));
ev.flags = keyidx & IW_MICFAILURE_KEY_ID;
if (hdr->addr1[0] & 0x01)
ev.flags |= IW_MICFAILURE_GROUP;
else
ev.flags |= IW_MICFAILURE_PAIRWISE;
ev.src_addr.sa_family = ARPHRD_ETHER;
memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN);
memset(&wrqu, 0, sizeof(wrqu));
wrqu.data.length = sizeof(ev);
wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *)&ev);
}
static int libipw_michael_mic_verify(struct sk_buff *skb, int keyidx,
int hdr_len, void *priv)
{
struct libipw_tkip_data *tkey = priv;
u8 mic[8];
if (!tkey->key_set)
return -1;
michael_mic(&tkey->key[24], (struct ieee80211_hdr *)skb->data,
skb->data + hdr_len, skb->len - 8 - hdr_len, mic);
if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) {
struct ieee80211_hdr *hdr;
hdr = (struct ieee80211_hdr *)skb->data;
printk(KERN_DEBUG "%s: Michael MIC verification failed for "
"MSDU from %pM keyidx=%d\n",
skb->dev ? skb->dev->name : "N/A", hdr->addr2,
keyidx);
if (skb->dev)
libipw_michael_mic_failure(skb->dev, hdr, keyidx);
tkey->dot11RSNAStatsTKIPLocalMICFailures++;
return -1;
}
/* Update TSC counters for RX now that the packet verification has
* completed. */
tkey->rx_iv32 = tkey->rx_iv32_new;
tkey->rx_iv16 = tkey->rx_iv16_new;
skb_trim(skb, skb->len - 8);
return 0;
}
static int libipw_tkip_set_key(void *key, int len, u8 * seq, void *priv)
{
struct libipw_tkip_data *tkey = priv;
int keyidx;
struct arc4_ctx *tfm2 = &tkey->tx_ctx_arc4;
struct arc4_ctx *tfm4 = &tkey->rx_ctx_arc4;
keyidx = tkey->key_idx;
memset(tkey, 0, sizeof(*tkey));
tkey->key_idx = keyidx;
tkey->tx_ctx_arc4 = *tfm2;
tkey->rx_ctx_arc4 = *tfm4;
if (len == TKIP_KEY_LEN) {
memcpy(tkey->key, key, TKIP_KEY_LEN);
tkey->key_set = 1;
tkey->tx_iv16 = 1; /* TSC is initialized to 1 */
if (seq) {
tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) |
(seq[3] << 8) | seq[2];
tkey->rx_iv16 = (seq[1] << 8) | seq[0];
}
} else if (len == 0)
tkey->key_set = 0;
else
return -1;
return 0;
}
static int libipw_tkip_get_key(void *key, int len, u8 * seq, void *priv)
{
struct libipw_tkip_data *tkey = priv;
if (len < TKIP_KEY_LEN)
return -1;
if (!tkey->key_set)
return 0;
memcpy(key, tkey->key, TKIP_KEY_LEN);
if (seq) {
/*
* Not clear if this should return the value as is
* or - as the code previously seemed to partially
* have been written as - subtract one from it. It
* was working this way for a long time so leave it.
*/
seq[0] = tkey->tx_iv16;
seq[1] = tkey->tx_iv16 >> 8;
seq[2] = tkey->tx_iv32;
seq[3] = tkey->tx_iv32 >> 8;
seq[4] = tkey->tx_iv32 >> 16;
seq[5] = tkey->tx_iv32 >> 24;
}
return TKIP_KEY_LEN;
}
static void libipw_tkip_print_stats(struct seq_file *m, void *priv)
{
struct libipw_tkip_data *tkip = priv;
seq_printf(m,
"key[%d] alg=TKIP key_set=%d "
"tx_pn=%02x%02x%02x%02x%02x%02x "
"rx_pn=%02x%02x%02x%02x%02x%02x "
"replays=%d icv_errors=%d local_mic_failures=%d\n",
tkip->key_idx, tkip->key_set,
(tkip->tx_iv32 >> 24) & 0xff,
(tkip->tx_iv32 >> 16) & 0xff,
(tkip->tx_iv32 >> 8) & 0xff,
tkip->tx_iv32 & 0xff,
(tkip->tx_iv16 >> 8) & 0xff,
tkip->tx_iv16 & 0xff,
(tkip->rx_iv32 >> 24) & 0xff,
(tkip->rx_iv32 >> 16) & 0xff,
(tkip->rx_iv32 >> 8) & 0xff,
tkip->rx_iv32 & 0xff,
(tkip->rx_iv16 >> 8) & 0xff,
tkip->rx_iv16 & 0xff,
tkip->dot11RSNAStatsTKIPReplays,
tkip->dot11RSNAStatsTKIPICVErrors,
tkip->dot11RSNAStatsTKIPLocalMICFailures);
}
static const struct libipw_crypto_ops libipw_crypt_tkip = {
.name = "TKIP",
.init = libipw_tkip_init,
.deinit = libipw_tkip_deinit,
.encrypt_mpdu = libipw_tkip_encrypt,
.decrypt_mpdu = libipw_tkip_decrypt,
.encrypt_msdu = libipw_michael_mic_add,
.decrypt_msdu = libipw_michael_mic_verify,
.set_key = libipw_tkip_set_key,
.get_key = libipw_tkip_get_key,
.print_stats = libipw_tkip_print_stats,
.extra_mpdu_prefix_len = 4 + 4, /* IV + ExtIV */
.extra_mpdu_postfix_len = 4, /* ICV */
.extra_msdu_postfix_len = 8, /* MIC */
.get_flags = libipw_tkip_get_flags,
.set_flags = libipw_tkip_set_flags,
.owner = THIS_MODULE,
};
int __init libipw_crypto_tkip_init(void)
{
return libipw_register_crypto_ops(&libipw_crypt_tkip);
}
void libipw_crypto_tkip_exit(void)
{
libipw_unregister_crypto_ops(&libipw_crypt_tkip);
}
@@ -0,0 +1,247 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* libipw crypt: host-based WEP encryption implementation for libipw
*
* Copyright (c) 2002-2004, Jouni Malinen <j@w1.fi>
* Copyright (c) 2008, John W. Linville <linville@tuxdriver.com>
*/
#include <linux/err.h>
#include <linux/fips.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/scatterlist.h>
#include <linux/skbuff.h>
#include <linux/mm.h>
#include <asm/string.h>
#include <crypto/arc4.h>
#include <linux/crc32.h>
#include "libipw.h"
struct libipw_wep_data {
u32 iv;
#define WEP_KEY_LEN 13
u8 key[WEP_KEY_LEN + 1];
u8 key_len;
u8 key_idx;
struct arc4_ctx tx_ctx;
struct arc4_ctx rx_ctx;
};
static void *libipw_wep_init(int keyidx)
{
struct libipw_wep_data *priv;
if (fips_enabled)
return NULL;
priv = kzalloc_obj(*priv, GFP_ATOMIC);
if (priv == NULL)
return NULL;
priv->key_idx = keyidx;
/* start WEP IV from a random value */
get_random_bytes(&priv->iv, 4);
return priv;
}
static void libipw_wep_deinit(void *priv)
{
kfree_sensitive(priv);
}
/* Add WEP IV/key info to a frame that has at least 4 bytes of headroom */
static int libipw_wep_build_iv(struct sk_buff *skb, int hdr_len,
u8 *key, int keylen, void *priv)
{
struct libipw_wep_data *wep = priv;
u32 klen;
u8 *pos;
if (skb_headroom(skb) < 4 || skb->len < hdr_len)
return -1;
pos = skb_push(skb, 4);
memmove(pos, pos + 4, hdr_len);
pos += hdr_len;
klen = 3 + wep->key_len;
wep->iv++;
/* Fluhrer, Mantin, and Shamir have reported weaknesses in the key
* scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N)
* can be used to speedup attacks, so avoid using them. */
if ((wep->iv & 0xff00) == 0xff00) {
u8 B = (wep->iv >> 16) & 0xff;
if (B >= 3 && B < klen)
wep->iv += 0x0100;
}
/* Prepend 24-bit IV to RC4 key and TX frame */
*pos++ = (wep->iv >> 16) & 0xff;
*pos++ = (wep->iv >> 8) & 0xff;
*pos++ = wep->iv & 0xff;
*pos++ = wep->key_idx << 6;
return 0;
}
/* Perform WEP encryption on given skb that has at least 4 bytes of headroom
* for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted,
* so the payload length increases with 8 bytes.
*
* WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data))
*/
static int libipw_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
{
struct libipw_wep_data *wep = priv;
u32 crc, klen, len;
u8 *pos, *icv;
u8 key[WEP_KEY_LEN + 3];
/* other checks are in libipw_wep_build_iv */
if (skb_tailroom(skb) < 4)
return -1;
/* add the IV to the frame */
if (libipw_wep_build_iv(skb, hdr_len, NULL, 0, priv))
return -1;
/* Copy the IV into the first 3 bytes of the key */
skb_copy_from_linear_data_offset(skb, hdr_len, key, 3);
/* Copy rest of the WEP key (the secret part) */
memcpy(key + 3, wep->key, wep->key_len);
len = skb->len - hdr_len - 4;
pos = skb->data + hdr_len + 4;
klen = 3 + wep->key_len;
/* Append little-endian CRC32 over only the data and encrypt it to produce ICV */
crc = ~crc32_le(~0, pos, len);
icv = skb_put(skb, 4);
icv[0] = crc;
icv[1] = crc >> 8;
icv[2] = crc >> 16;
icv[3] = crc >> 24;
arc4_setkey(&wep->tx_ctx, key, klen);
arc4_crypt(&wep->tx_ctx, pos, pos, len + 4);
return 0;
}
/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of
* the frame: IV (4 bytes), encrypted payload (including SNAP header),
* ICV (4 bytes). len includes both IV and ICV.
*
* Returns 0 if frame was decrypted successfully and ICV was correct and -1 on
* failure. If frame is OK, IV and ICV will be removed.
*/
static int libipw_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
{
struct libipw_wep_data *wep = priv;
u32 crc, klen, plen;
u8 key[WEP_KEY_LEN + 3];
u8 keyidx, *pos, icv[4];
if (skb->len < hdr_len + 8)
return -1;
pos = skb->data + hdr_len;
key[0] = *pos++;
key[1] = *pos++;
key[2] = *pos++;
keyidx = *pos++ >> 6;
if (keyidx != wep->key_idx)
return -1;
klen = 3 + wep->key_len;
/* Copy rest of the WEP key (the secret part) */
memcpy(key + 3, wep->key, wep->key_len);
/* Apply RC4 to data and compute CRC32 over decrypted data */
plen = skb->len - hdr_len - 8;
arc4_setkey(&wep->rx_ctx, key, klen);
arc4_crypt(&wep->rx_ctx, pos, pos, plen + 4);
crc = ~crc32_le(~0, pos, plen);
icv[0] = crc;
icv[1] = crc >> 8;
icv[2] = crc >> 16;
icv[3] = crc >> 24;
if (memcmp(icv, pos + plen, 4) != 0) {
/* ICV mismatch - drop frame */
return -2;
}
/* Remove IV and ICV */
memmove(skb->data + 4, skb->data, hdr_len);
skb_pull(skb, 4);
skb_trim(skb, skb->len - 4);
return 0;
}
static int libipw_wep_set_key(void *key, int len, u8 * seq, void *priv)
{
struct libipw_wep_data *wep = priv;
if (len < 0 || len > WEP_KEY_LEN)
return -1;
memcpy(wep->key, key, len);
wep->key_len = len;
return 0;
}
static int libipw_wep_get_key(void *key, int len, u8 * seq, void *priv)
{
struct libipw_wep_data *wep = priv;
if (len < wep->key_len)
return -1;
memcpy(key, wep->key, wep->key_len);
return wep->key_len;
}
static void libipw_wep_print_stats(struct seq_file *m, void *priv)
{
struct libipw_wep_data *wep = priv;
seq_printf(m, "key[%d] alg=WEP len=%d\n", wep->key_idx, wep->key_len);
}
static const struct libipw_crypto_ops libipw_crypt_wep = {
.name = "WEP",
.init = libipw_wep_init,
.deinit = libipw_wep_deinit,
.encrypt_mpdu = libipw_wep_encrypt,
.decrypt_mpdu = libipw_wep_decrypt,
.encrypt_msdu = NULL,
.decrypt_msdu = NULL,
.set_key = libipw_wep_set_key,
.get_key = libipw_wep_get_key,
.print_stats = libipw_wep_print_stats,
.extra_mpdu_prefix_len = 4, /* IV */
.extra_mpdu_postfix_len = 4, /* ICV */
.owner = THIS_MODULE,
};
int __init libipw_crypto_wep_init(void)
{
return libipw_register_crypto_ops(&libipw_crypt_wep);
}
void __exit libipw_crypto_wep_exit(void)
{
libipw_unregister_crypto_ops(&libipw_crypt_wep);
}
@@ -0,0 +1,179 @@
// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
Copyright(c) 2005 Intel Corporation. All rights reserved.
Contact Information:
Intel Linux Wireless <ilw@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/tcp.h>
#include <linux/types.h>
#include <linux/wireless.h>
#include <linux/etherdevice.h>
#include <linux/uaccess.h>
#include "libipw.h"
int libipw_is_valid_channel(struct libipw_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 & LIBIPW_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 & LIBIPW_CH_INVALID) &&
(!(ieee->mode & IEEE_G) ||
!(ieee->geo.bg[i].flags & LIBIPW_CH_B_ONLY)))
return LIBIPW_24GHZ_BAND;
if (ieee->freq_band & LIBIPW_52GHZ_BAND)
for (i = 0; i < ieee->geo.a_channels; i++)
if ((ieee->geo.a[i].channel == channel) &&
!(ieee->geo.a[i].flags & LIBIPW_CH_INVALID))
return LIBIPW_52GHZ_BAND;
return 0;
}
int libipw_channel_to_index(struct libipw_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 & LIBIPW_24GHZ_BAND)
for (i = 0; i < ieee->geo.bg_channels; i++)
if (ieee->geo.bg[i].channel == channel)
return i;
if (ieee->freq_band & LIBIPW_52GHZ_BAND)
for (i = 0; i < ieee->geo.a_channels; i++)
if (ieee->geo.a[i].channel == channel)
return i;
return -1;
}
u32 libipw_channel_to_freq(struct libipw_device * ieee, u8 channel)
{
const struct libipw_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 = libipw_get_channel(ieee, channel);
if (!ch->channel)
return 0;
return ch->freq;
}
u8 libipw_freq_to_channel(struct libipw_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 & LIBIPW_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 & LIBIPW_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;
}
void libipw_set_geo(struct libipw_device *ieee,
const struct libipw_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 libipw_channel));
memcpy(ieee->geo.a, geo->a, ieee->geo.a_channels *
sizeof(struct libipw_channel));
}
const struct libipw_geo *libipw_get_geo(struct libipw_device *ieee)
{
return &ieee->geo;
}
u8 libipw_get_channel_flags(struct libipw_device * ieee, u8 channel)
{
int index = libipw_channel_to_index(ieee, channel);
if (index == -1)
return LIBIPW_CH_INVALID;
if (channel <= LIBIPW_24GHZ_CHANNELS)
return ieee->geo.bg[index].flags;
return ieee->geo.a[index].flags;
}
static const struct libipw_channel bad_channel = {
.channel = 0,
.flags = LIBIPW_CH_INVALID,
.max_power = 0,
};
const struct libipw_channel *libipw_get_channel(struct libipw_device
*ieee, u8 channel)
{
int index = libipw_channel_to_index(ieee, channel);
if (index == -1)
return &bad_channel;
if (channel <= LIBIPW_24GHZ_CHANNELS)
return &ieee->geo.bg[index];
return &ieee->geo.a[index];
}
EXPORT_SYMBOL(libipw_get_channel);
EXPORT_SYMBOL(libipw_get_channel_flags);
EXPORT_SYMBOL(libipw_is_valid_channel);
EXPORT_SYMBOL(libipw_freq_to_channel);
EXPORT_SYMBOL(libipw_channel_to_freq);
EXPORT_SYMBOL(libipw_channel_to_index);
EXPORT_SYMBOL(libipw_set_geo);
EXPORT_SYMBOL(libipw_get_geo);
@@ -0,0 +1,328 @@
// SPDX-License-Identifier: GPL-2.0-only
/*******************************************************************************
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>
Contact Information:
Intel Linux Wireless <ilw@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 <linux/uaccess.h>
#include <net/net_namespace.h>
#include <net/arp.h>
#include "libipw.h"
#define DRV_DESCRIPTION "802.11 data/management/control stack"
#define DRV_NAME "libipw"
#define DRV_PROCNAME "ieee80211"
#define DRV_VERSION LIBIPW_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 struct cfg80211_ops libipw_config_ops = { };
static void *libipw_wiphy_privid = &libipw_wiphy_privid;
static int libipw_networks_allocate(struct libipw_device *ieee)
{
int i, j;
for (i = 0; i < MAX_NETWORK_COUNT; i++) {
ieee->networks[i] = kzalloc_obj(struct libipw_network);
if (!ieee->networks[i]) {
LIBIPW_ERROR("Out of memory allocating beacons\n");
for (j = 0; j < i; j++)
kfree(ieee->networks[j]);
return -ENOMEM;
}
}
return 0;
}
static inline void libipw_networks_free(struct libipw_device *ieee)
{
int i;
for (i = 0; i < MAX_NETWORK_COUNT; i++)
kfree(ieee->networks[i]);
}
void libipw_networks_age(struct libipw_device *ieee,
unsigned long age_secs)
{
struct libipw_network *network = NULL;
unsigned long flags;
unsigned long age_jiffies = secs_to_jiffies(age_secs);
spin_lock_irqsave(&ieee->lock, flags);
list_for_each_entry(network, &ieee->network_list, list) {
network->last_scanned -= age_jiffies;
}
spin_unlock_irqrestore(&ieee->lock, flags);
}
EXPORT_SYMBOL(libipw_networks_age);
static void libipw_networks_initialize(struct libipw_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);
}
struct net_device *alloc_libipw(int sizeof_priv, int monitor)
{
struct libipw_device *ieee;
struct net_device *dev;
int err;
LIBIPW_DEBUG_INFO("Initializing...\n");
dev = alloc_etherdev(sizeof(struct libipw_device) + sizeof_priv);
if (!dev)
goto failed;
ieee = netdev_priv(dev);
ieee->dev = dev;
if (!monitor) {
ieee->wdev.wiphy = wiphy_new(&libipw_config_ops, 0);
if (!ieee->wdev.wiphy) {
LIBIPW_ERROR("Unable to allocate wiphy.\n");
goto failed_free_netdev;
}
ieee->dev->ieee80211_ptr = &ieee->wdev;
ieee->wdev.iftype = NL80211_IFTYPE_STATION;
/* Fill-out wiphy structure bits we know... Not enough info
here to call set_wiphy_dev or set MAC address or channel info
-- have to do that in ->ndo_init... */
ieee->wdev.wiphy->privid = libipw_wiphy_privid;
ieee->wdev.wiphy->max_scan_ssids = 1;
ieee->wdev.wiphy->max_scan_ie_len = 0;
ieee->wdev.wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION)
| BIT(NL80211_IFTYPE_ADHOC);
}
err = libipw_networks_allocate(ieee);
if (err) {
LIBIPW_ERROR("Unable to allocate beacon storage: %d\n", err);
goto failed_free_wiphy;
}
libipw_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 fragmentation 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);
libipw_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_wiphy:
if (!monitor)
wiphy_free(ieee->wdev.wiphy);
failed_free_netdev:
free_netdev(dev);
failed:
return NULL;
}
EXPORT_SYMBOL(alloc_libipw);
void free_libipw(struct net_device *dev, int monitor)
{
struct libipw_device *ieee = netdev_priv(dev);
libipw_crypt_info_free(&ieee->crypt_info);
libipw_networks_free(ieee);
/* free cfg80211 resources */
if (!monitor)
wiphy_free(ieee->wdev.wiphy);
free_netdev(dev);
}
EXPORT_SYMBOL(free_libipw);
#ifdef CONFIG_LIBIPW_DEBUG
static int debug = 0;
u32 libipw_debug_level = 0;
EXPORT_SYMBOL_GPL(libipw_debug_level);
static struct proc_dir_entry *libipw_proc = NULL;
static int debug_level_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "0x%08X\n", libipw_debug_level);
return 0;
}
static int debug_level_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, debug_level_proc_show, NULL);
}
static ssize_t debug_level_proc_write(struct file *file,
const char __user *buffer, size_t count, loff_t *pos)
{
char buf[] = "0x00000000\n";
size_t len = min(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
libipw_debug_level = val;
return strnlen(buf, len);
}
static const struct proc_ops debug_level_proc_ops = {
.proc_open = debug_level_proc_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
.proc_write = debug_level_proc_write,
};
#endif /* CONFIG_LIBIPW_DEBUG */
static int __init libipw_init(void)
{
int err;
#ifdef CONFIG_LIBIPW_DEBUG
struct proc_dir_entry *e;
libipw_debug_level = debug;
libipw_proc = proc_mkdir(DRV_PROCNAME, init_net.proc_net);
if (libipw_proc == NULL) {
LIBIPW_ERROR("Unable to create " DRV_PROCNAME
" proc directory\n");
return -EIO;
}
e = proc_create("debug_level", 0644, libipw_proc,
&debug_level_proc_ops);
if (!e) {
remove_proc_entry(DRV_PROCNAME, init_net.proc_net);
libipw_proc = NULL;
return -EIO;
}
#endif /* CONFIG_LIBIPW_DEBUG */
printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
err = libipw_crypto_init();
if (err)
goto remove_debugfs;
err = libipw_crypto_ccmp_init();
if (err)
goto uninit_crypto;
err = libipw_crypto_tkip_init();
if (err)
goto uninit_crypto_ccmp;
err = libipw_crypto_wep_init();
if (err)
goto uninit_crypto_tkip;
return 0;
uninit_crypto_tkip:
libipw_crypto_tkip_exit();
uninit_crypto_ccmp:
libipw_crypto_ccmp_exit();
uninit_crypto:
libipw_crypto_exit();
remove_debugfs:
#ifdef CONFIG_LIBIPW_DEBUG
remove_proc_entry("debug_level", libipw_proc);
remove_proc_entry(DRV_PROCNAME, init_net.proc_net);
libipw_proc = NULL;
#endif
return err;
}
static void __exit libipw_exit(void)
{
#ifdef CONFIG_LIBIPW_DEBUG
if (libipw_proc) {
remove_proc_entry("debug_level", libipw_proc);
remove_proc_entry(DRV_PROCNAME, init_net.proc_net);
libipw_proc = NULL;
}
#endif /* CONFIG_LIBIPW_DEBUG */
libipw_crypto_ccmp_exit();
libipw_crypto_tkip_exit();
libipw_crypto_wep_exit();
libipw_crypto_exit();
}
#ifdef CONFIG_LIBIPW_DEBUG
#include <linux/moduleparam.h>
module_param(debug, int, 0444);
MODULE_PARM_DESC(debug, "debug output mask");
#endif /* CONFIG_LIBIPW_DEBUG */
module_exit(libipw_exit);
module_init(libipw_init);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,233 @@
/*
* This file implement the Wireless Extensions spy API.
*
* Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
* Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
*
* (As all part of the Linux kernel, this file is GPL)
*/
#include <linux/wireless.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/export.h>
#include <net/iw_handler.h>
#include <net/arp.h>
#include <net/wext.h>
#include "libipw.h"
static struct iw_spy_data *get_spydata(struct net_device *dev)
{
struct libipw_device *ieee = netdev_priv(dev);
if (ieee->spy_enabled)
return &ieee->spy_data;
return NULL;
}
int ipw_wx_set_spy(struct net_device * dev,
struct iw_request_info * info,
union iwreq_data * wrqu,
char * extra)
{
struct iw_spy_data * spydata = get_spydata(dev);
struct sockaddr * address = (struct sockaddr *) extra;
/* Make sure driver is not buggy or using the old API */
if (!spydata)
return -EOPNOTSUPP;
/* Disable spy collection while we copy the addresses.
* While we copy addresses, any call to libipw_spy_update()
* will NOP. This is OK, as anyway the addresses are changing. */
spydata->spy_number = 0;
/* We want to operate without locking, because libipw_spy_update()
* most likely will happen in the interrupt handler, and therefore
* have its own locking constraints and needs performance.
* The rtnl_lock() make sure we don't race with the other iw_handlers.
* This make sure libipw_spy_update() "see" that the spy list
* is temporarily disabled. */
smp_wmb();
/* Are there are addresses to copy? */
if (wrqu->data.length > 0) {
int i;
/* Copy addresses */
for (i = 0; i < wrqu->data.length; i++)
memcpy(spydata->spy_address[i], address[i].sa_data,
ETH_ALEN);
/* Reset stats */
memset(spydata->spy_stat, 0,
sizeof(struct iw_quality) * IW_MAX_SPY);
}
/* Make sure above is updated before re-enabling */
smp_wmb();
/* Enable addresses */
spydata->spy_number = wrqu->data.length;
return 0;
}
EXPORT_SYMBOL(ipw_wx_set_spy);
int ipw_wx_get_spy(struct net_device * dev,
struct iw_request_info * info,
union iwreq_data * wrqu,
char * extra)
{
struct iw_spy_data * spydata = get_spydata(dev);
struct sockaddr * address = (struct sockaddr *) extra;
int i;
/* Make sure driver is not buggy or using the old API */
if (!spydata)
return -EOPNOTSUPP;
wrqu->data.length = spydata->spy_number;
/* Copy addresses. */
for (i = 0; i < spydata->spy_number; i++) {
memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
address[i].sa_family = AF_UNIX;
}
/* Copy stats to the user buffer (just after). */
if (spydata->spy_number > 0)
memcpy(extra + (sizeof(struct sockaddr) *spydata->spy_number),
spydata->spy_stat,
sizeof(struct iw_quality) * spydata->spy_number);
/* Reset updated flags. */
for (i = 0; i < spydata->spy_number; i++)
spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
return 0;
}
EXPORT_SYMBOL(ipw_wx_get_spy);
/*------------------------------------------------------------------*/
/*
* Standard Wireless Handler : set spy threshold
*/
int ipw_wx_set_thrspy(struct net_device * dev,
struct iw_request_info * info,
union iwreq_data * wrqu,
char * extra)
{
struct iw_spy_data * spydata = get_spydata(dev);
struct iw_thrspy * threshold = (struct iw_thrspy *) extra;
/* Make sure driver is not buggy or using the old API */
if (!spydata)
return -EOPNOTSUPP;
/* Just do it */
spydata->spy_thr_low = threshold->low;
spydata->spy_thr_high = threshold->high;
/* Clear flag */
memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
return 0;
}
EXPORT_SYMBOL(ipw_wx_set_thrspy);
/*------------------------------------------------------------------*/
/*
* Standard Wireless Handler : get spy threshold
*/
int ipw_wx_get_thrspy(struct net_device * dev,
struct iw_request_info * info,
union iwreq_data * wrqu,
char * extra)
{
struct iw_spy_data * spydata = get_spydata(dev);
struct iw_thrspy * threshold = (struct iw_thrspy *) extra;
/* Make sure driver is not buggy or using the old API */
if (!spydata)
return -EOPNOTSUPP;
/* Just do it */
threshold->low = spydata->spy_thr_low;
threshold->high = spydata->spy_thr_high;
return 0;
}
EXPORT_SYMBOL(ipw_wx_get_thrspy);
/*------------------------------------------------------------------*/
/*
* Prepare and send a Spy Threshold event
*/
static void iw_send_thrspy_event(struct net_device * dev,
struct iw_spy_data * spydata,
unsigned char * address,
struct iw_quality * wstats)
{
union iwreq_data wrqu;
struct iw_thrspy threshold;
/* Init */
wrqu.data.length = 1;
wrqu.data.flags = 0;
/* Copy address */
memcpy(threshold.addr.sa_data, address, ETH_ALEN);
threshold.addr.sa_family = ARPHRD_ETHER;
/* Copy stats */
threshold.qual = *wstats;
/* Copy also thresholds */
threshold.low = spydata->spy_thr_low;
threshold.high = spydata->spy_thr_high;
/* Send event to user space */
wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
}
/* ---------------------------------------------------------------- */
/*
* Call for the driver to update the spy data.
* For now, the spy data is a simple array. As the size of the array is
* small, this is good enough. If we wanted to support larger number of
* spy addresses, we should use something more efficient...
*/
void libipw_spy_update(struct net_device * dev,
unsigned char * address,
struct iw_quality * wstats)
{
struct iw_spy_data * spydata = get_spydata(dev);
int i;
int match = -1;
/* Make sure driver is not buggy or using the old API */
if (!spydata)
return;
/* Update all records that match */
for (i = 0; i < spydata->spy_number; i++)
if (ether_addr_equal(address, spydata->spy_address[i])) {
memcpy(&(spydata->spy_stat[i]), wstats,
sizeof(struct iw_quality));
match = i;
}
/* Generate an event if we cross the spy threshold.
* To avoid event storms, we have a simple hysteresis : we generate
* event only when we go under the low threshold or above the
* high threshold. */
if (match >= 0) {
if (spydata->spy_thr_under[match]) {
if (wstats->level > spydata->spy_thr_high.level) {
spydata->spy_thr_under[match] = 0;
iw_send_thrspy_event(dev, spydata,
address, wstats);
}
} else {
if (wstats->level < spydata->spy_thr_low.level) {
spydata->spy_thr_under[match] = 1;
iw_send_thrspy_event(dev, spydata,
address, wstats);
}
}
}
}
@@ -0,0 +1,518 @@
// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
Copyright(c) 2003 - 2005 Intel Corporation. All rights reserved.
Contact Information:
Intel Linux Wireless <ilw@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 <linux/uaccess.h>
#include "libipw.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 libipw_copy_snap(u8 * data, __be16 h_proto)
{
struct libipw_snap_hdr *snap;
u8 *oui;
snap = (struct libipw_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 libipw_encrypt_fragment(struct libipw_device *ieee,
struct sk_buff *frag, int hdr_len)
{
struct libipw_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 libipw_txb_free(struct libipw_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 libipw_txb *libipw_alloc_txb(int nr_frags, int txb_size,
int headroom, gfp_t gfp_mask)
{
struct libipw_txb *txb;
int i;
txb = kzalloc_flex(*txb, fragments, nr_frags, gfp_mask);
if (!txb)
return NULL;
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 libipw_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) */
netdev_tx_t libipw_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct libipw_device *ieee = netdev_priv(dev);
struct libipw_txb *txb = NULL;
struct libipw_hdr_3addrqos *frag_hdr;
int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size,
rts_required;
unsigned long flags;
int encrypt, host_encrypt, host_encrypt_msdu;
__be16 ether_type;
int bytes, fc, hdr_len;
struct sk_buff *skb_frag;
struct libipw_hdr_3addrqos header = {/* Ensure zero initialized */
.duration_id = 0,
.seq_ctl = 0,
.qos_ctl = 0
};
u8 dest[ETH_ALEN], src[ETH_ALEN];
struct libipw_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;
if (!encrypt && ieee->ieee802_1x &&
ieee->drop_unencrypted && ether_type != htons(ETH_P_PAE)) {
dev->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)
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 = LIBIPW_3ADDR_LEN;
if (ieee->is_qos_active && ieee->is_qos_active(dev, skb)) {
fc |= IEEE80211_STYPE_QOS_DATA;
hdr_len += 2;
skb->priority = libipw_classify(skb);
header.qos_ctl |= cpu_to_le16(skb->priority & LIBIPW_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);
skb_put_data(skb_new, &header, hdr_len);
snapped = 1;
libipw_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) {
LIBIPW_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_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS))
bytes_per_frag -= LIBIPW_FCS_LEN;
/* Each fragment may need to have room for encryption
* pre/postfix */
if (host_encrypt && crypt && crypt->ops)
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_LIBIPW_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 = libipw_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 = 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_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_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)
skb_reserve(skb_frag,
crypt->ops->extra_mpdu_prefix_len);
frag_hdr = skb_put_data(skb_frag, &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) {
libipw_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)
libipw_encrypt_fragment(ieee, skb_frag, hdr_len);
if (ieee->config &
(CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS))
skb_put(skb_frag, 4);
}
success:
spin_unlock_irqrestore(&ieee->lock, flags);
dev_kfree_skb_any(skb);
if (txb) {
netdev_tx_t ret = (*ieee->hard_start_xmit)(txb, dev, priority);
if (ret == NETDEV_TX_OK) {
dev->stats.tx_packets++;
dev->stats.tx_bytes += txb->payload_size;
return NETDEV_TX_OK;
}
libipw_txb_free(txb);
}
return NETDEV_TX_OK;
failed:
spin_unlock_irqrestore(&ieee->lock, flags);
netif_stop_queue(dev);
dev->stats.tx_errors++;
return NETDEV_TX_BUSY;
}
EXPORT_SYMBOL(libipw_xmit);
EXPORT_SYMBOL(libipw_txb_free);
@@ -0,0 +1,722 @@
// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
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>
Contact Information:
Intel Linux Wireless <ilw@linux.intel.com>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
******************************************************************************/
#include <linux/hardirq.h>
#include <linux/kmod.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/jiffies.h>
#include <linux/wireless.h>
#include "libipw.h"
static const char *libipw_modes[] = {
"?", "a", "b", "ab", "g", "ag", "bg", "abg"
};
static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
{
unsigned long end = jiffies;
if (end >= start)
return jiffies_to_msecs(end - start);
return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
}
#define MAX_CUSTOM_LEN 64
static char *libipw_translate_scan(struct libipw_device *ieee,
char *start, char *stop,
struct libipw_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",
libipw_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 = libipw_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 & LIBIPW_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 & LIBIPW_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 & LIBIPW_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 += scnprintf(p, MAX_CUSTOM_LEN - (p - custom),
" Last beacon: %ums ago",
elapsed_jiffies_msecs(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 += scnprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
if (libipw_get_channel_flags(ieee, network->channel) &
LIBIPW_CH_INVALID) {
iwe.cmd = IWEVCUSTOM;
p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
}
if (libipw_get_channel_flags(ieee, network->channel) &
LIBIPW_CH_RADAR_DETECT) {
iwe.cmd = IWEVCUSTOM;
p += scnprintf(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 libipw_wx_get_scan(struct libipw_device *ieee,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct libipw_network *network;
unsigned long flags;
int err = 0;
char *ev = extra;
char *stop = ev + wrqu->data.length;
int i = 0;
LIBIPW_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 = libipw_translate_scan(ieee, ev, stop, network,
info);
else {
LIBIPW_DEBUG_SCAN("Not showing network '%*pE (%pM)' due to age (%ums).\n",
network->ssid_len, network->ssid,
network->bssid,
elapsed_jiffies_msecs(
network->last_scanned));
}
}
spin_unlock_irqrestore(&ieee->lock, flags);
wrqu->data.length = ev - extra;
wrqu->data.flags = 0;
LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
return err;
}
int libipw_wx_set_encode(struct libipw_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 libipw_security sec = {
.flags = 0
};
int i, key, key_provided, len;
struct libipw_crypt_data **crypt;
int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
LIBIPW_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;
}
LIBIPW_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) {
LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n",
key);
libipw_crypt_delayed_deinit(&ieee->crypt_info, crypt);
} else
LIBIPW_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;
libipw_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 */
libipw_crypt_delayed_deinit(&ieee->crypt_info, crypt);
}
if (*crypt == NULL && host_crypto) {
struct libipw_crypt_data *new_crypt;
/* take WEP into use */
new_crypt = kzalloc_obj(struct libipw_crypt_data);
if (new_crypt == NULL)
return -ENOMEM;
new_crypt->ops = libipw_get_crypto_ops("WEP");
if (!new_crypt->ops) {
request_module("libipw_crypt_wep");
new_crypt->ops = libipw_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 libipw_crypt_wep\n", dev->name);
return -EOPNOTSUPP;
}
*crypt = new_crypt;
}
/* If a new key was provided, set it up */
if (erq->length > 0) {
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);
LIBIPW_DEBUG_WX("Setting key %d to '%*pE' (%d:%d bytes)\n",
key, len, sec.keys[key],
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 */
LIBIPW_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) {
LIBIPW_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;
LIBIPW_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);
return 0;
}
int libipw_wx_get_encode(struct libipw_device *ieee,
struct iw_request_info *info,
union iwreq_data *wrqu, char *keybuf)
{
struct iw_point *erq = &(wrqu->encoding);
int len, key;
struct libipw_security *sec = &ieee->sec;
LIBIPW_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;
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 libipw_wx_set_encodeext(struct libipw_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;
const struct libipw_crypto_ops *ops;
struct libipw_crypt_data **crypt;
struct libipw_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)
libipw_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 = "libipw_crypt_wep";
break;
case IW_ENCODE_ALG_TKIP:
alg = "TKIP";
module = "libipw_crypt_tkip";
break;
case IW_ENCODE_ALG_CCMP:
alg = "CCMP";
module = "libipw_crypt_ccmp";
break;
default:
LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
dev->name, ext->alg);
ret = -EINVAL;
goto done;
}
ops = libipw_get_crypto_ops(alg);
if (ops == NULL) {
request_module(module);
ops = libipw_get_crypto_ops(alg);
}
if (ops == NULL) {
LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
dev->name, ext->alg);
ret = -EINVAL;
goto done;
}
if (*crypt == NULL || (*crypt)->ops != ops) {
struct libipw_crypt_data *new_crypt;
libipw_crypt_delayed_deinit(&ieee->crypt_info, crypt);
new_crypt = kzalloc_obj(*new_crypt);
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) {
LIBIPW_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) {
int key_len = clamp_val(ext->key_len, 0, SCM_KEY_LEN);
memcpy(sec.keys[idx], ext->key, key_len);
sec.key_sizes[idx] = 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(dev, &sec);
return ret;
}
int libipw_wx_get_encodeext(struct libipw_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 libipw_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(libipw_wx_set_encodeext);
EXPORT_SYMBOL(libipw_wx_get_encodeext);
EXPORT_SYMBOL(libipw_wx_get_scan);
EXPORT_SYMBOL(libipw_wx_set_encode);
EXPORT_SYMBOL(libipw_wx_get_encode);