diff options
Diffstat (limited to 'net/mac80211')
52 files changed, 4705 insertions, 2209 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index f5fdfcb..7d3b438 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -199,6 +199,19 @@ config MAC80211_VERBOSE_MPL_DEBUG Do not select this option. +config MAC80211_VERBOSE_MPATH_DEBUG + bool "Verbose mesh path debugging" + depends on MAC80211_DEBUG_MENU + depends on MAC80211_MESH + ---help--- + Selecting this option causes mac80211 to print out very + verbose mesh path selection debugging messages (when mac80211 + is taking part in a mesh network). + It should not be selected on production systems as those + messages are remotely triggerable. + + Do not select this option. + config MAC80211_VERBOSE_MHWMP_DEBUG bool "Verbose mesh HWMP routing debugging" depends on MAC80211_DEBUG_MENU @@ -212,6 +225,18 @@ config MAC80211_VERBOSE_MHWMP_DEBUG Do not select this option. +config MAC80211_VERBOSE_TDLS_DEBUG + bool "Verbose TDLS debugging" + depends on MAC80211_DEBUG_MENU + ---help--- + Selecting this option causes mac80211 to print out very + verbose TDLS selection debugging messages (when mac80211 + is a TDLS STA). + It should not be selected on production systems as those + messages are remotely triggerable. + + Do not select this option. + config MAC80211_DEBUG_COUNTERS bool "Extra statistics for TX/RX debugging" depends on MAC80211_DEBUG_MENU diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c index b9b595c..0785e95 100644 --- a/net/mac80211/aes_ccm.c +++ b/net/mac80211/aes_ccm.c @@ -11,6 +11,7 @@ #include <linux/types.h> #include <linux/crypto.h> #include <linux/err.h> +#include <crypto/aes.h> #include <net/mac80211.h> #include "key.h" @@ -21,21 +22,21 @@ static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *scratch, u8 *a) int i; u8 *b_0, *aad, *b, *s_0; - b_0 = scratch + 3 * AES_BLOCK_LEN; - aad = scratch + 4 * AES_BLOCK_LEN; + b_0 = scratch + 3 * AES_BLOCK_SIZE; + aad = scratch + 4 * AES_BLOCK_SIZE; b = scratch; - s_0 = scratch + AES_BLOCK_LEN; + s_0 = scratch + AES_BLOCK_SIZE; crypto_cipher_encrypt_one(tfm, b, b_0); /* Extra Authenticate-only data (always two AES blocks) */ - for (i = 0; i < AES_BLOCK_LEN; i++) + for (i = 0; i < AES_BLOCK_SIZE; i++) aad[i] ^= b[i]; crypto_cipher_encrypt_one(tfm, b, aad); - aad += AES_BLOCK_LEN; + aad += AES_BLOCK_SIZE; - for (i = 0; i < AES_BLOCK_LEN; i++) + for (i = 0; i < AES_BLOCK_SIZE; i++) aad[i] ^= b[i]; crypto_cipher_encrypt_one(tfm, a, aad); @@ -57,12 +58,12 @@ void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, u8 *pos, *cpos, *b, *s_0, *e, *b_0; b = scratch; - s_0 = scratch + AES_BLOCK_LEN; - e = scratch + 2 * AES_BLOCK_LEN; - b_0 = scratch + 3 * AES_BLOCK_LEN; + s_0 = scratch + AES_BLOCK_SIZE; + e = scratch + 2 * AES_BLOCK_SIZE; + b_0 = scratch + 3 * AES_BLOCK_SIZE; - num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); - last_len = data_len % AES_BLOCK_LEN; + num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); + last_len = data_len % AES_BLOCK_SIZE; aes_ccm_prepare(tfm, scratch, b); /* Process payload blocks */ @@ -70,7 +71,7 @@ void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, cpos = cdata; for (j = 1; j <= num_blocks; j++) { int blen = (j == num_blocks && last_len) ? - last_len : AES_BLOCK_LEN; + last_len : AES_BLOCK_SIZE; /* Authentication followed by encryption */ for (i = 0; i < blen; i++) @@ -96,12 +97,12 @@ int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, u8 *pos, *cpos, *b, *s_0, *a, *b_0; b = scratch; - s_0 = scratch + AES_BLOCK_LEN; - a = scratch + 2 * AES_BLOCK_LEN; - b_0 = scratch + 3 * AES_BLOCK_LEN; + s_0 = scratch + AES_BLOCK_SIZE; + a = scratch + 2 * AES_BLOCK_SIZE; + b_0 = scratch + 3 * AES_BLOCK_SIZE; - num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); - last_len = data_len % AES_BLOCK_LEN; + num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); + last_len = data_len % AES_BLOCK_SIZE; aes_ccm_prepare(tfm, scratch, a); /* Process payload blocks */ @@ -109,7 +110,7 @@ int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, pos = data; for (j = 1; j <= num_blocks; j++) { int blen = (j == num_blocks && last_len) ? - last_len : AES_BLOCK_LEN; + last_len : AES_BLOCK_SIZE; /* Decryption followed by authentication */ b_0[14] = (j >> 8) & 0xff; diff --git a/net/mac80211/aes_ccm.h b/net/mac80211/aes_ccm.h index 6e7820e..5b7d744 100644 --- a/net/mac80211/aes_ccm.h +++ b/net/mac80211/aes_ccm.h @@ -12,8 +12,6 @@ #include <linux/crypto.h> -#define AES_BLOCK_LEN 16 - struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]); void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, u8 *data, size_t data_len, diff --git a/net/mac80211/aes_cmac.c b/net/mac80211/aes_cmac.c index d502b26..8dfd70d 100644 --- a/net/mac80211/aes_cmac.c +++ b/net/mac80211/aes_cmac.c @@ -11,12 +11,12 @@ #include <linux/types.h> #include <linux/crypto.h> #include <linux/err.h> +#include <crypto/aes.h> #include <net/mac80211.h> #include "key.h" #include "aes_cmac.h" -#define AES_BLOCK_SIZE 16 #define AES_CMAC_KEY_LEN 16 #define CMAC_TLEN 8 /* CMAC TLen = 64 bits (8 octets) */ #define AAD_LEN 20 @@ -35,10 +35,10 @@ static void gf_mulx(u8 *pad) } -static void aes_128_cmac_vector(struct crypto_cipher *tfm, u8 *scratch, - size_t num_elem, +static void aes_128_cmac_vector(struct crypto_cipher *tfm, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { + u8 scratch[2 * AES_BLOCK_SIZE]; u8 *cbc, *pad; const u8 *pos, *end; size_t i, e, left, total_len; @@ -95,7 +95,7 @@ static void aes_128_cmac_vector(struct crypto_cipher *tfm, u8 *scratch, } -void ieee80211_aes_cmac(struct crypto_cipher *tfm, u8 *scratch, const u8 *aad, +void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad, const u8 *data, size_t data_len, u8 *mic) { const u8 *addr[3]; @@ -110,7 +110,7 @@ void ieee80211_aes_cmac(struct crypto_cipher *tfm, u8 *scratch, const u8 *aad, addr[2] = zero; len[2] = CMAC_TLEN; - aes_128_cmac_vector(tfm, scratch, 3, addr, len, mic); + aes_128_cmac_vector(tfm, 3, addr, len, mic); } diff --git a/net/mac80211/aes_cmac.h b/net/mac80211/aes_cmac.h index 0eb9a48..20785a6 100644 --- a/net/mac80211/aes_cmac.h +++ b/net/mac80211/aes_cmac.h @@ -12,7 +12,7 @@ #include <linux/crypto.h> struct crypto_cipher * ieee80211_aes_cmac_key_setup(const u8 key[]); -void ieee80211_aes_cmac(struct crypto_cipher *tfm, u8 *scratch, const u8 *aad, +void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad, const u8 *data, size_t data_len, u8 *mic); void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm); diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 1a41b14..6174785 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -38,6 +38,7 @@ #include <linux/ieee80211.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/mac80211.h> #include "ieee80211_i.h" #include "driver-ops.h" @@ -48,8 +49,6 @@ static void ieee80211_free_tid_rx(struct rcu_head *h) container_of(h, struct tid_ampdu_rx, rcu_head); int i; - del_timer_sync(&tid_rx->reorder_timer); - for (i = 0; i < tid_rx->buf_size; i++) dev_kfree_skb(tid_rx->reorder_buf[i]); kfree(tid_rx->reorder_buf); @@ -71,7 +70,7 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, if (!tid_rx) return; - rcu_assign_pointer(sta->ampdu_mlme.tid_rx[tid], NULL); + RCU_INIT_POINTER(sta->ampdu_mlme.tid_rx[tid], NULL); #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Rx BA session stop requested for %pM tid %u\n", @@ -90,6 +89,12 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, del_timer_sync(&tid_rx->session_timer); + /* make sure ieee80211_sta_reorder_release() doesn't re-arm the timer */ + spin_lock_bh(&tid_rx->reorder_lock); + tid_rx->removed = true; + spin_unlock_bh(&tid_rx->reorder_lock); + del_timer_sync(&tid_rx->reorder_timer); + call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx); } @@ -101,6 +106,29 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, mutex_unlock(&sta->ampdu_mlme.mtx); } +void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap, + const u8 *addr) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct sta_info *sta; + int i; + + rcu_read_lock(); + sta = sta_info_get(sdata, addr); + if (!sta) { + rcu_read_unlock(); + return; + } + + for (i = 0; i < STA_TID_NUM; i++) + if (ba_rx_bitmap & BIT(i)) + set_bit(i, sta->ampdu_mlme.tid_rx_stop_requested); + + ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work); + rcu_read_unlock(); +} +EXPORT_SYMBOL(ieee80211_stop_rx_ba_session); + /* * After accepting the AddBA Request we activated a timer, * resetting it after each frame that arrives from the originator. @@ -145,12 +173,8 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d u16 capab; skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom); - - if (!skb) { - printk(KERN_DEBUG "%s: failed to allocate buffer " - "for addba resp frame\n", sdata->name); + if (!skb) return; - } skb_reserve(skb, local->hw.extra_tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); @@ -205,7 +229,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, status = WLAN_STATUS_REQUEST_DECLINED; - if (test_sta_flags(sta, WLAN_STA_BLOCK_BA)) { + if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Suspend in progress. " "Denying ADDBA request\n"); @@ -248,19 +272,17 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, "%pM on tid %u\n", mgmt->sa, tid); #endif /* CONFIG_MAC80211_HT_DEBUG */ - goto end; + + /* delete existing Rx BA session on the same tid */ + ___ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT, + WLAN_STATUS_UNSPECIFIED_QOS, + false); } /* prepare A-MPDU MLME for Rx aggregation */ tid_agg_rx = kmalloc(sizeof(struct tid_ampdu_rx), GFP_KERNEL); - if (!tid_agg_rx) { -#ifdef CONFIG_MAC80211_HT_DEBUG - if (net_ratelimit()) - printk(KERN_ERR "allocate rx mlme to tid %d failed\n", - tid); -#endif + if (!tid_agg_rx) goto end; - } spin_lock_init(&tid_agg_rx->reorder_lock); @@ -280,11 +302,6 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, tid_agg_rx->reorder_time = kcalloc(buf_size, sizeof(unsigned long), GFP_KERNEL); if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) { -#ifdef CONFIG_MAC80211_HT_DEBUG - if (net_ratelimit()) - printk(KERN_ERR "can not allocate reordering buffer " - "to tid %d\n", tid); -#endif kfree(tid_agg_rx->reorder_buf); kfree(tid_agg_rx->reorder_time); kfree(tid_agg_rx); diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index b7f4f5c..2e4b961 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -15,6 +15,7 @@ #include <linux/ieee80211.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/mac80211.h> #include "ieee80211_i.h" #include "driver-ops.h" @@ -68,11 +69,9 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom); - if (!skb) { - printk(KERN_ERR "%s: failed to allocate buffer " - "for addba request frame\n", sdata->name); + if (!skb) return; - } + skb_reserve(skb, local->hw.extra_tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); memset(mgmt, 0, 24); @@ -106,19 +105,18 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, ieee80211_tx_skb(sdata, skb); } -void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn) +void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn) { + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_bar *bar; u16 bar_control = 0; skb = dev_alloc_skb(sizeof(*bar) + local->hw.extra_tx_headroom); - if (!skb) { - printk(KERN_ERR "%s: failed to allocate buffer for " - "bar frame\n", sdata->name); + if (!skb) return; - } + skb_reserve(skb, local->hw.extra_tx_headroom); bar = (struct ieee80211_bar *)skb_put(skb, sizeof(*bar)); memset(bar, 0, sizeof(*bar)); @@ -128,13 +126,14 @@ void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u1 memcpy(bar->ta, sdata->vif.addr, ETH_ALEN); bar_control |= (u16)IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL; bar_control |= (u16)IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA; - bar_control |= (u16)(tid << 12); + bar_control |= (u16)(tid << IEEE80211_BAR_CTRL_TID_INFO_SHIFT); bar->control = cpu_to_le16(bar_control); bar->start_seq_num = cpu_to_le16(ssn); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; ieee80211_tx_skb(sdata, skb); } +EXPORT_SYMBOL(ieee80211_send_bar); void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, struct tid_ampdu_tx *tid_tx) @@ -412,7 +411,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, return -EINVAL; if ((tid >= STA_TID_NUM) || - !(local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)) + !(local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) || + (local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW)) return -EINVAL; #ifdef CONFIG_MAC80211_HT_DEBUG @@ -431,7 +431,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, sdata->vif.type != NL80211_IFTYPE_AP) return -EINVAL; - if (test_sta_flags(sta, WLAN_STA_BLOCK_BA)) { + if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "BA sessions blocked. " "Denying BA session request\n"); @@ -461,11 +461,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, /* prepare A-MPDU MLME for Tx aggregation */ tid_tx = kzalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC); if (!tid_tx) { -#ifdef CONFIG_MAC80211_HT_DEBUG - if (net_ratelimit()) - printk(KERN_ERR "allocate tx mlme to tid %d failed\n", - tid); -#endif ret = -ENOMEM; goto err_unlock_sta; } @@ -590,14 +585,9 @@ void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, struct ieee80211_ra_tid *ra_tid; struct sk_buff *skb = dev_alloc_skb(0); - if (unlikely(!skb)) { -#ifdef CONFIG_MAC80211_HT_DEBUG - if (net_ratelimit()) - printk(KERN_WARNING "%s: Not enough memory, " - "dropping start BA session", sdata->name); -#endif + if (unlikely(!skb)) return; - } + ra_tid = (struct ieee80211_ra_tid *) &skb->cb; memcpy(&ra_tid->ra, ra, ETH_ALEN); ra_tid->tid = tid; @@ -743,14 +733,9 @@ void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, struct ieee80211_ra_tid *ra_tid; struct sk_buff *skb = dev_alloc_skb(0); - if (unlikely(!skb)) { -#ifdef CONFIG_MAC80211_HT_DEBUG - if (net_ratelimit()) - printk(KERN_WARNING "%s: Not enough memory, " - "dropping stop BA session", sdata->name); -#endif + if (unlikely(!skb)) return; - } + ra_tid = (struct ieee80211_ra_tid *) &skb->cb; memcpy(&ra_tid->ra, ra, ETH_ALEN); ra_tid->tid = tid; @@ -809,17 +794,14 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, goto out; } + /* + * IEEE 802.11-2007 7.3.1.14: + * In an ADDBA Response frame, when the Status Code field + * is set to 0, the Buffer Size subfield is set to a value + * of at least 1. + */ if (le16_to_cpu(mgmt->u.action.u.addba_resp.status) - == WLAN_STATUS_SUCCESS) { - /* - * IEEE 802.11-2007 7.3.1.14: - * In an ADDBA Response frame, when the Status Code field - * is set to 0, the Buffer Size subfield is set to a value - * of at least 1. - */ - if (!buf_size) - goto out; - + == WLAN_STATUS_SUCCESS && buf_size) { if (test_and_set_bit(HT_AGG_STATE_RESPONSE_RECEIVED, &tid_tx->state)) { /* ignore duplicate response */ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 143a006..11cee76 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -12,6 +12,7 @@ #include <linux/slab.h> #include <net/net_namespace.h> #include <linux/rcupdate.h> +#include <linux/if_ether.h> #include <net/cfg80211.h> #include "ieee80211_i.h" #include "driver-ops.h" @@ -62,7 +63,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy, if (type == NL80211_IFTYPE_AP_VLAN && params && params->use_4addr == 0) - rcu_assign_pointer(sdata->u.vlan.sta, NULL); + RCU_INIT_POINTER(sdata->u.vlan.sta, NULL); else if (type == NL80211_IFTYPE_STATION && params && params->use_4addr >= 0) sdata->u.mgd.use_4addr = params->use_4addr; @@ -209,6 +210,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, u8 seq[6] = {0}; struct key_params params; struct ieee80211_key *key = NULL; + u64 pn64; u32 iv32; u16 iv16; int err = -ENOENT; @@ -256,22 +258,24 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, params.seq_len = 6; break; case WLAN_CIPHER_SUITE_CCMP: - seq[0] = key->u.ccmp.tx_pn[5]; - seq[1] = key->u.ccmp.tx_pn[4]; - seq[2] = key->u.ccmp.tx_pn[3]; - seq[3] = key->u.ccmp.tx_pn[2]; - seq[4] = key->u.ccmp.tx_pn[1]; - seq[5] = key->u.ccmp.tx_pn[0]; + pn64 = atomic64_read(&key->u.ccmp.tx_pn); + seq[0] = pn64; + seq[1] = pn64 >> 8; + seq[2] = pn64 >> 16; + seq[3] = pn64 >> 24; + seq[4] = pn64 >> 32; + seq[5] = pn64 >> 40; params.seq = seq; params.seq_len = 6; break; case WLAN_CIPHER_SUITE_AES_CMAC: - seq[0] = key->u.aes_cmac.tx_pn[5]; - seq[1] = key->u.aes_cmac.tx_pn[4]; - seq[2] = key->u.aes_cmac.tx_pn[3]; - seq[3] = key->u.aes_cmac.tx_pn[2]; - seq[4] = key->u.aes_cmac.tx_pn[1]; - seq[5] = key->u.aes_cmac.tx_pn[0]; + pn64 = atomic64_read(&key->u.aes_cmac.tx_pn); + seq[0] = pn64; + seq[1] = pn64 >> 8; + seq[2] = pn64 >> 16; + seq[3] = pn64 >> 24; + seq[4] = pn64 >> 32; + seq[5] = pn64 >> 40; params.seq = seq; params.seq_len = 6; break; @@ -340,7 +344,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) STATION_INFO_RX_BITRATE | STATION_INFO_RX_DROP_MISC | STATION_INFO_BSS_PARAM | - STATION_INFO_CONNECTED_TIME; + STATION_INFO_CONNECTED_TIME | + STATION_INFO_STA_FLAGS; do_posix_clock_monotonic_gettime(&uptime); sinfo->connected_time = uptime.tv_sec - sta->last_connected; @@ -400,6 +405,23 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period; sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int; + + sinfo->sta_flags.set = 0; + sinfo->sta_flags.mask = BIT(NL80211_STA_FLAG_AUTHORIZED) | + BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | + BIT(NL80211_STA_FLAG_WME) | + BIT(NL80211_STA_FLAG_MFP) | + BIT(NL80211_STA_FLAG_AUTHENTICATED); + if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED); + if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); + if (test_sta_flag(sta, WLAN_STA_WME)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_WME); + if (test_sta_flag(sta, WLAN_STA_MFP)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP); + if (test_sta_flag(sta, WLAN_STA_AUTH)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED); } @@ -452,6 +474,20 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, return ret; } +static void ieee80211_config_ap_ssid(struct ieee80211_sub_if_data *sdata, + struct beacon_parameters *params) +{ + struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; + + bss_conf->ssid_len = params->ssid_len; + + if (params->ssid_len) + memcpy(bss_conf->ssid, params->ssid, params->ssid_len); + + bss_conf->hidden_ssid = + (params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE); +} + /* * This handles both adding a beacon and setting new beacon info */ @@ -545,8 +581,11 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata, kfree(old); + ieee80211_config_ap_ssid(sdata, params); + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | - BSS_CHANGED_BEACON); + BSS_CHANGED_BEACON | + BSS_CHANGED_SSID); return 0; } @@ -591,7 +630,7 @@ static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) if (!old) return -ENOENT; - rcu_assign_pointer(sdata->u.ap.beacon, NULL); + RCU_INIT_POINTER(sdata->u.ap.beacon, NULL); synchronize_rcu(); kfree(old); @@ -647,7 +686,6 @@ static void sta_apply_parameters(struct ieee80211_local *local, struct sta_info *sta, struct station_parameters *params) { - unsigned long flags; u32 rates; int i, j; struct ieee80211_supported_band *sband; @@ -656,40 +694,58 @@ static void sta_apply_parameters(struct ieee80211_local *local, sband = local->hw.wiphy->bands[local->oper_channel->band]; - spin_lock_irqsave(&sta->flaglock, flags); mask = params->sta_flags_mask; set = params->sta_flags_set; if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { - sta->flags &= ~WLAN_STA_AUTHORIZED; if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) - sta->flags |= WLAN_STA_AUTHORIZED; + set_sta_flag(sta, WLAN_STA_AUTHORIZED); + else + clear_sta_flag(sta, WLAN_STA_AUTHORIZED); } if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) { - sta->flags &= ~WLAN_STA_SHORT_PREAMBLE; if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) - sta->flags |= WLAN_STA_SHORT_PREAMBLE; + set_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE); + else + clear_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE); } if (mask & BIT(NL80211_STA_FLAG_WME)) { - sta->flags &= ~WLAN_STA_WME; - if (set & BIT(NL80211_STA_FLAG_WME)) - sta->flags |= WLAN_STA_WME; + if (set & BIT(NL80211_STA_FLAG_WME)) { + set_sta_flag(sta, WLAN_STA_WME); + sta->sta.wme = true; + } else { + clear_sta_flag(sta, WLAN_STA_WME); + sta->sta.wme = false; + } } if (mask & BIT(NL80211_STA_FLAG_MFP)) { - sta->flags &= ~WLAN_STA_MFP; if (set & BIT(NL80211_STA_FLAG_MFP)) - sta->flags |= WLAN_STA_MFP; + set_sta_flag(sta, WLAN_STA_MFP); + else + clear_sta_flag(sta, WLAN_STA_MFP); } if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { - sta->flags &= ~WLAN_STA_AUTH; if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) - sta->flags |= WLAN_STA_AUTH; + set_sta_flag(sta, WLAN_STA_AUTH); + else + clear_sta_flag(sta, WLAN_STA_AUTH); + } + + if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) { + if (set & BIT(NL80211_STA_FLAG_TDLS_PEER)) + set_sta_flag(sta, WLAN_STA_TDLS_PEER); + else + clear_sta_flag(sta, WLAN_STA_TDLS_PEER); + } + + if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) { + sta->sta.uapsd_queues = params->uapsd_queues; + sta->sta.max_sp = params->max_sp; } - spin_unlock_irqrestore(&sta->flaglock, flags); /* * cfg80211 validates this (1-2007) and allows setting the AID @@ -776,11 +832,18 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, if (is_multicast_ether_addr(mac)) return -EINVAL; + /* Only TDLS-supporting stations can add TDLS peers */ + if ((params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && + !((wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) && + sdata->vif.type == NL80211_IFTYPE_STATION)) + return -ENOTSUPP; + sta = sta_info_alloc(sdata, mac, GFP_KERNEL); if (!sta) return -ENOMEM; - sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC; + set_sta_flag(sta, WLAN_STA_AUTH); + set_sta_flag(sta, WLAN_STA_ASSOC); sta_apply_parameters(local, sta, params); @@ -836,6 +899,14 @@ static int ieee80211_change_station(struct wiphy *wiphy, return -ENOENT; } + /* The TDLS bit cannot be toggled after the STA was added */ + if ((params->sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) && + !!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) != + !!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { + rcu_read_unlock(); + return -EINVAL; + } + if (params->vlan && params->vlan != sta->sdata->dev) { vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); @@ -912,7 +983,7 @@ static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev, if (dst) return mesh_path_del(dst, sdata); - mesh_path_flush(sdata); + mesh_path_flush_by_iface(sdata); return 0; } @@ -1131,6 +1202,22 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy, conf->dot11MeshHWMPRootMode = nconf->dot11MeshHWMPRootMode; ieee80211_mesh_root_setup(ifmsh); } + if (_chg_mesh_attr(NL80211_MESHCONF_GATE_ANNOUNCEMENTS, mask)) { + /* our current gate announcement implementation rides on root + * announcements, so require this ifmsh to also be a root node + * */ + if (nconf->dot11MeshGateAnnouncementProtocol && + !conf->dot11MeshHWMPRootMode) { + conf->dot11MeshHWMPRootMode = 1; + ieee80211_mesh_root_setup(ifmsh); + } + conf->dot11MeshGateAnnouncementProtocol = + nconf->dot11MeshGateAnnouncementProtocol; + } + if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_RANN_INTERVAL, mask)) { + conf->dot11MeshHWMPRannInterval = + nconf->dot11MeshHWMPRannInterval; + } return 0; } @@ -1229,9 +1316,11 @@ static int ieee80211_change_bss(struct wiphy *wiphy, } static int ieee80211_set_txq_params(struct wiphy *wiphy, + struct net_device *dev, struct ieee80211_txq_params *params) { struct ieee80211_local *local = wiphy_priv(wiphy); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_tx_queue_params p; if (!local->ops->conf_tx) @@ -1249,7 +1338,11 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy, */ p.uapsd = false; - if (drv_conf_tx(local, params->queue, &p)) { + if (params->queue >= local->hw.queues) + return -EINVAL; + + sdata->tx_conf[params->queue] = p; + if (drv_conf_tx(local, sdata, params->queue, &p)) { wiphy_debug(local->hw.wiphy, "failed to set TX queue parameters for queue %d\n", params->queue); @@ -1554,6 +1647,19 @@ static int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len) return local->ops->testmode_cmd(&local->hw, data, len); } + +static int ieee80211_testmode_dump(struct wiphy *wiphy, + struct sk_buff *skb, + struct netlink_callback *cb, + void *data, int len) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + + if (!local->ops->testmode_dump) + return -EOPNOTSUPP; + + return local->ops->testmode_dump(&local->hw, skb, cb, data, len); +} #endif int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, @@ -1810,7 +1916,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, u64 *cookie) + const u8 *buf, size_t len, bool no_cck, + u64 *cookie) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; @@ -1837,6 +1944,9 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, flags |= IEEE80211_TX_CTL_TX_OFFCHAN; } + if (no_cck) + flags |= IEEE80211_TX_CTL_NO_CCK_RATE; + if (is_offchan && !offchan) return -EBUSY; @@ -1875,33 +1985,6 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, *cookie = (unsigned long) skb; - if (is_offchan && local->ops->offchannel_tx) { - int ret; - - IEEE80211_SKB_CB(skb)->band = chan->band; - - mutex_lock(&local->mtx); - - if (local->hw_offchan_tx_cookie) { - mutex_unlock(&local->mtx); - return -EBUSY; - } - - /* TODO: bitrate control, TX processing? */ - ret = drv_offchannel_tx(local, skb, chan, channel_type, wait); - - if (ret == 0) - local->hw_offchan_tx_cookie = *cookie; - mutex_unlock(&local->mtx); - - /* - * Allow driver to return 1 to indicate it wants to have the - * frame transmitted with a remain_on_channel + regular TX. - */ - if (ret != 1) - return ret; - } - if (is_offchan && local->ops->remain_on_channel) { unsigned int duration; int ret; @@ -1988,18 +2071,6 @@ static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, mutex_lock(&local->mtx); - if (local->ops->offchannel_tx_cancel_wait && - local->hw_offchan_tx_cookie == cookie) { - ret = drv_offchannel_tx_cancel_wait(local); - - if (!ret) - local->hw_offchan_tx_cookie = 0; - - mutex_unlock(&local->mtx); - - return ret; - } - if (local->ops->cancel_remain_on_channel) { cookie ^= 2; ret = ieee80211_cancel_remain_on_channel_hw(local, cookie); @@ -2085,6 +2156,338 @@ static void ieee80211_get_ringparam(struct wiphy *wiphy, drv_get_ringparam(local, tx, tx_max, rx, rx_max); } +static int ieee80211_set_rekey_data(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_gtk_rekey_data *data) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (!local->ops->set_rekey_data) + return -EOPNOTSUPP; + + drv_set_rekey_data(local, sdata, data); + + return 0; +} + +static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb) +{ + u8 *pos = (void *)skb_put(skb, 7); + + *pos++ = WLAN_EID_EXT_CAPABILITY; + *pos++ = 5; /* len */ + *pos++ = 0x0; + *pos++ = 0x0; + *pos++ = 0x0; + *pos++ = 0x0; + *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED; +} + +static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + u16 capab; + + capab = 0; + if (local->oper_channel->band != IEEE80211_BAND_2GHZ) + return capab; + + if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) + capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; + if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)) + capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; + + return capab; +} + +static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, + u8 *peer, u8 *bssid) +{ + struct ieee80211_tdls_lnkie *lnkid; + + lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); + + lnkid->ie_type = WLAN_EID_LINK_ID; + lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2; + + memcpy(lnkid->bssid, bssid, ETH_ALEN); + memcpy(lnkid->init_sta, src_addr, ETH_ALEN); + memcpy(lnkid->resp_sta, peer, ETH_ALEN); +} + +static int +ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, struct sk_buff *skb) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_tdls_data *tf; + + tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); + + memcpy(tf->da, peer, ETH_ALEN); + memcpy(tf->sa, sdata->vif.addr, ETH_ALEN); + tf->ether_type = cpu_to_be16(ETH_P_TDLS); + tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_REQUEST; + + skb_put(skb, sizeof(tf->u.setup_req)); + tf->u.setup_req.dialog_token = dialog_token; + tf->u.setup_req.capability = + cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + + ieee80211_add_srates_ie(&sdata->vif, skb); + ieee80211_add_ext_srates_ie(&sdata->vif, skb); + ieee80211_tdls_add_ext_capab(skb); + break; + case WLAN_TDLS_SETUP_RESPONSE: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_RESPONSE; + + skb_put(skb, sizeof(tf->u.setup_resp)); + tf->u.setup_resp.status_code = cpu_to_le16(status_code); + tf->u.setup_resp.dialog_token = dialog_token; + tf->u.setup_resp.capability = + cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + + ieee80211_add_srates_ie(&sdata->vif, skb); + ieee80211_add_ext_srates_ie(&sdata->vif, skb); + ieee80211_tdls_add_ext_capab(skb); + break; + case WLAN_TDLS_SETUP_CONFIRM: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_CONFIRM; + + skb_put(skb, sizeof(tf->u.setup_cfm)); + tf->u.setup_cfm.status_code = cpu_to_le16(status_code); + tf->u.setup_cfm.dialog_token = dialog_token; + break; + case WLAN_TDLS_TEARDOWN: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_TEARDOWN; + + skb_put(skb, sizeof(tf->u.teardown)); + tf->u.teardown.reason_code = cpu_to_le16(status_code); + break; + case WLAN_TDLS_DISCOVERY_REQUEST: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; + + skb_put(skb, sizeof(tf->u.discover_req)); + tf->u.discover_req.dialog_token = dialog_token; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int +ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, struct sk_buff *skb) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_mgmt *mgmt; + + mgmt = (void *)skb_put(skb, 24); + memset(mgmt, 0, 24); + memcpy(mgmt->da, peer, ETH_ALEN); + memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); + memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); + + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + + switch (action_code) { + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp)); + mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; + mgmt->u.action.u.tdls_discover_resp.action_code = + WLAN_PUB_ACTION_TDLS_DISCOVER_RES; + mgmt->u.action.u.tdls_discover_resp.dialog_token = + dialog_token; + mgmt->u.action.u.tdls_discover_resp.capability = + cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + + ieee80211_add_srates_ie(&sdata->vif, skb); + ieee80211_add_ext_srates_ie(&sdata->vif, skb); + ieee80211_tdls_add_ext_capab(skb); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + struct ieee80211_tx_info *info; + struct sk_buff *skb = NULL; + bool send_direct; + int ret; + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) + return -ENOTSUPP; + + /* make sure we are in managed mode, and associated */ + if (sdata->vif.type != NL80211_IFTYPE_STATION || + !sdata->u.mgd.associated) + return -EINVAL; + +#ifdef CONFIG_MAC80211_VERBOSE_TDLS_DEBUG + printk(KERN_DEBUG "TDLS mgmt action %d peer %pM\n", action_code, peer); +#endif + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + + max(sizeof(struct ieee80211_mgmt), + sizeof(struct ieee80211_tdls_data)) + + 50 + /* supported rates */ + 7 + /* ext capab */ + extra_ies_len + + sizeof(struct ieee80211_tdls_lnkie)); + if (!skb) + return -ENOMEM; + + info = IEEE80211_SKB_CB(skb); + skb_reserve(skb, local->hw.extra_tx_headroom); + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_RESPONSE: + case WLAN_TDLS_SETUP_CONFIRM: + case WLAN_TDLS_TEARDOWN: + case WLAN_TDLS_DISCOVERY_REQUEST: + ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer, + action_code, dialog_token, + status_code, skb); + send_direct = false; + break; + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code, + dialog_token, status_code, + skb); + send_direct = true; + break; + default: + ret = -ENOTSUPP; + break; + } + + if (ret < 0) + goto fail; + + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); + + /* the TDLS link IE is always added last */ + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_CONFIRM: + case WLAN_TDLS_TEARDOWN: + case WLAN_TDLS_DISCOVERY_REQUEST: + /* we are the initiator */ + ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer, + sdata->u.mgd.bssid); + break; + case WLAN_TDLS_SETUP_RESPONSE: + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + /* we are the responder */ + ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr, + sdata->u.mgd.bssid); + break; + default: + ret = -ENOTSUPP; + goto fail; + } + + if (send_direct) { + ieee80211_tx_skb(sdata, skb); + return 0; + } + + /* + * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise + * we should default to AC_VI. + */ + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_RESPONSE: + skb_set_queue_mapping(skb, IEEE80211_AC_BK); + skb->priority = 2; + break; + default: + skb_set_queue_mapping(skb, IEEE80211_AC_VI); + skb->priority = 5; + break; + } + + /* disable bottom halves when entering the Tx path */ + local_bh_disable(); + ret = ieee80211_subif_start_xmit(skb, dev); + local_bh_enable(); + + return ret; + +fail: + dev_kfree_skb(skb); + return ret; +} + +static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, enum nl80211_tdls_operation oper) +{ + struct sta_info *sta; + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) + return -ENOTSUPP; + + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return -EINVAL; + +#ifdef CONFIG_MAC80211_VERBOSE_TDLS_DEBUG + printk(KERN_DEBUG "TDLS oper %d peer %pM\n", oper, peer); +#endif + + switch (oper) { + case NL80211_TDLS_ENABLE_LINK: + rcu_read_lock(); + sta = sta_info_get(sdata, peer); + if (!sta) { + rcu_read_unlock(); + return -ENOLINK; + } + + set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); + rcu_read_unlock(); + break; + case NL80211_TDLS_DISABLE_LINK: + return sta_info_destroy_addr(sdata, peer); + case NL80211_TDLS_TEARDOWN: + case NL80211_TDLS_SETUP: + case NL80211_TDLS_DISCOVERY_REQ: + /* We don't support in-driver setup/teardown/discovery */ + return -ENOTSUPP; + default: + return -ENOTSUPP; + } + + return 0; +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -2134,6 +2537,7 @@ struct cfg80211_ops mac80211_config_ops = { .set_wds_peer = ieee80211_set_wds_peer, .rfkill_poll = ieee80211_rfkill_poll, CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) + CFG80211_TESTMODE_DUMP(ieee80211_testmode_dump) .set_power_mgmt = ieee80211_set_power_mgmt, .set_bitrate_mask = ieee80211_set_bitrate_mask, .remain_on_channel = ieee80211_remain_on_channel, @@ -2146,4 +2550,7 @@ struct cfg80211_ops mac80211_config_ops = { .get_antenna = ieee80211_get_antenna, .set_ringparam = ieee80211_set_ringparam, .get_ringparam = ieee80211_get_ringparam, + .set_rekey_data = ieee80211_set_rekey_data, + .tdls_oper = ieee80211_tdls_oper, + .tdls_mgmt = ieee80211_tdls_mgmt, }; diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 186e02f..883996b 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -78,57 +78,6 @@ DEBUGFS_READONLY_FILE(wep_iv, "%#08x", DEBUGFS_READONLY_FILE(rate_ctrl_alg, "%s", local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver"); -static ssize_t tsf_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - u64 tsf; - - tsf = drv_get_tsf(local); - - return mac80211_format_buffer(user_buf, count, ppos, "0x%016llx\n", - (unsigned long long) tsf); -} - -static ssize_t tsf_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - unsigned long long tsf; - char buf[100]; - size_t len; - - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - buf[len] = '\0'; - - if (strncmp(buf, "reset", 5) == 0) { - if (local->ops->reset_tsf) { - drv_reset_tsf(local); - wiphy_info(local->hw.wiphy, "debugfs reset TSF\n"); - } - } else { - tsf = simple_strtoul(buf, NULL, 0); - if (local->ops->set_tsf) { - drv_set_tsf(local, tsf); - wiphy_info(local->hw.wiphy, - "debugfs set TSF to %#018llx\n", tsf); - - } - } - - return count; -} - -static const struct file_operations tsf_ops = { - .read = tsf_read, - .write = tsf_write, - .open = mac80211_open_file_generic, - .llseek = default_llseek, -}; - static ssize_t reset_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { @@ -195,20 +144,12 @@ static ssize_t uapsd_queues_write(struct file *file, size_t count, loff_t *ppos) { struct ieee80211_local *local = file->private_data; - unsigned long val; - char buf[10]; - size_t len; + u8 val; int ret; - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - buf[len] = '\0'; - - ret = strict_strtoul(buf, 0, &val); - + ret = kstrtou8_from_user(user_buf, count, 0, &val); if (ret) - return -EINVAL; + return ret; if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) return -ERANGE; @@ -305,6 +246,9 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf, char *buf = kzalloc(mxln, GFP_KERNEL); int sf = 0; /* how many written so far */ + if (!buf) + return 0; + sf += snprintf(buf, mxln - sf, "0x%x\n", local->hw.flags); if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) sf += snprintf(buf + sf, mxln - sf, "HAS_RATE_CONTROL\n"); @@ -355,6 +299,8 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf, sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n"); if (local->hw.flags & IEEE80211_HW_AP_LINK_PS) sf += snprintf(buf + sf, mxln - sf, "AP_LINK_PS\n"); + if (local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW) + sf += snprintf(buf + sf, mxln - sf, "TX_AMPDU_SETUP_IN_HW\n"); rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); kfree(buf); @@ -450,7 +396,6 @@ void debugfs_hw_add(struct ieee80211_local *local) DEBUGFS_ADD(frequency); DEBUGFS_ADD(total_ps_buffered); DEBUGFS_ADD(wep_iv); - DEBUGFS_ADD(tsf); DEBUGFS_ADD(queues); DEBUGFS_ADD_MODE(reset, 0200); DEBUGFS_ADD(noack); diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index 33c58b8..38e6101 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c @@ -78,7 +78,7 @@ KEY_OPS(algorithm); static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { - const u8 *tpn; + u64 pn; char buf[20]; int len; struct ieee80211_key *key = file->private_data; @@ -94,15 +94,16 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf, key->u.tkip.tx.iv16); break; case WLAN_CIPHER_SUITE_CCMP: - tpn = key->u.ccmp.tx_pn; + pn = atomic64_read(&key->u.ccmp.tx_pn); len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", - tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], tpn[5]); + (u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24), + (u8)(pn >> 16), (u8)(pn >> 8), (u8)pn); break; case WLAN_CIPHER_SUITE_AES_CMAC: - tpn = key->u.aes_cmac.tx_pn; + pn = atomic64_read(&key->u.aes_cmac.tx_pn); len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", - tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], - tpn[5]); + (u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24), + (u8)(pn >> 16), (u8)(pn >> 8), (u8)pn); break; default: return 0; diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 9ea7c0d..0228ecb 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -21,6 +21,7 @@ #include "rate.h" #include "debugfs.h" #include "debugfs_netdev.h" +#include "driver-ops.h" static ssize_t ieee80211_if_read( struct ieee80211_sub_if_data *sdata, @@ -32,8 +33,7 @@ static ssize_t ieee80211_if_read( ssize_t ret = -EINVAL; read_lock(&dev_base_lock); - if (sdata->dev->reg_state == NETREG_REGISTERED) - ret = (*format)(sdata, buf, sizeof(buf)); + ret = (*format)(sdata, buf, sizeof(buf)); read_unlock(&dev_base_lock); if (ret >= 0) @@ -61,8 +61,7 @@ static ssize_t ieee80211_if_write( ret = -ENODEV; rtnl_lock(); - if (sdata->dev->reg_state == NETREG_REGISTERED) - ret = (*write)(sdata, buf, count); + ret = (*write)(sdata, buf, count); rtnl_unlock(); freebuf: @@ -331,6 +330,46 @@ static ssize_t ieee80211_if_fmt_num_buffered_multicast( } __IEEE80211_IF_FILE(num_buffered_multicast, NULL); +/* IBSS attributes */ +static ssize_t ieee80211_if_fmt_tsf( + const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) +{ + struct ieee80211_local *local = sdata->local; + u64 tsf; + + tsf = drv_get_tsf(local, (struct ieee80211_sub_if_data *)sdata); + + return scnprintf(buf, buflen, "0x%016llx\n", (unsigned long long) tsf); +} + +static ssize_t ieee80211_if_parse_tsf( + struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) +{ + struct ieee80211_local *local = sdata->local; + unsigned long long tsf; + int ret; + + if (strncmp(buf, "reset", 5) == 0) { + if (local->ops->reset_tsf) { + drv_reset_tsf(local, sdata); + wiphy_info(local->hw.wiphy, "debugfs reset TSF\n"); + } + } else { + ret = kstrtoull(buf, 10, &tsf); + if (ret < 0) + return -EINVAL; + if (local->ops->set_tsf) { + drv_set_tsf(local, sdata, tsf); + wiphy_info(local->hw.wiphy, + "debugfs set TSF to %#018llx\n", tsf); + } + } + + return buflen; +} +__IEEE80211_IF_FILE_W(tsf); + + /* WDS attributes */ IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC); @@ -340,6 +379,8 @@ IEEE80211_IF_FILE(fwded_mcast, u.mesh.mshstats.fwded_mcast, DEC); IEEE80211_IF_FILE(fwded_unicast, u.mesh.mshstats.fwded_unicast, DEC); IEEE80211_IF_FILE(fwded_frames, u.mesh.mshstats.fwded_frames, DEC); IEEE80211_IF_FILE(dropped_frames_ttl, u.mesh.mshstats.dropped_frames_ttl, DEC); +IEEE80211_IF_FILE(dropped_frames_congestion, + u.mesh.mshstats.dropped_frames_congestion, DEC); IEEE80211_IF_FILE(dropped_frames_no_route, u.mesh.mshstats.dropped_frames_no_route, DEC); IEEE80211_IF_FILE(estab_plinks, u.mesh.mshstats.estab_plinks, ATOMIC); @@ -372,6 +413,10 @@ IEEE80211_IF_FILE(min_discovery_timeout, u.mesh.mshcfg.min_discovery_timeout, DEC); IEEE80211_IF_FILE(dot11MeshHWMPRootMode, u.mesh.mshcfg.dot11MeshHWMPRootMode, DEC); +IEEE80211_IF_FILE(dot11MeshGateAnnouncementProtocol, + u.mesh.mshcfg.dot11MeshGateAnnouncementProtocol, DEC); +IEEE80211_IF_FILE(dot11MeshHWMPRannInterval, + u.mesh.mshcfg.dot11MeshHWMPRannInterval, DEC); #endif @@ -415,6 +460,11 @@ static void add_ap_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD_MODE(tkip_mic_test, 0200); } +static void add_ibss_files(struct ieee80211_sub_if_data *sdata) +{ + DEBUGFS_ADD_MODE(tsf, 0600); +} + static void add_wds_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_ADD(drop_unencrypted); @@ -459,6 +509,7 @@ static void add_mesh_stats(struct ieee80211_sub_if_data *sdata) MESHSTATS_ADD(fwded_frames); MESHSTATS_ADD(dropped_frames_ttl); MESHSTATS_ADD(dropped_frames_no_route); + MESHSTATS_ADD(dropped_frames_congestion); MESHSTATS_ADD(estab_plinks); #undef MESHSTATS_ADD } @@ -485,7 +536,9 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata) MESHPARAMS_ADD(dot11MeshHWMPmaxPREQretries); MESHPARAMS_ADD(path_refresh_time); MESHPARAMS_ADD(min_discovery_timeout); - + MESHPARAMS_ADD(dot11MeshHWMPRootMode); + MESHPARAMS_ADD(dot11MeshHWMPRannInterval); + MESHPARAMS_ADD(dot11MeshGateAnnouncementProtocol); #undef MESHPARAMS_ADD } #endif @@ -506,7 +559,7 @@ static void add_files(struct ieee80211_sub_if_data *sdata) add_sta_files(sdata); break; case NL80211_IFTYPE_ADHOC: - /* XXX */ + add_ibss_files(sdata); break; case NL80211_IFTYPE_AP: add_ap_files(sdata); @@ -545,6 +598,7 @@ void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata) debugfs_remove_recursive(sdata->debugfs.dir); sdata->debugfs.dir = NULL; + sdata->debugfs.subdir_stations = NULL; } void ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data *sdata) diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index a01d213..3110cbd 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -56,19 +56,22 @@ STA_FILE(last_signal, last_signal, D); static ssize_t sta_flags_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { - char buf[100]; + char buf[121]; struct sta_info *sta = file->private_data; - u32 staflags = get_sta_flags(sta); - int res = scnprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s", - staflags & WLAN_STA_AUTH ? "AUTH\n" : "", - staflags & WLAN_STA_ASSOC ? "ASSOC\n" : "", - staflags & WLAN_STA_PS_STA ? "PS (sta)\n" : "", - staflags & WLAN_STA_PS_DRIVER ? "PS (driver)\n" : "", - staflags & WLAN_STA_AUTHORIZED ? "AUTHORIZED\n" : "", - staflags & WLAN_STA_SHORT_PREAMBLE ? "SHORT PREAMBLE\n" : "", - staflags & WLAN_STA_WME ? "WME\n" : "", - staflags & WLAN_STA_WDS ? "WDS\n" : "", - staflags & WLAN_STA_MFP ? "MFP\n" : ""); + +#define TEST(flg) \ + test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : "" + + int res = scnprintf(buf, sizeof(buf), + "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + TEST(AUTH), TEST(ASSOC), TEST(PS_STA), + TEST(PS_DRIVER), TEST(AUTHORIZED), + TEST(SHORT_PREAMBLE), TEST(ASSOC_AP), + TEST(WME), TEST(WDS), TEST(CLEAR_PS_FILT), + TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL), + TEST(UAPSD), TEST(SP), TEST(TDLS_PEER), + TEST(TDLS_PEER_AUTH)); +#undef TEST return simple_read_from_buffer(userbuf, count, ppos, buf, res); } STA_OPS(flags); @@ -78,8 +81,14 @@ static ssize_t sta_num_ps_buf_frames_read(struct file *file, size_t count, loff_t *ppos) { struct sta_info *sta = file->private_data; - return mac80211_format_buffer(userbuf, count, ppos, "%u\n", - skb_queue_len(&sta->ps_tx_buf)); + char buf[17*IEEE80211_NUM_ACS], *p = buf; + int ac; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + p += scnprintf(p, sizeof(buf)+buf-p, "AC%d: %d\n", ac, + skb_queue_len(&sta->ps_tx_buf[ac]) + + skb_queue_len(&sta->tx_filtered[ac])); + return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); } STA_OPS(num_ps_buf_frames); @@ -265,9 +274,9 @@ static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf, PRINT_HT_CAP((htc->cap & BIT(10)), "HT Delayed Block Ack"); - PRINT_HT_CAP((htc->cap & BIT(11)), "Max AMSDU length: " - "3839 bytes"); PRINT_HT_CAP(!(htc->cap & BIT(11)), "Max AMSDU length: " + "3839 bytes"); + PRINT_HT_CAP((htc->cap & BIT(11)), "Max AMSDU length: " "7935 bytes"); /* diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index eebf7a6..5f165d7 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -130,6 +130,37 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, trace_drv_return_void(local); } +static inline int drv_tx_sync(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const u8 *bssid, + enum ieee80211_tx_sync_type type) +{ + int ret = 0; + + might_sleep(); + + trace_drv_tx_sync(local, sdata, bssid, type); + if (local->ops->tx_sync) + ret = local->ops->tx_sync(&local->hw, &sdata->vif, + bssid, type); + trace_drv_return_int(local, ret); + return ret; +} + +static inline void drv_finish_tx_sync(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const u8 *bssid, + enum ieee80211_tx_sync_type type) +{ + might_sleep(); + + trace_drv_finish_tx_sync(local, sdata, bssid, type); + if (local->ops->finish_tx_sync) + local->ops->finish_tx_sync(&local->hw, &sdata->vif, + bssid, type); + trace_drv_return_void(local); +} + static inline u64 drv_prepare_multicast(struct ieee80211_local *local, struct netdev_hw_addr_list *mc_list) { @@ -218,6 +249,16 @@ static inline int drv_hw_scan(struct ieee80211_local *local, return ret; } +static inline void drv_cancel_hw_scan(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + might_sleep(); + + trace_drv_cancel_hw_scan(local, sdata); + local->ops->cancel_hw_scan(&local->hw, &sdata->vif); + trace_drv_return_void(local); +} + static inline int drv_sched_scan_start(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, @@ -372,50 +413,56 @@ static inline void drv_sta_remove(struct ieee80211_local *local, trace_drv_return_void(local); } -static inline int drv_conf_tx(struct ieee80211_local *local, u16 queue, +static inline int drv_conf_tx(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, u16 queue, const struct ieee80211_tx_queue_params *params) { int ret = -EOPNOTSUPP; might_sleep(); - trace_drv_conf_tx(local, queue, params); + trace_drv_conf_tx(local, sdata, queue, params); if (local->ops->conf_tx) - ret = local->ops->conf_tx(&local->hw, queue, params); + ret = local->ops->conf_tx(&local->hw, &sdata->vif, + queue, params); trace_drv_return_int(local, ret); return ret; } -static inline u64 drv_get_tsf(struct ieee80211_local *local) +static inline u64 drv_get_tsf(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) { u64 ret = -1ULL; might_sleep(); - trace_drv_get_tsf(local); + trace_drv_get_tsf(local, sdata); if (local->ops->get_tsf) - ret = local->ops->get_tsf(&local->hw); + ret = local->ops->get_tsf(&local->hw, &sdata->vif); trace_drv_return_u64(local, ret); return ret; } -static inline void drv_set_tsf(struct ieee80211_local *local, u64 tsf) +static inline void drv_set_tsf(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + u64 tsf) { might_sleep(); - trace_drv_set_tsf(local, tsf); + trace_drv_set_tsf(local, sdata, tsf); if (local->ops->set_tsf) - local->ops->set_tsf(&local->hw, tsf); + local->ops->set_tsf(&local->hw, &sdata->vif, tsf); trace_drv_return_void(local); } -static inline void drv_reset_tsf(struct ieee80211_local *local) +static inline void drv_reset_tsf(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) { might_sleep(); - trace_drv_reset_tsf(local); + trace_drv_reset_tsf(local, sdata); if (local->ops->reset_tsf) - local->ops->reset_tsf(&local->hw); + local->ops->reset_tsf(&local->hw, &sdata->vif); trace_drv_return_void(local); } @@ -549,37 +596,6 @@ static inline int drv_cancel_remain_on_channel(struct ieee80211_local *local) return ret; } -static inline int drv_offchannel_tx(struct ieee80211_local *local, - struct sk_buff *skb, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, - unsigned int wait) -{ - int ret; - - might_sleep(); - - trace_drv_offchannel_tx(local, skb, chan, channel_type, wait); - ret = local->ops->offchannel_tx(&local->hw, skb, chan, - channel_type, wait); - trace_drv_return_int(local, ret); - - return ret; -} - -static inline int drv_offchannel_tx_cancel_wait(struct ieee80211_local *local) -{ - int ret; - - might_sleep(); - - trace_drv_offchannel_tx_cancel_wait(local); - ret = local->ops->offchannel_tx_cancel_wait(&local->hw); - trace_drv_return_int(local, ret); - - return ret; -} - static inline int drv_set_ringparam(struct ieee80211_local *local, u32 tx, u32 rx) { @@ -637,4 +653,52 @@ static inline int drv_set_bitrate_mask(struct ieee80211_local *local, return ret; } +static inline void drv_set_rekey_data(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct cfg80211_gtk_rekey_data *data) +{ + trace_drv_set_rekey_data(local, sdata, data); + if (local->ops->set_rekey_data) + local->ops->set_rekey_data(&local->hw, &sdata->vif, data); + trace_drv_return_void(local); +} + +static inline void drv_rssi_callback(struct ieee80211_local *local, + const enum ieee80211_rssi_event event) +{ + trace_drv_rssi_callback(local, event); + if (local->ops->rssi_callback) + local->ops->rssi_callback(&local->hw, event); + trace_drv_return_void(local); +} + +static inline void +drv_release_buffered_frames(struct ieee80211_local *local, + struct sta_info *sta, u16 tids, int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data) +{ + trace_drv_release_buffered_frames(local, &sta->sta, tids, num_frames, + reason, more_data); + if (local->ops->release_buffered_frames) + local->ops->release_buffered_frames(&local->hw, &sta->sta, tids, + num_frames, reason, + more_data); + trace_drv_return_void(local); +} + +static inline void +drv_allow_buffered_frames(struct ieee80211_local *local, + struct sta_info *sta, u16 tids, int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data) +{ + trace_drv_allow_buffered_frames(local, &sta->sta, tids, num_frames, + reason, more_data); + if (local->ops->allow_buffered_frames) + local->ops->allow_buffered_frames(&local->hw, &sta->sta, + tids, num_frames, reason, + more_data); + trace_drv_return_void(local); +} #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index ed9edcb..2af4fca 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -319,6 +319,49 @@ TRACE_EVENT(drv_bss_info_changed, ) ); +DECLARE_EVENT_CLASS(tx_sync_evt, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const u8 *bssid, + enum ieee80211_tx_sync_type type), + TP_ARGS(local, sdata, bssid, type), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __array(char, bssid, ETH_ALEN) + __field(u32, sync_type) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + memcpy(__entry->bssid, bssid, ETH_ALEN); + __entry->sync_type = type; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT " bssid:%pM type:%d", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->bssid, __entry->sync_type + ) +); + +DEFINE_EVENT(tx_sync_evt, drv_tx_sync, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const u8 *bssid, + enum ieee80211_tx_sync_type type), + TP_ARGS(local, sdata, bssid, type) +); + +DEFINE_EVENT(tx_sync_evt, drv_finish_tx_sync, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const u8 *bssid, + enum ieee80211_tx_sync_type type), + TP_ARGS(local, sdata, bssid, type) +); + TRACE_EVENT(drv_prepare_multicast, TP_PROTO(struct ieee80211_local *local, int mc_count), @@ -460,6 +503,12 @@ DEFINE_EVENT(local_sdata_evt, drv_hw_scan, TP_ARGS(local, sdata) ); +DEFINE_EVENT(local_sdata_evt, drv_cancel_hw_scan, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) +); + DEFINE_EVENT(local_sdata_evt, drv_sched_scan_start, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), @@ -648,64 +697,76 @@ TRACE_EVENT(drv_sta_remove, ); TRACE_EVENT(drv_conf_tx, - TP_PROTO(struct ieee80211_local *local, u16 queue, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + u16 queue, const struct ieee80211_tx_queue_params *params), - TP_ARGS(local, queue, params), + TP_ARGS(local, sdata, queue, params), TP_STRUCT__entry( LOCAL_ENTRY + VIF_ENTRY __field(u16, queue) __field(u16, txop) __field(u16, cw_min) __field(u16, cw_max) __field(u8, aifs) + __field(bool, uapsd) ), TP_fast_assign( LOCAL_ASSIGN; + VIF_ASSIGN; __entry->queue = queue; __entry->txop = params->txop; __entry->cw_max = params->cw_max; __entry->cw_min = params->cw_min; __entry->aifs = params->aifs; + __entry->uapsd = params->uapsd; ), TP_printk( - LOCAL_PR_FMT " queue:%d", - LOCAL_PR_ARG, __entry->queue + LOCAL_PR_FMT VIF_PR_FMT " queue:%d", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->queue ) ); -DEFINE_EVENT(local_only_evt, drv_get_tsf, - TP_PROTO(struct ieee80211_local *local), - TP_ARGS(local) +DEFINE_EVENT(local_sdata_evt, drv_get_tsf, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) ); TRACE_EVENT(drv_set_tsf, - TP_PROTO(struct ieee80211_local *local, u64 tsf), + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + u64 tsf), - TP_ARGS(local, tsf), + TP_ARGS(local, sdata, tsf), TP_STRUCT__entry( LOCAL_ENTRY + VIF_ENTRY __field(u64, tsf) ), TP_fast_assign( LOCAL_ASSIGN; + VIF_ASSIGN; __entry->tsf = tsf; ), TP_printk( - LOCAL_PR_FMT " tsf:%llu", - LOCAL_PR_ARG, (unsigned long long)__entry->tsf + LOCAL_PR_FMT VIF_PR_FMT " tsf:%llu", + LOCAL_PR_ARG, VIF_PR_ARG, (unsigned long long)__entry->tsf ) ); -DEFINE_EVENT(local_only_evt, drv_reset_tsf, - TP_PROTO(struct ieee80211_local *local), - TP_ARGS(local) +DEFINE_EVENT(local_sdata_evt, drv_reset_tsf, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) ); DEFINE_EVENT(local_only_evt, drv_tx_last_beacon, @@ -1018,6 +1079,111 @@ TRACE_EVENT(drv_set_bitrate_mask, ) ); +TRACE_EVENT(drv_set_rekey_data, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct cfg80211_gtk_rekey_data *data), + + TP_ARGS(local, sdata, data), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __array(u8, kek, NL80211_KEK_LEN) + __array(u8, kck, NL80211_KCK_LEN) + __array(u8, replay_ctr, NL80211_REPLAY_CTR_LEN) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + memcpy(__entry->kek, data->kek, NL80211_KEK_LEN); + memcpy(__entry->kck, data->kck, NL80211_KCK_LEN); + memcpy(__entry->replay_ctr, data->replay_ctr, + NL80211_REPLAY_CTR_LEN); + ), + + TP_printk(LOCAL_PR_FMT VIF_PR_FMT, + LOCAL_PR_ARG, VIF_PR_ARG) +); + +TRACE_EVENT(drv_rssi_callback, + TP_PROTO(struct ieee80211_local *local, + enum ieee80211_rssi_event rssi_event), + + TP_ARGS(local, rssi_event), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u32, rssi_event) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->rssi_event = rssi_event; + ), + + TP_printk( + LOCAL_PR_FMT " rssi_event:%d", + LOCAL_PR_ARG, __entry->rssi_event + ) +); + +DECLARE_EVENT_CLASS(release_evt, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sta *sta, + u16 tids, int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data), + + TP_ARGS(local, sta, tids, num_frames, reason, more_data), + + TP_STRUCT__entry( + LOCAL_ENTRY + STA_ENTRY + __field(u16, tids) + __field(int, num_frames) + __field(int, reason) + __field(bool, more_data) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + STA_ASSIGN; + __entry->tids = tids; + __entry->num_frames = num_frames; + __entry->reason = reason; + __entry->more_data = more_data; + ), + + TP_printk( + LOCAL_PR_FMT STA_PR_FMT + " TIDs:0x%.4x frames:%d reason:%d more:%d", + LOCAL_PR_ARG, STA_PR_ARG, __entry->tids, __entry->num_frames, + __entry->reason, __entry->more_data + ) +); + +DEFINE_EVENT(release_evt, drv_release_buffered_frames, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sta *sta, + u16 tids, int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data), + + TP_ARGS(local, sta, tids, num_frames, reason, more_data) +); + +DEFINE_EVENT(release_evt, drv_allow_buffered_frames, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sta *sta, + u16 tids, int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data), + + TP_ARGS(local, sta, tids, num_frames, reason, more_data) +); + /* * Tracing for API calls that drivers call. */ @@ -1287,6 +1453,73 @@ DEFINE_EVENT(local_only_evt, api_remain_on_channel_expired, TP_ARGS(local) ); +TRACE_EVENT(api_gtk_rekey_notify, + TP_PROTO(struct ieee80211_sub_if_data *sdata, + const u8 *bssid, const u8 *replay_ctr), + + TP_ARGS(sdata, bssid, replay_ctr), + + TP_STRUCT__entry( + VIF_ENTRY + __array(u8, bssid, ETH_ALEN) + __array(u8, replay_ctr, NL80211_REPLAY_CTR_LEN) + ), + + TP_fast_assign( + VIF_ASSIGN; + memcpy(__entry->bssid, bssid, ETH_ALEN); + memcpy(__entry->replay_ctr, replay_ctr, NL80211_REPLAY_CTR_LEN); + ), + + TP_printk(VIF_PR_FMT, VIF_PR_ARG) +); + +TRACE_EVENT(api_enable_rssi_reports, + TP_PROTO(struct ieee80211_sub_if_data *sdata, + int rssi_min_thold, int rssi_max_thold), + + TP_ARGS(sdata, rssi_min_thold, rssi_max_thold), + + TP_STRUCT__entry( + VIF_ENTRY + __field(int, rssi_min_thold) + __field(int, rssi_max_thold) + ), + + TP_fast_assign( + VIF_ASSIGN; + __entry->rssi_min_thold = rssi_min_thold; + __entry->rssi_max_thold = rssi_max_thold; + ), + + TP_printk( + VIF_PR_FMT " rssi_min_thold =%d, rssi_max_thold = %d", + VIF_PR_ARG, __entry->rssi_min_thold, __entry->rssi_max_thold + ) +); + +TRACE_EVENT(api_eosp, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sta *sta), + + TP_ARGS(local, sta), + + TP_STRUCT__entry( + LOCAL_ENTRY + STA_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + STA_ASSIGN; + ), + + TP_printk( + LOCAL_PR_FMT STA_PR_FMT, + LOCAL_PR_ARG, STA_PR_FMT + ) +); + /* * Tracing for internal functions * (which may also be called in response to driver calls) diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 591add2..f0fb737 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -14,6 +14,7 @@ */ #include <linux/ieee80211.h> +#include <linux/export.h> #include <net/mac80211.h> #include "ieee80211_i.h" #include "rate.h" @@ -130,7 +131,7 @@ void ieee80211_ba_session_work(struct work_struct *work) * down by the code that set the flag, so this * need not run. */ - if (test_sta_flags(sta, WLAN_STA_BLOCK_BA)) + if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) return; mutex_lock(&sta->ampdu_mlme.mtx); @@ -140,6 +141,12 @@ void ieee80211_ba_session_work(struct work_struct *work) sta, tid, WLAN_BACK_RECIPIENT, WLAN_REASON_QSTA_TIMEOUT, true); + if (test_and_clear_bit(tid, + sta->ampdu_mlme.tid_rx_stop_requested)) + ___ieee80211_stop_rx_ba_session( + sta, tid, WLAN_BACK_RECIPIENT, + WLAN_REASON_UNSPECIFIED, true); + tid_tx = sta->ampdu_mlme.tid_start_tx[tid]; if (tid_tx) { /* @@ -180,12 +187,8 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, u16 params; skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom); - - if (!skb) { - printk(KERN_ERR "%s: failed to allocate buffer " - "for delba frame\n", sdata->name); + if (!skb) return; - } skb_reserve(skb, local->hw.extra_tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index c6399f6..1c018d1 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -81,10 +81,10 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, lockdep_assert_held(&ifibss->mtx); /* Reset own TSF to allow time synchronization work. */ - drv_reset_tsf(local); + drv_reset_tsf(local, sdata); skb = ifibss->skb; - rcu_assign_pointer(ifibss->presp, NULL); + RCU_INIT_POINTER(ifibss->presp, NULL); synchronize_rcu(); skb->data = skb->head; skb->len = 0; @@ -314,7 +314,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, } if (sta && elems->wmm_info) - set_sta_flags(sta, WLAN_STA_WME); + set_sta_flag(sta, WLAN_STA_WME); rcu_read_unlock(); } @@ -382,7 +382,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, * second best option: get current TSF * (will return -1 if not supported) */ - rx_timestamp = drv_get_tsf(local); + rx_timestamp = drv_get_tsf(local, sdata); } #ifdef CONFIG_MAC80211_IBSS_DEBUG @@ -417,7 +417,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, * must be callable in atomic context. */ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, - u8 *bssid,u8 *addr, u32 supp_rates, + u8 *bssid, u8 *addr, u32 supp_rates, gfp_t gfp) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; @@ -452,7 +452,7 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, return NULL; sta->last_rx = jiffies; - set_sta_flags(sta, WLAN_STA_AUTHORIZED); + set_sta_flag(sta, WLAN_STA_AUTHORIZED); /* make sure mandatory rates are always added */ sta->sta.supp_rates[band] = supp_rates | @@ -914,6 +914,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, sdata->u.ibss.privacy = params->privacy; sdata->u.ibss.basic_rates = params->basic_rates; + sdata->u.ibss.last_scan_completed = jiffies; memcpy(sdata->vif.bss_conf.mcast_rate, params->mcast_rate, sizeof(params->mcast_rate)); @@ -995,7 +996,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) kfree(sdata->u.ibss.ie); skb = rcu_dereference_protected(sdata->u.ibss.presp, lockdep_is_held(&sdata->u.ibss.mtx)); - rcu_assign_pointer(sdata->u.ibss.presp, NULL); + RCU_INIT_POINTER(sdata->u.ibss.presp, NULL); sdata->vif.bss_conf.ibss_joined = false; ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_IBSS); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 62b86f0..8da371c5 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -53,11 +53,25 @@ struct ieee80211_local; #define TU_TO_EXP_TIME(x) (jiffies + usecs_to_jiffies((x) * 1024)) -#define IEEE80211_DEFAULT_UAPSD_QUEUES \ - (IEEE80211_WMM_IE_STA_QOSINFO_AC_BK | \ - IEEE80211_WMM_IE_STA_QOSINFO_AC_BE | \ - IEEE80211_WMM_IE_STA_QOSINFO_AC_VI | \ - IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) +/* + * Some APs experience problems when working with U-APSD. Decreasing the + * probability of that happening by using legacy mode for all ACs but VO isn't + * enough. + * + * Cisco 4410N originally forced us to enable VO by default only because it + * treated non-VO ACs as legacy. + * + * However some APs (notably Netgear R7000) silently reclassify packets to + * different ACs. Since u-APSD ACs require trigger frames for frame retrieval + * clients would never see some frames (e.g. ARP responses) or would fetch them + * accidentally after a long time. + * + * It makes little sense to enable u-APSD queues by default because it needs + * userspace applications to be aware of it to actually take advantage of the + * possible additional powersavings. Implicitly depending on driver autotrigger + * frame support doesn't make much sense. + */ +#define IEEE80211_DEFAULT_UAPSD_QUEUES 0 #define IEEE80211_DEFAULT_MAX_SP_LEN \ IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL @@ -136,7 +150,6 @@ typedef unsigned __bitwise__ ieee80211_tx_result; #define TX_DROP ((__force ieee80211_tx_result) 1u) #define TX_QUEUED ((__force ieee80211_tx_result) 2u) -#define IEEE80211_TX_FRAGMENTED BIT(0) #define IEEE80211_TX_UNICAST BIT(1) #define IEEE80211_TX_PS_BUFFERED BIT(2) @@ -149,7 +162,6 @@ struct ieee80211_tx_data { struct ieee80211_channel *channel; - u16 ethertype; unsigned int flags; }; @@ -202,7 +214,22 @@ struct ieee80211_rx_data { struct ieee80211_key *key; unsigned int flags; - int queue; + + /* + * Index into sequence numbers array, 0..16 + * since the last (16) is used for non-QoS, + * will be 16 on non-QoS frames. + */ + int seqno_idx; + + /* + * Index into the security IV/PN arrays, 0..16 + * since the last (16) is used for CCMP-encrypted + * management frames, will be set to 16 on mgmt + * frames and 0 on non-QoS frames. + */ + int security_idx; + u32 tkip_iv32; u16 tkip_iv16; }; @@ -246,6 +273,7 @@ struct mesh_stats { __u32 fwded_frames; /* Mesh total forwarded frames */ __u32 dropped_frames_ttl; /* Not transmitted since mesh_ttl == 0*/ __u32 dropped_frames_no_route; /* Not transmitted, no route found */ + __u32 dropped_frames_congestion;/* Not forwarded due to congestion */ atomic_t estab_plinks; }; @@ -308,6 +336,7 @@ struct ieee80211_work { u8 key[WLAN_KEY_LEN_WEP104]; u8 key_len, key_idx; bool privacy; + bool synced; } probe_auth; struct { struct cfg80211_bss *bss; @@ -321,6 +350,7 @@ struct ieee80211_work { u8 ssid_len; u8 supp_rates_len; bool wmm_used, use_11n, uapsd_used; + bool synced; } assoc; struct { u32 duration; @@ -419,6 +449,14 @@ struct ieee80211_if_managed { * generated for the current association. */ int last_cqm_event_signal; + + /* + * State variables for keeping track of RSSI of the AP currently + * connected to and informing driver when RSSI has gone + * below/above a certain threshold. + */ + int rssi_min_thold, rssi_max_thold; + int last_ave_beacon_signal; }; struct ieee80211_if_ibss { @@ -491,6 +529,7 @@ struct ieee80211_if_mesh { struct mesh_config mshcfg; u32 mesh_seqnum; bool accepting_plinks; + int num_gates; const u8 *ie; u8 ie_len; enum { @@ -517,12 +556,14 @@ struct ieee80211_if_mesh { * @IEEE80211_SDATA_DONT_BRIDGE_PACKETS: bridge packets between * associated stations and deliver multicast frames both * back to wireless media and to the local net stack. + * @IEEE80211_SDATA_DISCONNECT_RESUME: Disconnect after resume. */ enum ieee80211_sub_if_data_flags { IEEE80211_SDATA_ALLMULTI = BIT(0), IEEE80211_SDATA_PROMISC = BIT(1), IEEE80211_SDATA_OPERATING_GMODE = BIT(2), IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3), + IEEE80211_SDATA_DISCONNECT_RESUME = BIT(4), }; /** @@ -546,6 +587,9 @@ struct ieee80211_sub_if_data { /* keys */ struct list_head key_list; + /* count for keys needing tailroom space allocation */ + int crypto_tx_tailroom_needed_cnt; + struct net_device *dev; struct ieee80211_local *local; @@ -579,6 +623,8 @@ struct ieee80211_sub_if_data { __be16 control_port_protocol; bool control_port_no_encrypt; + struct ieee80211_tx_queue_params tx_conf[IEEE80211_MAX_QUEUES]; + struct work_struct work; struct sk_buff_head skb_queue; @@ -632,6 +678,11 @@ enum sdata_queue_type { enum { IEEE80211_RX_MSG = 1, IEEE80211_TX_STATUS_MSG = 2, + IEEE80211_EOSP_MSG = 3, +}; + +struct skb_eosp_msg_data { + u8 sta[ETH_ALEN], iface[ETH_ALEN]; }; enum queue_stop_reason { @@ -641,6 +692,7 @@ enum queue_stop_reason { IEEE80211_QUEUE_STOP_REASON_AGGREGATION, IEEE80211_QUEUE_STOP_REASON_SUSPEND, IEEE80211_QUEUE_STOP_REASON_SKB_ADD, + IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE, }; #ifdef CONFIG_MAC80211_LEDS @@ -664,16 +716,22 @@ struct tpt_led_trigger { * well be on the operating channel * @SCAN_HW_SCANNING: The hardware is scanning for us, we have no way to * determine if we are on the operating channel or not + * @SCAN_OFF_CHANNEL: We're off our operating channel for scanning, + * gets only set in conjunction with SCAN_SW_SCANNING * @SCAN_COMPLETED: Set for our scan work function when the driver reported * that the scan completed. * @SCAN_ABORTED: Set for our scan work function when the driver reported * a scan complete for an aborted scan. + * @SCAN_HW_CANCELLED: Set for our scan work function when the scan is being + * cancelled. */ enum { SCAN_SW_SCANNING, SCAN_HW_SCANNING, + SCAN_OFF_CHANNEL, SCAN_COMPLETED, SCAN_ABORTED, + SCAN_HW_CANCELLED, }; /** @@ -973,7 +1031,6 @@ struct ieee80211_local { unsigned int hw_roc_duration; u32 hw_roc_cookie; bool hw_roc_for_tx; - unsigned long hw_offchan_tx_cookie; /* dummy netdev for use w/ NAPI */ struct net_device napi_dev; @@ -993,69 +1050,6 @@ struct ieee80211_ra_tid { u16 tid; }; -/* Parsed Information Elements */ -struct ieee802_11_elems { - u8 *ie_start; - size_t total_len; - - /* pointers to IEs */ - u8 *ssid; - u8 *supp_rates; - u8 *fh_params; - u8 *ds_params; - u8 *cf_params; - struct ieee80211_tim_ie *tim; - u8 *ibss_params; - u8 *challenge; - u8 *wpa; - u8 *rsn; - u8 *erp_info; - u8 *ext_supp_rates; - u8 *wmm_info; - u8 *wmm_param; - struct ieee80211_ht_cap *ht_cap_elem; - struct ieee80211_ht_info *ht_info_elem; - struct ieee80211_meshconf_ie *mesh_config; - u8 *mesh_id; - u8 *peer_link; - u8 *preq; - u8 *prep; - u8 *perr; - struct ieee80211_rann_ie *rann; - u8 *ch_switch_elem; - u8 *country_elem; - u8 *pwr_constr_elem; - u8 *quiet_elem; /* first quite element */ - u8 *timeout_int; - - /* length of them, respectively */ - u8 ssid_len; - u8 supp_rates_len; - u8 fh_params_len; - u8 ds_params_len; - u8 cf_params_len; - u8 tim_len; - u8 ibss_params_len; - u8 challenge_len; - u8 wpa_len; - u8 rsn_len; - u8 erp_info_len; - u8 ext_supp_rates_len; - u8 wmm_info_len; - u8 wmm_param_len; - u8 mesh_id_len; - u8 peer_link_len; - u8 preq_len; - u8 prep_len; - u8 perr_len; - u8 ch_switch_elem_len; - u8 country_elem_len; - u8 pwr_constr_elem_len; - u8 quiet_elem_len; - u8 num_of_quiet_elem; /* can be more the one */ - u8 timeout_int_len; -}; - static inline struct ieee80211_local *hw_to_local( struct ieee80211_hw *hw) { @@ -1166,10 +1160,8 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata); void ieee80211_sched_scan_stopped_work(struct work_struct *work); /* off-channel helpers */ -bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local); -void ieee80211_offchannel_enable_all_ps(struct ieee80211_local *local, - bool tell_ap); -void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local); +void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local); +void ieee80211_offchannel_stop_station(struct ieee80211_local *local); void ieee80211_offchannel_return(struct ieee80211_local *local, bool enable_beaconing); void ieee80211_hw_roc_setup(struct ieee80211_local *local); @@ -1202,23 +1194,10 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev); -/* - * radiotap header for status frames - */ -struct ieee80211_tx_status_rtap_hdr { - struct ieee80211_radiotap_header hdr; - u8 rate; - u8 padding_for_rate; - __le16 tx_flags; - u8 data_retries; -} __packed; - - /* HT */ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband, struct ieee80211_ht_cap *ht_cap_ie, struct ieee80211_sta_ht_cap *ht_cap); -void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn); void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, const u8 *da, u16 tid, u16 initiator, u16 reason_code); @@ -1302,6 +1281,7 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int ke struct ieee80211_hdr *hdr, const u8 *tsc, gfp_t gfp); void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata); +void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); void ieee802_11_parse_elems(u8 *start, size_t len, struct ieee802_11_elems *elems); @@ -1333,11 +1313,11 @@ void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, enum queue_stop_reason reason); void ieee80211_add_pending_skb(struct ieee80211_local *local, struct sk_buff *skb); -int ieee80211_add_pending_skbs(struct ieee80211_local *local, - struct sk_buff_head *skbs); -int ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, - struct sk_buff_head *skbs, - void (*fn)(void *data), void *data); +void ieee80211_add_pending_skbs(struct ieee80211_local *local, + struct sk_buff_head *skbs); +void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, + struct sk_buff_head *skbs, + void (*fn)(void *data), void *data); void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, @@ -1348,12 +1328,14 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, enum ieee80211_band band, u32 rate_mask, u8 channel); struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, - u8 *dst, + u8 *dst, u32 ratemask, const u8 *ssid, size_t ssid_len, - const u8 *ie, size_t ie_len); + const u8 *ie, size_t ie_len, + bool directed); void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, const u8 *ssid, size_t ssid_len, - const u8 *ie, size_t ie_len); + const u8 *ie, size_t ie_len, + u32 ratemask, bool directed, bool no_cck); void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, const size_t supp_rates_len, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index bd1ef84..24ec86f 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -299,8 +299,8 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) goto err_del_interface; } - /* no locking required since STA is not live yet */ - sta->flags |= WLAN_STA_AUTHORIZED; + /* no atomic bitop required since STA is not live yet */ + set_sta_flag(sta, WLAN_STA_AUTHORIZED); res = sta_info_insert(sta); if (res) { @@ -363,8 +363,7 @@ static int ieee80211_open(struct net_device *dev) int err; /* fail early if user set an invalid address */ - if (!is_zero_ether_addr(dev->dev_addr) && - !is_valid_ether_addr(dev->dev_addr)) + if (!is_valid_ether_addr(dev->dev_addr)) return -EADDRNOTAVAIL; err = ieee80211_check_concurrent_iface(sdata, sdata->vif.type); @@ -383,10 +382,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, u32 hw_reconf_flags = 0; int i; enum nl80211_channel_type orig_ct; + bool cancel_scan; clear_bit(SDATA_STATE_RUNNING, &sdata->state); - if (local->scan_sdata == sdata) + cancel_scan = local->scan_sdata == sdata; + if (cancel_scan) ieee80211_scan_cancel(local); /* @@ -457,21 +458,19 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, BSS_CHANGED_BEACON_ENABLED); /* remove beacon */ - rcu_assign_pointer(sdata->u.ap.beacon, NULL); + RCU_INIT_POINTER(sdata->u.ap.beacon, NULL); synchronize_rcu(); kfree(old_beacon); - /* free all potentially still buffered bcast frames */ - while ((skb = skb_dequeue(&sdata->u.ap.ps_bc_buf))) { - local->total_ps_buffered--; - dev_kfree_skb(skb); - } - /* down all dependent devices, that is VLANs */ list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans, u.vlan.list) dev_close(vlan->dev); WARN_ON(!list_empty(&sdata->u.ap.vlans)); + + /* free all potentially still buffered bcast frames */ + local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps_bc_buf); + skb_queue_purge(&sdata->u.ap.ps_bc_buf); } if (going_down) @@ -546,6 +545,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, ieee80211_recalc_ps(local, -1); + if (cancel_scan) + flush_delayed_work(&local->scan_work); + if (local->open_count == 0) { if (local->ops->napi_poll) napi_disable(&local->napi); @@ -658,7 +660,7 @@ static const struct net_device_ops ieee80211_dataif_ops = { .ndo_stop = ieee80211_stop, .ndo_uninit = ieee80211_teardown_sdata, .ndo_start_xmit = ieee80211_subif_start_xmit, - .ndo_set_multicast_list = ieee80211_set_multicast_list, + .ndo_set_rx_mode = ieee80211_set_multicast_list, .ndo_change_mtu = ieee80211_change_mtu, .ndo_set_mac_address = ieee80211_change_mac, .ndo_select_queue = ieee80211_netdev_select_queue, @@ -702,7 +704,7 @@ static const struct net_device_ops ieee80211_monitorif_ops = { .ndo_stop = ieee80211_stop, .ndo_uninit = ieee80211_teardown_sdata, .ndo_start_xmit = ieee80211_monitor_start_xmit, - .ndo_set_multicast_list = ieee80211_set_multicast_list, + .ndo_set_rx_mode = ieee80211_set_multicast_list, .ndo_change_mtu = ieee80211_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_select_queue = ieee80211_monitor_select_queue, @@ -1143,8 +1145,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, ASSERT_RTNL(); - ndev = alloc_netdev_mq(sizeof(*sdata) + local->hw.vif_data_size, - name, ieee80211_if_setup, local->hw.queues); + ndev = alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size, + name, ieee80211_if_setup, local->hw.queues, 1); if (!ndev) return -ENOMEM; dev_net_set(ndev, wiphy_net(local->hw.wiphy)); @@ -1227,6 +1229,9 @@ void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata) list_del_rcu(&sdata->list); mutex_unlock(&sdata->local->iflist_mtx); + if (ieee80211_vif_is_mesh(&sdata->vif)) + mesh_path_flush_by_iface(sdata); + synchronize_rcu(); unregister_netdevice(sdata->dev); } @@ -1255,6 +1260,9 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local) list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) { list_del(&sdata->list); + if (ieee80211_vif_is_mesh(&sdata->vif)) + mesh_path_flush_by_iface(sdata); + unregister_netdevice_queue(sdata->dev, &unreg_list); } mutex_unlock(&local->iflist_mtx); diff --git a/net/mac80211/key.c b/net/mac80211/key.c index f825e2f..fb02ea5 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -15,6 +15,7 @@ #include <linux/rcupdate.h> #include <linux/rtnetlink.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/mac80211.h> #include "ieee80211_i.h" #include "driver-ops.h" @@ -61,6 +62,36 @@ static struct ieee80211_sta *get_sta_for_key(struct ieee80211_key *key) return NULL; } +static void increment_tailroom_need_count(struct ieee80211_sub_if_data *sdata) +{ + /* + * When this count is zero, SKB resizing for allocating tailroom + * for IV or MMIC is skipped. But, this check has created two race + * cases in xmit path while transiting from zero count to one: + * + * 1. SKB resize was skipped because no key was added but just before + * the xmit key is added and SW encryption kicks off. + * + * 2. SKB resize was skipped because all the keys were hw planted but + * just before xmit one of the key is deleted and SW encryption kicks + * off. + * + * In both the above case SW encryption will find not enough space for + * tailroom and exits with WARN_ON. (See WARN_ONs at wpa.c) + * + * Solution has been explained at + * http://mid.gmane.org/1308590980.4322.19.camel@jlt3.sipsolutions.net + */ + + if (!sdata->crypto_tx_tailroom_needed_cnt++) { + /* + * Flush all XMIT packets currently using HW encryption or no + * encryption at all if the count transition is from 0 -> 1. + */ + synchronize_net(); + } +} + static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) { struct ieee80211_sub_if_data *sdata; @@ -101,6 +132,11 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) if (!ret) { key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; + + if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) || + (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV))) + sdata->crypto_tx_tailroom_needed_cnt--; + return 0; } @@ -142,6 +178,10 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) sta = get_sta_for_key(key); sdata = key->sdata; + if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) || + (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV))) + increment_tailroom_need_count(sdata); + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, @@ -239,7 +279,7 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, bool defunikey, defmultikey, defmgmtkey; if (new) - list_add(&new->list, &sdata->key_list); + list_add_tail(&new->list, &sdata->key_list); if (sta && pairwise) { rcu_assign_pointer(sta->ptk, new); @@ -330,6 +370,7 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, get_unaligned_le16(seq); } } + spin_lock_init(&key->u.tkip.txlock); break; case WLAN_CIPHER_SUITE_CCMP: key->conf.iv_len = CCMP_HDR_LEN; @@ -394,8 +435,10 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key) ieee80211_aes_key_free(key->u.ccmp.tfm); if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC) ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm); - if (key->local) + if (key->local) { ieee80211_debugfs_key_remove(key); + key->sdata->crypto_tx_tailroom_needed_cnt--; + } kfree(key); } @@ -422,7 +465,7 @@ int ieee80211_key_link(struct ieee80211_key *key, * some hardware cannot handle TKIP with QoS, so * we indicate whether QoS could be in use. */ - if (test_sta_flags(sta, WLAN_STA_WME)) + if (test_sta_flag(sta, WLAN_STA_WME)) key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA; } else { if (sdata->vif.type == NL80211_IFTYPE_STATION) { @@ -436,7 +479,7 @@ int ieee80211_key_link(struct ieee80211_key *key, /* same here, the AP could be using QoS */ ap = sta_info_get(key->sdata, key->sdata->u.mgd.bssid); if (ap) { - if (test_sta_flags(ap, WLAN_STA_WME)) + if (test_sta_flag(ap, WLAN_STA_WME)) key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA; } @@ -452,6 +495,8 @@ int ieee80211_key_link(struct ieee80211_key *key, else old_key = key_mtx_dereference(sdata->local, sdata->keys[idx]); + increment_tailroom_need_count(sdata); + __ieee80211_key_replace(sdata, sta, pairwise, old_key, key); __ieee80211_key_destroy(old_key); @@ -498,12 +543,49 @@ void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata) mutex_lock(&sdata->local->key_mtx); - list_for_each_entry(key, &sdata->key_list, list) + sdata->crypto_tx_tailroom_needed_cnt = 0; + + list_for_each_entry(key, &sdata->key_list, list) { + increment_tailroom_need_count(sdata); ieee80211_key_enable_hw_accel(key); + } mutex_unlock(&sdata->local->key_mtx); } +void ieee80211_iter_keys(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + void (*iter)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *data), + void *iter_data) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_key *key; + struct ieee80211_sub_if_data *sdata; + + ASSERT_RTNL(); + + mutex_lock(&local->key_mtx); + if (vif) { + sdata = vif_to_sdata(vif); + list_for_each_entry(key, &sdata->key_list, list) + iter(hw, &sdata->vif, + key->sta ? &key->sta->sta : NULL, + &key->conf, iter_data); + } else { + list_for_each_entry(sdata, &local->interfaces, list) + list_for_each_entry(key, &sdata->key_list, list) + iter(hw, &sdata->vif, + key->sta ? &key->sta->sta : NULL, + &key->conf, iter_data); + } + mutex_unlock(&local->key_mtx); +} +EXPORT_SYMBOL(ieee80211_iter_keys); + void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata) { struct ieee80211_key *key; @@ -533,3 +615,89 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) mutex_unlock(&sdata->local->key_mtx); } + + +void ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const u8 *bssid, + const u8 *replay_ctr, gfp_t gfp) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + trace_api_gtk_rekey_notify(sdata, bssid, replay_ctr); + + cfg80211_gtk_rekey_notify(sdata->dev, bssid, replay_ctr, gfp); +} +EXPORT_SYMBOL_GPL(ieee80211_gtk_rekey_notify); + +void ieee80211_get_key_tx_seq(struct ieee80211_key_conf *keyconf, + struct ieee80211_key_seq *seq) +{ + struct ieee80211_key *key; + u64 pn64; + + if (WARN_ON(!(keyconf->flags & IEEE80211_KEY_FLAG_GENERATE_IV))) + return; + + key = container_of(keyconf, struct ieee80211_key, conf); + + switch (key->conf.cipher) { + case WLAN_CIPHER_SUITE_TKIP: + seq->tkip.iv32 = key->u.tkip.tx.iv32; + seq->tkip.iv16 = key->u.tkip.tx.iv16; + break; + case WLAN_CIPHER_SUITE_CCMP: + pn64 = atomic64_read(&key->u.ccmp.tx_pn); + seq->ccmp.pn[5] = pn64; + seq->ccmp.pn[4] = pn64 >> 8; + seq->ccmp.pn[3] = pn64 >> 16; + seq->ccmp.pn[2] = pn64 >> 24; + seq->ccmp.pn[1] = pn64 >> 32; + seq->ccmp.pn[0] = pn64 >> 40; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + pn64 = atomic64_read(&key->u.aes_cmac.tx_pn); + seq->ccmp.pn[5] = pn64; + seq->ccmp.pn[4] = pn64 >> 8; + seq->ccmp.pn[3] = pn64 >> 16; + seq->ccmp.pn[2] = pn64 >> 24; + seq->ccmp.pn[1] = pn64 >> 32; + seq->ccmp.pn[0] = pn64 >> 40; + break; + default: + WARN_ON(1); + } +} +EXPORT_SYMBOL(ieee80211_get_key_tx_seq); + +void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, + int tid, struct ieee80211_key_seq *seq) +{ + struct ieee80211_key *key; + const u8 *pn; + + key = container_of(keyconf, struct ieee80211_key, conf); + + switch (key->conf.cipher) { + case WLAN_CIPHER_SUITE_TKIP: + if (WARN_ON(tid < 0 || tid >= NUM_RX_DATA_QUEUES)) + return; + seq->tkip.iv32 = key->u.tkip.rx[tid].iv32; + seq->tkip.iv16 = key->u.tkip.rx[tid].iv16; + break; + case WLAN_CIPHER_SUITE_CCMP: + if (WARN_ON(tid < -1 || tid >= NUM_RX_DATA_QUEUES)) + return; + if (tid < 0) + pn = key->u.ccmp.rx_pn[NUM_RX_DATA_QUEUES]; + else + pn = key->u.ccmp.rx_pn[tid]; + memcpy(seq->ccmp.pn, pn, CCMP_PN_LEN); + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + if (WARN_ON(tid != 0)) + return; + pn = key->u.aes_cmac.rx_pn; + memcpy(seq->aes_cmac.pn, pn, CMAC_PN_LEN); + break; + } +} +EXPORT_SYMBOL(ieee80211_get_key_rx_seq); diff --git a/net/mac80211/key.h b/net/mac80211/key.h index d801d53..7d4e31f 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -28,8 +28,9 @@ #define CCMP_PN_LEN 6 #define TKIP_IV_LEN 8 #define TKIP_ICV_LEN 4 +#define CMAC_PN_LEN 6 -#define NUM_RX_DATA_QUEUES 17 +#define NUM_RX_DATA_QUEUES 16 struct ieee80211_local; struct ieee80211_sub_if_data; @@ -40,9 +41,11 @@ struct sta_info; * * @KEY_FLAG_UPLOADED_TO_HARDWARE: Indicates that this key is present * in the hardware for TX crypto hardware acceleration. + * @KEY_FLAG_TAINTED: Key is tainted and packets should be dropped. */ enum ieee80211_internal_key_flags { KEY_FLAG_UPLOADED_TO_HARDWARE = BIT(0), + KEY_FLAG_TAINTED = BIT(1), }; enum ieee80211_internal_tkip_state { @@ -52,9 +55,10 @@ enum ieee80211_internal_tkip_state { }; struct tkip_ctx { - u32 iv32; - u16 iv16; - u16 p1k[5]; + u32 iv32; /* current iv32 */ + u16 iv16; /* current iv16 */ + u16 p1k[5]; /* p1k cache */ + u32 p1k_iv32; /* iv32 for which p1k computed */ enum ieee80211_internal_tkip_state state; }; @@ -71,6 +75,9 @@ struct ieee80211_key { union { struct { + /* protects tx context */ + spinlock_t txlock; + /* last used TSC */ struct tkip_ctx tx; @@ -78,32 +85,23 @@ struct ieee80211_key { struct tkip_ctx rx[NUM_RX_DATA_QUEUES]; } tkip; struct { - u8 tx_pn[6]; + atomic64_t tx_pn; /* * Last received packet number. The first * NUM_RX_DATA_QUEUES counters are used with Data * frames and the last counter is used with Robust * Management frames. */ - u8 rx_pn[NUM_RX_DATA_QUEUES + 1][6]; + u8 rx_pn[NUM_RX_DATA_QUEUES + 1][CCMP_PN_LEN]; struct crypto_cipher *tfm; u32 replays; /* dot11RSNAStatsCCMPReplays */ - /* scratch buffers for virt_to_page() (crypto API) */ -#ifndef AES_BLOCK_LEN -#define AES_BLOCK_LEN 16 -#endif - u8 tx_crypto_buf[6 * AES_BLOCK_LEN]; - u8 rx_crypto_buf[6 * AES_BLOCK_LEN]; } ccmp; struct { - u8 tx_pn[6]; - u8 rx_pn[6]; + atomic64_t tx_pn; + u8 rx_pn[CMAC_PN_LEN]; struct crypto_cipher *tfm; u32 replays; /* dot11RSNAStatsCMACReplays */ u32 icverrors; /* dot11RSNAStatsCMACICVErrors */ - /* scratch buffers for virt_to_page() (crypto API) */ - u8 tx_crypto_buf[2 * AES_BLOCK_LEN]; - u8 rx_crypto_buf[2 * AES_BLOCK_LEN]; } aes_cmac; } u; diff --git a/net/mac80211/led.c b/net/mac80211/led.c index 1459033..1bf7903 100644 --- a/net/mac80211/led.c +++ b/net/mac80211/led.c @@ -9,6 +9,7 @@ /* just for IFNAMSIZ */ #include <linux/if.h> #include <linux/slab.h> +#include <linux/export.h> #include "led.h" void ieee80211_led_rx(struct ieee80211_local *local) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 1e36fb3..7d9b21d 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -19,7 +19,7 @@ #include <linux/if_arp.h> #include <linux/rtnetlink.h> #include <linux/bitmap.h> -#include <linux/pm_qos_params.h> +#include <linux/pm_qos.h> #include <linux/inetdevice.h> #include <net/net_namespace.h> #include <net/cfg80211.h> @@ -92,47 +92,6 @@ static void ieee80211_reconfig_filter(struct work_struct *work) ieee80211_configure_filter(local); } -/* - * Returns true if we are logically configured to be on - * the operating channel AND the hardware-conf is currently - * configured on the operating channel. Compares channel-type - * as well. - */ -bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local) -{ - struct ieee80211_channel *chan, *scan_chan; - enum nl80211_channel_type channel_type; - - /* This logic needs to match logic in ieee80211_hw_config */ - if (local->scan_channel) { - chan = local->scan_channel; - /* If scanning on oper channel, use whatever channel-type - * is currently in use. - */ - if (chan == local->oper_channel) - channel_type = local->_oper_channel_type; - else - channel_type = NL80211_CHAN_NO_HT; - } else if (local->tmp_channel) { - chan = scan_chan = local->tmp_channel; - channel_type = local->tmp_channel_type; - } else { - chan = local->oper_channel; - channel_type = local->_oper_channel_type; - } - - if (chan != local->oper_channel || - channel_type != local->_oper_channel_type) - return false; - - /* Check current hardware-config against oper_channel. */ - if ((local->oper_channel != local->hw.conf.channel) || - (local->_oper_channel_type != local->hw.conf.channel_type)) - return false; - - return true; -} - int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) { struct ieee80211_channel *chan, *scan_chan; @@ -145,9 +104,6 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) scan_chan = local->scan_channel; - /* If this off-channel logic ever changes, ieee80211_on_oper_channel - * may need to change as well. - */ offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; if (scan_chan) { chan = scan_chan; @@ -158,19 +114,17 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) channel_type = local->_oper_channel_type; else channel_type = NL80211_CHAN_NO_HT; - } else if (local->tmp_channel) { + local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL; + } else if (local->tmp_channel && + local->oper_channel != local->tmp_channel) { chan = scan_chan = local->tmp_channel; channel_type = local->tmp_channel_type; + local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL; } else { chan = local->oper_channel; channel_type = local->_oper_channel_type; - } - - if (chan != local->oper_channel || - channel_type != local->_oper_channel_type) - local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL; - else local->hw.conf.flags &= ~IEEE80211_CONF_OFFCHANNEL; + } offchannel_flag ^= local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; @@ -279,7 +233,7 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, if (changed & BSS_CHANGED_BEACON_ENABLED) { if (local->quiescing || !ieee80211_sdata_running(sdata) || - test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) { + test_bit(SCAN_SW_SCANNING, &local->scanning)) { sdata->vif.bss_conf.enable_beacon = false; } else { /* @@ -325,6 +279,8 @@ u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata) static void ieee80211_tasklet_handler(unsigned long data) { struct ieee80211_local *local = (struct ieee80211_local *) data; + struct sta_info *sta, *tmp; + struct skb_eosp_msg_data *eosp_data; struct sk_buff *skb; while ((skb = skb_dequeue(&local->skb_queue)) || @@ -340,6 +296,18 @@ static void ieee80211_tasklet_handler(unsigned long data) skb->pkt_type = 0; ieee80211_tx_status(local_to_hw(local), skb); break; + case IEEE80211_EOSP_MSG: + eosp_data = (void *)skb->cb; + for_each_sta_info(local, eosp_data->sta, sta, tmp) { + /* skip wrong virtual interface */ + if (memcmp(eosp_data->iface, + sta->sdata->vif.addr, ETH_ALEN)) + continue; + clear_sta_flag(sta, WLAN_STA_SP); + break; + } + dev_kfree_skb(skb); + break; default: WARN(1, "mac80211: Packet is of unknown type %d\n", skb->pkt_type); @@ -608,6 +576,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, local->hw.max_rates = 1; local->hw.max_report_rates = 0; local->hw.max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; + local->hw.max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; local->hw.conf.long_frame_max_tx_count = wiphy->retry_long; local->hw.conf.short_frame_max_tx_count = wiphy->retry_short; local->user_power_level = -1; @@ -742,6 +711,12 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (!local->int_scan_req) return -ENOMEM; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!local->hw.wiphy->bands[band]) + continue; + local->int_scan_req->rates[band] = (u32) -1; + } + /* if low-level driver supports AP, we also support VLAN */ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) { hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN); @@ -862,6 +837,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (local->ops->sched_scan_start) local->hw.wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; + /* mac80211 based drivers don't support internal TDLS setup */ + if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) + local->hw.wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP; + result = wiphy_register(local->hw.wiphy); if (result < 0) goto fail_wiphy_register; @@ -885,12 +864,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) * and we need some headroom for passing the frame to monitor * interfaces, but never both at the same time. */ -#ifndef __CHECKER__ - BUILD_BUG_ON(IEEE80211_TX_STATUS_HEADROOM != - sizeof(struct ieee80211_tx_status_rtap_hdr)); -#endif local->tx_headroom = max_t(unsigned int , local->hw.extra_tx_headroom, - sizeof(struct ieee80211_tx_status_rtap_hdr)); + IEEE80211_TX_STATUS_HEADROOM); debugfs_hw_add(local); @@ -1012,7 +987,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) cancel_work_sync(&local->reconfig_filter); ieee80211_clear_tx_pending(local); - sta_info_stop(local); rate_control_deinitialize(local); if (skb_queue_len(&local->skb_queue) || @@ -1024,6 +998,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) destroy_workqueue(local->workqueue); wiphy_unregister(local->hw.wiphy); + sta_info_stop(local); ieee80211_wep_free(local); ieee80211_led_exit(local); kfree(local->int_scan_req); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 370aa94..f85de8e 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -13,10 +13,6 @@ #include "ieee80211_i.h" #include "mesh.h" -#define IEEE80211_MESH_PEER_INACTIVITY_LIMIT (1800 * HZ) -#define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ) -#define IEEE80211_MESH_RANN_INTERVAL (1 * HZ) - #define MESHCONF_CAPAB_ACCEPT_PLINKS 0x01 #define MESHCONF_CAPAB_FORWARDING 0x08 @@ -27,6 +23,17 @@ int mesh_allocated; static struct kmem_cache *rm_cache; +#ifdef CONFIG_MAC80211_MESH +bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt) +{ + return (mgmt->u.action.u.mesh_action.action_code == + WLAN_MESH_ACTION_HWMP_PATH_SELECTION); +} +#else +bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt) +{ return false; } +#endif + void ieee80211s_init(void) { mesh_pathtbl_init(); @@ -193,10 +200,9 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr, } p = kmem_cache_alloc(rm_cache, GFP_ATOMIC); - if (!p) { - printk(KERN_DEBUG "o11s: could not allocate RMC entry\n"); + if (!p) return 0; - } + p->seqnum = seqnum; p->exp_time = jiffies + RMC_TIMEOUT; memcpy(p->sa, sa, ETH_ALEN); @@ -204,89 +210,136 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr, return 0; } -void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) +int +mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { - struct ieee80211_local *local = sdata->local; - struct ieee80211_supported_band *sband; - u8 *pos; - int len, i, rate; - u8 neighbors; - - sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; - len = sband->n_bitrates; - if (len > 8) - len = 8; - pos = skb_put(skb, len + 2); - *pos++ = WLAN_EID_SUPP_RATES; - *pos++ = len; - for (i = 0; i < len; i++) { - rate = sband->bitrates[i].bitrate; - *pos++ = (u8) (rate / 5); - } - - if (sband->n_bitrates > len) { - pos = skb_put(skb, sband->n_bitrates - len + 2); - *pos++ = WLAN_EID_EXT_SUPP_RATES; - *pos++ = sband->n_bitrates - len; - for (i = len; i < sband->n_bitrates; i++) { - rate = sband->bitrates[i].bitrate; - *pos++ = (u8) (rate / 5); - } - } - - if (sband->band == IEEE80211_BAND_2GHZ) { - pos = skb_put(skb, 2 + 1); - *pos++ = WLAN_EID_DS_PARAMS; - *pos++ = 1; - *pos++ = ieee80211_frequency_to_channel(local->hw.conf.channel->center_freq); - } + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + u8 *pos, neighbors; + u8 meshconf_len = sizeof(struct ieee80211_meshconf_ie); - pos = skb_put(skb, 2 + sdata->u.mesh.mesh_id_len); - *pos++ = WLAN_EID_MESH_ID; - *pos++ = sdata->u.mesh.mesh_id_len; - if (sdata->u.mesh.mesh_id_len) - memcpy(pos, sdata->u.mesh.mesh_id, sdata->u.mesh.mesh_id_len); + if (skb_tailroom(skb) < 2 + meshconf_len) + return -ENOMEM; - pos = skb_put(skb, 2 + sizeof(struct ieee80211_meshconf_ie)); + pos = skb_put(skb, 2 + meshconf_len); *pos++ = WLAN_EID_MESH_CONFIG; - *pos++ = sizeof(struct ieee80211_meshconf_ie); + *pos++ = meshconf_len; /* Active path selection protocol ID */ - *pos++ = sdata->u.mesh.mesh_pp_id; - + *pos++ = ifmsh->mesh_pp_id; /* Active path selection metric ID */ - *pos++ = sdata->u.mesh.mesh_pm_id; - + *pos++ = ifmsh->mesh_pm_id; /* Congestion control mode identifier */ - *pos++ = sdata->u.mesh.mesh_cc_id; - + *pos++ = ifmsh->mesh_cc_id; /* Synchronization protocol identifier */ - *pos++ = sdata->u.mesh.mesh_sp_id; - + *pos++ = ifmsh->mesh_sp_id; /* Authentication Protocol identifier */ - *pos++ = sdata->u.mesh.mesh_auth_id; - + *pos++ = ifmsh->mesh_auth_id; /* Mesh Formation Info - number of neighbors */ - neighbors = atomic_read(&sdata->u.mesh.mshstats.estab_plinks); + neighbors = atomic_read(&ifmsh->mshstats.estab_plinks); /* Number of neighbor mesh STAs or 15 whichever is smaller */ neighbors = (neighbors > 15) ? 15 : neighbors; *pos++ = neighbors << 1; - /* Mesh capability */ - sdata->u.mesh.accepting_plinks = mesh_plink_availables(sdata); + ifmsh->accepting_plinks = mesh_plink_availables(sdata); *pos = MESHCONF_CAPAB_FORWARDING; - *pos++ |= sdata->u.mesh.accepting_plinks ? + *pos++ |= ifmsh->accepting_plinks ? MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; *pos++ = 0x00; - if (sdata->u.mesh.ie) { - int len = sdata->u.mesh.ie_len; - const u8 *data = sdata->u.mesh.ie; - if (skb_tailroom(skb) > len) - memcpy(skb_put(skb, len), data, len); + return 0; +} + +int +mesh_add_meshid_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + u8 *pos; + + if (skb_tailroom(skb) < 2 + ifmsh->mesh_id_len) + return -ENOMEM; + + pos = skb_put(skb, 2 + ifmsh->mesh_id_len); + *pos++ = WLAN_EID_MESH_ID; + *pos++ = ifmsh->mesh_id_len; + if (ifmsh->mesh_id_len) + memcpy(pos, ifmsh->mesh_id, ifmsh->mesh_id_len); + + return 0; +} + +int +mesh_add_vendor_ies(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + u8 offset, len; + const u8 *data; + + if (!ifmsh->ie || !ifmsh->ie_len) + return 0; + + /* fast-forward to vendor IEs */ + offset = ieee80211_ie_split_vendor(ifmsh->ie, ifmsh->ie_len, 0); + + if (offset) { + len = ifmsh->ie_len - offset; + data = ifmsh->ie + offset; + if (skb_tailroom(skb) < len) + return -ENOMEM; + memcpy(skb_put(skb, len), data, len); } + + return 0; } +int +mesh_add_rsn_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + u8 len = 0; + const u8 *data; + + if (!ifmsh->ie || !ifmsh->ie_len) + return 0; + + /* find RSN IE */ + data = ifmsh->ie; + while (data < ifmsh->ie + ifmsh->ie_len) { + if (*data == WLAN_EID_RSN) { + len = data[1] + 2; + break; + } + data++; + } + + if (len) { + if (skb_tailroom(skb) < len) + return -ENOMEM; + memcpy(skb_put(skb, len), data, len); + } + + return 0; +} + +int mesh_add_ds_params_ie(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_supported_band *sband; + u8 *pos; + + if (skb_tailroom(skb) < 3) + return -ENOMEM; + + sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + if (sband->band == IEEE80211_BAND_2GHZ) { + pos = skb_put(skb, 2 + 1); + *pos++ = WLAN_EID_DS_PARAMS; + *pos++ = 1; + *pos++ = ieee80211_frequency_to_channel(local->hw.conf.channel->center_freq); + } + + return 0; +} static void ieee80211_mesh_path_timer(unsigned long data) { @@ -352,8 +405,7 @@ int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, memcpy(hdr->addr3, meshsa, ETH_ALEN); return 24; } else { - *fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | - IEEE80211_FCTL_TODS); + *fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); /* RA TA DA SA */ memset(hdr->addr1, 0, ETH_ALEN); /* RA is resolved later */ memcpy(hdr->addr2, meshsa, ETH_ALEN); @@ -425,7 +477,8 @@ static void ieee80211_mesh_rootpath(struct ieee80211_sub_if_data *sdata) mesh_path_tx_root_frame(sdata); mod_timer(&ifmsh->mesh_path_root_timer, - round_jiffies(jiffies + IEEE80211_MESH_RANN_INTERVAL)); + round_jiffies(TU_TO_EXP_TIME( + ifmsh->mshcfg.dot11MeshHWMPRannInterval))); } #ifdef CONFIG_PM @@ -433,7 +486,7 @@ void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; - /* use atomic bitops in case both timers fire at the same time */ + /* use atomic bitops in case all timers fire at the same time */ if (del_timer_sync(&ifmsh->housekeeping_timer)) set_bit(TMR_RUNNING_HK, &ifmsh->timers_running); @@ -558,11 +611,18 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata, struct ieee80211_rx_status *rx_status) { switch (mgmt->u.action.category) { - case WLAN_CATEGORY_MESH_ACTION: - mesh_rx_plink_frame(sdata, mgmt, len, rx_status); + case WLAN_CATEGORY_SELF_PROTECTED: + switch (mgmt->u.action.u.self_prot.action_code) { + case WLAN_SP_MESH_PEERING_OPEN: + case WLAN_SP_MESH_PEERING_CLOSE: + case WLAN_SP_MESH_PEERING_CONFIRM: + mesh_rx_plink_frame(sdata, mgmt, len, rx_status); + break; + } break; - case WLAN_CATEGORY_MESH_PATH_SEL: - mesh_rx_path_sel_frame(sdata, mgmt, len); + case WLAN_CATEGORY_MESH_ACTION: + if (mesh_action_is_path_sel(mgmt)) + mesh_rx_path_sel_frame(sdata, mgmt, len); break; } } @@ -634,6 +694,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) ifmsh->accepting_plinks = true; ifmsh->preq_id = 0; ifmsh->sn = 0; + ifmsh->num_gates = 0; atomic_set(&ifmsh->mpaths, 0); mesh_rmc_init(sdata); ifmsh->last_preq = jiffies; diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 249e733..8c00e2d 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -80,7 +80,10 @@ enum mesh_deferred_task_flags { * retry * @discovery_retries: number of discovery retries * @flags: mesh path flags, as specified on &enum mesh_path_flags - * @state_lock: mesh path state lock + * @state_lock: mesh path state lock used to protect changes to the + * mpath itself. No need to take this lock when adding or removing + * an mpath to a hash bucket on a path table. + * @is_gate: the destination station of this path is a mesh gate * * * The combination of dst and sdata is unique in the mesh path table. Since the @@ -104,6 +107,7 @@ struct mesh_path { u8 discovery_retries; enum mesh_path_flags flags; spinlock_t state_lock; + bool is_gate; }; /** @@ -120,6 +124,9 @@ struct mesh_path { * buckets * @mean_chain_len: maximum average length for the hash buckets' list, if it is * reached, the table will grow + * @known_gates: list of known mesh gates and their mpaths by the station. The + * gate's mpath may or may not be resolved and active. + * * rcu_head: RCU head to free the table */ struct mesh_table { @@ -133,6 +140,8 @@ struct mesh_table { int (*copy_node) (struct hlist_node *p, struct mesh_table *newtbl); int size_order; int mean_chain_len; + struct hlist_head *known_gates; + spinlock_t gates_lock; struct rcu_head rcu_head; }; @@ -166,6 +175,8 @@ struct mesh_rmc { u32 idx_mask; }; +#define IEEE80211_MESH_PEER_INACTIVITY_LIMIT (1800 * HZ) +#define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ) #define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units */ @@ -177,14 +188,6 @@ struct mesh_rmc { /* Maximum number of paths per interface */ #define MESH_MAX_MPATHS 1024 -/* Pending ANA approval */ -#define MESH_PATH_SEL_ACTION 0 - -/* PERR reason codes */ -#define PEER_RCODE_UNSPECIFIED 11 -#define PERR_RCODE_NO_ROUTE 12 -#define PERR_RCODE_DEST_UNREACH 13 - /* Public interfaces */ /* Various */ int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, @@ -199,6 +202,16 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, void mesh_ids_set_default(struct ieee80211_if_mesh *mesh); void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); +int mesh_add_meshconf_ie(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata); +int mesh_add_meshid_ie(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata); +int mesh_add_rsn_ie(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata); +int mesh_add_vendor_ies(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata); +int mesh_add_ds_params_ie(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata); void mesh_rmc_free(struct ieee80211_sub_if_data *sdata); int mesh_rmc_init(struct ieee80211_sub_if_data *sdata); void ieee80211s_init(void); @@ -223,10 +236,13 @@ struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data *sdata); void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop); void mesh_path_expire(struct ieee80211_sub_if_data *sdata); -void mesh_path_flush(struct ieee80211_sub_if_data *sdata); void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len); int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata); + +int mesh_path_add_gate(struct mesh_path *mpath); +int mesh_path_send_to_gates(struct mesh_path *mpath); +int mesh_gate_num(struct ieee80211_sub_if_data *sdata); /* Mesh plinks */ void mesh_neighbour_update(u8 *hw_addr, u32 rates, struct ieee80211_sub_if_data *sdata, @@ -256,12 +272,14 @@ void mesh_pathtbl_unregister(void); int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata); void mesh_path_timer(unsigned long data); void mesh_path_flush_by_nexthop(struct sta_info *sta); +void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata); void mesh_path_discard_frame(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); void mesh_path_quiesce(struct ieee80211_sub_if_data *sdata); void mesh_path_restart(struct ieee80211_sub_if_data *sdata); void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata); +bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt); extern int mesh_paths_generation; #ifdef CONFIG_MAC80211_MESH diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 2b18053..174040a 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -8,10 +8,12 @@ */ #include <linux/slab.h> +#include "wme.h" #include "mesh.h" #ifdef CONFIG_MAC80211_VERBOSE_MHWMP_DEBUG -#define mhwmp_dbg(fmt, args...) printk(KERN_DEBUG "Mesh HWMP: " fmt, ##args) +#define mhwmp_dbg(fmt, args...) \ + printk(KERN_DEBUG "Mesh HWMP (%s): " fmt "\n", sdata->name, ##args) #else #define mhwmp_dbg(fmt, args...) do { (void)(0); } while (0) #endif @@ -57,29 +59,29 @@ static inline u32 u16_field_get(u8 *preq_elem, int offset, bool ae) #define PREQ_IE_TTL(x) (*(x + 2)) #define PREQ_IE_PREQ_ID(x) u32_field_get(x, 3, 0) #define PREQ_IE_ORIG_ADDR(x) (x + 7) -#define PREQ_IE_ORIG_SN(x) u32_field_get(x, 13, 0); -#define PREQ_IE_LIFETIME(x) u32_field_get(x, 17, AE_F_SET(x)); -#define PREQ_IE_METRIC(x) u32_field_get(x, 21, AE_F_SET(x)); +#define PREQ_IE_ORIG_SN(x) u32_field_get(x, 13, 0) +#define PREQ_IE_LIFETIME(x) u32_field_get(x, 17, AE_F_SET(x)) +#define PREQ_IE_METRIC(x) u32_field_get(x, 21, AE_F_SET(x)) #define PREQ_IE_TARGET_F(x) (*(AE_F_SET(x) ? x + 32 : x + 26)) #define PREQ_IE_TARGET_ADDR(x) (AE_F_SET(x) ? x + 33 : x + 27) -#define PREQ_IE_TARGET_SN(x) u32_field_get(x, 33, AE_F_SET(x)); +#define PREQ_IE_TARGET_SN(x) u32_field_get(x, 33, AE_F_SET(x)) #define PREP_IE_FLAGS(x) PREQ_IE_FLAGS(x) #define PREP_IE_HOPCOUNT(x) PREQ_IE_HOPCOUNT(x) #define PREP_IE_TTL(x) PREQ_IE_TTL(x) -#define PREP_IE_ORIG_ADDR(x) (x + 3) -#define PREP_IE_ORIG_SN(x) u32_field_get(x, 9, 0); -#define PREP_IE_LIFETIME(x) u32_field_get(x, 13, AE_F_SET(x)); -#define PREP_IE_METRIC(x) u32_field_get(x, 17, AE_F_SET(x)); -#define PREP_IE_TARGET_ADDR(x) (AE_F_SET(x) ? x + 27 : x + 21) -#define PREP_IE_TARGET_SN(x) u32_field_get(x, 27, AE_F_SET(x)); +#define PREP_IE_ORIG_ADDR(x) (AE_F_SET(x) ? x + 27 : x + 21) +#define PREP_IE_ORIG_SN(x) u32_field_get(x, 27, AE_F_SET(x)) +#define PREP_IE_LIFETIME(x) u32_field_get(x, 13, AE_F_SET(x)) +#define PREP_IE_METRIC(x) u32_field_get(x, 17, AE_F_SET(x)) +#define PREP_IE_TARGET_ADDR(x) (x + 3) +#define PREP_IE_TARGET_SN(x) u32_field_get(x, 9, 0) #define PERR_IE_TTL(x) (*(x)) #define PERR_IE_TARGET_FLAGS(x) (*(x + 2)) #define PERR_IE_TARGET_ADDR(x) (x + 3) -#define PERR_IE_TARGET_SN(x) u32_field_get(x, 9, 0); -#define PERR_IE_TARGET_RCODE(x) u16_field_get(x, 13, 0); +#define PERR_IE_TARGET_SN(x) u32_field_get(x, 9, 0) +#define PERR_IE_TARGET_RCODE(x) u16_field_get(x, 13, 0) #define MSEC_TO_TU(x) (x*1000/1024) #define SN_GT(x, y) ((long) (y) - (long) (x) < 0) @@ -132,24 +134,25 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags, memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); /* BSSID == SA */ memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); - mgmt->u.action.category = WLAN_CATEGORY_MESH_PATH_SEL; - mgmt->u.action.u.mesh_action.action_code = MESH_PATH_SEL_ACTION; + mgmt->u.action.category = WLAN_CATEGORY_MESH_ACTION; + mgmt->u.action.u.mesh_action.action_code = + WLAN_MESH_ACTION_HWMP_PATH_SELECTION; switch (action) { case MPATH_PREQ: - mhwmp_dbg("sending PREQ to %pM\n", target); + mhwmp_dbg("sending PREQ to %pM", target); ie_len = 37; pos = skb_put(skb, 2 + ie_len); *pos++ = WLAN_EID_PREQ; break; case MPATH_PREP: - mhwmp_dbg("sending PREP to %pM\n", target); + mhwmp_dbg("sending PREP to %pM", target); ie_len = 31; pos = skb_put(skb, 2 + ie_len); *pos++ = WLAN_EID_PREP; break; case MPATH_RANN: - mhwmp_dbg("sending RANN from %pM\n", orig_addr); + mhwmp_dbg("sending RANN from %pM", orig_addr); ie_len = sizeof(struct ieee80211_rann_ie); pos = skb_put(skb, 2 + ie_len); *pos++ = WLAN_EID_RANN; @@ -163,35 +166,63 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags, *pos++ = flags; *pos++ = hop_count; *pos++ = ttl; - if (action == MPATH_PREQ) { - memcpy(pos, &preq_id, 4); + if (action == MPATH_PREP) { + memcpy(pos, target, ETH_ALEN); + pos += ETH_ALEN; + memcpy(pos, &target_sn, 4); pos += 4; - } - memcpy(pos, orig_addr, ETH_ALEN); - pos += ETH_ALEN; - memcpy(pos, &orig_sn, 4); - pos += 4; - if (action != MPATH_RANN) { - memcpy(pos, &lifetime, 4); + } else { + if (action == MPATH_PREQ) { + memcpy(pos, &preq_id, 4); + pos += 4; + } + memcpy(pos, orig_addr, ETH_ALEN); + pos += ETH_ALEN; + memcpy(pos, &orig_sn, 4); pos += 4; } + memcpy(pos, &lifetime, 4); /* interval for RANN */ + pos += 4; memcpy(pos, &metric, 4); pos += 4; if (action == MPATH_PREQ) { - /* destination count */ - *pos++ = 1; + *pos++ = 1; /* destination count */ *pos++ = target_flags; - } - if (action != MPATH_RANN) { memcpy(pos, target, ETH_ALEN); pos += ETH_ALEN; memcpy(pos, &target_sn, 4); + pos += 4; + } else if (action == MPATH_PREP) { + memcpy(pos, orig_addr, ETH_ALEN); + pos += ETH_ALEN; + memcpy(pos, &orig_sn, 4); + pos += 4; } ieee80211_tx_skb(sdata, skb); return 0; } + +/* Headroom is not adjusted. Caller should ensure that skb has sufficient + * headroom in case the frame is encrypted. */ +static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + skb_set_mac_header(skb, 0); + skb_set_network_header(skb, 0); + skb_set_transport_header(skb, 0); + + /* Send all internal mgmt frames on VO. Accordingly set TID to 7. */ + skb_set_queue_mapping(skb, IEEE80211_AC_VO); + skb->priority = 7; + + info->control.vif = &sdata->vif; + ieee80211_set_qos_hdr(sdata, skb); +} + /** * mesh_send_path error - Sends a PERR mesh management frame * @@ -199,6 +230,10 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags, * @target_sn: SN of the broken destination * @target_rcode: reason code for this PERR * @ra: node this frame is addressed to + * + * Note: This function may be called with driver locks taken that the driver + * also acquires in the TX path. To avoid a deadlock we don't transmit the + * frame directly but add it to the pending queue instead. */ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, __le16 target_rcode, const u8 *ra, @@ -212,7 +247,7 @@ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, if (!skb) return -1; - skb_reserve(skb, local->hw.extra_tx_headroom); + skb_reserve(skb, local->tx_headroom + local->hw.extra_tx_headroom); /* 25 is the size of the common mgmt part (24) plus the size of the * common action part (1) */ @@ -224,9 +259,11 @@ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, memcpy(mgmt->da, ra, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); - /* BSSID is left zeroed, wildcard value */ - mgmt->u.action.category = WLAN_CATEGORY_MESH_PATH_SEL; - mgmt->u.action.u.mesh_action.action_code = MESH_PATH_SEL_ACTION; + /* BSSID == SA */ + memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); + mgmt->u.action.category = WLAN_CATEGORY_MESH_ACTION; + mgmt->u.action.u.mesh_action.action_code = + WLAN_MESH_ACTION_HWMP_PATH_SELECTION; ie_len = 15; pos = skb_put(skb, 2 + ie_len); *pos++ = WLAN_EID_PERR; @@ -251,7 +288,9 @@ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, pos += 4; memcpy(pos, &target_rcode, 2); - ieee80211_tx_skb(sdata, skb); + /* see note in function header */ + prepare_frame_for_deferred_tx(sdata, skb); + ieee80211_add_pending_skb(local, skb); return 0; } @@ -449,7 +488,6 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, if (fresh_info) { mesh_path_assign_nexthop(mpath, sta); - mpath->flags &= ~MESH_PATH_SN_VALID; mpath->metric = last_hop_metric; mpath->exp_time = time_after(mpath->exp_time, exp_time) ? mpath->exp_time : exp_time; @@ -484,10 +522,10 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, orig_sn = PREQ_IE_ORIG_SN(preq_elem); target_flags = PREQ_IE_TARGET_F(preq_elem); - mhwmp_dbg("received PREQ from %pM\n", orig_addr); + mhwmp_dbg("received PREQ from %pM", orig_addr); if (memcmp(target_addr, sdata->vif.addr, ETH_ALEN) == 0) { - mhwmp_dbg("PREQ is for us\n"); + mhwmp_dbg("PREQ is for us"); forward = false; reply = true; metric = 0; @@ -523,7 +561,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, lifetime = PREQ_IE_LIFETIME(preq_elem); ttl = ifmsh->mshcfg.element_ttl; if (ttl != 0) { - mhwmp_dbg("replying to the PREQ\n"); + mhwmp_dbg("replying to the PREQ"); mesh_path_sel_frame_tx(MPATH_PREP, 0, target_addr, cpu_to_le32(target_sn), 0, orig_addr, cpu_to_le32(orig_sn), mgmt->sa, 0, ttl, @@ -543,7 +581,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, ifmsh->mshstats.dropped_frames_ttl++; return; } - mhwmp_dbg("forwarding the PREQ from %pM\n", orig_addr); + mhwmp_dbg("forwarding the PREQ from %pM", orig_addr); --ttl; flags = PREQ_IE_FLAGS(preq_elem); preq_id = PREQ_IE_PREQ_ID(preq_elem); @@ -578,7 +616,7 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, u8 next_hop[ETH_ALEN]; u32 target_sn, orig_sn, lifetime; - mhwmp_dbg("received PREP from %pM\n", PREP_IE_ORIG_ADDR(prep_elem)); + mhwmp_dbg("received PREP from %pM", PREP_IE_ORIG_ADDR(prep_elem)); /* Note that we divert from the draft nomenclature and denominate * destination to what the draft refers to as origininator. So in this @@ -684,6 +722,8 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, u8 ttl, flags, hopcount; u8 *orig_addr; u32 orig_sn, metric; + u32 interval = ifmsh->mshcfg.dot11MeshHWMPRannInterval; + bool root_is_gate; ttl = rann->rann_ttl; if (ttl <= 1) { @@ -692,12 +732,19 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, } ttl--; flags = rann->rann_flags; + root_is_gate = !!(flags & RANN_FLAG_IS_GATE); orig_addr = rann->rann_addr; orig_sn = rann->rann_seq; hopcount = rann->rann_hopcount; hopcount++; metric = rann->rann_metric; - mhwmp_dbg("received RANN from %pM\n", orig_addr); + + /* Ignore our own RANNs */ + if (memcmp(orig_addr, sdata->vif.addr, ETH_ALEN) == 0) + return; + + mhwmp_dbg("received RANN from %pM (is_gate=%d)", orig_addr, + root_is_gate); rcu_read_lock(); mpath = mesh_path_lookup(orig_addr, sdata); @@ -709,18 +756,28 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, sdata->u.mesh.mshstats.dropped_frames_no_route++; return; } - mesh_queue_preq(mpath, - PREQ_Q_F_START | PREQ_Q_F_REFRESH); } + + if ((!(mpath->flags & (MESH_PATH_ACTIVE | MESH_PATH_RESOLVING)) || + time_after(jiffies, mpath->exp_time - 1*HZ)) && + !(mpath->flags & MESH_PATH_FIXED)) { + mhwmp_dbg("%s time to refresh root mpath %pM", sdata->name, + orig_addr); + mesh_queue_preq(mpath, PREQ_Q_F_START | PREQ_Q_F_REFRESH); + } + if (mpath->sn < orig_sn) { mesh_path_sel_frame_tx(MPATH_RANN, flags, orig_addr, cpu_to_le32(orig_sn), 0, NULL, 0, broadcast_addr, - hopcount, ttl, 0, + hopcount, ttl, cpu_to_le32(interval), cpu_to_le32(metric + mpath->metric), 0, sdata); mpath->sn = orig_sn; } + if (root_is_gate) + mesh_path_add_gate(mpath); + rcu_read_unlock(); } @@ -732,11 +789,20 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, struct ieee802_11_elems elems; size_t baselen; u32 last_hop_metric; + struct sta_info *sta; /* need action_code */ if (len < IEEE80211_MIN_ACTION_SIZE + 1) return; + rcu_read_lock(); + sta = sta_info_get(sdata, mgmt->sa); + if (!sta || sta->plink_state != NL80211_PLINK_ESTAB) { + rcu_read_unlock(); + return; + } + rcu_read_unlock(); + baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt; ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable, len - baselen, &elems); @@ -788,16 +854,16 @@ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags) preq_node = kmalloc(sizeof(struct mesh_preq_queue), GFP_ATOMIC); if (!preq_node) { - mhwmp_dbg("could not allocate PREQ node\n"); + mhwmp_dbg("could not allocate PREQ node"); return; } - spin_lock(&ifmsh->mesh_preq_queue_lock); + spin_lock_bh(&ifmsh->mesh_preq_queue_lock); if (ifmsh->preq_queue_len == MAX_PREQ_QUEUE_LEN) { - spin_unlock(&ifmsh->mesh_preq_queue_lock); + spin_unlock_bh(&ifmsh->mesh_preq_queue_lock); kfree(preq_node); if (printk_ratelimit()) - mhwmp_dbg("PREQ node queue full\n"); + mhwmp_dbg("PREQ node queue full"); return; } @@ -806,7 +872,7 @@ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags) list_add_tail(&preq_node->list, &ifmsh->preq_queue.list); ++ifmsh->preq_queue_len; - spin_unlock(&ifmsh->mesh_preq_queue_lock); + spin_unlock_bh(&ifmsh->mesh_preq_queue_lock); if (time_after(jiffies, ifmsh->last_preq + min_preq_int_jiff(sdata))) ieee80211_queue_work(&sdata->local->hw, &sdata->work); @@ -982,35 +1048,46 @@ void mesh_path_timer(unsigned long data) { struct mesh_path *mpath = (void *) data; struct ieee80211_sub_if_data *sdata = mpath->sdata; + int ret; if (sdata->local->quiescing) return; spin_lock_bh(&mpath->state_lock); if (mpath->flags & MESH_PATH_RESOLVED || - (!(mpath->flags & MESH_PATH_RESOLVING))) + (!(mpath->flags & MESH_PATH_RESOLVING))) { mpath->flags &= ~(MESH_PATH_RESOLVING | MESH_PATH_RESOLVED); - else if (mpath->discovery_retries < max_preq_retries(sdata)) { + spin_unlock_bh(&mpath->state_lock); + } else if (mpath->discovery_retries < max_preq_retries(sdata)) { ++mpath->discovery_retries; mpath->discovery_timeout *= 2; + spin_unlock_bh(&mpath->state_lock); mesh_queue_preq(mpath, 0); } else { mpath->flags = 0; mpath->exp_time = jiffies; - mesh_path_flush_pending(mpath); + spin_unlock_bh(&mpath->state_lock); + if (!mpath->is_gate && mesh_gate_num(sdata) > 0) { + ret = mesh_path_send_to_gates(mpath); + if (ret) + mhwmp_dbg("no gate was reachable"); + } else + mesh_path_flush_pending(mpath); } - - spin_unlock_bh(&mpath->state_lock); } void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + u32 interval = ifmsh->mshcfg.dot11MeshHWMPRannInterval; + u8 flags; - mesh_path_sel_frame_tx(MPATH_RANN, 0, sdata->vif.addr, + flags = (ifmsh->mshcfg.dot11MeshGateAnnouncementProtocol) + ? RANN_FLAG_IS_GATE : 0; + mesh_path_sel_frame_tx(MPATH_RANN, flags, sdata->vif.addr, cpu_to_le32(++ifmsh->sn), 0, NULL, 0, broadcast_addr, 0, sdata->u.mesh.mshcfg.element_ttl, - 0, 0, 0, sdata); + cpu_to_le32(interval), 0, 0, sdata); } diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 0d2faac..7f54c50 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -14,9 +14,16 @@ #include <linux/spinlock.h> #include <linux/string.h> #include <net/mac80211.h> +#include "wme.h" #include "ieee80211_i.h" #include "mesh.h" +#ifdef CONFIG_MAC80211_VERBOSE_MPATH_DEBUG +#define mpath_dbg(fmt, args...) printk(KERN_DEBUG fmt, ##args) +#else +#define mpath_dbg(fmt, args...) do { (void)(0); } while (0) +#endif + /* There will be initially 2^INIT_PATHS_SIZE_ORDER buckets */ #define INIT_PATHS_SIZE_ORDER 2 @@ -42,8 +49,10 @@ static struct mesh_table __rcu *mpp_paths; /* Store paths for MPP&MAP */ int mesh_paths_generation; /* This lock will have the grow table function as writer and add / delete nodes - * as readers. When reading the table (i.e. doing lookups) we are well protected - * by RCU + * as readers. RCU provides sufficient protection only when reading the table + * (i.e. doing lookups). Adding or adding or removing nodes requires we take + * the read lock or we risk operating on an old table. The write lock is only + * needed when modifying the number of buckets a table. */ static DEFINE_RWLOCK(pathtbl_resize_lock); @@ -60,6 +69,8 @@ static inline struct mesh_table *resize_dereference_mpp_paths(void) lockdep_is_held(&pathtbl_resize_lock)); } +static int mesh_gate_add(struct mesh_table *tbl, struct mesh_path *mpath); + /* * CAREFUL -- "tbl" must not be an expression, * in particular not an rcu_dereference(), since @@ -103,6 +114,7 @@ static struct mesh_table *mesh_table_alloc(int size_order) sizeof(newtbl->hash_rnd)); for (i = 0; i <= newtbl->hash_mask; i++) spin_lock_init(&newtbl->hashwlock[i]); + spin_lock_init(&newtbl->gates_lock); return newtbl; } @@ -118,6 +130,7 @@ static void mesh_table_free(struct mesh_table *tbl, bool free_leafs) { struct hlist_head *mesh_hash; struct hlist_node *p, *q; + struct mpath_node *gate; int i; mesh_hash = tbl->hash_buckets; @@ -129,6 +142,17 @@ static void mesh_table_free(struct mesh_table *tbl, bool free_leafs) } spin_unlock_bh(&tbl->hashwlock[i]); } + if (free_leafs) { + spin_lock_bh(&tbl->gates_lock); + hlist_for_each_entry_safe(gate, p, q, + tbl->known_gates, list) { + hlist_del(&gate->list); + kfree(gate); + } + kfree(tbl->known_gates); + spin_unlock_bh(&tbl->gates_lock); + } + __mesh_table_free(tbl); } @@ -146,6 +170,7 @@ static int mesh_table_grow(struct mesh_table *oldtbl, newtbl->free_node = oldtbl->free_node; newtbl->mean_chain_len = oldtbl->mean_chain_len; newtbl->copy_node = oldtbl->copy_node; + newtbl->known_gates = oldtbl->known_gates; atomic_set(&newtbl->entries, atomic_read(&oldtbl->entries)); oldhash = oldtbl->hash_buckets; @@ -188,6 +213,7 @@ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta) struct ieee80211_hdr *hdr; struct sk_buff_head tmpq; unsigned long flags; + struct ieee80211_sub_if_data *sdata = mpath->sdata; rcu_assign_pointer(mpath->next_hop, sta); @@ -198,6 +224,8 @@ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta) while ((skb = __skb_dequeue(&mpath->frame_queue)) != NULL) { hdr = (struct ieee80211_hdr *) skb->data; memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN); + skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, skb)); + ieee80211_set_qos_hdr(sdata, skb); __skb_queue_tail(&tmpq, skb); } @@ -205,62 +233,128 @@ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta) spin_unlock_irqrestore(&mpath->frame_queue.lock, flags); } +static void prepare_for_gate(struct sk_buff *skb, char *dst_addr, + struct mesh_path *gate_mpath) +{ + struct ieee80211_hdr *hdr; + struct ieee80211s_hdr *mshdr; + int mesh_hdrlen, hdrlen; + char *next_hop; + + hdr = (struct ieee80211_hdr *) skb->data; + hdrlen = ieee80211_hdrlen(hdr->frame_control); + mshdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); + + if (!(mshdr->flags & MESH_FLAGS_AE)) { + /* size of the fixed part of the mesh header */ + mesh_hdrlen = 6; + + /* make room for the two extended addresses */ + skb_push(skb, 2 * ETH_ALEN); + memmove(skb->data, hdr, hdrlen + mesh_hdrlen); + + hdr = (struct ieee80211_hdr *) skb->data; + + /* we preserve the previous mesh header and only add + * the new addreses */ + mshdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); + mshdr->flags = MESH_FLAGS_AE_A5_A6; + memcpy(mshdr->eaddr1, hdr->addr3, ETH_ALEN); + memcpy(mshdr->eaddr2, hdr->addr4, ETH_ALEN); + } + + /* update next hop */ + hdr = (struct ieee80211_hdr *) skb->data; + rcu_read_lock(); + next_hop = rcu_dereference(gate_mpath->next_hop)->sta.addr; + memcpy(hdr->addr1, next_hop, ETH_ALEN); + rcu_read_unlock(); + memcpy(hdr->addr3, dst_addr, ETH_ALEN); +} /** - * mesh_path_lookup - look up a path in the mesh path table - * @dst: hardware address (ETH_ALEN length) of destination - * @sdata: local subif * - * Returns: pointer to the mesh path structure, or NULL if not found + * mesh_path_move_to_queue - Move or copy frames from one mpath queue to another * - * Locking: must be called within a read rcu section. + * This function is used to transfer or copy frames from an unresolved mpath to + * a gate mpath. The function also adds the Address Extension field and + * updates the next hop. + * + * If a frame already has an Address Extension field, only the next hop and + * destination addresses are updated. + * + * The gate mpath must be an active mpath with a valid mpath->next_hop. + * + * @mpath: An active mpath the frames will be sent to (i.e. the gate) + * @from_mpath: The failed mpath + * @copy: When true, copy all the frames to the new mpath queue. When false, + * move them. */ -struct mesh_path *mesh_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) +static void mesh_path_move_to_queue(struct mesh_path *gate_mpath, + struct mesh_path *from_mpath, + bool copy) { - struct mesh_path *mpath; - struct hlist_node *n; - struct hlist_head *bucket; - struct mesh_table *tbl; - struct mpath_node *node; + struct sk_buff *skb, *cp_skb = NULL; + struct sk_buff_head gateq, failq; + unsigned long flags; + int num_skbs; - tbl = rcu_dereference(mesh_paths); + BUG_ON(gate_mpath == from_mpath); + BUG_ON(!gate_mpath->next_hop); - bucket = &tbl->hash_buckets[mesh_table_hash(dst, sdata, tbl)]; - hlist_for_each_entry_rcu(node, n, bucket, list) { - mpath = node->mpath; - if (mpath->sdata == sdata && - memcmp(dst, mpath->dst, ETH_ALEN) == 0) { - if (MPATH_EXPIRED(mpath)) { - spin_lock_bh(&mpath->state_lock); - if (MPATH_EXPIRED(mpath)) - mpath->flags &= ~MESH_PATH_ACTIVE; - spin_unlock_bh(&mpath->state_lock); - } - return mpath; + __skb_queue_head_init(&gateq); + __skb_queue_head_init(&failq); + + spin_lock_irqsave(&from_mpath->frame_queue.lock, flags); + skb_queue_splice_init(&from_mpath->frame_queue, &failq); + spin_unlock_irqrestore(&from_mpath->frame_queue.lock, flags); + + num_skbs = skb_queue_len(&failq); + + while (num_skbs--) { + skb = __skb_dequeue(&failq); + if (copy) { + cp_skb = skb_copy(skb, GFP_ATOMIC); + if (cp_skb) + __skb_queue_tail(&failq, cp_skb); } + + prepare_for_gate(skb, gate_mpath->dst, gate_mpath); + __skb_queue_tail(&gateq, skb); } - return NULL; + + spin_lock_irqsave(&gate_mpath->frame_queue.lock, flags); + skb_queue_splice(&gateq, &gate_mpath->frame_queue); + mpath_dbg("Mpath queue for gate %pM has %d frames\n", + gate_mpath->dst, + skb_queue_len(&gate_mpath->frame_queue)); + spin_unlock_irqrestore(&gate_mpath->frame_queue.lock, flags); + + if (!copy) + return; + + spin_lock_irqsave(&from_mpath->frame_queue.lock, flags); + skb_queue_splice(&failq, &from_mpath->frame_queue); + spin_unlock_irqrestore(&from_mpath->frame_queue.lock, flags); } -struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) + +static struct mesh_path *path_lookup(struct mesh_table *tbl, u8 *dst, + struct ieee80211_sub_if_data *sdata) { struct mesh_path *mpath; struct hlist_node *n; struct hlist_head *bucket; - struct mesh_table *tbl; struct mpath_node *node; - tbl = rcu_dereference(mpp_paths); - bucket = &tbl->hash_buckets[mesh_table_hash(dst, sdata, tbl)]; hlist_for_each_entry_rcu(node, n, bucket, list) { mpath = node->mpath; if (mpath->sdata == sdata && - memcmp(dst, mpath->dst, ETH_ALEN) == 0) { + memcmp(dst, mpath->dst, ETH_ALEN) == 0) { if (MPATH_EXPIRED(mpath)) { spin_lock_bh(&mpath->state_lock); - if (MPATH_EXPIRED(mpath)) - mpath->flags &= ~MESH_PATH_ACTIVE; + mpath->flags &= ~MESH_PATH_ACTIVE; spin_unlock_bh(&mpath->state_lock); } return mpath; @@ -269,6 +363,25 @@ struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) return NULL; } +/** + * mesh_path_lookup - look up a path in the mesh path table + * @dst: hardware address (ETH_ALEN length) of destination + * @sdata: local subif + * + * Returns: pointer to the mesh path structure, or NULL if not found + * + * Locking: must be called within a read rcu section. + */ +struct mesh_path *mesh_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) +{ + return path_lookup(rcu_dereference(mesh_paths), dst, sdata); +} + +struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) +{ + return path_lookup(rcu_dereference(mpp_paths), dst, sdata); +} + /** * mesh_path_lookup_by_idx - look up a path in the mesh path table by its index @@ -293,8 +406,7 @@ struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data if (j++ == idx) { if (MPATH_EXPIRED(node->mpath)) { spin_lock_bh(&node->mpath->state_lock); - if (MPATH_EXPIRED(node->mpath)) - node->mpath->flags &= ~MESH_PATH_ACTIVE; + node->mpath->flags &= ~MESH_PATH_ACTIVE; spin_unlock_bh(&node->mpath->state_lock); } return node->mpath; @@ -304,6 +416,109 @@ struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data return NULL; } +static void mesh_gate_node_reclaim(struct rcu_head *rp) +{ + struct mpath_node *node = container_of(rp, struct mpath_node, rcu); + kfree(node); +} + +/** + * mesh_gate_add - mark mpath as path to a mesh gate and add to known_gates + * @mesh_tbl: table which contains known_gates list + * @mpath: mpath to known mesh gate + * + * Returns: 0 on success + * + */ +static int mesh_gate_add(struct mesh_table *tbl, struct mesh_path *mpath) +{ + struct mpath_node *gate, *new_gate; + struct hlist_node *n; + int err; + + rcu_read_lock(); + tbl = rcu_dereference(tbl); + + hlist_for_each_entry_rcu(gate, n, tbl->known_gates, list) + if (gate->mpath == mpath) { + err = -EEXIST; + goto err_rcu; + } + + new_gate = kzalloc(sizeof(struct mpath_node), GFP_ATOMIC); + if (!new_gate) { + err = -ENOMEM; + goto err_rcu; + } + + mpath->is_gate = true; + mpath->sdata->u.mesh.num_gates++; + new_gate->mpath = mpath; + spin_lock_bh(&tbl->gates_lock); + hlist_add_head_rcu(&new_gate->list, tbl->known_gates); + spin_unlock_bh(&tbl->gates_lock); + rcu_read_unlock(); + mpath_dbg("Mesh path (%s): Recorded new gate: %pM. %d known gates\n", + mpath->sdata->name, mpath->dst, + mpath->sdata->u.mesh.num_gates); + return 0; +err_rcu: + rcu_read_unlock(); + return err; +} + +/** + * mesh_gate_del - remove a mesh gate from the list of known gates + * @tbl: table which holds our list of known gates + * @mpath: gate mpath + * + * Returns: 0 on success + * + * Locking: must be called inside rcu_read_lock() section + */ +static int mesh_gate_del(struct mesh_table *tbl, struct mesh_path *mpath) +{ + struct mpath_node *gate; + struct hlist_node *p, *q; + + tbl = rcu_dereference(tbl); + + hlist_for_each_entry_safe(gate, p, q, tbl->known_gates, list) + if (gate->mpath == mpath) { + spin_lock_bh(&tbl->gates_lock); + hlist_del_rcu(&gate->list); + call_rcu(&gate->rcu, mesh_gate_node_reclaim); + spin_unlock_bh(&tbl->gates_lock); + mpath->sdata->u.mesh.num_gates--; + mpath->is_gate = false; + mpath_dbg("Mesh path (%s): Deleted gate: %pM. " + "%d known gates\n", mpath->sdata->name, + mpath->dst, mpath->sdata->u.mesh.num_gates); + break; + } + + return 0; +} + +/** + * + * mesh_path_add_gate - add the given mpath to a mesh gate to our path table + * @mpath: gate path to add to table + */ +int mesh_path_add_gate(struct mesh_path *mpath) +{ + return mesh_gate_add(mesh_paths, mpath); +} + +/** + * mesh_gate_num - number of gates known to this interface + * @sdata: subif data + */ +int mesh_gate_num(struct ieee80211_sub_if_data *sdata) +{ + return sdata->u.mesh.num_gates; +} + /** * mesh_path_add - allocate and add a new path to the mesh path table * @addr: destination address of the path (ETH_ALEN length) @@ -481,6 +696,7 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) new_mpath->flags = 0; skb_queue_head_init(&new_mpath->frame_queue); new_node->mpath = new_mpath; + init_timer(&new_mpath->timer); new_mpath->exp_time = jiffies; spin_lock_init(&new_mpath->state_lock); @@ -539,28 +755,53 @@ void mesh_plink_broken(struct sta_info *sta) struct hlist_node *p; struct ieee80211_sub_if_data *sdata = sta->sdata; int i; + __le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_DEST_UNREACHABLE); rcu_read_lock(); tbl = rcu_dereference(mesh_paths); for_each_mesh_entry(tbl, p, node, i) { mpath = node->mpath; - spin_lock_bh(&mpath->state_lock); if (rcu_dereference(mpath->next_hop) == sta && mpath->flags & MESH_PATH_ACTIVE && !(mpath->flags & MESH_PATH_FIXED)) { + spin_lock_bh(&mpath->state_lock); mpath->flags &= ~MESH_PATH_ACTIVE; ++mpath->sn; spin_unlock_bh(&mpath->state_lock); mesh_path_error_tx(sdata->u.mesh.mshcfg.element_ttl, mpath->dst, cpu_to_le32(mpath->sn), - cpu_to_le16(PERR_RCODE_DEST_UNREACH), - bcast, sdata); - } else - spin_unlock_bh(&mpath->state_lock); + reason, bcast, sdata); + } } rcu_read_unlock(); } +static void mesh_path_node_reclaim(struct rcu_head *rp) +{ + struct mpath_node *node = container_of(rp, struct mpath_node, rcu); + struct ieee80211_sub_if_data *sdata = node->mpath->sdata; + + del_timer_sync(&node->mpath->timer); + atomic_dec(&sdata->u.mesh.mpaths); + kfree(node->mpath); + kfree(node); +} + +/* needs to be called with the corresponding hashwlock taken */ +static void __mesh_path_del(struct mesh_table *tbl, struct mpath_node *node) +{ + struct mesh_path *mpath; + mpath = node->mpath; + spin_lock(&mpath->state_lock); + mpath->flags |= MESH_PATH_RESOLVING; + if (mpath->is_gate) + mesh_gate_del(tbl, mpath); + hlist_del_rcu(&node->list); + call_rcu(&node->rcu, mesh_path_node_reclaim); + spin_unlock(&mpath->state_lock); + atomic_dec(&tbl->entries); +} + /** * mesh_path_flush_by_nexthop - Deletes mesh paths if their next hop matches * @@ -581,42 +822,59 @@ void mesh_path_flush_by_nexthop(struct sta_info *sta) int i; rcu_read_lock(); - tbl = rcu_dereference(mesh_paths); + read_lock_bh(&pathtbl_resize_lock); + tbl = resize_dereference_mesh_paths(); for_each_mesh_entry(tbl, p, node, i) { mpath = node->mpath; - if (rcu_dereference(mpath->next_hop) == sta) - mesh_path_del(mpath->dst, mpath->sdata); + if (rcu_dereference(mpath->next_hop) == sta) { + spin_lock_bh(&tbl->hashwlock[i]); + __mesh_path_del(tbl, node); + spin_unlock_bh(&tbl->hashwlock[i]); + } } + read_unlock_bh(&pathtbl_resize_lock); rcu_read_unlock(); } -void mesh_path_flush(struct ieee80211_sub_if_data *sdata) +static void table_flush_by_iface(struct mesh_table *tbl, + struct ieee80211_sub_if_data *sdata) { - struct mesh_table *tbl; struct mesh_path *mpath; struct mpath_node *node; struct hlist_node *p; int i; - rcu_read_lock(); - tbl = rcu_dereference(mesh_paths); + WARN_ON(!rcu_read_lock_held()); for_each_mesh_entry(tbl, p, node, i) { mpath = node->mpath; - if (mpath->sdata == sdata) - mesh_path_del(mpath->dst, mpath->sdata); + if (mpath->sdata != sdata) + continue; + spin_lock_bh(&tbl->hashwlock[i]); + __mesh_path_del(tbl, node); + spin_unlock_bh(&tbl->hashwlock[i]); } - rcu_read_unlock(); } -static void mesh_path_node_reclaim(struct rcu_head *rp) +/** + * mesh_path_flush_by_iface - Deletes all mesh paths associated with a given iface + * + * This function deletes both mesh paths as well as mesh portal paths. + * + * @sdata - interface data to match + * + */ +void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata) { - struct mpath_node *node = container_of(rp, struct mpath_node, rcu); - struct ieee80211_sub_if_data *sdata = node->mpath->sdata; + struct mesh_table *tbl; - del_timer_sync(&node->mpath->timer); - atomic_dec(&sdata->u.mesh.mpaths); - kfree(node->mpath); - kfree(node); + rcu_read_lock(); + read_lock_bh(&pathtbl_resize_lock); + tbl = resize_dereference_mesh_paths(); + table_flush_by_iface(tbl, sdata); + tbl = resize_dereference_mpp_paths(); + table_flush_by_iface(tbl, sdata); + read_unlock_bh(&pathtbl_resize_lock); + rcu_read_unlock(); } /** @@ -647,12 +905,7 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) mpath = node->mpath; if (mpath->sdata == sdata && memcmp(addr, mpath->dst, ETH_ALEN) == 0) { - spin_lock_bh(&mpath->state_lock); - mpath->flags |= MESH_PATH_RESOLVING; - hlist_del_rcu(&node->list); - call_rcu(&node->rcu, mesh_path_node_reclaim); - atomic_dec(&tbl->entries); - spin_unlock_bh(&mpath->state_lock); + __mesh_path_del(tbl, node); goto enddel; } } @@ -681,6 +934,58 @@ void mesh_path_tx_pending(struct mesh_path *mpath) } /** + * mesh_path_send_to_gates - sends pending frames to all known mesh gates + * + * @mpath: mesh path whose queue will be emptied + * + * If there is only one gate, the frames are transferred from the failed mpath + * queue to that gate's queue. If there are more than one gates, the frames + * are copied from each gate to the next. After frames are copied, the + * mpath queues are emptied onto the transmission queue. + */ +int mesh_path_send_to_gates(struct mesh_path *mpath) +{ + struct ieee80211_sub_if_data *sdata = mpath->sdata; + struct hlist_node *n; + struct mesh_table *tbl; + struct mesh_path *from_mpath = mpath; + struct mpath_node *gate = NULL; + bool copy = false; + struct hlist_head *known_gates; + + rcu_read_lock(); + tbl = rcu_dereference(mesh_paths); + known_gates = tbl->known_gates; + rcu_read_unlock(); + + if (!known_gates) + return -EHOSTUNREACH; + + hlist_for_each_entry_rcu(gate, n, known_gates, list) { + if (gate->mpath->sdata != sdata) + continue; + + if (gate->mpath->flags & MESH_PATH_ACTIVE) { + mpath_dbg("Forwarding to %pM\n", gate->mpath->dst); + mesh_path_move_to_queue(gate->mpath, from_mpath, copy); + from_mpath = gate->mpath; + copy = true; + } else { + mpath_dbg("Not forwarding %p\n", gate->mpath); + mpath_dbg("flags %x\n", gate->mpath->flags); + } + } + + hlist_for_each_entry_rcu(gate, n, known_gates, list) + if (gate->mpath->sdata == sdata) { + mpath_dbg("Sending to %pM\n", gate->mpath->dst); + mesh_path_tx_pending(gate->mpath); + } + + return (from_mpath == mpath) ? -EHOSTUNREACH : 0; +} + +/** * mesh_path_discard_frame - discard a frame whose path could not be resolved * * @skb: frame to discard @@ -699,18 +1004,23 @@ void mesh_path_discard_frame(struct sk_buff *skb, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct mesh_path *mpath; u32 sn = 0; + __le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_NOFORWARD); if (memcmp(hdr->addr4, sdata->vif.addr, ETH_ALEN) != 0) { u8 *ra, *da; da = hdr->addr3; ra = hdr->addr1; + rcu_read_lock(); mpath = mesh_path_lookup(da, sdata); - if (mpath) + if (mpath) { + spin_lock_bh(&mpath->state_lock); sn = ++mpath->sn; + spin_unlock_bh(&mpath->state_lock); + } + rcu_read_unlock(); mesh_path_error_tx(sdata->u.mesh.mshcfg.element_ttl, skb->data, - cpu_to_le32(sn), - cpu_to_le16(PERR_RCODE_NO_ROUTE), ra, sdata); + cpu_to_le32(sn), reason, ra, sdata); } kfree_skb(skb); @@ -728,8 +1038,7 @@ void mesh_path_flush_pending(struct mesh_path *mpath) { struct sk_buff *skb; - while ((skb = skb_dequeue(&mpath->frame_queue)) && - (mpath->flags & MESH_PATH_ACTIVE)) + while ((skb = skb_dequeue(&mpath->frame_queue)) != NULL) mesh_path_discard_frame(skb, mpath->sdata); } @@ -790,6 +1099,7 @@ static int mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl) int mesh_pathtbl_init(void) { struct mesh_table *tbl_path, *tbl_mpp; + int ret; tbl_path = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); if (!tbl_path) @@ -797,21 +1107,40 @@ int mesh_pathtbl_init(void) tbl_path->free_node = &mesh_path_node_free; tbl_path->copy_node = &mesh_path_node_copy; tbl_path->mean_chain_len = MEAN_CHAIN_LEN; + tbl_path->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC); + if (!tbl_path->known_gates) { + ret = -ENOMEM; + goto free_path; + } + INIT_HLIST_HEAD(tbl_path->known_gates); + tbl_mpp = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); if (!tbl_mpp) { - mesh_table_free(tbl_path, true); - return -ENOMEM; + ret = -ENOMEM; + goto free_path; } tbl_mpp->free_node = &mesh_path_node_free; tbl_mpp->copy_node = &mesh_path_node_copy; tbl_mpp->mean_chain_len = MEAN_CHAIN_LEN; + tbl_mpp->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC); + if (!tbl_mpp->known_gates) { + ret = -ENOMEM; + goto free_mpp; + } + INIT_HLIST_HEAD(tbl_mpp->known_gates); /* Need no locking since this is during init */ RCU_INIT_POINTER(mesh_paths, tbl_path); RCU_INIT_POINTER(mpp_paths, tbl_mpp); return 0; + +free_mpp: + mesh_table_free(tbl_mpp, true); +free_path: + mesh_table_free(tbl_path, true); + return ret; } void mesh_path_expire(struct ieee80211_sub_if_data *sdata) @@ -828,14 +1157,10 @@ void mesh_path_expire(struct ieee80211_sub_if_data *sdata) if (node->mpath->sdata != sdata) continue; mpath = node->mpath; - spin_lock_bh(&mpath->state_lock); if ((!(mpath->flags & MESH_PATH_RESOLVING)) && (!(mpath->flags & MESH_PATH_FIXED)) && - time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE)) { - spin_unlock_bh(&mpath->state_lock); + time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE)) mesh_path_del(mpath->dst, mpath->sdata); - } else - spin_unlock_bh(&mpath->state_lock); } rcu_read_unlock(); } @@ -843,6 +1168,6 @@ void mesh_path_expire(struct ieee80211_sub_if_data *sdata) void mesh_pathtbl_unregister(void) { /* no need for locking during exit path */ - mesh_table_free(rcu_dereference_raw(mesh_paths), true); - mesh_table_free(rcu_dereference_raw(mpp_paths), true); + mesh_table_free(rcu_dereference_protected(mesh_paths, 1), true); + mesh_table_free(rcu_dereference_protected(mpp_paths, 1), true); } diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index f4adc09..7e57f5d 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -19,35 +19,18 @@ #define mpl_dbg(fmt, args...) do { (void)(0); } while (0) #endif -#define PLINK_GET_LLID(p) (p + 4) -#define PLINK_GET_PLID(p) (p + 6) +#define PLINK_GET_LLID(p) (p + 2) +#define PLINK_GET_PLID(p) (p + 4) #define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \ jiffies + HZ * t / 1000)) -/* Peer link cancel reasons, all subject to ANA approval */ -#define MESH_LINK_CANCELLED 2 -#define MESH_MAX_NEIGHBORS 3 -#define MESH_CAPABILITY_POLICY_VIOLATION 4 -#define MESH_CLOSE_RCVD 5 -#define MESH_MAX_RETRIES 6 -#define MESH_CONFIRM_TIMEOUT 7 -#define MESH_SECURITY_ROLE_NEGOTIATION_DIFFERS 8 -#define MESH_SECURITY_AUTHENTICATION_IMPOSSIBLE 9 -#define MESH_SECURITY_FAILED_VERIFICATION 10 - #define dot11MeshMaxRetries(s) (s->u.mesh.mshcfg.dot11MeshMaxRetries) #define dot11MeshRetryTimeout(s) (s->u.mesh.mshcfg.dot11MeshRetryTimeout) #define dot11MeshConfirmTimeout(s) (s->u.mesh.mshcfg.dot11MeshConfirmTimeout) #define dot11MeshHoldingTimeout(s) (s->u.mesh.mshcfg.dot11MeshHoldingTimeout) #define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks) -enum plink_frame_type { - PLINK_OPEN = 1, - PLINK_CONFIRM, - PLINK_CLOSE -}; - enum plink_event { PLINK_UNDEFINED, OPN_ACPT, @@ -60,6 +43,10 @@ enum plink_event { CLS_IGNR }; +static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, + enum ieee80211_self_protected_actioncode action, + u8 *da, __le16 llid, __le16 plid, __le16 reason); + static inline void mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata) { @@ -105,7 +92,9 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, if (!sta) return NULL; - sta->flags = WLAN_STA_AUTHORIZED | WLAN_STA_AUTH; + set_sta_flag(sta, WLAN_STA_AUTH); + set_sta_flag(sta, WLAN_STA_AUTHORIZED); + set_sta_flag(sta, WLAN_STA_WME); sta->sta.supp_rates[local->hw.conf.channel->band] = rates; rate_control_rate_init(sta); @@ -150,6 +139,10 @@ void mesh_plink_deactivate(struct sta_info *sta) spin_lock_bh(&sta->lock); deactivated = __mesh_plink_deactivate(sta); + sta->reason = cpu_to_le16(WLAN_REASON_MESH_PEER_CANCELED); + mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, + sta->sta.addr, sta->llid, sta->plid, + sta->reason); spin_unlock_bh(&sta->lock); if (deactivated) @@ -157,16 +150,16 @@ void mesh_plink_deactivate(struct sta_info *sta) } static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, - enum plink_frame_type action, u8 *da, __le16 llid, __le16 plid, - __le16 reason) { + enum ieee80211_self_protected_actioncode action, + u8 *da, __le16 llid, __le16 plid, __le16 reason) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 + sdata->u.mesh.ie_len); struct ieee80211_mgmt *mgmt; bool include_plid = false; - static const u8 meshpeeringproto[] = { 0x00, 0x0F, 0xAC, 0x2A }; + int ie_len = 4; + u16 peering_proto = 0; u8 *pos; - int ie_len; if (!skb) return -1; @@ -175,63 +168,75 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, * common action part (1) */ mgmt = (struct ieee80211_mgmt *) - skb_put(skb, 25 + sizeof(mgmt->u.action.u.plink_action)); - memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.plink_action)); + skb_put(skb, 25 + sizeof(mgmt->u.action.u.self_prot)); + memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.self_prot)); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); - mgmt->u.action.category = WLAN_CATEGORY_MESH_ACTION; - mgmt->u.action.u.plink_action.action_code = action; - - if (action == PLINK_CLOSE) - mgmt->u.action.u.plink_action.aux = reason; - else { - mgmt->u.action.u.plink_action.aux = cpu_to_le16(0x0); - if (action == PLINK_CONFIRM) { - pos = skb_put(skb, 4); - /* two-byte status code followed by two-byte AID */ - memset(pos, 0, 2); + mgmt->u.action.category = WLAN_CATEGORY_SELF_PROTECTED; + mgmt->u.action.u.self_prot.action_code = action; + + if (action != WLAN_SP_MESH_PEERING_CLOSE) { + /* capability info */ + pos = skb_put(skb, 2); + memset(pos, 0, 2); + if (action == WLAN_SP_MESH_PEERING_CONFIRM) { + /* AID */ + pos = skb_put(skb, 2); memcpy(pos + 2, &plid, 2); } - mesh_mgmt_ies_add(skb, sdata); + if (ieee80211_add_srates_ie(&sdata->vif, skb) || + ieee80211_add_ext_srates_ie(&sdata->vif, skb) || + mesh_add_rsn_ie(skb, sdata) || + mesh_add_meshid_ie(skb, sdata) || + mesh_add_meshconf_ie(skb, sdata)) + return -1; + } else { /* WLAN_SP_MESH_PEERING_CLOSE */ + if (mesh_add_meshid_ie(skb, sdata)) + return -1; } - /* Add Peer Link Management element */ + /* Add Mesh Peering Management element */ switch (action) { - case PLINK_OPEN: - ie_len = 6; + case WLAN_SP_MESH_PEERING_OPEN: break; - case PLINK_CONFIRM: - ie_len = 8; + case WLAN_SP_MESH_PEERING_CONFIRM: + ie_len += 2; include_plid = true; break; - case PLINK_CLOSE: - default: - if (!plid) - ie_len = 8; - else { - ie_len = 10; + case WLAN_SP_MESH_PEERING_CLOSE: + if (plid) { + ie_len += 2; include_plid = true; } + ie_len += 2; /* reason code */ break; + default: + return -EINVAL; } + if (WARN_ON(skb_tailroom(skb) < 2 + ie_len)) + return -ENOMEM; + pos = skb_put(skb, 2 + ie_len); - *pos++ = WLAN_EID_PEER_LINK; + *pos++ = WLAN_EID_PEER_MGMT; *pos++ = ie_len; - memcpy(pos, meshpeeringproto, sizeof(meshpeeringproto)); - pos += 4; + memcpy(pos, &peering_proto, 2); + pos += 2; memcpy(pos, &llid, 2); + pos += 2; if (include_plid) { - pos += 2; memcpy(pos, &plid, 2); - } - if (action == PLINK_CLOSE) { pos += 2; + } + if (action == WLAN_SP_MESH_PEERING_CLOSE) { memcpy(pos, &reason, 2); + pos += 2; } + if (mesh_add_vendor_ies(skb, sdata)) + return -1; ieee80211_tx_skb(sdata, skb); return 0; @@ -322,21 +327,21 @@ static void mesh_plink_timer(unsigned long data) ++sta->plink_retries; mod_plink_timer(sta, sta->plink_timeout); spin_unlock_bh(&sta->lock); - mesh_plink_frame_tx(sdata, PLINK_OPEN, sta->sta.addr, llid, - 0, 0); + mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, + sta->sta.addr, llid, 0, 0); break; } - reason = cpu_to_le16(MESH_MAX_RETRIES); + reason = cpu_to_le16(WLAN_REASON_MESH_MAX_RETRIES); /* fall through on else */ case NL80211_PLINK_CNF_RCVD: /* confirm timer */ if (!reason) - reason = cpu_to_le16(MESH_CONFIRM_TIMEOUT); + reason = cpu_to_le16(WLAN_REASON_MESH_CONFIRM_TIMEOUT); sta->plink_state = NL80211_PLINK_HOLDING; mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); spin_unlock_bh(&sta->lock); - mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, plid, - reason); + mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, + sta->sta.addr, llid, plid, reason); break; case NL80211_PLINK_HOLDING: /* holding timer */ @@ -380,7 +385,7 @@ int mesh_plink_open(struct sta_info *sta) __le16 llid; struct ieee80211_sub_if_data *sdata = sta->sdata; - if (!test_sta_flags(sta, WLAN_STA_AUTH)) + if (!test_sta_flag(sta, WLAN_STA_AUTH)) return -EPERM; spin_lock_bh(&sta->lock); @@ -396,7 +401,7 @@ int mesh_plink_open(struct sta_info *sta) mpl_dbg("Mesh plink: starting establishment with %pM\n", sta->sta.addr); - return mesh_plink_frame_tx(sdata, PLINK_OPEN, + return mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, sta->sta.addr, llid, 0, 0); } @@ -422,7 +427,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m struct ieee802_11_elems elems; struct sta_info *sta; enum plink_event event; - enum plink_frame_type ftype; + enum ieee80211_self_protected_actioncode ftype; size_t baselen; bool deactivated, matches_local = true; u8 ie_len; @@ -449,14 +454,15 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m return; } - baseaddr = mgmt->u.action.u.plink_action.variable; - baselen = (u8 *) mgmt->u.action.u.plink_action.variable - (u8 *) mgmt; - if (mgmt->u.action.u.plink_action.action_code == PLINK_CONFIRM) { + baseaddr = mgmt->u.action.u.self_prot.variable; + baselen = (u8 *) mgmt->u.action.u.self_prot.variable - (u8 *) mgmt; + if (mgmt->u.action.u.self_prot.action_code == + WLAN_SP_MESH_PEERING_CONFIRM) { baseaddr += 4; baselen += 4; } ieee802_11_parse_elems(baseaddr, len - baselen, &elems); - if (!elems.peer_link) { + if (!elems.peering) { mpl_dbg("Mesh plink: missing necessary peer link ie\n"); return; } @@ -466,37 +472,40 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m return; } - ftype = mgmt->u.action.u.plink_action.action_code; - ie_len = elems.peer_link_len; - if ((ftype == PLINK_OPEN && ie_len != 6) || - (ftype == PLINK_CONFIRM && ie_len != 8) || - (ftype == PLINK_CLOSE && ie_len != 8 && ie_len != 10)) { + ftype = mgmt->u.action.u.self_prot.action_code; + ie_len = elems.peering_len; + if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) || + (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) || + (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6 + && ie_len != 8)) { mpl_dbg("Mesh plink: incorrect plink ie length %d %d\n", ftype, ie_len); return; } - if (ftype != PLINK_CLOSE && (!elems.mesh_id || !elems.mesh_config)) { + if (ftype != WLAN_SP_MESH_PEERING_CLOSE && + (!elems.mesh_id || !elems.mesh_config)) { mpl_dbg("Mesh plink: missing necessary ie\n"); return; } /* Note the lines below are correct, the llid in the frame is the plid * from the point of view of this host. */ - memcpy(&plid, PLINK_GET_LLID(elems.peer_link), 2); - if (ftype == PLINK_CONFIRM || (ftype == PLINK_CLOSE && ie_len == 10)) - memcpy(&llid, PLINK_GET_PLID(elems.peer_link), 2); + memcpy(&plid, PLINK_GET_LLID(elems.peering), 2); + if (ftype == WLAN_SP_MESH_PEERING_CONFIRM || + (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) + memcpy(&llid, PLINK_GET_PLID(elems.peering), 2); rcu_read_lock(); sta = sta_info_get(sdata, mgmt->sa); - if (!sta && ftype != PLINK_OPEN) { + if (!sta && ftype != WLAN_SP_MESH_PEERING_OPEN) { mpl_dbg("Mesh plink: cls or cnf from unknown peer\n"); rcu_read_unlock(); return; } - if (sta && !test_sta_flags(sta, WLAN_STA_AUTH)) { + if (sta && !test_sta_flag(sta, WLAN_STA_AUTH)) { mpl_dbg("Mesh plink: Action frame from non-authed peer\n"); rcu_read_unlock(); return; @@ -509,30 +518,30 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m /* Now we will figure out the appropriate event... */ event = PLINK_UNDEFINED; - if (ftype != PLINK_CLOSE && (!mesh_matches_local(&elems, sdata))) { + if (ftype != WLAN_SP_MESH_PEERING_CLOSE && + (!mesh_matches_local(&elems, sdata))) { matches_local = false; switch (ftype) { - case PLINK_OPEN: + case WLAN_SP_MESH_PEERING_OPEN: event = OPN_RJCT; break; - case PLINK_CONFIRM: + case WLAN_SP_MESH_PEERING_CONFIRM: event = CNF_RJCT; break; - case PLINK_CLOSE: - /* avoid warning */ + default: break; } } if (!sta && !matches_local) { rcu_read_unlock(); - reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); + reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); llid = 0; - mesh_plink_frame_tx(sdata, PLINK_CLOSE, mgmt->sa, llid, - plid, reason); + mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, + mgmt->sa, llid, plid, reason); return; } else if (!sta) { - /* ftype == PLINK_OPEN */ + /* ftype == WLAN_SP_MESH_PEERING_OPEN */ u32 rates; rcu_read_unlock(); @@ -557,21 +566,21 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m } else if (matches_local) { spin_lock_bh(&sta->lock); switch (ftype) { - case PLINK_OPEN: + case WLAN_SP_MESH_PEERING_OPEN: if (!mesh_plink_free_count(sdata) || (sta->plid && sta->plid != plid)) event = OPN_IGNR; else event = OPN_ACPT; break; - case PLINK_CONFIRM: + case WLAN_SP_MESH_PEERING_CONFIRM: if (!mesh_plink_free_count(sdata) || (sta->llid != llid || sta->plid != plid)) event = CNF_IGNR; else event = CNF_ACPT; break; - case PLINK_CLOSE: + case WLAN_SP_MESH_PEERING_CLOSE: if (sta->plink_state == NL80211_PLINK_ESTAB) /* Do not check for llid or plid. This does not * follow the standard but since multiple plinks @@ -620,10 +629,12 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m sta->llid = llid; mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); spin_unlock_bh(&sta->lock); - mesh_plink_frame_tx(sdata, PLINK_OPEN, sta->sta.addr, llid, - 0, 0); - mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, - llid, plid, 0); + mesh_plink_frame_tx(sdata, + WLAN_SP_MESH_PEERING_OPEN, + sta->sta.addr, llid, 0, 0); + mesh_plink_frame_tx(sdata, + WLAN_SP_MESH_PEERING_CONFIRM, + sta->sta.addr, llid, plid, 0); break; default: spin_unlock_bh(&sta->lock); @@ -635,10 +646,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m switch (event) { case OPN_RJCT: case CNF_RJCT: - reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); + reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); case CLS_ACPT: if (!reason) - reason = cpu_to_le16(MESH_CLOSE_RCVD); + reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); sta->reason = reason; sta->plink_state = NL80211_PLINK_HOLDING; if (!mod_plink_timer(sta, @@ -647,8 +658,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m llid = sta->llid; spin_unlock_bh(&sta->lock); - mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, - plid, reason); + mesh_plink_frame_tx(sdata, + WLAN_SP_MESH_PEERING_CLOSE, + sta->sta.addr, llid, plid, reason); break; case OPN_ACPT: /* retry timer is left untouched */ @@ -656,8 +668,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m sta->plid = plid; llid = sta->llid; spin_unlock_bh(&sta->lock); - mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, - plid, 0); + mesh_plink_frame_tx(sdata, + WLAN_SP_MESH_PEERING_CONFIRM, + sta->sta.addr, llid, plid, 0); break; case CNF_ACPT: sta->plink_state = NL80211_PLINK_CNF_RCVD; @@ -677,10 +690,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m switch (event) { case OPN_RJCT: case CNF_RJCT: - reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); + reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); case CLS_ACPT: if (!reason) - reason = cpu_to_le16(MESH_CLOSE_RCVD); + reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); sta->reason = reason; sta->plink_state = NL80211_PLINK_HOLDING; if (!mod_plink_timer(sta, @@ -689,14 +702,15 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m llid = sta->llid; spin_unlock_bh(&sta->lock); - mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, - plid, reason); + mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, + sta->sta.addr, llid, plid, reason); break; case OPN_ACPT: llid = sta->llid; spin_unlock_bh(&sta->lock); - mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, - plid, 0); + mesh_plink_frame_tx(sdata, + WLAN_SP_MESH_PEERING_CONFIRM, + sta->sta.addr, llid, plid, 0); break; case CNF_ACPT: del_timer(&sta->plink_timer); @@ -717,10 +731,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m switch (event) { case OPN_RJCT: case CNF_RJCT: - reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); + reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); case CLS_ACPT: if (!reason) - reason = cpu_to_le16(MESH_CLOSE_RCVD); + reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); sta->reason = reason; sta->plink_state = NL80211_PLINK_HOLDING; if (!mod_plink_timer(sta, @@ -729,8 +743,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m llid = sta->llid; spin_unlock_bh(&sta->lock); - mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, - plid, reason); + mesh_plink_frame_tx(sdata, + WLAN_SP_MESH_PEERING_CLOSE, + sta->sta.addr, llid, plid, reason); break; case OPN_ACPT: del_timer(&sta->plink_timer); @@ -740,8 +755,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); mpl_dbg("Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); - mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, - plid, 0); + mesh_plink_frame_tx(sdata, + WLAN_SP_MESH_PEERING_CONFIRM, + sta->sta.addr, llid, plid, 0); break; default: spin_unlock_bh(&sta->lock); @@ -752,7 +768,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m case NL80211_PLINK_ESTAB: switch (event) { case CLS_ACPT: - reason = cpu_to_le16(MESH_CLOSE_RCVD); + reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); sta->reason = reason; deactivated = __mesh_plink_deactivate(sta); sta->plink_state = NL80211_PLINK_HOLDING; @@ -761,14 +777,15 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m spin_unlock_bh(&sta->lock); if (deactivated) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); - mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, - plid, reason); + mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, + sta->sta.addr, llid, plid, reason); break; case OPN_ACPT: llid = sta->llid; spin_unlock_bh(&sta->lock); - mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, - plid, 0); + mesh_plink_frame_tx(sdata, + WLAN_SP_MESH_PEERING_CONFIRM, + sta->sta.addr, llid, plid, 0); break; default: spin_unlock_bh(&sta->lock); @@ -790,8 +807,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m llid = sta->llid; reason = sta->reason; spin_unlock_bh(&sta->lock); - mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, - llid, plid, reason); + mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, + sta->sta.addr, llid, plid, reason); break; default: spin_unlock_bh(&sta->lock); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 1563250..9da8626 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -16,10 +16,12 @@ #include <linux/skbuff.h> #include <linux/if_arp.h> #include <linux/etherdevice.h> +#include <linux/moduleparam.h> #include <linux/rtnetlink.h> -#include <linux/pm_qos_params.h> +#include <linux/pm_qos.h> #include <linux/crc32.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/mac80211.h> #include <asm/unaligned.h> @@ -160,7 +162,8 @@ static int ecw2cw(int ecw) */ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, struct ieee80211_ht_info *hti, - const u8 *bssid, u16 ap_ht_cap_flags) + const u8 *bssid, u16 ap_ht_cap_flags, + bool beacon_htcap_ie) { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; @@ -232,6 +235,21 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, WARN_ON(!ieee80211_set_channel_type(local, sdata, channel_type)); } + if (beacon_htcap_ie && (prev_chantype != channel_type)) { + /* + * Whenever the AP announces the HT mode change that can be + * 40MHz intolerant or etc., it would be safer to stop tx + * queues before doing hw config to avoid buffer overflow. + */ + ieee80211_stop_queues_by_reason(&sdata->local->hw, + IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE); + + /* flush out all packets */ + synchronize_net(); + + drv_flush(local, false); + } + /* channel_type change automatically detected */ ieee80211_hw_config(local, 0); @@ -243,6 +261,10 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, IEEE80211_RC_HT_CHANGED, channel_type); rcu_read_unlock(); + + if (beacon_htcap_ie) + ieee80211_wake_queues_by_reason(&sdata->local->hw, + IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE); } ht_opmode = le16_to_cpu(hti->operation_mode); @@ -271,11 +293,9 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt; skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt)); - if (!skb) { - printk(KERN_DEBUG "%s: failed to allocate buffer for " - "deauth/disassoc frame\n", sdata->name); + if (!skb) return; - } + skb_reserve(skb, local->hw.extra_tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); @@ -330,6 +350,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, { struct sk_buff *skb; struct ieee80211_hdr_3addr *nullfunc; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif); if (!skb) @@ -340,6 +361,10 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, nullfunc->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | + IEEE80211_STA_CONNECTION_POLL)) + IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE; + ieee80211_tx_skb(sdata, skb); } @@ -354,11 +379,9 @@ static void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local, return; skb = dev_alloc_skb(local->hw.extra_tx_headroom + 30); - if (!skb) { - printk(KERN_DEBUG "%s: failed to allocate buffer for 4addr " - "nullfunc frame\n", sdata->name); + if (!skb) return; - } + skb_reserve(skb, local->hw.extra_tx_headroom); nullfunc = (struct ieee80211_hdr *) skb_put(skb, 30); @@ -394,6 +417,9 @@ static void ieee80211_chswitch_work(struct work_struct *work) /* call "hw_config" only if doing sw channel switch */ ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL); + } else { + /* update the device channel directly */ + sdata->local->hw.conf.channel = sdata->local->oper_channel; } /* XXX: shouldn't really modify cfg80211-owned data! */ @@ -608,7 +634,7 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *mgd = &sdata->u.mgd; struct sta_info *sta = NULL; - u32 sta_flags = 0; + bool authorized = false; if (!mgd->powersave) return false; @@ -629,13 +655,10 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata) rcu_read_lock(); sta = sta_info_get(sdata, mgd->bssid); if (sta) - sta_flags = get_sta_flags(sta); + authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); rcu_read_unlock(); - if (!(sta_flags & WLAN_STA_AUTHORIZED)) - return false; - - return true; + return authorized; } /* need to hold RTNL or interface lock */ @@ -752,7 +775,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) container_of(work, struct ieee80211_local, dynamic_ps_enable_work); struct ieee80211_sub_if_data *sdata = local->ps_sdata; - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_if_managed *ifmgd; unsigned long flags; int q; @@ -760,26 +783,39 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) if (!sdata) return; + ifmgd = &sdata->u.mgd; + if (local->hw.conf.flags & IEEE80211_CONF_PS) return; - /* - * transmission can be stopped by others which leads to - * dynamic_ps_timer expiry. Postpond the ps timer if it - * is not the actual idle state. - */ - spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - for (q = 0; q < local->hw.queues; q++) { - if (local->queue_stop_reasons[q]) { - spin_unlock_irqrestore(&local->queue_stop_reason_lock, - flags); + if (!local->disable_dynamic_ps && + local->hw.conf.dynamic_ps_timeout > 0) { + /* don't enter PS if TX frames are pending */ + if (drv_tx_frames_pending(local)) { mod_timer(&local->dynamic_ps_timer, jiffies + msecs_to_jiffies( local->hw.conf.dynamic_ps_timeout)); return; } + + /* + * transmission can be stopped by others which leads to + * dynamic_ps_timer expiry. Postpone the ps timer if it + * is not the actual idle state. + */ + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + for (q = 0; q < local->hw.queues; q++) { + if (local->queue_stop_reasons[q]) { + spin_unlock_irqrestore(&local->queue_stop_reason_lock, + flags); + mod_timer(&local->dynamic_ps_timer, jiffies + + msecs_to_jiffies( + local->hw.conf.dynamic_ps_timeout)); + return; + } + } + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } - spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) && (!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED))) { @@ -804,7 +840,8 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } - netif_tx_wake_all_queues(sdata->dev); + if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) + netif_tx_wake_all_queues(sdata->dev); } void ieee80211_dynamic_ps_timer(unsigned long data) @@ -906,7 +943,8 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, params.aifs, params.cw_min, params.cw_max, params.txop, params.uapsd); #endif - if (drv_conf_tx(local, queue, ¶ms)) + sdata->tx_conf[queue] = params; + if (drv_conf_tx(local, sdata, queue, ¶ms)) wiphy_debug(local->hw.wiphy, "failed to set TX queue parameters for queue %d\n", queue); @@ -1064,7 +1102,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, mutex_lock(&local->sta_mtx); sta = sta_info_get(sdata, bssid); if (sta) { - set_sta_flags(sta, WLAN_STA_BLOCK_BA); + set_sta_flag(sta, WLAN_STA_BLOCK_BA); ieee80211_sta_tear_down_BA_sessions(sta, tx); } mutex_unlock(&local->sta_mtx); @@ -1106,8 +1144,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT; ieee80211_bss_info_change_notify(sdata, changed); + /* remove AP and TDLS peers */ if (remove_sta) - sta_info_destroy_addr(sdata, bssid); + sta_info_flush(local, sdata); del_timer_sync(&sdata->u.mgd.conn_mon_timer); del_timer_sync(&sdata->u.mgd.bcn_mon_timer); @@ -1207,7 +1246,8 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) ieee80211_send_nullfunc(sdata->local, sdata, 0); } else { ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID); - ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0); + ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0, + (u32) -1, true, false); } ifmgd->probe_send_count++; @@ -1292,7 +1332,8 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID); skb = ieee80211_build_probe_req(sdata, ifmgd->associated->bssid, - ssid + 2, ssid[1], NULL, 0); + (u32) -1, ssid + 2, ssid[1], + NULL, 0, true); return skb; } @@ -1446,6 +1487,7 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, int i, j, err; bool have_higher_than_11mbit = false; u16 ap_ht_cap_flags; + int min_rate = INT_MAX, min_rate_index = -1; /* AssocResp and ReassocResp have identical structure */ @@ -1479,17 +1521,22 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, ifmgd->aid = aid; - sta = sta_info_alloc(sdata, cbss->bssid, GFP_KERNEL); - if (!sta) { - printk(KERN_DEBUG "%s: failed to alloc STA entry for" - " the AP\n", sdata->name); + mutex_lock(&sdata->local->sta_mtx); + /* + * station info was already allocated and inserted before + * the association and should be available to us + */ + sta = sta_info_get_rx(sdata, cbss->bssid); + if (WARN_ON(!sta)) { + mutex_unlock(&sdata->local->sta_mtx); return false; } - set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC | - WLAN_STA_ASSOC_AP); + set_sta_flag(sta, WLAN_STA_AUTH); + set_sta_flag(sta, WLAN_STA_ASSOC); + set_sta_flag(sta, WLAN_STA_ASSOC_AP); if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) - set_sta_flags(sta, WLAN_STA_AUTHORIZED); + set_sta_flag(sta, WLAN_STA_AUTHORIZED); rates = 0; basic_rates = 0; @@ -1507,6 +1554,10 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, rates |= BIT(j); if (is_basic) basic_rates |= BIT(j); + if (rate < min_rate) { + min_rate = rate; + min_rate_index = j; + } break; } } @@ -1524,11 +1575,25 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, rates |= BIT(j); if (is_basic) basic_rates |= BIT(j); + if (rate < min_rate) { + min_rate = rate; + min_rate_index = j; + } break; } } } + /* + * some buggy APs don't advertise basic_rates. use the lowest + * supported rate instead. + */ + if (unlikely(!basic_rates) && min_rate_index >= 0) { + printk(KERN_DEBUG "%s: No basic rates in AssocResp. " + "Using min supported rate instead.\n", sdata->name); + basic_rates = BIT(min_rate_index); + } + sta->sta.supp_rates[wk->chan->band] = rates; sdata->vif.bss_conf.basic_rates = basic_rates; @@ -1548,12 +1613,13 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, rate_control_rate_init(sta); if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) - set_sta_flags(sta, WLAN_STA_MFP); + set_sta_flag(sta, WLAN_STA_MFP); if (elems.wmm_param) - set_sta_flags(sta, WLAN_STA_WME); + set_sta_flag(sta, WLAN_STA_WME); - err = sta_info_insert(sta); + /* sta_info_reinsert will also unlock the mutex lock */ + err = sta_info_reinsert(sta); sta = NULL; if (err) { printk(KERN_DEBUG "%s: failed to insert STA entry for" @@ -1581,7 +1647,8 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, (sdata->local->hw.queues >= 4) && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem, - cbss->bssid, ap_ht_cap_flags); + cbss->bssid, ap_ht_cap_flags, + false); /* set AID and assoc capability, * ieee80211_set_associated() will tell the driver */ @@ -1762,6 +1829,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ifmgd->ave_beacon_signal = rx_status->signal * 16; ifmgd->last_cqm_event_signal = 0; ifmgd->count_beacon_signal = 1; + ifmgd->last_ave_beacon_signal = 0; } else { ifmgd->ave_beacon_signal = (IEEE80211_SIGNAL_AVE_WEIGHT * rx_status->signal * 16 + @@ -1769,6 +1837,28 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ifmgd->ave_beacon_signal) / 16; ifmgd->count_beacon_signal++; } + + if (ifmgd->rssi_min_thold != ifmgd->rssi_max_thold && + ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) { + int sig = ifmgd->ave_beacon_signal; + int last_sig = ifmgd->last_ave_beacon_signal; + + /* + * if signal crosses either of the boundaries, invoke callback + * with appropriate parameters + */ + if (sig > ifmgd->rssi_max_thold && + (last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) { + ifmgd->last_ave_beacon_signal = sig; + drv_rssi_callback(local, RSSI_EVENT_HIGH); + } else if (sig < ifmgd->rssi_min_thold && + (last_sig >= ifmgd->rssi_max_thold || + last_sig == 0)) { + ifmgd->last_ave_beacon_signal = sig; + drv_rssi_callback(local, RSSI_EVENT_LOW); + } + } + if (bss_conf->cqm_rssi_thold && ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT && !(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) { @@ -1892,7 +1982,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem, - bssid, ap_ht_cap_flags); + bssid, ap_ht_cap_flags, true); } /* Note: country IE parsing is done for us by cfg80211 */ @@ -2028,7 +2118,7 @@ static void ieee80211_sta_timer(unsigned long data) } static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, - u8 *bssid) + u8 *bssid, u8 reason) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -2046,8 +2136,7 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, * but that's not a problem. */ ieee80211_send_deauth_disassoc(sdata, bssid, - IEEE80211_STYPE_DEAUTH, - WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, + IEEE80211_STYPE_DEAUTH, reason, NULL, true); mutex_lock(&ifmgd->mtx); } @@ -2093,7 +2182,8 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) " AP %pM, disconnecting.\n", sdata->name, bssid); #endif - ieee80211_sta_connection_lost(sdata, bssid); + ieee80211_sta_connection_lost(sdata, bssid, + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); } } else if (time_is_after_jiffies(ifmgd->probe_timeout)) run_again(ifmgd, ifmgd->probe_timeout); @@ -2105,7 +2195,8 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) sdata->name, bssid, probe_wait_ms); #endif - ieee80211_sta_connection_lost(sdata, bssid); + ieee80211_sta_connection_lost(sdata, bssid, + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); } else if (ifmgd->probe_send_count < max_tries) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG wiphy_debug(local->hw.wiphy, @@ -2127,7 +2218,8 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) sdata->name, bssid, probe_wait_ms); - ieee80211_sta_connection_lost(sdata, bssid); + ieee80211_sta_connection_lost(sdata, bssid, + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); } } @@ -2196,6 +2288,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata) cancel_work_sync(&ifmgd->request_smps_work); + cancel_work_sync(&ifmgd->monitor_work); cancel_work_sync(&ifmgd->beacon_connection_loss_work); if (del_timer_sync(&ifmgd->timer)) set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); @@ -2204,7 +2297,6 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata) if (del_timer_sync(&ifmgd->chswitch_timer)) set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running); - cancel_work_sync(&ifmgd->monitor_work); /* these will just be re-established on connection */ del_timer_sync(&ifmgd->conn_mon_timer); del_timer_sync(&ifmgd->bcn_mon_timer); @@ -2217,12 +2309,31 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) if (!ifmgd->associated) return; + if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) { + sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME; + mutex_lock(&ifmgd->mtx); + if (ifmgd->associated) { +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + wiphy_debug(sdata->local->hw.wiphy, + "%s: driver requested disconnect after resume.\n", + sdata->name); +#endif + ieee80211_sta_connection_lost(sdata, + ifmgd->associated->bssid, + WLAN_REASON_UNSPECIFIED); + mutex_unlock(&ifmgd->mtx); + return; + } + mutex_unlock(&ifmgd->mtx); + } + if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running)) add_timer(&ifmgd->timer); if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running)) add_timer(&ifmgd->chswitch_timer); ieee80211_sta_reset_beacon_monitor(sdata); ieee80211_restart_sta_timer(sdata); + ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.monitor_work); } #endif @@ -2288,14 +2399,16 @@ static enum work_done_result ieee80211_probe_auth_done(struct ieee80211_work *wk, struct sk_buff *skb) { + struct ieee80211_local *local = wk->sdata->local; + if (!skb) { cfg80211_send_auth_timeout(wk->sdata->dev, wk->filter_ta); - return WORK_DONE_DESTROY; + goto destroy; } if (wk->type == IEEE80211_WORK_AUTH) { cfg80211_send_rx_auth(wk->sdata->dev, skb->data, skb->len); - return WORK_DONE_DESTROY; + goto destroy; } mutex_lock(&wk->sdata->u.mgd.mtx); @@ -2305,6 +2418,12 @@ ieee80211_probe_auth_done(struct ieee80211_work *wk, wk->type = IEEE80211_WORK_AUTH; wk->probe_auth.tries = 0; return WORK_DONE_REQUEUE; + destroy: + if (wk->probe_auth.synced) + drv_finish_tx_sync(local, wk->sdata, wk->filter_ta, + IEEE80211_TX_SYNC_AUTH); + + return WORK_DONE_DESTROY; } int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, @@ -2374,17 +2493,43 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, return 0; } +/* create and insert a dummy station entry */ +static int ieee80211_pre_assoc(struct ieee80211_sub_if_data *sdata, + u8 *bssid) { + struct sta_info *sta; + int err; + + sta = sta_info_alloc(sdata, bssid, GFP_KERNEL); + if (!sta) + return -ENOMEM; + + sta->dummy = true; + + err = sta_info_insert(sta); + sta = NULL; + if (err) { + printk(KERN_DEBUG "%s: failed to insert Dummy STA entry for" + " the AP (error %d)\n", sdata->name, err); + return err; + } + + return 0; +} + static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk, struct sk_buff *skb) { + struct ieee80211_local *local = wk->sdata->local; struct ieee80211_mgmt *mgmt; struct ieee80211_rx_status *rx_status; struct ieee802_11_elems elems; + struct cfg80211_bss *cbss = wk->assoc.bss; u16 status; if (!skb) { + sta_info_destroy_addr(wk->sdata, cbss->bssid); cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta); - return WORK_DONE_DESTROY; + goto destroy; } if (wk->type == IEEE80211_WORK_ASSOC_BEACON_WAIT) { @@ -2404,19 +2549,32 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk, status = le16_to_cpu(mgmt->u.assoc_resp.status_code); if (status == WLAN_STATUS_SUCCESS) { + if (wk->assoc.synced) + drv_finish_tx_sync(local, wk->sdata, wk->filter_ta, + IEEE80211_TX_SYNC_ASSOC); + mutex_lock(&wk->sdata->u.mgd.mtx); if (!ieee80211_assoc_success(wk, mgmt, skb->len)) { mutex_unlock(&wk->sdata->u.mgd.mtx); /* oops -- internal error -- send timeout for now */ + sta_info_destroy_addr(wk->sdata, cbss->bssid); cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta); return WORK_DONE_DESTROY; } mutex_unlock(&wk->sdata->u.mgd.mtx); + } else { + /* assoc failed - destroy the dummy station entry */ + sta_info_destroy_addr(wk->sdata, cbss->bssid); } cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len); + destroy: + if (wk->assoc.synced) + drv_finish_tx_sync(local, wk->sdata, wk->filter_ta, + IEEE80211_TX_SYNC_ASSOC); + return WORK_DONE_DESTROY; } @@ -2427,7 +2585,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)req->bss->priv; struct ieee80211_work *wk; const u8 *ssid; - int i; + int i, err; mutex_lock(&ifmgd->mtx); if (ifmgd->associated) { @@ -2452,6 +2610,16 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, if (!wk) return -ENOMEM; + /* + * create a dummy station info entry in order + * to start accepting incoming EAPOL packets from the station + */ + err = ieee80211_pre_assoc(sdata, req->bss->bssid); + if (err) { + kfree(wk); + return err; + } + ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N; ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED; @@ -2551,7 +2719,6 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - struct ieee80211_work *wk; u8 bssid[ETH_ALEN]; bool assoc_bss = false; @@ -2564,30 +2731,47 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, assoc_bss = true; } else { bool not_auth_yet = false; + struct ieee80211_work *tmp, *wk = NULL; mutex_unlock(&ifmgd->mtx); mutex_lock(&local->mtx); - list_for_each_entry(wk, &local->work_list, list) { - if (wk->sdata != sdata) + list_for_each_entry(tmp, &local->work_list, list) { + if (tmp->sdata != sdata) continue; - if (wk->type != IEEE80211_WORK_DIRECT_PROBE && - wk->type != IEEE80211_WORK_AUTH && - wk->type != IEEE80211_WORK_ASSOC && - wk->type != IEEE80211_WORK_ASSOC_BEACON_WAIT) + if (tmp->type != IEEE80211_WORK_DIRECT_PROBE && + tmp->type != IEEE80211_WORK_AUTH && + tmp->type != IEEE80211_WORK_ASSOC && + tmp->type != IEEE80211_WORK_ASSOC_BEACON_WAIT) continue; - if (memcmp(req->bss->bssid, wk->filter_ta, ETH_ALEN)) + if (memcmp(req->bss->bssid, tmp->filter_ta, ETH_ALEN)) continue; - not_auth_yet = wk->type == IEEE80211_WORK_DIRECT_PROBE; - list_del_rcu(&wk->list); - free_work(wk); + not_auth_yet = tmp->type == IEEE80211_WORK_DIRECT_PROBE; + list_del_rcu(&tmp->list); + synchronize_rcu(); + wk = tmp; break; } mutex_unlock(&local->mtx); + if (wk && wk->type == IEEE80211_WORK_ASSOC) { + /* clean up dummy sta & TX sync */ + sta_info_destroy_addr(wk->sdata, wk->filter_ta); + if (wk->assoc.synced) + drv_finish_tx_sync(local, wk->sdata, + wk->filter_ta, + IEEE80211_TX_SYNC_ASSOC); + } else if (wk && wk->type == IEEE80211_WORK_AUTH) { + if (wk->probe_auth.synced) + drv_finish_tx_sync(local, wk->sdata, + wk->filter_ta, + IEEE80211_TX_SYNC_AUTH); + } + kfree(wk); + /* * If somebody requests authentication and we haven't * sent out an auth frame yet there's no need to send @@ -2609,7 +2793,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, req->reason_code, cookie, !req->local_state_change); if (assoc_bss) - sta_info_destroy_addr(sdata, bssid); + sta_info_flush(sdata->local, sdata); mutex_lock(&sdata->local->mtx); ieee80211_recalc_idle(sdata->local); @@ -2649,7 +2833,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_send_deauth_disassoc(sdata, req->bss->bssid, IEEE80211_STYPE_DISASSOC, req->reason_code, cookie, !req->local_state_change); - sta_info_destroy_addr(sdata, bssid); + sta_info_flush(sdata->local, sdata); mutex_lock(&sdata->local->mtx); ieee80211_recalc_idle(sdata->local); @@ -2669,3 +2853,10 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp); } EXPORT_SYMBOL(ieee80211_cqm_rssi_notify); + +unsigned char ieee80211_get_operstate(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + return sdata->dev->operstate; +} +EXPORT_SYMBOL(ieee80211_get_operstate); diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index ecc4922..db2c215 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -12,19 +12,16 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include <linux/export.h> #include <net/mac80211.h> #include "ieee80211_i.h" #include "driver-trace.h" /* - * Tell our hardware to disable PS. - * Optionally inform AP that we will go to sleep so that it will buffer - * the frames while we are doing off-channel work. This is optional - * because we *may* be doing work on-operating channel, and want our - * hardware unconditionally awake, but still let the AP send us normal frames. + * inform AP that we will go to sleep so that it will buffer the frames + * while we scan */ -static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata, - bool tell_ap) +static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -45,8 +42,8 @@ static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata, ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } - if (tell_ap && (!local->offchannel_ps_enabled || - !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK))) + if (!(local->offchannel_ps_enabled) || + !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) /* * If power save was enabled, no need to send a nullfunc * frame because AP knows that we are sleeping. But if the @@ -81,9 +78,6 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata) * we are sleeping, let's just enable power save mode in * hardware. */ - /* TODO: Only set hardware if CONF_PS changed? - * TODO: Should we set offchannel_ps_enabled to false? - */ local->hw.conf.flags |= IEEE80211_CONF_PS; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } else if (local->hw.conf.dynamic_ps_timeout > 0) { @@ -102,52 +96,57 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata) ieee80211_sta_reset_conn_monitor(sdata); } -void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local) +void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; - /* - * notify the AP about us leaving the channel and stop all - * STA interfaces. - */ mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) continue; - if (sdata->vif.type != NL80211_IFTYPE_MONITOR) - set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); - - /* Check to see if we should disable beaconing. */ + /* disable beaconing */ if (sdata->vif.type == NL80211_IFTYPE_AP || sdata->vif.type == NL80211_IFTYPE_ADHOC || sdata->vif.type == NL80211_IFTYPE_MESH_POINT) ieee80211_bss_info_change_notify( sdata, BSS_CHANGED_BEACON_ENABLED); - if (sdata->vif.type != NL80211_IFTYPE_MONITOR) { + /* + * only handle non-STA interfaces here, STA interfaces + * are handled in ieee80211_offchannel_stop_station(), + * e.g., from the background scan state machine. + * + * In addition, do not stop monitor interface to allow it to be + * used from user space controlled off-channel operations. + */ + if (sdata->vif.type != NL80211_IFTYPE_STATION && + sdata->vif.type != NL80211_IFTYPE_MONITOR) { + set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); netif_tx_stop_all_queues(sdata->dev); - if (sdata->vif.type == NL80211_IFTYPE_STATION && - sdata->u.mgd.associated) - ieee80211_offchannel_ps_enable(sdata, true); } } mutex_unlock(&local->iflist_mtx); } -void ieee80211_offchannel_enable_all_ps(struct ieee80211_local *local, - bool tell_ap) +void ieee80211_offchannel_stop_station(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; + /* + * notify the AP about us leaving the channel and stop all STA interfaces + */ mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) continue; - if (sdata->vif.type == NL80211_IFTYPE_STATION && - sdata->u.mgd.associated) - ieee80211_offchannel_ps_enable(sdata, tell_ap); + if (sdata->vif.type == NL80211_IFTYPE_STATION) { + set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); + netif_tx_stop_all_queues(sdata->dev); + if (sdata->u.mgd.associated) + ieee80211_offchannel_ps_enable(sdata); + } } mutex_unlock(&local->iflist_mtx); } @@ -163,9 +162,10 @@ void ieee80211_offchannel_return(struct ieee80211_local *local, continue; /* Tell AP we're back */ - if (sdata->vif.type == NL80211_IFTYPE_STATION && - sdata->u.mgd.associated) - ieee80211_offchannel_ps_disable(sdata); + if (sdata->vif.type == NL80211_IFTYPE_STATION) { + if (sdata->u.mgd.associated) + ieee80211_offchannel_ps_disable(sdata); + } if (sdata->vif.type != NL80211_IFTYPE_MONITOR) { clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); @@ -182,7 +182,7 @@ void ieee80211_offchannel_return(struct ieee80211_local *local, netif_tx_wake_all_queues(sdata->dev); } - /* Check to see if we should re-enable beaconing */ + /* re-enable beaconing */ if (enable_beaconing && (sdata->vif.type == NL80211_IFTYPE_AP || sdata->vif.type == NL80211_IFTYPE_ADHOC || diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 730778a..9ee7164 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -6,18 +6,43 @@ #include "driver-ops.h" #include "led.h" +/* return value indicates whether the driver should be further notified */ +static bool ieee80211_quiesce(struct ieee80211_sub_if_data *sdata) +{ + switch (sdata->vif.type) { + case NL80211_IFTYPE_STATION: + ieee80211_sta_quiesce(sdata); + return true; + case NL80211_IFTYPE_ADHOC: + ieee80211_ibss_quiesce(sdata); + return true; + case NL80211_IFTYPE_MESH_POINT: + ieee80211_mesh_quiesce(sdata); + return true; + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_MONITOR: + /* don't tell driver about this */ + return false; + default: + return true; + } +} + int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; struct sta_info *sta; + if (!local->open_count) + goto suspend; + ieee80211_scan_cancel(local); if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { - set_sta_flags(sta, WLAN_STA_BLOCK_BA); + set_sta_flag(sta, WLAN_STA_BLOCK_BA); ieee80211_sta_tear_down_BA_sessions(sta, true); } mutex_unlock(&local->sta_mtx); @@ -50,11 +75,19 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) local->wowlan = wowlan && local->open_count; if (local->wowlan) { int err = drv_suspend(local, wowlan); - if (err) { + if (err < 0) { local->quiescing = false; return err; + } else if (err > 0) { + WARN_ON(err != 1); + local->wowlan = false; + } else { + list_for_each_entry(sdata, &local->interfaces, list) { + cancel_work_sync(&sdata->work); + ieee80211_quiesce(sdata); + } + goto suspend; } - goto suspend; } /* disable keys */ @@ -82,23 +115,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) list_for_each_entry(sdata, &local->interfaces, list) { cancel_work_sync(&sdata->work); - switch(sdata->vif.type) { - case NL80211_IFTYPE_STATION: - ieee80211_sta_quiesce(sdata); - break; - case NL80211_IFTYPE_ADHOC: - ieee80211_ibss_quiesce(sdata); - break; - case NL80211_IFTYPE_MESH_POINT: - ieee80211_mesh_quiesce(sdata); - break; - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_MONITOR: - /* don't tell driver about this */ + if (!ieee80211_quiesce(sdata)) continue; - default: - break; - } if (!ieee80211_sdata_running(sdata)) continue; diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 816590b..7d84b87 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -11,6 +11,7 @@ #include <linux/kernel.h> #include <linux/rtnetlink.h> #include <linux/slab.h> +#include <linux/module.h> #include "rate.h" #include "ieee80211_i.h" #include "debugfs.h" @@ -199,7 +200,7 @@ static void rate_control_release(struct kref *kref) kfree(ctrl_ref); } -static bool rc_no_data_or_no_ack(struct ieee80211_tx_rate_control *txrc) +static bool rc_no_data_or_no_ack_use_min(struct ieee80211_tx_rate_control *txrc) { struct sk_buff *skb = txrc->skb; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; @@ -208,7 +209,9 @@ static bool rc_no_data_or_no_ack(struct ieee80211_tx_rate_control *txrc) fc = hdr->frame_control; - return (info->flags & IEEE80211_TX_CTL_NO_ACK) || !ieee80211_is_data(fc); + return (info->flags & (IEEE80211_TX_CTL_NO_ACK | + IEEE80211_TX_CTL_USE_MINRATE)) || + !ieee80211_is_data(fc); } static void rc_send_low_broadcast(s8 *idx, u32 basic_rates, @@ -233,6 +236,27 @@ static void rc_send_low_broadcast(s8 *idx, u32 basic_rates, /* could not find a basic rate; use original selection */ } +static inline s8 +rate_lowest_non_cck_index(struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta) +{ + int i; + + for (i = 0; i < sband->n_bitrates; i++) { + struct ieee80211_rate *srate = &sband->bitrates[i]; + if ((srate->bitrate == 10) || (srate->bitrate == 20) || + (srate->bitrate == 55) || (srate->bitrate == 110)) + continue; + + if (rate_supported(sta, sband->band, i)) + return i; + } + + /* No matching rate found */ + return 0; +} + + bool rate_control_send_low(struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_tx_rate_control *txrc) @@ -241,8 +265,14 @@ bool rate_control_send_low(struct ieee80211_sta *sta, struct ieee80211_supported_band *sband = txrc->sband; int mcast_rate; - if (!sta || !priv_sta || rc_no_data_or_no_ack(txrc)) { - info->control.rates[0].idx = rate_lowest_index(txrc->sband, sta); + if (!sta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) { + if ((sband->band != IEEE80211_BAND_2GHZ) || + !(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)) + info->control.rates[0].idx = + rate_lowest_index(txrc->sband, sta); + else + info->control.rates[0].idx = + rate_lowest_non_cck_index(txrc->sband, sta); info->control.rates[0].count = (info->flags & IEEE80211_TX_CTL_NO_ACK) ? 1 : txrc->hw->max_rate_tries; diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 8adac67..58a8955 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -532,12 +532,21 @@ minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) mp->hw = hw; mp->update_interval = 100; +#ifdef CONFIG_MAC80211_DEBUGFS + mp->fixed_rate_idx = (u32) -1; + mp->dbg_fixed_rate = debugfs_create_u32("fixed_rate_idx", + S_IRUGO | S_IWUGO, debugfsdir, &mp->fixed_rate_idx); +#endif + return mp; } static void minstrel_free(void *priv) { +#ifdef CONFIG_MAC80211_DEBUGFS + debugfs_remove(((struct minstrel_priv *)priv)->dbg_fixed_rate); +#endif kfree(priv); } diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h index 0f5a833..5d278ec 100644 --- a/net/mac80211/rc80211_minstrel.h +++ b/net/mac80211/rc80211_minstrel.h @@ -78,6 +78,18 @@ struct minstrel_priv { unsigned int update_interval; unsigned int lookaround_rate; unsigned int lookaround_rate_mrr; + +#ifdef CONFIG_MAC80211_DEBUGFS + /* + * enable fixed rate processing per RC + * - write static index to debugfs:ieee80211/phyX/rc/fixed_rate_idx + * - write -1 to enable RC processing again + * - setting will be applied on next update + */ + u32 fixed_rate_idx; + struct dentry *dbg_fixed_rate; +#endif + }; struct minstrel_debugfs_info { diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c index a290ad2..d5a5622 100644 --- a/net/mac80211/rc80211_minstrel_debugfs.c +++ b/net/mac80211/rc80211_minstrel_debugfs.c @@ -50,6 +50,7 @@ #include <linux/debugfs.h> #include <linux/ieee80211.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/mac80211.h> #include "rc80211_minstrel.h" diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 333b511..cdb2853 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -281,6 +281,8 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) mr = minstrel_get_ratestats(mi, mg->max_tp_rate); if (cur_tp < mr->cur_tp) { + mi->max_tp_rate2 = mi->max_tp_rate; + cur_tp2 = cur_tp; mi->max_tp_rate = mg->max_tp_rate; cur_tp = mr->cur_tp; } @@ -452,7 +454,8 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) { minstrel_ht_update_stats(mp, mi); - minstrel_aggr_check(mp, sta, skb); + if (!(info->flags & IEEE80211_TX_CTL_AMPDU)) + minstrel_aggr_check(mp, sta, skb); } } @@ -608,7 +611,20 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, return mac80211_minstrel.get_rate(priv, sta, &msp->legacy, txrc); info->flags |= mi->tx_flags; - sample_idx = minstrel_get_sample_rate(mp, mi); + + /* Don't use EAPOL frames for sampling on non-mrr hw */ + if (mp->hw->max_rates == 1 && + txrc->skb->protocol == cpu_to_be16(ETH_P_PAE)) + sample_idx = -1; + else + sample_idx = minstrel_get_sample_rate(mp, mi); + +#ifdef CONFIG_MAC80211_DEBUGFS + /* use fixed index if set */ + if (mp->fixed_rate_idx != -1) + sample_idx = mp->fixed_rate_idx; +#endif + if (sample_idx >= 0) { sample = true; minstrel_ht_set_rate(mp, mi, &ar[0], sample_idx, diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c index cefcb5d..e788f76 100644 --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c @@ -10,6 +10,7 @@ #include <linux/skbuff.h> #include <linux/debugfs.h> #include <linux/ieee80211.h> +#include <linux/export.h> #include <net/mac80211.h> #include "rc80211_minstrel.h" #include "rc80211_minstrel_ht.h" diff --git a/net/mac80211/rc80211_pid_debugfs.c b/net/mac80211/rc80211_pid_debugfs.c index 4851e9e..c97a065 100644 --- a/net/mac80211/rc80211_pid_debugfs.c +++ b/net/mac80211/rc80211_pid_debugfs.c @@ -13,6 +13,7 @@ #include <linux/types.h> #include <linux/skbuff.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/mac80211.h> #include "rate.h" diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 10e8842..7c53eff 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -16,6 +16,7 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/rcupdate.h> +#include <linux/export.h> #include <net/mac80211.h> #include <net/ieee80211_radiotap.h> @@ -334,15 +335,18 @@ static void ieee80211_parse_qos(struct ieee80211_rx_data *rx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); - int tid; + int tid, seqno_idx, security_idx; /* does the frame have a qos control field? */ if (ieee80211_is_data_qos(hdr->frame_control)) { u8 *qc = ieee80211_get_qos_ctl(hdr); /* frame has qos control */ tid = *qc & IEEE80211_QOS_CTL_TID_MASK; - if (*qc & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT) + if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT) status->rx_flags |= IEEE80211_RX_AMSDU; + + seqno_idx = tid; + security_idx = tid; } else { /* * IEEE 802.11-2007, 7.1.3.4.1 ("Sequence Number field"): @@ -355,10 +359,15 @@ static void ieee80211_parse_qos(struct ieee80211_rx_data *rx) * * We also use that counter for non-QoS STAs. */ - tid = NUM_RX_DATA_QUEUES - 1; + seqno_idx = NUM_RX_DATA_QUEUES; + security_idx = 0; + if (ieee80211_is_mgmt(hdr->frame_control)) + security_idx = NUM_RX_DATA_QUEUES; + tid = 0; } - rx->queue = tid; + rx->seqno_idx = seqno_idx; + rx->security_idx = security_idx; /* Set skb->priority to 1d tag if highest order bit of TID is not set. * For now, set skb->priority to 0 for other cases. */ rx->skb->priority = (tid > 7) ? 0 : tid; @@ -412,10 +421,16 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx) return RX_CONTINUE; if (test_bit(SCAN_HW_SCANNING, &local->scanning) || - test_bit(SCAN_SW_SCANNING, &local->scanning) || local->sched_scanning) return ieee80211_scan_rx(rx->sdata, skb); + if (test_bit(SCAN_SW_SCANNING, &local->scanning)) { + /* drop all the other packets during a software scan anyway */ + if (ieee80211_scan_rx(rx->sdata, skb) != RX_QUEUED) + dev_kfree_skb(skb); + return RX_QUEUED; + } + /* scanning finished during invoking of handlers */ I802_DEBUG_INC(local->rx_handlers_drop_passive_scan); return RX_DROP_UNUSABLE; @@ -471,7 +486,6 @@ static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; - unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control); char *dev_addr = rx->sdata->vif.addr; if (ieee80211_is_data(hdr->frame_control)) { @@ -501,6 +515,11 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) if (ieee80211_is_action(hdr->frame_control)) { u8 category; + + /* make sure category field is present */ + if (rx->skb->len < IEEE80211_MIN_ACTION_SIZE) + return RX_DROP_MONITOR; + mgmt = (struct ieee80211_mgmt *)hdr; category = mgmt->u.action.category; if (category != WLAN_CATEGORY_MESH_ACTION && @@ -519,14 +538,6 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) } -#define msh_h_get(h, l) ((struct ieee80211s_hdr *) ((u8 *)h + l)) - - if (ieee80211_is_data(hdr->frame_control) && - is_multicast_ether_addr(hdr->addr1) && - mesh_rmc_check(hdr->addr3, msh_h_get(hdr, hdrlen), rx->sdata)) - return RX_DROP_MONITOR; -#undef msh_h_get - return RX_CONTINUE; } @@ -659,9 +670,10 @@ static void ieee80211_sta_reorder_release(struct ieee80211_hw *hw, set_release_timer: - mod_timer(&tid_agg_rx->reorder_timer, - tid_agg_rx->reorder_time[j] + 1 + - HT_RX_REORDER_BUF_TIMEOUT); + if (!tid_agg_rx->removed) + mod_timer(&tid_agg_rx->reorder_timer, + tid_agg_rx->reorder_time[j] + 1 + + HT_RX_REORDER_BUF_TIMEOUT); } else { del_timer(&tid_agg_rx->reorder_timer); } @@ -753,7 +765,8 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx) u16 sc; int tid; - if (!ieee80211_is_data_qos(hdr->frame_control)) + if (!ieee80211_is_data_qos(hdr->frame_control) || + is_multicast_ether_addr(hdr->addr1)) goto dont_reorder; /* @@ -819,7 +832,7 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx) !ieee80211_is_qos_nullfunc(hdr->frame_control) && !is_multicast_ether_addr(hdr->addr1)) { if (unlikely(ieee80211_has_retry(hdr->frame_control) && - rx->sta->last_seq_ctrl[rx->queue] == + rx->sta->last_seq_ctrl[rx->seqno_idx] == hdr->seq_ctrl)) { if (status->rx_flags & IEEE80211_RX_RA_MATCH) { rx->local->dot11FrameDuplicateCount++; @@ -827,7 +840,7 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx) } return RX_DROP_UNUSABLE; } else - rx->sta->last_seq_ctrl[rx->queue] = hdr->seq_ctrl; + rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl; } if (unlikely(rx->skb->len < 16)) { @@ -851,8 +864,23 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx) ieee80211_is_pspoll(hdr->frame_control)) && rx->sdata->vif.type != NL80211_IFTYPE_ADHOC && rx->sdata->vif.type != NL80211_IFTYPE_WDS && - (!rx->sta || !test_sta_flags(rx->sta, WLAN_STA_ASSOC)))) + (!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))) { + if (rx->sta && rx->sta->dummy && + ieee80211_is_data_present(hdr->frame_control)) { + unsigned int hdrlen; + __be16 ethertype; + + hdrlen = ieee80211_hdrlen(hdr->frame_control); + + if (rx->skb->len < hdrlen + 8) + return RX_DROP_MONITOR; + + skb_copy_bits(rx->skb, hdrlen + 6, ðertype, 2); + if (ethertype == rx->sdata->control_port_protocol) + return RX_CONTINUE; + } return RX_DROP_MONITOR; + } return RX_CONTINUE; } @@ -1020,6 +1048,9 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) } if (rx->key) { + if (unlikely(rx->key->flags & KEY_FLAG_TAINTED)) + return RX_DROP_MONITOR; + rx->key->tx_rx_count++; /* TODO: add threshold stuff again */ } else { @@ -1104,7 +1135,7 @@ static void ap_sta_ps_start(struct sta_info *sta) struct ieee80211_local *local = sdata->local; atomic_inc(&sdata->bss->num_sta_ps); - set_sta_flags(sta, WLAN_STA_PS_STA); + set_sta_flag(sta, WLAN_STA_PS_STA); if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta); #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG @@ -1124,7 +1155,7 @@ static void ap_sta_ps_end(struct sta_info *sta) sdata->name, sta->sta.addr, sta->sta.aid); #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ - if (test_sta_flags(sta, WLAN_STA_PS_DRIVER)) { + if (test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG printk(KERN_DEBUG "%s: STA %pM aid %d driver-ps-blocked\n", sdata->name, sta->sta.addr, sta->sta.aid); @@ -1143,7 +1174,7 @@ int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start) WARN_ON(!(sta_inf->local->hw.flags & IEEE80211_HW_AP_LINK_PS)); /* Don't let the same PS state be set twice */ - in_ps = test_sta_flags(sta_inf, WLAN_STA_PS_STA); + in_ps = test_sta_flag(sta_inf, WLAN_STA_PS_STA); if ((start && in_ps) || (!start && !in_ps)) return -EINVAL; @@ -1157,6 +1188,81 @@ int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start) EXPORT_SYMBOL(ieee80211_sta_ps_transition); static ieee80211_rx_result debug_noinline +ieee80211_rx_h_uapsd_and_pspoll(struct ieee80211_rx_data *rx) +{ + struct ieee80211_sub_if_data *sdata = rx->sdata; + struct ieee80211_hdr *hdr = (void *)rx->skb->data; + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); + int tid, ac; + + if (!rx->sta || !(status->rx_flags & IEEE80211_RX_RA_MATCH)) + return RX_CONTINUE; + + if (sdata->vif.type != NL80211_IFTYPE_AP && + sdata->vif.type != NL80211_IFTYPE_AP_VLAN) + return RX_CONTINUE; + + /* + * The device handles station powersave, so don't do anything about + * uAPSD and PS-Poll frames (the latter shouldn't even come up from + * it to mac80211 since they're handled.) + */ + if (sdata->local->hw.flags & IEEE80211_HW_AP_LINK_PS) + return RX_CONTINUE; + + /* + * Don't do anything if the station isn't already asleep. In + * the uAPSD case, the station will probably be marked asleep, + * in the PS-Poll case the station must be confused ... + */ + if (!test_sta_flag(rx->sta, WLAN_STA_PS_STA)) + return RX_CONTINUE; + + if (unlikely(ieee80211_is_pspoll(hdr->frame_control))) { + if (!test_sta_flag(rx->sta, WLAN_STA_SP)) { + if (!test_sta_flag(rx->sta, WLAN_STA_PS_DRIVER)) + ieee80211_sta_ps_deliver_poll_response(rx->sta); + else + set_sta_flag(rx->sta, WLAN_STA_PSPOLL); + } + + /* Free PS Poll skb here instead of returning RX_DROP that would + * count as an dropped frame. */ + dev_kfree_skb(rx->skb); + + return RX_QUEUED; + } else if (!ieee80211_has_morefrags(hdr->frame_control) && + !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) && + ieee80211_has_pm(hdr->frame_control) && + (ieee80211_is_data_qos(hdr->frame_control) || + ieee80211_is_qos_nullfunc(hdr->frame_control))) { + tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; + ac = ieee802_1d_to_ac[tid & 7]; + + /* + * If this AC is not trigger-enabled do nothing. + * + * NB: This could/should check a separate bitmap of trigger- + * enabled queues, but for now we only implement uAPSD w/o + * TSPEC changes to the ACs, so they're always the same. + */ + if (!(rx->sta->sta.uapsd_queues & BIT(ac))) + return RX_CONTINUE; + + /* if we are in a service period, do nothing */ + if (test_sta_flag(rx->sta, WLAN_STA_SP)) + return RX_CONTINUE; + + if (!test_sta_flag(rx->sta, WLAN_STA_PS_DRIVER)) + ieee80211_sta_ps_deliver_uapsd(rx->sta); + else + set_sta_flag(rx->sta, WLAN_STA_UAPSD); + } + + return RX_CONTINUE; +} + +static ieee80211_rx_result debug_noinline ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) { struct sta_info *sta = rx->sta; @@ -1214,7 +1320,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) && (rx->sdata->vif.type == NL80211_IFTYPE_AP || rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) { - if (test_sta_flags(sta, WLAN_STA_PS_STA)) { + if (test_sta_flag(sta, WLAN_STA_PS_STA)) { /* * Ignore doze->wake transitions that are * indicated by non-data frames, the standard @@ -1365,11 +1471,14 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) sc = le16_to_cpu(hdr->seq_ctrl); frag = sc & IEEE80211_SCTL_FRAG; - if (likely((!ieee80211_has_morefrags(fc) && frag == 0) || - is_multicast_ether_addr(hdr->addr1))) { - /* not fragmented */ - goto out; + if (is_multicast_ether_addr(hdr->addr1)) { + rx->local->dot11MulticastReceivedFrameCount++; + goto out_no_led; } + + if (likely(!ieee80211_has_morefrags(fc) && frag == 0)) + goto out; + I802_DEBUG_INC(rx->local->rx_handlers_fragments); if (skb_linearize(rx->skb)) @@ -1386,11 +1495,10 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) if (frag == 0) { /* This is the first fragment of a new frame. */ entry = ieee80211_reassemble_add(rx->sdata, frag, seq, - rx->queue, &(rx->skb)); + rx->seqno_idx, &(rx->skb)); if (rx->key && rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP && ieee80211_has_protected(fc)) { - int queue = ieee80211_is_mgmt(fc) ? - NUM_RX_DATA_QUEUES : rx->queue; + int queue = rx->security_idx; /* Store CCMP PN so that we can verify that the next * fragment has a sequential PN value. */ entry->ccmp = 1; @@ -1404,7 +1512,8 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) /* 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, frag, seq, rx->queue, hdr); + entry = ieee80211_reassemble_find(rx->sdata, frag, seq, + rx->seqno_idx, hdr); if (!entry) { I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag); return RX_DROP_MONITOR; @@ -1424,8 +1533,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) if (pn[i]) break; } - queue = ieee80211_is_mgmt(fc) ? - NUM_RX_DATA_QUEUES : rx->queue; + queue = rx->security_idx; rpn = rx->key->u.ccmp.rx_pn[queue]; if (memcmp(pn, rpn, CCMP_PN_LEN)) return RX_DROP_UNUSABLE; @@ -1461,43 +1569,14 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) status->rx_flags |= IEEE80211_RX_FRAGMENTED; out: + ieee80211_led_rx(rx->local); + out_no_led: if (rx->sta) rx->sta->rx_packets++; - if (is_multicast_ether_addr(hdr->addr1)) - rx->local->dot11MulticastReceivedFrameCount++; - else - ieee80211_led_rx(rx->local); return RX_CONTINUE; } static ieee80211_rx_result debug_noinline -ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx) -{ - struct ieee80211_sub_if_data *sdata = rx->sdata; - __le16 fc = ((struct ieee80211_hdr *)rx->skb->data)->frame_control; - struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); - - if (likely(!rx->sta || !ieee80211_is_pspoll(fc) || - !(status->rx_flags & IEEE80211_RX_RA_MATCH))) - return RX_CONTINUE; - - if ((sdata->vif.type != NL80211_IFTYPE_AP) && - (sdata->vif.type != NL80211_IFTYPE_AP_VLAN)) - return RX_DROP_UNUSABLE; - - if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER)) - ieee80211_sta_ps_deliver_poll_response(rx->sta); - else - set_sta_flags(rx->sta, WLAN_STA_PSPOLL); - - /* Free PS Poll skb here instead of returning RX_DROP that would - * count as an dropped frame. */ - dev_kfree_skb(rx->skb); - - return RX_QUEUED; -} - -static ieee80211_rx_result debug_noinline ieee80211_rx_h_remove_qos_control(struct ieee80211_rx_data *rx) { u8 *data = rx->skb->data; @@ -1520,7 +1599,7 @@ static int ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx) { if (unlikely(!rx->sta || - !test_sta_flags(rx->sta, WLAN_STA_AUTHORIZED))) + !test_sta_flag(rx->sta, WLAN_STA_AUTHORIZED))) return -EACCES; return 0; @@ -1563,7 +1642,7 @@ ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx) if (status->flag & RX_FLAG_DECRYPTED) return 0; - if (rx->sta && test_sta_flags(rx->sta, WLAN_STA_MFP)) { + if (rx->sta && test_sta_flag(rx->sta, WLAN_STA_MFP)) { if (unlikely(!ieee80211_has_protected(fc) && ieee80211_is_unicast_robust_mgmt_frame(rx->skb) && rx->key)) { @@ -1827,15 +1906,45 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) hdr = (struct ieee80211_hdr *) skb->data; hdrlen = ieee80211_hdrlen(hdr->frame_control); + + /* make sure fixed part of mesh header is there, also checks skb len */ + if (!pskb_may_pull(rx->skb, hdrlen + 6)) + return RX_DROP_MONITOR; + mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); - if (!ieee80211_is_data(hdr->frame_control)) + /* make sure full mesh header is there, also checks skb len */ + if (!pskb_may_pull(rx->skb, + hdrlen + ieee80211_get_mesh_hdrlen(mesh_hdr))) + return RX_DROP_MONITOR; + + /* reload pointers */ + hdr = (struct ieee80211_hdr *) skb->data; + mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); + + if (ieee80211_drop_unencrypted(rx, hdr->frame_control)) + return RX_DROP_MONITOR; + + /* frame is in RMC, don't forward */ + if (ieee80211_is_data(hdr->frame_control) && + is_multicast_ether_addr(hdr->addr1) && + mesh_rmc_check(hdr->addr3, mesh_hdr, rx->sdata)) + return RX_DROP_MONITOR; + + if (!ieee80211_is_data(hdr->frame_control) || + !(status->rx_flags & IEEE80211_RX_RA_MATCH)) return RX_CONTINUE; if (!mesh_hdr->ttl) /* illegal frame */ return RX_DROP_MONITOR; + if (ieee80211_queue_stopped(&local->hw, skb_get_queue_mapping(skb))) { + IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, + dropped_frames_congestion); + return RX_DROP_MONITOR; + } + if (mesh_hdr->flags & MESH_FLAGS_AE) { struct mesh_path *mppath; char *proxied_addr; @@ -1844,9 +1953,12 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) if (is_multicast_ether_addr(hdr->addr1)) { mpp_addr = hdr->addr3; proxied_addr = mesh_hdr->eaddr1; - } else { + } else if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6) { + /* has_a4 already checked in ieee80211_rx_mesh_check */ mpp_addr = hdr->addr4; proxied_addr = mesh_hdr->eaddr2; + } else { + return RX_DROP_MONITOR; } rcu_read_lock(); @@ -1869,7 +1981,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) mesh_hdr->ttl--; - if (status->rx_flags & IEEE80211_RX_RA_MATCH) { + { if (!mesh_hdr->ttl) IEEE80211_IFSTA_MESH_CTR_INC(&rx->sdata->u.mesh, dropped_frames_ttl); @@ -1891,13 +2003,13 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) memset(info, 0, sizeof(*info)); info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; info->control.vif = &rx->sdata->vif; - skb_set_queue_mapping(skb, - ieee80211_select_queue(rx->sdata, fwd_skb)); - ieee80211_set_qos_hdr(local, skb); - if (is_multicast_ether_addr(fwd_hdr->addr1)) + if (is_multicast_ether_addr(fwd_hdr->addr1)) { IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, fwded_mcast); - else { + skb_set_queue_mapping(fwd_skb, + ieee80211_select_queue(sdata, fwd_skb)); + ieee80211_set_qos_hdr(sdata, fwd_skb); + } else { int err; /* * Save TA to addr1 to send TA a path error if a @@ -2222,12 +2334,37 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) goto handled; } break; + case WLAN_CATEGORY_SELF_PROTECTED: + if (len < (IEEE80211_MIN_ACTION_SIZE + + sizeof(mgmt->u.action.u.self_prot.action_code))) + break; + + switch (mgmt->u.action.u.self_prot.action_code) { + case WLAN_SP_MESH_PEERING_OPEN: + case WLAN_SP_MESH_PEERING_CLOSE: + case WLAN_SP_MESH_PEERING_CONFIRM: + if (!ieee80211_vif_is_mesh(&sdata->vif)) + goto invalid; + if (sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE) + /* userspace handles this frame */ + break; + goto queue; + case WLAN_SP_MGK_INFORM: + case WLAN_SP_MGK_ACK: + if (!ieee80211_vif_is_mesh(&sdata->vif)) + goto invalid; + break; + } + break; case WLAN_CATEGORY_MESH_ACTION: + if (len < (IEEE80211_MIN_ACTION_SIZE + + sizeof(mgmt->u.action.u.mesh_action.action_code))) + break; + if (!ieee80211_vif_is_mesh(&sdata->vif)) break; - goto queue; - case WLAN_CATEGORY_MESH_PATH_SEL: - if (!mesh_path_sel_is_hwmp(sdata)) + if (mesh_action_is_path_sel(mgmt) && + (!mesh_path_sel_is_hwmp(sdata))) break; goto queue; } @@ -2539,17 +2676,17 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx) CALL_RXH(ieee80211_rx_h_decrypt) CALL_RXH(ieee80211_rx_h_check_more_data) + CALL_RXH(ieee80211_rx_h_uapsd_and_pspoll) CALL_RXH(ieee80211_rx_h_sta_process) CALL_RXH(ieee80211_rx_h_defragment) - CALL_RXH(ieee80211_rx_h_ps_poll) CALL_RXH(ieee80211_rx_h_michael_mic_verify) /* must be after MMIC verify so header is counted in MPDU mic */ - CALL_RXH(ieee80211_rx_h_remove_qos_control) - CALL_RXH(ieee80211_rx_h_amsdu) #ifdef CONFIG_MAC80211_MESH if (ieee80211_vif_is_mesh(&rx->sdata->vif)) CALL_RXH(ieee80211_rx_h_mesh_fwding); #endif + CALL_RXH(ieee80211_rx_h_remove_qos_control) + CALL_RXH(ieee80211_rx_h_amsdu) CALL_RXH(ieee80211_rx_h_data) CALL_RXH(ieee80211_rx_h_ctrl); CALL_RXH(ieee80211_rx_h_mgmt_check) @@ -2605,7 +2742,9 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid) .sta = sta, .sdata = sta->sdata, .local = sta->local, - .queue = tid, + /* This is OK -- must be QoS data frame */ + .security_idx = tid, + .seqno_idx = tid, .flags = 0, }; struct tid_ampdu_rx *tid_agg_rx; @@ -2647,6 +2786,9 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx, case NL80211_IFTYPE_ADHOC: if (!bssid) return 0; + if (compare_ether_addr(sdata->vif.addr, hdr->addr2) == 0 || + compare_ether_addr(sdata->u.ibss.bssid, hdr->addr2) == 0) + return 0; if (ieee80211_is_beacon(hdr->frame_control)) { return 1; } @@ -2689,7 +2831,9 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx, } else if (!ieee80211_bssid_match(bssid, sdata->vif.addr)) { if (!(status->rx_flags & IEEE80211_RX_IN_SCAN) && - !ieee80211_is_beacon(hdr->frame_control)) + !ieee80211_is_beacon(hdr->frame_control) && + !(ieee80211_is_action(hdr->frame_control) && + sdata->vif.p2p)) return 0; status->rx_flags &= ~IEEE80211_RX_RA_MATCH; } @@ -2774,7 +2918,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, local->dot11ReceivedFragmentCount++; if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning) || - test_bit(SCAN_SW_SCANNING, &local->scanning))) + test_bit(SCAN_OFF_CHANNEL, &local->scanning))) status->rx_flags |= IEEE80211_RX_IN_SCAN; if (ieee80211_is_mgmt(fc)) { @@ -2799,7 +2943,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, if (ieee80211_is_data(fc)) { prev_sta = NULL; - for_each_sta_info(local, hdr->addr2, sta, tmp) { + for_each_sta_info_rx(local, hdr->addr2, sta, tmp) { if (!prev_sta) { prev_sta = sta; continue; @@ -2843,7 +2987,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, continue; } - rx.sta = sta_info_get_bss(prev, hdr->addr2); + rx.sta = sta_info_get_bss_rx(prev, hdr->addr2); rx.sdata = prev; ieee80211_prepare_and_rx_handle(&rx, skb, false); @@ -2851,7 +2995,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, } if (prev) { - rx.sta = sta_info_get_bss(prev, hdr->addr2); + rx.sta = sta_info_get_bss_rx(prev, hdr->addr2); rx.sdata = prev; if (ieee80211_prepare_and_rx_handle(&rx, skb, true)) diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 7c75741..0aeea49 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -14,9 +14,10 @@ #include <linux/if_arp.h> #include <linux/rtnetlink.h> -#include <linux/pm_qos_params.h> +#include <linux/pm_qos.h> #include <net/sch_generic.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/mac80211.h> #include "ieee80211_i.h" @@ -212,14 +213,6 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) if (bss) ieee80211_rx_bss_put(sdata->local, bss); - /* If we are on-operating-channel, and this packet is for the - * current channel, pass the pkt on up the stack so that - * the rest of the stack can make use of it. - */ - if (ieee80211_cfg_on_oper_channel(sdata->local) - && (channel == sdata->local->oper_channel)) - return RX_CONTINUE; - dev_kfree_skb(skb); return RX_QUEUED; } @@ -231,6 +224,9 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) enum ieee80211_band band; int i, ielen, n_chans; + if (test_bit(SCAN_HW_CANCELLED, &local->scanning)) + return false; + do { if (local->hw_scan_band == IEEE80211_NUM_BANDS) return false; @@ -251,9 +247,10 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) local->hw_scan_req->n_channels = n_chans; ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie, - req->ie, req->ie_len, band, (u32) -1, - 0); + req->ie, req->ie_len, band, + req->rates[band], 0); local->hw_scan_req->ie_len = ielen; + local->hw_scan_req->no_cck = req->no_cck; return true; } @@ -262,8 +259,6 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, bool was_hw_scan) { struct ieee80211_local *local = hw_to_local(hw); - bool on_oper_chan; - bool enable_beacons = false; lockdep_assert_held(&local->mtx); @@ -296,25 +291,11 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, local->scanning = 0; local->scan_channel = NULL; - on_oper_chan = ieee80211_cfg_on_oper_channel(local); - - if (was_hw_scan || !on_oper_chan) - ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); - else - /* Set power back to normal operating levels. */ - ieee80211_hw_config(local, 0); - + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); if (!was_hw_scan) { - bool on_oper_chan2; ieee80211_configure_filter(local); drv_sw_scan_complete(local); - on_oper_chan2 = ieee80211_cfg_on_oper_channel(local); - /* We should always be on-channel at this point. */ - WARN_ON(!on_oper_chan2); - if (on_oper_chan2 && (on_oper_chan != on_oper_chan2)) - enable_beacons = true; - - ieee80211_offchannel_return(local, enable_beacons); + ieee80211_offchannel_return(local, true); } ieee80211_recalc_idle(local); @@ -355,15 +336,13 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) */ drv_sw_scan_start(local); + ieee80211_offchannel_stop_beaconing(local); + local->leave_oper_channel_time = 0; local->next_scan_state = SCAN_DECISION; local->scan_channel_idx = 0; - /* We always want to use off-channel PS, even if we - * are not really leaving oper-channel. Don't - * tell the AP though, as long as we are on-channel. - */ - ieee80211_offchannel_enable_all_ps(local, false); + drv_flush(local, false); ieee80211_configure_filter(local); @@ -506,20 +485,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, } mutex_unlock(&local->iflist_mtx); - next_chan = local->scan_req->channels[local->scan_channel_idx]; - - if (ieee80211_cfg_on_oper_channel(local)) { - /* We're currently on operating channel. */ - if (next_chan == local->oper_channel) - /* We don't need to move off of operating channel. */ - local->next_scan_state = SCAN_SET_CHANNEL; - else - /* - * We do need to leave operating channel, as next - * scan is somewhere else. - */ - local->next_scan_state = SCAN_LEAVE_OPER_CHANNEL; - } else { + if (local->scan_channel) { /* * we're currently scanning a different channel, let's * see if we can scan another channel without interfering @@ -535,6 +501,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, * * Otherwise switch back to the operating channel. */ + next_chan = local->scan_req->channels[local->scan_channel_idx]; bad_latency = time_after(jiffies + ieee80211_scan_get_channel_time(next_chan), @@ -552,6 +519,12 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, local->next_scan_state = SCAN_ENTER_OPER_CHANNEL; else local->next_scan_state = SCAN_SET_CHANNEL; + } else { + /* + * we're on the operating channel currently, let's + * leave that channel now to scan another one + */ + local->next_scan_state = SCAN_LEAVE_OPER_CHANNEL; } *next_delay = 0; @@ -560,10 +533,9 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, unsigned long *next_delay) { - /* PS will already be in off-channel mode, - * we do that once at the beginning of scanning. - */ - ieee80211_offchannel_stop_vifs(local); + ieee80211_offchannel_stop_station(local); + + __set_bit(SCAN_OFF_CHANNEL, &local->scanning); /* * What if the nullfunc frames didn't arrive? @@ -586,15 +558,15 @@ static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *loca { /* switch back to the operating channel */ local->scan_channel = NULL; - if (!ieee80211_cfg_on_oper_channel(local)) - ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); /* - * Re-enable vifs and beaconing. Leave PS - * in off-channel state..will put that back - * on-channel at the end of scanning. + * Only re-enable station mode interface now; beaconing will be + * re-enabled once the full scan has been completed. */ - ieee80211_offchannel_return(local, true); + ieee80211_offchannel_return(local, false); + + __clear_bit(SCAN_OFF_CHANNEL, &local->scanning); *next_delay = HZ / 5; local->next_scan_state = SCAN_DECISION; @@ -652,13 +624,16 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, { int i; struct ieee80211_sub_if_data *sdata = local->scan_sdata; + enum ieee80211_band band = local->hw.conf.channel->band; for (i = 0; i < local->scan_req->n_ssids; i++) ieee80211_send_probe_req( sdata, NULL, local->scan_req->ssids[i].ssid, local->scan_req->ssids[i].ssid_len, - local->scan_req->ie, local->scan_req->ie_len); + local->scan_req->ie, local->scan_req->ie_len, + local->scan_req->rates[band], false, + local->scan_req->no_cck); /* * After sending probe requests, wait for probe responses @@ -821,10 +796,8 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, */ void ieee80211_scan_cancel(struct ieee80211_local *local) { - bool abortscan; - /* - * We are only canceling software scan, or deferred scan that was not + * We are canceling software scan, or deferred scan that was not * yet really started (see __ieee80211_start_scan ). * * Regarding hardware scan: @@ -836,23 +809,46 @@ void ieee80211_scan_cancel(struct ieee80211_local *local) * - we can not cancel scan_work since driver can schedule it * by ieee80211_scan_completed(..., true) to finish scan * - * Hence low lever driver is responsible for canceling HW scan. + * Hence we only call the cancel_hw_scan() callback, but the low-level + * driver is still responsible for calling ieee80211_scan_completed() + * after the scan was completed/aborted. */ mutex_lock(&local->mtx); - abortscan = local->scan_req && !test_bit(SCAN_HW_SCANNING, &local->scanning); - if (abortscan) { + if (!local->scan_req) + goto out; + + /* + * We have a scan running and the driver already reported completion, + * but the worker hasn't run yet or is stuck on the mutex - mark it as + * cancelled. + */ + if (test_bit(SCAN_HW_SCANNING, &local->scanning) && + test_bit(SCAN_COMPLETED, &local->scanning)) { + set_bit(SCAN_HW_CANCELLED, &local->scanning); + goto out; + } + + if (test_bit(SCAN_HW_SCANNING, &local->scanning)) { /* - * The scan is canceled, but stop work from being pending. - * - * If the work is currently running, it must be blocked on - * the mutex, but we'll set scan_sdata = NULL and it'll - * simply exit once it acquires the mutex. + * Make sure that __ieee80211_scan_completed doesn't trigger a + * scan on another band. */ - cancel_delayed_work(&local->scan_work); - /* and clean up */ - __ieee80211_scan_completed(&local->hw, true, false); + set_bit(SCAN_HW_CANCELLED, &local->scanning); + if (local->ops->cancel_hw_scan) + drv_cancel_hw_scan(local, local->scan_sdata); + goto out; } + + /* + * If the work is currently running, it must be blocked on + * the mutex, but we'll set scan_sdata = NULL and it'll + * simply exit once it acquires the mutex. + */ + cancel_delayed_work(&local->scan_work); + /* and clean up */ + __ieee80211_scan_completed(&local->hw, true, false); +out: mutex_unlock(&local->mtx); } diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index 7733f66..578eea3 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -32,12 +32,8 @@ static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_da skb = dev_alloc_skb(sizeof(*msr_report) + local->hw.extra_tx_headroom + sizeof(struct ieee80211_msrment_ie)); - - if (!skb) { - printk(KERN_ERR "%s: failed to allocate buffer for " - "measurement report frame\n", sdata->name); + if (!skb) return; - } skb_reserve(skb, local->hw.extra_tx_headroom); msr_report = (struct ieee80211_mgmt *)skb_put(skb, 24); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 065a971..1914f5a 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -24,6 +24,7 @@ #include "sta_info.h" #include "debugfs_sta.h" #include "mesh.h" +#include "wme.h" /** * DOC: STA information lifetime rules @@ -97,7 +98,27 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, struct sta_info *sta; sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], - rcu_read_lock_held() || + lockdep_is_held(&local->sta_lock) || + lockdep_is_held(&local->sta_mtx)); + while (sta) { + if (sta->sdata == sdata && !sta->dummy && + memcmp(sta->sta.addr, addr, ETH_ALEN) == 0) + break; + sta = rcu_dereference_check(sta->hnext, + lockdep_is_held(&local->sta_lock) || + lockdep_is_held(&local->sta_mtx)); + } + return sta; +} + +/* get a station info entry even if it is a dummy station*/ +struct sta_info *sta_info_get_rx(struct ieee80211_sub_if_data *sdata, + const u8 *addr) +{ + struct ieee80211_local *local = sdata->local; + struct sta_info *sta; + + sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], lockdep_is_held(&local->sta_lock) || lockdep_is_held(&local->sta_mtx)); while (sta) { @@ -105,7 +126,6 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, memcmp(sta->sta.addr, addr, ETH_ALEN) == 0) break; sta = rcu_dereference_check(sta->hnext, - rcu_read_lock_held() || lockdep_is_held(&local->sta_lock) || lockdep_is_held(&local->sta_mtx)); } @@ -123,7 +143,32 @@ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, struct sta_info *sta; sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], - rcu_read_lock_held() || + lockdep_is_held(&local->sta_lock) || + lockdep_is_held(&local->sta_mtx)); + while (sta) { + if ((sta->sdata == sdata || + (sta->sdata->bss && sta->sdata->bss == sdata->bss)) && + !sta->dummy && + memcmp(sta->sta.addr, addr, ETH_ALEN) == 0) + break; + sta = rcu_dereference_check(sta->hnext, + lockdep_is_held(&local->sta_lock) || + lockdep_is_held(&local->sta_mtx)); + } + return sta; +} + +/* + * Get sta info either from the specified interface + * or from one of its vlans (including dummy stations) + */ +struct sta_info *sta_info_get_bss_rx(struct ieee80211_sub_if_data *sdata, + const u8 *addr) +{ + struct ieee80211_local *local = sdata->local; + struct sta_info *sta; + + sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], lockdep_is_held(&local->sta_lock) || lockdep_is_held(&local->sta_mtx)); while (sta) { @@ -132,7 +177,6 @@ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, memcmp(sta->sta.addr, addr, ETH_ALEN) == 0) break; sta = rcu_dereference_check(sta->hnext, - rcu_read_lock_held() || lockdep_is_held(&local->sta_lock) || lockdep_is_held(&local->sta_mtx)); } @@ -200,13 +244,22 @@ static void sta_unblock(struct work_struct *wk) if (sta->dead) return; - if (!test_sta_flags(sta, WLAN_STA_PS_STA)) + if (!test_sta_flag(sta, WLAN_STA_PS_STA)) ieee80211_sta_ps_deliver_wakeup(sta); - else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) { - clear_sta_flags(sta, WLAN_STA_PS_DRIVER); + else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL)) { + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + + local_bh_disable(); ieee80211_sta_ps_deliver_poll_response(sta); + local_bh_enable(); + } else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD)) { + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + + local_bh_disable(); + ieee80211_sta_ps_deliver_uapsd(sta); + local_bh_enable(); } else - clear_sta_flags(sta, WLAN_STA_PS_DRIVER); + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); } static int sta_prepare_rate_control(struct ieee80211_local *local, @@ -239,7 +292,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, return NULL; spin_lock_init(&sta->lock); - spin_lock_init(&sta->flaglock); + spin_lock_init(&sta->ps_lock); INIT_WORK(&sta->drv_unblock_wk, sta_unblock); INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work); mutex_init(&sta->ampdu_mlme.mtx); @@ -266,8 +319,10 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, */ sta->timer_to_tid[i] = i; } - skb_queue_head_init(&sta->ps_tx_buf); - skb_queue_head_init(&sta->tx_filtered); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + skb_queue_head_init(&sta->ps_tx_buf[i]); + skb_queue_head_init(&sta->tx_filtered[i]); + } for (i = 0; i < NUM_RX_DATA_QUEUES; i++) sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX); @@ -284,7 +339,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, return sta; } -static int sta_info_finish_insert(struct sta_info *sta, bool async) +static int sta_info_finish_insert(struct sta_info *sta, + bool async, bool dummy_reinsert) { struct ieee80211_local *local = sta->local; struct ieee80211_sub_if_data *sdata = sta->sdata; @@ -294,51 +350,58 @@ static int sta_info_finish_insert(struct sta_info *sta, bool async) lockdep_assert_held(&local->sta_mtx); - /* notify driver */ - if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) - sdata = container_of(sdata->bss, - struct ieee80211_sub_if_data, - u.ap); - err = drv_sta_add(local, sdata, &sta->sta); - if (err) { - if (!async) - return err; - printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to driver (%d)" - " - keeping it anyway.\n", - sdata->name, sta->sta.addr, err); - } else { - sta->uploaded = true; + if (!sta->dummy || dummy_reinsert) { + /* notify driver */ + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + sdata = container_of(sdata->bss, + struct ieee80211_sub_if_data, + u.ap); + err = drv_sta_add(local, sdata, &sta->sta); + if (err) { + if (!async) + return err; + printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to " + "driver (%d) - keeping it anyway.\n", + sdata->name, sta->sta.addr, err); + } else { + sta->uploaded = true; #ifdef CONFIG_MAC80211_VERBOSE_DEBUG - if (async) - wiphy_debug(local->hw.wiphy, - "Finished adding IBSS STA %pM\n", - sta->sta.addr); + if (async) + wiphy_debug(local->hw.wiphy, + "Finished adding IBSS STA %pM\n", + sta->sta.addr); #endif + } + + sdata = sta->sdata; } - sdata = sta->sdata; + if (!dummy_reinsert) { + if (!async) { + local->num_sta++; + local->sta_generation++; + smp_mb(); - if (!async) { - local->num_sta++; - local->sta_generation++; - smp_mb(); + /* make the station visible */ + spin_lock_irqsave(&local->sta_lock, flags); + sta_info_hash_add(local, sta); + spin_unlock_irqrestore(&local->sta_lock, flags); + } - /* make the station visible */ - spin_lock_irqsave(&local->sta_lock, flags); - sta_info_hash_add(local, sta); - spin_unlock_irqrestore(&local->sta_lock, flags); + list_add(&sta->list, &local->sta_list); + } else { + sta->dummy = false; } - list_add(&sta->list, &local->sta_list); - - ieee80211_sta_debugfs_add(sta); - rate_control_add_sta_debugfs(sta); - - memset(&sinfo, 0, sizeof(sinfo)); - sinfo.filled = 0; - sinfo.generation = local->sta_generation; - cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL); + if (!sta->dummy) { + ieee80211_sta_debugfs_add(sta); + rate_control_add_sta_debugfs(sta); + memset(&sinfo, 0, sizeof(sinfo)); + sinfo.filled = 0; + sinfo.generation = local->sta_generation; + cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL); + } return 0; } @@ -355,7 +418,7 @@ static void sta_info_finish_pending(struct ieee80211_local *local) list_del(&sta->list); spin_unlock_irqrestore(&local->sta_lock, flags); - sta_info_finish_insert(sta, true); + sta_info_finish_insert(sta, true, false); spin_lock_irqsave(&local->sta_lock, flags); } @@ -372,106 +435,117 @@ static void sta_info_finish_work(struct work_struct *work) mutex_unlock(&local->sta_mtx); } -int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU) +static int sta_info_insert_check(struct sta_info *sta) { - struct ieee80211_local *local = sta->local; struct ieee80211_sub_if_data *sdata = sta->sdata; - unsigned long flags; - int err = 0; /* * Can't be a WARN_ON because it can be triggered through a race: * something inserts a STA (on one CPU) without holding the RTNL * and another CPU turns off the net device. */ - if (unlikely(!ieee80211_sdata_running(sdata))) { - err = -ENETDOWN; - rcu_read_lock(); - goto out_free; - } + if (unlikely(!ieee80211_sdata_running(sdata))) + return -ENETDOWN; if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->vif.addr) == 0 || - is_multicast_ether_addr(sta->sta.addr))) { - err = -EINVAL; + is_multicast_ether_addr(sta->sta.addr))) + return -EINVAL; + + return 0; +} + +static int sta_info_insert_ibss(struct sta_info *sta) __acquires(RCU) +{ + struct ieee80211_local *local = sta->local; + struct ieee80211_sub_if_data *sdata = sta->sdata; + unsigned long flags; + + spin_lock_irqsave(&local->sta_lock, flags); + /* check if STA exists already */ + if (sta_info_get_bss_rx(sdata, sta->sta.addr)) { + spin_unlock_irqrestore(&local->sta_lock, flags); rcu_read_lock(); - goto out_free; + return -EEXIST; } - /* - * In ad-hoc mode, we sometimes need to insert stations - * from tasklet context from the RX path. To avoid races, - * always do so in that case -- see the comment below. - */ - if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { - spin_lock_irqsave(&local->sta_lock, flags); - /* check if STA exists already */ - if (sta_info_get_bss(sdata, sta->sta.addr)) { - spin_unlock_irqrestore(&local->sta_lock, flags); - rcu_read_lock(); - err = -EEXIST; - goto out_free; - } - - local->num_sta++; - local->sta_generation++; - smp_mb(); - sta_info_hash_add(local, sta); + local->num_sta++; + local->sta_generation++; + smp_mb(); + sta_info_hash_add(local, sta); - list_add_tail(&sta->list, &local->sta_pending_list); + list_add_tail(&sta->list, &local->sta_pending_list); - rcu_read_lock(); - spin_unlock_irqrestore(&local->sta_lock, flags); + rcu_read_lock(); + spin_unlock_irqrestore(&local->sta_lock, flags); #ifdef CONFIG_MAC80211_VERBOSE_DEBUG - wiphy_debug(local->hw.wiphy, "Added IBSS STA %pM\n", - sta->sta.addr); + wiphy_debug(local->hw.wiphy, "Added IBSS STA %pM\n", + sta->sta.addr); #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ - ieee80211_queue_work(&local->hw, &local->sta_finish_work); + ieee80211_queue_work(&local->hw, &local->sta_finish_work); - return 0; - } + return 0; +} + +/* + * should be called with sta_mtx locked + * this function replaces the mutex lock + * with a RCU lock + */ +static int sta_info_insert_non_ibss(struct sta_info *sta) __acquires(RCU) +{ + struct ieee80211_local *local = sta->local; + struct ieee80211_sub_if_data *sdata = sta->sdata; + unsigned long flags; + struct sta_info *exist_sta; + bool dummy_reinsert = false; + int err = 0; + + lockdep_assert_held(&local->sta_mtx); /* * On first glance, this will look racy, because the code - * below this point, which inserts a station with sleeping, + * in this function, which inserts a station with sleeping, * unlocks the sta_lock between checking existence in the * hash table and inserting into it. * * However, it is not racy against itself because it keeps - * the mutex locked. It still seems to race against the - * above code that atomically inserts the station... That, - * however, is not true because the above code can only - * be invoked for IBSS interfaces, and the below code will - * not be -- and the two do not race against each other as - * the hash table also keys off the interface. + * the mutex locked. */ - might_sleep(); - - mutex_lock(&local->sta_mtx); - spin_lock_irqsave(&local->sta_lock, flags); - /* check if STA exists already */ - if (sta_info_get_bss(sdata, sta->sta.addr)) { - spin_unlock_irqrestore(&local->sta_lock, flags); - mutex_unlock(&local->sta_mtx); - rcu_read_lock(); - err = -EEXIST; - goto out_free; + /* + * check if STA exists already. + * only accept a scenario of a second call to sta_info_insert_non_ibss + * with a dummy station entry that was inserted earlier + * in that case - assume that the dummy station flag should + * be removed. + */ + exist_sta = sta_info_get_bss_rx(sdata, sta->sta.addr); + if (exist_sta) { + if (exist_sta == sta && sta->dummy) { + dummy_reinsert = true; + } else { + spin_unlock_irqrestore(&local->sta_lock, flags); + mutex_unlock(&local->sta_mtx); + rcu_read_lock(); + return -EEXIST; + } } spin_unlock_irqrestore(&local->sta_lock, flags); - err = sta_info_finish_insert(sta, false); + err = sta_info_finish_insert(sta, false, dummy_reinsert); if (err) { mutex_unlock(&local->sta_mtx); rcu_read_lock(); - goto out_free; + return err; } #ifdef CONFIG_MAC80211_VERBOSE_DEBUG - wiphy_debug(local->hw.wiphy, "Inserted STA %pM\n", sta->sta.addr); + wiphy_debug(local->hw.wiphy, "Inserted %sSTA %pM\n", + sta->dummy ? "dummy " : "", sta->sta.addr); #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ /* move reference to rcu-protected */ @@ -482,6 +556,51 @@ int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU) mesh_accept_plinks_update(sdata); return 0; +} + +int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU) +{ + struct ieee80211_local *local = sta->local; + struct ieee80211_sub_if_data *sdata = sta->sdata; + int err = 0; + + err = sta_info_insert_check(sta); + if (err) { + rcu_read_lock(); + goto out_free; + } + + /* + * In ad-hoc mode, we sometimes need to insert stations + * from tasklet context from the RX path. To avoid races, + * always do so in that case -- see the comment below. + */ + if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { + err = sta_info_insert_ibss(sta); + if (err) + goto out_free; + + return 0; + } + + /* + * It might seem that the function called below is in race against + * the function call above that atomically inserts the station... That, + * however, is not true because the above code can only + * be invoked for IBSS interfaces, and the below code will + * not be -- and the two do not race against each other as + * the hash table also keys off the interface. + */ + + might_sleep(); + + mutex_lock(&local->sta_mtx); + + err = sta_info_insert_non_ibss(sta); + if (err) + goto out_free; + + return 0; out_free: BUG_ON(!err); __sta_info_free(local, sta); @@ -497,6 +616,25 @@ int sta_info_insert(struct sta_info *sta) return err; } +/* Caller must hold sta->local->sta_mtx */ +int sta_info_reinsert(struct sta_info *sta) +{ + struct ieee80211_local *local = sta->local; + int err = 0; + + err = sta_info_insert_check(sta); + if (err) { + mutex_unlock(&local->sta_mtx); + return err; + } + + might_sleep(); + + err = sta_info_insert_non_ibss(sta); + rcu_read_unlock(); + return err; +} + static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid) { /* @@ -515,64 +653,93 @@ static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, u16 aid) bss->tim[aid / 8] &= ~(1 << (aid % 8)); } -static void __sta_info_set_tim_bit(struct ieee80211_if_ap *bss, - struct sta_info *sta) +static unsigned long ieee80211_tids_for_ac(int ac) { - BUG_ON(!bss); - - __bss_tim_set(bss, sta->sta.aid); - - if (sta->local->ops->set_tim) { - sta->local->tim_in_locked_section = true; - drv_set_tim(sta->local, &sta->sta, true); - sta->local->tim_in_locked_section = false; + /* If we ever support TIDs > 7, this obviously needs to be adjusted */ + switch (ac) { + case IEEE80211_AC_VO: + return BIT(6) | BIT(7); + case IEEE80211_AC_VI: + return BIT(4) | BIT(5); + case IEEE80211_AC_BE: + return BIT(0) | BIT(3); + case IEEE80211_AC_BK: + return BIT(1) | BIT(2); + default: + WARN_ON(1); + return 0; } } -void sta_info_set_tim_bit(struct sta_info *sta) +void sta_info_recalc_tim(struct sta_info *sta) { + struct ieee80211_local *local = sta->local; + struct ieee80211_if_ap *bss = sta->sdata->bss; unsigned long flags; + bool indicate_tim = false; + u8 ignore_for_tim = sta->sta.uapsd_queues; + int ac; - BUG_ON(!sta->sdata->bss); + if (WARN_ON_ONCE(!sta->sdata->bss)) + return; - spin_lock_irqsave(&sta->local->sta_lock, flags); - __sta_info_set_tim_bit(sta->sdata->bss, sta); - spin_unlock_irqrestore(&sta->local->sta_lock, flags); -} + /* No need to do anything if the driver does all */ + if (local->hw.flags & IEEE80211_HW_AP_LINK_PS) + return; -static void __sta_info_clear_tim_bit(struct ieee80211_if_ap *bss, - struct sta_info *sta) -{ - BUG_ON(!bss); + if (sta->dead) + goto done; - __bss_tim_clear(bss, sta->sta.aid); + /* + * If all ACs are delivery-enabled then we should build + * the TIM bit for all ACs anyway; if only some are then + * we ignore those and build the TIM bit using only the + * non-enabled ones. + */ + if (ignore_for_tim == BIT(IEEE80211_NUM_ACS) - 1) + ignore_for_tim = 0; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + unsigned long tids; + + if (ignore_for_tim & BIT(ac)) + continue; + + indicate_tim |= !skb_queue_empty(&sta->tx_filtered[ac]) || + !skb_queue_empty(&sta->ps_tx_buf[ac]); + if (indicate_tim) + break; - if (sta->local->ops->set_tim) { - sta->local->tim_in_locked_section = true; - drv_set_tim(sta->local, &sta->sta, false); - sta->local->tim_in_locked_section = false; + tids = ieee80211_tids_for_ac(ac); + + indicate_tim |= + sta->driver_buffered_tids & tids; } -} -void sta_info_clear_tim_bit(struct sta_info *sta) -{ - unsigned long flags; + done: + spin_lock_irqsave(&local->sta_lock, flags); - BUG_ON(!sta->sdata->bss); + if (indicate_tim) + __bss_tim_set(bss, sta->sta.aid); + else + __bss_tim_clear(bss, sta->sta.aid); - spin_lock_irqsave(&sta->local->sta_lock, flags); - __sta_info_clear_tim_bit(sta->sdata->bss, sta); - spin_unlock_irqrestore(&sta->local->sta_lock, flags); + if (local->ops->set_tim) { + local->tim_in_locked_section = true; + drv_set_tim(local, &sta->sta, indicate_tim); + local->tim_in_locked_section = false; + } + + spin_unlock_irqrestore(&local->sta_lock, flags); } -static int sta_info_buffer_expired(struct sta_info *sta, - struct sk_buff *skb) +static bool sta_info_buffer_expired(struct sta_info *sta, struct sk_buff *skb) { struct ieee80211_tx_info *info; int timeout; if (!skb) - return 0; + return false; info = IEEE80211_SKB_CB(skb); @@ -586,24 +753,59 @@ static int sta_info_buffer_expired(struct sta_info *sta, } -static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, - struct sta_info *sta) +static bool sta_info_cleanup_expire_buffered_ac(struct ieee80211_local *local, + struct sta_info *sta, int ac) { unsigned long flags; struct sk_buff *skb; - if (skb_queue_empty(&sta->ps_tx_buf)) - return false; + /* + * First check for frames that should expire on the filtered + * queue. Frames here were rejected by the driver and are on + * a separate queue to avoid reordering with normal PS-buffered + * frames. They also aren't accounted for right now in the + * total_ps_buffered counter. + */ + for (;;) { + spin_lock_irqsave(&sta->tx_filtered[ac].lock, flags); + skb = skb_peek(&sta->tx_filtered[ac]); + if (sta_info_buffer_expired(sta, skb)) + skb = __skb_dequeue(&sta->tx_filtered[ac]); + else + skb = NULL; + spin_unlock_irqrestore(&sta->tx_filtered[ac].lock, flags); + + /* + * Frames are queued in order, so if this one + * hasn't expired yet we can stop testing. If + * we actually reached the end of the queue we + * also need to stop, of course. + */ + if (!skb) + break; + dev_kfree_skb(skb); + } + /* + * Now also check the normal PS-buffered queue, this will + * only find something if the filtered queue was emptied + * since the filtered frames are all before the normal PS + * buffered frames. + */ for (;;) { - spin_lock_irqsave(&sta->ps_tx_buf.lock, flags); - skb = skb_peek(&sta->ps_tx_buf); + spin_lock_irqsave(&sta->ps_tx_buf[ac].lock, flags); + skb = skb_peek(&sta->ps_tx_buf[ac]); if (sta_info_buffer_expired(sta, skb)) - skb = __skb_dequeue(&sta->ps_tx_buf); + skb = __skb_dequeue(&sta->ps_tx_buf[ac]); else skb = NULL; - spin_unlock_irqrestore(&sta->ps_tx_buf.lock, flags); + spin_unlock_irqrestore(&sta->ps_tx_buf[ac].lock, flags); + /* + * frames are queued in order, so if this one + * hasn't expired yet (or we reached the end of + * the queue) we can stop testing + */ if (!skb) break; @@ -613,22 +815,47 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, sta->sta.addr); #endif dev_kfree_skb(skb); - - if (skb_queue_empty(&sta->ps_tx_buf) && - !test_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF)) - sta_info_clear_tim_bit(sta); } - return true; + /* + * Finally, recalculate the TIM bit for this station -- it might + * now be clear because the station was too slow to retrieve its + * frames. + */ + sta_info_recalc_tim(sta); + + /* + * Return whether there are any frames still buffered, this is + * used to check whether the cleanup timer still needs to run, + * if there are no frames we don't need to rearm the timer. + */ + return !(skb_queue_empty(&sta->ps_tx_buf[ac]) && + skb_queue_empty(&sta->tx_filtered[ac])); +} + +static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, + struct sta_info *sta) +{ + bool have_buffered = false; + int ac; + + /* This is only necessary for stations on BSS interfaces */ + if (!sta->sdata->bss) + return false; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + have_buffered |= + sta_info_cleanup_expire_buffered_ac(local, sta, ac); + + return have_buffered; } static int __must_check __sta_info_destroy(struct sta_info *sta) { struct ieee80211_local *local; struct ieee80211_sub_if_data *sdata; - struct sk_buff *skb; unsigned long flags; - int ret, i; + int ret, i, ac; might_sleep(); @@ -644,7 +871,7 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) * sessions -- block that to make sure the tear-down * will be sufficient. */ - set_sta_flags(sta, WLAN_STA_BLOCK_BA); + set_sta_flag(sta, WLAN_STA_BLOCK_BA); ieee80211_sta_tear_down_BA_sessions(sta, true); spin_lock_irqsave(&local->sta_lock, flags); @@ -665,19 +892,22 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) sta->dead = true; - if (test_and_clear_sta_flags(sta, - WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) { + if (test_sta_flag(sta, WLAN_STA_PS_STA) || + test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { BUG_ON(!sdata->bss); + clear_sta_flag(sta, WLAN_STA_PS_STA); + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + atomic_dec(&sdata->bss->num_sta_ps); - sta_info_clear_tim_bit(sta); + sta_info_recalc_tim(sta); } local->num_sta--; local->sta_generation++; if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) - rcu_assign_pointer(sdata->u.vlan.sta, NULL); + RCU_INIT_POINTER(sdata->u.vlan.sta, NULL); if (sta->uploaded) { if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) @@ -696,6 +926,12 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) */ synchronize_rcu(); + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]); + __skb_queue_purge(&sta->ps_tx_buf[ac]); + __skb_queue_purge(&sta->tx_filtered[ac]); + } + #ifdef CONFIG_MAC80211_MESH if (ieee80211_vif_is_mesh(&sdata->vif)) mesh_accept_plinks_update(sdata); @@ -718,14 +954,6 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) } #endif - while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { - local->total_ps_buffered--; - dev_kfree_skb_any(skb); - } - - while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) - dev_kfree_skb_any(skb); - __sta_info_free(local, sta); return 0; @@ -737,7 +965,7 @@ int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata, const u8 *addr) int ret; mutex_lock(&sdata->local->sta_mtx); - sta = sta_info_get(sdata, addr); + sta = sta_info_get_rx(sdata, addr); ret = __sta_info_destroy(sta); mutex_unlock(&sdata->local->sta_mtx); @@ -751,7 +979,7 @@ int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata, int ret; mutex_lock(&sdata->local->sta_mtx); - sta = sta_info_get_bss(sdata, addr); + sta = sta_info_get_bss_rx(sdata, addr); ret = __sta_info_destroy(sta); mutex_unlock(&sdata->local->sta_mtx); @@ -891,7 +1119,8 @@ static void clear_sta_ps_flags(void *_sta) { struct sta_info *sta = _sta; - clear_sta_flags(sta, WLAN_STA_PS_DRIVER | WLAN_STA_PS_STA); + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + clear_sta_flag(sta, WLAN_STA_PS_STA); } /* powersave support code */ @@ -899,88 +1128,350 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; - int sent, buffered; + struct sk_buff_head pending; + int filtered = 0, buffered = 0, ac; + unsigned long flags; + + clear_sta_flag(sta, WLAN_STA_SP); + + BUILD_BUG_ON(BITS_TO_LONGS(STA_TID_NUM) > 1); + sta->driver_buffered_tids = 0; - clear_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF); if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta); - if (!skb_queue_empty(&sta->ps_tx_buf)) - sta_info_clear_tim_bit(sta); + skb_queue_head_init(&pending); + /* sync with ieee80211_tx_h_unicast_ps_buf */ + spin_lock(&sta->ps_lock); /* Send all buffered frames to the station */ - sent = ieee80211_add_pending_skbs(local, &sta->tx_filtered); - buffered = ieee80211_add_pending_skbs_fn(local, &sta->ps_tx_buf, - clear_sta_ps_flags, sta); - sent += buffered; + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + int count = skb_queue_len(&pending), tmp; + + spin_lock_irqsave(&sta->tx_filtered[ac].lock, flags); + skb_queue_splice_tail_init(&sta->tx_filtered[ac], &pending); + spin_unlock_irqrestore(&sta->tx_filtered[ac].lock, flags); + tmp = skb_queue_len(&pending); + filtered += tmp - count; + count = tmp; + + spin_lock_irqsave(&sta->ps_tx_buf[ac].lock, flags); + skb_queue_splice_tail_init(&sta->ps_tx_buf[ac], &pending); + spin_unlock_irqrestore(&sta->ps_tx_buf[ac].lock, flags); + tmp = skb_queue_len(&pending); + buffered += tmp - count; + } + + ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta); + spin_unlock(&sta->ps_lock); + local->total_ps_buffered -= buffered; + sta_info_recalc_tim(sta); + #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG printk(KERN_DEBUG "%s: STA %pM aid %d sending %d filtered/%d PS frames " "since STA not sleeping anymore\n", sdata->name, - sta->sta.addr, sta->sta.aid, sent - buffered, buffered); + sta->sta.addr, sta->sta.aid, filtered, buffered); #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ } -void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) +static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, int tid, + enum ieee80211_frame_release_type reason) { - struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; + struct ieee80211_qos_hdr *nullfunc; struct sk_buff *skb; - int no_pending_pkts; + int size = sizeof(*nullfunc); + __le16 fc; + bool qos = test_sta_flag(sta, WLAN_STA_WME); + struct ieee80211_tx_info *info; - skb = skb_dequeue(&sta->tx_filtered); - if (!skb) { - skb = skb_dequeue(&sta->ps_tx_buf); - if (skb) - local->total_ps_buffered--; + if (qos) { + fc = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_NULLFUNC | + IEEE80211_FCTL_FROMDS); + } else { + size -= 2; + fc = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_NULLFUNC | + IEEE80211_FCTL_FROMDS); + } + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + size); + if (!skb) + return; + + skb_reserve(skb, local->hw.extra_tx_headroom); + + nullfunc = (void *) skb_put(skb, size); + nullfunc->frame_control = fc; + nullfunc->duration_id = 0; + memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN); + memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN); + memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN); + nullfunc->seq_ctrl = 0; + + skb->priority = tid; + skb_set_queue_mapping(skb, ieee802_1d_to_ac[tid]); + if (qos) { + nullfunc->qos_ctrl = cpu_to_le16(tid); + + if (reason == IEEE80211_FRAME_RELEASE_UAPSD) + nullfunc->qos_ctrl |= + cpu_to_le16(IEEE80211_QOS_CTL_EOSP); } - no_pending_pkts = skb_queue_empty(&sta->tx_filtered) && - skb_queue_empty(&sta->ps_tx_buf); - if (skb) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *hdr = - (struct ieee80211_hdr *) skb->data; + info = IEEE80211_SKB_CB(skb); + + /* + * Tell TX path to send this frame even though the + * STA may still remain is PS mode after this frame + * exchange. Also set EOSP to indicate this packet + * ends the poll/service period. + */ + info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE | + IEEE80211_TX_STATUS_EOSP | + IEEE80211_TX_CTL_REQ_TX_STATUS; + + drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false); + + ieee80211_xmit(sdata, skb); +} + +static void +ieee80211_sta_ps_deliver_response(struct sta_info *sta, + int n_frames, u8 ignored_acs, + enum ieee80211_frame_release_type reason) +{ + struct ieee80211_sub_if_data *sdata = sta->sdata; + struct ieee80211_local *local = sdata->local; + bool found = false; + bool more_data = false; + int ac; + unsigned long driver_release_tids = 0; + struct sk_buff_head frames; + + /* Service or PS-Poll period starts */ + set_sta_flag(sta, WLAN_STA_SP); + + __skb_queue_head_init(&frames); + + /* + * Get response frame(s) and more data bit for it. + */ + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + unsigned long tids; + + if (ignored_acs & BIT(ac)) + continue; + + tids = ieee80211_tids_for_ac(ac); + + if (!found) { + driver_release_tids = sta->driver_buffered_tids & tids; + if (driver_release_tids) { + found = true; + } else { + struct sk_buff *skb; + + while (n_frames > 0) { + skb = skb_dequeue(&sta->tx_filtered[ac]); + if (!skb) { + skb = skb_dequeue( + &sta->ps_tx_buf[ac]); + if (skb) + local->total_ps_buffered--; + } + if (!skb) + break; + n_frames--; + found = true; + __skb_queue_tail(&frames, skb); + } + } + + /* + * If the driver has data on more than one TID then + * certainly there's more data if we release just a + * single frame now (from a single TID). + */ + if (reason == IEEE80211_FRAME_RELEASE_PSPOLL && + hweight16(driver_release_tids) > 1) { + more_data = true; + driver_release_tids = + BIT(ffs(driver_release_tids) - 1); + break; + } + } + + if (!skb_queue_empty(&sta->tx_filtered[ac]) || + !skb_queue_empty(&sta->ps_tx_buf[ac])) { + more_data = true; + break; + } + } + + if (!found) { + int tid; /* - * Tell TX path to send this frame even though the STA may - * still remain is PS mode after this frame exchange. + * For PS-Poll, this can only happen due to a race condition + * when we set the TIM bit and the station notices it, but + * before it can poll for the frame we expire it. + * + * For uAPSD, this is said in the standard (11.2.1.5 h): + * At each unscheduled SP for a non-AP STA, the AP shall + * attempt to transmit at least one MSDU or MMPDU, but no + * more than the value specified in the Max SP Length field + * in the QoS Capability element from delivery-enabled ACs, + * that are destined for the non-AP STA. + * + * Since we have no other MSDU/MMPDU, transmit a QoS null frame. */ - info->flags |= IEEE80211_TX_CTL_PSPOLL_RESPONSE; -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n", - sta->sta.addr, sta->sta.aid, - skb_queue_len(&sta->ps_tx_buf)); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ + /* This will evaluate to 1, 3, 5 or 7. */ + tid = 7 - ((ffs(~ignored_acs) - 1) << 1); - /* 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); - else - hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); + ieee80211_send_null_response(sdata, sta, tid, reason); + return; + } + + if (!driver_release_tids) { + struct sk_buff_head pending; + struct sk_buff *skb; + int num = 0; + u16 tids = 0; + + skb_queue_head_init(&pending); + + while ((skb = __skb_dequeue(&frames))) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *) skb->data; + u8 *qoshdr = NULL; + + num++; + + /* + * Tell TX path to send this frame even though the + * STA may still remain is PS mode after this frame + * exchange. + */ + info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE; + + /* + * Use MoreData flag to indicate whether there are + * more buffered frames for this STA + */ + if (more_data || !skb_queue_empty(&frames)) + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + else + hdr->frame_control &= + cpu_to_le16(~IEEE80211_FCTL_MOREDATA); + + if (ieee80211_is_data_qos(hdr->frame_control) || + ieee80211_is_qos_nullfunc(hdr->frame_control)) + qoshdr = ieee80211_get_qos_ctl(hdr); + + /* set EOSP for the frame */ + if (reason == IEEE80211_FRAME_RELEASE_UAPSD && + qoshdr && skb_queue_empty(&frames)) + *qoshdr |= IEEE80211_QOS_CTL_EOSP; + + info->flags |= IEEE80211_TX_STATUS_EOSP | + IEEE80211_TX_CTL_REQ_TX_STATUS; + + if (qoshdr) + tids |= BIT(*qoshdr & IEEE80211_QOS_CTL_TID_MASK); + else + tids |= BIT(0); + + __skb_queue_tail(&pending, skb); + } - ieee80211_add_pending_skb(local, skb); + drv_allow_buffered_frames(local, sta, tids, num, + reason, more_data); - if (no_pending_pkts) - sta_info_clear_tim_bit(sta); -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG + ieee80211_add_pending_skbs(local, &pending); + + sta_info_recalc_tim(sta); } else { /* - * FIXME: This can be the result of a race condition between - * us expiring a frame and the station polling for it. - * Should we send it a null-func frame indicating we - * have nothing buffered for it? + * We need to release a frame that is buffered somewhere in the + * driver ... it'll have to handle that. + * Note that, as per the comment above, it'll also have to see + * if there is more than just one frame on the specific TID that + * we're releasing from, and it needs to set the more-data bit + * accordingly if we tell it that there's no more data. If we do + * tell it there's more data, then of course the more-data bit + * needs to be set anyway. + */ + drv_release_buffered_frames(local, sta, driver_release_tids, + n_frames, reason, more_data); + + /* + * Note that we don't recalculate the TIM bit here as it would + * most likely have no effect at all unless the driver told us + * that the TID became empty before returning here from the + * release function. + * Either way, however, when the driver tells us that the TID + * became empty we'll do the TIM recalculation. */ - printk(KERN_DEBUG "%s: STA %pM sent PS Poll even " - "though there are no buffered frames for it\n", - sdata->name, sta->sta.addr); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ } } +void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) +{ + u8 ignore_for_response = sta->sta.uapsd_queues; + + /* + * If all ACs are delivery-enabled then we should reply + * from any of them, if only some are enabled we reply + * only from the non-enabled ones. + */ + if (ignore_for_response == BIT(IEEE80211_NUM_ACS) - 1) + ignore_for_response = 0; + + ieee80211_sta_ps_deliver_response(sta, 1, ignore_for_response, + IEEE80211_FRAME_RELEASE_PSPOLL); +} + +void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta) +{ + int n_frames = sta->sta.max_sp; + u8 delivery_enabled = sta->sta.uapsd_queues; + + /* + * If we ever grow support for TSPEC this might happen if + * the TSPEC update from hostapd comes in between a trigger + * frame setting WLAN_STA_UAPSD in the RX path and this + * actually getting called. + */ + if (!delivery_enabled) + return; + + switch (sta->sta.max_sp) { + case 1: + n_frames = 2; + break; + case 2: + n_frames = 4; + break; + case 3: + n_frames = 6; + break; + case 0: + /* XXX: what is a good value? */ + n_frames = 8; + break; + } + + ieee80211_sta_ps_deliver_response(sta, n_frames, ~delivery_enabled, + IEEE80211_FRAME_RELEASE_UAPSD); +} + void ieee80211_sta_block_awake(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, bool block) { @@ -989,17 +1480,50 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, trace_api_sta_block_awake(sta->local, pubsta, block); if (block) - set_sta_flags(sta, WLAN_STA_PS_DRIVER); - else if (test_sta_flags(sta, WLAN_STA_PS_DRIVER)) + set_sta_flag(sta, WLAN_STA_PS_DRIVER); + else if (test_sta_flag(sta, WLAN_STA_PS_DRIVER)) ieee80211_queue_work(hw, &sta->drv_unblock_wk); } EXPORT_SYMBOL(ieee80211_sta_block_awake); -void ieee80211_sta_set_tim(struct ieee80211_sta *pubsta) +void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta) { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); + struct ieee80211_local *local = sta->local; + struct sk_buff *skb; + struct skb_eosp_msg_data *data; + + trace_api_eosp(local, pubsta); + + skb = alloc_skb(0, GFP_ATOMIC); + if (!skb) { + /* too bad ... but race is better than loss */ + clear_sta_flag(sta, WLAN_STA_SP); + return; + } + + data = (void *)skb->cb; + memcpy(data->sta, pubsta->addr, ETH_ALEN); + memcpy(data->iface, sta->sdata->vif.addr, ETH_ALEN); + skb->pkt_type = IEEE80211_EOSP_MSG; + skb_queue_tail(&local->skb_queue, skb); + tasklet_schedule(&local->tasklet); +} +EXPORT_SYMBOL(ieee80211_sta_eosp_irqsafe); + +void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta, + u8 tid, bool buffered) +{ + struct sta_info *sta = container_of(pubsta, struct sta_info, sta); + + if (WARN_ON(tid >= STA_TID_NUM)) + return; + + if (buffered) + set_bit(tid, &sta->driver_buffered_tids); + else + clear_bit(tid, &sta->driver_buffered_tids); - set_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF); - sta_info_set_tim_bit(sta); + sta_info_recalc_tim(sta); } -EXPORT_SYMBOL(ieee80211_sta_set_tim); +EXPORT_SYMBOL(ieee80211_sta_set_buffered); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index c6ae871..556fbcc 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -19,7 +19,8 @@ /** * enum ieee80211_sta_info_flags - Stations flags * - * These flags are used with &struct sta_info's @flags member. + * These flags are used with &struct sta_info's @flags member, but + * only indirectly with set_sta_flag() and friends. * * @WLAN_STA_AUTH: Station is authenticated. * @WLAN_STA_ASSOC: Station is associated. @@ -43,24 +44,33 @@ * be in the queues * @WLAN_STA_PSPOLL: Station sent PS-poll while driver was keeping * station in power-save mode, reply when the driver unblocks. - * @WLAN_STA_PS_DRIVER_BUF: Station has frames pending in driver internal - * buffers. Automatically cleared on station wake-up. + * @WLAN_STA_TDLS_PEER: Station is a TDLS peer. + * @WLAN_STA_TDLS_PEER_AUTH: This TDLS peer is authorized to send direct + * packets. This means the link is enabled. + * @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was + * keeping station in power-save mode, reply when the driver + * unblocks the station. + * @WLAN_STA_SP: Station is in a service period, so don't try to + * reply to other uAPSD trigger frames or PS-Poll. */ enum ieee80211_sta_info_flags { - WLAN_STA_AUTH = 1<<0, - WLAN_STA_ASSOC = 1<<1, - WLAN_STA_PS_STA = 1<<2, - WLAN_STA_AUTHORIZED = 1<<3, - WLAN_STA_SHORT_PREAMBLE = 1<<4, - WLAN_STA_ASSOC_AP = 1<<5, - WLAN_STA_WME = 1<<6, - WLAN_STA_WDS = 1<<7, - WLAN_STA_CLEAR_PS_FILT = 1<<9, - WLAN_STA_MFP = 1<<10, - WLAN_STA_BLOCK_BA = 1<<11, - WLAN_STA_PS_DRIVER = 1<<12, - WLAN_STA_PSPOLL = 1<<13, - WLAN_STA_PS_DRIVER_BUF = 1<<14, + WLAN_STA_AUTH, + WLAN_STA_ASSOC, + WLAN_STA_PS_STA, + WLAN_STA_AUTHORIZED, + WLAN_STA_SHORT_PREAMBLE, + WLAN_STA_ASSOC_AP, + WLAN_STA_WME, + WLAN_STA_WDS, + WLAN_STA_CLEAR_PS_FILT, + WLAN_STA_MFP, + WLAN_STA_BLOCK_BA, + WLAN_STA_PS_DRIVER, + WLAN_STA_PSPOLL, + WLAN_STA_TDLS_PEER, + WLAN_STA_TDLS_PEER_AUTH, + WLAN_STA_UAPSD, + WLAN_STA_SP, }; #define STA_TID_NUM 16 @@ -86,6 +96,8 @@ enum ieee80211_sta_info_flags { * @stop_initiator: initiator of a session stop * @tx_stop: TX DelBA frame when stopping * @buf_size: reorder buffer size at receiver + * @failed_bar_ssn: ssn of the last failed BAR tx attempt + * @bar_pending: BAR needs to be re-sent * * This structure's lifetime is managed by RCU, assignments to * the array holding it must hold the aggregation mutex. @@ -106,6 +118,9 @@ struct tid_ampdu_tx { u8 stop_initiator; bool tx_stop; u8 buf_size; + + u16 failed_bar_ssn; + bool bar_pending; }; /** @@ -123,6 +138,7 @@ struct tid_ampdu_tx { * @dialog_token: dialog token for aggregation session * @rcu_head: RCU head used for freeing this struct * @reorder_lock: serializes access to reorder buffer, see below. + * @removed: this session is removed (but might have been found due to RCU) * * This structure's lifetime is managed by RCU, assignments to * the array holding it must hold the aggregation mutex. @@ -145,6 +161,7 @@ struct tid_ampdu_rx { u16 buf_size; u16 timeout; u8 dialog_token; + bool removed; }; /** @@ -158,6 +175,8 @@ struct tid_ampdu_rx { * @work: work struct for starting/stopping aggregation * @tid_rx_timer_expired: bitmap indicating on which TIDs the * RX timer expired until the work for it runs + * @tid_rx_stop_requested: bitmap indicating which BA sessions per TID the + * driver requested to close until the work for it runs * @mtx: mutex to protect all TX data (except non-NULL assignments * to tid_tx[idx], which are protected by the sta spinlock) */ @@ -166,6 +185,7 @@ struct sta_ampdu_mlme { /* rx */ struct tid_ampdu_rx __rcu *tid_rx[STA_TID_NUM]; unsigned long tid_rx_timer_expired[BITS_TO_LONGS(STA_TID_NUM)]; + unsigned long tid_rx_stop_requested[BITS_TO_LONGS(STA_TID_NUM)]; /* tx */ struct work_struct work; struct tid_ampdu_tx __rcu *tid_tx[STA_TID_NUM]; @@ -195,15 +215,17 @@ struct sta_ampdu_mlme { * @last_rx_rate_flag: rx status flag of the last data packet * @lock: used for locking all fields that require locking, see comments * in the header file. - * @flaglock: spinlock for flags accesses * @drv_unblock_wk: used for driver PS unblocking * @listen_interval: listen interval of this station, when we're acting as AP - * @flags: STA flags, see &enum ieee80211_sta_info_flags - * @ps_tx_buf: buffer of frames to transmit to this station - * when it leaves power saving state - * @tx_filtered: buffer of frames we already tried to transmit - * but were filtered by hardware due to STA having entered - * power saving state + * @_flags: STA flags, see &enum ieee80211_sta_info_flags, do not use directly + * @ps_lock: used for powersave (when mac80211 is the AP) related locking + * @ps_tx_buf: buffers (per AC) of frames to transmit to this station + * when it leaves power saving state or polls + * @tx_filtered: buffers (per AC) of frames we already tried to + * transmit but were filtered by hardware due to STA having + * entered power saving state, these are also delivered to + * the station when it leaves powersave or polls for frames + * @driver_buffered_tids: bitmap of TIDs the driver has data buffered on * @rx_packets: Number of MSDUs received from this STA * @rx_bytes: Number of bytes received from this STA * @wep_weak_iv_count: number of weak WEP IVs received from this station @@ -235,10 +257,12 @@ struct sta_ampdu_mlme { * @plink_timer: peer link watch timer * @plink_timer_was_running: used by suspend/resume to restore timers * @debugfs: debug filesystem info - * @sta: station information we share with the driver * @dead: set to true when sta is unlinked * @uploaded: set to true when sta is uploaded to the driver * @lost_packets: number of consecutive lost packets + * @dummy: indicate a dummy station created for receiving + * EAP frames before association + * @sta: station information we share with the driver */ struct sta_info { /* General information, mostly static */ @@ -251,7 +275,6 @@ struct sta_info { struct rate_control_ref *rate_ctrl; void *rate_ctrl_priv; spinlock_t lock; - spinlock_t flaglock; struct work_struct drv_unblock_wk; @@ -261,18 +284,14 @@ struct sta_info { bool uploaded; - /* - * frequently updated, locked with own spinlock (flaglock), - * use the accessors defined below - */ - u32 flags; + /* use the accessors defined below */ + unsigned long _flags; - /* - * STA powersave frame queues, no more than the internal - * locking required. - */ - struct sk_buff_head ps_tx_buf; - struct sk_buff_head tx_filtered; + /* STA powersave lock and frame queues */ + spinlock_t ps_lock; + struct sk_buff_head ps_tx_buf[IEEE80211_NUM_ACS]; + struct sk_buff_head tx_filtered[IEEE80211_NUM_ACS]; + unsigned long driver_buffered_tids; /* Updated from RX path only, no locking requirements */ unsigned long rx_packets, rx_bytes; @@ -284,7 +303,8 @@ struct sta_info { unsigned long rx_dropped; int last_signal; struct ewma avg_signal; - __le16 last_seq_ctrl[NUM_RX_DATA_QUEUES]; + /* Plus 1 for non-QoS frames */ + __le16 last_seq_ctrl[NUM_RX_DATA_QUEUES + 1]; /* Updated from TX status path only, no locking requirements */ unsigned long tx_filtered_count; @@ -332,6 +352,9 @@ struct sta_info { unsigned int lost_packets; + /* should be right in front of sta to be in the same cache line */ + bool dummy; + /* keep last! */ struct ieee80211_sta sta; }; @@ -344,60 +367,28 @@ static inline enum nl80211_plink_state sta_plink_state(struct sta_info *sta) return NL80211_PLINK_LISTEN; } -static inline void set_sta_flags(struct sta_info *sta, const u32 flags) +static inline void set_sta_flag(struct sta_info *sta, + enum ieee80211_sta_info_flags flag) { - unsigned long irqfl; - - spin_lock_irqsave(&sta->flaglock, irqfl); - sta->flags |= flags; - spin_unlock_irqrestore(&sta->flaglock, irqfl); + set_bit(flag, &sta->_flags); } -static inline void clear_sta_flags(struct sta_info *sta, const u32 flags) +static inline void clear_sta_flag(struct sta_info *sta, + enum ieee80211_sta_info_flags flag) { - unsigned long irqfl; - - spin_lock_irqsave(&sta->flaglock, irqfl); - sta->flags &= ~flags; - spin_unlock_irqrestore(&sta->flaglock, irqfl); + clear_bit(flag, &sta->_flags); } -static inline u32 test_sta_flags(struct sta_info *sta, const u32 flags) +static inline int test_sta_flag(struct sta_info *sta, + enum ieee80211_sta_info_flags flag) { - u32 ret; - unsigned long irqfl; - - spin_lock_irqsave(&sta->flaglock, irqfl); - ret = sta->flags & flags; - spin_unlock_irqrestore(&sta->flaglock, irqfl); - - return ret; + return test_bit(flag, &sta->_flags); } -static inline u32 test_and_clear_sta_flags(struct sta_info *sta, - const u32 flags) +static inline int test_and_clear_sta_flag(struct sta_info *sta, + enum ieee80211_sta_info_flags flag) { - u32 ret; - unsigned long irqfl; - - spin_lock_irqsave(&sta->flaglock, irqfl); - ret = sta->flags & flags; - sta->flags &= ~flags; - spin_unlock_irqrestore(&sta->flaglock, irqfl); - - return ret; -} - -static inline u32 get_sta_flags(struct sta_info *sta) -{ - u32 ret; - unsigned long irqfl; - - spin_lock_irqsave(&sta->flaglock, irqfl); - ret = sta->flags; - spin_unlock_irqrestore(&sta->flaglock, irqfl); - - return ret; + return test_and_clear_bit(flag, &sta->_flags); } void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, @@ -415,8 +406,8 @@ rcu_dereference_protected_tid_tx(struct sta_info *sta, int tid) #define STA_HASH(sta) (sta[5]) -/* Maximum number of frames to buffer per power saving station */ -#define STA_MAX_TX_BUFFER 128 +/* Maximum number of frames to buffer per power saving station per AC */ +#define STA_MAX_TX_BUFFER 64 /* Minimum buffered frame expiry time. If STA uses listen interval that is * smaller than this value, the minimum value here is used instead. */ @@ -432,9 +423,15 @@ rcu_dereference_protected_tid_tx(struct sta_info *sta, int tid) struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, const u8 *addr); +struct sta_info *sta_info_get_rx(struct ieee80211_sub_if_data *sdata, + const u8 *addr); + struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, const u8 *addr); +struct sta_info *sta_info_get_bss_rx(struct ieee80211_sub_if_data *sdata, + const u8 *addr); + static inline void for_each_sta_info_type_check(struct ieee80211_local *local, const u8 *addr, @@ -455,6 +452,22 @@ void for_each_sta_info_type_check(struct ieee80211_local *local, _sta = nxt, \ nxt = _sta ? rcu_dereference(_sta->hnext) : NULL \ ) \ + /* run code only if address matches and it's not a dummy sta */ \ + if (memcmp(_sta->sta.addr, (_addr), ETH_ALEN) == 0 && \ + !_sta->dummy) + +#define for_each_sta_info_rx(local, _addr, _sta, nxt) \ + for ( /* initialise loop */ \ + _sta = rcu_dereference(local->sta_hash[STA_HASH(_addr)]),\ + nxt = _sta ? rcu_dereference(_sta->hnext) : NULL; \ + /* typecheck */ \ + for_each_sta_info_type_check(local, (_addr), _sta, nxt),\ + /* continue condition */ \ + _sta; \ + /* advance loop */ \ + _sta = nxt, \ + nxt = _sta ? rcu_dereference(_sta->hnext) : NULL \ + ) \ /* compare address and run code only if it matches */ \ if (memcmp(_sta->sta.addr, (_addr), ETH_ALEN) == 0) @@ -480,14 +493,14 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, int sta_info_insert(struct sta_info *sta); int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU); int sta_info_insert_atomic(struct sta_info *sta); +int sta_info_reinsert(struct sta_info *sta); int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata, const u8 *addr); int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata, const u8 *addr); -void sta_info_set_tim_bit(struct sta_info *sta); -void sta_info_clear_tim_bit(struct sta_info *sta); +void sta_info_recalc_tim(struct sta_info *sta); void sta_info_init(struct ieee80211_local *local); void sta_info_stop(struct ieee80211_local *local); @@ -498,5 +511,6 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta); void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta); +void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta); #endif /* STA_INFO_H */ diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 04cdbaf..1a49354 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -9,11 +9,13 @@ * published by the Free Software Foundation. */ +#include <linux/export.h> #include <net/mac80211.h> #include "ieee80211_i.h" #include "rate.h" #include "mesh.h" #include "led.h" +#include "wme.h" void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, @@ -43,6 +45,8 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; + int ac; /* * This skb 'survived' a round-trip through the driver, and @@ -63,11 +67,37 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, sta->tx_filtered_count++; /* + * Clear more-data bit on filtered frames, it might be set + * but later frames might time out so it might have to be + * clear again ... It's all rather unlikely (this frame + * should time out first, right?) but let's not confuse + * peers unnecessarily. + */ + if (hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_MOREDATA)) + hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_MOREDATA); + + if (ieee80211_is_data_qos(hdr->frame_control)) { + u8 *p = ieee80211_get_qos_ctl(hdr); + int tid = *p & IEEE80211_QOS_CTL_TID_MASK; + + /* + * Clear EOSP if set, this could happen e.g. + * if an absence period (us being a P2P GO) + * shortens the SP. + */ + if (*p & IEEE80211_QOS_CTL_EOSP) + *p &= ~IEEE80211_QOS_CTL_EOSP; + ac = ieee802_1d_to_ac[tid & 7]; + } else { + ac = IEEE80211_AC_BE; + } + + /* * 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 wakes up for the next time. */ - set_sta_flags(sta, WLAN_STA_CLEAR_PS_FILT); + set_sta_flag(sta, WLAN_STA_CLEAR_PS_FILT); /* * This code races in the following way: @@ -103,13 +133,19 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, * changes before calling TX status events if ordering can be * unknown. */ - if (test_sta_flags(sta, WLAN_STA_PS_STA) && - skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) { - skb_queue_tail(&sta->tx_filtered, skb); + if (test_sta_flag(sta, WLAN_STA_PS_STA) && + skb_queue_len(&sta->tx_filtered[ac]) < STA_MAX_TX_BUFFER) { + skb_queue_tail(&sta->tx_filtered[ac], skb); + sta_info_recalc_tim(sta); + + if (!timer_pending(&local->sta_cleanup)) + mod_timer(&local->sta_cleanup, + round_jiffies(jiffies + + STA_INFO_CLEANUP_INTERVAL)); return; } - if (!test_sta_flags(sta, WLAN_STA_PS_STA) && + if (!test_sta_flag(sta, WLAN_STA_PS_STA) && !(info->flags & IEEE80211_TX_INTFL_RETRIED)) { /* Software retry the packet once */ info->flags |= IEEE80211_TX_INTFL_RETRIED; @@ -121,18 +157,41 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, if (net_ratelimit()) wiphy_debug(local->hw.wiphy, "dropped TX filtered frame, queue_len=%d PS=%d @%lu\n", - skb_queue_len(&sta->tx_filtered), - !!test_sta_flags(sta, WLAN_STA_PS_STA), jiffies); + skb_queue_len(&sta->tx_filtered[ac]), + !!test_sta_flag(sta, WLAN_STA_PS_STA), jiffies); #endif dev_kfree_skb(skb); } +static void ieee80211_check_pending_bar(struct sta_info *sta, u8 *addr, u8 tid) +{ + struct tid_ampdu_tx *tid_tx; + + tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); + if (!tid_tx || !tid_tx->bar_pending) + return; + + tid_tx->bar_pending = false; + ieee80211_send_bar(&sta->sdata->vif, addr, tid, tid_tx->failed_bar_ssn); +} + static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) { struct ieee80211_mgmt *mgmt = (void *) skb->data; struct ieee80211_local *local = sta->local; struct ieee80211_sub_if_data *sdata = sta->sdata; + if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) + sta->last_rx = jiffies; + + if (ieee80211_is_data_qos(mgmt->frame_control)) { + struct ieee80211_hdr *hdr = (void *) skb->data; + u8 *qc = ieee80211_get_qos_ctl(hdr); + u16 tid = qc[0] & 0xf; + + ieee80211_check_pending_bar(sta, hdr->addr1, tid); + } + if (ieee80211_is_action(mgmt->frame_control) && sdata->vif.type == NL80211_IFTYPE_STATION && mgmt->u.action.category == WLAN_CATEGORY_HT && @@ -161,6 +220,114 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) } } +static void ieee80211_set_bar_pending(struct sta_info *sta, u8 tid, u16 ssn) +{ + struct tid_ampdu_tx *tid_tx; + + tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); + if (!tid_tx) + return; + + tid_tx->failed_bar_ssn = ssn; + tid_tx->bar_pending = true; +} + +static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info) +{ + int len = sizeof(struct ieee80211_radiotap_header); + + /* IEEE80211_RADIOTAP_RATE rate */ + if (info->status.rates[0].idx >= 0 && + !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) + len += 2; + + /* IEEE80211_RADIOTAP_TX_FLAGS */ + len += 2; + + /* IEEE80211_RADIOTAP_DATA_RETRIES */ + len += 1; + + /* IEEE80211_TX_RC_MCS */ + if (info->status.rates[0].idx >= 0 && + info->status.rates[0].flags & IEEE80211_TX_RC_MCS) + len += 3; + + return len; +} + +static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band + *sband, struct sk_buff *skb, + int retry_count, int rtap_len) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_radiotap_header *rthdr; + unsigned char *pos; + u16 txflags; + + rthdr = (struct ieee80211_radiotap_header *) skb_push(skb, rtap_len); + + memset(rthdr, 0, rtap_len); + rthdr->it_len = cpu_to_le16(rtap_len); + rthdr->it_present = + cpu_to_le32((1 << IEEE80211_RADIOTAP_TX_FLAGS) | + (1 << IEEE80211_RADIOTAP_DATA_RETRIES)); + pos = (unsigned char *)(rthdr + 1); + + /* + * XXX: Once radiotap gets the bitmap reset thing the vendor + * extensions proposal contains, we can actually report + * the whole set of tries we did. + */ + + /* IEEE80211_RADIOTAP_RATE */ + if (info->status.rates[0].idx >= 0 && + !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) { + rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE); + *pos = sband->bitrates[info->status.rates[0].idx].bitrate / 5; + /* padding for tx flags */ + pos += 2; + } + + /* IEEE80211_RADIOTAP_TX_FLAGS */ + txflags = 0; + if (!(info->flags & IEEE80211_TX_STAT_ACK) && + !is_multicast_ether_addr(hdr->addr1)) + txflags |= IEEE80211_RADIOTAP_F_TX_FAIL; + + if ((info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || + (info->status.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) + txflags |= IEEE80211_RADIOTAP_F_TX_CTS; + else if (info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) + txflags |= IEEE80211_RADIOTAP_F_TX_RTS; + + put_unaligned_le16(txflags, pos); + pos += 2; + + /* IEEE80211_RADIOTAP_DATA_RETRIES */ + /* for now report the total retry_count */ + *pos = retry_count; + pos++; + + /* IEEE80211_TX_RC_MCS */ + if (info->status.rates[0].idx >= 0 && + info->status.rates[0].flags & IEEE80211_TX_RC_MCS) { + rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS); + pos[0] = IEEE80211_RADIOTAP_MCS_HAVE_MCS | + IEEE80211_RADIOTAP_MCS_HAVE_GI | + IEEE80211_RADIOTAP_MCS_HAVE_BW; + if (info->status.rates[0].flags & IEEE80211_TX_RC_SHORT_GI) + pos[1] |= IEEE80211_RADIOTAP_MCS_SGI; + if (info->status.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + pos[1] |= IEEE80211_RADIOTAP_MCS_BW_40; + if (info->status.rates[0].flags & IEEE80211_TX_RC_GREEN_FIELD) + pos[1] |= IEEE80211_RADIOTAP_MCS_FMT_GF; + pos[2] = info->status.rates[0].idx; + pos += 3; + } + +} + /* * Use a static threshold for now, best value to be determined * by testing ... @@ -179,7 +346,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) u16 frag, type; __le16 fc; struct ieee80211_supported_band *sband; - struct ieee80211_tx_status_rtap_hdr *rthdr; struct ieee80211_sub_if_data *sdata; struct net_device *prev_dev = NULL; struct sta_info *sta, *tmp; @@ -187,6 +353,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) int rates_idx = -1; bool send_to_cooked; bool acked; + struct ieee80211_bar *bar; + u16 tid; + int rtap_len; for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { if (info->status.rates[i].idx < 0) { @@ -215,8 +384,11 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) if (memcmp(hdr->addr2, sta->sdata->vif.addr, ETH_ALEN)) continue; + if (info->flags & IEEE80211_TX_STATUS_EOSP) + clear_sta_flag(sta, WLAN_STA_SP); + acked = !!(info->flags & IEEE80211_TX_STAT_ACK); - if (!acked && test_sta_flags(sta, WLAN_STA_PS_STA)) { + if (!acked && test_sta_flag(sta, WLAN_STA_PS_STA)) { /* * The STA is in power save mode, so assume * that this TX packet failed because of that. @@ -239,10 +411,35 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) tid = qc[0] & 0xf; ssn = ((le16_to_cpu(hdr->seq_ctrl) + 0x10) & IEEE80211_SCTL_SEQ); - ieee80211_send_bar(sta->sdata, hdr->addr1, + ieee80211_send_bar(&sta->sdata->vif, hdr->addr1, tid, ssn); } + if (!acked && ieee80211_is_back_req(fc)) { + u16 control; + + /* + * BAR failed, store the last SSN and retry sending + * the BAR when the next unicast transmission on the + * same TID succeeds. + */ + bar = (struct ieee80211_bar *) skb->data; + control = le16_to_cpu(bar->control); + if (!(control & IEEE80211_BAR_CTRL_MULTI_TID)) { + u16 ssn = le16_to_cpu(bar->start_seq_num); + + tid = (control & + IEEE80211_BAR_CTRL_TID_INFO_MASK) >> + IEEE80211_BAR_CTRL_TID_INFO_SHIFT; + + if (local->hw.flags & + IEEE80211_HW_TEARDOWN_AGGR_ON_BAR_FAIL) + ieee80211_stop_tx_ba_session(&sta->sta, tid); + else + ieee80211_set_bar_pending(sta, tid, ssn); + } + } + if (info->flags & IEEE80211_TX_STAT_TX_FILTERED) { ieee80211_handle_filtered_frame(local, sta, skb); rcu_read_unlock(); @@ -345,9 +542,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) local->hw_roc_skb_for_status = NULL; } - if (cookie == local->hw_offchan_tx_cookie) - local->hw_offchan_tx_cookie = 0; - cfg80211_mgmt_tx_status( skb->dev, cookie, skb->data, skb->len, !!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC); @@ -370,44 +564,13 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) } /* send frame to monitor interfaces now */ - - if (skb_headroom(skb) < sizeof(*rthdr)) { + rtap_len = ieee80211_tx_radiotap_len(info); + if (WARN_ON_ONCE(skb_headroom(skb) < rtap_len)) { 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) | - (1 << IEEE80211_RADIOTAP_RATE)); - - if (!(info->flags & IEEE80211_TX_STAT_ACK) && - !is_multicast_ether_addr(hdr->addr1)) - rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_FAIL); - - /* - * XXX: Once radiotap gets the bitmap reset thing the vendor - * extensions proposal contains, we can actually report - * the whole set of tries we did. - */ - if ((info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || - (info->status.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) - rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_CTS); - else if (info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) - rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_RTS); - if (info->status.rates[0].idx >= 0 && - !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) - rthdr->rate = sband->bitrates[ - info->status.rates[0].idx].bitrate / 5; - - /* for now report the total retry_count */ - rthdr->data_retries = retry_count; + ieee80211_add_tx_radiotap_header(sband, skb, retry_count, rtap_len); /* XXX: is this sufficient for BPF? */ skb_set_mac_header(skb, 0); diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c index 757e4eb..51077a9 100644 --- a/net/mac80211/tkip.c +++ b/net/mac80211/tkip.c @@ -10,6 +10,7 @@ #include <linux/bitops.h> #include <linux/types.h> #include <linux/netdevice.h> +#include <linux/export.h> #include <asm/unaligned.h> #include <net/mac80211.h> @@ -101,6 +102,7 @@ static void tkip_mixing_phase1(const u8 *tk, struct tkip_ctx *ctx, p1k[4] += tkipS(p1k[3] ^ get_unaligned_le16(tk + 0 + j)) + i; } ctx->state = TKIP_STATE_PHASE1_DONE; + ctx->p1k_iv32 = tsc_IV32; } static void tkip_mixing_phase2(const u8 *tk, struct tkip_ctx *ctx, @@ -140,60 +142,80 @@ static void tkip_mixing_phase2(const u8 *tk, struct tkip_ctx *ctx, /* Add TKIP IV and Ext. IV at @pos. @iv0, @iv1, and @iv2 are the first octets * of the IV. Returns pointer to the octet following IVs (i.e., beginning of * the packet payload). */ -u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key, u16 iv16) +u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key) { - pos = write_tkip_iv(pos, iv16); + lockdep_assert_held(&key->u.tkip.txlock); + + pos = write_tkip_iv(pos, key->u.tkip.tx.iv16); *pos++ = (key->conf.keyidx << 6) | (1 << 5) /* Ext IV */; put_unaligned_le32(key->u.tkip.tx.iv32, pos); return pos + 4; } -void ieee80211_get_tkip_key(struct ieee80211_key_conf *keyconf, - struct sk_buff *skb, enum ieee80211_tkip_key_type type, - u8 *outkey) +static void ieee80211_compute_tkip_p1k(struct ieee80211_key *key, u32 iv32) { - struct ieee80211_key *key = (struct ieee80211_key *) - container_of(keyconf, struct ieee80211_key, conf); - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - u8 *data; - const u8 *tk; - struct tkip_ctx *ctx; - u16 iv16; - u32 iv32; + struct ieee80211_sub_if_data *sdata = key->sdata; + struct tkip_ctx *ctx = &key->u.tkip.tx; + const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; - data = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control); - iv16 = data[2] | (data[0] << 8); - iv32 = get_unaligned_le32(&data[4]); + lockdep_assert_held(&key->u.tkip.txlock); + + /* + * Update the P1K when the IV32 is different from the value it + * had when we last computed it (or when not initialised yet). + * This might flip-flop back and forth if packets are processed + * out-of-order due to the different ACs, but then we have to + * just compute the P1K more often. + */ + if (ctx->p1k_iv32 != iv32 || ctx->state == TKIP_STATE_NOT_INIT) + tkip_mixing_phase1(tk, ctx, sdata->vif.addr, iv32); +} - tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; - ctx = &key->u.tkip.tx; +void ieee80211_get_tkip_p1k_iv(struct ieee80211_key_conf *keyconf, + u32 iv32, u16 *p1k) +{ + struct ieee80211_key *key = (struct ieee80211_key *) + container_of(keyconf, struct ieee80211_key, conf); + struct tkip_ctx *ctx = &key->u.tkip.tx; + unsigned long flags; -#ifdef CONFIG_MAC80211_TKIP_DEBUG - printk(KERN_DEBUG "TKIP encrypt: iv16 = 0x%04x, iv32 = 0x%08x\n", - iv16, iv32); - - if (iv32 != ctx->iv32) { - printk(KERN_DEBUG "skb: iv32 = 0x%08x key: iv32 = 0x%08x\n", - iv32, ctx->iv32); - printk(KERN_DEBUG "Wrap around of iv16 in the middle of a " - "fragmented packet\n"); - } -#endif + spin_lock_irqsave(&key->u.tkip.txlock, flags); + ieee80211_compute_tkip_p1k(key, iv32); + memcpy(p1k, ctx->p1k, sizeof(ctx->p1k)); + spin_unlock_irqrestore(&key->u.tkip.txlock, flags); +} +EXPORT_SYMBOL(ieee80211_get_tkip_p1k_iv); - /* Update the p1k only when the iv16 in the packet wraps around, this - * might occur after the wrap around of iv16 in the key in case of - * fragmented packets. */ - if (iv16 == 0 || ctx->state == TKIP_STATE_NOT_INIT) - tkip_mixing_phase1(tk, ctx, hdr->addr2, iv32); +void ieee80211_get_tkip_rx_p1k(struct ieee80211_key_conf *keyconf, + const u8 *ta, u32 iv32, u16 *p1k) +{ + const u8 *tk = &keyconf->key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; + struct tkip_ctx ctx; - if (type == IEEE80211_TKIP_P1_KEY) { - memcpy(outkey, ctx->p1k, sizeof(u16) * 5); - return; - } + tkip_mixing_phase1(tk, &ctx, ta, iv32); + memcpy(p1k, ctx.p1k, sizeof(ctx.p1k)); +} +EXPORT_SYMBOL(ieee80211_get_tkip_rx_p1k); - tkip_mixing_phase2(tk, ctx, iv16, outkey); +void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf, + struct sk_buff *skb, u8 *p2k) +{ + struct ieee80211_key *key = (struct ieee80211_key *) + container_of(keyconf, struct ieee80211_key, conf); + const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; + struct tkip_ctx *ctx = &key->u.tkip.tx; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + const u8 *data = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control); + u32 iv32 = get_unaligned_le32(&data[4]); + u16 iv16 = data[2] | (data[0] << 8); + unsigned long flags; + + spin_lock_irqsave(&key->u.tkip.txlock, flags); + ieee80211_compute_tkip_p1k(key, iv32); + tkip_mixing_phase2(tk, ctx, iv16, p2k); + spin_unlock_irqrestore(&key->u.tkip.txlock, flags); } -EXPORT_SYMBOL(ieee80211_get_tkip_key); +EXPORT_SYMBOL(ieee80211_get_tkip_p2k); /* * Encrypt packet payload with TKIP using @key. @pos is a pointer to the @@ -204,19 +226,15 @@ EXPORT_SYMBOL(ieee80211_get_tkip_key); */ int ieee80211_tkip_encrypt_data(struct crypto_cipher *tfm, struct ieee80211_key *key, - u8 *pos, size_t payload_len, u8 *ta) + struct sk_buff *skb, + u8 *payload, size_t payload_len) { u8 rc4key[16]; - struct tkip_ctx *ctx = &key->u.tkip.tx; - const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; - - /* Calculate per-packet key */ - if (ctx->iv16 == 0 || ctx->state == TKIP_STATE_NOT_INIT) - tkip_mixing_phase1(tk, ctx, ta, ctx->iv32); - tkip_mixing_phase2(tk, ctx, ctx->iv16, rc4key); + ieee80211_get_tkip_p2k(&key->conf, skb, rc4key); - return ieee80211_wep_encrypt_data(tfm, rc4key, 16, pos, payload_len); + return ieee80211_wep_encrypt_data(tfm, rc4key, 16, + payload, payload_len); } /* Decrypt packet payload with TKIP using @key. @pos is a pointer to the diff --git a/net/mac80211/tkip.h b/net/mac80211/tkip.h index 1cab9c8..e3ecb65 100644 --- a/net/mac80211/tkip.h +++ b/net/mac80211/tkip.h @@ -13,11 +13,13 @@ #include <linux/crypto.h> #include "key.h" -u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key, u16 iv16); +u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key); int ieee80211_tkip_encrypt_data(struct crypto_cipher *tfm, - struct ieee80211_key *key, - u8 *pos, size_t payload_len, u8 *ta); + struct ieee80211_key *key, + struct sk_buff *skb, + u8 *payload, size_t payload_len); + enum { TKIP_DECRYPT_OK = 0, TKIP_DECRYPT_NO_EXT_IV = -1, diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index da878c1..91826b6 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -18,6 +18,7 @@ #include <linux/etherdevice.h> #include <linux/bitmap.h> #include <linux/rcupdate.h> +#include <linux/export.h> #include <net/net_namespace.h> #include <net/ieee80211_radiotap.h> #include <net/cfg80211.h> @@ -253,13 +254,12 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); - u32 sta_flags; + bool assoc = false; if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED)) return TX_CONTINUE; - if (unlikely(test_bit(SCAN_SW_SCANNING, &tx->local->scanning)) && - test_bit(SDATA_STATE_OFFCHANNEL, &tx->sdata->state) && + if (unlikely(test_bit(SCAN_OFF_CHANNEL, &tx->local->scanning)) && !ieee80211_is_probe_req(hdr->frame_control) && !ieee80211_is_nullfunc(hdr->frame_control)) /* @@ -278,16 +278,14 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) if (tx->sdata->vif.type == NL80211_IFTYPE_WDS) return TX_CONTINUE; - if (tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT) - return TX_CONTINUE; - if (tx->flags & IEEE80211_TX_PS_BUFFERED) return TX_CONTINUE; - sta_flags = tx->sta ? get_sta_flags(tx->sta) : 0; + if (tx->sta) + assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC); if (likely(tx->flags & IEEE80211_TX_UNICAST)) { - if (unlikely(!(sta_flags & WLAN_STA_ASSOC) && + if (unlikely(!assoc && tx->sdata->vif.type != NL80211_IFTYPE_ADHOC && ieee80211_is_data(hdr->frame_control))) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG @@ -343,13 +341,22 @@ static void purge_old_ps_buffers(struct ieee80211_local *local) total += skb_queue_len(&ap->ps_bc_buf); } + /* + * Drop one frame from each station from the lowest-priority + * AC that has frames at all. + */ list_for_each_entry_rcu(sta, &local->sta_list, list) { - skb = skb_dequeue(&sta->ps_tx_buf); - if (skb) { - purged++; - dev_kfree_skb(skb); + int ac; + + for (ac = IEEE80211_AC_BK; ac >= IEEE80211_AC_VO; ac--) { + skb = skb_dequeue(&sta->ps_tx_buf[ac]); + total += skb_queue_len(&sta->ps_tx_buf[ac]); + if (skb) { + purged++; + dev_kfree_skb(skb); + break; + } } - total += skb_queue_len(&sta->ps_tx_buf); } rcu_read_unlock(); @@ -418,7 +425,7 @@ static int ieee80211_use_mfp(__le16 fc, struct sta_info *sta, if (!ieee80211_is_mgmt(fc)) return 0; - if (sta == NULL || !test_sta_flags(sta, WLAN_STA_MFP)) + if (sta == NULL || !test_sta_flag(sta, WLAN_STA_MFP)) return 0; if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) @@ -435,7 +442,6 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; struct ieee80211_local *local = tx->local; - u32 staflags; if (unlikely(!sta || ieee80211_is_probe_resp(hdr->frame_control) || @@ -444,57 +450,67 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) ieee80211_is_reassoc_resp(hdr->frame_control))) return TX_CONTINUE; - staflags = get_sta_flags(sta); + if (unlikely((test_sta_flag(sta, WLAN_STA_PS_STA) || + test_sta_flag(sta, WLAN_STA_PS_DRIVER)) && + !(info->flags & IEEE80211_TX_CTL_POLL_RESPONSE))) { + int ac = skb_get_queue_mapping(tx->skb); - if (unlikely((staflags & (WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) && - !(info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE))) { #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - printk(KERN_DEBUG "STA %pM aid %d: PS buffer (entries " - "before %d)\n", - sta->sta.addr, sta->sta.aid, - skb_queue_len(&sta->ps_tx_buf)); + printk(KERN_DEBUG "STA %pM aid %d: PS buffer for AC %d\n", + sta->sta.addr, sta->sta.aid, ac); #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ 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); + + /* sync with ieee80211_sta_ps_deliver_wakeup */ + spin_lock(&sta->ps_lock); + /* + * STA woke up the meantime and all the frames on ps_tx_buf have + * been queued to pending queue. No reordering can happen, go + * ahead and Tx the packet. + */ + if (!test_sta_flag(sta, WLAN_STA_PS_STA) && + !test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { + spin_unlock(&sta->ps_lock); + return TX_CONTINUE; + } + + if (skb_queue_len(&sta->ps_tx_buf[ac]) >= STA_MAX_TX_BUFFER) { + struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf[ac]); #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: STA %pM TX " - "buffer full - dropping oldest frame\n", - tx->sdata->name, sta->sta.addr); - } + if (net_ratelimit()) + printk(KERN_DEBUG "%s: STA %pM TX buffer for " + "AC %d full - dropping oldest frame\n", + tx->sdata->name, sta->sta.addr, ac); #endif dev_kfree_skb(old); } else tx->local->total_ps_buffered++; - /* - * Queue frame to be sent after STA wakes up/polls, - * but don't set the TIM bit if the driver is blocking - * wakeup or poll response transmissions anyway. - */ - if (skb_queue_empty(&sta->ps_tx_buf) && - !(staflags & WLAN_STA_PS_DRIVER)) - sta_info_set_tim_bit(sta); - info->control.jiffies = jiffies; info->control.vif = &tx->sdata->vif; info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; - skb_queue_tail(&sta->ps_tx_buf, tx->skb); + skb_queue_tail(&sta->ps_tx_buf[ac], tx->skb); + spin_unlock(&sta->ps_lock); if (!timer_pending(&local->sta_cleanup)) mod_timer(&local->sta_cleanup, round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL)); + /* + * We queued up some frames, so the TIM bit might + * need to be set, recalculate it. + */ + sta_info_recalc_tim(sta); + return TX_QUEUED; } #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - else if (unlikely(staflags & WLAN_STA_PS_STA)) { - printk(KERN_DEBUG "%s: STA %pM in PS mode, but pspoll " - "set -> send frame\n", tx->sdata->name, - sta->sta.addr); + else if (unlikely(test_sta_flag(sta, WLAN_STA_PS_STA))) { + printk(KERN_DEBUG + "%s: STA %pM in PS mode, but polling/in SP -> send frame\n", + tx->sdata->name, sta->sta.addr); } #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ @@ -518,9 +534,11 @@ ieee80211_tx_h_check_control_port_protocol(struct ieee80211_tx_data *tx) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); - if (unlikely(tx->sdata->control_port_protocol == tx->skb->protocol && - tx->sdata->control_port_no_encrypt)) - info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + if (unlikely(tx->sdata->control_port_protocol == tx->skb->protocol)) { + if (tx->sdata->control_port_no_encrypt) + info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + info->flags |= IEEE80211_TX_CTL_USE_MINRATE; + } return TX_CONTINUE; } @@ -552,7 +570,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) !(info->flags & IEEE80211_TX_CTL_INJECTED) && (!ieee80211_is_robust_mgmt_frame(hdr) || (ieee80211_is_action(hdr->frame_control) && - tx->sta && test_sta_flags(tx->sta, WLAN_STA_MFP)))) { + tx->sta && test_sta_flag(tx->sta, WLAN_STA_MFP)))) { I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted); return TX_DROP; } else @@ -589,6 +607,9 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) break; } + if (unlikely(tx->key && tx->key->flags & KEY_FLAG_TAINTED)) + return TX_DROP; + if (!skip_hw && tx->key && tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) info->control.hw_key = &tx->key->conf; @@ -608,7 +629,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) u32 len; bool inval = false, rts = false, short_preamble = false; struct ieee80211_tx_rate_control txrc; - u32 sta_flags; + bool assoc = false; memset(&txrc, 0, sizeof(txrc)); @@ -644,17 +665,17 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) */ if (tx->sdata->vif.bss_conf.use_short_preamble && (ieee80211_is_data(hdr->frame_control) || - (tx->sta && test_sta_flags(tx->sta, WLAN_STA_SHORT_PREAMBLE)))) + (tx->sta && test_sta_flag(tx->sta, WLAN_STA_SHORT_PREAMBLE)))) txrc.short_preamble = short_preamble = true; - sta_flags = tx->sta ? get_sta_flags(tx->sta) : 0; + if (tx->sta) + assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC); /* * Lets not bother rate control if we're associated and cannot * talk to the sta. This should not happen. */ - if (WARN(test_bit(SCAN_SW_SCANNING, &tx->local->scanning) && - (sta_flags & WLAN_STA_ASSOC) && + if (WARN(test_bit(SCAN_SW_SCANNING, &tx->local->scanning) && assoc && !rate_usable_index_exists(sband, &tx->sta->sta), "%s: Dropped data frame as no usable bitrate found while " "scanning and associated. Target station: " @@ -797,6 +818,9 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) if (ieee80211_hdrlen(hdr->frame_control) < 24) return TX_CONTINUE; + if (ieee80211_is_qos_nullfunc(hdr->frame_control)) + return TX_CONTINUE; + /* * Anything but QoS data that has a sequence number field * (is long enough) gets a sequence number from the global @@ -874,7 +898,7 @@ static int ieee80211_fragment(struct ieee80211_local *local, pos += fraglen; } - skb->len = hdrlen + per_fragm; + skb_trim(skb, hdrlen + per_fragm); return 0; } @@ -888,7 +912,10 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) int hdrlen; int fragnum; - if (!(tx->flags & IEEE80211_TX_FRAGMENTED)) + if (info->flags & IEEE80211_TX_CTL_DONTFRAG) + return TX_CONTINUE; + + if (tx->local->ops->set_frag_threshold) return TX_CONTINUE; /* @@ -901,7 +928,7 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) hdrlen = ieee80211_hdrlen(hdr->frame_control); - /* internal error, why is TX_FRAGMENTED set? */ + /* internal error, why isn't DONTFRAG set? */ if (WARN_ON(skb->len + FCS_LEN <= frag_threshold)) return TX_DROP; @@ -1022,100 +1049,6 @@ ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx) /* actual transmit path */ -/* - * deal with packet injection down monitor interface - * with Radiotap Header -- only called for monitor mode interface - */ -static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx, - struct sk_buff *skb) -{ - /* - * this is the moment to interpret and discard the radiotap header that - * must be at the start of the packet injected in Monitor mode - * - * Need to take some care with endian-ness since radiotap - * args are little-endian - */ - - struct ieee80211_radiotap_iterator iterator; - struct ieee80211_radiotap_header *rthdr = - (struct ieee80211_radiotap_header *) skb->data; - bool hw_frag; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len, - NULL); - - info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; - tx->flags &= ~IEEE80211_TX_FRAGMENTED; - - /* packet is fragmented in HW if we have a non-NULL driver callback */ - hw_frag = (tx->local->ops->set_frag_threshold != NULL); - - /* - * for every radiotap entry that is present - * (ieee80211_radiotap_iterator_next returns -ENOENT when no more - * entries present, or -EINVAL on error) - */ - - while (!ret) { - 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_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 false; - - skb_trim(skb, skb->len - FCS_LEN); - } - if (*iterator.this_arg & IEEE80211_RADIOTAP_F_WEP) - info->flags &= ~IEEE80211_TX_INTFL_DONT_ENCRYPT; - if ((*iterator.this_arg & IEEE80211_RADIOTAP_F_FRAG) && - !hw_frag) - tx->flags |= IEEE80211_TX_FRAGMENTED; - break; - - /* - * Please update the file - * Documentation/networking/mac80211-injection.txt - * when parsing new fields here. - */ - - default: - break; - } - } - - if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */ - return false; - - /* - * remove the radiotap header - * iterator->_max_length was sanity-checked against - * skb->len by iterator init - */ - skb_pull(skb, iterator._max_length); - - return true; -} - static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx, struct sk_buff *skb, struct ieee80211_tx_info *info, @@ -1180,7 +1113,7 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee80211_hdr *hdr; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - int hdrlen, tid; + int tid; u8 *qc; memset(tx, 0, sizeof(*tx)); @@ -1188,26 +1121,6 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, tx->local = local; tx->sdata = sdata; tx->channel = local->hw.conf.channel; - /* - * Set this flag (used below to indicate "automatic fragmentation"), - * it will be cleared/left by radiotap as desired. - * Only valid when fragmentation is done by the stack. - */ - if (!local->ops->set_frag_threshold) - tx->flags |= IEEE80211_TX_FRAGMENTED; - - /* process and remove the injection radiotap header */ - if (unlikely(info->flags & IEEE80211_TX_INTFL_HAS_RADIOTAP)) { - if (!__ieee80211_parse_tx_radiotap(tx, skb)) - return TX_DROP; - - /* - * __ieee80211_parse_tx_radiotap has now removed - * the radiotap header that was present and pre-filled - * 'tx' with tx control information. - */ - info->flags &= ~IEEE80211_TX_INTFL_HAS_RADIOTAP; - } /* * If this flag is set to true anywhere, and we get here, @@ -1230,7 +1143,9 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, tx->sta = sta_info_get(sdata, hdr->addr1); if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) && - (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)) { + !ieee80211_is_qos_nullfunc(hdr->frame_control) && + (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) && + !(local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW)) { struct tid_ampdu_tx *tid_tx; qc = ieee80211_get_qos_ctl(hdr); @@ -1255,29 +1170,25 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, tx->flags |= IEEE80211_TX_UNICAST; if (unlikely(local->wifi_wme_noack_test)) info->flags |= IEEE80211_TX_CTL_NO_ACK; - else - info->flags &= ~IEEE80211_TX_CTL_NO_ACK; + /* + * Flags are initialized to 0. Hence, no need to + * explicitly unset IEEE80211_TX_CTL_NO_ACK since + * it might already be set for injected frames. + */ } - if (tx->flags & IEEE80211_TX_FRAGMENTED) { - if ((tx->flags & IEEE80211_TX_UNICAST) && - skb->len + FCS_LEN > local->hw.wiphy->frag_threshold && - !(info->flags & IEEE80211_TX_CTL_AMPDU)) - tx->flags |= IEEE80211_TX_FRAGMENTED; - else - tx->flags &= ~IEEE80211_TX_FRAGMENTED; + if (!(info->flags & IEEE80211_TX_CTL_DONTFRAG)) { + if (!(tx->flags & IEEE80211_TX_UNICAST) || + skb->len + FCS_LEN <= local->hw.wiphy->frag_threshold || + info->flags & IEEE80211_TX_CTL_AMPDU) + info->flags |= IEEE80211_TX_CTL_DONTFRAG; } if (!tx->sta) info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; - else if (test_and_clear_sta_flags(tx->sta, WLAN_STA_CLEAR_PS_FILT)) + else if (test_and_clear_sta_flag(tx->sta, WLAN_STA_CLEAR_PS_FILT)) info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; - hdrlen = ieee80211_hdrlen(hdr->frame_control); - if (skb->len > hdrlen + sizeof(rfc1042_header) + 2) { - u8 *pos = &skb->data[hdrlen + sizeof(rfc1042_header)]; - tx->ethertype = (pos[0] << 8) | pos[1]; - } info->flags |= IEEE80211_TX_CTL_FIRST_FRAGMENT; return TX_CONTINUE; @@ -1475,28 +1386,19 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, /* device xmit handlers */ -static int ieee80211_skb_resize(struct ieee80211_local *local, +static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, int head_need, bool may_encrypt) { + struct ieee80211_local *local = sdata->local; int tail_need = 0; - /* - * This could be optimised, devices that do full hardware - * crypto (including TKIP MMIC) need no tailroom... But we - * have no drivers for such devices currently. - */ - if (may_encrypt) { + if (may_encrypt && sdata->crypto_tx_tailroom_needed_cnt) { tail_need = IEEE80211_ENCRYPT_TAILROOM; tail_need -= skb_tailroom(skb); tail_need = max_t(int, tail_need, 0); } - if (head_need || tail_need) { - /* Sorry. Can't account for this any more */ - skb_orphan(skb); - } - if (skb_cloned(skb)) I802_DEBUG_INC(local->tx_expand_skb_head_cloned); else if (head_need || tail_need) @@ -1510,67 +1412,19 @@ static int ieee80211_skb_resize(struct ieee80211_local *local, return -ENOMEM; } - /* update truesize too */ - skb->truesize += head_need + tail_need; - return 0; } -static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb) +void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - struct ieee80211_sub_if_data *tmp_sdata; int headroom; bool may_encrypt; rcu_read_lock(); - if (unlikely(sdata->vif.type == NL80211_IFTYPE_MONITOR)) { - int hdrlen; - u16 len_rthdr; - - info->flags |= IEEE80211_TX_CTL_INJECTED | - IEEE80211_TX_INTFL_HAS_RADIOTAP; - - len_rthdr = ieee80211_get_radiotap_len(skb->data); - hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr); - hdrlen = ieee80211_hdrlen(hdr->frame_control); - - /* check the header is complete in the frame */ - if (likely(skb->len >= len_rthdr + hdrlen)) { - /* - * We process outgoing injected frames that have a - * local address we handle as though they are our - * own frames. - * This code here isn't entirely correct, the local - * MAC address is not necessarily enough to find - * the interface to use; for that proper VLAN/WDS - * support we will need a different mechanism. - */ - - list_for_each_entry_rcu(tmp_sdata, &local->interfaces, - list) { - if (!ieee80211_sdata_running(tmp_sdata)) - continue; - if (tmp_sdata->vif.type == - NL80211_IFTYPE_MONITOR || - tmp_sdata->vif.type == - NL80211_IFTYPE_AP_VLAN || - tmp_sdata->vif.type == - NL80211_IFTYPE_WDS) - continue; - if (compare_ether_addr(tmp_sdata->vif.addr, - hdr->addr2) == 0) { - sdata = tmp_sdata; - break; - } - } - } - } - may_encrypt = !(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT); headroom = local->tx_headroom; @@ -1579,7 +1433,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, headroom -= skb_headroom(skb); headroom = max_t(int, 0, headroom); - if (ieee80211_skb_resize(local, skb, headroom, may_encrypt)) { + if (ieee80211_skb_resize(sdata, skb, headroom, may_encrypt)) { dev_kfree_skb(skb); rcu_read_unlock(); return; @@ -1597,11 +1451,94 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, return; } - ieee80211_set_qos_hdr(local, skb); + ieee80211_set_qos_hdr(sdata, skb); ieee80211_tx(sdata, skb, false); rcu_read_unlock(); } +static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb) +{ + struct ieee80211_radiotap_iterator iterator; + struct ieee80211_radiotap_header *rthdr = + (struct ieee80211_radiotap_header *) skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len, + NULL); + u16 txflags; + + info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | + IEEE80211_TX_CTL_DONTFRAG; + + /* + * for every radiotap entry that is present + * (ieee80211_radiotap_iterator_next returns -ENOENT when no more + * entries present, or -EINVAL on error) + */ + + while (!ret) { + 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_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 false; + + skb_trim(skb, skb->len - FCS_LEN); + } + if (*iterator.this_arg & IEEE80211_RADIOTAP_F_WEP) + info->flags &= ~IEEE80211_TX_INTFL_DONT_ENCRYPT; + if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FRAG) + info->flags &= ~IEEE80211_TX_CTL_DONTFRAG; + break; + + case IEEE80211_RADIOTAP_TX_FLAGS: + txflags = get_unaligned_le16(iterator.this_arg); + if (txflags & IEEE80211_RADIOTAP_F_TX_NOACK) + info->flags |= IEEE80211_TX_CTL_NO_ACK; + break; + + /* + * Please update the file + * Documentation/networking/mac80211-injection.txt + * when parsing new fields here. + */ + + default: + break; + } + } + + if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */ + return false; + + /* + * remove the radiotap header + * iterator->_max_length was sanity-checked against + * skb->len by iterator init + */ + skb_pull(skb, iterator._max_length); + + return true; +} + netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev) { @@ -1610,7 +1547,10 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, struct ieee80211_radiotap_header *prthdr = (struct ieee80211_radiotap_header *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr; + struct ieee80211_sub_if_data *tmp_sdata, *sdata; u16 len_rthdr; + int hdrlen; /* * Frame injection is not allowed if beaconing is not allowed @@ -1661,12 +1601,65 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, skb_set_network_header(skb, len_rthdr); skb_set_transport_header(skb, len_rthdr); + if (skb->len < len_rthdr + 2) + goto fail; + + hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr); + hdrlen = ieee80211_hdrlen(hdr->frame_control); + + if (skb->len < len_rthdr + hdrlen) + goto fail; + + /* + * Initialize skb->protocol if the injected frame is a data frame + * carrying a rfc1042 header + */ + if (ieee80211_is_data(hdr->frame_control) && + skb->len >= len_rthdr + hdrlen + sizeof(rfc1042_header) + 2) { + u8 *payload = (u8 *)hdr + hdrlen; + + if (compare_ether_addr(payload, rfc1042_header) == 0) + skb->protocol = cpu_to_be16((payload[6] << 8) | + payload[7]); + } + memset(info, 0, sizeof(*info)); - info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS | + IEEE80211_TX_CTL_INJECTED; + + /* process and remove the injection radiotap header */ + if (!ieee80211_parse_tx_radiotap(skb)) + goto fail; + + rcu_read_lock(); + + /* + * We process outgoing injected frames that have a local address + * we handle as though they are non-injected frames. + * This code here isn't entirely correct, the local MAC address + * isn't always enough to find the interface to use; for proper + * VLAN/WDS support we will need a different mechanism (which + * likely isn't going to be monitor interfaces). + */ + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + list_for_each_entry_rcu(tmp_sdata, &local->interfaces, list) { + if (!ieee80211_sdata_running(tmp_sdata)) + continue; + if (tmp_sdata->vif.type == NL80211_IFTYPE_MONITOR || + tmp_sdata->vif.type == NL80211_IFTYPE_AP_VLAN || + tmp_sdata->vif.type == NL80211_IFTYPE_WDS) + continue; + if (compare_ether_addr(tmp_sdata->vif.addr, hdr->addr2) == 0) { + sdata = tmp_sdata; + break; + } + } + + ieee80211_xmit(sdata, skb); + rcu_read_unlock(); - /* pass the radiotap header up to xmit */ - ieee80211_xmit(IEEE80211_DEV_TO_SUB_IF(dev), skb); return NETDEV_TX_OK; fail: @@ -1705,8 +1698,9 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, int encaps_len, skip_header_bytes; int nh_pos, h_pos; struct sta_info *sta = NULL; - u32 sta_flags = 0; + bool wme_sta = false, authorized = false, tdls_auth = false; struct sk_buff *tmp_skb; + bool tdls_direct = false; if (unlikely(skb->len < ETH_HLEN)) { ret = NETDEV_TX_OK; @@ -1730,7 +1724,8 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, memcpy(hdr.addr3, skb->data, ETH_ALEN); memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); hdrlen = 30; - sta_flags = get_sta_flags(sta); + authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); + wme_sta = test_sta_flag(sta, WLAN_STA_WME); } rcu_read_unlock(); if (sta) @@ -1818,11 +1813,50 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, break; #endif case NL80211_IFTYPE_STATION: - memcpy(hdr.addr1, sdata->u.mgd.bssid, ETH_ALEN); - if (sdata->u.mgd.use_4addr && - cpu_to_be16(ethertype) != sdata->control_port_protocol) { - fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); + if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) { + bool tdls_peer = false; + + rcu_read_lock(); + sta = sta_info_get(sdata, skb->data); + if (sta) { + authorized = test_sta_flag(sta, + WLAN_STA_AUTHORIZED); + wme_sta = test_sta_flag(sta, WLAN_STA_WME); + tdls_peer = test_sta_flag(sta, + WLAN_STA_TDLS_PEER); + tdls_auth = test_sta_flag(sta, + WLAN_STA_TDLS_PEER_AUTH); + } + rcu_read_unlock(); + + /* + * If the TDLS link is enabled, send everything + * directly. Otherwise, allow TDLS setup frames + * to be transmitted indirectly. + */ + tdls_direct = tdls_peer && (tdls_auth || + !(ethertype == ETH_P_TDLS && skb->len > 14 && + skb->data[14] == WLAN_TDLS_SNAP_RFTYPE)); + } + + if (tdls_direct) { + /* link during setup - throw out frames to peer */ + if (!tdls_auth) { + ret = NETDEV_TX_OK; + goto fail; + } + + /* DA SA BSSID */ + memcpy(hdr.addr1, skb->data, ETH_ALEN); + memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); + memcpy(hdr.addr3, sdata->u.mgd.bssid, ETH_ALEN); + hdrlen = 24; + } else if (sdata->u.mgd.use_4addr && + cpu_to_be16(ethertype) != sdata->control_port_protocol) { + fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | + IEEE80211_FCTL_TODS); /* RA TA DA SA */ + memcpy(hdr.addr1, sdata->u.mgd.bssid, ETH_ALEN); memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN); memcpy(hdr.addr3, skb->data, ETH_ALEN); memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); @@ -1830,6 +1864,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, } else { fc |= cpu_to_le16(IEEE80211_FCTL_TODS); /* BSSID SA DA */ + memcpy(hdr.addr1, sdata->u.mgd.bssid, ETH_ALEN); memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); memcpy(hdr.addr3, skb->data, ETH_ALEN); hdrlen = 24; @@ -1855,13 +1890,19 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, if (!is_multicast_ether_addr(hdr.addr1)) { rcu_read_lock(); sta = sta_info_get(sdata, hdr.addr1); - if (sta) - sta_flags = get_sta_flags(sta); + if (sta) { + authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); + wme_sta = test_sta_flag(sta, WLAN_STA_WME); + } rcu_read_unlock(); } + /* For mesh, the use of the QoS header is mandatory */ + if (ieee80211_vif_is_mesh(&sdata->vif)) + wme_sta = true; + /* receiver and we are QoS enabled, use a QoS type frame */ - if ((sta_flags & WLAN_STA_WME) && local->hw.queues >= 4) { + if (wme_sta && local->hw.queues >= 4) { fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA); hdrlen += 2; } @@ -1870,12 +1911,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, * Drop unicast frames to unauthorised stations unless they are * EAPOL frames from the local station. */ - if (!ieee80211_vif_is_mesh(&sdata->vif) && - unlikely(!is_multicast_ether_addr(hdr.addr1) && - !(sta_flags & WLAN_STA_AUTHORIZED) && - !(cpu_to_be16(ethertype) == sdata->control_port_protocol && - compare_ether_addr(sdata->vif.addr, - skb->data + ETH_ALEN) == 0))) { + if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) && + !is_multicast_ether_addr(hdr.addr1) && !authorized && + (cpu_to_be16(ethertype) != sdata->control_port_protocol || + compare_ether_addr(sdata->vif.addr, skb->data + ETH_ALEN)))) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG if (net_ratelimit()) printk(KERN_DEBUG "%s: dropped frame to %pM" @@ -1946,7 +1985,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, head_need += IEEE80211_ENCRYPT_HEADROOM; head_need += local->tx_headroom; head_need = max_t(int, 0, head_need); - if (ieee80211_skb_resize(local, skb, head_need, true)) + if (ieee80211_skb_resize(sdata, skb, head_need, true)) goto fail; } @@ -2277,13 +2316,23 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); mgmt->u.beacon.beacon_int = cpu_to_le16(sdata->vif.bss_conf.beacon_int); - mgmt->u.beacon.capab_info = 0x0; /* 0x0 for MPs */ + mgmt->u.beacon.capab_info |= cpu_to_le16( + sdata->u.mesh.security ? WLAN_CAPABILITY_PRIVACY : 0); pos = skb_put(skb, 2); *pos++ = WLAN_EID_SSID; *pos++ = 0x0; - mesh_mgmt_ies_add(skb, sdata); + if (ieee80211_add_srates_ie(&sdata->vif, skb) || + mesh_add_ds_params_ie(skb, sdata) || + ieee80211_add_ext_srates_ie(&sdata->vif, skb) || + mesh_add_rsn_ie(skb, sdata) || + mesh_add_meshid_ie(skb, sdata) || + mesh_add_meshconf_ie(skb, sdata) || + mesh_add_vendor_ies(skb, sdata)) { + pr_err("o11s: couldn't add ies!\n"); + goto out; + } } else { WARN_ON(1); goto out; @@ -2337,11 +2386,9 @@ struct sk_buff *ieee80211_pspoll_get(struct ieee80211_hw *hw, local = sdata->local; skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*pspoll)); - if (!skb) { - printk(KERN_DEBUG "%s: failed to allocate buffer for " - "pspoll template\n", sdata->name); + if (!skb) return NULL; - } + skb_reserve(skb, local->hw.extra_tx_headroom); pspoll = (struct ieee80211_pspoll *) skb_put(skb, sizeof(*pspoll)); @@ -2377,11 +2424,9 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw, local = sdata->local; skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*nullfunc)); - if (!skb) { - printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc " - "template\n", sdata->name); + if (!skb) return NULL; - } + skb_reserve(skb, local->hw.extra_tx_headroom); nullfunc = (struct ieee80211_hdr_3addr *) skb_put(skb, @@ -2416,11 +2461,8 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*hdr) + ie_ssid_len + ie_len); - if (!skb) { - printk(KERN_DEBUG "%s: failed to allocate buffer for probe " - "request template\n", sdata->name); + if (!skb) return NULL; - } skb_reserve(skb, local->hw.extra_tx_headroom); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 11d9d49..7095ae5 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -13,13 +13,13 @@ #include <net/mac80211.h> #include <linux/netdevice.h> +#include <linux/export.h> #include <linux/types.h> #include <linux/slab.h> #include <linux/skbuff.h> #include <linux/etherdevice.h> #include <linux/if_arp.h> #include <linux/bitmap.h> -#include <linux/crc32.h> #include <net/net_namespace.h> #include <net/cfg80211.h> #include <net/rtnetlink.h> @@ -368,14 +368,14 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local, spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } -int ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, - struct sk_buff_head *skbs, - void (*fn)(void *data), void *data) +void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, + struct sk_buff_head *skbs, + void (*fn)(void *data), void *data) { struct ieee80211_hw *hw = &local->hw; struct sk_buff *skb; unsigned long flags; - int queue, ret = 0, i; + int queue, i; spin_lock_irqsave(&local->queue_stop_reason_lock, flags); for (i = 0; i < hw->queues; i++) @@ -390,7 +390,6 @@ int ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, continue; } - ret++; queue = skb_get_queue_mapping(skb); __skb_queue_tail(&local->pending[queue], skb); } @@ -402,14 +401,12 @@ int ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, __ieee80211_wake_queue(hw, i, IEEE80211_QUEUE_STOP_REASON_SKB_ADD); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); - - return ret; } -int ieee80211_add_pending_skbs(struct ieee80211_local *local, - struct sk_buff_head *skbs) +void ieee80211_add_pending_skbs(struct ieee80211_local *local, + struct sk_buff_head *skbs) { - return ieee80211_add_pending_skbs_fn(local, skbs, NULL, NULL); + ieee80211_add_pending_skbs_fn(local, skbs, NULL, NULL); } void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, @@ -573,172 +570,6 @@ void ieee802_11_parse_elems(u8 *start, size_t len, ieee802_11_parse_elems_crc(start, len, elems, 0, 0); } -u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, - struct ieee802_11_elems *elems, - u64 filter, u32 crc) -{ - size_t left = len; - u8 *pos = start; - bool calc_crc = filter != 0; - - memset(elems, 0, sizeof(*elems)); - elems->ie_start = start; - elems->total_len = len; - - while (left >= 2) { - u8 id, elen; - - id = *pos++; - elen = *pos++; - left -= 2; - - if (elen > left) - break; - - if (calc_crc && id < 64 && (filter & (1ULL << id))) - crc = crc32_be(crc, pos - 2, elen + 2); - - switch (id) { - case WLAN_EID_SSID: - elems->ssid = pos; - elems->ssid_len = elen; - break; - case WLAN_EID_SUPP_RATES: - elems->supp_rates = pos; - elems->supp_rates_len = elen; - break; - case WLAN_EID_FH_PARAMS: - elems->fh_params = pos; - elems->fh_params_len = elen; - break; - case WLAN_EID_DS_PARAMS: - elems->ds_params = pos; - elems->ds_params_len = elen; - break; - case WLAN_EID_CF_PARAMS: - elems->cf_params = pos; - elems->cf_params_len = elen; - break; - case WLAN_EID_TIM: - if (elen >= sizeof(struct ieee80211_tim_ie)) { - elems->tim = (void *)pos; - elems->tim_len = elen; - } - break; - case WLAN_EID_IBSS_PARAMS: - elems->ibss_params = pos; - elems->ibss_params_len = elen; - break; - case WLAN_EID_CHALLENGE: - elems->challenge = pos; - elems->challenge_len = elen; - break; - case WLAN_EID_VENDOR_SPECIFIC: - if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && - pos[2] == 0xf2) { - /* Microsoft OUI (00:50:F2) */ - - if (calc_crc) - crc = crc32_be(crc, pos - 2, elen + 2); - - if (pos[3] == 1) { - /* OUI Type 1 - WPA IE */ - elems->wpa = pos; - elems->wpa_len = elen; - } else if (elen >= 5 && pos[3] == 2) { - /* OUI Type 2 - WMM IE */ - if (pos[4] == 0) { - elems->wmm_info = pos; - elems->wmm_info_len = elen; - } else if (pos[4] == 1) { - elems->wmm_param = pos; - elems->wmm_param_len = elen; - } - } - } - break; - case WLAN_EID_RSN: - elems->rsn = pos; - elems->rsn_len = elen; - break; - case WLAN_EID_ERP_INFO: - elems->erp_info = pos; - elems->erp_info_len = elen; - break; - case WLAN_EID_EXT_SUPP_RATES: - elems->ext_supp_rates = pos; - elems->ext_supp_rates_len = elen; - break; - case WLAN_EID_HT_CAPABILITY: - if (elen >= sizeof(struct ieee80211_ht_cap)) - elems->ht_cap_elem = (void *)pos; - break; - case WLAN_EID_HT_INFORMATION: - if (elen >= sizeof(struct ieee80211_ht_info)) - elems->ht_info_elem = (void *)pos; - break; - case WLAN_EID_MESH_ID: - elems->mesh_id = pos; - elems->mesh_id_len = elen; - break; - case WLAN_EID_MESH_CONFIG: - if (elen >= sizeof(struct ieee80211_meshconf_ie)) - elems->mesh_config = (void *)pos; - break; - case WLAN_EID_PEER_LINK: - elems->peer_link = pos; - elems->peer_link_len = elen; - break; - case WLAN_EID_PREQ: - elems->preq = pos; - elems->preq_len = elen; - break; - case WLAN_EID_PREP: - elems->prep = pos; - elems->prep_len = elen; - break; - case WLAN_EID_PERR: - elems->perr = pos; - elems->perr_len = elen; - break; - case WLAN_EID_RANN: - if (elen >= sizeof(struct ieee80211_rann_ie)) - elems->rann = (void *)pos; - break; - case WLAN_EID_CHANNEL_SWITCH: - elems->ch_switch_elem = pos; - elems->ch_switch_elem_len = elen; - break; - case WLAN_EID_QUIET: - if (!elems->quiet_elem) { - elems->quiet_elem = pos; - elems->quiet_elem_len = elen; - } - elems->num_of_quiet_elem++; - break; - case WLAN_EID_COUNTRY: - elems->country_elem = pos; - elems->country_elem_len = elen; - break; - case WLAN_EID_PWR_CONSTRAINT: - elems->pwr_constr_elem = pos; - elems->pwr_constr_elem_len = elen; - break; - case WLAN_EID_TIMEOUT_INTERVAL: - elems->timeout_int = pos; - elems->timeout_int_len = elen; - break; - default: - break; - } - - left -= elen; - pos += elen; - } - - return crc; -} - void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; @@ -799,7 +630,8 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata) qparam.uapsd = false; - drv_conf_tx(local, queue, &qparam); + sdata->tx_conf[queue] = qparam; + drv_conf_tx(local, sdata, queue, &qparam); } /* after reinitialize QoS TX queues setting to default, @@ -873,11 +705,9 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 6 + extra_len); - if (!skb) { - printk(KERN_DEBUG "%s: failed to allocate buffer for auth " - "frame\n", sdata->name); + if (!skb) return; - } + skb_reserve(skb, local->hw.extra_tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6); @@ -1016,9 +846,10 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, } struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, - u8 *dst, + u8 *dst, u32 ratemask, const u8 *ssid, size_t ssid_len, - const u8 *ie, size_t ie_len) + const u8 *ie, size_t ie_len, + bool directed) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; @@ -1029,20 +860,23 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, /* FIXME: come up with a proper value */ buf = kmalloc(200 + ie_len, GFP_KERNEL); - if (!buf) { - printk(KERN_DEBUG "%s: failed to allocate temporary IE " - "buffer\n", sdata->name); + if (!buf) return NULL; - } - chan = ieee80211_frequency_to_channel( - local->hw.conf.channel->center_freq); + /* + * Do not send DS Channel parameter for directed probe requests + * in order to maximize the chance that we get a response. Some + * badly-behaved APs don't respond when this parameter is included. + */ + if (directed) + chan = 0; + else + chan = ieee80211_frequency_to_channel( + local->hw.conf.channel->center_freq); buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len, local->hw.conf.channel->band, - sdata->rc_rateidx_mask - [local->hw.conf.channel->band], - chan); + ratemask, chan); skb = ieee80211_probereq_get(&local->hw, &sdata->vif, ssid, ssid_len, @@ -1066,13 +900,19 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, const u8 *ssid, size_t ssid_len, - const u8 *ie, size_t ie_len) + const u8 *ie, size_t ie_len, + u32 ratemask, bool directed, bool no_cck) { struct sk_buff *skb; - skb = ieee80211_build_probe_req(sdata, dst, ssid, ssid_len, ie, ie_len); - if (skb) + skb = ieee80211_build_probe_req(sdata, dst, ratemask, ssid, ssid_len, + ie, ie_len, directed); + if (skb) { + if (no_cck) + IEEE80211_SKB_CB(skb)->flags |= + IEEE80211_TX_CTL_NO_CCK_RATE; ieee80211_tx_skb(sdata, skb); + } } u32 ieee80211_sta_get_rates(struct ieee80211_local *local, @@ -1127,7 +967,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) struct ieee80211_hw *hw = &local->hw; struct ieee80211_sub_if_data *sdata; struct sta_info *sta; - int res; + int res, i; #ifdef CONFIG_PM if (local->suspended) @@ -1150,27 +990,37 @@ int ieee80211_reconfig(struct ieee80211_local *local) } #endif - /* restart hardware */ - if (local->open_count) { - /* - * Upon resume hardware can sometimes be goofy due to - * various platform / driver / bus issues, so restarting - * the device may at times not work immediately. Propagate - * the error. - */ - res = drv_start(local); - if (res) { - WARN(local->suspended, "Hardware became unavailable " - "upon resume. This could be a software issue " - "prior to suspend or a hardware issue.\n"); - return res; - } + /* setup fragmentation threshold */ + drv_set_frag_threshold(local, hw->wiphy->frag_threshold); + + /* setup RTS threshold */ + drv_set_rts_threshold(local, hw->wiphy->rts_threshold); - ieee80211_led_radio(local, true); - ieee80211_mod_tpt_led_trig(local, - IEEE80211_TPT_LEDTRIG_FL_RADIO, 0); + /* reset coverage class */ + drv_set_coverage_class(local, hw->wiphy->coverage_class); + + /* everything else happens only if HW was up & running */ + if (!local->open_count) + goto wake_up; + + /* + * Upon resume hardware can sometimes be goofy due to + * various platform / driver / bus issues, so restarting + * the device may at times not work immediately. Propagate + * the error. + */ + res = drv_start(local); + if (res) { + WARN(local->suspended, "Hardware became unavailable " + "upon resume. This could be a software issue " + "prior to suspend or a hardware issue.\n"); + return res; } + ieee80211_led_radio(local, true); + ieee80211_mod_tpt_led_trig(local, + IEEE80211_TPT_LEDTRIG_FL_RADIO, 0); + /* add interfaces */ list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && @@ -1194,11 +1044,16 @@ int ieee80211_reconfig(struct ieee80211_local *local) } mutex_unlock(&local->sta_mtx); - /* setup fragmentation threshold */ - drv_set_frag_threshold(local, hw->wiphy->frag_threshold); + /* reconfigure tx conf */ + list_for_each_entry(sdata, &local->interfaces, list) { + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN || + sdata->vif.type == NL80211_IFTYPE_MONITOR || + !ieee80211_sdata_running(sdata)) + continue; - /* setup RTS threshold */ - drv_set_rts_threshold(local, hw->wiphy->rts_threshold); + for (i = 0; i < hw->queues; i++) + drv_conf_tx(local, sdata, i, &sdata->tx_conf[i]); + } /* reconfigure hardware */ ieee80211_hw_config(local, ~0); @@ -1234,6 +1089,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) changed |= BSS_CHANGED_IBSS; /* fall through */ case NL80211_IFTYPE_AP: + changed |= BSS_CHANGED_SSID; + /* fall through */ case NL80211_IFTYPE_MESH_POINT: changed |= BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED; @@ -1275,7 +1132,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) list_for_each_entry(sta, &local->sta_list, list) { ieee80211_sta_tear_down_BA_sessions(sta, true); - clear_sta_flags(sta, WLAN_STA_BLOCK_BA); + clear_sta_flag(sta, WLAN_STA_BLOCK_BA); } mutex_unlock(&local->sta_mtx); @@ -1325,6 +1182,33 @@ int ieee80211_reconfig(struct ieee80211_local *local) return 0; } +void ieee80211_resume_disconnect(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata; + struct ieee80211_local *local; + struct ieee80211_key *key; + + if (WARN_ON(!vif)) + return; + + sdata = vif_to_sdata(vif); + local = sdata->local; + + if (WARN_ON(!local->resuming)) + return; + + if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) + return; + + sdata->flags |= IEEE80211_SDATA_DISCONNECT_RESUME; + + mutex_lock(&local->key_mtx); + list_for_each_entry(key, &sdata->key_list, list) + key->flags |= KEY_FLAG_TAINTED; + mutex_unlock(&local->key_mtx); +} +EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect); + static int check_mgd_smps(struct ieee80211_if_managed *ifmgd, enum ieee80211_smps_mode *smps_mode) { @@ -1441,3 +1325,100 @@ size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset) return pos; } + +static void _ieee80211_enable_rssi_reports(struct ieee80211_sub_if_data *sdata, + int rssi_min_thold, + int rssi_max_thold) +{ + trace_api_enable_rssi_reports(sdata, rssi_min_thold, rssi_max_thold); + + if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) + return; + + /* + * Scale up threshold values before storing it, as the RSSI averaging + * algorithm uses a scaled up value as well. Change this scaling + * factor if the RSSI averaging algorithm changes. + */ + sdata->u.mgd.rssi_min_thold = rssi_min_thold*16; + sdata->u.mgd.rssi_max_thold = rssi_max_thold*16; +} + +void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif, + int rssi_min_thold, + int rssi_max_thold) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + WARN_ON(rssi_min_thold == rssi_max_thold || + rssi_min_thold > rssi_max_thold); + + _ieee80211_enable_rssi_reports(sdata, rssi_min_thold, + rssi_max_thold); +} +EXPORT_SYMBOL(ieee80211_enable_rssi_reports); + +void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + _ieee80211_enable_rssi_reports(sdata, 0, 0); +} +EXPORT_SYMBOL(ieee80211_disable_rssi_reports); + +int ieee80211_add_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_local *local = sdata->local; + struct ieee80211_supported_band *sband; + int rate; + u8 i, rates, *pos; + + sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + rates = sband->n_bitrates; + if (rates > 8) + rates = 8; + + if (skb_tailroom(skb) < rates + 2) + return -ENOMEM; + + pos = skb_put(skb, rates + 2); + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = rates; + for (i = 0; i < rates; i++) { + rate = sband->bitrates[i].bitrate; + *pos++ = (u8) (rate / 5); + } + + return 0; +} + +int ieee80211_add_ext_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_local *local = sdata->local; + struct ieee80211_supported_band *sband; + int rate; + u8 i, exrates, *pos; + + sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + exrates = sband->n_bitrates; + if (exrates > 8) + exrates -= 8; + else + exrates = 0; + + if (skb_tailroom(skb) < exrates + 2) + return -ENOMEM; + + if (exrates) { + pos = skb_put(skb, exrates + 2); + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = exrates; + for (i = 8; i < sband->n_bitrates; i++) { + rate = sband->bitrates[i].bitrate; + *pos++ = (u8) (rate / 5); + } + } + return 0; +} diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c index a1c6bfd..34583c5 100644 --- a/net/mac80211/wep.c +++ b/net/mac80211/wep.c @@ -97,8 +97,7 @@ static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local, hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); - if (WARN_ON(skb_tailroom(skb) < WEP_ICV_LEN || - skb_headroom(skb) < WEP_IV_LEN)) + if (WARN_ON(skb_headroom(skb) < WEP_IV_LEN)) return NULL; hdrlen = ieee80211_hdrlen(hdr->frame_control); @@ -160,6 +159,9 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local, size_t len; u8 rc4key[3 + WLAN_KEY_LEN_WEP104]; + if (WARN_ON(skb_tailroom(skb) < WEP_ICV_LEN)) + return -1; + iv = ieee80211_wep_add_iv(local, skb, keylen, keyidx); if (!iv) return -1; diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 28bc084..fd52e69 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -72,7 +72,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_AP_VLAN: sta = rcu_dereference(sdata->u.vlan.sta); if (sta) { - qos = get_sta_flags(sta) & WLAN_STA_WME; + qos = test_sta_flag(sta, WLAN_STA_WME); break; } case NL80211_IFTYPE_AP: @@ -83,11 +83,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, break; #ifdef CONFIG_MAC80211_MESH case NL80211_IFTYPE_MESH_POINT: - /* - * XXX: This is clearly broken ... but already was before, - * because ieee80211_fill_mesh_addresses() would clear A1 - * except for multicast addresses. - */ + ra = skb->data; break; #endif case NL80211_IFTYPE_STATION: @@ -103,7 +99,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, if (!sta && ra && !is_multicast_ether_addr(ra)) { sta = sta_info_get(sdata, ra); if (sta) - qos = get_sta_flags(sta) & WLAN_STA_WME; + qos = test_sta_flag(sta, WLAN_STA_WME); } rcu_read_unlock(); @@ -139,7 +135,8 @@ u16 ieee80211_downgrade_queue(struct ieee80211_local *local, return ieee802_1d_to_ac[skb->priority]; } -void ieee80211_set_qos_hdr(struct ieee80211_local *local, struct sk_buff *skb) +void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) { struct ieee80211_hdr *hdr = (void *)skb->data; @@ -150,11 +147,11 @@ void ieee80211_set_qos_hdr(struct ieee80211_local *local, struct sk_buff *skb) tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; - if (unlikely(local->wifi_wme_noack_test)) - ack_policy |= QOS_CONTROL_ACK_POLICY_NOACK << - QOS_CONTROL_ACK_POLICY_SHIFT; - /* qos header is 2 bytes, second reserved */ + if (unlikely(sdata->local->wifi_wme_noack_test)) + ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK; + /* qos header is 2 bytes */ *p++ = ack_policy | tid; - *p = 0; + *p = ieee80211_vif_is_mesh(&sdata->vif) ? + (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8) : 0; } } diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h index 6053b1c..34e166f 100644 --- a/net/mac80211/wme.h +++ b/net/mac80211/wme.h @@ -13,16 +13,12 @@ #include <linux/netdevice.h> #include "ieee80211_i.h" -#define QOS_CONTROL_ACK_POLICY_NORMAL 0 -#define QOS_CONTROL_ACK_POLICY_NOACK 1 - -#define QOS_CONTROL_ACK_POLICY_SHIFT 5 - extern const int ieee802_1d_to_ac[8]; u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); -void ieee80211_set_qos_hdr(struct ieee80211_local *local, struct sk_buff *skb); +void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb); u16 ieee80211_downgrade_queue(struct ieee80211_local *local, struct sk_buff *skb); diff --git a/net/mac80211/work.c b/net/mac80211/work.c index c9acfda..99165ef 100644 --- a/net/mac80211/work.c +++ b/net/mac80211/work.c @@ -25,6 +25,7 @@ #include "ieee80211_i.h" #include "rate.h" +#include "driver-ops.h" #define IEEE80211_AUTH_TIMEOUT (HZ / 5) #define IEEE80211_AUTH_MAX_TRIES 3 @@ -228,11 +229,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, wk->ie_len + /* extra IEs */ 9, /* WMM */ GFP_KERNEL); - if (!skb) { - printk(KERN_DEBUG "%s: failed to allocate buffer for assoc " - "frame\n", sdata->name); + if (!skb) return; - } + skb_reserve(skb, local->hw.extra_tx_headroom); capab = WLAN_CAPABILITY_ESS; @@ -427,6 +426,14 @@ ieee80211_direct_probe(struct ieee80211_work *wk) struct ieee80211_sub_if_data *sdata = wk->sdata; struct ieee80211_local *local = sdata->local; + if (!wk->probe_auth.synced) { + int ret = drv_tx_sync(local, sdata, wk->filter_ta, + IEEE80211_TX_SYNC_AUTH); + if (ret) + return WORK_ACT_TIMEOUT; + } + wk->probe_auth.synced = true; + wk->probe_auth.tries++; if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) { printk(KERN_DEBUG "%s: direct probe to %pM timed out\n", @@ -450,7 +457,8 @@ ieee80211_direct_probe(struct ieee80211_work *wk) * will not answer to direct packet in unassociated state. */ ieee80211_send_probe_req(sdata, NULL, wk->probe_auth.ssid, - wk->probe_auth.ssid_len, NULL, 0); + wk->probe_auth.ssid_len, NULL, 0, + (u32) -1, true, false); wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; run_again(local, wk->timeout); @@ -465,6 +473,14 @@ ieee80211_authenticate(struct ieee80211_work *wk) struct ieee80211_sub_if_data *sdata = wk->sdata; struct ieee80211_local *local = sdata->local; + if (!wk->probe_auth.synced) { + int ret = drv_tx_sync(local, sdata, wk->filter_ta, + IEEE80211_TX_SYNC_AUTH); + if (ret) + return WORK_ACT_TIMEOUT; + } + wk->probe_auth.synced = true; + wk->probe_auth.tries++; if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) { printk(KERN_DEBUG "%s: authentication with %pM" @@ -498,6 +514,14 @@ ieee80211_associate(struct ieee80211_work *wk) struct ieee80211_sub_if_data *sdata = wk->sdata; struct ieee80211_local *local = sdata->local; + if (!wk->assoc.synced) { + int ret = drv_tx_sync(local, sdata, wk->filter_ta, + IEEE80211_TX_SYNC_ASSOC); + if (ret) + return WORK_ACT_TIMEOUT; + } + wk->assoc.synced = true; + wk->assoc.tries++; if (wk->assoc.tries > IEEE80211_ASSOC_MAX_TRIES) { printk(KERN_DEBUG "%s: association with %pM" @@ -875,26 +899,6 @@ static bool ieee80211_work_ct_coexists(enum nl80211_channel_type wk_ct, return false; } -static enum nl80211_channel_type -ieee80211_calc_ct(enum nl80211_channel_type wk_ct, - enum nl80211_channel_type oper_ct) -{ - switch (wk_ct) { - case NL80211_CHAN_NO_HT: - return oper_ct; - case NL80211_CHAN_HT20: - if (oper_ct != NL80211_CHAN_NO_HT) - return oper_ct; - return wk_ct; - case NL80211_CHAN_HT40MINUS: - case NL80211_CHAN_HT40PLUS: - return wk_ct; - } - WARN_ON(1); /* shouldn't get here */ - return wk_ct; -} - - static void ieee80211_work_timer(unsigned long data) { struct ieee80211_local *local = (void *) data; @@ -945,50 +949,18 @@ static void ieee80211_work_work(struct work_struct *work) } if (!started && !local->tmp_channel) { - bool on_oper_chan; - bool tmp_chan_changed = false; - bool on_oper_chan2; - enum nl80211_channel_type wk_ct; - on_oper_chan = ieee80211_cfg_on_oper_channel(local); - - /* Work with existing channel type if possible. */ - wk_ct = wk->chan_type; - if (wk->chan == local->hw.conf.channel) - wk_ct = ieee80211_calc_ct(wk->chan_type, - local->hw.conf.channel_type); - - if (local->tmp_channel) - if ((local->tmp_channel != wk->chan) || - (local->tmp_channel_type != wk_ct)) - tmp_chan_changed = true; - - local->tmp_channel = wk->chan; - local->tmp_channel_type = wk_ct; /* - * Leave the station vifs in awake mode if they - * happen to be on the same channel as - * the requested channel. + * TODO: could optimize this by leaving the + * station vifs in awake mode if they + * happen to be on the same channel as + * the requested channel */ - on_oper_chan2 = ieee80211_cfg_on_oper_channel(local); - if (on_oper_chan != on_oper_chan2) { - if (on_oper_chan2) { - /* going off oper channel, PS too */ - ieee80211_offchannel_stop_vifs(local); - ieee80211_hw_config(local, 0); - } else { - /* going on channel, but leave PS - * off-channel. */ - ieee80211_hw_config(local, 0); - ieee80211_offchannel_return(local, - true); - } - } else if (tmp_chan_changed) - /* Still off-channel, but on some other - * channel, so update hardware. - * PS should already be off-channel. - */ - ieee80211_hw_config(local, 0); + ieee80211_offchannel_stop_beaconing(local); + ieee80211_offchannel_stop_station(local); + local->tmp_channel = wk->chan; + local->tmp_channel_type = wk->chan_type; + ieee80211_hw_config(local, 0); started = true; wk->timeout = jiffies; } @@ -1074,8 +1046,7 @@ static void ieee80211_work_work(struct work_struct *work) * we still need to do a hardware config. Currently, * we cannot be here while scanning, however. */ - if (!ieee80211_cfg_on_oper_channel(local)) - ieee80211_hw_config(local, 0); + ieee80211_hw_config(local, 0); /* At the least, we need to disable offchannel_ps, * so just go ahead and run the entire offchannel diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index d9e03cf..a582504 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -15,6 +15,7 @@ #include <linux/gfp.h> #include <asm/unaligned.h> #include <net/mac80211.h> +#include <crypto/aes.h> #include "ieee80211_i.h" #include "michael.h" @@ -52,7 +53,8 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx) } if (info->control.hw_key && - !(tx->flags & IEEE80211_TX_FRAGMENTED) && + (info->flags & IEEE80211_TX_CTL_DONTFRAG || + tx->local->ops->set_frag_threshold) && !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) { /* hwaccel - with no need for SW-generated MMIC */ return TX_CONTINUE; @@ -86,11 +88,6 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) struct sk_buff *skb = rx->skb; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - int queue = rx->queue; - - /* otherwise, TKIP is vulnerable to TID 0 vs. non-QoS replays */ - if (rx->queue == NUM_RX_DATA_QUEUES - 1) - queue = 0; /* * it makes no sense to check for MIC errors on anything other @@ -154,8 +151,8 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) update_iv: /* update IV in key information to be able to detect replays */ - rx->key->u.tkip.rx[queue].iv32 = rx->tkip_iv32; - rx->key->u.tkip.rx[queue].iv16 = rx->tkip_iv16; + rx->key->u.tkip.rx[rx->security_idx].iv32 = rx->tkip_iv32; + rx->key->u.tkip.rx[rx->security_idx].iv16 = rx->tkip_iv16; return RX_CONTINUE; @@ -177,6 +174,7 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_key *key = tx->key; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + unsigned long flags; unsigned int hdrlen; int len, tail; u8 *pos; @@ -204,11 +202,12 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) pos += hdrlen; /* Increase IV for the frame */ + spin_lock_irqsave(&key->u.tkip.txlock, flags); key->u.tkip.tx.iv16++; if (key->u.tkip.tx.iv16 == 0) key->u.tkip.tx.iv32++; - - pos = ieee80211_tkip_add_iv(pos, key, key->u.tkip.tx.iv16); + pos = ieee80211_tkip_add_iv(pos, key); + spin_unlock_irqrestore(&key->u.tkip.txlock, flags); /* hwaccel - with software IV */ if (info->control.hw_key) @@ -217,9 +216,8 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) /* Add room for ICV */ skb_put(skb, TKIP_ICV_LEN); - hdr = (struct ieee80211_hdr *) skb->data; return ieee80211_tkip_encrypt_data(tx->local->wep_tx_tfm, - key, pos, len, hdr->addr2); + key, skb, pos, len); } @@ -247,11 +245,6 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx) struct ieee80211_key *key = rx->key; struct sk_buff *skb = rx->skb; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); - int queue = rx->queue; - - /* otherwise, TKIP is vulnerable to TID 0 vs. non-QoS replays */ - if (rx->queue == NUM_RX_DATA_QUEUES - 1) - queue = 0; hdrlen = ieee80211_hdrlen(hdr->frame_control); @@ -272,7 +265,7 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx) res = ieee80211_tkip_decrypt_data(rx->local->wep_rx_tfm, key, skb->data + hdrlen, skb->len - hdrlen, rx->sta->sta.addr, - hdr->addr1, hwaccel, queue, + hdr->addr1, hwaccel, rx->security_idx, &rx->tkip_iv32, &rx->tkip_iv16); if (res != TKIP_DECRYPT_OK) @@ -300,8 +293,10 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, unsigned int hdrlen; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - b_0 = scratch + 3 * AES_BLOCK_LEN; - aad = scratch + 4 * AES_BLOCK_LEN; + memset(scratch, 0, 6 * AES_BLOCK_SIZE); + + b_0 = scratch + 3 * AES_BLOCK_SIZE; + aad = scratch + 4 * AES_BLOCK_SIZE; /* * Mask FC: zero subtype b4 b5 b6 (if not mgmt) @@ -390,8 +385,10 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) struct ieee80211_key *key = tx->key; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int hdrlen, len, tail; - u8 *pos, *pn; - int i; + u8 *pos; + u8 pn[6]; + u64 pn64; + u8 scratch[6 * AES_BLOCK_SIZE]; if (info->control.hw_key && !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) { @@ -419,14 +416,14 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) hdr = (struct ieee80211_hdr *) pos; pos += hdrlen; - /* PN = PN + 1 */ - pn = key->u.ccmp.tx_pn; + pn64 = atomic64_inc_return(&key->u.ccmp.tx_pn); - for (i = CCMP_PN_LEN - 1; i >= 0; i--) { - pn[i]++; - if (pn[i]) - break; - } + pn[5] = pn64; + pn[4] = pn64 >> 8; + pn[3] = pn64 >> 16; + pn[2] = pn64 >> 24; + pn[1] = pn64 >> 32; + pn[0] = pn64 >> 40; ccmp_pn2hdr(pos, pn, key->conf.keyidx); @@ -435,8 +432,8 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) return 0; pos += CCMP_HDR_LEN; - ccmp_special_blocks(skb, pn, key->u.ccmp.tx_crypto_buf, 0); - ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, key->u.ccmp.tx_crypto_buf, pos, len, + ccmp_special_blocks(skb, pn, scratch, 0); + ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len, pos, skb_put(skb, CCMP_MIC_LEN)); return 0; @@ -483,8 +480,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) ccmp_hdr2pn(pn, skb->data + hdrlen); - queue = ieee80211_is_mgmt(hdr->frame_control) ? - NUM_RX_DATA_QUEUES : rx->queue; + queue = rx->security_idx; if (memcmp(pn, key->u.ccmp.rx_pn[queue], CCMP_PN_LEN) <= 0) { key->u.ccmp.replays++; @@ -492,11 +488,12 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) } if (!(status->flag & RX_FLAG_DECRYPTED)) { + u8 scratch[6 * AES_BLOCK_SIZE]; /* hardware didn't decrypt/verify MIC */ - ccmp_special_blocks(skb, pn, key->u.ccmp.rx_crypto_buf, 1); + ccmp_special_blocks(skb, pn, scratch, 1); if (ieee80211_aes_ccm_decrypt( - key->u.ccmp.tfm, key->u.ccmp.rx_crypto_buf, + key->u.ccmp.tfm, scratch, skb->data + hdrlen + CCMP_HDR_LEN, data_len, skb->data + skb->len - CCMP_MIC_LEN, skb->data + hdrlen + CCMP_HDR_LEN)) @@ -527,6 +524,16 @@ static void bip_aad(struct sk_buff *skb, u8 *aad) } +static inline void bip_ipn_set64(u8 *d, u64 pn) +{ + *d++ = pn; + *d++ = pn >> 8; + *d++ = pn >> 16; + *d++ = pn >> 24; + *d++ = pn >> 32; + *d = pn >> 40; +} + static inline void bip_ipn_swap(u8 *d, const u8 *s) { *d++ = s[5]; @@ -545,8 +552,8 @@ ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx) struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_key *key = tx->key; struct ieee80211_mmie *mmie; - u8 *pn, aad[20]; - int i; + u8 aad[20]; + u64 pn64; if (info->control.hw_key) return 0; @@ -560,22 +567,17 @@ ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx) mmie->key_id = cpu_to_le16(key->conf.keyidx); /* PN = PN + 1 */ - pn = key->u.aes_cmac.tx_pn; + pn64 = atomic64_inc_return(&key->u.aes_cmac.tx_pn); - for (i = sizeof(key->u.aes_cmac.tx_pn) - 1; i >= 0; i--) { - pn[i]++; - if (pn[i]) - break; - } - bip_ipn_swap(mmie->sequence_number, pn); + bip_ipn_set64(mmie->sequence_number, pn64); bip_aad(skb, aad); /* * MIC = AES-128-CMAC(IGTK, AAD || Management Frame Body || MMIE, 64) */ - ieee80211_aes_cmac(key->u.aes_cmac.tfm, key->u.aes_cmac.tx_crypto_buf, - aad, skb->data + 24, skb->len - 24, mmie->mic); + ieee80211_aes_cmac(key->u.aes_cmac.tfm, aad, + skb->data + 24, skb->len - 24, mmie->mic); return TX_CONTINUE; } @@ -613,8 +615,7 @@ ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx) if (!(status->flag & RX_FLAG_DECRYPTED)) { /* hardware didn't decrypt/verify MIC */ bip_aad(skb, aad); - ieee80211_aes_cmac(key->u.aes_cmac.tfm, - key->u.aes_cmac.rx_crypto_buf, aad, + ieee80211_aes_cmac(key->u.aes_cmac.tfm, aad, skb->data + 24, skb->len - 24, mic); if (memcmp(mic, mmie->mic, sizeof(mmie->mic)) != 0) { key->u.aes_cmac.icverrors++; |