ca9938fea5
When mac80211 is built into the kernel it needs to init earlier so that device registrations are run after it has initialised. The same applies to rate control algorithms. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
5267 lines
146 KiB
C
5267 lines
146 KiB
C
/*
|
|
* Copyright 2002-2005, Instant802 Networks, Inc.
|
|
* Copyright 2005-2006, Devicescape Software, Inc.
|
|
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <net/mac80211.h>
|
|
#include <net/ieee80211_radiotap.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/types.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/if_arp.h>
|
|
#include <linux/wireless.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include <net/iw_handler.h>
|
|
#include <linux/compiler.h>
|
|
#include <linux/bitmap.h>
|
|
#include <net/cfg80211.h>
|
|
#include <asm/unaligned.h>
|
|
|
|
#include "ieee80211_common.h"
|
|
#include "ieee80211_i.h"
|
|
#include "ieee80211_rate.h"
|
|
#include "wep.h"
|
|
#include "wpa.h"
|
|
#include "tkip.h"
|
|
#include "wme.h"
|
|
#include "aes_ccm.h"
|
|
#include "ieee80211_led.h"
|
|
#include "ieee80211_cfg.h"
|
|
#include "debugfs.h"
|
|
#include "debugfs_netdev.h"
|
|
#include "debugfs_key.h"
|
|
|
|
/* privid for wiphys to determine whether they belong to us or not */
|
|
void *mac80211_wiphy_privid = &mac80211_wiphy_privid;
|
|
|
|
/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
|
|
/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
|
|
static const unsigned char rfc1042_header[] =
|
|
{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
|
|
|
|
/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
|
|
static const unsigned char bridge_tunnel_header[] =
|
|
{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
|
|
|
|
/* No encapsulation header if EtherType < 0x600 (=length) */
|
|
static const unsigned char eapol_header[] =
|
|
{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x88, 0x8e };
|
|
|
|
|
|
/*
|
|
* For seeing transmitted packets on monitor interfaces
|
|
* we have a radiotap header too.
|
|
*/
|
|
struct ieee80211_tx_status_rtap_hdr {
|
|
struct ieee80211_radiotap_header hdr;
|
|
__le16 tx_flags;
|
|
u8 data_retries;
|
|
} __attribute__ ((packed));
|
|
|
|
|
|
static inline void ieee80211_include_sequence(struct ieee80211_sub_if_data *sdata,
|
|
struct ieee80211_hdr *hdr)
|
|
{
|
|
/* Set the sequence number for this frame. */
|
|
hdr->seq_ctrl = cpu_to_le16(sdata->sequence);
|
|
|
|
/* Increase the sequence number. */
|
|
sdata->sequence = (sdata->sequence + 0x10) & IEEE80211_SCTL_SEQ;
|
|
}
|
|
|
|
struct ieee80211_key_conf *
|
|
ieee80211_key_data2conf(struct ieee80211_local *local,
|
|
const struct ieee80211_key *data)
|
|
{
|
|
struct ieee80211_key_conf *conf;
|
|
|
|
conf = kmalloc(sizeof(*conf) + data->keylen, GFP_ATOMIC);
|
|
if (!conf)
|
|
return NULL;
|
|
|
|
conf->hw_key_idx = data->hw_key_idx;
|
|
conf->alg = data->alg;
|
|
conf->keylen = data->keylen;
|
|
conf->flags = 0;
|
|
if (data->force_sw_encrypt)
|
|
conf->flags |= IEEE80211_KEY_FORCE_SW_ENCRYPT;
|
|
conf->keyidx = data->keyidx;
|
|
if (data->default_tx_key)
|
|
conf->flags |= IEEE80211_KEY_DEFAULT_TX_KEY;
|
|
if (local->default_wep_only)
|
|
conf->flags |= IEEE80211_KEY_DEFAULT_WEP_ONLY;
|
|
memcpy(conf->key, data->key, data->keylen);
|
|
|
|
return conf;
|
|
}
|
|
|
|
struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
|
|
int idx, size_t key_len, gfp_t flags)
|
|
{
|
|
struct ieee80211_key *key;
|
|
|
|
key = kzalloc(sizeof(struct ieee80211_key) + key_len, flags);
|
|
if (!key)
|
|
return NULL;
|
|
kref_init(&key->kref);
|
|
return key;
|
|
}
|
|
|
|
static void ieee80211_key_release(struct kref *kref)
|
|
{
|
|
struct ieee80211_key *key;
|
|
|
|
key = container_of(kref, struct ieee80211_key, kref);
|
|
if (key->alg == ALG_CCMP)
|
|
ieee80211_aes_key_free(key->u.ccmp.tfm);
|
|
ieee80211_debugfs_key_remove(key);
|
|
kfree(key);
|
|
}
|
|
|
|
void ieee80211_key_free(struct ieee80211_key *key)
|
|
{
|
|
if (key)
|
|
kref_put(&key->kref, ieee80211_key_release);
|
|
}
|
|
|
|
static int rate_list_match(const int *rate_list, int rate)
|
|
{
|
|
int i;
|
|
|
|
if (!rate_list)
|
|
return 0;
|
|
|
|
for (i = 0; rate_list[i] >= 0; i++)
|
|
if (rate_list[i] == rate)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void ieee80211_prepare_rates(struct ieee80211_local *local,
|
|
struct ieee80211_hw_mode *mode)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < mode->num_rates; i++) {
|
|
struct ieee80211_rate *rate = &mode->rates[i];
|
|
|
|
rate->flags &= ~(IEEE80211_RATE_SUPPORTED |
|
|
IEEE80211_RATE_BASIC);
|
|
|
|
if (local->supp_rates[mode->mode]) {
|
|
if (!rate_list_match(local->supp_rates[mode->mode],
|
|
rate->rate))
|
|
continue;
|
|
}
|
|
|
|
rate->flags |= IEEE80211_RATE_SUPPORTED;
|
|
|
|
/* Use configured basic rate set if it is available. If not,
|
|
* use defaults that are sane for most cases. */
|
|
if (local->basic_rates[mode->mode]) {
|
|
if (rate_list_match(local->basic_rates[mode->mode],
|
|
rate->rate))
|
|
rate->flags |= IEEE80211_RATE_BASIC;
|
|
} else switch (mode->mode) {
|
|
case MODE_IEEE80211A:
|
|
if (rate->rate == 60 || rate->rate == 120 ||
|
|
rate->rate == 240)
|
|
rate->flags |= IEEE80211_RATE_BASIC;
|
|
break;
|
|
case MODE_IEEE80211B:
|
|
if (rate->rate == 10 || rate->rate == 20)
|
|
rate->flags |= IEEE80211_RATE_BASIC;
|
|
break;
|
|
case MODE_ATHEROS_TURBO:
|
|
if (rate->rate == 120 || rate->rate == 240 ||
|
|
rate->rate == 480)
|
|
rate->flags |= IEEE80211_RATE_BASIC;
|
|
break;
|
|
case MODE_IEEE80211G:
|
|
if (rate->rate == 10 || rate->rate == 20 ||
|
|
rate->rate == 55 || rate->rate == 110)
|
|
rate->flags |= IEEE80211_RATE_BASIC;
|
|
break;
|
|
}
|
|
|
|
/* Set ERP and MANDATORY flags based on phymode */
|
|
switch (mode->mode) {
|
|
case MODE_IEEE80211A:
|
|
if (rate->rate == 60 || rate->rate == 120 ||
|
|
rate->rate == 240)
|
|
rate->flags |= IEEE80211_RATE_MANDATORY;
|
|
break;
|
|
case MODE_IEEE80211B:
|
|
if (rate->rate == 10)
|
|
rate->flags |= IEEE80211_RATE_MANDATORY;
|
|
break;
|
|
case MODE_ATHEROS_TURBO:
|
|
break;
|
|
case MODE_IEEE80211G:
|
|
if (rate->rate == 10 || rate->rate == 20 ||
|
|
rate->rate == 55 || rate->rate == 110 ||
|
|
rate->rate == 60 || rate->rate == 120 ||
|
|
rate->rate == 240)
|
|
rate->flags |= IEEE80211_RATE_MANDATORY;
|
|
break;
|
|
}
|
|
if (ieee80211_is_erp_rate(mode->mode, rate->rate))
|
|
rate->flags |= IEEE80211_RATE_ERP;
|
|
}
|
|
}
|
|
|
|
|
|
static void ieee80211_key_threshold_notify(struct net_device *dev,
|
|
struct ieee80211_key *key,
|
|
struct sta_info *sta)
|
|
{
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
struct sk_buff *skb;
|
|
struct ieee80211_msg_key_notification *msg;
|
|
|
|
/* if no one will get it anyway, don't even allocate it.
|
|
* unlikely because this is only relevant for APs
|
|
* where the device must be open... */
|
|
if (unlikely(!local->apdev))
|
|
return;
|
|
|
|
skb = dev_alloc_skb(sizeof(struct ieee80211_frame_info) +
|
|
sizeof(struct ieee80211_msg_key_notification));
|
|
if (!skb)
|
|
return;
|
|
|
|
skb_reserve(skb, sizeof(struct ieee80211_frame_info));
|
|
msg = (struct ieee80211_msg_key_notification *)
|
|
skb_put(skb, sizeof(struct ieee80211_msg_key_notification));
|
|
msg->tx_rx_count = key->tx_rx_count;
|
|
memcpy(msg->ifname, dev->name, IFNAMSIZ);
|
|
if (sta)
|
|
memcpy(msg->addr, sta->addr, ETH_ALEN);
|
|
else
|
|
memset(msg->addr, 0xff, ETH_ALEN);
|
|
|
|
key->tx_rx_count = 0;
|
|
|
|
ieee80211_rx_mgmt(local, skb, NULL,
|
|
ieee80211_msg_key_threshold_notification);
|
|
}
|
|
|
|
|
|
static u8 * ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len)
|
|
{
|
|
u16 fc;
|
|
|
|
if (len < 24)
|
|
return NULL;
|
|
|
|
fc = le16_to_cpu(hdr->frame_control);
|
|
|
|
switch (fc & IEEE80211_FCTL_FTYPE) {
|
|
case IEEE80211_FTYPE_DATA:
|
|
switch (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
|
|
case IEEE80211_FCTL_TODS:
|
|
return hdr->addr1;
|
|
case (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
|
|
return NULL;
|
|
case IEEE80211_FCTL_FROMDS:
|
|
return hdr->addr2;
|
|
case 0:
|
|
return hdr->addr3;
|
|
}
|
|
break;
|
|
case IEEE80211_FTYPE_MGMT:
|
|
return hdr->addr3;
|
|
case IEEE80211_FTYPE_CTL:
|
|
if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)
|
|
return hdr->addr1;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int ieee80211_get_hdrlen(u16 fc)
|
|
{
|
|
int hdrlen = 24;
|
|
|
|
switch (fc & IEEE80211_FCTL_FTYPE) {
|
|
case IEEE80211_FTYPE_DATA:
|
|
if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS))
|
|
hdrlen = 30; /* Addr4 */
|
|
/*
|
|
* The QoS Control field is two bytes and its presence is
|
|
* indicated by the IEEE80211_STYPE_QOS_DATA bit. Add 2 to
|
|
* hdrlen if that bit is set.
|
|
* This works by masking out the bit and shifting it to
|
|
* bit position 1 so the result has the value 0 or 2.
|
|
*/
|
|
hdrlen += (fc & IEEE80211_STYPE_QOS_DATA)
|
|
>> (ilog2(IEEE80211_STYPE_QOS_DATA)-1);
|
|
break;
|
|
case IEEE80211_FTYPE_CTL:
|
|
/*
|
|
* ACK and CTS are 10 bytes, all others 16. To see how
|
|
* to get this condition consider
|
|
* subtype mask: 0b0000000011110000 (0x00F0)
|
|
* ACK subtype: 0b0000000011010000 (0x00D0)
|
|
* CTS subtype: 0b0000000011000000 (0x00C0)
|
|
* bits that matter: ^^^ (0x00E0)
|
|
* value of those: 0b0000000011000000 (0x00C0)
|
|
*/
|
|
if ((fc & 0xE0) == 0xC0)
|
|
hdrlen = 10;
|
|
else
|
|
hdrlen = 16;
|
|
break;
|
|
}
|
|
|
|
return hdrlen;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_get_hdrlen);
|
|
|
|
int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
|
|
{
|
|
const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) skb->data;
|
|
int hdrlen;
|
|
|
|
if (unlikely(skb->len < 10))
|
|
return 0;
|
|
hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control));
|
|
if (unlikely(hdrlen > skb->len))
|
|
return 0;
|
|
return hdrlen;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
|
|
|
|
static int ieee80211_get_radiotap_len(struct sk_buff *skb)
|
|
{
|
|
struct ieee80211_radiotap_header *hdr =
|
|
(struct ieee80211_radiotap_header *) skb->data;
|
|
|
|
return le16_to_cpu(hdr->it_len);
|
|
}
|
|
|
|
#ifdef CONFIG_MAC80211_LOWTX_FRAME_DUMP
|
|
static void ieee80211_dump_frame(const char *ifname, const char *title,
|
|
const struct sk_buff *skb)
|
|
{
|
|
const struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
|
u16 fc;
|
|
int hdrlen;
|
|
|
|
printk(KERN_DEBUG "%s: %s (len=%d)", ifname, title, skb->len);
|
|
if (skb->len < 4) {
|
|
printk("\n");
|
|
return;
|
|
}
|
|
|
|
fc = le16_to_cpu(hdr->frame_control);
|
|
hdrlen = ieee80211_get_hdrlen(fc);
|
|
if (hdrlen > skb->len)
|
|
hdrlen = skb->len;
|
|
if (hdrlen >= 4)
|
|
printk(" FC=0x%04x DUR=0x%04x",
|
|
fc, le16_to_cpu(hdr->duration_id));
|
|
if (hdrlen >= 10)
|
|
printk(" A1=" MAC_FMT, MAC_ARG(hdr->addr1));
|
|
if (hdrlen >= 16)
|
|
printk(" A2=" MAC_FMT, MAC_ARG(hdr->addr2));
|
|
if (hdrlen >= 24)
|
|
printk(" A3=" MAC_FMT, MAC_ARG(hdr->addr3));
|
|
if (hdrlen >= 30)
|
|
printk(" A4=" MAC_FMT, MAC_ARG(hdr->addr4));
|
|
printk("\n");
|
|
}
|
|
#else /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */
|
|
static inline void ieee80211_dump_frame(const char *ifname, const char *title,
|
|
struct sk_buff *skb)
|
|
{
|
|
}
|
|
#endif /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */
|
|
|
|
|
|
static int ieee80211_is_eapol(const struct sk_buff *skb)
|
|
{
|
|
const struct ieee80211_hdr *hdr;
|
|
u16 fc;
|
|
int hdrlen;
|
|
|
|
if (unlikely(skb->len < 10))
|
|
return 0;
|
|
|
|
hdr = (const struct ieee80211_hdr *) skb->data;
|
|
fc = le16_to_cpu(hdr->frame_control);
|
|
|
|
if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
|
|
return 0;
|
|
|
|
hdrlen = ieee80211_get_hdrlen(fc);
|
|
|
|
if (unlikely(skb->len >= hdrlen + sizeof(eapol_header) &&
|
|
memcmp(skb->data + hdrlen, eapol_header,
|
|
sizeof(eapol_header)) == 0))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_tx_h_rate_ctrl(struct ieee80211_txrx_data *tx)
|
|
{
|
|
struct rate_control_extra extra;
|
|
|
|
memset(&extra, 0, sizeof(extra));
|
|
extra.mode = tx->u.tx.mode;
|
|
extra.mgmt_data = tx->sdata &&
|
|
tx->sdata->type == IEEE80211_IF_TYPE_MGMT;
|
|
extra.ethertype = tx->ethertype;
|
|
|
|
tx->u.tx.rate = rate_control_get_rate(tx->local, tx->dev, tx->skb,
|
|
&extra);
|
|
if (unlikely(extra.probe != NULL)) {
|
|
tx->u.tx.control->flags |= IEEE80211_TXCTL_RATE_CTRL_PROBE;
|
|
tx->u.tx.probe_last_frag = 1;
|
|
tx->u.tx.control->alt_retry_rate = tx->u.tx.rate->val;
|
|
tx->u.tx.rate = extra.probe;
|
|
} else {
|
|
tx->u.tx.control->alt_retry_rate = -1;
|
|
}
|
|
if (!tx->u.tx.rate)
|
|
return TXRX_DROP;
|
|
if (tx->u.tx.mode->mode == MODE_IEEE80211G &&
|
|
tx->sdata->use_protection && tx->fragmented &&
|
|
extra.nonerp) {
|
|
tx->u.tx.last_frag_rate = tx->u.tx.rate;
|
|
tx->u.tx.probe_last_frag = extra.probe ? 1 : 0;
|
|
|
|
tx->u.tx.rate = extra.nonerp;
|
|
tx->u.tx.control->rate = extra.nonerp;
|
|
tx->u.tx.control->flags &= ~IEEE80211_TXCTL_RATE_CTRL_PROBE;
|
|
} else {
|
|
tx->u.tx.last_frag_rate = tx->u.tx.rate;
|
|
tx->u.tx.control->rate = tx->u.tx.rate;
|
|
}
|
|
tx->u.tx.control->tx_rate = tx->u.tx.rate->val;
|
|
if ((tx->u.tx.rate->flags & IEEE80211_RATE_PREAMBLE2) &&
|
|
tx->local->short_preamble &&
|
|
(!tx->sta || (tx->sta->flags & WLAN_STA_SHORT_PREAMBLE))) {
|
|
tx->u.tx.short_preamble = 1;
|
|
tx->u.tx.control->tx_rate = tx->u.tx.rate->val2;
|
|
}
|
|
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_tx_h_select_key(struct ieee80211_txrx_data *tx)
|
|
{
|
|
if (tx->sta)
|
|
tx->u.tx.control->key_idx = tx->sta->key_idx_compression;
|
|
else
|
|
tx->u.tx.control->key_idx = HW_KEY_IDX_INVALID;
|
|
|
|
if (unlikely(tx->u.tx.control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT))
|
|
tx->key = NULL;
|
|
else if (tx->sta && tx->sta->key)
|
|
tx->key = tx->sta->key;
|
|
else if (tx->sdata->default_key)
|
|
tx->key = tx->sdata->default_key;
|
|
else if (tx->sdata->drop_unencrypted &&
|
|
!(tx->sdata->eapol && ieee80211_is_eapol(tx->skb))) {
|
|
I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted);
|
|
return TXRX_DROP;
|
|
} else
|
|
tx->key = NULL;
|
|
|
|
if (tx->key) {
|
|
tx->key->tx_rx_count++;
|
|
if (unlikely(tx->local->key_tx_rx_threshold &&
|
|
tx->key->tx_rx_count >
|
|
tx->local->key_tx_rx_threshold)) {
|
|
ieee80211_key_threshold_notify(tx->dev, tx->key,
|
|
tx->sta);
|
|
}
|
|
}
|
|
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_tx_h_fragment(struct ieee80211_txrx_data *tx)
|
|
{
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
|
|
size_t hdrlen, per_fragm, num_fragm, payload_len, left;
|
|
struct sk_buff **frags, *first, *frag;
|
|
int i;
|
|
u16 seq;
|
|
u8 *pos;
|
|
int frag_threshold = tx->local->fragmentation_threshold;
|
|
|
|
if (!tx->fragmented)
|
|
return TXRX_CONTINUE;
|
|
|
|
first = tx->skb;
|
|
|
|
hdrlen = ieee80211_get_hdrlen(tx->fc);
|
|
payload_len = first->len - hdrlen;
|
|
per_fragm = frag_threshold - hdrlen - FCS_LEN;
|
|
num_fragm = (payload_len + per_fragm - 1) / per_fragm;
|
|
|
|
frags = kzalloc(num_fragm * sizeof(struct sk_buff *), GFP_ATOMIC);
|
|
if (!frags)
|
|
goto fail;
|
|
|
|
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
|
|
seq = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ;
|
|
pos = first->data + hdrlen + per_fragm;
|
|
left = payload_len - per_fragm;
|
|
for (i = 0; i < num_fragm - 1; i++) {
|
|
struct ieee80211_hdr *fhdr;
|
|
size_t copylen;
|
|
|
|
if (left <= 0)
|
|
goto fail;
|
|
|
|
/* reserve enough extra head and tail room for possible
|
|
* encryption */
|
|
frag = frags[i] =
|
|
dev_alloc_skb(tx->local->tx_headroom +
|
|
frag_threshold +
|
|
IEEE80211_ENCRYPT_HEADROOM +
|
|
IEEE80211_ENCRYPT_TAILROOM);
|
|
if (!frag)
|
|
goto fail;
|
|
/* Make sure that all fragments use the same priority so
|
|
* that they end up using the same TX queue */
|
|
frag->priority = first->priority;
|
|
skb_reserve(frag, tx->local->tx_headroom +
|
|
IEEE80211_ENCRYPT_HEADROOM);
|
|
fhdr = (struct ieee80211_hdr *) skb_put(frag, hdrlen);
|
|
memcpy(fhdr, first->data, hdrlen);
|
|
if (i == num_fragm - 2)
|
|
fhdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREFRAGS);
|
|
fhdr->seq_ctrl = cpu_to_le16(seq | ((i + 1) & IEEE80211_SCTL_FRAG));
|
|
copylen = left > per_fragm ? per_fragm : left;
|
|
memcpy(skb_put(frag, copylen), pos, copylen);
|
|
|
|
pos += copylen;
|
|
left -= copylen;
|
|
}
|
|
skb_trim(first, hdrlen + per_fragm);
|
|
|
|
tx->u.tx.num_extra_frag = num_fragm - 1;
|
|
tx->u.tx.extra_frag = frags;
|
|
|
|
return TXRX_CONTINUE;
|
|
|
|
fail:
|
|
printk(KERN_DEBUG "%s: failed to fragment frame\n", tx->dev->name);
|
|
if (frags) {
|
|
for (i = 0; i < num_fragm - 1; i++)
|
|
if (frags[i])
|
|
dev_kfree_skb(frags[i]);
|
|
kfree(frags);
|
|
}
|
|
I802_DEBUG_INC(tx->local->tx_handlers_drop_fragment);
|
|
return TXRX_DROP;
|
|
}
|
|
|
|
|
|
static int wep_encrypt_skb(struct ieee80211_txrx_data *tx, struct sk_buff *skb)
|
|
{
|
|
if (tx->key->force_sw_encrypt) {
|
|
if (ieee80211_wep_encrypt(tx->local, skb, tx->key))
|
|
return -1;
|
|
} else {
|
|
tx->u.tx.control->key_idx = tx->key->hw_key_idx;
|
|
if (tx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) {
|
|
if (ieee80211_wep_add_iv(tx->local, skb, tx->key) ==
|
|
NULL)
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx)
|
|
{
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
|
|
|
|
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
|
|
if (tx->u.tx.extra_frag) {
|
|
struct ieee80211_hdr *fhdr;
|
|
int i;
|
|
for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
|
|
fhdr = (struct ieee80211_hdr *)
|
|
tx->u.tx.extra_frag[i]->data;
|
|
fhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_tx_h_wep_encrypt(struct ieee80211_txrx_data *tx)
|
|
{
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
|
|
u16 fc;
|
|
|
|
fc = le16_to_cpu(hdr->frame_control);
|
|
|
|
if (!tx->key || tx->key->alg != ALG_WEP ||
|
|
((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
|
|
((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
|
|
(fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH)))
|
|
return TXRX_CONTINUE;
|
|
|
|
tx->u.tx.control->iv_len = WEP_IV_LEN;
|
|
tx->u.tx.control->icv_len = WEP_ICV_LEN;
|
|
ieee80211_tx_set_iswep(tx);
|
|
|
|
if (wep_encrypt_skb(tx, tx->skb) < 0) {
|
|
I802_DEBUG_INC(tx->local->tx_handlers_drop_wep);
|
|
return TXRX_DROP;
|
|
}
|
|
|
|
if (tx->u.tx.extra_frag) {
|
|
int i;
|
|
for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
|
|
if (wep_encrypt_skb(tx, tx->u.tx.extra_frag[i]) < 0) {
|
|
I802_DEBUG_INC(tx->local->
|
|
tx_handlers_drop_wep);
|
|
return TXRX_DROP;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
|
|
static int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
|
|
int rate, int erp, int short_preamble)
|
|
{
|
|
int dur;
|
|
|
|
/* calculate duration (in microseconds, rounded up to next higher
|
|
* integer if it includes a fractional microsecond) to send frame of
|
|
* len bytes (does not include FCS) at the given rate. Duration will
|
|
* also include SIFS.
|
|
*
|
|
* rate is in 100 kbps, so divident is multiplied by 10 in the
|
|
* DIV_ROUND_UP() operations.
|
|
*/
|
|
|
|
if (local->hw.conf.phymode == MODE_IEEE80211A || erp ||
|
|
local->hw.conf.phymode == MODE_ATHEROS_TURBO) {
|
|
/*
|
|
* OFDM:
|
|
*
|
|
* N_DBPS = DATARATE x 4
|
|
* N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS)
|
|
* (16 = SIGNAL time, 6 = tail bits)
|
|
* TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext
|
|
*
|
|
* T_SYM = 4 usec
|
|
* 802.11a - 17.5.2: aSIFSTime = 16 usec
|
|
* 802.11g - 19.8.4: aSIFSTime = 10 usec +
|
|
* signal ext = 6 usec
|
|
*/
|
|
/* FIX: Atheros Turbo may have different (shorter) duration? */
|
|
dur = 16; /* SIFS + signal ext */
|
|
dur += 16; /* 17.3.2.3: T_PREAMBLE = 16 usec */
|
|
dur += 4; /* 17.3.2.3: T_SIGNAL = 4 usec */
|
|
dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10,
|
|
4 * rate); /* T_SYM x N_SYM */
|
|
} else {
|
|
/*
|
|
* 802.11b or 802.11g with 802.11b compatibility:
|
|
* 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime +
|
|
* Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0.
|
|
*
|
|
* 802.11 (DS): 15.3.3, 802.11b: 18.3.4
|
|
* aSIFSTime = 10 usec
|
|
* aPreambleLength = 144 usec or 72 usec with short preamble
|
|
* aPLCPHeaderLength = 48 usec or 24 usec with short preamble
|
|
*/
|
|
dur = 10; /* aSIFSTime = 10 usec */
|
|
dur += short_preamble ? (72 + 24) : (144 + 48);
|
|
|
|
dur += DIV_ROUND_UP(8 * (len + 4) * 10, rate);
|
|
}
|
|
|
|
return dur;
|
|
}
|
|
|
|
|
|
/* Exported duration function for driver use */
|
|
__le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
|
|
size_t frame_len, int rate)
|
|
{
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
u16 dur;
|
|
int erp;
|
|
|
|
erp = ieee80211_is_erp_rate(hw->conf.phymode, rate);
|
|
dur = ieee80211_frame_duration(local, frame_len, rate,
|
|
erp, local->short_preamble);
|
|
|
|
return cpu_to_le16(dur);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_generic_frame_duration);
|
|
|
|
|
|
static u16 ieee80211_duration(struct ieee80211_txrx_data *tx, int group_addr,
|
|
int next_frag_len)
|
|
{
|
|
int rate, mrate, erp, dur, i;
|
|
struct ieee80211_rate *txrate = tx->u.tx.rate;
|
|
struct ieee80211_local *local = tx->local;
|
|
struct ieee80211_hw_mode *mode = tx->u.tx.mode;
|
|
|
|
erp = txrate->flags & IEEE80211_RATE_ERP;
|
|
|
|
/*
|
|
* data and mgmt (except PS Poll):
|
|
* - during CFP: 32768
|
|
* - during contention period:
|
|
* if addr1 is group address: 0
|
|
* if more fragments = 0 and addr1 is individual address: time to
|
|
* transmit one ACK plus SIFS
|
|
* if more fragments = 1 and addr1 is individual address: time to
|
|
* transmit next fragment plus 2 x ACK plus 3 x SIFS
|
|
*
|
|
* IEEE 802.11, 9.6:
|
|
* - control response frame (CTS or ACK) shall be transmitted using the
|
|
* same rate as the immediately previous frame in the frame exchange
|
|
* sequence, if this rate belongs to the PHY mandatory rates, or else
|
|
* at the highest possible rate belonging to the PHY rates in the
|
|
* BSSBasicRateSet
|
|
*/
|
|
|
|
if ((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) {
|
|
/* TODO: These control frames are not currently sent by
|
|
* 80211.o, but should they be implemented, this function
|
|
* needs to be updated to support duration field calculation.
|
|
*
|
|
* RTS: time needed to transmit pending data/mgmt frame plus
|
|
* one CTS frame plus one ACK frame plus 3 x SIFS
|
|
* CTS: duration of immediately previous RTS minus time
|
|
* required to transmit CTS and its SIFS
|
|
* ACK: 0 if immediately previous directed data/mgmt had
|
|
* more=0, with more=1 duration in ACK frame is duration
|
|
* from previous frame minus time needed to transmit ACK
|
|
* and its SIFS
|
|
* PS Poll: BIT(15) | BIT(14) | aid
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
/* data/mgmt */
|
|
if (0 /* FIX: data/mgmt during CFP */)
|
|
return 32768;
|
|
|
|
if (group_addr) /* Group address as the destination - no ACK */
|
|
return 0;
|
|
|
|
/* Individual destination address:
|
|
* IEEE 802.11, Ch. 9.6 (after IEEE 802.11g changes)
|
|
* CTS and ACK frames shall be transmitted using the highest rate in
|
|
* basic rate set that is less than or equal to the rate of the
|
|
* immediately previous frame and that is using the same modulation
|
|
* (CCK or OFDM). If no basic rate set matches with these requirements,
|
|
* the highest mandatory rate of the PHY that is less than or equal to
|
|
* the rate of the previous frame is used.
|
|
* Mandatory rates for IEEE 802.11g PHY: 1, 2, 5.5, 11, 6, 12, 24 Mbps
|
|
*/
|
|
rate = -1;
|
|
mrate = 10; /* use 1 Mbps if everything fails */
|
|
for (i = 0; i < mode->num_rates; i++) {
|
|
struct ieee80211_rate *r = &mode->rates[i];
|
|
if (r->rate > txrate->rate)
|
|
break;
|
|
|
|
if (IEEE80211_RATE_MODULATION(txrate->flags) !=
|
|
IEEE80211_RATE_MODULATION(r->flags))
|
|
continue;
|
|
|
|
if (r->flags & IEEE80211_RATE_BASIC)
|
|
rate = r->rate;
|
|
else if (r->flags & IEEE80211_RATE_MANDATORY)
|
|
mrate = r->rate;
|
|
}
|
|
if (rate == -1) {
|
|
/* No matching basic rate found; use highest suitable mandatory
|
|
* PHY rate */
|
|
rate = mrate;
|
|
}
|
|
|
|
/* Time needed to transmit ACK
|
|
* (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up
|
|
* to closest integer */
|
|
|
|
dur = ieee80211_frame_duration(local, 10, rate, erp,
|
|
local->short_preamble);
|
|
|
|
if (next_frag_len) {
|
|
/* Frame is fragmented: duration increases with time needed to
|
|
* transmit next fragment plus ACK and 2 x SIFS. */
|
|
dur *= 2; /* ACK + SIFS */
|
|
/* next fragment */
|
|
dur += ieee80211_frame_duration(local, next_frag_len,
|
|
txrate->rate, erp,
|
|
local->short_preamble);
|
|
}
|
|
|
|
return dur;
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
|
|
{
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
|
|
u16 dur;
|
|
struct ieee80211_tx_control *control = tx->u.tx.control;
|
|
struct ieee80211_hw_mode *mode = tx->u.tx.mode;
|
|
|
|
if (!is_multicast_ether_addr(hdr->addr1)) {
|
|
if (tx->skb->len + FCS_LEN > tx->local->rts_threshold &&
|
|
tx->local->rts_threshold < IEEE80211_MAX_RTS_THRESHOLD) {
|
|
control->flags |= IEEE80211_TXCTL_USE_RTS_CTS;
|
|
control->retry_limit =
|
|
tx->local->long_retry_limit;
|
|
} else {
|
|
control->retry_limit =
|
|
tx->local->short_retry_limit;
|
|
}
|
|
} else {
|
|
control->retry_limit = 1;
|
|
}
|
|
|
|
if (tx->fragmented) {
|
|
/* Do not use multiple retry rates when sending fragmented
|
|
* frames.
|
|
* TODO: The last fragment could still use multiple retry
|
|
* rates. */
|
|
control->alt_retry_rate = -1;
|
|
}
|
|
|
|
/* Use CTS protection for unicast frames sent using extended rates if
|
|
* there are associated non-ERP stations and RTS/CTS is not configured
|
|
* for the frame. */
|
|
if (mode->mode == MODE_IEEE80211G &&
|
|
(tx->u.tx.rate->flags & IEEE80211_RATE_ERP) &&
|
|
tx->u.tx.unicast && tx->sdata->use_protection &&
|
|
!(control->flags & IEEE80211_TXCTL_USE_RTS_CTS))
|
|
control->flags |= IEEE80211_TXCTL_USE_CTS_PROTECT;
|
|
|
|
/* Setup duration field for the first fragment of the frame. Duration
|
|
* for remaining fragments will be updated when they are being sent
|
|
* to low-level driver in ieee80211_tx(). */
|
|
dur = ieee80211_duration(tx, is_multicast_ether_addr(hdr->addr1),
|
|
tx->fragmented ? tx->u.tx.extra_frag[0]->len :
|
|
0);
|
|
hdr->duration_id = cpu_to_le16(dur);
|
|
|
|
if ((control->flags & IEEE80211_TXCTL_USE_RTS_CTS) ||
|
|
(control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)) {
|
|
struct ieee80211_rate *rate;
|
|
|
|
/* Do not use multiple retry rates when using RTS/CTS */
|
|
control->alt_retry_rate = -1;
|
|
|
|
/* Use min(data rate, max base rate) as CTS/RTS rate */
|
|
rate = tx->u.tx.rate;
|
|
while (rate > mode->rates &&
|
|
!(rate->flags & IEEE80211_RATE_BASIC))
|
|
rate--;
|
|
|
|
control->rts_cts_rate = rate->val;
|
|
control->rts_rate = rate;
|
|
}
|
|
|
|
if (tx->sta) {
|
|
tx->sta->tx_packets++;
|
|
tx->sta->tx_fragments++;
|
|
tx->sta->tx_bytes += tx->skb->len;
|
|
if (tx->u.tx.extra_frag) {
|
|
int i;
|
|
tx->sta->tx_fragments += tx->u.tx.num_extra_frag;
|
|
for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
|
|
tx->sta->tx_bytes +=
|
|
tx->u.tx.extra_frag[i]->len;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx)
|
|
{
|
|
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
|
struct sk_buff *skb = tx->skb;
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
|
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
|
|
u32 sta_flags;
|
|
|
|
if (unlikely(tx->local->sta_scanning != 0) &&
|
|
((tx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
|
|
(tx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PROBE_REQ))
|
|
return TXRX_DROP;
|
|
|
|
if (tx->u.tx.ps_buffered)
|
|
return TXRX_CONTINUE;
|
|
|
|
sta_flags = tx->sta ? tx->sta->flags : 0;
|
|
|
|
if (likely(tx->u.tx.unicast)) {
|
|
if (unlikely(!(sta_flags & WLAN_STA_ASSOC) &&
|
|
tx->sdata->type != IEEE80211_IF_TYPE_IBSS &&
|
|
(tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)) {
|
|
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
|
printk(KERN_DEBUG "%s: dropped data frame to not "
|
|
"associated station " MAC_FMT "\n",
|
|
tx->dev->name, MAC_ARG(hdr->addr1));
|
|
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
|
|
I802_DEBUG_INC(tx->local->tx_handlers_drop_not_assoc);
|
|
return TXRX_DROP;
|
|
}
|
|
} else {
|
|
if (unlikely((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
|
|
tx->local->num_sta == 0 &&
|
|
!tx->local->allow_broadcast_always &&
|
|
tx->sdata->type != IEEE80211_IF_TYPE_IBSS)) {
|
|
/*
|
|
* No associated STAs - no need to send multicast
|
|
* frames.
|
|
*/
|
|
return TXRX_DROP;
|
|
}
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
if (unlikely(!tx->u.tx.mgmt_interface && tx->sdata->ieee802_1x &&
|
|
!(sta_flags & WLAN_STA_AUTHORIZED))) {
|
|
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
|
printk(KERN_DEBUG "%s: dropped frame to " MAC_FMT
|
|
" (unauthorized port)\n", tx->dev->name,
|
|
MAC_ARG(hdr->addr1));
|
|
#endif
|
|
I802_DEBUG_INC(tx->local->tx_handlers_drop_unauth_port);
|
|
return TXRX_DROP;
|
|
}
|
|
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_tx_h_sequence(struct ieee80211_txrx_data *tx)
|
|
{
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
|
|
|
|
if (ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control)) >= 24)
|
|
ieee80211_include_sequence(tx->sdata, hdr);
|
|
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
/* This function is called whenever the AP is about to exceed the maximum limit
|
|
* of buffered frames for power saving STAs. This situation should not really
|
|
* happen often during normal operation, so dropping the oldest buffered packet
|
|
* from each queue should be OK to make some room for new frames. */
|
|
static void purge_old_ps_buffers(struct ieee80211_local *local)
|
|
{
|
|
int total = 0, purged = 0;
|
|
struct sk_buff *skb;
|
|
struct ieee80211_sub_if_data *sdata;
|
|
struct sta_info *sta;
|
|
|
|
read_lock(&local->sub_if_lock);
|
|
list_for_each_entry(sdata, &local->sub_if_list, list) {
|
|
struct ieee80211_if_ap *ap;
|
|
if (sdata->dev == local->mdev ||
|
|
sdata->type != IEEE80211_IF_TYPE_AP)
|
|
continue;
|
|
ap = &sdata->u.ap;
|
|
skb = skb_dequeue(&ap->ps_bc_buf);
|
|
if (skb) {
|
|
purged++;
|
|
dev_kfree_skb(skb);
|
|
}
|
|
total += skb_queue_len(&ap->ps_bc_buf);
|
|
}
|
|
read_unlock(&local->sub_if_lock);
|
|
|
|
spin_lock_bh(&local->sta_lock);
|
|
list_for_each_entry(sta, &local->sta_list, list) {
|
|
skb = skb_dequeue(&sta->ps_tx_buf);
|
|
if (skb) {
|
|
purged++;
|
|
dev_kfree_skb(skb);
|
|
}
|
|
total += skb_queue_len(&sta->ps_tx_buf);
|
|
}
|
|
spin_unlock_bh(&local->sta_lock);
|
|
|
|
local->total_ps_buffered = total;
|
|
printk(KERN_DEBUG "%s: PS buffers full - purged %d frames\n",
|
|
local->mdev->name, purged);
|
|
}
|
|
|
|
|
|
static inline ieee80211_txrx_result
|
|
ieee80211_tx_h_multicast_ps_buf(struct ieee80211_txrx_data *tx)
|
|
{
|
|
/* broadcast/multicast frame */
|
|
/* If any of the associated stations is in power save mode,
|
|
* the frame is buffered to be sent after DTIM beacon frame */
|
|
if ((tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING) &&
|
|
tx->sdata->type != IEEE80211_IF_TYPE_WDS &&
|
|
tx->sdata->bss && atomic_read(&tx->sdata->bss->num_sta_ps) &&
|
|
!(tx->fc & IEEE80211_FCTL_ORDER)) {
|
|
if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
|
|
purge_old_ps_buffers(tx->local);
|
|
if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >=
|
|
AP_MAX_BC_BUFFER) {
|
|
if (net_ratelimit()) {
|
|
printk(KERN_DEBUG "%s: BC TX buffer full - "
|
|
"dropping the oldest frame\n",
|
|
tx->dev->name);
|
|
}
|
|
dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf));
|
|
} else
|
|
tx->local->total_ps_buffered++;
|
|
skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb);
|
|
return TXRX_QUEUED;
|
|
}
|
|
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
|
|
static inline ieee80211_txrx_result
|
|
ieee80211_tx_h_unicast_ps_buf(struct ieee80211_txrx_data *tx)
|
|
{
|
|
struct sta_info *sta = tx->sta;
|
|
|
|
if (unlikely(!sta ||
|
|
((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT &&
|
|
(tx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP)))
|
|
return TXRX_CONTINUE;
|
|
|
|
if (unlikely((sta->flags & WLAN_STA_PS) && !sta->pspoll)) {
|
|
struct ieee80211_tx_packet_data *pkt_data;
|
|
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
|
|
printk(KERN_DEBUG "STA " MAC_FMT " aid %d: PS buffer (entries "
|
|
"before %d)\n",
|
|
MAC_ARG(sta->addr), sta->aid,
|
|
skb_queue_len(&sta->ps_tx_buf));
|
|
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
|
|
sta->flags |= WLAN_STA_TIM;
|
|
if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
|
|
purge_old_ps_buffers(tx->local);
|
|
if (skb_queue_len(&sta->ps_tx_buf) >= STA_MAX_TX_BUFFER) {
|
|
struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf);
|
|
if (net_ratelimit()) {
|
|
printk(KERN_DEBUG "%s: STA " MAC_FMT " TX "
|
|
"buffer full - dropping oldest frame\n",
|
|
tx->dev->name, MAC_ARG(sta->addr));
|
|
}
|
|
dev_kfree_skb(old);
|
|
} else
|
|
tx->local->total_ps_buffered++;
|
|
/* Queue frame to be sent after STA sends an PS Poll frame */
|
|
if (skb_queue_empty(&sta->ps_tx_buf)) {
|
|
if (tx->local->ops->set_tim)
|
|
tx->local->ops->set_tim(local_to_hw(tx->local),
|
|
sta->aid, 1);
|
|
if (tx->sdata->bss)
|
|
bss_tim_set(tx->local, tx->sdata->bss, sta->aid);
|
|
}
|
|
pkt_data = (struct ieee80211_tx_packet_data *)tx->skb->cb;
|
|
pkt_data->jiffies = jiffies;
|
|
skb_queue_tail(&sta->ps_tx_buf, tx->skb);
|
|
return TXRX_QUEUED;
|
|
}
|
|
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
|
|
else if (unlikely(sta->flags & WLAN_STA_PS)) {
|
|
printk(KERN_DEBUG "%s: STA " MAC_FMT " in PS mode, but pspoll "
|
|
"set -> send frame\n", tx->dev->name,
|
|
MAC_ARG(sta->addr));
|
|
}
|
|
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
|
|
sta->pspoll = 0;
|
|
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_tx_h_ps_buf(struct ieee80211_txrx_data *tx)
|
|
{
|
|
if (unlikely(tx->u.tx.ps_buffered))
|
|
return TXRX_CONTINUE;
|
|
|
|
if (tx->u.tx.unicast)
|
|
return ieee80211_tx_h_unicast_ps_buf(tx);
|
|
else
|
|
return ieee80211_tx_h_multicast_ps_buf(tx);
|
|
}
|
|
|
|
|
|
/*
|
|
* deal with packet injection down monitor interface
|
|
* with Radiotap Header -- only called for monitor mode interface
|
|
*/
|
|
|
|
static ieee80211_txrx_result
|
|
__ieee80211_parse_tx_radiotap(
|
|
struct ieee80211_txrx_data *tx,
|
|
struct sk_buff *skb, struct ieee80211_tx_control *control)
|
|
{
|
|
/*
|
|
* this is the moment to interpret and discard the radiotap header that
|
|
* must be at the start of the packet injected in Monitor mode
|
|
*
|
|
* Need to take some care with endian-ness since radiotap
|
|
* args are little-endian
|
|
*/
|
|
|
|
struct ieee80211_radiotap_iterator iterator;
|
|
struct ieee80211_radiotap_header *rthdr =
|
|
(struct ieee80211_radiotap_header *) skb->data;
|
|
struct ieee80211_hw_mode *mode = tx->local->hw.conf.mode;
|
|
int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
|
|
|
|
/*
|
|
* default control situation for all injected packets
|
|
* FIXME: this does not suit all usage cases, expand to allow control
|
|
*/
|
|
|
|
control->retry_limit = 1; /* no retry */
|
|
control->key_idx = -1; /* no encryption key */
|
|
control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS |
|
|
IEEE80211_TXCTL_USE_CTS_PROTECT);
|
|
control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT |
|
|
IEEE80211_TXCTL_NO_ACK;
|
|
control->antenna_sel_tx = 0; /* default to default antenna */
|
|
|
|
/*
|
|
* for every radiotap entry that is present
|
|
* (ieee80211_radiotap_iterator_next returns -ENOENT when no more
|
|
* entries present, or -EINVAL on error)
|
|
*/
|
|
|
|
while (!ret) {
|
|
int i, target_rate;
|
|
|
|
ret = ieee80211_radiotap_iterator_next(&iterator);
|
|
|
|
if (ret)
|
|
continue;
|
|
|
|
/* see if this argument is something we can use */
|
|
switch (iterator.this_arg_index) {
|
|
/*
|
|
* You must take care when dereferencing iterator.this_arg
|
|
* for multibyte types... the pointer is not aligned. Use
|
|
* get_unaligned((type *)iterator.this_arg) to dereference
|
|
* iterator.this_arg for type "type" safely on all arches.
|
|
*/
|
|
case IEEE80211_RADIOTAP_RATE:
|
|
/*
|
|
* radiotap rate u8 is in 500kbps units eg, 0x02=1Mbps
|
|
* ieee80211 rate int is in 100kbps units eg, 0x0a=1Mbps
|
|
*/
|
|
target_rate = (*iterator.this_arg) * 5;
|
|
for (i = 0; i < mode->num_rates; i++) {
|
|
struct ieee80211_rate *r = &mode->rates[i];
|
|
|
|
if (r->rate > target_rate)
|
|
continue;
|
|
|
|
control->rate = r;
|
|
|
|
if (r->flags & IEEE80211_RATE_PREAMBLE2)
|
|
control->tx_rate = r->val2;
|
|
else
|
|
control->tx_rate = r->val;
|
|
|
|
/* end on exact match */
|
|
if (r->rate == target_rate)
|
|
i = mode->num_rates;
|
|
}
|
|
break;
|
|
|
|
case IEEE80211_RADIOTAP_ANTENNA:
|
|
/*
|
|
* radiotap uses 0 for 1st ant, mac80211 is 1 for
|
|
* 1st ant
|
|
*/
|
|
control->antenna_sel_tx = (*iterator.this_arg) + 1;
|
|
break;
|
|
|
|
case IEEE80211_RADIOTAP_DBM_TX_POWER:
|
|
control->power_level = *iterator.this_arg;
|
|
break;
|
|
|
|
case IEEE80211_RADIOTAP_FLAGS:
|
|
if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FCS) {
|
|
/*
|
|
* this indicates that the skb we have been
|
|
* handed has the 32-bit FCS CRC at the end...
|
|
* we should react to that by snipping it off
|
|
* because it will be recomputed and added
|
|
* on transmission
|
|
*/
|
|
if (skb->len < (iterator.max_length + FCS_LEN))
|
|
return TXRX_DROP;
|
|
|
|
skb_trim(skb, skb->len - FCS_LEN);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */
|
|
return TXRX_DROP;
|
|
|
|
/*
|
|
* remove the radiotap header
|
|
* iterator->max_length was sanity-checked against
|
|
* skb->len by iterator init
|
|
*/
|
|
skb_pull(skb, iterator.max_length);
|
|
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result inline
|
|
__ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
|
|
struct sk_buff *skb,
|
|
struct net_device *dev,
|
|
struct ieee80211_tx_control *control)
|
|
{
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
|
struct ieee80211_sub_if_data *sdata;
|
|
ieee80211_txrx_result res = TXRX_CONTINUE;
|
|
|
|
int hdrlen;
|
|
|
|
memset(tx, 0, sizeof(*tx));
|
|
tx->skb = skb;
|
|
tx->dev = dev; /* use original interface */
|
|
tx->local = local;
|
|
tx->sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
tx->sta = sta_info_get(local, hdr->addr1);
|
|
tx->fc = le16_to_cpu(hdr->frame_control);
|
|
|
|
/*
|
|
* set defaults for things that can be set by
|
|
* injected radiotap headers
|
|
*/
|
|
control->power_level = local->hw.conf.power_level;
|
|
control->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
|
|
if (local->sta_antenna_sel != STA_ANTENNA_SEL_AUTO && tx->sta)
|
|
control->antenna_sel_tx = tx->sta->antenna_sel_tx;
|
|
|
|
/* process and remove the injection radiotap header */
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
if (unlikely(sdata->type == IEEE80211_IF_TYPE_MNTR)) {
|
|
if (__ieee80211_parse_tx_radiotap(tx, skb, control) ==
|
|
TXRX_DROP) {
|
|
return TXRX_DROP;
|
|
}
|
|
/*
|
|
* we removed the radiotap header after this point,
|
|
* we filled control with what we could use
|
|
* set to the actual ieee header now
|
|
*/
|
|
hdr = (struct ieee80211_hdr *) skb->data;
|
|
res = TXRX_QUEUED; /* indication it was monitor packet */
|
|
}
|
|
|
|
tx->u.tx.control = control;
|
|
tx->u.tx.unicast = !is_multicast_ether_addr(hdr->addr1);
|
|
if (is_multicast_ether_addr(hdr->addr1))
|
|
control->flags |= IEEE80211_TXCTL_NO_ACK;
|
|
else
|
|
control->flags &= ~IEEE80211_TXCTL_NO_ACK;
|
|
tx->fragmented = local->fragmentation_threshold <
|
|
IEEE80211_MAX_FRAG_THRESHOLD && tx->u.tx.unicast &&
|
|
skb->len + FCS_LEN > local->fragmentation_threshold &&
|
|
(!local->ops->set_frag_threshold);
|
|
if (!tx->sta)
|
|
control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK;
|
|
else if (tx->sta->clear_dst_mask) {
|
|
control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK;
|
|
tx->sta->clear_dst_mask = 0;
|
|
}
|
|
hdrlen = ieee80211_get_hdrlen(tx->fc);
|
|
if (skb->len > hdrlen + sizeof(rfc1042_header) + 2) {
|
|
u8 *pos = &skb->data[hdrlen + sizeof(rfc1042_header)];
|
|
tx->ethertype = (pos[0] << 8) | pos[1];
|
|
}
|
|
control->flags |= IEEE80211_TXCTL_FIRST_FRAGMENT;
|
|
|
|
return res;
|
|
}
|
|
|
|
static int inline is_ieee80211_device(struct net_device *dev,
|
|
struct net_device *master)
|
|
{
|
|
return (wdev_priv(dev->ieee80211_ptr) ==
|
|
wdev_priv(master->ieee80211_ptr));
|
|
}
|
|
|
|
/* Device in tx->dev has a reference added; use dev_put(tx->dev) when
|
|
* finished with it. */
|
|
static int inline ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
|
|
struct sk_buff *skb,
|
|
struct net_device *mdev,
|
|
struct ieee80211_tx_control *control)
|
|
{
|
|
struct ieee80211_tx_packet_data *pkt_data;
|
|
struct net_device *dev;
|
|
|
|
pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
|
|
dev = dev_get_by_index(pkt_data->ifindex);
|
|
if (unlikely(dev && !is_ieee80211_device(dev, mdev))) {
|
|
dev_put(dev);
|
|
dev = NULL;
|
|
}
|
|
if (unlikely(!dev))
|
|
return -ENODEV;
|
|
__ieee80211_tx_prepare(tx, skb, dev, control);
|
|
return 0;
|
|
}
|
|
|
|
static inline int __ieee80211_queue_stopped(const struct ieee80211_local *local,
|
|
int queue)
|
|
{
|
|
return test_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
|
|
}
|
|
|
|
static inline int __ieee80211_queue_pending(const struct ieee80211_local *local,
|
|
int queue)
|
|
{
|
|
return test_bit(IEEE80211_LINK_STATE_PENDING, &local->state[queue]);
|
|
}
|
|
|
|
#define IEEE80211_TX_OK 0
|
|
#define IEEE80211_TX_AGAIN 1
|
|
#define IEEE80211_TX_FRAG_AGAIN 2
|
|
|
|
static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
|
|
struct ieee80211_txrx_data *tx)
|
|
{
|
|
struct ieee80211_tx_control *control = tx->u.tx.control;
|
|
int ret, i;
|
|
|
|
if (!ieee80211_qdisc_installed(local->mdev) &&
|
|
__ieee80211_queue_stopped(local, 0)) {
|
|
netif_stop_queue(local->mdev);
|
|
return IEEE80211_TX_AGAIN;
|
|
}
|
|
if (skb) {
|
|
ieee80211_dump_frame(local->mdev->name, "TX to low-level driver", skb);
|
|
ret = local->ops->tx(local_to_hw(local), skb, control);
|
|
if (ret)
|
|
return IEEE80211_TX_AGAIN;
|
|
local->mdev->trans_start = jiffies;
|
|
ieee80211_led_tx(local, 1);
|
|
}
|
|
if (tx->u.tx.extra_frag) {
|
|
control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS |
|
|
IEEE80211_TXCTL_USE_CTS_PROTECT |
|
|
IEEE80211_TXCTL_CLEAR_DST_MASK |
|
|
IEEE80211_TXCTL_FIRST_FRAGMENT);
|
|
for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
|
|
if (!tx->u.tx.extra_frag[i])
|
|
continue;
|
|
if (__ieee80211_queue_stopped(local, control->queue))
|
|
return IEEE80211_TX_FRAG_AGAIN;
|
|
if (i == tx->u.tx.num_extra_frag) {
|
|
control->tx_rate = tx->u.tx.last_frag_hwrate;
|
|
control->rate = tx->u.tx.last_frag_rate;
|
|
if (tx->u.tx.probe_last_frag)
|
|
control->flags |=
|
|
IEEE80211_TXCTL_RATE_CTRL_PROBE;
|
|
else
|
|
control->flags &=
|
|
~IEEE80211_TXCTL_RATE_CTRL_PROBE;
|
|
}
|
|
|
|
ieee80211_dump_frame(local->mdev->name,
|
|
"TX to low-level driver",
|
|
tx->u.tx.extra_frag[i]);
|
|
ret = local->ops->tx(local_to_hw(local),
|
|
tx->u.tx.extra_frag[i],
|
|
control);
|
|
if (ret)
|
|
return IEEE80211_TX_FRAG_AGAIN;
|
|
local->mdev->trans_start = jiffies;
|
|
ieee80211_led_tx(local, 1);
|
|
tx->u.tx.extra_frag[i] = NULL;
|
|
}
|
|
kfree(tx->u.tx.extra_frag);
|
|
tx->u.tx.extra_frag = NULL;
|
|
}
|
|
return IEEE80211_TX_OK;
|
|
}
|
|
|
|
static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
|
|
struct ieee80211_tx_control *control, int mgmt)
|
|
{
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
struct sta_info *sta;
|
|
ieee80211_tx_handler *handler;
|
|
struct ieee80211_txrx_data tx;
|
|
ieee80211_txrx_result res = TXRX_DROP, res_prepare;
|
|
int ret, i;
|
|
|
|
WARN_ON(__ieee80211_queue_pending(local, control->queue));
|
|
|
|
if (unlikely(skb->len < 10)) {
|
|
dev_kfree_skb(skb);
|
|
return 0;
|
|
}
|
|
|
|
res_prepare = __ieee80211_tx_prepare(&tx, skb, dev, control);
|
|
|
|
if (res_prepare == TXRX_DROP) {
|
|
dev_kfree_skb(skb);
|
|
return 0;
|
|
}
|
|
|
|
sta = tx.sta;
|
|
tx.u.tx.mgmt_interface = mgmt;
|
|
tx.u.tx.mode = local->hw.conf.mode;
|
|
|
|
if (res_prepare == TXRX_QUEUED) { /* if it was an injected packet */
|
|
res = TXRX_CONTINUE;
|
|
} else {
|
|
for (handler = local->tx_handlers; *handler != NULL;
|
|
handler++) {
|
|
res = (*handler)(&tx);
|
|
if (res != TXRX_CONTINUE)
|
|
break;
|
|
}
|
|
}
|
|
|
|
skb = tx.skb; /* handlers are allowed to change skb */
|
|
|
|
if (sta)
|
|
sta_info_put(sta);
|
|
|
|
if (unlikely(res == TXRX_DROP)) {
|
|
I802_DEBUG_INC(local->tx_handlers_drop);
|
|
goto drop;
|
|
}
|
|
|
|
if (unlikely(res == TXRX_QUEUED)) {
|
|
I802_DEBUG_INC(local->tx_handlers_queued);
|
|
return 0;
|
|
}
|
|
|
|
if (tx.u.tx.extra_frag) {
|
|
for (i = 0; i < tx.u.tx.num_extra_frag; i++) {
|
|
int next_len, dur;
|
|
struct ieee80211_hdr *hdr =
|
|
(struct ieee80211_hdr *)
|
|
tx.u.tx.extra_frag[i]->data;
|
|
|
|
if (i + 1 < tx.u.tx.num_extra_frag) {
|
|
next_len = tx.u.tx.extra_frag[i + 1]->len;
|
|
} else {
|
|
next_len = 0;
|
|
tx.u.tx.rate = tx.u.tx.last_frag_rate;
|
|
tx.u.tx.last_frag_hwrate = tx.u.tx.rate->val;
|
|
}
|
|
dur = ieee80211_duration(&tx, 0, next_len);
|
|
hdr->duration_id = cpu_to_le16(dur);
|
|
}
|
|
}
|
|
|
|
retry:
|
|
ret = __ieee80211_tx(local, skb, &tx);
|
|
if (ret) {
|
|
struct ieee80211_tx_stored_packet *store =
|
|
&local->pending_packet[control->queue];
|
|
|
|
if (ret == IEEE80211_TX_FRAG_AGAIN)
|
|
skb = NULL;
|
|
set_bit(IEEE80211_LINK_STATE_PENDING,
|
|
&local->state[control->queue]);
|
|
smp_mb();
|
|
/* When the driver gets out of buffers during sending of
|
|
* fragments and calls ieee80211_stop_queue, there is
|
|
* a small window between IEEE80211_LINK_STATE_XOFF and
|
|
* IEEE80211_LINK_STATE_PENDING flags are set. If a buffer
|
|
* gets available in that window (i.e. driver calls
|
|
* ieee80211_wake_queue), we would end up with ieee80211_tx
|
|
* called with IEEE80211_LINK_STATE_PENDING. Prevent this by
|
|
* continuing transmitting here when that situation is
|
|
* possible to have happened. */
|
|
if (!__ieee80211_queue_stopped(local, control->queue)) {
|
|
clear_bit(IEEE80211_LINK_STATE_PENDING,
|
|
&local->state[control->queue]);
|
|
goto retry;
|
|
}
|
|
memcpy(&store->control, control,
|
|
sizeof(struct ieee80211_tx_control));
|
|
store->skb = skb;
|
|
store->extra_frag = tx.u.tx.extra_frag;
|
|
store->num_extra_frag = tx.u.tx.num_extra_frag;
|
|
store->last_frag_hwrate = tx.u.tx.last_frag_hwrate;
|
|
store->last_frag_rate = tx.u.tx.last_frag_rate;
|
|
store->last_frag_rate_ctrl_probe = tx.u.tx.probe_last_frag;
|
|
}
|
|
return 0;
|
|
|
|
drop:
|
|
if (skb)
|
|
dev_kfree_skb(skb);
|
|
for (i = 0; i < tx.u.tx.num_extra_frag; i++)
|
|
if (tx.u.tx.extra_frag[i])
|
|
dev_kfree_skb(tx.u.tx.extra_frag[i]);
|
|
kfree(tx.u.tx.extra_frag);
|
|
return 0;
|
|
}
|
|
|
|
static void ieee80211_tx_pending(unsigned long data)
|
|
{
|
|
struct ieee80211_local *local = (struct ieee80211_local *)data;
|
|
struct net_device *dev = local->mdev;
|
|
struct ieee80211_tx_stored_packet *store;
|
|
struct ieee80211_txrx_data tx;
|
|
int i, ret, reschedule = 0;
|
|
|
|
netif_tx_lock_bh(dev);
|
|
for (i = 0; i < local->hw.queues; i++) {
|
|
if (__ieee80211_queue_stopped(local, i))
|
|
continue;
|
|
if (!__ieee80211_queue_pending(local, i)) {
|
|
reschedule = 1;
|
|
continue;
|
|
}
|
|
store = &local->pending_packet[i];
|
|
tx.u.tx.control = &store->control;
|
|
tx.u.tx.extra_frag = store->extra_frag;
|
|
tx.u.tx.num_extra_frag = store->num_extra_frag;
|
|
tx.u.tx.last_frag_hwrate = store->last_frag_hwrate;
|
|
tx.u.tx.last_frag_rate = store->last_frag_rate;
|
|
tx.u.tx.probe_last_frag = store->last_frag_rate_ctrl_probe;
|
|
ret = __ieee80211_tx(local, store->skb, &tx);
|
|
if (ret) {
|
|
if (ret == IEEE80211_TX_FRAG_AGAIN)
|
|
store->skb = NULL;
|
|
} else {
|
|
clear_bit(IEEE80211_LINK_STATE_PENDING,
|
|
&local->state[i]);
|
|
reschedule = 1;
|
|
}
|
|
}
|
|
netif_tx_unlock_bh(dev);
|
|
if (reschedule) {
|
|
if (!ieee80211_qdisc_installed(dev)) {
|
|
if (!__ieee80211_queue_stopped(local, 0))
|
|
netif_wake_queue(dev);
|
|
} else
|
|
netif_schedule(dev);
|
|
}
|
|
}
|
|
|
|
static void ieee80211_clear_tx_pending(struct ieee80211_local *local)
|
|
{
|
|
int i, j;
|
|
struct ieee80211_tx_stored_packet *store;
|
|
|
|
for (i = 0; i < local->hw.queues; i++) {
|
|
if (!__ieee80211_queue_pending(local, i))
|
|
continue;
|
|
store = &local->pending_packet[i];
|
|
kfree_skb(store->skb);
|
|
for (j = 0; j < store->num_extra_frag; j++)
|
|
kfree_skb(store->extra_frag[j]);
|
|
kfree(store->extra_frag);
|
|
clear_bit(IEEE80211_LINK_STATE_PENDING, &local->state[i]);
|
|
}
|
|
}
|
|
|
|
static int ieee80211_master_start_xmit(struct sk_buff *skb,
|
|
struct net_device *dev)
|
|
{
|
|
struct ieee80211_tx_control control;
|
|
struct ieee80211_tx_packet_data *pkt_data;
|
|
struct net_device *odev = NULL;
|
|
struct ieee80211_sub_if_data *osdata;
|
|
int headroom;
|
|
int ret;
|
|
|
|
/*
|
|
* copy control out of the skb so other people can use skb->cb
|
|
*/
|
|
pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
|
|
memset(&control, 0, sizeof(struct ieee80211_tx_control));
|
|
|
|
if (pkt_data->ifindex)
|
|
odev = dev_get_by_index(pkt_data->ifindex);
|
|
if (unlikely(odev && !is_ieee80211_device(odev, dev))) {
|
|
dev_put(odev);
|
|
odev = NULL;
|
|
}
|
|
if (unlikely(!odev)) {
|
|
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
|
printk(KERN_DEBUG "%s: Discarded packet with nonexistent "
|
|
"originating device\n", dev->name);
|
|
#endif
|
|
dev_kfree_skb(skb);
|
|
return 0;
|
|
}
|
|
osdata = IEEE80211_DEV_TO_SUB_IF(odev);
|
|
|
|
headroom = osdata->local->tx_headroom + IEEE80211_ENCRYPT_HEADROOM;
|
|
if (skb_headroom(skb) < headroom) {
|
|
if (pskb_expand_head(skb, headroom, 0, GFP_ATOMIC)) {
|
|
dev_kfree_skb(skb);
|
|
dev_put(odev);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
control.ifindex = odev->ifindex;
|
|
control.type = osdata->type;
|
|
if (pkt_data->req_tx_status)
|
|
control.flags |= IEEE80211_TXCTL_REQ_TX_STATUS;
|
|
if (pkt_data->do_not_encrypt)
|
|
control.flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
|
|
if (pkt_data->requeue)
|
|
control.flags |= IEEE80211_TXCTL_REQUEUE;
|
|
control.queue = pkt_data->queue;
|
|
|
|
ret = ieee80211_tx(odev, skb, &control,
|
|
control.type == IEEE80211_IF_TYPE_MGMT);
|
|
dev_put(odev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int ieee80211_monitor_start_xmit(struct sk_buff *skb,
|
|
struct net_device *dev)
|
|
{
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
struct ieee80211_tx_packet_data *pkt_data;
|
|
struct ieee80211_radiotap_header *prthdr =
|
|
(struct ieee80211_radiotap_header *)skb->data;
|
|
u16 len;
|
|
|
|
/*
|
|
* there must be a radiotap header at the
|
|
* start in this case
|
|
*/
|
|
if (unlikely(prthdr->it_version)) {
|
|
/* only version 0 is supported */
|
|
dev_kfree_skb(skb);
|
|
return NETDEV_TX_OK;
|
|
}
|
|
|
|
skb->dev = local->mdev;
|
|
|
|
pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
|
|
memset(pkt_data, 0, sizeof(*pkt_data));
|
|
pkt_data->ifindex = dev->ifindex;
|
|
pkt_data->mgmt_iface = 0;
|
|
pkt_data->do_not_encrypt = 1;
|
|
|
|
/* above needed because we set skb device to master */
|
|
|
|
/*
|
|
* fix up the pointers accounting for the radiotap
|
|
* header still being in there. We are being given
|
|
* a precooked IEEE80211 header so no need for
|
|
* normal processing
|
|
*/
|
|
len = le16_to_cpu(get_unaligned(&prthdr->it_len));
|
|
skb_set_mac_header(skb, len);
|
|
skb_set_network_header(skb, len + sizeof(struct ieee80211_hdr));
|
|
skb_set_transport_header(skb, len + sizeof(struct ieee80211_hdr));
|
|
|
|
/*
|
|
* pass the radiotap header up to
|
|
* the next stage intact
|
|
*/
|
|
dev_queue_xmit(skb);
|
|
|
|
return NETDEV_TX_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
* ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type
|
|
* subinterfaces (wlan#, WDS, and VLAN interfaces)
|
|
* @skb: packet to be sent
|
|
* @dev: incoming interface
|
|
*
|
|
* Returns: 0 on success (and frees skb in this case) or 1 on failure (skb will
|
|
* not be freed, and caller is responsible for either retrying later or freeing
|
|
* skb).
|
|
*
|
|
* This function takes in an Ethernet header and encapsulates it with suitable
|
|
* IEEE 802.11 header based on which interface the packet is coming in. The
|
|
* encapsulated packet will then be passed to master interface, wlan#.11, for
|
|
* transmission (through low-level driver).
|
|
*/
|
|
int ieee80211_subif_start_xmit(struct sk_buff *skb,
|
|
struct net_device *dev)
|
|
{
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
struct ieee80211_tx_packet_data *pkt_data;
|
|
struct ieee80211_sub_if_data *sdata;
|
|
int ret = 1, head_need;
|
|
u16 ethertype, hdrlen, fc;
|
|
struct ieee80211_hdr hdr;
|
|
const u8 *encaps_data;
|
|
int encaps_len, skip_header_bytes;
|
|
int nh_pos, h_pos, no_encrypt = 0;
|
|
struct sta_info *sta;
|
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
if (unlikely(skb->len < ETH_HLEN)) {
|
|
printk(KERN_DEBUG "%s: short skb (len=%d)\n",
|
|
dev->name, skb->len);
|
|
ret = 0;
|
|
goto fail;
|
|
}
|
|
|
|
nh_pos = skb_network_header(skb) - skb->data;
|
|
h_pos = skb_transport_header(skb) - skb->data;
|
|
|
|
/* convert Ethernet header to proper 802.11 header (based on
|
|
* operation mode) */
|
|
ethertype = (skb->data[12] << 8) | skb->data[13];
|
|
/* TODO: handling for 802.1x authorized/unauthorized port */
|
|
fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA;
|
|
|
|
if (likely(sdata->type == IEEE80211_IF_TYPE_AP ||
|
|
sdata->type == IEEE80211_IF_TYPE_VLAN)) {
|
|
fc |= IEEE80211_FCTL_FROMDS;
|
|
/* DA BSSID SA */
|
|
memcpy(hdr.addr1, skb->data, ETH_ALEN);
|
|
memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
|
|
memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
|
|
hdrlen = 24;
|
|
} else if (sdata->type == IEEE80211_IF_TYPE_WDS) {
|
|
fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS;
|
|
/* RA TA DA SA */
|
|
memcpy(hdr.addr1, sdata->u.wds.remote_addr, ETH_ALEN);
|
|
memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
|
|
memcpy(hdr.addr3, skb->data, ETH_ALEN);
|
|
memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
|
|
hdrlen = 30;
|
|
} else if (sdata->type == IEEE80211_IF_TYPE_STA) {
|
|
fc |= IEEE80211_FCTL_TODS;
|
|
/* BSSID SA DA */
|
|
memcpy(hdr.addr1, sdata->u.sta.bssid, ETH_ALEN);
|
|
memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
|
|
memcpy(hdr.addr3, skb->data, ETH_ALEN);
|
|
hdrlen = 24;
|
|
} else if (sdata->type == IEEE80211_IF_TYPE_IBSS) {
|
|
/* DA SA BSSID */
|
|
memcpy(hdr.addr1, skb->data, ETH_ALEN);
|
|
memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
|
|
memcpy(hdr.addr3, sdata->u.sta.bssid, ETH_ALEN);
|
|
hdrlen = 24;
|
|
} else {
|
|
ret = 0;
|
|
goto fail;
|
|
}
|
|
|
|
/* receiver is QoS enabled, use a QoS type frame */
|
|
sta = sta_info_get(local, hdr.addr1);
|
|
if (sta) {
|
|
if (sta->flags & WLAN_STA_WME) {
|
|
fc |= IEEE80211_STYPE_QOS_DATA;
|
|
hdrlen += 2;
|
|
}
|
|
sta_info_put(sta);
|
|
}
|
|
|
|
hdr.frame_control = cpu_to_le16(fc);
|
|
hdr.duration_id = 0;
|
|
hdr.seq_ctrl = 0;
|
|
|
|
skip_header_bytes = ETH_HLEN;
|
|
if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) {
|
|
encaps_data = bridge_tunnel_header;
|
|
encaps_len = sizeof(bridge_tunnel_header);
|
|
skip_header_bytes -= 2;
|
|
} else if (ethertype >= 0x600) {
|
|
encaps_data = rfc1042_header;
|
|
encaps_len = sizeof(rfc1042_header);
|
|
skip_header_bytes -= 2;
|
|
} else {
|
|
encaps_data = NULL;
|
|
encaps_len = 0;
|
|
}
|
|
|
|
skb_pull(skb, skip_header_bytes);
|
|
nh_pos -= skip_header_bytes;
|
|
h_pos -= skip_header_bytes;
|
|
|
|
/* TODO: implement support for fragments so that there is no need to
|
|
* reallocate and copy payload; it might be enough to support one
|
|
* extra fragment that would be copied in the beginning of the frame
|
|
* data.. anyway, it would be nice to include this into skb structure
|
|
* somehow
|
|
*
|
|
* There are few options for this:
|
|
* use skb->cb as an extra space for 802.11 header
|
|
* allocate new buffer if not enough headroom
|
|
* make sure that there is enough headroom in every skb by increasing
|
|
* build in headroom in __dev_alloc_skb() (linux/skbuff.h) and
|
|
* alloc_skb() (net/core/skbuff.c)
|
|
*/
|
|
head_need = hdrlen + encaps_len + local->tx_headroom;
|
|
head_need -= skb_headroom(skb);
|
|
|
|
/* We are going to modify skb data, so make a copy of it if happens to
|
|
* be cloned. This could happen, e.g., with Linux bridge code passing
|
|
* us broadcast frames. */
|
|
|
|
if (head_need > 0 || skb_cloned(skb)) {
|
|
#if 0
|
|
printk(KERN_DEBUG "%s: need to reallocate buffer for %d bytes "
|
|
"of headroom\n", dev->name, head_need);
|
|
#endif
|
|
|
|
if (skb_cloned(skb))
|
|
I802_DEBUG_INC(local->tx_expand_skb_head_cloned);
|
|
else
|
|
I802_DEBUG_INC(local->tx_expand_skb_head);
|
|
/* Since we have to reallocate the buffer, make sure that there
|
|
* is enough room for possible WEP IV/ICV and TKIP (8 bytes
|
|
* before payload and 12 after). */
|
|
if (pskb_expand_head(skb, (head_need > 0 ? head_need + 8 : 8),
|
|
12, GFP_ATOMIC)) {
|
|
printk(KERN_DEBUG "%s: failed to reallocate TX buffer"
|
|
"\n", dev->name);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
if (encaps_data) {
|
|
memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len);
|
|
nh_pos += encaps_len;
|
|
h_pos += encaps_len;
|
|
}
|
|
memcpy(skb_push(skb, hdrlen), &hdr, hdrlen);
|
|
nh_pos += hdrlen;
|
|
h_pos += hdrlen;
|
|
|
|
pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
|
|
memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
|
|
pkt_data->ifindex = dev->ifindex;
|
|
pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT);
|
|
pkt_data->do_not_encrypt = no_encrypt;
|
|
|
|
skb->dev = local->mdev;
|
|
sdata->stats.tx_packets++;
|
|
sdata->stats.tx_bytes += skb->len;
|
|
|
|
/* Update skb pointers to various headers since this modified frame
|
|
* is going to go through Linux networking code that may potentially
|
|
* need things like pointer to IP header. */
|
|
skb_set_mac_header(skb, 0);
|
|
skb_set_network_header(skb, nh_pos);
|
|
skb_set_transport_header(skb, h_pos);
|
|
|
|
dev->trans_start = jiffies;
|
|
dev_queue_xmit(skb);
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
if (!ret)
|
|
dev_kfree_skb(skb);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* This is the transmit routine for the 802.11 type interfaces
|
|
* called by upper layers of the linux networking
|
|
* stack when it has a frame to transmit
|
|
*/
|
|
static int
|
|
ieee80211_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
{
|
|
struct ieee80211_sub_if_data *sdata;
|
|
struct ieee80211_tx_packet_data *pkt_data;
|
|
struct ieee80211_hdr *hdr;
|
|
u16 fc;
|
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
if (skb->len < 10) {
|
|
dev_kfree_skb(skb);
|
|
return 0;
|
|
}
|
|
|
|
if (skb_headroom(skb) < sdata->local->tx_headroom) {
|
|
if (pskb_expand_head(skb, sdata->local->tx_headroom,
|
|
0, GFP_ATOMIC)) {
|
|
dev_kfree_skb(skb);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
hdr = (struct ieee80211_hdr *) skb->data;
|
|
fc = le16_to_cpu(hdr->frame_control);
|
|
|
|
pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
|
|
memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
|
|
pkt_data->ifindex = sdata->dev->ifindex;
|
|
pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT);
|
|
|
|
skb->priority = 20; /* use hardcoded priority for mgmt TX queue */
|
|
skb->dev = sdata->local->mdev;
|
|
|
|
/*
|
|
* We're using the protocol field of the the frame control header
|
|
* to request TX callback for hostapd. BIT(1) is checked.
|
|
*/
|
|
if ((fc & BIT(1)) == BIT(1)) {
|
|
pkt_data->req_tx_status = 1;
|
|
fc &= ~BIT(1);
|
|
hdr->frame_control = cpu_to_le16(fc);
|
|
}
|
|
|
|
pkt_data->do_not_encrypt = !(fc & IEEE80211_FCTL_PROTECTED);
|
|
|
|
sdata->stats.tx_packets++;
|
|
sdata->stats.tx_bytes += skb->len;
|
|
|
|
dev_queue_xmit(skb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
|
|
struct ieee80211_if_ap *bss,
|
|
struct sk_buff *skb)
|
|
{
|
|
u8 *pos, *tim;
|
|
int aid0 = 0;
|
|
int i, have_bits = 0, n1, n2;
|
|
|
|
/* Generate bitmap for TIM only if there are any STAs in power save
|
|
* mode. */
|
|
spin_lock_bh(&local->sta_lock);
|
|
if (atomic_read(&bss->num_sta_ps) > 0)
|
|
/* in the hope that this is faster than
|
|
* checking byte-for-byte */
|
|
have_bits = !bitmap_empty((unsigned long*)bss->tim,
|
|
IEEE80211_MAX_AID+1);
|
|
|
|
if (bss->dtim_count == 0)
|
|
bss->dtim_count = bss->dtim_period - 1;
|
|
else
|
|
bss->dtim_count--;
|
|
|
|
tim = pos = (u8 *) skb_put(skb, 6);
|
|
*pos++ = WLAN_EID_TIM;
|
|
*pos++ = 4;
|
|
*pos++ = bss->dtim_count;
|
|
*pos++ = bss->dtim_period;
|
|
|
|
if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf))
|
|
aid0 = 1;
|
|
|
|
if (have_bits) {
|
|
/* Find largest even number N1 so that bits numbered 1 through
|
|
* (N1 x 8) - 1 in the bitmap are 0 and number N2 so that bits
|
|
* (N2 + 1) x 8 through 2007 are 0. */
|
|
n1 = 0;
|
|
for (i = 0; i < IEEE80211_MAX_TIM_LEN; i++) {
|
|
if (bss->tim[i]) {
|
|
n1 = i & 0xfe;
|
|
break;
|
|
}
|
|
}
|
|
n2 = n1;
|
|
for (i = IEEE80211_MAX_TIM_LEN - 1; i >= n1; i--) {
|
|
if (bss->tim[i]) {
|
|
n2 = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Bitmap control */
|
|
*pos++ = n1 | aid0;
|
|
/* Part Virt Bitmap */
|
|
memcpy(pos, bss->tim + n1, n2 - n1 + 1);
|
|
|
|
tim[1] = n2 - n1 + 4;
|
|
skb_put(skb, n2 - n1);
|
|
} else {
|
|
*pos++ = aid0; /* Bitmap control */
|
|
*pos++ = 0; /* Part Virt Bitmap */
|
|
}
|
|
spin_unlock_bh(&local->sta_lock);
|
|
}
|
|
|
|
|
|
struct sk_buff * ieee80211_beacon_get(struct ieee80211_hw *hw, int if_id,
|
|
struct ieee80211_tx_control *control)
|
|
{
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
struct sk_buff *skb;
|
|
struct net_device *bdev;
|
|
struct ieee80211_sub_if_data *sdata = NULL;
|
|
struct ieee80211_if_ap *ap = NULL;
|
|
struct ieee80211_rate *rate;
|
|
struct rate_control_extra extra;
|
|
u8 *b_head, *b_tail;
|
|
int bh_len, bt_len;
|
|
|
|
bdev = dev_get_by_index(if_id);
|
|
if (bdev) {
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(bdev);
|
|
ap = &sdata->u.ap;
|
|
dev_put(bdev);
|
|
}
|
|
|
|
if (!ap || sdata->type != IEEE80211_IF_TYPE_AP ||
|
|
!ap->beacon_head) {
|
|
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
|
if (net_ratelimit())
|
|
printk(KERN_DEBUG "no beacon data avail for idx=%d "
|
|
"(%s)\n", if_id, bdev ? bdev->name : "N/A");
|
|
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
|
|
return NULL;
|
|
}
|
|
|
|
/* Assume we are generating the normal beacon locally */
|
|
b_head = ap->beacon_head;
|
|
b_tail = ap->beacon_tail;
|
|
bh_len = ap->beacon_head_len;
|
|
bt_len = ap->beacon_tail_len;
|
|
|
|
skb = dev_alloc_skb(local->tx_headroom +
|
|
bh_len + bt_len + 256 /* maximum TIM len */);
|
|
if (!skb)
|
|
return NULL;
|
|
|
|
skb_reserve(skb, local->tx_headroom);
|
|
memcpy(skb_put(skb, bh_len), b_head, bh_len);
|
|
|
|
ieee80211_include_sequence(sdata, (struct ieee80211_hdr *)skb->data);
|
|
|
|
ieee80211_beacon_add_tim(local, ap, skb);
|
|
|
|
if (b_tail) {
|
|
memcpy(skb_put(skb, bt_len), b_tail, bt_len);
|
|
}
|
|
|
|
if (control) {
|
|
memset(&extra, 0, sizeof(extra));
|
|
extra.mode = local->oper_hw_mode;
|
|
|
|
rate = rate_control_get_rate(local, local->mdev, skb, &extra);
|
|
if (!rate) {
|
|
if (net_ratelimit()) {
|
|
printk(KERN_DEBUG "%s: ieee80211_beacon_get: no rate "
|
|
"found\n", local->mdev->name);
|
|
}
|
|
dev_kfree_skb(skb);
|
|
return NULL;
|
|
}
|
|
|
|
control->tx_rate = (local->short_preamble &&
|
|
(rate->flags & IEEE80211_RATE_PREAMBLE2)) ?
|
|
rate->val2 : rate->val;
|
|
control->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
|
|
control->power_level = local->hw.conf.power_level;
|
|
control->flags |= IEEE80211_TXCTL_NO_ACK;
|
|
control->retry_limit = 1;
|
|
control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK;
|
|
}
|
|
|
|
ap->num_beacons++;
|
|
return skb;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_beacon_get);
|
|
|
|
__le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
|
|
size_t frame_len,
|
|
const struct ieee80211_tx_control *frame_txctl)
|
|
{
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
struct ieee80211_rate *rate;
|
|
int short_preamble = local->short_preamble;
|
|
int erp;
|
|
u16 dur;
|
|
|
|
rate = frame_txctl->rts_rate;
|
|
erp = !!(rate->flags & IEEE80211_RATE_ERP);
|
|
|
|
/* CTS duration */
|
|
dur = ieee80211_frame_duration(local, 10, rate->rate,
|
|
erp, short_preamble);
|
|
/* Data frame duration */
|
|
dur += ieee80211_frame_duration(local, frame_len, rate->rate,
|
|
erp, short_preamble);
|
|
/* ACK duration */
|
|
dur += ieee80211_frame_duration(local, 10, rate->rate,
|
|
erp, short_preamble);
|
|
|
|
return cpu_to_le16(dur);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_rts_duration);
|
|
|
|
|
|
__le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
|
|
size_t frame_len,
|
|
const struct ieee80211_tx_control *frame_txctl)
|
|
{
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
struct ieee80211_rate *rate;
|
|
int short_preamble = local->short_preamble;
|
|
int erp;
|
|
u16 dur;
|
|
|
|
rate = frame_txctl->rts_rate;
|
|
erp = !!(rate->flags & IEEE80211_RATE_ERP);
|
|
|
|
/* Data frame duration */
|
|
dur = ieee80211_frame_duration(local, frame_len, rate->rate,
|
|
erp, short_preamble);
|
|
if (!(frame_txctl->flags & IEEE80211_TXCTL_NO_ACK)) {
|
|
/* ACK duration */
|
|
dur += ieee80211_frame_duration(local, 10, rate->rate,
|
|
erp, short_preamble);
|
|
}
|
|
|
|
return cpu_to_le16(dur);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_ctstoself_duration);
|
|
|
|
void ieee80211_rts_get(struct ieee80211_hw *hw,
|
|
const void *frame, size_t frame_len,
|
|
const struct ieee80211_tx_control *frame_txctl,
|
|
struct ieee80211_rts *rts)
|
|
{
|
|
const struct ieee80211_hdr *hdr = frame;
|
|
u16 fctl;
|
|
|
|
fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS;
|
|
rts->frame_control = cpu_to_le16(fctl);
|
|
rts->duration = ieee80211_rts_duration(hw, frame_len, frame_txctl);
|
|
memcpy(rts->ra, hdr->addr1, sizeof(rts->ra));
|
|
memcpy(rts->ta, hdr->addr2, sizeof(rts->ta));
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_rts_get);
|
|
|
|
void ieee80211_ctstoself_get(struct ieee80211_hw *hw,
|
|
const void *frame, size_t frame_len,
|
|
const struct ieee80211_tx_control *frame_txctl,
|
|
struct ieee80211_cts *cts)
|
|
{
|
|
const struct ieee80211_hdr *hdr = frame;
|
|
u16 fctl;
|
|
|
|
fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS;
|
|
cts->frame_control = cpu_to_le16(fctl);
|
|
cts->duration = ieee80211_ctstoself_duration(hw, frame_len, frame_txctl);
|
|
memcpy(cts->ra, hdr->addr1, sizeof(cts->ra));
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_ctstoself_get);
|
|
|
|
struct sk_buff *
|
|
ieee80211_get_buffered_bc(struct ieee80211_hw *hw, int if_id,
|
|
struct ieee80211_tx_control *control)
|
|
{
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
struct sk_buff *skb;
|
|
struct sta_info *sta;
|
|
ieee80211_tx_handler *handler;
|
|
struct ieee80211_txrx_data tx;
|
|
ieee80211_txrx_result res = TXRX_DROP;
|
|
struct net_device *bdev;
|
|
struct ieee80211_sub_if_data *sdata;
|
|
struct ieee80211_if_ap *bss = NULL;
|
|
|
|
bdev = dev_get_by_index(if_id);
|
|
if (bdev) {
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(bdev);
|
|
bss = &sdata->u.ap;
|
|
dev_put(bdev);
|
|
}
|
|
if (!bss || sdata->type != IEEE80211_IF_TYPE_AP || !bss->beacon_head)
|
|
return NULL;
|
|
|
|
if (bss->dtim_count != 0)
|
|
return NULL; /* send buffered bc/mc only after DTIM beacon */
|
|
memset(control, 0, sizeof(*control));
|
|
while (1) {
|
|
skb = skb_dequeue(&bss->ps_bc_buf);
|
|
if (!skb)
|
|
return NULL;
|
|
local->total_ps_buffered--;
|
|
|
|
if (!skb_queue_empty(&bss->ps_bc_buf) && skb->len >= 2) {
|
|
struct ieee80211_hdr *hdr =
|
|
(struct ieee80211_hdr *) skb->data;
|
|
/* more buffered multicast/broadcast frames ==> set
|
|
* MoreData flag in IEEE 802.11 header to inform PS
|
|
* STAs */
|
|
hdr->frame_control |=
|
|
cpu_to_le16(IEEE80211_FCTL_MOREDATA);
|
|
}
|
|
|
|
if (ieee80211_tx_prepare(&tx, skb, local->mdev, control) == 0)
|
|
break;
|
|
dev_kfree_skb_any(skb);
|
|
}
|
|
sta = tx.sta;
|
|
tx.u.tx.ps_buffered = 1;
|
|
|
|
for (handler = local->tx_handlers; *handler != NULL; handler++) {
|
|
res = (*handler)(&tx);
|
|
if (res == TXRX_DROP || res == TXRX_QUEUED)
|
|
break;
|
|
}
|
|
dev_put(tx.dev);
|
|
skb = tx.skb; /* handlers are allowed to change skb */
|
|
|
|
if (res == TXRX_DROP) {
|
|
I802_DEBUG_INC(local->tx_handlers_drop);
|
|
dev_kfree_skb(skb);
|
|
skb = NULL;
|
|
} else if (res == TXRX_QUEUED) {
|
|
I802_DEBUG_INC(local->tx_handlers_queued);
|
|
skb = NULL;
|
|
}
|
|
|
|
if (sta)
|
|
sta_info_put(sta);
|
|
|
|
return skb;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_get_buffered_bc);
|
|
|
|
static int __ieee80211_if_config(struct net_device *dev,
|
|
struct sk_buff *beacon,
|
|
struct ieee80211_tx_control *control)
|
|
{
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
struct ieee80211_if_conf conf;
|
|
static u8 scan_bssid[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
|
|
|
if (!local->ops->config_interface || !netif_running(dev))
|
|
return 0;
|
|
|
|
memset(&conf, 0, sizeof(conf));
|
|
conf.type = sdata->type;
|
|
if (sdata->type == IEEE80211_IF_TYPE_STA ||
|
|
sdata->type == IEEE80211_IF_TYPE_IBSS) {
|
|
if (local->sta_scanning &&
|
|
local->scan_dev == dev)
|
|
conf.bssid = scan_bssid;
|
|
else
|
|
conf.bssid = sdata->u.sta.bssid;
|
|
conf.ssid = sdata->u.sta.ssid;
|
|
conf.ssid_len = sdata->u.sta.ssid_len;
|
|
conf.generic_elem = sdata->u.sta.extra_ie;
|
|
conf.generic_elem_len = sdata->u.sta.extra_ie_len;
|
|
} else if (sdata->type == IEEE80211_IF_TYPE_AP) {
|
|
conf.ssid = sdata->u.ap.ssid;
|
|
conf.ssid_len = sdata->u.ap.ssid_len;
|
|
conf.generic_elem = sdata->u.ap.generic_elem;
|
|
conf.generic_elem_len = sdata->u.ap.generic_elem_len;
|
|
conf.beacon = beacon;
|
|
conf.beacon_control = control;
|
|
}
|
|
return local->ops->config_interface(local_to_hw(local),
|
|
dev->ifindex, &conf);
|
|
}
|
|
|
|
int ieee80211_if_config(struct net_device *dev)
|
|
{
|
|
return __ieee80211_if_config(dev, NULL, NULL);
|
|
}
|
|
|
|
int ieee80211_if_config_beacon(struct net_device *dev)
|
|
{
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
struct ieee80211_tx_control control;
|
|
struct sk_buff *skb;
|
|
|
|
if (!(local->hw.flags & IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE))
|
|
return 0;
|
|
skb = ieee80211_beacon_get(local_to_hw(local), dev->ifindex, &control);
|
|
if (!skb)
|
|
return -ENOMEM;
|
|
return __ieee80211_if_config(dev, skb, &control);
|
|
}
|
|
|
|
int ieee80211_hw_config(struct ieee80211_local *local)
|
|
{
|
|
struct ieee80211_hw_mode *mode;
|
|
struct ieee80211_channel *chan;
|
|
int ret = 0;
|
|
|
|
if (local->sta_scanning) {
|
|
chan = local->scan_channel;
|
|
mode = local->scan_hw_mode;
|
|
} else {
|
|
chan = local->oper_channel;
|
|
mode = local->oper_hw_mode;
|
|
}
|
|
|
|
local->hw.conf.channel = chan->chan;
|
|
local->hw.conf.channel_val = chan->val;
|
|
local->hw.conf.power_level = chan->power_level;
|
|
local->hw.conf.freq = chan->freq;
|
|
local->hw.conf.phymode = mode->mode;
|
|
local->hw.conf.antenna_max = chan->antenna_max;
|
|
local->hw.conf.chan = chan;
|
|
local->hw.conf.mode = mode;
|
|
|
|
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
|
printk(KERN_DEBUG "HW CONFIG: channel=%d freq=%d "
|
|
"phymode=%d\n", local->hw.conf.channel, local->hw.conf.freq,
|
|
local->hw.conf.phymode);
|
|
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
|
|
|
|
if (local->ops->config)
|
|
ret = local->ops->config(local_to_hw(local), &local->hw.conf);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
|
|
{
|
|
/* FIX: what would be proper limits for MTU?
|
|
* This interface uses 802.3 frames. */
|
|
if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6) {
|
|
printk(KERN_WARNING "%s: invalid MTU %d\n",
|
|
dev->name, new_mtu);
|
|
return -EINVAL;
|
|
}
|
|
|
|
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
|
printk(KERN_DEBUG "%s: setting MTU %d\n", dev->name, new_mtu);
|
|
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
|
|
dev->mtu = new_mtu;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int ieee80211_change_mtu_apdev(struct net_device *dev, int new_mtu)
|
|
{
|
|
/* FIX: what would be proper limits for MTU?
|
|
* This interface uses 802.11 frames. */
|
|
if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN) {
|
|
printk(KERN_WARNING "%s: invalid MTU %d\n",
|
|
dev->name, new_mtu);
|
|
return -EINVAL;
|
|
}
|
|
|
|
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
|
printk(KERN_DEBUG "%s: setting MTU %d\n", dev->name, new_mtu);
|
|
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
|
|
dev->mtu = new_mtu;
|
|
return 0;
|
|
}
|
|
|
|
enum netif_tx_lock_class {
|
|
TX_LOCK_NORMAL,
|
|
TX_LOCK_MASTER,
|
|
};
|
|
|
|
static inline void netif_tx_lock_nested(struct net_device *dev, int subclass)
|
|
{
|
|
spin_lock_nested(&dev->_xmit_lock, subclass);
|
|
dev->xmit_lock_owner = smp_processor_id();
|
|
}
|
|
|
|
static void ieee80211_set_multicast_list(struct net_device *dev)
|
|
{
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
unsigned short flags;
|
|
|
|
netif_tx_lock_nested(local->mdev, TX_LOCK_MASTER);
|
|
if (((dev->flags & IFF_ALLMULTI) != 0) ^ (sdata->allmulti != 0)) {
|
|
if (sdata->allmulti) {
|
|
sdata->allmulti = 0;
|
|
local->iff_allmultis--;
|
|
} else {
|
|
sdata->allmulti = 1;
|
|
local->iff_allmultis++;
|
|
}
|
|
}
|
|
if (((dev->flags & IFF_PROMISC) != 0) ^ (sdata->promisc != 0)) {
|
|
if (sdata->promisc) {
|
|
sdata->promisc = 0;
|
|
local->iff_promiscs--;
|
|
} else {
|
|
sdata->promisc = 1;
|
|
local->iff_promiscs++;
|
|
}
|
|
}
|
|
if (dev->mc_count != sdata->mc_count) {
|
|
local->mc_count = local->mc_count - sdata->mc_count +
|
|
dev->mc_count;
|
|
sdata->mc_count = dev->mc_count;
|
|
}
|
|
if (local->ops->set_multicast_list) {
|
|
flags = local->mdev->flags;
|
|
if (local->iff_allmultis)
|
|
flags |= IFF_ALLMULTI;
|
|
if (local->iff_promiscs)
|
|
flags |= IFF_PROMISC;
|
|
read_lock(&local->sub_if_lock);
|
|
local->ops->set_multicast_list(local_to_hw(local), flags,
|
|
local->mc_count);
|
|
read_unlock(&local->sub_if_lock);
|
|
}
|
|
netif_tx_unlock(local->mdev);
|
|
}
|
|
|
|
struct dev_mc_list *ieee80211_get_mc_list_item(struct ieee80211_hw *hw,
|
|
struct dev_mc_list *prev,
|
|
void **ptr)
|
|
{
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
struct ieee80211_sub_if_data *sdata = *ptr;
|
|
struct dev_mc_list *mc;
|
|
|
|
if (!prev) {
|
|
WARN_ON(sdata);
|
|
sdata = NULL;
|
|
}
|
|
if (!prev || !prev->next) {
|
|
if (sdata)
|
|
sdata = list_entry(sdata->list.next,
|
|
struct ieee80211_sub_if_data, list);
|
|
else
|
|
sdata = list_entry(local->sub_if_list.next,
|
|
struct ieee80211_sub_if_data, list);
|
|
if (&sdata->list != &local->sub_if_list)
|
|
mc = sdata->dev->mc_list;
|
|
else
|
|
mc = NULL;
|
|
} else
|
|
mc = prev->next;
|
|
|
|
*ptr = sdata;
|
|
return mc;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_get_mc_list_item);
|
|
|
|
static struct net_device_stats *ieee80211_get_stats(struct net_device *dev)
|
|
{
|
|
struct ieee80211_sub_if_data *sdata;
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
return &(sdata->stats);
|
|
}
|
|
|
|
static void ieee80211_if_shutdown(struct net_device *dev)
|
|
{
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
ASSERT_RTNL();
|
|
switch (sdata->type) {
|
|
case IEEE80211_IF_TYPE_STA:
|
|
case IEEE80211_IF_TYPE_IBSS:
|
|
sdata->u.sta.state = IEEE80211_DISABLED;
|
|
del_timer_sync(&sdata->u.sta.timer);
|
|
skb_queue_purge(&sdata->u.sta.skb_queue);
|
|
if (!local->ops->hw_scan &&
|
|
local->scan_dev == sdata->dev) {
|
|
local->sta_scanning = 0;
|
|
cancel_delayed_work(&local->scan_work);
|
|
}
|
|
flush_workqueue(local->hw.workqueue);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static inline int identical_mac_addr_allowed(int type1, int type2)
|
|
{
|
|
return (type1 == IEEE80211_IF_TYPE_MNTR ||
|
|
type2 == IEEE80211_IF_TYPE_MNTR ||
|
|
(type1 == IEEE80211_IF_TYPE_AP &&
|
|
type2 == IEEE80211_IF_TYPE_WDS) ||
|
|
(type1 == IEEE80211_IF_TYPE_WDS &&
|
|
(type2 == IEEE80211_IF_TYPE_WDS ||
|
|
type2 == IEEE80211_IF_TYPE_AP)) ||
|
|
(type1 == IEEE80211_IF_TYPE_AP &&
|
|
type2 == IEEE80211_IF_TYPE_VLAN) ||
|
|
(type1 == IEEE80211_IF_TYPE_VLAN &&
|
|
(type2 == IEEE80211_IF_TYPE_AP ||
|
|
type2 == IEEE80211_IF_TYPE_VLAN)));
|
|
}
|
|
|
|
static int ieee80211_master_open(struct net_device *dev)
|
|
{
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
struct ieee80211_sub_if_data *sdata;
|
|
int res = -EOPNOTSUPP;
|
|
|
|
read_lock(&local->sub_if_lock);
|
|
list_for_each_entry(sdata, &local->sub_if_list, list) {
|
|
if (sdata->dev != dev && netif_running(sdata->dev)) {
|
|
res = 0;
|
|
break;
|
|
}
|
|
}
|
|
read_unlock(&local->sub_if_lock);
|
|
return res;
|
|
}
|
|
|
|
static int ieee80211_master_stop(struct net_device *dev)
|
|
{
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
struct ieee80211_sub_if_data *sdata;
|
|
|
|
read_lock(&local->sub_if_lock);
|
|
list_for_each_entry(sdata, &local->sub_if_list, list)
|
|
if (sdata->dev != dev && netif_running(sdata->dev))
|
|
dev_close(sdata->dev);
|
|
read_unlock(&local->sub_if_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ieee80211_mgmt_open(struct net_device *dev)
|
|
{
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
|
|
if (!netif_running(local->mdev))
|
|
return -EOPNOTSUPP;
|
|
return 0;
|
|
}
|
|
|
|
static int ieee80211_mgmt_stop(struct net_device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* Check if running monitor interfaces should go to a "soft monitor" mode
|
|
* and switch them if necessary. */
|
|
static inline void ieee80211_start_soft_monitor(struct ieee80211_local *local)
|
|
{
|
|
struct ieee80211_if_init_conf conf;
|
|
|
|
if (local->open_count && local->open_count == local->monitors &&
|
|
!(local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER) &&
|
|
local->ops->remove_interface) {
|
|
conf.if_id = -1;
|
|
conf.type = IEEE80211_IF_TYPE_MNTR;
|
|
conf.mac_addr = NULL;
|
|
local->ops->remove_interface(local_to_hw(local), &conf);
|
|
}
|
|
}
|
|
|
|
/* Check if running monitor interfaces should go to a "hard monitor" mode
|
|
* and switch them if necessary. */
|
|
static void ieee80211_start_hard_monitor(struct ieee80211_local *local)
|
|
{
|
|
struct ieee80211_if_init_conf conf;
|
|
|
|
if (local->open_count && local->open_count == local->monitors &&
|
|
!(local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER)) {
|
|
conf.if_id = -1;
|
|
conf.type = IEEE80211_IF_TYPE_MNTR;
|
|
conf.mac_addr = NULL;
|
|
local->ops->add_interface(local_to_hw(local), &conf);
|
|
}
|
|
}
|
|
|
|
static int ieee80211_open(struct net_device *dev)
|
|
{
|
|
struct ieee80211_sub_if_data *sdata, *nsdata;
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
struct ieee80211_if_init_conf conf;
|
|
int res;
|
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
read_lock(&local->sub_if_lock);
|
|
list_for_each_entry(nsdata, &local->sub_if_list, list) {
|
|
struct net_device *ndev = nsdata->dev;
|
|
|
|
if (ndev != dev && ndev != local->mdev && netif_running(ndev) &&
|
|
compare_ether_addr(dev->dev_addr, ndev->dev_addr) == 0 &&
|
|
!identical_mac_addr_allowed(sdata->type, nsdata->type)) {
|
|
read_unlock(&local->sub_if_lock);
|
|
return -ENOTUNIQ;
|
|
}
|
|
}
|
|
read_unlock(&local->sub_if_lock);
|
|
|
|
if (sdata->type == IEEE80211_IF_TYPE_WDS &&
|
|
is_zero_ether_addr(sdata->u.wds.remote_addr))
|
|
return -ENOLINK;
|
|
|
|
if (sdata->type == IEEE80211_IF_TYPE_MNTR && local->open_count &&
|
|
!(local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER)) {
|
|
/* run the interface in a "soft monitor" mode */
|
|
local->monitors++;
|
|
local->open_count++;
|
|
local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP;
|
|
return 0;
|
|
}
|
|
ieee80211_start_soft_monitor(local);
|
|
|
|
conf.if_id = dev->ifindex;
|
|
conf.type = sdata->type;
|
|
conf.mac_addr = dev->dev_addr;
|
|
res = local->ops->add_interface(local_to_hw(local), &conf);
|
|
if (res) {
|
|
if (sdata->type == IEEE80211_IF_TYPE_MNTR)
|
|
ieee80211_start_hard_monitor(local);
|
|
return res;
|
|
}
|
|
|
|
if (local->open_count == 0) {
|
|
res = 0;
|
|
tasklet_enable(&local->tx_pending_tasklet);
|
|
tasklet_enable(&local->tasklet);
|
|
if (local->ops->open)
|
|
res = local->ops->open(local_to_hw(local));
|
|
if (res == 0) {
|
|
res = dev_open(local->mdev);
|
|
if (res) {
|
|
if (local->ops->stop)
|
|
local->ops->stop(local_to_hw(local));
|
|
} else {
|
|
res = ieee80211_hw_config(local);
|
|
if (res && local->ops->stop)
|
|
local->ops->stop(local_to_hw(local));
|
|
else if (!res && local->apdev)
|
|
dev_open(local->apdev);
|
|
}
|
|
}
|
|
if (res) {
|
|
if (local->ops->remove_interface)
|
|
local->ops->remove_interface(local_to_hw(local),
|
|
&conf);
|
|
return res;
|
|
}
|
|
}
|
|
local->open_count++;
|
|
|
|
if (sdata->type == IEEE80211_IF_TYPE_MNTR) {
|
|
local->monitors++;
|
|
local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP;
|
|
} else
|
|
ieee80211_if_config(dev);
|
|
|
|
if (sdata->type == IEEE80211_IF_TYPE_STA &&
|
|
!local->user_space_mlme)
|
|
netif_carrier_off(dev);
|
|
else
|
|
netif_carrier_on(dev);
|
|
|
|
netif_start_queue(dev);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int ieee80211_stop(struct net_device *dev)
|
|
{
|
|
struct ieee80211_sub_if_data *sdata;
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
if (sdata->type == IEEE80211_IF_TYPE_MNTR &&
|
|
local->open_count > 1 &&
|
|
!(local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER)) {
|
|
/* remove "soft monitor" interface */
|
|
local->open_count--;
|
|
local->monitors--;
|
|
if (!local->monitors)
|
|
local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP;
|
|
return 0;
|
|
}
|
|
|
|
netif_stop_queue(dev);
|
|
ieee80211_if_shutdown(dev);
|
|
|
|
if (sdata->type == IEEE80211_IF_TYPE_MNTR) {
|
|
local->monitors--;
|
|
if (!local->monitors)
|
|
local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP;
|
|
}
|
|
|
|
local->open_count--;
|
|
if (local->open_count == 0) {
|
|
if (netif_running(local->mdev))
|
|
dev_close(local->mdev);
|
|
if (local->apdev)
|
|
dev_close(local->apdev);
|
|
if (local->ops->stop)
|
|
local->ops->stop(local_to_hw(local));
|
|
tasklet_disable(&local->tx_pending_tasklet);
|
|
tasklet_disable(&local->tasklet);
|
|
}
|
|
if (local->ops->remove_interface) {
|
|
struct ieee80211_if_init_conf conf;
|
|
|
|
conf.if_id = dev->ifindex;
|
|
conf.type = sdata->type;
|
|
conf.mac_addr = dev->dev_addr;
|
|
local->ops->remove_interface(local_to_hw(local), &conf);
|
|
}
|
|
|
|
ieee80211_start_hard_monitor(local);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int header_parse_80211(struct sk_buff *skb, unsigned char *haddr)
|
|
{
|
|
memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */
|
|
return ETH_ALEN;
|
|
}
|
|
|
|
static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
|
|
{
|
|
return compare_ether_addr(raddr, addr) == 0 ||
|
|
is_broadcast_ether_addr(raddr);
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
|
|
{
|
|
struct net_device *dev = rx->dev;
|
|
struct ieee80211_local *local = rx->local;
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
|
|
u16 fc, hdrlen, ethertype;
|
|
u8 *payload;
|
|
u8 dst[ETH_ALEN];
|
|
u8 src[ETH_ALEN];
|
|
struct sk_buff *skb = rx->skb, *skb2;
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
fc = rx->fc;
|
|
if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA))
|
|
return TXRX_CONTINUE;
|
|
|
|
if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
|
|
return TXRX_DROP;
|
|
|
|
hdrlen = ieee80211_get_hdrlen(fc);
|
|
|
|
/* convert IEEE 802.11 header + possible LLC headers into Ethernet
|
|
* header
|
|
* IEEE 802.11 address fields:
|
|
* ToDS FromDS Addr1 Addr2 Addr3 Addr4
|
|
* 0 0 DA SA BSSID n/a
|
|
* 0 1 DA BSSID SA n/a
|
|
* 1 0 BSSID SA DA n/a
|
|
* 1 1 RA TA DA SA
|
|
*/
|
|
|
|
switch (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
|
|
case IEEE80211_FCTL_TODS:
|
|
/* BSSID SA DA */
|
|
memcpy(dst, hdr->addr3, ETH_ALEN);
|
|
memcpy(src, hdr->addr2, ETH_ALEN);
|
|
|
|
if (unlikely(sdata->type != IEEE80211_IF_TYPE_AP &&
|
|
sdata->type != IEEE80211_IF_TYPE_VLAN)) {
|
|
printk(KERN_DEBUG "%s: dropped ToDS frame (BSSID="
|
|
MAC_FMT " SA=" MAC_FMT " DA=" MAC_FMT ")\n",
|
|
dev->name, MAC_ARG(hdr->addr1),
|
|
MAC_ARG(hdr->addr2), MAC_ARG(hdr->addr3));
|
|
return TXRX_DROP;
|
|
}
|
|
break;
|
|
case (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
|
|
/* RA TA DA SA */
|
|
memcpy(dst, hdr->addr3, ETH_ALEN);
|
|
memcpy(src, hdr->addr4, ETH_ALEN);
|
|
|
|
if (unlikely(sdata->type != IEEE80211_IF_TYPE_WDS)) {
|
|
printk(KERN_DEBUG "%s: dropped FromDS&ToDS frame (RA="
|
|
MAC_FMT " TA=" MAC_FMT " DA=" MAC_FMT " SA="
|
|
MAC_FMT ")\n",
|
|
rx->dev->name, MAC_ARG(hdr->addr1),
|
|
MAC_ARG(hdr->addr2), MAC_ARG(hdr->addr3),
|
|
MAC_ARG(hdr->addr4));
|
|
return TXRX_DROP;
|
|
}
|
|
break;
|
|
case IEEE80211_FCTL_FROMDS:
|
|
/* DA BSSID SA */
|
|
memcpy(dst, hdr->addr1, ETH_ALEN);
|
|
memcpy(src, hdr->addr3, ETH_ALEN);
|
|
|
|
if (sdata->type != IEEE80211_IF_TYPE_STA) {
|
|
return TXRX_DROP;
|
|
}
|
|
break;
|
|
case 0:
|
|
/* DA SA BSSID */
|
|
memcpy(dst, hdr->addr1, ETH_ALEN);
|
|
memcpy(src, hdr->addr2, ETH_ALEN);
|
|
|
|
if (sdata->type != IEEE80211_IF_TYPE_IBSS) {
|
|
if (net_ratelimit()) {
|
|
printk(KERN_DEBUG "%s: dropped IBSS frame (DA="
|
|
MAC_FMT " SA=" MAC_FMT " BSSID=" MAC_FMT
|
|
")\n",
|
|
dev->name, MAC_ARG(hdr->addr1),
|
|
MAC_ARG(hdr->addr2),
|
|
MAC_ARG(hdr->addr3));
|
|
}
|
|
return TXRX_DROP;
|
|
}
|
|
break;
|
|
}
|
|
|
|
payload = skb->data + hdrlen;
|
|
|
|
if (unlikely(skb->len - hdrlen < 8)) {
|
|
if (net_ratelimit()) {
|
|
printk(KERN_DEBUG "%s: RX too short data frame "
|
|
"payload\n", dev->name);
|
|
}
|
|
return TXRX_DROP;
|
|
}
|
|
|
|
ethertype = (payload[6] << 8) | payload[7];
|
|
|
|
if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
|
|
ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
|
|
compare_ether_addr(payload, bridge_tunnel_header) == 0)) {
|
|
/* remove RFC1042 or Bridge-Tunnel encapsulation and
|
|
* replace EtherType */
|
|
skb_pull(skb, hdrlen + 6);
|
|
memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
|
|
memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
|
|
} else {
|
|
struct ethhdr *ehdr;
|
|
__be16 len;
|
|
skb_pull(skb, hdrlen);
|
|
len = htons(skb->len);
|
|
ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
|
|
memcpy(ehdr->h_dest, dst, ETH_ALEN);
|
|
memcpy(ehdr->h_source, src, ETH_ALEN);
|
|
ehdr->h_proto = len;
|
|
}
|
|
skb->dev = dev;
|
|
|
|
skb2 = NULL;
|
|
|
|
sdata->stats.rx_packets++;
|
|
sdata->stats.rx_bytes += skb->len;
|
|
|
|
if (local->bridge_packets && (sdata->type == IEEE80211_IF_TYPE_AP
|
|
|| sdata->type == IEEE80211_IF_TYPE_VLAN) && rx->u.rx.ra_match) {
|
|
if (is_multicast_ether_addr(skb->data)) {
|
|
/* send multicast frames both to higher layers in
|
|
* local net stack and back to the wireless media */
|
|
skb2 = skb_copy(skb, GFP_ATOMIC);
|
|
if (!skb2)
|
|
printk(KERN_DEBUG "%s: failed to clone "
|
|
"multicast frame\n", dev->name);
|
|
} else {
|
|
struct sta_info *dsta;
|
|
dsta = sta_info_get(local, skb->data);
|
|
if (dsta && !dsta->dev) {
|
|
printk(KERN_DEBUG "Station with null dev "
|
|
"structure!\n");
|
|
} else if (dsta && dsta->dev == dev) {
|
|
/* Destination station is associated to this
|
|
* AP, so send the frame directly to it and
|
|
* do not pass the frame to local net stack.
|
|
*/
|
|
skb2 = skb;
|
|
skb = NULL;
|
|
}
|
|
if (dsta)
|
|
sta_info_put(dsta);
|
|
}
|
|
}
|
|
|
|
if (skb) {
|
|
/* deliver to local stack */
|
|
skb->protocol = eth_type_trans(skb, dev);
|
|
memset(skb->cb, 0, sizeof(skb->cb));
|
|
netif_rx(skb);
|
|
}
|
|
|
|
if (skb2) {
|
|
/* send to wireless media */
|
|
skb2->protocol = __constant_htons(ETH_P_802_3);
|
|
skb_set_network_header(skb2, 0);
|
|
skb_set_mac_header(skb2, 0);
|
|
dev_queue_xmit(skb2);
|
|
}
|
|
|
|
return TXRX_QUEUED;
|
|
}
|
|
|
|
|
|
static struct ieee80211_rate *
|
|
ieee80211_get_rate(struct ieee80211_local *local, int phymode, int hw_rate)
|
|
{
|
|
struct ieee80211_hw_mode *mode;
|
|
int r;
|
|
|
|
list_for_each_entry(mode, &local->modes_list, list) {
|
|
if (mode->mode != phymode)
|
|
continue;
|
|
for (r = 0; r < mode->num_rates; r++) {
|
|
struct ieee80211_rate *rate = &mode->rates[r];
|
|
if (rate->val == hw_rate ||
|
|
(rate->flags & IEEE80211_RATE_PREAMBLE2 &&
|
|
rate->val2 == hw_rate))
|
|
return rate;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
ieee80211_fill_frame_info(struct ieee80211_local *local,
|
|
struct ieee80211_frame_info *fi,
|
|
struct ieee80211_rx_status *status)
|
|
{
|
|
if (status) {
|
|
struct timespec ts;
|
|
struct ieee80211_rate *rate;
|
|
|
|
jiffies_to_timespec(jiffies, &ts);
|
|
fi->hosttime = cpu_to_be64((u64) ts.tv_sec * 1000000 +
|
|
ts.tv_nsec / 1000);
|
|
fi->mactime = cpu_to_be64(status->mactime);
|
|
switch (status->phymode) {
|
|
case MODE_IEEE80211A:
|
|
fi->phytype = htonl(ieee80211_phytype_ofdm_dot11_a);
|
|
break;
|
|
case MODE_IEEE80211B:
|
|
fi->phytype = htonl(ieee80211_phytype_dsss_dot11_b);
|
|
break;
|
|
case MODE_IEEE80211G:
|
|
fi->phytype = htonl(ieee80211_phytype_pbcc_dot11_g);
|
|
break;
|
|
case MODE_ATHEROS_TURBO:
|
|
fi->phytype =
|
|
htonl(ieee80211_phytype_dsss_dot11_turbo);
|
|
break;
|
|
default:
|
|
fi->phytype = htonl(0xAAAAAAAA);
|
|
break;
|
|
}
|
|
fi->channel = htonl(status->channel);
|
|
rate = ieee80211_get_rate(local, status->phymode,
|
|
status->rate);
|
|
if (rate) {
|
|
fi->datarate = htonl(rate->rate);
|
|
if (rate->flags & IEEE80211_RATE_PREAMBLE2) {
|
|
if (status->rate == rate->val)
|
|
fi->preamble = htonl(2); /* long */
|
|
else if (status->rate == rate->val2)
|
|
fi->preamble = htonl(1); /* short */
|
|
} else
|
|
fi->preamble = htonl(0);
|
|
} else {
|
|
fi->datarate = htonl(0);
|
|
fi->preamble = htonl(0);
|
|
}
|
|
|
|
fi->antenna = htonl(status->antenna);
|
|
fi->priority = htonl(0xffffffff); /* no clue */
|
|
fi->ssi_type = htonl(ieee80211_ssi_raw);
|
|
fi->ssi_signal = htonl(status->ssi);
|
|
fi->ssi_noise = 0x00000000;
|
|
fi->encoding = 0;
|
|
} else {
|
|
/* clear everything because we really don't know.
|
|
* the msg_type field isn't present on monitor frames
|
|
* so we don't know whether it will be present or not,
|
|
* but it's ok to not clear it since it'll be assigned
|
|
* anyway */
|
|
memset(fi, 0, sizeof(*fi) - sizeof(fi->msg_type));
|
|
|
|
fi->ssi_type = htonl(ieee80211_ssi_none);
|
|
}
|
|
fi->version = htonl(IEEE80211_FI_VERSION);
|
|
fi->length = cpu_to_be32(sizeof(*fi) - sizeof(fi->msg_type));
|
|
}
|
|
|
|
/* this routine is actually not just for this, but also
|
|
* for pushing fake 'management' frames into userspace.
|
|
* it shall be replaced by a netlink-based system. */
|
|
void
|
|
ieee80211_rx_mgmt(struct ieee80211_local *local, struct sk_buff *skb,
|
|
struct ieee80211_rx_status *status, u32 msg_type)
|
|
{
|
|
struct ieee80211_frame_info *fi;
|
|
const size_t hlen = sizeof(struct ieee80211_frame_info);
|
|
struct ieee80211_sub_if_data *sdata;
|
|
|
|
skb->dev = local->apdev;
|
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(local->apdev);
|
|
|
|
if (skb_headroom(skb) < hlen) {
|
|
I802_DEBUG_INC(local->rx_expand_skb_head);
|
|
if (pskb_expand_head(skb, hlen, 0, GFP_ATOMIC)) {
|
|
dev_kfree_skb(skb);
|
|
return;
|
|
}
|
|
}
|
|
|
|
fi = (struct ieee80211_frame_info *) skb_push(skb, hlen);
|
|
|
|
ieee80211_fill_frame_info(local, fi, status);
|
|
fi->msg_type = htonl(msg_type);
|
|
|
|
sdata->stats.rx_packets++;
|
|
sdata->stats.rx_bytes += skb->len;
|
|
|
|
skb_set_mac_header(skb, 0);
|
|
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
|
skb->pkt_type = PACKET_OTHERHOST;
|
|
skb->protocol = htons(ETH_P_802_2);
|
|
memset(skb->cb, 0, sizeof(skb->cb));
|
|
netif_rx(skb);
|
|
}
|
|
|
|
static void
|
|
ieee80211_rx_monitor(struct net_device *dev, struct sk_buff *skb,
|
|
struct ieee80211_rx_status *status)
|
|
{
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
struct ieee80211_sub_if_data *sdata;
|
|
struct ieee80211_rate *rate;
|
|
struct ieee80211_rtap_hdr {
|
|
struct ieee80211_radiotap_header hdr;
|
|
u8 flags;
|
|
u8 rate;
|
|
__le16 chan_freq;
|
|
__le16 chan_flags;
|
|
u8 antsignal;
|
|
} __attribute__ ((packed)) *rthdr;
|
|
|
|
skb->dev = dev;
|
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
if (status->flag & RX_FLAG_RADIOTAP)
|
|
goto out;
|
|
|
|
if (skb_headroom(skb) < sizeof(*rthdr)) {
|
|
I802_DEBUG_INC(local->rx_expand_skb_head);
|
|
if (pskb_expand_head(skb, sizeof(*rthdr), 0, GFP_ATOMIC)) {
|
|
dev_kfree_skb(skb);
|
|
return;
|
|
}
|
|
}
|
|
|
|
rthdr = (struct ieee80211_rtap_hdr *) skb_push(skb, sizeof(*rthdr));
|
|
memset(rthdr, 0, sizeof(*rthdr));
|
|
rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr));
|
|
rthdr->hdr.it_present =
|
|
cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
|
|
(1 << IEEE80211_RADIOTAP_RATE) |
|
|
(1 << IEEE80211_RADIOTAP_CHANNEL) |
|
|
(1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL));
|
|
rthdr->flags = local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS ?
|
|
IEEE80211_RADIOTAP_F_FCS : 0;
|
|
rate = ieee80211_get_rate(local, status->phymode, status->rate);
|
|
if (rate)
|
|
rthdr->rate = rate->rate / 5;
|
|
rthdr->chan_freq = cpu_to_le16(status->freq);
|
|
rthdr->chan_flags =
|
|
status->phymode == MODE_IEEE80211A ?
|
|
cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ) :
|
|
cpu_to_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ);
|
|
rthdr->antsignal = status->ssi;
|
|
|
|
out:
|
|
sdata->stats.rx_packets++;
|
|
sdata->stats.rx_bytes += skb->len;
|
|
|
|
skb_set_mac_header(skb, 0);
|
|
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
|
skb->pkt_type = PACKET_OTHERHOST;
|
|
skb->protocol = htons(ETH_P_802_2);
|
|
memset(skb->cb, 0, sizeof(skb->cb));
|
|
netif_rx(skb);
|
|
}
|
|
|
|
int ieee80211_radar_status(struct ieee80211_hw *hw, int channel,
|
|
int radar, int radar_type)
|
|
{
|
|
struct sk_buff *skb;
|
|
struct ieee80211_radar_info *msg;
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
|
|
if (!local->apdev)
|
|
return 0;
|
|
|
|
skb = dev_alloc_skb(sizeof(struct ieee80211_frame_info) +
|
|
sizeof(struct ieee80211_radar_info));
|
|
|
|
if (!skb)
|
|
return -ENOMEM;
|
|
skb_reserve(skb, sizeof(struct ieee80211_frame_info));
|
|
|
|
msg = (struct ieee80211_radar_info *)
|
|
skb_put(skb, sizeof(struct ieee80211_radar_info));
|
|
msg->channel = channel;
|
|
msg->radar = radar;
|
|
msg->radar_type = radar_type;
|
|
|
|
ieee80211_rx_mgmt(local, skb, NULL, ieee80211_msg_radar);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_radar_status);
|
|
|
|
|
|
static void ap_sta_ps_start(struct net_device *dev, struct sta_info *sta)
|
|
{
|
|
struct ieee80211_sub_if_data *sdata;
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
|
|
|
|
if (sdata->bss)
|
|
atomic_inc(&sdata->bss->num_sta_ps);
|
|
sta->flags |= WLAN_STA_PS;
|
|
sta->pspoll = 0;
|
|
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
|
|
printk(KERN_DEBUG "%s: STA " MAC_FMT " aid %d enters power "
|
|
"save mode\n", dev->name, MAC_ARG(sta->addr), sta->aid);
|
|
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
|
|
}
|
|
|
|
|
|
static int ap_sta_ps_end(struct net_device *dev, struct sta_info *sta)
|
|
{
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
struct sk_buff *skb;
|
|
int sent = 0;
|
|
struct ieee80211_sub_if_data *sdata;
|
|
struct ieee80211_tx_packet_data *pkt_data;
|
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
|
|
if (sdata->bss)
|
|
atomic_dec(&sdata->bss->num_sta_ps);
|
|
sta->flags &= ~(WLAN_STA_PS | WLAN_STA_TIM);
|
|
sta->pspoll = 0;
|
|
if (!skb_queue_empty(&sta->ps_tx_buf)) {
|
|
if (local->ops->set_tim)
|
|
local->ops->set_tim(local_to_hw(local), sta->aid, 0);
|
|
if (sdata->bss)
|
|
bss_tim_clear(local, sdata->bss, sta->aid);
|
|
}
|
|
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
|
|
printk(KERN_DEBUG "%s: STA " MAC_FMT " aid %d exits power "
|
|
"save mode\n", dev->name, MAC_ARG(sta->addr), sta->aid);
|
|
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
|
|
/* Send all buffered frames to the station */
|
|
while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
|
|
pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
|
|
sent++;
|
|
pkt_data->requeue = 1;
|
|
dev_queue_xmit(skb);
|
|
}
|
|
while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
|
|
pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
|
|
local->total_ps_buffered--;
|
|
sent++;
|
|
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
|
|
printk(KERN_DEBUG "%s: STA " MAC_FMT " aid %d send PS frame "
|
|
"since STA not sleeping anymore\n", dev->name,
|
|
MAC_ARG(sta->addr), sta->aid);
|
|
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
|
|
pkt_data->requeue = 1;
|
|
dev_queue_xmit(skb);
|
|
}
|
|
|
|
return sent;
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx)
|
|
{
|
|
struct sk_buff *skb;
|
|
int no_pending_pkts;
|
|
|
|
if (likely(!rx->sta ||
|
|
(rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_CTL ||
|
|
(rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PSPOLL ||
|
|
!rx->u.rx.ra_match))
|
|
return TXRX_CONTINUE;
|
|
|
|
skb = skb_dequeue(&rx->sta->tx_filtered);
|
|
if (!skb) {
|
|
skb = skb_dequeue(&rx->sta->ps_tx_buf);
|
|
if (skb)
|
|
rx->local->total_ps_buffered--;
|
|
}
|
|
no_pending_pkts = skb_queue_empty(&rx->sta->tx_filtered) &&
|
|
skb_queue_empty(&rx->sta->ps_tx_buf);
|
|
|
|
if (skb) {
|
|
struct ieee80211_hdr *hdr =
|
|
(struct ieee80211_hdr *) skb->data;
|
|
|
|
/* tell TX path to send one frame even though the STA may
|
|
* still remain is PS mode after this frame exchange */
|
|
rx->sta->pspoll = 1;
|
|
|
|
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
|
|
printk(KERN_DEBUG "STA " MAC_FMT " aid %d: PS Poll (entries "
|
|
"after %d)\n",
|
|
MAC_ARG(rx->sta->addr), rx->sta->aid,
|
|
skb_queue_len(&rx->sta->ps_tx_buf));
|
|
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
|
|
|
|
/* Use MoreData flag to indicate whether there are more
|
|
* buffered frames for this STA */
|
|
if (no_pending_pkts) {
|
|
hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
|
|
rx->sta->flags &= ~WLAN_STA_TIM;
|
|
} else
|
|
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
|
|
|
|
dev_queue_xmit(skb);
|
|
|
|
if (no_pending_pkts) {
|
|
if (rx->local->ops->set_tim)
|
|
rx->local->ops->set_tim(local_to_hw(rx->local),
|
|
rx->sta->aid, 0);
|
|
if (rx->sdata->bss)
|
|
bss_tim_clear(rx->local, rx->sdata->bss, rx->sta->aid);
|
|
}
|
|
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
|
|
} else if (!rx->u.rx.sent_ps_buffered) {
|
|
printk(KERN_DEBUG "%s: STA " MAC_FMT " sent PS Poll even "
|
|
"though there is no buffered frames for it\n",
|
|
rx->dev->name, MAC_ARG(rx->sta->addr));
|
|
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
|
|
|
|
}
|
|
|
|
/* Free PS Poll skb here instead of returning TXRX_DROP that would
|
|
* count as an dropped frame. */
|
|
dev_kfree_skb(rx->skb);
|
|
|
|
return TXRX_QUEUED;
|
|
}
|
|
|
|
|
|
static inline struct ieee80211_fragment_entry *
|
|
ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
|
|
unsigned int frag, unsigned int seq, int rx_queue,
|
|
struct sk_buff **skb)
|
|
{
|
|
struct ieee80211_fragment_entry *entry;
|
|
int idx;
|
|
|
|
idx = sdata->fragment_next;
|
|
entry = &sdata->fragments[sdata->fragment_next++];
|
|
if (sdata->fragment_next >= IEEE80211_FRAGMENT_MAX)
|
|
sdata->fragment_next = 0;
|
|
|
|
if (!skb_queue_empty(&entry->skb_list)) {
|
|
#ifdef CONFIG_MAC80211_DEBUG
|
|
struct ieee80211_hdr *hdr =
|
|
(struct ieee80211_hdr *) entry->skb_list.next->data;
|
|
printk(KERN_DEBUG "%s: RX reassembly removed oldest "
|
|
"fragment entry (idx=%d age=%lu seq=%d last_frag=%d "
|
|
"addr1=" MAC_FMT " addr2=" MAC_FMT "\n",
|
|
sdata->dev->name, idx,
|
|
jiffies - entry->first_frag_time, entry->seq,
|
|
entry->last_frag, MAC_ARG(hdr->addr1),
|
|
MAC_ARG(hdr->addr2));
|
|
#endif /* CONFIG_MAC80211_DEBUG */
|
|
__skb_queue_purge(&entry->skb_list);
|
|
}
|
|
|
|
__skb_queue_tail(&entry->skb_list, *skb); /* no need for locking */
|
|
*skb = NULL;
|
|
entry->first_frag_time = jiffies;
|
|
entry->seq = seq;
|
|
entry->rx_queue = rx_queue;
|
|
entry->last_frag = frag;
|
|
entry->ccmp = 0;
|
|
entry->extra_len = 0;
|
|
|
|
return entry;
|
|
}
|
|
|
|
|
|
static inline struct ieee80211_fragment_entry *
|
|
ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata,
|
|
u16 fc, unsigned int frag, unsigned int seq,
|
|
int rx_queue, struct ieee80211_hdr *hdr)
|
|
{
|
|
struct ieee80211_fragment_entry *entry;
|
|
int i, idx;
|
|
|
|
idx = sdata->fragment_next;
|
|
for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) {
|
|
struct ieee80211_hdr *f_hdr;
|
|
u16 f_fc;
|
|
|
|
idx--;
|
|
if (idx < 0)
|
|
idx = IEEE80211_FRAGMENT_MAX - 1;
|
|
|
|
entry = &sdata->fragments[idx];
|
|
if (skb_queue_empty(&entry->skb_list) || entry->seq != seq ||
|
|
entry->rx_queue != rx_queue ||
|
|
entry->last_frag + 1 != frag)
|
|
continue;
|
|
|
|
f_hdr = (struct ieee80211_hdr *) entry->skb_list.next->data;
|
|
f_fc = le16_to_cpu(f_hdr->frame_control);
|
|
|
|
if ((fc & IEEE80211_FCTL_FTYPE) != (f_fc & IEEE80211_FCTL_FTYPE) ||
|
|
compare_ether_addr(hdr->addr1, f_hdr->addr1) != 0 ||
|
|
compare_ether_addr(hdr->addr2, f_hdr->addr2) != 0)
|
|
continue;
|
|
|
|
if (entry->first_frag_time + 2 * HZ < jiffies) {
|
|
__skb_queue_purge(&entry->skb_list);
|
|
continue;
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx)
|
|
{
|
|
struct ieee80211_hdr *hdr;
|
|
u16 sc;
|
|
unsigned int frag, seq;
|
|
struct ieee80211_fragment_entry *entry;
|
|
struct sk_buff *skb;
|
|
|
|
hdr = (struct ieee80211_hdr *) rx->skb->data;
|
|
sc = le16_to_cpu(hdr->seq_ctrl);
|
|
frag = sc & IEEE80211_SCTL_FRAG;
|
|
|
|
if (likely((!(rx->fc & IEEE80211_FCTL_MOREFRAGS) && frag == 0) ||
|
|
(rx->skb)->len < 24 ||
|
|
is_multicast_ether_addr(hdr->addr1))) {
|
|
/* not fragmented */
|
|
goto out;
|
|
}
|
|
I802_DEBUG_INC(rx->local->rx_handlers_fragments);
|
|
|
|
seq = (sc & IEEE80211_SCTL_SEQ) >> 4;
|
|
|
|
if (frag == 0) {
|
|
/* This is the first fragment of a new frame. */
|
|
entry = ieee80211_reassemble_add(rx->sdata, frag, seq,
|
|
rx->u.rx.queue, &(rx->skb));
|
|
if (rx->key && rx->key->alg == ALG_CCMP &&
|
|
(rx->fc & IEEE80211_FCTL_PROTECTED)) {
|
|
/* Store CCMP PN so that we can verify that the next
|
|
* fragment has a sequential PN value. */
|
|
entry->ccmp = 1;
|
|
memcpy(entry->last_pn,
|
|
rx->key->u.ccmp.rx_pn[rx->u.rx.queue],
|
|
CCMP_PN_LEN);
|
|
}
|
|
return TXRX_QUEUED;
|
|
}
|
|
|
|
/* This is a fragment for a frame that should already be pending in
|
|
* fragment cache. Add this fragment to the end of the pending entry.
|
|
*/
|
|
entry = ieee80211_reassemble_find(rx->sdata, rx->fc, frag, seq,
|
|
rx->u.rx.queue, hdr);
|
|
if (!entry) {
|
|
I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
|
|
return TXRX_DROP;
|
|
}
|
|
|
|
/* Verify that MPDUs within one MSDU have sequential PN values.
|
|
* (IEEE 802.11i, 8.3.3.4.5) */
|
|
if (entry->ccmp) {
|
|
int i;
|
|
u8 pn[CCMP_PN_LEN], *rpn;
|
|
if (!rx->key || rx->key->alg != ALG_CCMP)
|
|
return TXRX_DROP;
|
|
memcpy(pn, entry->last_pn, CCMP_PN_LEN);
|
|
for (i = CCMP_PN_LEN - 1; i >= 0; i--) {
|
|
pn[i]++;
|
|
if (pn[i])
|
|
break;
|
|
}
|
|
rpn = rx->key->u.ccmp.rx_pn[rx->u.rx.queue];
|
|
if (memcmp(pn, rpn, CCMP_PN_LEN) != 0) {
|
|
printk(KERN_DEBUG "%s: defrag: CCMP PN not sequential"
|
|
" A2=" MAC_FMT " PN=%02x%02x%02x%02x%02x%02x "
|
|
"(expected %02x%02x%02x%02x%02x%02x)\n",
|
|
rx->dev->name, MAC_ARG(hdr->addr2),
|
|
rpn[0], rpn[1], rpn[2], rpn[3], rpn[4], rpn[5],
|
|
pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]);
|
|
return TXRX_DROP;
|
|
}
|
|
memcpy(entry->last_pn, pn, CCMP_PN_LEN);
|
|
}
|
|
|
|
skb_pull(rx->skb, ieee80211_get_hdrlen(rx->fc));
|
|
__skb_queue_tail(&entry->skb_list, rx->skb);
|
|
entry->last_frag = frag;
|
|
entry->extra_len += rx->skb->len;
|
|
if (rx->fc & IEEE80211_FCTL_MOREFRAGS) {
|
|
rx->skb = NULL;
|
|
return TXRX_QUEUED;
|
|
}
|
|
|
|
rx->skb = __skb_dequeue(&entry->skb_list);
|
|
if (skb_tailroom(rx->skb) < entry->extra_len) {
|
|
I802_DEBUG_INC(rx->local->rx_expand_skb_head2);
|
|
if (unlikely(pskb_expand_head(rx->skb, 0, entry->extra_len,
|
|
GFP_ATOMIC))) {
|
|
I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
|
|
__skb_queue_purge(&entry->skb_list);
|
|
return TXRX_DROP;
|
|
}
|
|
}
|
|
while ((skb = __skb_dequeue(&entry->skb_list))) {
|
|
memcpy(skb_put(rx->skb, skb->len), skb->data, skb->len);
|
|
dev_kfree_skb(skb);
|
|
}
|
|
|
|
/* Complete frame has been reassembled - process it now */
|
|
rx->fragmented = 1;
|
|
|
|
out:
|
|
if (rx->sta)
|
|
rx->sta->rx_packets++;
|
|
if (is_multicast_ether_addr(hdr->addr1))
|
|
rx->local->dot11MulticastReceivedFrameCount++;
|
|
else
|
|
ieee80211_led_rx(rx->local);
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_rx_h_monitor(struct ieee80211_txrx_data *rx)
|
|
{
|
|
if (rx->sdata->type == IEEE80211_IF_TYPE_MNTR) {
|
|
ieee80211_rx_monitor(rx->dev, rx->skb, rx->u.rx.status);
|
|
return TXRX_QUEUED;
|
|
}
|
|
|
|
if (rx->u.rx.status->flag & RX_FLAG_RADIOTAP)
|
|
skb_pull(rx->skb, ieee80211_get_radiotap_len(rx->skb));
|
|
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
|
|
{
|
|
struct ieee80211_hdr *hdr;
|
|
int always_sta_key;
|
|
hdr = (struct ieee80211_hdr *) rx->skb->data;
|
|
|
|
/* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */
|
|
if (rx->sta && !is_multicast_ether_addr(hdr->addr1)) {
|
|
if (unlikely(rx->fc & IEEE80211_FCTL_RETRY &&
|
|
rx->sta->last_seq_ctrl[rx->u.rx.queue] ==
|
|
hdr->seq_ctrl)) {
|
|
if (rx->u.rx.ra_match) {
|
|
rx->local->dot11FrameDuplicateCount++;
|
|
rx->sta->num_duplicates++;
|
|
}
|
|
return TXRX_DROP;
|
|
} else
|
|
rx->sta->last_seq_ctrl[rx->u.rx.queue] = hdr->seq_ctrl;
|
|
}
|
|
|
|
if ((rx->local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) &&
|
|
rx->skb->len > FCS_LEN)
|
|
skb_trim(rx->skb, rx->skb->len - FCS_LEN);
|
|
|
|
if (unlikely(rx->skb->len < 16)) {
|
|
I802_DEBUG_INC(rx->local->rx_handlers_drop_short);
|
|
return TXRX_DROP;
|
|
}
|
|
|
|
if (!rx->u.rx.ra_match)
|
|
rx->skb->pkt_type = PACKET_OTHERHOST;
|
|
else if (compare_ether_addr(rx->dev->dev_addr, hdr->addr1) == 0)
|
|
rx->skb->pkt_type = PACKET_HOST;
|
|
else if (is_multicast_ether_addr(hdr->addr1)) {
|
|
if (is_broadcast_ether_addr(hdr->addr1))
|
|
rx->skb->pkt_type = PACKET_BROADCAST;
|
|
else
|
|
rx->skb->pkt_type = PACKET_MULTICAST;
|
|
} else
|
|
rx->skb->pkt_type = PACKET_OTHERHOST;
|
|
|
|
/* Drop disallowed frame classes based on STA auth/assoc state;
|
|
* IEEE 802.11, Chap 5.5.
|
|
*
|
|
* 80211.o does filtering only based on association state, i.e., it
|
|
* drops Class 3 frames from not associated stations. hostapd sends
|
|
* deauth/disassoc frames when needed. In addition, hostapd is
|
|
* responsible for filtering on both auth and assoc states.
|
|
*/
|
|
if (unlikely(((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA ||
|
|
((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL &&
|
|
(rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)) &&
|
|
rx->sdata->type != IEEE80211_IF_TYPE_IBSS &&
|
|
(!rx->sta || !(rx->sta->flags & WLAN_STA_ASSOC)))) {
|
|
if ((!(rx->fc & IEEE80211_FCTL_FROMDS) &&
|
|
!(rx->fc & IEEE80211_FCTL_TODS) &&
|
|
(rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)
|
|
|| !rx->u.rx.ra_match) {
|
|
/* Drop IBSS frames and frames for other hosts
|
|
* silently. */
|
|
return TXRX_DROP;
|
|
}
|
|
|
|
if (!rx->local->apdev)
|
|
return TXRX_DROP;
|
|
|
|
ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
|
|
ieee80211_msg_sta_not_assoc);
|
|
return TXRX_QUEUED;
|
|
}
|
|
|
|
if (rx->sdata->type == IEEE80211_IF_TYPE_STA)
|
|
always_sta_key = 0;
|
|
else
|
|
always_sta_key = 1;
|
|
|
|
if (rx->sta && rx->sta->key && always_sta_key) {
|
|
rx->key = rx->sta->key;
|
|
} else {
|
|
if (rx->sta && rx->sta->key)
|
|
rx->key = rx->sta->key;
|
|
else
|
|
rx->key = rx->sdata->default_key;
|
|
|
|
if ((rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) &&
|
|
rx->fc & IEEE80211_FCTL_PROTECTED) {
|
|
int keyidx = ieee80211_wep_get_keyidx(rx->skb);
|
|
|
|
if (keyidx >= 0 && keyidx < NUM_DEFAULT_KEYS &&
|
|
(!rx->sta || !rx->sta->key || keyidx > 0))
|
|
rx->key = rx->sdata->keys[keyidx];
|
|
|
|
if (!rx->key) {
|
|
if (!rx->u.rx.ra_match)
|
|
return TXRX_DROP;
|
|
printk(KERN_DEBUG "%s: RX WEP frame with "
|
|
"unknown keyidx %d (A1=" MAC_FMT " A2="
|
|
MAC_FMT " A3=" MAC_FMT ")\n",
|
|
rx->dev->name, keyidx,
|
|
MAC_ARG(hdr->addr1),
|
|
MAC_ARG(hdr->addr2),
|
|
MAC_ARG(hdr->addr3));
|
|
if (!rx->local->apdev)
|
|
return TXRX_DROP;
|
|
ieee80211_rx_mgmt(
|
|
rx->local, rx->skb, rx->u.rx.status,
|
|
ieee80211_msg_wep_frame_unknown_key);
|
|
return TXRX_QUEUED;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rx->fc & IEEE80211_FCTL_PROTECTED && rx->key && rx->u.rx.ra_match) {
|
|
rx->key->tx_rx_count++;
|
|
if (unlikely(rx->local->key_tx_rx_threshold &&
|
|
rx->key->tx_rx_count >
|
|
rx->local->key_tx_rx_threshold)) {
|
|
ieee80211_key_threshold_notify(rx->dev, rx->key,
|
|
rx->sta);
|
|
}
|
|
}
|
|
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_rx_h_sta_process(struct ieee80211_txrx_data *rx)
|
|
{
|
|
struct sta_info *sta = rx->sta;
|
|
struct net_device *dev = rx->dev;
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
|
|
|
|
if (!sta)
|
|
return TXRX_CONTINUE;
|
|
|
|
/* Update last_rx only for IBSS packets which are for the current
|
|
* BSSID to avoid keeping the current IBSS network alive in cases where
|
|
* other STAs are using different BSSID. */
|
|
if (rx->sdata->type == IEEE80211_IF_TYPE_IBSS) {
|
|
u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len);
|
|
if (compare_ether_addr(bssid, rx->sdata->u.sta.bssid) == 0)
|
|
sta->last_rx = jiffies;
|
|
} else
|
|
if (!is_multicast_ether_addr(hdr->addr1) ||
|
|
rx->sdata->type == IEEE80211_IF_TYPE_STA) {
|
|
/* Update last_rx only for unicast frames in order to prevent
|
|
* the Probe Request frames (the only broadcast frames from a
|
|
* STA in infrastructure mode) from keeping a connection alive.
|
|
*/
|
|
sta->last_rx = jiffies;
|
|
}
|
|
|
|
if (!rx->u.rx.ra_match)
|
|
return TXRX_CONTINUE;
|
|
|
|
sta->rx_fragments++;
|
|
sta->rx_bytes += rx->skb->len;
|
|
sta->last_rssi = (sta->last_rssi * 15 +
|
|
rx->u.rx.status->ssi) / 16;
|
|
sta->last_signal = (sta->last_signal * 15 +
|
|
rx->u.rx.status->signal) / 16;
|
|
sta->last_noise = (sta->last_noise * 15 +
|
|
rx->u.rx.status->noise) / 16;
|
|
|
|
if (!(rx->fc & IEEE80211_FCTL_MOREFRAGS)) {
|
|
/* Change STA power saving mode only in the end of a frame
|
|
* exchange sequence */
|
|
if ((sta->flags & WLAN_STA_PS) && !(rx->fc & IEEE80211_FCTL_PM))
|
|
rx->u.rx.sent_ps_buffered += ap_sta_ps_end(dev, sta);
|
|
else if (!(sta->flags & WLAN_STA_PS) &&
|
|
(rx->fc & IEEE80211_FCTL_PM))
|
|
ap_sta_ps_start(dev, sta);
|
|
}
|
|
|
|
/* Drop data::nullfunc frames silently, since they are used only to
|
|
* control station power saving mode. */
|
|
if ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
|
|
(rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_NULLFUNC) {
|
|
I802_DEBUG_INC(rx->local->rx_handlers_drop_nullfunc);
|
|
/* Update counter and free packet here to avoid counting this
|
|
* as a dropped packed. */
|
|
sta->rx_packets++;
|
|
dev_kfree_skb(rx->skb);
|
|
return TXRX_QUEUED;
|
|
}
|
|
|
|
return TXRX_CONTINUE;
|
|
} /* ieee80211_rx_h_sta_process */
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_rx_h_wep_weak_iv_detection(struct ieee80211_txrx_data *rx)
|
|
{
|
|
if (!rx->sta || !(rx->fc & IEEE80211_FCTL_PROTECTED) ||
|
|
(rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
|
|
!rx->key || rx->key->alg != ALG_WEP || !rx->u.rx.ra_match)
|
|
return TXRX_CONTINUE;
|
|
|
|
/* Check for weak IVs, if hwaccel did not remove IV from the frame */
|
|
if ((rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) ||
|
|
rx->key->force_sw_encrypt) {
|
|
u8 *iv = ieee80211_wep_is_weak_iv(rx->skb, rx->key);
|
|
if (iv) {
|
|
rx->sta->wep_weak_iv_count++;
|
|
}
|
|
}
|
|
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_rx_h_wep_decrypt(struct ieee80211_txrx_data *rx)
|
|
{
|
|
/* If the device handles decryption totally, skip this test */
|
|
if (rx->local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP)
|
|
return TXRX_CONTINUE;
|
|
|
|
if ((rx->key && rx->key->alg != ALG_WEP) ||
|
|
!(rx->fc & IEEE80211_FCTL_PROTECTED) ||
|
|
((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
|
|
((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
|
|
(rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH)))
|
|
return TXRX_CONTINUE;
|
|
|
|
if (!rx->key) {
|
|
printk(KERN_DEBUG "%s: RX WEP frame, but no key set\n",
|
|
rx->dev->name);
|
|
return TXRX_DROP;
|
|
}
|
|
|
|
if (!(rx->u.rx.status->flag & RX_FLAG_DECRYPTED) ||
|
|
rx->key->force_sw_encrypt) {
|
|
if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key)) {
|
|
printk(KERN_DEBUG "%s: RX WEP frame, decrypt "
|
|
"failed\n", rx->dev->name);
|
|
return TXRX_DROP;
|
|
}
|
|
} else if (rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) {
|
|
ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
|
|
/* remove ICV */
|
|
skb_trim(rx->skb, rx->skb->len - 4);
|
|
}
|
|
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_rx_h_802_1x_pae(struct ieee80211_txrx_data *rx)
|
|
{
|
|
if (rx->sdata->eapol && ieee80211_is_eapol(rx->skb) &&
|
|
rx->sdata->type != IEEE80211_IF_TYPE_STA && rx->u.rx.ra_match) {
|
|
/* Pass both encrypted and unencrypted EAPOL frames to user
|
|
* space for processing. */
|
|
if (!rx->local->apdev)
|
|
return TXRX_DROP;
|
|
ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
|
|
ieee80211_msg_normal);
|
|
return TXRX_QUEUED;
|
|
}
|
|
|
|
if (unlikely(rx->sdata->ieee802_1x &&
|
|
(rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
|
|
(rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_NULLFUNC &&
|
|
(!rx->sta || !(rx->sta->flags & WLAN_STA_AUTHORIZED)) &&
|
|
!ieee80211_is_eapol(rx->skb))) {
|
|
#ifdef CONFIG_MAC80211_DEBUG
|
|
struct ieee80211_hdr *hdr =
|
|
(struct ieee80211_hdr *) rx->skb->data;
|
|
printk(KERN_DEBUG "%s: dropped frame from " MAC_FMT
|
|
" (unauthorized port)\n", rx->dev->name,
|
|
MAC_ARG(hdr->addr2));
|
|
#endif /* CONFIG_MAC80211_DEBUG */
|
|
return TXRX_DROP;
|
|
}
|
|
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_rx_h_drop_unencrypted(struct ieee80211_txrx_data *rx)
|
|
{
|
|
/* If the device handles decryption totally, skip this test */
|
|
if (rx->local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP)
|
|
return TXRX_CONTINUE;
|
|
|
|
/* Drop unencrypted frames if key is set. */
|
|
if (unlikely(!(rx->fc & IEEE80211_FCTL_PROTECTED) &&
|
|
(rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
|
|
(rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_NULLFUNC &&
|
|
(rx->key || rx->sdata->drop_unencrypted) &&
|
|
(rx->sdata->eapol == 0 ||
|
|
!ieee80211_is_eapol(rx->skb)))) {
|
|
printk(KERN_DEBUG "%s: RX non-WEP frame, but expected "
|
|
"encryption\n", rx->dev->name);
|
|
return TXRX_DROP;
|
|
}
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx)
|
|
{
|
|
struct ieee80211_sub_if_data *sdata;
|
|
|
|
if (!rx->u.rx.ra_match)
|
|
return TXRX_DROP;
|
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
|
|
if ((sdata->type == IEEE80211_IF_TYPE_STA ||
|
|
sdata->type == IEEE80211_IF_TYPE_IBSS) &&
|
|
!rx->local->user_space_mlme) {
|
|
ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status);
|
|
} else {
|
|
/* Management frames are sent to hostapd for processing */
|
|
if (!rx->local->apdev)
|
|
return TXRX_DROP;
|
|
ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
|
|
ieee80211_msg_normal);
|
|
}
|
|
return TXRX_QUEUED;
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx)
|
|
{
|
|
struct ieee80211_local *local = rx->local;
|
|
struct sk_buff *skb = rx->skb;
|
|
|
|
if (unlikely(local->sta_scanning != 0)) {
|
|
ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status);
|
|
return TXRX_QUEUED;
|
|
}
|
|
|
|
if (unlikely(rx->u.rx.in_scan)) {
|
|
/* scanning finished during invoking of handlers */
|
|
I802_DEBUG_INC(local->rx_handlers_drop_passive_scan);
|
|
return TXRX_DROP;
|
|
}
|
|
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
|
|
static void ieee80211_rx_michael_mic_report(struct net_device *dev,
|
|
struct ieee80211_hdr *hdr,
|
|
struct sta_info *sta,
|
|
struct ieee80211_txrx_data *rx)
|
|
{
|
|
int keyidx, hdrlen;
|
|
|
|
hdrlen = ieee80211_get_hdrlen_from_skb(rx->skb);
|
|
if (rx->skb->len >= hdrlen + 4)
|
|
keyidx = rx->skb->data[hdrlen + 3] >> 6;
|
|
else
|
|
keyidx = -1;
|
|
|
|
/* TODO: verify that this is not triggered by fragmented
|
|
* frames (hw does not verify MIC for them). */
|
|
printk(KERN_DEBUG "%s: TKIP hwaccel reported Michael MIC "
|
|
"failure from " MAC_FMT " to " MAC_FMT " keyidx=%d\n",
|
|
dev->name, MAC_ARG(hdr->addr2), MAC_ARG(hdr->addr1), keyidx);
|
|
|
|
if (!sta) {
|
|
/* Some hardware versions seem to generate incorrect
|
|
* Michael MIC reports; ignore them to avoid triggering
|
|
* countermeasures. */
|
|
printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
|
|
"error for unknown address " MAC_FMT "\n",
|
|
dev->name, MAC_ARG(hdr->addr2));
|
|
goto ignore;
|
|
}
|
|
|
|
if (!(rx->fc & IEEE80211_FCTL_PROTECTED)) {
|
|
printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
|
|
"error for a frame with no ISWEP flag (src "
|
|
MAC_FMT ")\n", dev->name, MAC_ARG(hdr->addr2));
|
|
goto ignore;
|
|
}
|
|
|
|
if ((rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) &&
|
|
rx->sdata->type == IEEE80211_IF_TYPE_AP) {
|
|
keyidx = ieee80211_wep_get_keyidx(rx->skb);
|
|
/* AP with Pairwise keys support should never receive Michael
|
|
* MIC errors for non-zero keyidx because these are reserved
|
|
* for group keys and only the AP is sending real multicast
|
|
* frames in BSS. */
|
|
if (keyidx) {
|
|
printk(KERN_DEBUG "%s: ignored Michael MIC error for "
|
|
"a frame with non-zero keyidx (%d) (src " MAC_FMT
|
|
")\n", dev->name, keyidx, MAC_ARG(hdr->addr2));
|
|
goto ignore;
|
|
}
|
|
}
|
|
|
|
if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
|
|
((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
|
|
(rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH)) {
|
|
printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
|
|
"error for a frame that cannot be encrypted "
|
|
"(fc=0x%04x) (src " MAC_FMT ")\n",
|
|
dev->name, rx->fc, MAC_ARG(hdr->addr2));
|
|
goto ignore;
|
|
}
|
|
|
|
do {
|
|
union iwreq_data wrqu;
|
|
char *buf = kmalloc(128, GFP_ATOMIC);
|
|
if (!buf)
|
|
break;
|
|
|
|
/* TODO: needed parameters: count, key type, TSC */
|
|
sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
|
|
"keyid=%d %scast addr=" MAC_FMT ")",
|
|
keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni",
|
|
MAC_ARG(hdr->addr2));
|
|
memset(&wrqu, 0, sizeof(wrqu));
|
|
wrqu.data.length = strlen(buf);
|
|
wireless_send_event(rx->dev, IWEVCUSTOM, &wrqu, buf);
|
|
kfree(buf);
|
|
} while (0);
|
|
|
|
/* TODO: consider verifying the MIC error report with software
|
|
* implementation if we get too many spurious reports from the
|
|
* hardware. */
|
|
if (!rx->local->apdev)
|
|
goto ignore;
|
|
ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
|
|
ieee80211_msg_michael_mic_failure);
|
|
return;
|
|
|
|
ignore:
|
|
dev_kfree_skb(rx->skb);
|
|
rx->skb = NULL;
|
|
}
|
|
|
|
static inline ieee80211_txrx_result __ieee80211_invoke_rx_handlers(
|
|
struct ieee80211_local *local,
|
|
ieee80211_rx_handler *handlers,
|
|
struct ieee80211_txrx_data *rx,
|
|
struct sta_info *sta)
|
|
{
|
|
ieee80211_rx_handler *handler;
|
|
ieee80211_txrx_result res = TXRX_DROP;
|
|
|
|
for (handler = handlers; *handler != NULL; handler++) {
|
|
res = (*handler)(rx);
|
|
if (res != TXRX_CONTINUE) {
|
|
if (res == TXRX_DROP) {
|
|
I802_DEBUG_INC(local->rx_handlers_drop);
|
|
if (sta)
|
|
sta->rx_dropped++;
|
|
}
|
|
if (res == TXRX_QUEUED)
|
|
I802_DEBUG_INC(local->rx_handlers_queued);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (res == TXRX_DROP) {
|
|
dev_kfree_skb(rx->skb);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static inline void ieee80211_invoke_rx_handlers(struct ieee80211_local *local,
|
|
ieee80211_rx_handler *handlers,
|
|
struct ieee80211_txrx_data *rx,
|
|
struct sta_info *sta)
|
|
{
|
|
if (__ieee80211_invoke_rx_handlers(local, handlers, rx, sta) ==
|
|
TXRX_CONTINUE)
|
|
dev_kfree_skb(rx->skb);
|
|
}
|
|
|
|
/*
|
|
* This is the receive path handler. It is called by a low level driver when an
|
|
* 802.11 MPDU is received from the hardware.
|
|
*/
|
|
void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
|
|
struct ieee80211_rx_status *status)
|
|
{
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
struct ieee80211_sub_if_data *sdata;
|
|
struct sta_info *sta;
|
|
struct ieee80211_hdr *hdr;
|
|
struct ieee80211_txrx_data rx;
|
|
u16 type;
|
|
int multicast;
|
|
int radiotap_len = 0;
|
|
|
|
if (status->flag & RX_FLAG_RADIOTAP) {
|
|
radiotap_len = ieee80211_get_radiotap_len(skb);
|
|
skb_pull(skb, radiotap_len);
|
|
}
|
|
|
|
hdr = (struct ieee80211_hdr *) skb->data;
|
|
memset(&rx, 0, sizeof(rx));
|
|
rx.skb = skb;
|
|
rx.local = local;
|
|
|
|
rx.u.rx.status = status;
|
|
rx.fc = skb->len >= 2 ? le16_to_cpu(hdr->frame_control) : 0;
|
|
type = rx.fc & IEEE80211_FCTL_FTYPE;
|
|
if (type == IEEE80211_FTYPE_DATA || type == IEEE80211_FTYPE_MGMT)
|
|
local->dot11ReceivedFragmentCount++;
|
|
multicast = is_multicast_ether_addr(hdr->addr1);
|
|
|
|
if (skb->len >= 16)
|
|
sta = rx.sta = sta_info_get(local, hdr->addr2);
|
|
else
|
|
sta = rx.sta = NULL;
|
|
|
|
if (sta) {
|
|
rx.dev = sta->dev;
|
|
rx.sdata = IEEE80211_DEV_TO_SUB_IF(rx.dev);
|
|
}
|
|
|
|
if ((status->flag & RX_FLAG_MMIC_ERROR)) {
|
|
ieee80211_rx_michael_mic_report(local->mdev, hdr, sta, &rx);
|
|
goto end;
|
|
}
|
|
|
|
if (unlikely(local->sta_scanning))
|
|
rx.u.rx.in_scan = 1;
|
|
|
|
if (__ieee80211_invoke_rx_handlers(local, local->rx_pre_handlers, &rx,
|
|
sta) != TXRX_CONTINUE)
|
|
goto end;
|
|
skb = rx.skb;
|
|
|
|
skb_push(skb, radiotap_len);
|
|
if (sta && !sta->assoc_ap && !(sta->flags & WLAN_STA_WDS) &&
|
|
!local->iff_promiscs && !multicast) {
|
|
rx.u.rx.ra_match = 1;
|
|
ieee80211_invoke_rx_handlers(local, local->rx_handlers, &rx,
|
|
sta);
|
|
} else {
|
|
struct ieee80211_sub_if_data *prev = NULL;
|
|
struct sk_buff *skb_new;
|
|
u8 *bssid = ieee80211_get_bssid(hdr, skb->len - radiotap_len);
|
|
|
|
read_lock(&local->sub_if_lock);
|
|
list_for_each_entry(sdata, &local->sub_if_list, list) {
|
|
rx.u.rx.ra_match = 1;
|
|
switch (sdata->type) {
|
|
case IEEE80211_IF_TYPE_STA:
|
|
if (!bssid)
|
|
continue;
|
|
if (!ieee80211_bssid_match(bssid,
|
|
sdata->u.sta.bssid)) {
|
|
if (!rx.u.rx.in_scan)
|
|
continue;
|
|
rx.u.rx.ra_match = 0;
|
|
} else if (!multicast &&
|
|
compare_ether_addr(sdata->dev->dev_addr,
|
|
hdr->addr1) != 0) {
|
|
if (!sdata->promisc)
|
|
continue;
|
|
rx.u.rx.ra_match = 0;
|
|
}
|
|
break;
|
|
case IEEE80211_IF_TYPE_IBSS:
|
|
if (!bssid)
|
|
continue;
|
|
if (!ieee80211_bssid_match(bssid,
|
|
sdata->u.sta.bssid)) {
|
|
if (!rx.u.rx.in_scan)
|
|
continue;
|
|
rx.u.rx.ra_match = 0;
|
|
} else if (!multicast &&
|
|
compare_ether_addr(sdata->dev->dev_addr,
|
|
hdr->addr1) != 0) {
|
|
if (!sdata->promisc)
|
|
continue;
|
|
rx.u.rx.ra_match = 0;
|
|
} else if (!sta)
|
|
sta = rx.sta =
|
|
ieee80211_ibss_add_sta(sdata->dev,
|
|
skb, bssid,
|
|
hdr->addr2);
|
|
break;
|
|
case IEEE80211_IF_TYPE_AP:
|
|
if (!bssid) {
|
|
if (compare_ether_addr(sdata->dev->dev_addr,
|
|
hdr->addr1) != 0)
|
|
continue;
|
|
} else if (!ieee80211_bssid_match(bssid,
|
|
sdata->dev->dev_addr)) {
|
|
if (!rx.u.rx.in_scan)
|
|
continue;
|
|
rx.u.rx.ra_match = 0;
|
|
}
|
|
if (sdata->dev == local->mdev &&
|
|
!rx.u.rx.in_scan)
|
|
/* do not receive anything via
|
|
* master device when not scanning */
|
|
continue;
|
|
break;
|
|
case IEEE80211_IF_TYPE_WDS:
|
|
if (bssid ||
|
|
(rx.fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)
|
|
continue;
|
|
if (compare_ether_addr(sdata->u.wds.remote_addr,
|
|
hdr->addr2) != 0)
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
if (prev) {
|
|
skb_new = skb_copy(skb, GFP_ATOMIC);
|
|
if (!skb_new) {
|
|
if (net_ratelimit())
|
|
printk(KERN_DEBUG "%s: failed to copy "
|
|
"multicast frame for %s",
|
|
local->mdev->name, prev->dev->name);
|
|
continue;
|
|
}
|
|
rx.skb = skb_new;
|
|
rx.dev = prev->dev;
|
|
rx.sdata = prev;
|
|
ieee80211_invoke_rx_handlers(local,
|
|
local->rx_handlers,
|
|
&rx, sta);
|
|
}
|
|
prev = sdata;
|
|
}
|
|
if (prev) {
|
|
rx.skb = skb;
|
|
rx.dev = prev->dev;
|
|
rx.sdata = prev;
|
|
ieee80211_invoke_rx_handlers(local, local->rx_handlers,
|
|
&rx, sta);
|
|
} else
|
|
dev_kfree_skb(skb);
|
|
read_unlock(&local->sub_if_lock);
|
|
}
|
|
|
|
end:
|
|
if (sta)
|
|
sta_info_put(sta);
|
|
}
|
|
EXPORT_SYMBOL(__ieee80211_rx);
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_tx_h_load_stats(struct ieee80211_txrx_data *tx)
|
|
{
|
|
struct ieee80211_local *local = tx->local;
|
|
struct ieee80211_hw_mode *mode = tx->u.tx.mode;
|
|
struct sk_buff *skb = tx->skb;
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
|
u32 load = 0, hdrtime;
|
|
|
|
/* TODO: this could be part of tx_status handling, so that the number
|
|
* of retries would be known; TX rate should in that case be stored
|
|
* somewhere with the packet */
|
|
|
|
/* Estimate total channel use caused by this frame */
|
|
|
|
/* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values,
|
|
* 1 usec = 1/8 * (1080 / 10) = 13.5 */
|
|
|
|
if (mode->mode == MODE_IEEE80211A ||
|
|
mode->mode == MODE_ATHEROS_TURBO ||
|
|
mode->mode == MODE_ATHEROS_TURBOG ||
|
|
(mode->mode == MODE_IEEE80211G &&
|
|
tx->u.tx.rate->flags & IEEE80211_RATE_ERP))
|
|
hdrtime = CHAN_UTIL_HDR_SHORT;
|
|
else
|
|
hdrtime = CHAN_UTIL_HDR_LONG;
|
|
|
|
load = hdrtime;
|
|
if (!is_multicast_ether_addr(hdr->addr1))
|
|
load += hdrtime;
|
|
|
|
if (tx->u.tx.control->flags & IEEE80211_TXCTL_USE_RTS_CTS)
|
|
load += 2 * hdrtime;
|
|
else if (tx->u.tx.control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)
|
|
load += hdrtime;
|
|
|
|
load += skb->len * tx->u.tx.rate->rate_inv;
|
|
|
|
if (tx->u.tx.extra_frag) {
|
|
int i;
|
|
for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
|
|
load += 2 * hdrtime;
|
|
load += tx->u.tx.extra_frag[i]->len *
|
|
tx->u.tx.rate->rate;
|
|
}
|
|
}
|
|
|
|
/* Divide channel_use by 8 to avoid wrapping around the counter */
|
|
load >>= CHAN_UTIL_SHIFT;
|
|
local->channel_use_raw += load;
|
|
if (tx->sta)
|
|
tx->sta->channel_use_raw += load;
|
|
tx->sdata->channel_use_raw += load;
|
|
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_rx_h_load_stats(struct ieee80211_txrx_data *rx)
|
|
{
|
|
struct ieee80211_local *local = rx->local;
|
|
struct sk_buff *skb = rx->skb;
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
|
u32 load = 0, hdrtime;
|
|
struct ieee80211_rate *rate;
|
|
struct ieee80211_hw_mode *mode = local->hw.conf.mode;
|
|
int i;
|
|
|
|
/* Estimate total channel use caused by this frame */
|
|
|
|
if (unlikely(mode->num_rates < 0))
|
|
return TXRX_CONTINUE;
|
|
|
|
rate = &mode->rates[0];
|
|
for (i = 0; i < mode->num_rates; i++) {
|
|
if (mode->rates[i].val == rx->u.rx.status->rate) {
|
|
rate = &mode->rates[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values,
|
|
* 1 usec = 1/8 * (1080 / 10) = 13.5 */
|
|
|
|
if (mode->mode == MODE_IEEE80211A ||
|
|
mode->mode == MODE_ATHEROS_TURBO ||
|
|
mode->mode == MODE_ATHEROS_TURBOG ||
|
|
(mode->mode == MODE_IEEE80211G &&
|
|
rate->flags & IEEE80211_RATE_ERP))
|
|
hdrtime = CHAN_UTIL_HDR_SHORT;
|
|
else
|
|
hdrtime = CHAN_UTIL_HDR_LONG;
|
|
|
|
load = hdrtime;
|
|
if (!is_multicast_ether_addr(hdr->addr1))
|
|
load += hdrtime;
|
|
|
|
load += skb->len * rate->rate_inv;
|
|
|
|
/* Divide channel_use by 8 to avoid wrapping around the counter */
|
|
load >>= CHAN_UTIL_SHIFT;
|
|
local->channel_use_raw += load;
|
|
if (rx->sta)
|
|
rx->sta->channel_use_raw += load;
|
|
rx->u.rx.load = load;
|
|
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
static ieee80211_txrx_result
|
|
ieee80211_rx_h_if_stats(struct ieee80211_txrx_data *rx)
|
|
{
|
|
rx->sdata->channel_use_raw += rx->u.rx.load;
|
|
return TXRX_CONTINUE;
|
|
}
|
|
|
|
static void ieee80211_stat_refresh(unsigned long data)
|
|
{
|
|
struct ieee80211_local *local = (struct ieee80211_local *) data;
|
|
struct sta_info *sta;
|
|
struct ieee80211_sub_if_data *sdata;
|
|
|
|
if (!local->stat_time)
|
|
return;
|
|
|
|
/* go through all stations */
|
|
spin_lock_bh(&local->sta_lock);
|
|
list_for_each_entry(sta, &local->sta_list, list) {
|
|
sta->channel_use = (sta->channel_use_raw / local->stat_time) /
|
|
CHAN_UTIL_PER_10MS;
|
|
sta->channel_use_raw = 0;
|
|
}
|
|
spin_unlock_bh(&local->sta_lock);
|
|
|
|
/* go through all subinterfaces */
|
|
read_lock(&local->sub_if_lock);
|
|
list_for_each_entry(sdata, &local->sub_if_list, list) {
|
|
sdata->channel_use = (sdata->channel_use_raw /
|
|
local->stat_time) / CHAN_UTIL_PER_10MS;
|
|
sdata->channel_use_raw = 0;
|
|
}
|
|
read_unlock(&local->sub_if_lock);
|
|
|
|
/* hardware interface */
|
|
local->channel_use = (local->channel_use_raw /
|
|
local->stat_time) / CHAN_UTIL_PER_10MS;
|
|
local->channel_use_raw = 0;
|
|
|
|
local->stat_timer.expires = jiffies + HZ * local->stat_time / 100;
|
|
add_timer(&local->stat_timer);
|
|
}
|
|
|
|
|
|
/* This is a version of the rx handler that can be called from hard irq
|
|
* context. Post the skb on the queue and schedule the tasklet */
|
|
void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb,
|
|
struct ieee80211_rx_status *status)
|
|
{
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
|
|
BUILD_BUG_ON(sizeof(struct ieee80211_rx_status) > sizeof(skb->cb));
|
|
|
|
skb->dev = local->mdev;
|
|
/* copy status into skb->cb for use by tasklet */
|
|
memcpy(skb->cb, status, sizeof(*status));
|
|
skb->pkt_type = IEEE80211_RX_MSG;
|
|
skb_queue_tail(&local->skb_queue, skb);
|
|
tasklet_schedule(&local->tasklet);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_rx_irqsafe);
|
|
|
|
void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
|
|
struct sk_buff *skb,
|
|
struct ieee80211_tx_status *status)
|
|
{
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
struct ieee80211_tx_status *saved;
|
|
int tmp;
|
|
|
|
skb->dev = local->mdev;
|
|
saved = kmalloc(sizeof(struct ieee80211_tx_status), GFP_ATOMIC);
|
|
if (unlikely(!saved)) {
|
|
if (net_ratelimit())
|
|
printk(KERN_WARNING "%s: Not enough memory, "
|
|
"dropping tx status", skb->dev->name);
|
|
/* should be dev_kfree_skb_irq, but due to this function being
|
|
* named _irqsafe instead of just _irq we can't be sure that
|
|
* people won't call it from non-irq contexts */
|
|
dev_kfree_skb_any(skb);
|
|
return;
|
|
}
|
|
memcpy(saved, status, sizeof(struct ieee80211_tx_status));
|
|
/* copy pointer to saved status into skb->cb for use by tasklet */
|
|
memcpy(skb->cb, &saved, sizeof(saved));
|
|
|
|
skb->pkt_type = IEEE80211_TX_STATUS_MSG;
|
|
skb_queue_tail(status->control.flags & IEEE80211_TXCTL_REQ_TX_STATUS ?
|
|
&local->skb_queue : &local->skb_queue_unreliable, skb);
|
|
tmp = skb_queue_len(&local->skb_queue) +
|
|
skb_queue_len(&local->skb_queue_unreliable);
|
|
while (tmp > IEEE80211_IRQSAFE_QUEUE_LIMIT &&
|
|
(skb = skb_dequeue(&local->skb_queue_unreliable))) {
|
|
memcpy(&saved, skb->cb, sizeof(saved));
|
|
kfree(saved);
|
|
dev_kfree_skb_irq(skb);
|
|
tmp--;
|
|
I802_DEBUG_INC(local->tx_status_drop);
|
|
}
|
|
tasklet_schedule(&local->tasklet);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_tx_status_irqsafe);
|
|
|
|
static void ieee80211_tasklet_handler(unsigned long data)
|
|
{
|
|
struct ieee80211_local *local = (struct ieee80211_local *) data;
|
|
struct sk_buff *skb;
|
|
struct ieee80211_rx_status rx_status;
|
|
struct ieee80211_tx_status *tx_status;
|
|
|
|
while ((skb = skb_dequeue(&local->skb_queue)) ||
|
|
(skb = skb_dequeue(&local->skb_queue_unreliable))) {
|
|
switch (skb->pkt_type) {
|
|
case IEEE80211_RX_MSG:
|
|
/* status is in skb->cb */
|
|
memcpy(&rx_status, skb->cb, sizeof(rx_status));
|
|
/* Clear skb->type in order to not confuse kernel
|
|
* netstack. */
|
|
skb->pkt_type = 0;
|
|
__ieee80211_rx(local_to_hw(local), skb, &rx_status);
|
|
break;
|
|
case IEEE80211_TX_STATUS_MSG:
|
|
/* get pointer to saved status out of skb->cb */
|
|
memcpy(&tx_status, skb->cb, sizeof(tx_status));
|
|
skb->pkt_type = 0;
|
|
ieee80211_tx_status(local_to_hw(local),
|
|
skb, tx_status);
|
|
kfree(tx_status);
|
|
break;
|
|
default: /* should never get here! */
|
|
printk(KERN_ERR "%s: Unknown message type (%d)\n",
|
|
local->mdev->name, skb->pkt_type);
|
|
dev_kfree_skb(skb);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Remove added headers (e.g., QoS control), encryption header/MIC, etc. to
|
|
* make a prepared TX frame (one that has been given to hw) to look like brand
|
|
* new IEEE 802.11 frame that is ready to go through TX processing again.
|
|
* Also, tx_packet_data in cb is restored from tx_control. */
|
|
static void ieee80211_remove_tx_extra(struct ieee80211_local *local,
|
|
struct ieee80211_key *key,
|
|
struct sk_buff *skb,
|
|
struct ieee80211_tx_control *control)
|
|
{
|
|
int hdrlen, iv_len, mic_len;
|
|
struct ieee80211_tx_packet_data *pkt_data;
|
|
|
|
pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
|
|
pkt_data->ifindex = control->ifindex;
|
|
pkt_data->mgmt_iface = (control->type == IEEE80211_IF_TYPE_MGMT);
|
|
pkt_data->req_tx_status = !!(control->flags & IEEE80211_TXCTL_REQ_TX_STATUS);
|
|
pkt_data->do_not_encrypt = !!(control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT);
|
|
pkt_data->requeue = !!(control->flags & IEEE80211_TXCTL_REQUEUE);
|
|
pkt_data->queue = control->queue;
|
|
|
|
hdrlen = ieee80211_get_hdrlen_from_skb(skb);
|
|
|
|
if (!key)
|
|
goto no_key;
|
|
|
|
switch (key->alg) {
|
|
case ALG_WEP:
|
|
iv_len = WEP_IV_LEN;
|
|
mic_len = WEP_ICV_LEN;
|
|
break;
|
|
case ALG_TKIP:
|
|
iv_len = TKIP_IV_LEN;
|
|
mic_len = TKIP_ICV_LEN;
|
|
break;
|
|
case ALG_CCMP:
|
|
iv_len = CCMP_HDR_LEN;
|
|
mic_len = CCMP_MIC_LEN;
|
|
break;
|
|
default:
|
|
goto no_key;
|
|
}
|
|
|
|
if (skb->len >= mic_len && key->force_sw_encrypt)
|
|
skb_trim(skb, skb->len - mic_len);
|
|
if (skb->len >= iv_len && skb->len > hdrlen) {
|
|
memmove(skb->data + iv_len, skb->data, hdrlen);
|
|
skb_pull(skb, iv_len);
|
|
}
|
|
|
|
no_key:
|
|
{
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
|
u16 fc = le16_to_cpu(hdr->frame_control);
|
|
if ((fc & 0x8C) == 0x88) /* QoS Control Field */ {
|
|
fc &= ~IEEE80211_STYPE_QOS_DATA;
|
|
hdr->frame_control = cpu_to_le16(fc);
|
|
memmove(skb->data + 2, skb->data, hdrlen - 2);
|
|
skb_pull(skb, 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
|
|
struct ieee80211_tx_status *status)
|
|
{
|
|
struct sk_buff *skb2;
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
u16 frag, type;
|
|
u32 msg_type;
|
|
struct ieee80211_tx_status_rtap_hdr *rthdr;
|
|
struct ieee80211_sub_if_data *sdata;
|
|
int monitors;
|
|
|
|
if (!status) {
|
|
printk(KERN_ERR
|
|
"%s: ieee80211_tx_status called with NULL status\n",
|
|
local->mdev->name);
|
|
dev_kfree_skb(skb);
|
|
return;
|
|
}
|
|
|
|
if (status->excessive_retries) {
|
|
struct sta_info *sta;
|
|
sta = sta_info_get(local, hdr->addr1);
|
|
if (sta) {
|
|
if (sta->flags & WLAN_STA_PS) {
|
|
/* The STA is in power save mode, so assume
|
|
* that this TX packet failed because of that.
|
|
*/
|
|
status->excessive_retries = 0;
|
|
status->flags |= IEEE80211_TX_STATUS_TX_FILTERED;
|
|
}
|
|
sta_info_put(sta);
|
|
}
|
|
}
|
|
|
|
if (status->flags & IEEE80211_TX_STATUS_TX_FILTERED) {
|
|
struct sta_info *sta;
|
|
sta = sta_info_get(local, hdr->addr1);
|
|
if (sta) {
|
|
sta->tx_filtered_count++;
|
|
|
|
/* Clear the TX filter mask for this STA when sending
|
|
* the next packet. If the STA went to power save mode,
|
|
* this will happen when it is waking up for the next
|
|
* time. */
|
|
sta->clear_dst_mask = 1;
|
|
|
|
/* TODO: Is the WLAN_STA_PS flag always set here or is
|
|
* the race between RX and TX status causing some
|
|
* packets to be filtered out before 80211.o gets an
|
|
* update for PS status? This seems to be the case, so
|
|
* no changes are likely to be needed. */
|
|
if (sta->flags & WLAN_STA_PS &&
|
|
skb_queue_len(&sta->tx_filtered) <
|
|
STA_MAX_TX_BUFFER) {
|
|
ieee80211_remove_tx_extra(local, sta->key,
|
|
skb,
|
|
&status->control);
|
|
skb_queue_tail(&sta->tx_filtered, skb);
|
|
} else if (!(sta->flags & WLAN_STA_PS) &&
|
|
!(status->control.flags & IEEE80211_TXCTL_REQUEUE)) {
|
|
/* Software retry the packet once */
|
|
status->control.flags |= IEEE80211_TXCTL_REQUEUE;
|
|
ieee80211_remove_tx_extra(local, sta->key,
|
|
skb,
|
|
&status->control);
|
|
dev_queue_xmit(skb);
|
|
} else {
|
|
if (net_ratelimit()) {
|
|
printk(KERN_DEBUG "%s: dropped TX "
|
|
"filtered frame queue_len=%d "
|
|
"PS=%d @%lu\n",
|
|
local->mdev->name,
|
|
skb_queue_len(
|
|
&sta->tx_filtered),
|
|
!!(sta->flags & WLAN_STA_PS),
|
|
jiffies);
|
|
}
|
|
dev_kfree_skb(skb);
|
|
}
|
|
sta_info_put(sta);
|
|
return;
|
|
}
|
|
} else {
|
|
/* FIXME: STUPID to call this with both local and local->mdev */
|
|
rate_control_tx_status(local, local->mdev, skb, status);
|
|
}
|
|
|
|
ieee80211_led_tx(local, 0);
|
|
|
|
/* SNMP counters
|
|
* Fragments are passed to low-level drivers as separate skbs, so these
|
|
* are actually fragments, not frames. Update frame counters only for
|
|
* the first fragment of the frame. */
|
|
|
|
frag = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG;
|
|
type = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_FTYPE;
|
|
|
|
if (status->flags & IEEE80211_TX_STATUS_ACK) {
|
|
if (frag == 0) {
|
|
local->dot11TransmittedFrameCount++;
|
|
if (is_multicast_ether_addr(hdr->addr1))
|
|
local->dot11MulticastTransmittedFrameCount++;
|
|
if (status->retry_count > 0)
|
|
local->dot11RetryCount++;
|
|
if (status->retry_count > 1)
|
|
local->dot11MultipleRetryCount++;
|
|
}
|
|
|
|
/* This counter shall be incremented for an acknowledged MPDU
|
|
* with an individual address in the address 1 field or an MPDU
|
|
* with a multicast address in the address 1 field of type Data
|
|
* or Management. */
|
|
if (!is_multicast_ether_addr(hdr->addr1) ||
|
|
type == IEEE80211_FTYPE_DATA ||
|
|
type == IEEE80211_FTYPE_MGMT)
|
|
local->dot11TransmittedFragmentCount++;
|
|
} else {
|
|
if (frag == 0)
|
|
local->dot11FailedCount++;
|
|
}
|
|
|
|
msg_type = (status->flags & IEEE80211_TX_STATUS_ACK) ?
|
|
ieee80211_msg_tx_callback_ack : ieee80211_msg_tx_callback_fail;
|
|
|
|
/* this was a transmitted frame, but now we want to reuse it */
|
|
skb_orphan(skb);
|
|
|
|
if ((status->control.flags & IEEE80211_TXCTL_REQ_TX_STATUS) &&
|
|
local->apdev) {
|
|
if (local->monitors) {
|
|
skb2 = skb_clone(skb, GFP_ATOMIC);
|
|
} else {
|
|
skb2 = skb;
|
|
skb = NULL;
|
|
}
|
|
|
|
if (skb2)
|
|
/* Send frame to hostapd */
|
|
ieee80211_rx_mgmt(local, skb2, NULL, msg_type);
|
|
|
|
if (!skb)
|
|
return;
|
|
}
|
|
|
|
if (!local->monitors) {
|
|
dev_kfree_skb(skb);
|
|
return;
|
|
}
|
|
|
|
/* send frame to monitor interfaces now */
|
|
|
|
if (skb_headroom(skb) < sizeof(*rthdr)) {
|
|
printk(KERN_ERR "ieee80211_tx_status: headroom too small\n");
|
|
dev_kfree_skb(skb);
|
|
return;
|
|
}
|
|
|
|
rthdr = (struct ieee80211_tx_status_rtap_hdr*)
|
|
skb_push(skb, sizeof(*rthdr));
|
|
|
|
memset(rthdr, 0, sizeof(*rthdr));
|
|
rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr));
|
|
rthdr->hdr.it_present =
|
|
cpu_to_le32((1 << IEEE80211_RADIOTAP_TX_FLAGS) |
|
|
(1 << IEEE80211_RADIOTAP_DATA_RETRIES));
|
|
|
|
if (!(status->flags & IEEE80211_TX_STATUS_ACK) &&
|
|
!is_multicast_ether_addr(hdr->addr1))
|
|
rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_FAIL);
|
|
|
|
if ((status->control.flags & IEEE80211_TXCTL_USE_RTS_CTS) &&
|
|
(status->control.flags & IEEE80211_TXCTL_USE_CTS_PROTECT))
|
|
rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_CTS);
|
|
else if (status->control.flags & IEEE80211_TXCTL_USE_RTS_CTS)
|
|
rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_RTS);
|
|
|
|
rthdr->data_retries = status->retry_count;
|
|
|
|
read_lock(&local->sub_if_lock);
|
|
monitors = local->monitors;
|
|
list_for_each_entry(sdata, &local->sub_if_list, list) {
|
|
/*
|
|
* Using the monitors counter is possibly racy, but
|
|
* if the value is wrong we simply either clone the skb
|
|
* once too much or forget sending it to one monitor iface
|
|
* The latter case isn't nice but fixing the race is much
|
|
* more complicated.
|
|
*/
|
|
if (!monitors || !skb)
|
|
goto out;
|
|
|
|
if (sdata->type == IEEE80211_IF_TYPE_MNTR) {
|
|
if (!netif_running(sdata->dev))
|
|
continue;
|
|
monitors--;
|
|
if (monitors)
|
|
skb2 = skb_clone(skb, GFP_KERNEL);
|
|
else
|
|
skb2 = NULL;
|
|
skb->dev = sdata->dev;
|
|
/* XXX: is this sufficient for BPF? */
|
|
skb_set_mac_header(skb, 0);
|
|
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
|
skb->pkt_type = PACKET_OTHERHOST;
|
|
skb->protocol = htons(ETH_P_802_2);
|
|
memset(skb->cb, 0, sizeof(skb->cb));
|
|
netif_rx(skb);
|
|
skb = skb2;
|
|
}
|
|
}
|
|
out:
|
|
read_unlock(&local->sub_if_lock);
|
|
if (skb)
|
|
dev_kfree_skb(skb);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_tx_status);
|
|
|
|
/* TODO: implement register/unregister functions for adding TX/RX handlers
|
|
* into ordered list */
|
|
|
|
/* rx_pre handlers don't have dev and sdata fields available in
|
|
* ieee80211_txrx_data */
|
|
static ieee80211_rx_handler ieee80211_rx_pre_handlers[] =
|
|
{
|
|
ieee80211_rx_h_parse_qos,
|
|
ieee80211_rx_h_load_stats,
|
|
NULL
|
|
};
|
|
|
|
static ieee80211_rx_handler ieee80211_rx_handlers[] =
|
|
{
|
|
ieee80211_rx_h_if_stats,
|
|
ieee80211_rx_h_monitor,
|
|
ieee80211_rx_h_passive_scan,
|
|
ieee80211_rx_h_check,
|
|
ieee80211_rx_h_sta_process,
|
|
ieee80211_rx_h_ccmp_decrypt,
|
|
ieee80211_rx_h_tkip_decrypt,
|
|
ieee80211_rx_h_wep_weak_iv_detection,
|
|
ieee80211_rx_h_wep_decrypt,
|
|
ieee80211_rx_h_defragment,
|
|
ieee80211_rx_h_ps_poll,
|
|
ieee80211_rx_h_michael_mic_verify,
|
|
/* this must be after decryption - so header is counted in MPDU mic
|
|
* must be before pae and data, so QOS_DATA format frames
|
|
* are not passed to user space by these functions
|
|
*/
|
|
ieee80211_rx_h_remove_qos_control,
|
|
ieee80211_rx_h_802_1x_pae,
|
|
ieee80211_rx_h_drop_unencrypted,
|
|
ieee80211_rx_h_data,
|
|
ieee80211_rx_h_mgmt,
|
|
NULL
|
|
};
|
|
|
|
static ieee80211_tx_handler ieee80211_tx_handlers[] =
|
|
{
|
|
ieee80211_tx_h_check_assoc,
|
|
ieee80211_tx_h_sequence,
|
|
ieee80211_tx_h_ps_buf,
|
|
ieee80211_tx_h_select_key,
|
|
ieee80211_tx_h_michael_mic_add,
|
|
ieee80211_tx_h_fragment,
|
|
ieee80211_tx_h_tkip_encrypt,
|
|
ieee80211_tx_h_ccmp_encrypt,
|
|
ieee80211_tx_h_wep_encrypt,
|
|
ieee80211_tx_h_rate_ctrl,
|
|
ieee80211_tx_h_misc,
|
|
ieee80211_tx_h_load_stats,
|
|
NULL
|
|
};
|
|
|
|
|
|
int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr)
|
|
{
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
struct sta_info *sta;
|
|
|
|
if (compare_ether_addr(remote_addr, sdata->u.wds.remote_addr) == 0)
|
|
return 0;
|
|
|
|
/* Create STA entry for the new peer */
|
|
sta = sta_info_add(local, dev, remote_addr, GFP_KERNEL);
|
|
if (!sta)
|
|
return -ENOMEM;
|
|
sta_info_put(sta);
|
|
|
|
/* Remove STA entry for the old peer */
|
|
sta = sta_info_get(local, sdata->u.wds.remote_addr);
|
|
if (sta) {
|
|
sta_info_put(sta);
|
|
sta_info_free(sta, 0);
|
|
} else {
|
|
printk(KERN_DEBUG "%s: could not find STA entry for WDS link "
|
|
"peer " MAC_FMT "\n",
|
|
dev->name, MAC_ARG(sdata->u.wds.remote_addr));
|
|
}
|
|
|
|
/* Update WDS link data */
|
|
memcpy(&sdata->u.wds.remote_addr, remote_addr, ETH_ALEN);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Must not be called for mdev and apdev */
|
|
void ieee80211_if_setup(struct net_device *dev)
|
|
{
|
|
ether_setup(dev);
|
|
dev->hard_start_xmit = ieee80211_subif_start_xmit;
|
|
dev->wireless_handlers = &ieee80211_iw_handler_def;
|
|
dev->set_multicast_list = ieee80211_set_multicast_list;
|
|
dev->change_mtu = ieee80211_change_mtu;
|
|
dev->get_stats = ieee80211_get_stats;
|
|
dev->open = ieee80211_open;
|
|
dev->stop = ieee80211_stop;
|
|
dev->uninit = ieee80211_if_reinit;
|
|
dev->destructor = ieee80211_if_free;
|
|
}
|
|
|
|
void ieee80211_if_mgmt_setup(struct net_device *dev)
|
|
{
|
|
ether_setup(dev);
|
|
dev->hard_start_xmit = ieee80211_mgmt_start_xmit;
|
|
dev->change_mtu = ieee80211_change_mtu_apdev;
|
|
dev->get_stats = ieee80211_get_stats;
|
|
dev->open = ieee80211_mgmt_open;
|
|
dev->stop = ieee80211_mgmt_stop;
|
|
dev->type = ARPHRD_IEEE80211_PRISM;
|
|
dev->hard_header_parse = header_parse_80211;
|
|
dev->uninit = ieee80211_if_reinit;
|
|
dev->destructor = ieee80211_if_free;
|
|
}
|
|
|
|
int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
|
|
const char *name)
|
|
{
|
|
struct rate_control_ref *ref, *old;
|
|
|
|
ASSERT_RTNL();
|
|
if (local->open_count || netif_running(local->mdev) ||
|
|
(local->apdev && netif_running(local->apdev)))
|
|
return -EBUSY;
|
|
|
|
ref = rate_control_alloc(name, local);
|
|
if (!ref) {
|
|
printk(KERN_WARNING "%s: Failed to select rate control "
|
|
"algorithm\n", local->mdev->name);
|
|
return -ENOENT;
|
|
}
|
|
|
|
old = local->rate_ctrl;
|
|
local->rate_ctrl = ref;
|
|
if (old) {
|
|
rate_control_put(old);
|
|
sta_info_flush(local, NULL);
|
|
}
|
|
|
|
printk(KERN_DEBUG "%s: Selected rate control "
|
|
"algorithm '%s'\n", local->mdev->name,
|
|
ref->ops->name);
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rate_control_deinitialize(struct ieee80211_local *local)
|
|
{
|
|
struct rate_control_ref *ref;
|
|
|
|
ref = local->rate_ctrl;
|
|
local->rate_ctrl = NULL;
|
|
rate_control_put(ref);
|
|
}
|
|
|
|
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
|
const struct ieee80211_ops *ops)
|
|
{
|
|
struct net_device *mdev;
|
|
struct ieee80211_local *local;
|
|
struct ieee80211_sub_if_data *sdata;
|
|
int priv_size;
|
|
struct wiphy *wiphy;
|
|
|
|
/* Ensure 32-byte alignment of our private data and hw private data.
|
|
* We use the wiphy priv data for both our ieee80211_local and for
|
|
* the driver's private data
|
|
*
|
|
* In memory it'll be like this:
|
|
*
|
|
* +-------------------------+
|
|
* | struct wiphy |
|
|
* +-------------------------+
|
|
* | struct ieee80211_local |
|
|
* +-------------------------+
|
|
* | driver's private data |
|
|
* +-------------------------+
|
|
*
|
|
*/
|
|
priv_size = ((sizeof(struct ieee80211_local) +
|
|
NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST) +
|
|
priv_data_len;
|
|
|
|
wiphy = wiphy_new(&mac80211_config_ops, priv_size);
|
|
|
|
if (!wiphy)
|
|
return NULL;
|
|
|
|
wiphy->privid = mac80211_wiphy_privid;
|
|
|
|
local = wiphy_priv(wiphy);
|
|
local->hw.wiphy = wiphy;
|
|
|
|
local->hw.priv = (char *)local +
|
|
((sizeof(struct ieee80211_local) +
|
|
NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST);
|
|
|
|
BUG_ON(!ops->tx);
|
|
BUG_ON(!ops->config);
|
|
BUG_ON(!ops->add_interface);
|
|
local->ops = ops;
|
|
|
|
/* for now, mdev needs sub_if_data :/ */
|
|
mdev = alloc_netdev(sizeof(struct ieee80211_sub_if_data),
|
|
"wmaster%d", ether_setup);
|
|
if (!mdev) {
|
|
wiphy_free(wiphy);
|
|
return NULL;
|
|
}
|
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(mdev);
|
|
mdev->ieee80211_ptr = &sdata->wdev;
|
|
sdata->wdev.wiphy = wiphy;
|
|
|
|
local->hw.queues = 1; /* default */
|
|
|
|
local->mdev = mdev;
|
|
local->rx_pre_handlers = ieee80211_rx_pre_handlers;
|
|
local->rx_handlers = ieee80211_rx_handlers;
|
|
local->tx_handlers = ieee80211_tx_handlers;
|
|
|
|
local->bridge_packets = 1;
|
|
|
|
local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
|
|
local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
|
|
local->short_retry_limit = 7;
|
|
local->long_retry_limit = 4;
|
|
local->hw.conf.radio_enabled = 1;
|
|
|
|
local->enabled_modes = (unsigned int) -1;
|
|
|
|
INIT_LIST_HEAD(&local->modes_list);
|
|
|
|
rwlock_init(&local->sub_if_lock);
|
|
INIT_LIST_HEAD(&local->sub_if_list);
|
|
|
|
INIT_DELAYED_WORK(&local->scan_work, ieee80211_sta_scan_work);
|
|
init_timer(&local->stat_timer);
|
|
local->stat_timer.function = ieee80211_stat_refresh;
|
|
local->stat_timer.data = (unsigned long) local;
|
|
ieee80211_rx_bss_list_init(mdev);
|
|
|
|
sta_info_init(local);
|
|
|
|
mdev->hard_start_xmit = ieee80211_master_start_xmit;
|
|
mdev->open = ieee80211_master_open;
|
|
mdev->stop = ieee80211_master_stop;
|
|
mdev->type = ARPHRD_IEEE80211;
|
|
mdev->hard_header_parse = header_parse_80211;
|
|
|
|
sdata->type = IEEE80211_IF_TYPE_AP;
|
|
sdata->dev = mdev;
|
|
sdata->local = local;
|
|
sdata->u.ap.force_unicast_rateidx = -1;
|
|
sdata->u.ap.max_ratectrl_rateidx = -1;
|
|
ieee80211_if_sdata_init(sdata);
|
|
list_add_tail(&sdata->list, &local->sub_if_list);
|
|
|
|
tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
|
|
(unsigned long)local);
|
|
tasklet_disable(&local->tx_pending_tasklet);
|
|
|
|
tasklet_init(&local->tasklet,
|
|
ieee80211_tasklet_handler,
|
|
(unsigned long) local);
|
|
tasklet_disable(&local->tasklet);
|
|
|
|
skb_queue_head_init(&local->skb_queue);
|
|
skb_queue_head_init(&local->skb_queue_unreliable);
|
|
|
|
return local_to_hw(local);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_alloc_hw);
|
|
|
|
int ieee80211_register_hw(struct ieee80211_hw *hw)
|
|
{
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
const char *name;
|
|
int result;
|
|
|
|
result = wiphy_register(local->hw.wiphy);
|
|
if (result < 0)
|
|
return result;
|
|
|
|
name = wiphy_dev(local->hw.wiphy)->driver->name;
|
|
local->hw.workqueue = create_singlethread_workqueue(name);
|
|
if (!local->hw.workqueue) {
|
|
result = -ENOMEM;
|
|
goto fail_workqueue;
|
|
}
|
|
|
|
/*
|
|
* The hardware needs headroom for sending the frame,
|
|
* and we need some headroom for passing the frame to monitor
|
|
* interfaces, but never both at the same time.
|
|
*/
|
|
local->tx_headroom = max_t(unsigned int , local->hw.extra_tx_headroom,
|
|
sizeof(struct ieee80211_tx_status_rtap_hdr));
|
|
|
|
debugfs_hw_add(local);
|
|
|
|
local->hw.conf.beacon_int = 1000;
|
|
|
|
local->wstats_flags |= local->hw.max_rssi ?
|
|
IW_QUAL_LEVEL_UPDATED : IW_QUAL_LEVEL_INVALID;
|
|
local->wstats_flags |= local->hw.max_signal ?
|
|
IW_QUAL_QUAL_UPDATED : IW_QUAL_QUAL_INVALID;
|
|
local->wstats_flags |= local->hw.max_noise ?
|
|
IW_QUAL_NOISE_UPDATED : IW_QUAL_NOISE_INVALID;
|
|
if (local->hw.max_rssi < 0 || local->hw.max_noise < 0)
|
|
local->wstats_flags |= IW_QUAL_DBM;
|
|
|
|
result = sta_info_start(local);
|
|
if (result < 0)
|
|
goto fail_sta_info;
|
|
|
|
rtnl_lock();
|
|
result = dev_alloc_name(local->mdev, local->mdev->name);
|
|
if (result < 0)
|
|
goto fail_dev;
|
|
|
|
memcpy(local->mdev->dev_addr, local->hw.wiphy->perm_addr, ETH_ALEN);
|
|
SET_NETDEV_DEV(local->mdev, wiphy_dev(local->hw.wiphy));
|
|
|
|
result = register_netdevice(local->mdev);
|
|
if (result < 0)
|
|
goto fail_dev;
|
|
|
|
ieee80211_debugfs_add_netdev(IEEE80211_DEV_TO_SUB_IF(local->mdev));
|
|
|
|
result = ieee80211_init_rate_ctrl_alg(local, NULL);
|
|
if (result < 0) {
|
|
printk(KERN_DEBUG "%s: Failed to initialize rate control "
|
|
"algorithm\n", local->mdev->name);
|
|
goto fail_rate;
|
|
}
|
|
|
|
result = ieee80211_wep_init(local);
|
|
|
|
if (result < 0) {
|
|
printk(KERN_DEBUG "%s: Failed to initialize wep\n",
|
|
local->mdev->name);
|
|
goto fail_wep;
|
|
}
|
|
|
|
ieee80211_install_qdisc(local->mdev);
|
|
|
|
/* add one default STA interface */
|
|
result = ieee80211_if_add(local->mdev, "wlan%d", NULL,
|
|
IEEE80211_IF_TYPE_STA);
|
|
if (result)
|
|
printk(KERN_WARNING "%s: Failed to add default virtual iface\n",
|
|
local->mdev->name);
|
|
|
|
local->reg_state = IEEE80211_DEV_REGISTERED;
|
|
rtnl_unlock();
|
|
|
|
ieee80211_led_init(local);
|
|
|
|
return 0;
|
|
|
|
fail_wep:
|
|
rate_control_deinitialize(local);
|
|
fail_rate:
|
|
ieee80211_debugfs_remove_netdev(IEEE80211_DEV_TO_SUB_IF(local->mdev));
|
|
unregister_netdevice(local->mdev);
|
|
fail_dev:
|
|
rtnl_unlock();
|
|
sta_info_stop(local);
|
|
fail_sta_info:
|
|
debugfs_hw_del(local);
|
|
destroy_workqueue(local->hw.workqueue);
|
|
fail_workqueue:
|
|
wiphy_unregister(local->hw.wiphy);
|
|
return result;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_register_hw);
|
|
|
|
int ieee80211_register_hwmode(struct ieee80211_hw *hw,
|
|
struct ieee80211_hw_mode *mode)
|
|
{
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
struct ieee80211_rate *rate;
|
|
int i;
|
|
|
|
INIT_LIST_HEAD(&mode->list);
|
|
list_add_tail(&mode->list, &local->modes_list);
|
|
|
|
local->hw_modes |= (1 << mode->mode);
|
|
for (i = 0; i < mode->num_rates; i++) {
|
|
rate = &(mode->rates[i]);
|
|
rate->rate_inv = CHAN_UTIL_RATE_LCM / rate->rate;
|
|
}
|
|
ieee80211_prepare_rates(local, mode);
|
|
|
|
if (!local->oper_hw_mode) {
|
|
/* Default to this mode */
|
|
local->hw.conf.phymode = mode->mode;
|
|
local->oper_hw_mode = local->scan_hw_mode = mode;
|
|
local->oper_channel = local->scan_channel = &mode->channels[0];
|
|
local->hw.conf.mode = local->oper_hw_mode;
|
|
local->hw.conf.chan = local->oper_channel;
|
|
}
|
|
|
|
if (!(hw->flags & IEEE80211_HW_DEFAULT_REG_DOMAIN_CONFIGURED))
|
|
ieee80211_set_default_regdomain(mode);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_register_hwmode);
|
|
|
|
void ieee80211_unregister_hw(struct ieee80211_hw *hw)
|
|
{
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
struct ieee80211_sub_if_data *sdata, *tmp;
|
|
struct list_head tmp_list;
|
|
int i;
|
|
|
|
tasklet_kill(&local->tx_pending_tasklet);
|
|
tasklet_kill(&local->tasklet);
|
|
|
|
rtnl_lock();
|
|
|
|
BUG_ON(local->reg_state != IEEE80211_DEV_REGISTERED);
|
|
|
|
local->reg_state = IEEE80211_DEV_UNREGISTERED;
|
|
if (local->apdev)
|
|
ieee80211_if_del_mgmt(local);
|
|
|
|
write_lock_bh(&local->sub_if_lock);
|
|
list_replace_init(&local->sub_if_list, &tmp_list);
|
|
write_unlock_bh(&local->sub_if_lock);
|
|
|
|
list_for_each_entry_safe(sdata, tmp, &tmp_list, list)
|
|
__ieee80211_if_del(local, sdata);
|
|
|
|
rtnl_unlock();
|
|
|
|
if (local->stat_time)
|
|
del_timer_sync(&local->stat_timer);
|
|
|
|
ieee80211_rx_bss_list_deinit(local->mdev);
|
|
ieee80211_clear_tx_pending(local);
|
|
sta_info_stop(local);
|
|
rate_control_deinitialize(local);
|
|
debugfs_hw_del(local);
|
|
|
|
for (i = 0; i < NUM_IEEE80211_MODES; i++) {
|
|
kfree(local->supp_rates[i]);
|
|
kfree(local->basic_rates[i]);
|
|
}
|
|
|
|
if (skb_queue_len(&local->skb_queue)
|
|
|| skb_queue_len(&local->skb_queue_unreliable))
|
|
printk(KERN_WARNING "%s: skb_queue not empty\n",
|
|
local->mdev->name);
|
|
skb_queue_purge(&local->skb_queue);
|
|
skb_queue_purge(&local->skb_queue_unreliable);
|
|
|
|
destroy_workqueue(local->hw.workqueue);
|
|
wiphy_unregister(local->hw.wiphy);
|
|
ieee80211_wep_free(local);
|
|
ieee80211_led_exit(local);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_unregister_hw);
|
|
|
|
void ieee80211_free_hw(struct ieee80211_hw *hw)
|
|
{
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
|
|
ieee80211_if_free(local->mdev);
|
|
wiphy_free(local->hw.wiphy);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_free_hw);
|
|
|
|
void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
|
|
{
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
|
|
if (test_and_clear_bit(IEEE80211_LINK_STATE_XOFF,
|
|
&local->state[queue])) {
|
|
if (test_bit(IEEE80211_LINK_STATE_PENDING,
|
|
&local->state[queue]))
|
|
tasklet_schedule(&local->tx_pending_tasklet);
|
|
else
|
|
if (!ieee80211_qdisc_installed(local->mdev)) {
|
|
if (queue == 0)
|
|
netif_wake_queue(local->mdev);
|
|
} else
|
|
__netif_schedule(local->mdev);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_wake_queue);
|
|
|
|
void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
|
|
{
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
|
|
if (!ieee80211_qdisc_installed(local->mdev) && queue == 0)
|
|
netif_stop_queue(local->mdev);
|
|
set_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_stop_queue);
|
|
|
|
void ieee80211_start_queues(struct ieee80211_hw *hw)
|
|
{
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
int i;
|
|
|
|
for (i = 0; i < local->hw.queues; i++)
|
|
clear_bit(IEEE80211_LINK_STATE_XOFF, &local->state[i]);
|
|
if (!ieee80211_qdisc_installed(local->mdev))
|
|
netif_start_queue(local->mdev);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_start_queues);
|
|
|
|
void ieee80211_stop_queues(struct ieee80211_hw *hw)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < hw->queues; i++)
|
|
ieee80211_stop_queue(hw, i);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_stop_queues);
|
|
|
|
void ieee80211_wake_queues(struct ieee80211_hw *hw)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < hw->queues; i++)
|
|
ieee80211_wake_queue(hw, i);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_wake_queues);
|
|
|
|
struct net_device_stats *ieee80211_dev_stats(struct net_device *dev)
|
|
{
|
|
struct ieee80211_sub_if_data *sdata;
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
return &sdata->stats;
|
|
}
|
|
|
|
static int __init ieee80211_init(void)
|
|
{
|
|
struct sk_buff *skb;
|
|
int ret;
|
|
|
|
BUILD_BUG_ON(sizeof(struct ieee80211_tx_packet_data) > sizeof(skb->cb));
|
|
|
|
ret = ieee80211_wme_register();
|
|
if (ret) {
|
|
printk(KERN_DEBUG "ieee80211_init: failed to "
|
|
"initialize WME (err=%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ieee80211_debugfs_netdev_init();
|
|
ieee80211_regdomain_init();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void __exit ieee80211_exit(void)
|
|
{
|
|
ieee80211_wme_unregister();
|
|
ieee80211_debugfs_netdev_exit();
|
|
}
|
|
|
|
|
|
subsys_initcall(ieee80211_init);
|
|
module_exit(ieee80211_exit);
|
|
|
|
MODULE_DESCRIPTION("IEEE 802.11 subsystem");
|
|
MODULE_LICENSE("GPL");
|