From 68e6d7d60c0b35990cb8056e317ba97f0fe73047 Mon Sep 17 00:00:00 2001 From: Wolfgang Wiedmeyer Date: Sat, 21 Jan 2017 01:33:48 +0100 Subject: Backport mac80211 from 3.4 kernel The ath9k_htc driver depends on mac80211, but mac80211 can't be build. The reason is that net/wireless is almost completely backported from a 3.4 kernel. To follow suit, mac80211 is also backported from 3.4, more precisely from 3.4.113. This makes mac80211 build. Signed-off-by: Wolfgang Wiedmeyer --- fs/libfs.c | 7 + include/linux/errqueue.h | 3 +- include/linux/fs.h | 1 + include/linux/ieee80211.h | 176 ++- include/linux/if_ether.h | 1 + include/linux/skbuff.h | 16 + include/net/ieee80211_radiotap.h | 9 +- include/net/mac80211.h | 751 +++++++++-- net/core/skbuff.c | 20 + net/mac80211/Kconfig | 37 +- net/mac80211/Makefile | 6 +- net/mac80211/aes_ccm.c | 37 +- net/mac80211/aes_ccm.h | 2 - net/mac80211/aes_cmac.c | 10 +- net/mac80211/aes_cmac.h | 2 +- net/mac80211/agg-rx.c | 75 +- net/mac80211/agg-tx.c | 164 ++- net/mac80211/cfg.c | 951 +++++++++++--- net/mac80211/chan.c | 55 +- net/mac80211/debugfs.c | 199 +-- net/mac80211/debugfs.h | 1 - net/mac80211/debugfs_key.c | 24 +- net/mac80211/debugfs_netdev.c | 188 ++- net/mac80211/debugfs_sta.c | 46 +- net/mac80211/driver-ops.h | 238 +++- net/mac80211/driver-trace.h | 263 +++- net/mac80211/ht.c | 107 +- net/mac80211/ibss.c | 365 ++++-- net/mac80211/ieee80211_i.h | 372 ++++-- net/mac80211/iface.c | 122 +- net/mac80211/key.c | 212 ++- net/mac80211/key.h | 32 +- net/mac80211/led.c | 1 + net/mac80211/main.c | 165 +-- net/mac80211/mesh.c | 282 ++-- net/mac80211/mesh.h | 52 +- net/mac80211/mesh_hwmp.c | 437 ++++--- net/mac80211/mesh_pathtbl.c | 496 +++++-- net/mac80211/mesh_plink.c | 322 +++-- net/mac80211/mlme.c | 1965 ++++++++++++++++++++-------- net/mac80211/offchannel.c | 35 +- net/mac80211/pm.c | 69 +- net/mac80211/rate.c | 193 ++- net/mac80211/rate.h | 7 +- net/mac80211/rc80211_minstrel.c | 16 +- net/mac80211/rc80211_minstrel.h | 12 + net/mac80211/rc80211_minstrel_debugfs.c | 1 + net/mac80211/rc80211_minstrel_ht.c | 96 +- net/mac80211/rc80211_minstrel_ht_debugfs.c | 1 + net/mac80211/rc80211_pid_algo.c | 4 +- net/mac80211/rc80211_pid_debugfs.c | 1 + net/mac80211/rx.c | 654 +++++---- net/mac80211/scan.c | 369 +++--- net/mac80211/spectmgmt.c | 6 +- net/mac80211/sta_info.c | 1105 +++++++++++----- net/mac80211/sta_info.h | 213 +-- net/mac80211/status.c | 372 ++++-- net/mac80211/tkip.c | 116 +- net/mac80211/tkip.h | 8 +- net/mac80211/tx.c | 1062 ++++++++------- net/mac80211/util.c | 522 ++++++-- net/mac80211/wep.c | 32 +- net/mac80211/wep.h | 1 - net/mac80211/wme.c | 57 +- net/mac80211/wme.h | 11 +- net/mac80211/work.c | 919 +------------ net/mac80211/wpa.c | 172 ++- net/mac80211/wpa.h | 2 + 68 files changed, 9502 insertions(+), 4766 deletions(-) diff --git a/fs/libfs.c b/fs/libfs.c index 275ca474..3af183c 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -264,6 +264,13 @@ Enomem: return ERR_PTR(-ENOMEM); } +int simple_open(struct inode *inode, struct file *file) +{ + if (inode->i_private) + file->private_data = inode->i_private; + return 0; +} + int simple_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { struct inode *inode = old_dentry->d_inode; diff --git a/include/linux/errqueue.h b/include/linux/errqueue.h index 034072c..c9f522b 100644 --- a/include/linux/errqueue.h +++ b/include/linux/errqueue.h @@ -17,7 +17,8 @@ struct sock_extended_err { #define SO_EE_ORIGIN_LOCAL 1 #define SO_EE_ORIGIN_ICMP 2 #define SO_EE_ORIGIN_ICMP6 3 -#define SO_EE_ORIGIN_TIMESTAMPING 4 +#define SO_EE_ORIGIN_TXSTATUS 4 +#define SO_EE_ORIGIN_TIMESTAMPING SO_EE_ORIGIN_TXSTATUS #define SO_EE_OFFENDER(ee) ((struct sockaddr*)((ee)+1)) diff --git a/include/linux/fs.h b/include/linux/fs.h index f74b004..39fbfd3 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2467,6 +2467,7 @@ extern int dcache_readdir(struct file *, void *, filldir_t); extern int simple_setattr(struct dentry *, struct iattr *); extern int simple_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern int simple_statfs(struct dentry *, struct kstatfs *); +extern int simple_open(struct inode *inode, struct file *file); extern int simple_link(struct dentry *, struct inode *, struct dentry *); extern int simple_unlink(struct inode *, struct dentry *); extern int simple_rmdir(struct inode *, struct dentry *); diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 1c4085e..7dd1458 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -117,8 +117,22 @@ #define IEEE80211_MAX_MESH_ID_LEN 32 #define IEEE80211_QOS_CTL_LEN 2 -#define IEEE80211_QOS_CTL_TID_MASK 0x000F -#define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007 +/* 1d tag mask */ +#define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007 +/* TID mask */ +#define IEEE80211_QOS_CTL_TID_MASK 0x000f +/* EOSP */ +#define IEEE80211_QOS_CTL_EOSP 0x0010 +/* ACK policy */ +#define IEEE80211_QOS_CTL_ACK_POLICY_NORMAL 0x0000 +#define IEEE80211_QOS_CTL_ACK_POLICY_NOACK 0x0020 +#define IEEE80211_QOS_CTL_ACK_POLICY_NO_EXPL 0x0040 +#define IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK 0x0060 +#define IEEE80211_QOS_CTL_ACK_POLICY_MASK 0x0060 +/* A-MSDU 802.11n */ +#define IEEE80211_QOS_CTL_A_MSDU_PRESENT 0x0080 +/* Mesh Control 802.11s */ +#define IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT 0x0100 /* U-APSD queue for WMM IEs sent by AP */ #define IEEE80211_WMM_IE_AP_QOSINFO_UAPSD (1<<7) @@ -530,6 +544,15 @@ static inline int ieee80211_is_qos_nullfunc(__le16 fc) cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC); } +/** + * ieee80211_is_first_frag - check if IEEE80211_SCTL_FRAG is not set + * @seq_ctrl: frame sequence control bytes in little-endian byteorder + */ +static inline int ieee80211_is_first_frag(__le16 seq_ctrl) +{ + return (seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG)) == 0; +} + struct ieee80211s_hdr { u8 flags; u8 ttl; @@ -738,6 +761,10 @@ struct ieee80211_mgmt { */ u8 variable[0]; } __attribute__((packed)) plink_action; + struct { + u8 action_code; + u8 variable[0]; + } __attribute__((packed)) self_prot; struct{ u8 action_code; u8 variable[0]; @@ -750,11 +777,20 @@ struct ieee80211_mgmt { u8 action; u8 smps_control; } __attribute__ ((packed)) ht_smps; + struct { + u8 action_code; + u8 dialog_token; + __le16 capability; + u8 variable[0]; + } __packed tdls_discover_resp; } u; } __attribute__ ((packed)) action; } u; } __attribute__ ((packed)); +/* Supported Rates value encodings in 802.11n-2009 7.3.2.2 */ +#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127 + /* mgmt header + 1 byte category code */ #define IEEE80211_MIN_ACTION_SIZE offsetof(struct ieee80211_mgmt, u.action.u) @@ -768,6 +804,13 @@ struct ieee80211_mmie { u8 mic[8]; } __attribute__ ((packed)); +struct ieee80211_vendor_ie { + u8 element_id; + u8 len; + u8 oui[3]; + u8 oui_type; +} __packed; + /* Control frames */ struct ieee80211_rts { __le16 frame_control; @@ -789,6 +832,52 @@ struct ieee80211_pspoll { u8 ta[6]; } __attribute__ ((packed)); +/* TDLS */ + +/* Link-id information element */ +struct ieee80211_tdls_lnkie { + u8 ie_type; /* Link Identifier IE */ + u8 ie_len; + u8 bssid[6]; + u8 init_sta[6]; + u8 resp_sta[6]; +} __packed; + +struct ieee80211_tdls_data { + u8 da[6]; + u8 sa[6]; + __be16 ether_type; + u8 payload_type; + u8 category; + u8 action_code; + union { + struct { + u8 dialog_token; + __le16 capability; + u8 variable[0]; + } __packed setup_req; + struct { + __le16 status_code; + u8 dialog_token; + __le16 capability; + u8 variable[0]; + } __packed setup_resp; + struct { + __le16 status_code; + u8 dialog_token; + u8 variable[0]; + } __packed setup_cfm; + struct { + __le16 reason_code; + u8 variable[0]; + } __packed teardown; + struct { + u8 dialog_token; + u8 variable[0]; + } __packed discover_req; + } u; +} __packed; + /** * struct ieee80211_bar - HT Block Ack Request * @@ -805,9 +894,11 @@ struct ieee80211_bar { } __attribute__((packed)); /* 802.11 BAR control masks */ -#define IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL 0x0000 -#define IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA 0x0004 - +#define IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL 0x0000 +#define IEEE80211_BAR_CTRL_MULTI_TID 0x0002 +#define IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA 0x0004 +#define IEEE80211_BAR_CTRL_TID_INFO_MASK 0xf000 +#define IEEE80211_BAR_CTRL_TID_INFO_SHIFT 12 #define IEEE80211_HT_MCS_MASK_LEN 10 @@ -1178,6 +1269,8 @@ enum ieee80211_eid { WLAN_EID_TS_DELAY = 43, WLAN_EID_TCLAS_PROCESSING = 44, WLAN_EID_QOS_CAPA = 46, + /* 802.11z */ + WLAN_EID_LINK_ID = 101, /* 802.11s */ WLAN_EID_MESH_CONFIG = 113, WLAN_EID_MESH_ID = 114, @@ -1266,6 +1359,7 @@ enum ieee80211_category { WLAN_CATEGORY_HT = 7, WLAN_CATEGORY_SA_QUERY = 8, WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION = 9, + WLAN_CATEGORY_TDLS = 12, WLAN_CATEGORY_MESH_ACTION = 13, WLAN_CATEGORY_MULTIHOP_ACTION = 14, WLAN_CATEGORY_SELF_PROTECTED = 15, @@ -1298,6 +1392,31 @@ enum ieee80211_ht_actioncode { WLAN_HT_ACTION_ASEL_IDX_FEEDBACK = 7, }; +/* Self Protected Action codes */ +enum ieee80211_self_protected_actioncode { + WLAN_SP_RESERVED = 0, + WLAN_SP_MESH_PEERING_OPEN = 1, + WLAN_SP_MESH_PEERING_CONFIRM = 2, + WLAN_SP_MESH_PEERING_CLOSE = 3, + WLAN_SP_MGK_INFORM = 4, + WLAN_SP_MGK_ACK = 5, +}; + +/* Mesh action codes */ +enum ieee80211_mesh_actioncode { + WLAN_MESH_ACTION_LINK_METRIC_REPORT, + WLAN_MESH_ACTION_HWMP_PATH_SELECTION, + WLAN_MESH_ACTION_GATE_ANNOUNCEMENT, + WLAN_MESH_ACTION_CONGESTION_CONTROL_NOTIFICATION, + WLAN_MESH_ACTION_MCCA_SETUP_REQUEST, + WLAN_MESH_ACTION_MCCA_SETUP_REPLY, + WLAN_MESH_ACTION_MCCA_ADVERTISEMENT_REQUEST, + WLAN_MESH_ACTION_MCCA_ADVERTISEMENT, + WLAN_MESH_ACTION_MCCA_TEARDOWN, + WLAN_MESH_ACTION_TBTT_ADJUSTMENT_REQUEST, + WLAN_MESH_ACTION_TBTT_ADJUSTMENT_RESPONSE, +}; + /* Security key length */ enum ieee80211_key_len { WLAN_KEY_LEN_WEP40 = 5, @@ -1308,6 +1427,36 @@ enum ieee80211_key_len { WLAN_KEY_LEN_WAPI_SMS4 = 32, }; +/* Public action codes */ +enum ieee80211_pub_actioncode { + WLAN_PUB_ACTION_TDLS_DISCOVER_RES = 14, +}; + +/* TDLS action codes */ +enum ieee80211_tdls_actioncode { + WLAN_TDLS_SETUP_REQUEST = 0, + WLAN_TDLS_SETUP_RESPONSE = 1, + WLAN_TDLS_SETUP_CONFIRM = 2, + WLAN_TDLS_TEARDOWN = 3, + WLAN_TDLS_PEER_TRAFFIC_INDICATION = 4, + WLAN_TDLS_CHANNEL_SWITCH_REQUEST = 5, + WLAN_TDLS_CHANNEL_SWITCH_RESPONSE = 6, + WLAN_TDLS_PEER_PSM_REQUEST = 7, + WLAN_TDLS_PEER_PSM_RESPONSE = 8, + WLAN_TDLS_PEER_TRAFFIC_RESPONSE = 9, + WLAN_TDLS_DISCOVERY_REQUEST = 10, +}; + +/* + * TDLS capabililites to be enabled in the 5th byte of the + * @WLAN_EID_EXT_CAPABILITY information element + */ +#define WLAN_EXT_CAPA5_TDLS_ENABLED BIT(5) +#define WLAN_EXT_CAPA5_TDLS_PROHIBITED BIT(6) + +/* TDLS specific payload type in the LLC/SNAP header */ +#define WLAN_TDLS_SNAP_RFTYPE 0x2 + /** * enum - mesh path selection protocol identifier * @@ -1534,6 +1683,23 @@ static inline bool ieee80211_is_robust_mgmt_frame(struct ieee80211_hdr *hdr) } /** + * ieee80211_is_public_action - check if frame is a public action frame + * @hdr: the frame + * @len: length of the frame + */ +static inline bool ieee80211_is_public_action(struct ieee80211_hdr *hdr, + size_t len) +{ + struct ieee80211_mgmt *mgmt = (void *)hdr; + + if (len < IEEE80211_MIN_ACTION_SIZE) + return false; + if (!ieee80211_is_action(hdr->frame_control)) + return false; + return mgmt->u.action.category == WLAN_CATEGORY_PUBLIC; +} + +/** * ieee80211_fhss_chan_to_freq - get channel frequency * @channel: the FHSS channel * diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index 0065ffd..b706e8b 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -81,6 +81,7 @@ #define ETH_P_TIPC 0x88CA /* TIPC */ #define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */ #define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */ +#define ETH_P_TDLS 0x890D /* TDLS */ #define ETH_P_FIP 0x8914 /* FCoE Initialization Protocol */ #define ETH_P_EDSA 0xDADA /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */ diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index cfcbb17..bcbeac0 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -187,6 +187,9 @@ enum { /* ensure the originating sk reference is available on driver level */ SKBTX_DRV_NEEDS_SK_REF = 1 << 3, + + /* generate wifi status information (where possible) */ + SKBTX_WIFI_STATUS = 1 << 4, }; /* This data is invariant across clones and lives at @@ -315,6 +318,8 @@ typedef unsigned char *sk_buff_data_t; * @queue_mapping: Queue mapping for multiqueue devices * @tc_index: Traffic control index * @tc_verd: traffic control verdict + * @wifi_acked_valid: wifi_acked was set + * @wifi_acked: whether frame was acked on wifi or not * @ndisc_nodetype: router type (from link layer) * @dma_cookie: a cookie to one of several possible DMA operations * done by skb DMA functions @@ -397,6 +402,8 @@ struct sk_buff { __u8 ndisc_nodetype:2; #endif __u8 ooo_okay:1; + __u8 wifi_acked_valid:1; + __u8 wifi_acked:1; kmemcheck_bitfield_end(flags2); /* 0/13 bit hole */ @@ -2059,6 +2066,15 @@ static inline void skb_tx_timestamp(struct sk_buff *skb) sw_tx_timestamp(skb); } +/** + * skb_complete_wifi_ack - deliver skb with wifi status + * + * @skb: the original outgoing packet + * @acked: ack status + * + */ +void skb_complete_wifi_ack(struct sk_buff *skb, bool acked); + extern __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len); extern __sum16 __skb_checksum_complete(struct sk_buff *skb); diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index b0be5fb..7139254 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -251,6 +251,7 @@ enum ieee80211_radiotap_type { * retries */ #define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ #define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ +#define IEEE80211_RADIOTAP_F_TX_NOACK 0x0008 /* don't expect an ack */ /* For IEEE80211_RADIOTAP_MCS */ @@ -270,14 +271,6 @@ enum ieee80211_radiotap_type { #define IEEE80211_RADIOTAP_MCS_FEC_LDPC 0x10 -/* Ugly macro to convert literal channel numbers into their mhz equivalents - * There are certianly some conditions that will break this (like feeding it '30') - * but they shouldn't arise since nothing talks on channel 30. */ -#define ieee80211chan2mhz(x) \ - (((x) <= 14) ? \ - (((x) == 14) ? 2484 : ((x) * 5) + 2407) : \ - ((x) + 1000) * 5) - /* helpers */ static inline int ieee80211_get_radiotap_len(unsigned char *data) { diff --git a/include/net/mac80211.h b/include/net/mac80211.h index e6d6a66..aa12b39 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -13,13 +13,13 @@ #ifndef MAC80211_H #define MAC80211_H +#include #include #include #include -#include -#include #include #include +#include /** * DOC: Introduction @@ -87,6 +87,8 @@ * */ +struct device; + /** * enum ieee80211_max_queues - maximum number of queues * @@ -109,6 +111,7 @@ enum ieee80211_ac_numbers { IEEE80211_AC_BE = 2, IEEE80211_AC_BK = 3, }; +#define IEEE80211_NUM_ACS 4 /** * struct ieee80211_tx_queue_params - transmit queue configuration @@ -164,13 +167,15 @@ struct ieee80211_low_level_stats { * @BSS_CHANGED_QOS: QoS for this association was enabled/disabled. Note * that it is only ever disabled for station mode. * @BSS_CHANGED_IDLE: Idle changed for this BSS/interface. + * @BSS_CHANGED_SSID: SSID changed for this BSS (AP mode) + * @BSS_CHANGED_AP_PROBE_RESP: Probe Response changed for this BSS (AP mode) */ enum ieee80211_bss_change { BSS_CHANGED_ASSOC = 1<<0, BSS_CHANGED_ERP_CTS_PROT = 1<<1, BSS_CHANGED_ERP_PREAMBLE = 1<<2, BSS_CHANGED_ERP_SLOT = 1<<3, - BSS_CHANGED_HT = 1<<4, + BSS_CHANGED_HT = 1<<4, BSS_CHANGED_BASIC_RATES = 1<<5, BSS_CHANGED_BEACON_INT = 1<<6, BSS_CHANGED_BSSID = 1<<7, @@ -181,6 +186,8 @@ enum ieee80211_bss_change { BSS_CHANGED_ARP_FILTER = 1<<12, BSS_CHANGED_QOS = 1<<13, BSS_CHANGED_IDLE = 1<<14, + BSS_CHANGED_SSID = 1<<15, + BSS_CHANGED_AP_PROBE_RESP = 1<<16, /* when adding here, make sure to change ieee80211_reconfig */ }; @@ -193,6 +200,17 @@ enum ieee80211_bss_change { #define IEEE80211_BSS_ARP_ADDR_LIST_LEN 4 /** + * enum ieee80211_rssi_event - RSSI threshold event + * An indicator for when RSSI goes below/above a certain threshold. + * @RSSI_EVENT_HIGH: AP's rssi crossed the high threshold set by the driver. + * @RSSI_EVENT_LOW: AP's rssi crossed the low threshold set by the driver. + */ +enum ieee80211_rssi_event { + RSSI_EVENT_HIGH, + RSSI_EVENT_LOW, +}; + +/** * struct ieee80211_bss_conf - holds the BSS's changing parameters * * This structure keeps information about a BSS (and an association @@ -213,7 +231,8 @@ enum ieee80211_bss_change { * valid in station mode only while @assoc is true and if also * requested by %IEEE80211_HW_NEED_DTIM_PERIOD (cf. also hw conf * @ps_dtim_period) - * @timestamp: beacon timestamp + * @last_tsf: last beacon's/probe response's TSF timestamp (could be old + * as it may have been received during scanning long ago) * @beacon_int: beacon interval * @assoc_capability: capabilities taken from assoc resp * @basic_rates: bitmap of basic rates, each bit stands for an @@ -243,6 +262,9 @@ enum ieee80211_bss_change { * @idle: This interface is idle. There's also a global idle flag in the * hardware config which may be more appropriate depending on what * your driver/device needs to do. + * @ssid: The SSID of the current vif. Only valid in AP-mode. + * @ssid_len: Length of SSID given in @ssid. + * @hidden_ssid: The SSID of the current vif is hidden. Only valid in AP-mode. */ struct ieee80211_bss_conf { const u8 *bssid; @@ -257,7 +279,7 @@ struct ieee80211_bss_conf { u8 dtim_period; u16 beacon_int; u16 assoc_capability; - u64 timestamp; + u64 last_tsf; u32 basic_rates; int mcast_rate[IEEE80211_NUM_BANDS]; u16 ht_operation_mode; @@ -269,6 +291,9 @@ struct ieee80211_bss_conf { bool arp_filter_enabled; bool qos; bool idle; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + size_t ssid_len; + bool hidden_ssid; }; /** @@ -319,9 +344,9 @@ struct ieee80211_bss_conf { * used to indicate that a frame was already retried due to PS * @IEEE80211_TX_INTFL_DONT_ENCRYPT: completely internal to mac80211, * used to indicate frame should not be encrypted - * @IEEE80211_TX_CTL_PSPOLL_RESPONSE: (internal?) - * This frame is a response to a PS-poll frame and should be sent - * although the station is in powersave mode. + * @IEEE80211_TX_CTL_NO_PS_BUFFER: This frame is a response to a poll + * frame (PS-Poll or uAPSD) or a non-bufferable MMPDU and must + * be sent although the station is in powersave mode. * @IEEE80211_TX_CTL_MORE_FRAMES: More frames will be passed to the * transmit function after the current frame, this can be used * by drivers to kick the DMA queue only if unset or when the @@ -329,8 +354,6 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_INTFL_RETRANSMISSION: This frame is being retransmitted * after TX status because the destination was asleep, it must not * be modified again (no seqno assignment, crypto, etc.) - * @IEEE80211_TX_INTFL_HAS_RADIOTAP: This frame was injected and still - * has a radiotap header at skb->data. * @IEEE80211_TX_INTFL_NL80211_FRAME_TX: Frame was requested through nl80211 * MLME command (internal to mac80211 to figure out whether to send TX * status to user space) @@ -344,6 +367,20 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_INTFL_TKIP_MIC_FAILURE: Marks this packet to be used for TKIP * testing. It will be sent out with incorrect Michael MIC key to allow * TKIP countermeasures to be tested. + * @IEEE80211_TX_CTL_NO_CCK_RATE: This frame will be sent at non CCK rate. + * This flag is actually used for management frame especially for P2P + * frames not being sent at CCK rate in 2GHz band. + * @IEEE80211_TX_STATUS_EOSP: This packet marks the end of service period, + * when its status is reported the service period ends. For frames in + * an SP that mac80211 transmits, it is already set; for driver frames + * the driver may set this flag. It is also used to do the same for + * PS-Poll responses. + * @IEEE80211_TX_CTL_USE_MINRATE: This frame will be sent at lowest rate. + * This flag is used to send nullfunc frame at minimum rate when + * the nullfunc is used for connection monitoring purpose. + * @IEEE80211_TX_CTL_DONTFRAG: Don't fragment this packet even if it + * would be fragmented by size (this is optional, only used for + * monitor injection). * * Note: If you have to add new flags to the enumeration, then don't * forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary. @@ -365,15 +402,19 @@ enum mac80211_tx_control_flags { IEEE80211_TX_INTFL_NEED_TXPROCESSING = BIT(14), IEEE80211_TX_INTFL_RETRIED = BIT(15), IEEE80211_TX_INTFL_DONT_ENCRYPT = BIT(16), - IEEE80211_TX_CTL_PSPOLL_RESPONSE = BIT(17), + IEEE80211_TX_CTL_NO_PS_BUFFER = BIT(17), IEEE80211_TX_CTL_MORE_FRAMES = BIT(18), IEEE80211_TX_INTFL_RETRANSMISSION = BIT(19), - IEEE80211_TX_INTFL_HAS_RADIOTAP = BIT(20), + /* hole at 20, use later */ IEEE80211_TX_INTFL_NL80211_FRAME_TX = BIT(21), IEEE80211_TX_CTL_LDPC = BIT(22), IEEE80211_TX_CTL_STBC = BIT(23) | BIT(24), IEEE80211_TX_CTL_TX_OFFCHAN = BIT(25), IEEE80211_TX_INTFL_TKIP_MIC_FAILURE = BIT(26), + IEEE80211_TX_CTL_NO_CCK_RATE = BIT(27), + IEEE80211_TX_STATUS_EOSP = BIT(28), + IEEE80211_TX_CTL_USE_MINRATE = BIT(29), + IEEE80211_TX_CTL_DONTFRAG = BIT(30), }; #define IEEE80211_TX_CTL_STBC_SHIFT 23 @@ -387,9 +428,9 @@ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_SEND_AFTER_DTIM | IEEE80211_TX_CTL_AMPDU | \ IEEE80211_TX_STAT_TX_FILTERED | IEEE80211_TX_STAT_ACK | \ IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_STAT_AMPDU_NO_BACK | \ - IEEE80211_TX_CTL_RATE_CTRL_PROBE | IEEE80211_TX_CTL_PSPOLL_RESPONSE | \ + IEEE80211_TX_CTL_RATE_CTRL_PROBE | IEEE80211_TX_CTL_NO_PS_BUFFER | \ IEEE80211_TX_CTL_MORE_FRAMES | IEEE80211_TX_CTL_LDPC | \ - IEEE80211_TX_CTL_STBC) + IEEE80211_TX_CTL_STBC | IEEE80211_TX_STATUS_EOSP) /** * enum mac80211_rate_control_flags - per-rate flags set by the @@ -482,7 +523,7 @@ struct ieee80211_tx_rate { * @flags: transmit info flags, defined above * @band: the band to transmit on (use for checking for races) * @antenna_sel_tx: antenna to use, 0 for automatic diversity - * @pad: padding, ignore + * @ack_frame_id: internal frame ID for TX status, used internally * @control: union for control data * @status: union for status data * @driver_data: array of driver_data pointers @@ -499,8 +540,7 @@ struct ieee80211_tx_info { u8 antenna_sel_tx; - /* 2 byte hole */ - u8 pad[2]; + u16 ack_frame_id; union { struct { @@ -622,6 +662,8 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) * @RX_FLAG_HT: HT MCS was used and rate_idx is MCS index * @RX_FLAG_40MHZ: HT40 (40 MHz) was used * @RX_FLAG_SHORT_GI: Short guard interval was used + * @RX_FLAG_NO_SIGNAL_VAL: The signal strength value is not present. + * Valid only for data frames (mainly A-MPDU) */ enum mac80211_rx_flags { RX_FLAG_MMIC_ERROR = 1<<0, @@ -635,6 +677,7 @@ enum mac80211_rx_flags { RX_FLAG_HT = 1<<9, RX_FLAG_40MHZ = 1<<10, RX_FLAG_SHORT_GI = 1<<11, + RX_FLAG_NO_SIGNAL_VAL = 1<<12, }; /** @@ -815,6 +858,21 @@ struct ieee80211_channel_switch { }; /** + * enum ieee80211_vif_flags - virtual interface flags + * + * @IEEE80211_VIF_BEACON_FILTER: the device performs beacon filtering + * on this virtual interface to avoid unnecessary CPU wakeups + * @IEEE80211_VIF_SUPPORTS_CQM_RSSI: the device can do connection quality + * monitoring on this virtual interface -- i.e. it can monitor + * connection quality related parameters, such as the RSSI level and + * provide notifications if configured trigger levels are reached. + */ +enum ieee80211_vif_flags { + IEEE80211_VIF_BEACON_FILTER = BIT(0), + IEEE80211_VIF_SUPPORTS_CQM_RSSI = BIT(1), +}; + +/** * struct ieee80211_vif - per-interface data * * Data in this structure is continually present for driver @@ -826,6 +884,10 @@ struct ieee80211_channel_switch { * @addr: address of this interface * @p2p: indicates whether this AP or STA interface is a p2p * interface, i.e. a GO or p2p-sta respectively + * @driver_flags: flags/capabilities the driver has for this interface, + * these need to be set (or cleared) when the interface is added + * or, if supported by the driver, the interface type is changed + * at runtime, mac80211 will never touch this field * @drv_priv: data area for driver use, will always be aligned to * sizeof(void *). */ @@ -834,6 +896,7 @@ struct ieee80211_vif { struct ieee80211_bss_conf bss_conf; u8 addr[ETH_ALEN]; bool p2p; + u32 driver_flags; /* must be last */ u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *)))); }; @@ -865,6 +928,10 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif) * @IEEE80211_KEY_FLAG_SW_MGMT: This flag should be set by the driver for a * CCMP key if it requires CCMP encryption of management frames (MFP) to * be done in software. + * @IEEE80211_KEY_FLAG_PUT_IV_SPACE: This flag should be set by the driver + * for a CCMP key if space should be prepared for the IV, but the IV + * itself should not be generated. Do not set together with + * @IEEE80211_KEY_FLAG_GENERATE_IV on the same key. */ enum ieee80211_key_flags { IEEE80211_KEY_FLAG_WMM_STA = 1<<0, @@ -872,6 +939,7 @@ enum ieee80211_key_flags { IEEE80211_KEY_FLAG_GENERATE_MMIC= 1<<2, IEEE80211_KEY_FLAG_PAIRWISE = 1<<3, IEEE80211_KEY_FLAG_SW_MGMT = 1<<4, + IEEE80211_KEY_FLAG_PUT_IV_SPACE = 1<<5, }; /** @@ -920,6 +988,25 @@ enum set_key_cmd { }; /** + * enum ieee80211_sta_state - station state + * + * @IEEE80211_STA_NOTEXIST: station doesn't exist at all, + * this is a special state for add/remove transitions + * @IEEE80211_STA_NONE: station exists without special state + * @IEEE80211_STA_AUTH: station is authenticated + * @IEEE80211_STA_ASSOC: station is associated + * @IEEE80211_STA_AUTHORIZED: station is authorized (802.1X) + */ +enum ieee80211_sta_state { + /* NOTE: These need to be ordered correctly! */ + IEEE80211_STA_NOTEXIST, + IEEE80211_STA_NONE, + IEEE80211_STA_AUTH, + IEEE80211_STA_ASSOC, + IEEE80211_STA_AUTHORIZED, +}; + +/** * struct ieee80211_sta - station table entry * * A station table entry represents a station we are possibly @@ -933,14 +1020,21 @@ enum set_key_cmd { * @aid: AID we assigned to the station if we're an AP * @supp_rates: Bitmap of supported rates (per band) * @ht_cap: HT capabilities of this STA; restricted to our own TX capabilities + * @wme: indicates whether the STA supports WME. Only valid during AP-mode. * @drv_priv: data area for driver use, will always be aligned to * sizeof(void *), size is determined in hw information. + * @uapsd_queues: bitmap of queues configured for uapsd. Only valid + * if wme is supported. + * @max_sp: max Service Period. Only valid if wme is supported. */ struct ieee80211_sta { u32 supp_rates[IEEE80211_NUM_BANDS]; u8 addr[ETH_ALEN]; u16 aid; struct ieee80211_sta_ht_cap ht_cap; + bool wme; + u8 uapsd_queues; + u8 max_sp; /* must be last */ u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *)))); @@ -960,21 +1054,6 @@ enum sta_notify_cmd { }; /** - * enum ieee80211_tkip_key_type - get tkip key - * - * Used by drivers which need to get a tkip key for skb. Some drivers need a - * phase 1 key, others need a phase 2 key. A single function allows the driver - * to get the key, this enum indicates what type of key is required. - * - * @IEEE80211_TKIP_P1_KEY: the driver needs a phase 1 key - * @IEEE80211_TKIP_P2_KEY: the driver needs a phase 2 key - */ -enum ieee80211_tkip_key_type { - IEEE80211_TKIP_P1_KEY, - IEEE80211_TKIP_P2_KEY, -}; - -/** * enum ieee80211_hw_flags - hardware flags * * These flags are used to indicate hardware capabilities to @@ -1045,10 +1124,6 @@ enum ieee80211_tkip_key_type { * @IEEE80211_HW_MFP_CAPABLE: * Hardware supports management frame protection (MFP, IEEE 802.11w). * - * @IEEE80211_HW_BEACON_FILTER: - * Hardware supports dropping of irrelevant beacon frames to - * avoid waking up cpu. - * * @IEEE80211_HW_SUPPORTS_STATIC_SMPS: * Hardware supports static spatial multiplexing powersave, * ie. can turn off all but one chain even on HT connections @@ -1074,11 +1149,6 @@ enum ieee80211_tkip_key_type { * When this flag is set, signaling beacon-loss will cause an immediate * change to disassociated state. * - * @IEEE80211_HW_SUPPORTS_CQM_RSSI: - * Hardware can do connection quality monitoring - i.e. it can monitor - * connection quality related parameters, such as the RSSI level and - * provide notifications if configured trigger levels are reached. - * * @IEEE80211_HW_NEED_DTIM_PERIOD: * This device needs to know the DTIM period for the BSS before * associating. @@ -1096,6 +1166,18 @@ enum ieee80211_tkip_key_type { * stations based on the PM bit of incoming frames. * Use ieee80211_start_ps()/ieee8021_end_ps() to manually configure * the PS mode of connected stations. + * + * @IEEE80211_HW_TX_AMPDU_SETUP_IN_HW: The device handles TX A-MPDU session + * setup strictly in HW. mac80211 should not attempt to do this in + * software. + * + * @IEEE80211_HW_SCAN_WHILE_IDLE: The device can do hw scan while + * being idle (i.e. mac80211 doesn't have to go idle-off during the + * the scan). + * + * @IEEE80211_HW_TEARDOWN_AGGR_ON_BAR_FAIL: On this hardware TX BA session + * should be tear down once BAR frame will not be acked. + * */ enum ieee80211_hw_flags { IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, @@ -1112,15 +1194,18 @@ enum ieee80211_hw_flags { IEEE80211_HW_PS_NULLFUNC_STACK = 1<<11, IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12, IEEE80211_HW_MFP_CAPABLE = 1<<13, - IEEE80211_HW_BEACON_FILTER = 1<<14, + /* reuse bit 14 */ IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15, IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16, IEEE80211_HW_SUPPORTS_UAPSD = 1<<17, IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18, IEEE80211_HW_CONNECTION_MONITOR = 1<<19, - IEEE80211_HW_SUPPORTS_CQM_RSSI = 1<<20, + /* reuse bit 20 */ IEEE80211_HW_SUPPORTS_PER_STA_GTK = 1<<21, IEEE80211_HW_AP_LINK_PS = 1<<22, + IEEE80211_HW_TX_AMPDU_SETUP_IN_HW = 1<<23, + IEEE80211_HW_SCAN_WHILE_IDLE = 1<<24, + IEEE80211_HW_TEARDOWN_AGGR_ON_BAR_FAIL = 1<<26, }; /** @@ -1247,7 +1332,7 @@ static inline struct ieee80211_rate * ieee80211_get_tx_rate(const struct ieee80211_hw *hw, const struct ieee80211_tx_info *c) { - if (WARN_ON(c->control.rates[0].idx < 0)) + if (WARN_ON_ONCE(c->control.rates[0].idx < 0)) return NULL; return &hw->wiphy->bands[c->band]->bitrates[c->control.rates[0].idx]; } @@ -1271,6 +1356,16 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, } /** + * ieee80211_free_txskb - free TX skb + * @hw: the hardware + * @skb: the skb + * + * Free a transmit skb. Use this funtion when some failure + * to transmit happened and thus status cannot be reported. + */ +void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb); + +/** * DOC: Hardware crypto acceleration * * mac80211 is capable of taking advantage of many hardware @@ -1390,15 +1485,15 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, * DOC: Beacon filter support * * Some hardware have beacon filter support to reduce host cpu wakeups - * which will reduce system power consumption. It usuallly works so that + * which will reduce system power consumption. It usually works so that * the firmware creates a checksum of the beacon but omits all constantly * changing elements (TSF, TIM etc). Whenever the checksum changes the * beacon is forwarded to the host, otherwise it will be just dropped. That * way the host will only receive beacons where some relevant information * (for example ERP protection or WMM settings) have changed. * - * Beacon filter support is advertised with the %IEEE80211_HW_BEACON_FILTER - * hardware capability. The driver needs to enable beacon filter support + * Beacon filter support is advertised with the %IEEE80211_VIF_BEACON_FILTER + * interface capability. The driver needs to enable beacon filter support * whenever power save is enabled, that is %IEEE80211_CONF_PS is set. When * power save is enabled, the stack will not check for beacon loss and the * driver needs to notify about loss of beacons with ieee80211_beacon_loss(). @@ -1512,6 +1607,98 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, */ /** + * DOC: AP support for powersaving clients + * + * In order to implement AP and P2P GO modes, mac80211 has support for + * client powersaving, both "legacy" PS (PS-Poll/null data) and uAPSD. + * There currently is no support for sAPSD. + * + * There is one assumption that mac80211 makes, namely that a client + * will not poll with PS-Poll and trigger with uAPSD at the same time. + * Both are supported, and both can be used by the same client, but + * they can't be used concurrently by the same client. This simplifies + * the driver code. + * + * The first thing to keep in mind is that there is a flag for complete + * driver implementation: %IEEE80211_HW_AP_LINK_PS. If this flag is set, + * mac80211 expects the driver to handle most of the state machine for + * powersaving clients and will ignore the PM bit in incoming frames. + * Drivers then use ieee80211_sta_ps_transition() to inform mac80211 of + * stations' powersave transitions. In this mode, mac80211 also doesn't + * handle PS-Poll/uAPSD. + * + * In the mode without %IEEE80211_HW_AP_LINK_PS, mac80211 will check the + * PM bit in incoming frames for client powersave transitions. When a + * station goes to sleep, we will stop transmitting to it. There is, + * however, a race condition: a station might go to sleep while there is + * data buffered on hardware queues. If the device has support for this + * it will reject frames, and the driver should give the frames back to + * mac80211 with the %IEEE80211_TX_STAT_TX_FILTERED flag set which will + * cause mac80211 to retry the frame when the station wakes up. The + * driver is also notified of powersave transitions by calling its + * @sta_notify callback. + * + * When the station is asleep, it has three choices: it can wake up, + * it can PS-Poll, or it can possibly start a uAPSD service period. + * Waking up is implemented by simply transmitting all buffered (and + * filtered) frames to the station. This is the easiest case. When + * the station sends a PS-Poll or a uAPSD trigger frame, mac80211 + * will inform the driver of this with the @allow_buffered_frames + * callback; this callback is optional. mac80211 will then transmit + * the frames as usual and set the %IEEE80211_TX_CTL_NO_PS_BUFFER + * on each frame. The last frame in the service period (or the only + * response to a PS-Poll) also has %IEEE80211_TX_STATUS_EOSP set to + * indicate that it ends the service period; as this frame must have + * TX status report it also sets %IEEE80211_TX_CTL_REQ_TX_STATUS. + * When TX status is reported for this frame, the service period is + * marked has having ended and a new one can be started by the peer. + * + * Additionally, non-bufferable MMPDUs can also be transmitted by + * mac80211 with the %IEEE80211_TX_CTL_NO_PS_BUFFER set in them. + * + * Another race condition can happen on some devices like iwlwifi + * when there are frames queued for the station and it wakes up + * or polls; the frames that are already queued could end up being + * transmitted first instead, causing reordering and/or wrong + * processing of the EOSP. The cause is that allowing frames to be + * transmitted to a certain station is out-of-band communication to + * the device. To allow this problem to be solved, the driver can + * call ieee80211_sta_block_awake() if frames are buffered when it + * is notified that the station went to sleep. When all these frames + * have been filtered (see above), it must call the function again + * to indicate that the station is no longer blocked. + * + * If the driver buffers frames in the driver for aggregation in any + * way, it must use the ieee80211_sta_set_buffered() call when it is + * notified of the station going to sleep to inform mac80211 of any + * TIDs that have frames buffered. Note that when a station wakes up + * this information is reset (hence the requirement to call it when + * informed of the station going to sleep). Then, when a service + * period starts for any reason, @release_buffered_frames is called + * with the number of frames to be released and which TIDs they are + * to come from. In this case, the driver is responsible for setting + * the EOSP (for uAPSD) and MORE_DATA bits in the released frames, + * to help the @more_data paramter is passed to tell the driver if + * there is more data on other TIDs -- the TIDs to release frames + * from are ignored since mac80211 doesn't know how many frames the + * buffers for those TIDs contain. + * + * If the driver also implement GO mode, where absence periods may + * shorten service periods (or abort PS-Poll responses), it must + * filter those response frames except in the case of frames that + * are buffered in the driver -- those must remain buffered to avoid + * reordering. Because it is possible that no frames are released + * in this case, the driver must call ieee80211_sta_eosp_irqsafe() + * to indicate to mac80211 that the service period ended anyway. + * + * Finally, if frames from multiple TIDs are released from mac80211 + * but the driver might reorder them, it must clear & set the flags + * appropriately (only the last frame may have %IEEE80211_TX_STATUS_EOSP) + * and also take care of the EOSP and MORE_DATA bits in the frame. + * The driver may also use ieee80211_sta_eosp_irqsafe() in this case. + */ + +/** * enum ieee80211_filter_flags - hardware filter flags * * These flags determine what the filter in hardware should be @@ -1587,6 +1774,17 @@ enum ieee80211_ampdu_mlme_action { }; /** + * enum ieee80211_frame_release_type - frame release reason + * @IEEE80211_FRAME_RELEASE_PSPOLL: frame released for PS-Poll + * @IEEE80211_FRAME_RELEASE_UAPSD: frame(s) released due to + * frame received on trigger-enabled AC + */ +enum ieee80211_frame_release_type { + IEEE80211_FRAME_RELEASE_PSPOLL, + IEEE80211_FRAME_RELEASE_UAPSD, +}; + +/** * struct ieee80211_ops - callbacks from mac80211 to the driver * * This structure contains various callbacks that the driver may @@ -1597,11 +1795,21 @@ enum ieee80211_ampdu_mlme_action { * skb contains the buffer starting from the IEEE 802.11 header. * The low-level driver should send the frame out based on * configuration in the TX control data. This handler should, - * preferably, never fail and stop queues appropriately, more - * importantly, however, it must never fail for A-MPDU-queues. - * This function should return NETDEV_TX_OK except in very - * limited cases. - * Must be implemented and atomic. + * preferably, never fail and stop queues appropriately. + * This must be implemented if @tx_frags is not. + * Must be atomic. + * + * @tx_frags: Called to transmit multiple fragments of a single MSDU. + * This handler must consume all fragments, sending out some of + * them only is useless and it can't ask for some of them to be + * queued again. If the frame is not fragmented the queue has a + * single SKB only. To avoid issues with the networking stack + * when TX status is reported the frames should be removed from + * the skb queue. + * If this is used, the tx_info @vif and @sta pointers will be + * invalid -- you must not use them in that case. + * This must be implemented if @tx isn't. + * Must be atomic. * * @start: Called before the first netdevice attached to the hardware * is enabled. This should turn on the hardware and must turn on @@ -1626,6 +1834,10 @@ enum ieee80211_ampdu_mlme_action { * ask the device to suspend. This is only invoked when WoWLAN is * configured, otherwise the device is deconfigured completely and * reconfigured at resume time. + * The driver may also impose special conditions under which it + * wants to use the "normal" suspend (deconfigure), say if it only + * supports WoWLAN when the device is associated. In this case, it + * must return 1 from this function. * * @resume: If WoWLAN was configured, this indicates that mac80211 is * now resuming its operation, after this the device must be fully @@ -1694,6 +1906,12 @@ enum ieee80211_ampdu_mlme_action { * which set IEEE80211_KEY_FLAG_TKIP_REQ_RX_P1_KEY. * The callback must be atomic. * + * @set_rekey_data: If the device supports GTK rekeying, for example while the + * host is suspended, it can assign this callback to retrieve the data + * necessary to do GTK rekeying, this is the KEK, KCK and replay counter. + * After rekeying was done it should (for example during resume) notify + * userspace of the new replay counter using ieee80211_gtk_rekey_notify(). + * * @hw_scan: Ask the hardware to service the scan request, no need to start * the scan state machine in stack. The scan must honour the channel * configuration done by the regulatory agent in the wiphy's @@ -1708,6 +1926,14 @@ enum ieee80211_ampdu_mlme_action { * any error unless this callback returned a negative error code. * The callback can sleep. * + * @cancel_hw_scan: Ask the low-level tp cancel the active hw scan. + * The driver should ask the hardware to cancel the scan (if possible), + * but the scan will be completed only after the driver will call + * ieee80211_scan_completed(). + * This callback is needed for wowlan, to prevent enqueueing a new + * scan_work after the low-level driver was already suspended. + * The callback can sleep. + * * @sched_scan_start: Ask the hardware to start scanning repeatedly at * specific intervals. The driver must call the * ieee80211_sched_scan_results() function whenever it finds results. @@ -1752,6 +1978,13 @@ enum ieee80211_ampdu_mlme_action { * in AP mode, this callback will not be called when the flag * %IEEE80211_HW_AP_LINK_PS is set. Must be atomic. * + * @sta_state: Notifies low level driver about state transition of a + * station (which can be the AP, a client, IBSS/WDS/mesh peer etc.) + * This callback is mutually exclusive with @sta_add/@sta_remove. + * It must not fail for down transitions but may fail for transitions + * up the list of states. + * The callback can sleep. + * * @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max), * bursting) for a hardware TX queue. * Returns a negative error code on failure. @@ -1816,6 +2049,7 @@ enum ieee80211_ampdu_mlme_action { * * @testmode_cmd: Implement a cfg80211 test mode command. * The callback can sleep. + * @testmode_dump: Implement a cfg80211 test mode dump. The callback can sleep. * * @flush: Flush all pending frames from the hardware queue, making sure * that the hardware queues are empty. If the parameter @drop is set @@ -1844,11 +2078,6 @@ enum ieee80211_ampdu_mlme_action { * ieee80211_remain_on_channel_expired(). This callback may sleep. * @cancel_remain_on_channel: Requests that an ongoing off-channel period is * aborted before it expires. This callback may sleep. - * @offchannel_tx: Transmit frame on another channel, wait for a response - * and return. Reliable TX status must be reported for the frame. If the - * return value is 1, then the @remain_on_channel will be used with a - * regular transmission (if supported.) - * @offchannel_tx_cancel_wait: cancel wait associated with offchannel TX * * @set_ringparam: Set tx and rx ring sizes. * @@ -1860,9 +2089,52 @@ enum ieee80211_ampdu_mlme_action { * @set_bitrate_mask: Set a mask of rates to be used for rate control selection * when transmitting a frame. Currently only legacy rates are handled. * The callback can sleep. + * @rssi_callback: Notify driver when the average RSSI goes above/below + * thresholds that were registered previously. The callback can sleep. + * + * @release_buffered_frames: Release buffered frames according to the given + * parameters. In the case where the driver buffers some frames for + * sleeping stations mac80211 will use this callback to tell the driver + * to release some frames, either for PS-poll or uAPSD. + * Note that if the @more_data paramter is %false the driver must check + * if there are more frames on the given TIDs, and if there are more than + * the frames being released then it must still set the more-data bit in + * the frame. If the @more_data parameter is %true, then of course the + * more-data bit must always be set. + * The @tids parameter tells the driver which TIDs to release frames + * from, for PS-poll it will always have only a single bit set. + * In the case this is used for a PS-poll initiated release, the + * @num_frames parameter will always be 1 so code can be shared. In + * this case the driver must also set %IEEE80211_TX_STATUS_EOSP flag + * on the TX status (and must report TX status) so that the PS-poll + * period is properly ended. This is used to avoid sending multiple + * responses for a retried PS-poll frame. + * In the case this is used for uAPSD, the @num_frames parameter may be + * bigger than one, but the driver may send fewer frames (it must send + * at least one, however). In this case it is also responsible for + * setting the EOSP flag in the QoS header of the frames. Also, when the + * service period ends, the driver must set %IEEE80211_TX_STATUS_EOSP + * on the last frame in the SP. Alternatively, it may call the function + * ieee80211_sta_eosp_irqsafe() to inform mac80211 of the end of the SP. + * This callback must be atomic. + * @allow_buffered_frames: Prepare device to allow the given number of frames + * to go out to the given station. The frames will be sent by mac80211 + * via the usual TX path after this call. The TX information for frames + * released will also have the %IEEE80211_TX_CTL_NO_PS_BUFFER flag set + * and the last one will also have %IEEE80211_TX_STATUS_EOSP set. In case + * frames from multiple TIDs are released and the driver might reorder + * them between the TIDs, it must set the %IEEE80211_TX_STATUS_EOSP flag + * on the last frame and clear it on all others and also handle the EOSP + * bit in the QoS header correctly. Alternatively, it can also call the + * ieee80211_sta_eosp_irqsafe() function. + * The @tids parameter is a bitmap and tells the driver which TIDs the + * frames will be on; it will at most have two bits set. + * This callback must be atomic. */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); + void (*tx_frags)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct sk_buff_head *skbs); int (*start)(struct ieee80211_hw *hw); void (*stop)(struct ieee80211_hw *hw); #ifdef CONFIG_PM @@ -1881,6 +2153,7 @@ struct ieee80211_ops { struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, u32 changed); + u64 (*prepare_multicast)(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list); void (*configure_filter)(struct ieee80211_hw *hw, @@ -1897,8 +2170,13 @@ struct ieee80211_ops { struct ieee80211_key_conf *conf, struct ieee80211_sta *sta, u32 iv32, u16 *phase1key); + void (*set_rekey_data)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data); int (*hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_scan_request *req); + void (*cancel_hw_scan)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); int (*sched_scan_start)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_sched_scan_request *req, @@ -1919,11 +2197,17 @@ struct ieee80211_ops { struct ieee80211_sta *sta); void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd, struct ieee80211_sta *sta); - int (*conf_tx)(struct ieee80211_hw *hw, u16 queue, + int (*sta_state)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state); + int (*conf_tx)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params); - u64 (*get_tsf)(struct ieee80211_hw *hw); - void (*set_tsf)(struct ieee80211_hw *hw, u64 tsf); - void (*reset_tsf)(struct ieee80211_hw *hw); + u64 (*get_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); + void (*set_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u64 tsf); + void (*reset_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int (*tx_last_beacon)(struct ieee80211_hw *hw); int (*ampdu_action)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -1936,6 +2220,9 @@ struct ieee80211_ops { void (*set_coverage_class)(struct ieee80211_hw *hw, u8 coverage_class); #ifdef CONFIG_NL80211_TESTMODE int (*testmode_cmd)(struct ieee80211_hw *hw, void *data, int len); + int (*testmode_dump)(struct ieee80211_hw *hw, struct sk_buff *skb, + struct netlink_callback *cb, + void *data, int len); #endif void (*flush)(struct ieee80211_hw *hw, bool drop); void (*channel_switch)(struct ieee80211_hw *hw, @@ -1949,17 +2236,25 @@ struct ieee80211_ops { enum nl80211_channel_type channel_type, int duration); int (*cancel_remain_on_channel)(struct ieee80211_hw *hw); - int (*offchannel_tx)(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, - unsigned int wait); - int (*offchannel_tx_cancel_wait)(struct ieee80211_hw *hw); int (*set_ringparam)(struct ieee80211_hw *hw, u32 tx, u32 rx); void (*get_ringparam)(struct ieee80211_hw *hw, u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max); bool (*tx_frames_pending)(struct ieee80211_hw *hw); int (*set_bitrate_mask)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const struct cfg80211_bitrate_mask *mask); + void (*rssi_callback)(struct ieee80211_hw *hw, + enum ieee80211_rssi_event rssi_event); + + void (*allow_buffered_frames)(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u16 tids, int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data); + void (*release_buffered_frames)(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u16 tids, int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data); }; /** @@ -2274,20 +2569,38 @@ static inline int ieee80211_sta_ps_transition_ni(struct ieee80211_sta *sta, * The TX headroom reserved by mac80211 for its own tx_status functions. * This is enough for the radiotap header. */ -#define IEEE80211_TX_STATUS_HEADROOM 13 +#define IEEE80211_TX_STATUS_HEADROOM 14 /** - * ieee80211_sta_set_tim - set the TIM bit for a sleeping station + * ieee80211_sta_set_buffered - inform mac80211 about driver-buffered frames * @sta: &struct ieee80211_sta pointer for the sleeping station + * @tid: the TID that has buffered frames + * @buffered: indicates whether or not frames are buffered for this TID * * If a driver buffers frames for a powersave station instead of passing - * them back to mac80211 for retransmission, the station needs to be told - * to wake up using the TIM bitmap in the beacon. + * them back to mac80211 for retransmission, the station may still need + * to be told that there are buffered frames via the TIM bit. + * + * This function informs mac80211 whether or not there are frames that are + * buffered in the driver for a given TID; mac80211 can then use this data + * to set the TIM bit (NOTE: This may call back into the driver's set_tim + * call! Beware of the locking!) * - * This function sets the station's TIM bit - it will be cleared when the - * station wakes up. + * If all frames are released to the station (due to PS-poll or uAPSD) + * then the driver needs to inform mac80211 that there no longer are + * frames buffered. However, when the station wakes up mac80211 assumes + * that all buffered frames will be transmitted and clears this data, + * drivers need to make sure they inform mac80211 about all buffered + * frames on the sleep transition (sta_notify() with %STA_NOTIFY_SLEEP). + * + * Note that technically mac80211 only needs to know this per AC, not per + * TID, but since driver buffering will inevitably happen per TID (since + * it is related to aggregation) it is easier to make mac80211 map the + * TID to the AC as required instead of keeping track in all drivers that + * use this API. */ -void ieee80211_sta_set_tim(struct ieee80211_sta *sta); +void ieee80211_sta_set_buffered(struct ieee80211_sta *sta, + u8 tid, bool buffered); /** * ieee80211_tx_status - transmit status callback @@ -2395,6 +2708,19 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, } /** + * ieee80211_proberesp_get - retrieve a Probe Response template + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * Creates a Probe Response template which can, for example, be uploaded to + * hardware. The destination address should be set by the caller. + * + * Can only be called in AP mode. + */ +struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + +/** * ieee80211_pspoll_get - retrieve a PS Poll template * @hw: pointer obtained from ieee80211_alloc_hw(). * @vif: &struct ieee80211_vif pointer from the add_interface callback. @@ -2550,21 +2876,136 @@ struct sk_buff * ieee80211_get_buffered_bc(struct ieee80211_hw *hw, struct ieee80211_vif *vif); /** - * ieee80211_get_tkip_key - get a TKIP rc4 for skb + * ieee80211_get_tkip_p1k_iv - get a TKIP phase 1 key for IV32 * - * This function computes a TKIP rc4 key for an skb. It computes - * a phase 1 key if needed (iv16 wraps around). This function is to - * be used by drivers which can do HW encryption but need to compute - * to phase 1/2 key in SW. + * This function returns the TKIP phase 1 key for the given IV32. * * @keyconf: the parameter passed with the set key - * @skb: the skb for which the key is needed - * @type: TBD - * @key: a buffer to which the key will be written + * @iv32: IV32 to get the P1K for + * @p1k: a buffer to which the key will be written, as 5 u16 values */ -void ieee80211_get_tkip_key(struct ieee80211_key_conf *keyconf, - struct sk_buff *skb, - enum ieee80211_tkip_key_type type, u8 *key); +void ieee80211_get_tkip_p1k_iv(struct ieee80211_key_conf *keyconf, + u32 iv32, u16 *p1k); + +/** + * ieee80211_get_tkip_p1k - get a TKIP phase 1 key + * + * This function returns the TKIP phase 1 key for the IV32 taken + * from the given packet. + * + * @keyconf: the parameter passed with the set key + * @skb: the packet to take the IV32 value from that will be encrypted + * with this P1K + * @p1k: a buffer to which the key will be written, as 5 u16 values + */ +static inline void ieee80211_get_tkip_p1k(struct ieee80211_key_conf *keyconf, + struct sk_buff *skb, u16 *p1k) +{ + 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]); + + ieee80211_get_tkip_p1k_iv(keyconf, iv32, p1k); +} + +/** + * ieee80211_get_tkip_rx_p1k - get a TKIP phase 1 key for RX + * + * This function returns the TKIP phase 1 key for the given IV32 + * and transmitter address. + * + * @keyconf: the parameter passed with the set key + * @ta: TA that will be used with the key + * @iv32: IV32 to get the P1K for + * @p1k: a buffer to which the key will be written, as 5 u16 values + */ +void ieee80211_get_tkip_rx_p1k(struct ieee80211_key_conf *keyconf, + const u8 *ta, u32 iv32, u16 *p1k); + +/** + * ieee80211_get_tkip_p2k - get a TKIP phase 2 key + * + * This function computes the TKIP RC4 key for the IV values + * in the packet. + * + * @keyconf: the parameter passed with the set key + * @skb: the packet to take the IV32/IV16 values from that will be + * encrypted with this key + * @p2k: a buffer to which the key will be written, 16 bytes + */ +void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf, + struct sk_buff *skb, u8 *p2k); + +/** + * struct ieee80211_key_seq - key sequence counter + * + * @tkip: TKIP data, containing IV32 and IV16 in host byte order + * @ccmp: PN data, most significant byte first (big endian, + * reverse order than in packet) + * @aes_cmac: PN data, most significant byte first (big endian, + * reverse order than in packet) + */ +struct ieee80211_key_seq { + union { + struct { + u32 iv32; + u16 iv16; + } tkip; + struct { + u8 pn[6]; + } ccmp; + struct { + u8 pn[6]; + } aes_cmac; + }; +}; + +/** + * ieee80211_get_key_tx_seq - get key TX sequence counter + * + * @keyconf: the parameter passed with the set key + * @seq: buffer to receive the sequence data + * + * This function allows a driver to retrieve the current TX IV/PN + * for the given key. It must not be called if IV generation is + * offloaded to the device. + * + * Note that this function may only be called when no TX processing + * can be done concurrently, for example when queues are stopped + * and the stop has been synchronized. + */ +void ieee80211_get_key_tx_seq(struct ieee80211_key_conf *keyconf, + struct ieee80211_key_seq *seq); + +/** + * ieee80211_get_key_rx_seq - get key RX sequence counter + * + * @keyconf: the parameter passed with the set key + * @tid: The TID, or -1 for the management frame value (CCMP only); + * the value on TID 0 is also used for non-QoS frames. For + * CMAC, only TID 0 is valid. + * @seq: buffer to receive the sequence data + * + * This function allows a driver to retrieve the current RX IV/PNs + * for the given key. It must not be called if IV checking is done + * by the device and not by mac80211. + * + * Note that this function may only be called when no RX processing + * can be done concurrently. + */ +void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, + int tid, struct ieee80211_key_seq *seq); + +/** + * ieee80211_gtk_rekey_notify - notify userspace supplicant of rekeying + * @vif: virtual interface the rekeying was done on + * @bssid: The BSSID of the AP, for checking association + * @replay_ctr: the new replay counter after GTK rekeying + * @gfp: allocation flags + */ +void ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const u8 *bssid, + const u8 *replay_ctr, gfp_t gfp); + /** * ieee80211_wake_queue - wake specific queue * @hw: pointer as obtained from ieee80211_alloc_hw(). @@ -2830,6 +3271,51 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, bool block); /** + * ieee80211_sta_eosp - notify mac80211 about end of SP + * @pubsta: the station + * + * When a device transmits frames in a way that it can't tell + * mac80211 in the TX status about the EOSP, it must clear the + * %IEEE80211_TX_STATUS_EOSP bit and call this function instead. + * This applies for PS-Poll as well as uAPSD. + * + * Note that there is no non-_irqsafe version right now as + * it wasn't needed, but just like _tx_status() and _rx() + * must not be mixed in irqsafe/non-irqsafe versions, this + * function must not be mixed with those either. Use the + * all irqsafe, or all non-irqsafe, don't mix! If you need + * the non-irqsafe version of this, you need to add it. + */ +void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta); + +/** + * ieee80211_iter_keys - iterate keys programmed into the device + * @hw: pointer obtained from ieee80211_alloc_hw() + * @vif: virtual interface to iterate, may be %NULL for all + * @iter: iterator function that will be called for each key + * @iter_data: custom data to pass to the iterator function + * + * This function can be used to iterate all the keys known to + * mac80211, even those that weren't previously programmed into + * the device. This is intended for use in WoWLAN if the device + * needs reprogramming of the keys during suspend. Note that due + * to locking reasons, it is also only safe to call this at few + * spots since it must hold the RTNL and be able to sleep. + * + * The order in which the keys are iterated matches the order + * in which they were originally installed and handed to the + * set_key callback. + */ +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); + +/** * ieee80211_ap_probereq_get - retrieve a Probe Request template * @hw: pointer obtained from ieee80211_alloc_hw(). * @vif: &struct ieee80211_vif pointer from the add_interface callback. @@ -2849,7 +3335,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, * * @vif: &struct ieee80211_vif pointer from the add_interface callback. * - * When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTER and + * When beacon filtering is enabled with %IEEE80211_VIF_BEACON_FILTER and * %IEEE80211_CONF_PS is set, the driver needs to inform whenever the * hardware is not receiving beacons with this function. */ @@ -2860,7 +3346,7 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif); * * @vif: &struct ieee80211_vif pointer from the add_interface callback. * - * When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTER, and + * When beacon filtering is enabled with %IEEE80211_VIF_BEACON_FILTER, and * %IEEE80211_CONF_PS and %IEEE80211_HW_CONNECTION_MONITOR are set, the driver * needs to inform if the connection to the AP has been lost. * @@ -2870,6 +3356,29 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif); void ieee80211_connection_loss(struct ieee80211_vif *vif); /** + * ieee80211_resume_disconnect - disconnect from AP after resume + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * Instructs mac80211 to disconnect from the AP after resume. + * Drivers can use this after WoWLAN if they know that the + * connection cannot be kept up, for example because keys were + * used while the device was asleep but the replay counters or + * similar cannot be retrieved from the device during resume. + * + * Note that due to implementation issues, if the driver uses + * the reconfiguration functionality during resume the interface + * will still be added as associated first during resume and then + * disconnect normally later. + * + * This function can only be called from the resume callback and + * the driver must not be holding any of its own locks while it + * calls this function, or at least not any locks it needs in the + * key configuration paths (if it supports HW crypto). + */ +void ieee80211_resume_disconnect(struct ieee80211_vif *vif); + +/** * ieee80211_disable_dyn_ps - force mac80211 to temporarily disable dynamic psm * * @vif: &struct ieee80211_vif pointer from the add_interface callback. @@ -2907,7 +3416,7 @@ void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif); * @rssi_event: the RSSI trigger event type * @gfp: context flags * - * When the %IEEE80211_HW_SUPPORTS_CQM_RSSI is set, and a connection quality + * When the %IEEE80211_VIF_SUPPORTS_CQM_RSSI is set, and a connection quality * monitoring is configured with an rssi threshold, the driver will inform * whenever the rssi level reaches the threshold. */ @@ -2916,6 +3425,16 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, gfp_t gfp); /** + * ieee80211_get_operstate - get the operstate of the vif + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * The driver might need to know the operstate of the net_device + * (specifically, whether the link is IF_OPER_UP after resume) + */ +unsigned char ieee80211_get_operstate(struct ieee80211_vif *vif); + +/** * ieee80211_chswitch_done - Complete channel switch process * @vif: &struct ieee80211_vif pointer from the add_interface callback. * @success: make the channel switch successful or not @@ -2965,6 +3484,36 @@ void ieee80211_ready_on_channel(struct ieee80211_hw *hw); */ void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw); +/** + * ieee80211_stop_rx_ba_session - callback to stop existing BA sessions + * + * in order not to harm the system performance and user experience, the device + * may request not to allow any rx ba session and tear down existing rx ba + * sessions based on system constraints such as periodic BT activity that needs + * to limit wlan activity (eg.sco or a2dp)." + * in such cases, the intention is to limit the duration of the rx ppdu and + * therefore prevent the peer device to use a-mpdu aggregation. + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @ba_rx_bitmap: Bit map of open rx ba per tid + * @addr: & to bssid mac address + */ +void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap, + const u8 *addr); + +/** + * ieee80211_send_bar - send a BlockAckReq frame + * + * can be used to flush pending frames from the peer's aggregation reorder + * buffer. + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @ra: the peer's destination address + * @tid: the TID of the aggregation session + * @ssn: the new starting sequence number for the receiver + */ +void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn); + /* Rate control API */ /** @@ -2972,9 +3521,12 @@ void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw); * * @IEEE80211_RC_HT_CHANGED: The HT parameters of the operating channel have * changed, rate control algorithm can update its internal state if needed. + * @IEEE80211_RC_SMPS_CHANGED: The SMPS state of the station changed, the rate + * control algorithm needs to adjust accordingly. */ enum rate_control_changed { - IEEE80211_RC_HT_CHANGED = BIT(0) + IEEE80211_RC_HT_CHANGED = BIT(0), + IEEE80211_RC_SMPS_CHANGED = BIT(1), }; /** @@ -2983,6 +3535,8 @@ enum rate_control_changed { * @hw: The hardware the algorithm is invoked for. * @sband: The band this frame is being transmitted on. * @bss_conf: the current BSS configuration + * @skb: the skb that will be transmitted, the control information in it needs + * to be filled in * @reported_rate: The rate control algorithm can fill this in to indicate * which rate should be reported to userspace as the current rate and * used for rate calculations in the mesh network. @@ -2990,12 +3544,11 @@ enum rate_control_changed { * RTS threshold * @short_preamble: whether mac80211 will request short-preamble transmission * if the selected rate supports it - * @max_rate_idx: user-requested maximum rate (not MCS for now) + * @max_rate_idx: user-requested maximum (legacy) rate * (deprecated; this will be removed once drivers get updated to use * rate_idx_mask) - * @rate_idx_mask: user-requested rate mask (not MCS for now) - * @skb: the skb that will be transmitted, the control information in it needs - * to be filled in + * @rate_idx_mask: user-requested (legacy) rate mask + * @rate_idx_mcs_mask: user-requested MCS rate mask * @bss: whether this frame is sent out in AP or IBSS mode */ struct ieee80211_tx_rate_control { @@ -3007,6 +3560,7 @@ struct ieee80211_tx_rate_control { bool rts, short_preamble; u8 max_rate_idx; u32 rate_idx_mask; + u8 rate_idx_mcs_mask[IEEE80211_HT_MCS_MASK_LEN]; bool bss; }; @@ -3078,8 +3632,9 @@ rate_lowest_index(struct ieee80211_supported_band *sband, return i; /* warn when we cannot find a rate. */ - WARN_ON(1); + WARN_ON_ONCE(1); + /* and return 0 (the lowest index) */ return 0; } @@ -3150,4 +3705,14 @@ ieee80211_vif_type_p2p(struct ieee80211_vif *vif) return ieee80211_iftype_p2p(vif->type, vif->p2p); } +void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif, + int rssi_min_thold, + int rssi_max_thold); + +void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif); + +int ieee80211_add_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb); + +int ieee80211_add_ext_srates_ie(struct ieee80211_vif *vif, + struct sk_buff *skb); #endif /* MAC80211_H */ diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 1d11fa2..9e745e21 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3046,6 +3046,26 @@ void skb_tstamp_tx(struct sk_buff *orig_skb, } EXPORT_SYMBOL_GPL(skb_tstamp_tx); +void skb_complete_wifi_ack(struct sk_buff *skb, bool acked) +{ + struct sock *sk = skb->sk; + struct sock_exterr_skb *serr; + int err; + + skb->wifi_acked_valid = 1; + skb->wifi_acked = acked; + + serr = SKB_EXT_ERR(skb); + memset(serr, 0, sizeof(*serr)); + serr->ee.ee_errno = ENOMSG; + serr->ee.ee_origin = SO_EE_ORIGIN_TXSTATUS; + + err = sock_queue_err_skb(sk, skb); + if (err) + kfree_skb(skb); +} +EXPORT_SYMBOL_GPL(skb_complete_wifi_ack); + /** * skb_partial_csum_set - set up and verify partial csum values for packet diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index f5fdfcb..96ddb72 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 @@ -222,15 +247,3 @@ config MAC80211_DEBUG_COUNTERS and show them in debugfs. If unsure, say N. - -config MAC80211_DRIVER_API_TRACER - bool "Driver API tracer" - depends on MAC80211_DEBUG_MENU - depends on EVENT_TRACING - help - Say Y here to make mac80211 register with the ftrace - framework for the driver API -- you can then see which - driver methods it is calling and which API functions - drivers are calling by looking at the trace. - - If unsure, say Y. diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index fdb54e6..1be7a45 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -9,7 +9,7 @@ mac80211-y := \ scan.o offchannel.o \ ht.o agg-tx.o agg-rx.o \ ibss.o \ - mlme.o work.o \ + work.o \ iface.o \ rate.o \ michael.o \ @@ -24,7 +24,8 @@ mac80211-y := \ util.o \ wme.o \ event.o \ - chan.o + chan.o \ + driver-trace.o mlme.o mac80211-$(CONFIG_MAC80211_LEDS) += led.o mac80211-$(CONFIG_MAC80211_DEBUGFS) += \ @@ -41,7 +42,6 @@ mac80211-$(CONFIG_MAC80211_MESH) += \ mac80211-$(CONFIG_PM) += pm.o -mac80211-$(CONFIG_MAC80211_DRIVER_API_TRACER) += driver-trace.o CFLAGS_driver-trace.o := -I$(src) # objects for PID algorithm 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 #include #include +#include #include #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 -#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 #include #include +#include #include #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 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..df2b6ac 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -38,6 +38,7 @@ #include #include +#include #include #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,11 +70,14 @@ 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", - sta->sta.addr, tid); + printk(KERN_DEBUG + "Rx BA session stop requested for %pM tid %u %s reason: %d\n", + sta->sta.addr, tid, + initiator == WLAN_BACK_RECIPIENT ? "recipient" : "inititator", + (int)reason); #endif /* CONFIG_MAC80211_HT_DEBUG */ if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP, @@ -86,10 +88,16 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, /* check if this is a self generated aggregation halt */ if (initiator == WLAN_BACK_RECIPIENT && tx) ieee80211_send_delba(sta->sdata, sta->sta.addr, - tid, 0, reason); + tid, WLAN_BACK_RECIPIENT, reason); 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 +109,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_bss(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 +176,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); @@ -158,10 +185,13 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); if (sdata->vif.type == NL80211_IFTYPE_AP || - sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + sdata->vif.type == NL80211_IFTYPE_AP_VLAN || + sdata->vif.type == NL80211_IFTYPE_MESH_POINT) memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); else if (sdata->vif.type == NL80211_IFTYPE_STATION) memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); + else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) + memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); @@ -205,7 +235,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 +278,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 +308,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..76be617 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -15,6 +15,7 @@ #include #include +#include #include #include "ieee80211_i.h" #include "driver-ops.h" @@ -54,6 +55,8 @@ * @ampdu_action function will be called with the action * %IEEE80211_AMPDU_TX_STOP. In this case, the call must not fail, * and the driver must later call ieee80211_stop_tx_ba_cb_irqsafe(). + * Note that the sta can get destroyed before the BA tear down is + * complete. */ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, @@ -68,21 +71,22 @@ 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); memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); if (sdata->vif.type == NL80211_IFTYPE_AP || - sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + sdata->vif.type == NL80211_IFTYPE_AP_VLAN || + sdata->vif.type == NL80211_IFTYPE_MESH_POINT) memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); else if (sdata->vif.type == NL80211_IFTYPE_STATION) memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); + else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) + memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); @@ -103,22 +107,21 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, mgmt->u.action.u.addba_req.start_seq_num = cpu_to_le16(start_seq_num << 4); - ieee80211_tx_skb(sdata, skb); + ieee80211_tx_skb_tid(sdata, skb, tid); } -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 +131,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); + ieee80211_tx_skb_tid(sdata, skb, tid); } +EXPORT_SYMBOL(ieee80211_send_bar); void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, struct tid_ampdu_tx *tid_tx) @@ -186,6 +190,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, #endif /* CONFIG_MAC80211_HT_DEBUG */ del_timer_sync(&tid_tx->addba_resp_timer); + del_timer_sync(&tid_tx->session_timer); /* * After this packets are no longer handed right through @@ -387,6 +392,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) #endif spin_lock_bh(&sta->lock); + sta->ampdu_mlme.last_addba_req_time[tid] = jiffies; sta->ampdu_mlme.addba_req_num[tid]++; spin_unlock_bh(&sta->lock); @@ -397,6 +403,28 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) tid_tx->timeout); } +/* + * After accepting the AddBA Response we activated a timer, + * resetting it after each frame that we send. + */ +static void sta_tx_agg_session_timer_expired(unsigned long data) +{ + /* not an elegant detour, but there is no choice as the timer passes + * only one argument, and various sta_info are needed here, so init + * flow in sta_info_create gives the TID as data, while the timer_to_id + * array gives the sta through container_of */ + u8 *ptid = (u8 *)data; + u8 *timer_to_id = ptid - *ptid; + struct sta_info *sta = container_of(timer_to_id, struct sta_info, + timer_to_tid[0]); + +#ifdef CONFIG_MAC80211_HT_DEBUG + printk(KERN_DEBUG "tx session timer expired on tid %d\n", (u16)*ptid); +#endif + + ieee80211_stop_tx_ba_session(&sta->sta, *ptid); +} + int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, u16 timeout) { @@ -412,7 +440,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 @@ -420,18 +449,14 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, pubsta->addr, tid); #endif /* CONFIG_MAC80211_HT_DEBUG */ - /* - * The aggregation code is not prepared to handle - * anything but STA/AP due to the BSSID handling. - * IBSS could work in the code but isn't supported - * by drivers or the standard. - */ if (sdata->vif.type != NL80211_IFTYPE_STATION && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT && sdata->vif.type != NL80211_IFTYPE_AP_VLAN && - sdata->vif.type != NL80211_IFTYPE_AP) + sdata->vif.type != NL80211_IFTYPE_AP && + sdata->vif.type != NL80211_IFTYPE_ADHOC) 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"); @@ -439,6 +464,27 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, return -EINVAL; } + /* + * 802.11n-2009 11.5.1.1: If the initiating STA is an HT STA, is a + * member of an IBSS, and has no other existing Block Ack agreement + * with the recipient STA, then the initiating STA shall transmit a + * Probe Request frame to the recipient STA and shall not transmit an + * ADDBA Request frame unless it receives a Probe Response frame + * from the recipient within dot11ADDBAFailureTimeout. + * + * The probe request mechanism for ADDBA is currently not implemented, + * but we only build up Block Ack session with HT STAs. This information + * is set when we receive a bss info from a probe response or a beacon. + */ + if (sta->sdata->vif.type == NL80211_IFTYPE_ADHOC && + !sta->sta.ht_cap.ht_supported) { +#ifdef CONFIG_MAC80211_HT_DEBUG + printk(KERN_DEBUG "BA request denied - IBSS STA %pM" + "does not advertise HT support\n", pubsta->addr); +#endif /* CONFIG_MAC80211_HT_DEBUG */ + return -EINVAL; + } + spin_lock_bh(&sta->lock); /* we have tried too many times, receiver does not want A-MPDU */ @@ -447,6 +493,24 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, goto err_unlock_sta; } + /* + * if we have tried more than HT_AGG_BURST_RETRIES times we + * will spread our requests in time to avoid stalling connection + * for too long + */ + if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_BURST_RETRIES && + time_before(jiffies, sta->ampdu_mlme.last_addba_req_time[tid] + + HT_AGG_RETRIES_PERIOD)) { +#ifdef CONFIG_MAC80211_HT_DEBUG + printk(KERN_DEBUG "BA request denied - " + "waiting a grace period after %d failed requests " + "on tid %u\n", + sta->ampdu_mlme.addba_req_num[tid], tid); +#endif /* CONFIG_MAC80211_HT_DEBUG */ + ret = -EBUSY; + goto err_unlock_sta; + } + tid_tx = rcu_dereference_protected_tid_tx(sta, tid); /* check if the TID is not in aggregation flow already */ if (tid_tx || sta->ampdu_mlme.tid_start_tx[tid]) { @@ -461,11 +525,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; } @@ -475,11 +534,16 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, tid_tx->timeout = timeout; - /* Tx timer */ + /* response timer */ tid_tx->addba_resp_timer.function = sta_addba_resp_timer_expired; tid_tx->addba_resp_timer.data = (unsigned long)&sta->timer_to_tid[tid]; init_timer(&tid_tx->addba_resp_timer); + /* tx timer */ + tid_tx->session_timer.function = sta_tx_agg_session_timer_expired; + tid_tx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid]; + init_timer(&tid_tx->session_timer); + /* assign a dialog token */ sta->ampdu_mlme.dialog_token_allocator++; tid_tx->dialog_token = sta->ampdu_mlme.dialog_token_allocator; @@ -552,7 +616,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) } mutex_lock(&local->sta_mtx); - sta = sta_info_get(sdata, ra); + sta = sta_info_get_bss(sdata, ra); if (!sta) { mutex_unlock(&local->sta_mtx); #ifdef CONFIG_MAC80211_HT_DEBUG @@ -590,14 +654,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; @@ -686,7 +745,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) mutex_lock(&local->sta_mtx); - sta = sta_info_get(sdata, ra); + sta = sta_info_get_bss(sdata, ra); if (!sta) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Could not find station: %pM\n", ra); @@ -743,14 +802,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 +863,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 */ @@ -832,6 +883,11 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, ieee80211_agg_tx_operational(local, sta, tid); sta->ampdu_mlme.addba_req_num[tid] = 0; + + if (tid_tx->timeout) + mod_timer(&tid_tx->session_timer, + TU_TO_EXP_TIME(tid_tx->timeout)); + } else { ___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR, true); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 143a006..9443348 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #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; @@ -101,6 +102,16 @@ static int ieee80211_change_iface(struct wiphy *wiphy, return 0; } +static int ieee80211_set_noack_map(struct wiphy *wiphy, + struct net_device *dev, + u16 noack_map) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + sdata->noack_map = noack_map; + return 0; +} + static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, bool pairwise, const u8 *mac_addr, struct key_params *params) @@ -140,7 +151,17 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, sta = sta_info_get(sdata, mac_addr); else sta = sta_info_get_bss(sdata, mac_addr); - if (!sta) { + /* + * The ASSOC test makes sure the driver is ready to + * receive the key. When wpa_supplicant has roamed + * using FT, it attempts to set the key before + * association has completed, this rejects that attempt + * so it will set the key again after assocation. + * + * TODO: accept the key if we have a station entry and + * add it to the device after the station. + */ + if (!sta || !test_sta_flag(sta, WLAN_STA_ASSOC)) { ieee80211_key_free(sdata->local, key); err = -ENOENT; goto out_unlock; @@ -209,6 +230,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 +278,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; @@ -322,6 +346,20 @@ static void rate_idx_to_bitrate(struct rate_info *rate, struct sta_info *sta, in rate->mcs = idx; } +void sta_set_rate_info_tx(struct sta_info *sta, + const struct ieee80211_tx_rate *rate, + struct rate_info *rinfo) +{ + rinfo->flags = 0; + if (rate->flags & IEEE80211_TX_RC_MCS) + rinfo->flags |= RATE_INFO_FLAGS_MCS; + if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) + rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; + rate_idx_to_bitrate(rinfo, sta, rate->idx); +} + static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = sta->sdata; @@ -340,7 +378,9 @@ 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 | + STATION_INFO_BEACON_LOSS_COUNT; do_posix_clock_monotonic_gettime(&uptime); sinfo->connected_time = uptime.tv_sec - sta->last_connected; @@ -353,6 +393,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->tx_retries = sta->tx_retry_count; sinfo->tx_failed = sta->tx_retry_failed; sinfo->rx_dropped_misc = sta->rx_dropped; + sinfo->beacon_loss_count = sta->beacon_loss_count; if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) || (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) { @@ -361,14 +402,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal); } - sinfo->txrate.flags = 0; - if (sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS) - sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; - if (sta->last_tx_rate.flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; - if (sta->last_tx_rate.flags & IEEE80211_TX_RC_SHORT_GI) - sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; - rate_idx_to_bitrate(&sinfo->txrate, sta, sta->last_tx_rate.idx); + sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate); sinfo->rxrate.flags = 0; if (sta->last_rx_rate_flag & RX_FLAG_HT) @@ -400,6 +434,26 @@ 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) | + BIT(NL80211_STA_FLAG_TDLS_PEER); + 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); + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); } @@ -452,41 +506,45 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, return ret; } -/* - * This handles both adding a beacon and setting new beacon info - */ -static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata, - struct beacon_parameters *params) +static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata, + const u8 *resp, size_t resp_len) { - struct beacon_data *new, *old; - int new_head_len, new_tail_len; - int size; - int err = -EINVAL; + struct sk_buff *new, *old; - old = rtnl_dereference(sdata->u.ap.beacon); + if (!resp || !resp_len) + return 1; - /* head must not be zero-length */ - if (params->head && !params->head_len) - return -EINVAL; + old = rtnl_dereference(sdata->u.ap.probe_resp); - /* - * This is a kludge. beacon interval should really be part - * of the beacon information. - */ - if (params->interval && - (sdata->vif.bss_conf.beacon_int != params->interval)) { - sdata->vif.bss_conf.beacon_int = params->interval; - ieee80211_bss_info_change_notify(sdata, - BSS_CHANGED_BEACON_INT); + new = dev_alloc_skb(resp_len); + if (!new) + return -ENOMEM; + + memcpy(skb_put(new, resp_len), resp, resp_len); + + rcu_assign_pointer(sdata->u.ap.probe_resp, new); + if (old) { + /* TODO: use call_rcu() */ + synchronize_rcu(); + dev_kfree_skb(old); } + return 0; +} + +static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, + struct cfg80211_beacon_data *params) +{ + struct beacon_data *new, *old; + int new_head_len, new_tail_len; + int size, err; + u32 changed = BSS_CHANGED_BEACON; + + old = rtnl_dereference(sdata->u.ap.beacon); + /* Need to have a beacon head if we don't have one yet */ if (!params->head && !old) - return err; - - /* sorry, no way to start beaconing without dtim period */ - if (!params->dtim_period && !old) - return err; + return -EINVAL; /* new or old head? */ if (params->head) @@ -509,12 +567,6 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata, /* start filling the new info now */ - /* new or old dtim period? */ - if (params->dtim_period) - new->dtim_period = params->dtim_period; - else - new->dtim_period = old->dtim_period; - /* * pointers go into the block we allocated, * memory is | beacon_data | head | tail | @@ -537,39 +589,76 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata, if (old) memcpy(new->tail, old->tail, new_tail_len); - sdata->vif.bss_conf.dtim_period = new->dtim_period; + err = ieee80211_set_probe_resp(sdata, params->probe_resp, + params->probe_resp_len); + if (err < 0) + return err; + if (err == 0) + changed |= BSS_CHANGED_AP_PROBE_RESP; rcu_assign_pointer(sdata->u.ap.beacon, new); - synchronize_rcu(); - - kfree(old); + if (old) + kfree_rcu(old, rcu_head); - ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | - BSS_CHANGED_BEACON); - return 0; + return changed; } -static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *params) +static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ap_settings *params) { - struct ieee80211_sub_if_data *sdata; + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct beacon_data *old; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_sub_if_data *vlan; + u32 changed = BSS_CHANGED_BEACON_INT | + BSS_CHANGED_BEACON_ENABLED | + BSS_CHANGED_BEACON | + BSS_CHANGED_SSID; + int err; old = rtnl_dereference(sdata->u.ap.beacon); if (old) return -EALREADY; - return ieee80211_config_beacon(sdata, params); + /* + * Apply control port protocol, this allows us to + * not encrypt dynamic WEP control frames. + */ + sdata->control_port_protocol = params->crypto.control_port_ethertype; + sdata->control_port_no_encrypt = params->crypto.control_port_no_encrypt; + list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { + vlan->control_port_protocol = + params->crypto.control_port_ethertype; + vlan->control_port_no_encrypt = + params->crypto.control_port_no_encrypt; + } + + sdata->vif.bss_conf.beacon_int = params->beacon_interval; + sdata->vif.bss_conf.dtim_period = params->dtim_period; + + sdata->vif.bss_conf.ssid_len = params->ssid_len; + if (params->ssid_len) + memcpy(sdata->vif.bss_conf.ssid, params->ssid, + params->ssid_len); + sdata->vif.bss_conf.hidden_ssid = + (params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE); + + err = ieee80211_assign_beacon(sdata, ¶ms->beacon); + if (err < 0) + return err; + changed |= err; + + ieee80211_bss_info_change_notify(sdata, changed); + + return 0; } -static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *params) +static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_beacon_data *params) { struct ieee80211_sub_if_data *sdata; struct beacon_data *old; + int err; sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -577,10 +666,14 @@ static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, if (!old) return -ENOENT; - return ieee80211_config_beacon(sdata, params); + err = ieee80211_assign_beacon(sdata, params); + if (err < 0) + return err; + ieee80211_bss_info_change_notify(sdata, err); + return 0; } -static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) +static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) { struct ieee80211_sub_if_data *sdata; struct beacon_data *old; @@ -591,11 +684,12 @@ static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) if (!old) return -ENOENT; - rcu_assign_pointer(sdata->u.ap.beacon, NULL); - synchronize_rcu(); - kfree(old); + RCU_INIT_POINTER(sdata->u.ap.beacon, NULL); + + kfree_rcu(old, rcu_head); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); + return 0; } @@ -643,11 +737,11 @@ static void ieee80211_send_layer2_update(struct sta_info *sta) netif_rx_ni(skb); } -static void sta_apply_parameters(struct ieee80211_local *local, - struct sta_info *sta, - struct station_parameters *params) +static int sta_apply_parameters(struct ieee80211_local *local, + struct sta_info *sta, + struct station_parameters *params) { - unsigned long flags; + int ret = 0; u32 rates; int i, j; struct ieee80211_supported_band *sband; @@ -656,40 +750,91 @@ 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; + /* + * In mesh mode, we can clear AUTHENTICATED flag but must + * also make ASSOCIATED follow appropriately for the driver + * API. See also below, after AUTHORIZED changes. + */ + if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { + /* cfg80211 should not allow this in non-mesh modes */ + if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif))) + return -EINVAL; + + if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED) && + !test_sta_flag(sta, WLAN_STA_AUTH)) { + ret = sta_info_move_state(sta, IEEE80211_STA_AUTH); + if (ret) + return ret; + ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); + if (ret) + return ret; + } + } + if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { - sta->flags &= ~WLAN_STA_AUTHORIZED; if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) - sta->flags |= WLAN_STA_AUTHORIZED; + ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); + else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) + ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); + if (ret) + return ret; + } + + if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { + /* cfg80211 should not allow this in non-mesh modes */ + if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif))) + return -EINVAL; + + if (!(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) && + test_sta_flag(sta, WLAN_STA_AUTH)) { + ret = sta_info_move_state(sta, IEEE80211_STA_AUTH); + if (ret) + return ret; + ret = sta_info_move_state(sta, IEEE80211_STA_NONE); + if (ret) + return ret; + } } + 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; + 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 @@ -722,7 +867,7 @@ static void sta_apply_parameters(struct ieee80211_local *local, } if (params->ht_capa) - ieee80211_ht_cap_ie_to_sta_ht_cap(sband, + ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, params->ht_capa, &sta->sta.ht_cap); @@ -750,6 +895,8 @@ static void sta_apply_parameters(struct ieee80211_local *local, } #endif } + + return 0; } static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, @@ -780,11 +927,21 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, if (!sta) return -ENOMEM; - sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC; + sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); + sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); - sta_apply_parameters(local, sta, params); + err = sta_apply_parameters(local, sta, params); + if (err) { + sta_info_free(local, sta); + return err; + } - rate_control_rate_init(sta); + /* + * for TDLS, rate control should be initialized only when supported + * rates are known. + */ + if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) + rate_control_rate_init(sta); layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN || sdata->vif.type == NL80211_IFTYPE_AP; @@ -827,27 +984,36 @@ static int ieee80211_change_station(struct wiphy *wiphy, struct ieee80211_local *local = wiphy_priv(wiphy); struct sta_info *sta; struct ieee80211_sub_if_data *vlansdata; + int err; - rcu_read_lock(); + mutex_lock(&local->sta_mtx); sta = sta_info_get_bss(sdata, mac); if (!sta) { - rcu_read_unlock(); + mutex_unlock(&local->sta_mtx); return -ENOENT; } + /* in station mode, supported rates are only valid with TDLS */ + if (sdata->vif.type == NL80211_IFTYPE_STATION && + params->supported_rates && + !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { + mutex_unlock(&local->sta_mtx); + return -EINVAL; + } + if (params->vlan && params->vlan != sta->sdata->dev) { vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); if (vlansdata->vif.type != NL80211_IFTYPE_AP_VLAN && vlansdata->vif.type != NL80211_IFTYPE_AP) { - rcu_read_unlock(); + mutex_unlock(&local->sta_mtx); return -EINVAL; } if (params->vlan->ieee80211_ptr->use_4addr) { if (vlansdata->u.vlan.sta) { - rcu_read_unlock(); + mutex_unlock(&local->sta_mtx); return -EBUSY; } @@ -858,9 +1024,16 @@ static int ieee80211_change_station(struct wiphy *wiphy, ieee80211_send_layer2_update(sta); } - sta_apply_parameters(local, sta, params); + err = sta_apply_parameters(local, sta, params); + if (err) { + mutex_unlock(&local->sta_mtx); + return err; + } - rcu_read_unlock(); + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && params->supported_rates) + rate_control_rate_init(sta); + + mutex_unlock(&local->sta_mtx); if (sdata->vif.type == NL80211_IFTYPE_STATION && params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) @@ -912,7 +1085,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; } @@ -1052,6 +1225,8 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, { u8 *new_ie; const u8 *old_ie; + struct ieee80211_sub_if_data *sdata = container_of(ifmsh, + struct ieee80211_sub_if_data, u.mesh); /* allocate information elements */ new_ie = NULL; @@ -1078,6 +1253,10 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, if (setup->is_secure) ifmsh->security |= IEEE80211_MESH_SEC_SECURED; + /* mcast rate setting in Mesh Node */ + memcpy(sdata->vif.bss_conf.mcast_rate, setup->mcast_rate, + sizeof(setup->mcast_rate)); + return 0; } @@ -1123,6 +1302,9 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy, if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, mask)) conf->dot11MeshHWMPpreqMinInterval = nconf->dot11MeshHWMPpreqMinInterval; + if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, mask)) + conf->dot11MeshHWMPperrMinInterval = + nconf->dot11MeshHWMPperrMinInterval; if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, mask)) conf->dot11MeshHWMPnetDiameterTraversalTime = @@ -1131,6 +1313,32 @@ 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; + } + if (_chg_mesh_attr(NL80211_MESHCONF_FORWARDING, mask)) + conf->dot11MeshForwarding = nconf->dot11MeshForwarding; + if (_chg_mesh_attr(NL80211_MESHCONF_RSSI_THRESHOLD, mask)) { + /* our RSSI threshold implementation is supported only for + * devices that report signal in dBm. + */ + if (!(sdata->local->hw.flags & IEEE80211_HW_SIGNAL_DBM)) + return -ENOTSUPP; + conf->rssi_threshold = nconf->rssi_threshold; + } return 0; } @@ -1229,9 +1437,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 +1459,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); @@ -1301,7 +1515,7 @@ static int ieee80211_set_channel(struct wiphy *wiphy, (old_oper_type != local->_oper_channel_type)) ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); - if ((sdata && sdata->vif.type != NL80211_IFTYPE_MONITOR) && + if (sdata && sdata->vif.type != NL80211_IFTYPE_MONITOR && old_vif_oper_type != sdata->vif.bss_conf.channel_type) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_HT); @@ -1406,19 +1620,15 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_deauth_request *req, - void *cookie) + struct cfg80211_deauth_request *req) { - return ieee80211_mgd_deauth(IEEE80211_DEV_TO_SUB_IF(dev), - req, cookie); + return ieee80211_mgd_deauth(IEEE80211_DEV_TO_SUB_IF(dev), req); } static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_disassoc_request *req, - void *cookie) + struct cfg80211_disassoc_request *req) { - return ieee80211_mgd_disassoc(IEEE80211_DEV_TO_SUB_IF(dev), - req, cookie); + return ieee80211_mgd_disassoc(IEEE80211_DEV_TO_SUB_IF(dev), req); } static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, @@ -1554,6 +1764,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, @@ -1639,7 +1862,6 @@ static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy, s32 rssi_thold, u32 rssi_hyst) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_vif *vif = &sdata->vif; struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; @@ -1650,14 +1872,9 @@ static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy, bss_conf->cqm_rssi_thold = rssi_thold; bss_conf->cqm_rssi_hyst = rssi_hyst; - if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) { - if (sdata->vif.type != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; - return 0; - } - /* tell the driver upon association, unless already associated */ - if (sdata->u.mgd.associated) + if (sdata->u.mgd.associated && + sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_CQM); return 0; @@ -1678,8 +1895,11 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, return ret; } - for (i = 0; i < IEEE80211_NUM_BANDS; i++) + for (i = 0; i < IEEE80211_NUM_BANDS; i++) { sdata->rc_rateidx_mask[i] = mask->control[i].legacy; + memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].mcs, + sizeof(mask->control[i].mcs)); + } return 0; } @@ -1801,7 +2021,7 @@ ieee80211_offchan_tx_done(struct ieee80211_work *wk, struct sk_buff *skb) if (wk->offchan_tx.wait && !wk->offchan_tx.status) cfg80211_mgmt_tx_status(wk->sdata->dev, (unsigned long) wk->offchan_tx.frame, - wk->ie, wk->ie_len, false, GFP_KERNEL); + wk->data, wk->data_len, false, GFP_KERNEL); return WORK_DONE_DESTROY; } @@ -1810,7 +2030,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, + bool dont_wait_for_ack, u64 *cookie) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; @@ -1818,10 +2039,15 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, struct sta_info *sta; struct ieee80211_work *wk; const struct ieee80211_mgmt *mgmt = (void *)buf; - u32 flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX | - IEEE80211_TX_CTL_REQ_TX_STATUS; + u32 flags; bool is_offchan = false; + if (dont_wait_for_ack) + flags = IEEE80211_TX_CTL_NO_ACK; + else + flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX | + IEEE80211_TX_CTL_REQ_TX_STATUS; + /* Check that we are on the requested channel for transmission */ if (chan != local->tmp_channel && chan != local->oper_channel) @@ -1837,6 +2063,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 +2104,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; @@ -1970,8 +2172,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, wk->done = ieee80211_offchan_tx_done; wk->offchan_tx.frame = skb; wk->offchan_tx.wait = wait; - wk->ie_len = len; - memcpy(wk->ie, buf, len); + wk->data_len = len; + memcpy(wk->data, buf, len); ieee80211_add_work(wk); return 0; @@ -1988,18 +2190,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 +2275,414 @@ 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; +} + +static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u64 *cookie) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + struct ieee80211_qos_hdr *nullfunc; + struct sk_buff *skb; + int size = sizeof(*nullfunc); + __le16 fc; + bool qos; + struct ieee80211_tx_info *info; + struct sta_info *sta; + + rcu_read_lock(); + sta = sta_info_get(sdata, peer); + if (sta) { + qos = test_sta_flag(sta, WLAN_STA_WME); + rcu_read_unlock(); + } else { + rcu_read_unlock(); + return -ENOLINK; + } + + 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 -ENOMEM; + + skb->dev = dev; + + 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; + + info = IEEE80211_SKB_CB(skb); + + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS | + IEEE80211_TX_INTFL_NL80211_FRAME_TX; + + skb_set_queue_mapping(skb, IEEE80211_AC_VO); + skb->priority = 7; + if (qos) + nullfunc->qos_ctrl = cpu_to_le16(7); + + local_bh_disable(); + ieee80211_xmit(sdata, skb); + local_bh_enable(); + + *cookie = (unsigned long) skb; + return 0; +} + +static struct ieee80211_channel * +ieee80211_wiphy_get_channel(struct wiphy *wiphy) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + + return local->oper_channel; +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -2094,9 +2692,9 @@ struct cfg80211_ops mac80211_config_ops = { .get_key = ieee80211_get_key, .set_default_key = ieee80211_config_default_key, .set_default_mgmt_key = ieee80211_config_default_mgmt_key, - .add_beacon = ieee80211_add_beacon, - .set_beacon = ieee80211_set_beacon, - .del_beacon = ieee80211_del_beacon, + .start_ap = ieee80211_start_ap, + .change_beacon = ieee80211_change_beacon, + .stop_ap = ieee80211_stop_ap, .add_station = ieee80211_add_station, .del_station = ieee80211_del_station, .change_station = ieee80211_change_station, @@ -2134,6 +2732,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 +2745,10 @@ 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, + .probe_client = ieee80211_probe_client, + .get_channel = ieee80211_wiphy_get_channel, + .set_noack_map = ieee80211_set_noack_map, }; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 889c3e9..e00ce8c 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -3,6 +3,7 @@ */ #include +#include #include "ieee80211_i.h" static enum ieee80211_chan_mode @@ -20,23 +21,29 @@ __ieee80211_get_channel_mode(struct ieee80211_local *local, if (!ieee80211_sdata_running(sdata)) continue; - if (sdata->vif.type == NL80211_IFTYPE_MONITOR) + switch (sdata->vif.type) { + case NL80211_IFTYPE_MONITOR: continue; - - if (sdata->vif.type == NL80211_IFTYPE_STATION && - !sdata->u.mgd.associated) - continue; - - if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { + case NL80211_IFTYPE_STATION: + if (!sdata->u.mgd.associated) + continue; + break; + case NL80211_IFTYPE_ADHOC: if (!sdata->u.ibss.ssid_len) continue; if (!sdata->u.ibss.fixed_channel) return CHAN_MODE_HOPPING; - } - - if (sdata->vif.type == NL80211_IFTYPE_AP && - !sdata->u.ap.beacon) + break; + case NL80211_IFTYPE_AP_VLAN: + /* will also have _AP interface */ continue; + case NL80211_IFTYPE_AP: + if (!sdata->u.ap.beacon) + continue; + break; + default: + break; + } return CHAN_MODE_FIXED; } @@ -128,3 +135,29 @@ bool ieee80211_set_channel_type(struct ieee80211_local *local, return result; } + +/* + * ieee80211_get_tx_channel_type returns the channel type we should + * use for packet transmission, given the channel capability and + * whatever regulatory flags we have been given. + */ +enum nl80211_channel_type ieee80211_get_tx_channel_type( + struct ieee80211_local *local, + enum nl80211_channel_type channel_type) +{ + switch (channel_type) { + case NL80211_CHAN_HT40PLUS: + if (local->hw.conf.channel->flags & + IEEE80211_CHAN_NO_HT40PLUS) + return NL80211_CHAN_HT20; + break; + case NL80211_CHAN_HT40MINUS: + if (local->hw.conf.channel->flags & + IEEE80211_CHAN_NO_HT40MINUS) + return NL80211_CHAN_HT20; + break; + default: + break; + } + return channel_type; +} diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 186e02f..778e591 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -15,12 +15,6 @@ #include "rate.h" #include "debugfs.h" -int mac80211_open_file_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - #define DEBUGFS_FORMAT_BUFFER_SIZE 100 int mac80211_format_buffer(char __user *userbuf, size_t count, @@ -50,7 +44,7 @@ static ssize_t name## _read(struct file *file, char __user *userbuf, \ #define DEBUGFS_READONLY_FILE_OPS(name) \ static const struct file_operations name## _ops = { \ .read = name## _read, \ - .open = mac80211_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; @@ -78,57 +72,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) { @@ -144,131 +87,10 @@ static ssize_t reset_write(struct file *file, const char __user *user_buf, static const struct file_operations reset_ops = { .write = reset_write, - .open = mac80211_open_file_generic, + .open = simple_open, .llseek = noop_llseek, }; -static ssize_t noack_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - - return mac80211_format_buffer(user_buf, count, ppos, "%d\n", - local->wifi_wme_noack_test); -} - -static ssize_t noack_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - char buf[10]; - size_t len; - - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - buf[len] = '\0'; - - local->wifi_wme_noack_test = !!simple_strtoul(buf, NULL, 0); - - return count; -} - -static const struct file_operations noack_ops = { - .read = noack_read, - .write = noack_write, - .open = mac80211_open_file_generic, - .llseek = default_llseek, -}; - -static ssize_t uapsd_queues_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - return mac80211_format_buffer(user_buf, count, ppos, "0x%x\n", - local->uapsd_queues); -} - -static ssize_t uapsd_queues_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - unsigned long val; - char buf[10]; - size_t len; - 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); - - if (ret) - return -EINVAL; - - if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) - return -ERANGE; - - local->uapsd_queues = val; - - return count; -} - -static const struct file_operations uapsd_queues_ops = { - .read = uapsd_queues_read, - .write = uapsd_queues_write, - .open = mac80211_open_file_generic, - .llseek = default_llseek, -}; - -static ssize_t uapsd_max_sp_len_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - - return mac80211_format_buffer(user_buf, count, ppos, "0x%x\n", - local->uapsd_max_sp_len); -} - -static ssize_t uapsd_max_sp_len_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - unsigned long val; - char buf[10]; - size_t len; - 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); - - if (ret) - return -EINVAL; - - if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) - return -ERANGE; - - local->uapsd_max_sp_len = val; - - return count; -} - -static const struct file_operations uapsd_max_sp_len_ops = { - .read = uapsd_max_sp_len_read, - .write = uapsd_max_sp_len_write, - .open = mac80211_open_file_generic, - .llseek = default_llseek, -}; - static ssize_t channel_type_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -305,6 +127,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"); @@ -337,8 +162,6 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf, sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n"); if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE) sf += snprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n"); - if (local->hw.flags & IEEE80211_HW_BEACON_FILTER) - sf += snprintf(buf + sf, mxln - sf, "BEACON_FILTER\n"); if (local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS) sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n"); if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) @@ -349,12 +172,14 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf, sf += snprintf(buf + sf, mxln - sf, "REPORTS_TX_ACK_STATUS\n"); if (local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) sf += snprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n"); - if (local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) - sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_CQM_RSSI\n"); if (local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK) 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"); + if (local->hw.flags & IEEE80211_HW_SCAN_WHILE_IDLE) + sf += snprintf(buf + sf, mxln - sf, "SCAN_WHILE_IDLE\n"); rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); kfree(buf); @@ -423,7 +248,7 @@ static ssize_t stats_ ##name## _read(struct file *file, \ \ static const struct file_operations stats_ ##name## _ops = { \ .read = stats_ ##name## _read, \ - .open = mac80211_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ }; @@ -450,12 +275,8 @@ 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); - DEBUGFS_ADD(uapsd_queues); - DEBUGFS_ADD(uapsd_max_sp_len); DEBUGFS_ADD(channel_type); DEBUGFS_ADD(hwflags); DEBUGFS_ADD(user_power); diff --git a/net/mac80211/debugfs.h b/net/mac80211/debugfs.h index 7c87529..9be4e6d 100644 --- a/net/mac80211/debugfs.h +++ b/net/mac80211/debugfs.h @@ -3,7 +3,6 @@ #ifdef CONFIG_MAC80211_DEBUGFS extern void debugfs_hw_add(struct ieee80211_local *local); -extern int mac80211_open_file_generic(struct inode *inode, struct file *file); extern int mac80211_format_buffer(char __user *userbuf, size_t count, loff_t *ppos, char *fmt, ...); #else diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index 33c58b8..7932767 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c @@ -30,7 +30,7 @@ static ssize_t key_##name##_read(struct file *file, \ #define KEY_OPS(name) \ static const struct file_operations key_ ##name## _ops = { \ .read = key_##name##_read, \ - .open = mac80211_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ } @@ -45,7 +45,7 @@ static const struct file_operations key_ ##name## _ops = { \ #define KEY_CONF_OPS(name) \ static const struct file_operations key_ ##name## _ops = { \ .read = key_conf_##name##_read, \ - .open = mac80211_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ } @@ -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; @@ -224,9 +225,9 @@ KEY_OPS(key); key, &key_##name##_ops); void ieee80211_debugfs_key_add(struct ieee80211_key *key) - { +{ static int keycount; - char buf[50]; + char buf[100]; struct sta_info *sta; if (!key->local->debugfs.keys) @@ -243,7 +244,8 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key) sta = key->sta; if (sta) { - sprintf(buf, "../../stations/%pM", sta->sta.addr); + sprintf(buf, "../../netdev:%s/stations/%pM", + sta->sdata->name, sta->sta.addr); key->debugfs.stalink = debugfs_create_symlink("station", key->debugfs.dir, buf); } diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 9ea7c0d..fa4a689 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) @@ -48,25 +48,21 @@ static ssize_t ieee80211_if_write( size_t count, loff_t *ppos, ssize_t (*write)(struct ieee80211_sub_if_data *, const char *, int)) { - u8 *buf; + char buf[64]; ssize_t ret; - buf = kmalloc(count, GFP_KERNEL); - if (!buf) - return -ENOMEM; + if (count >= sizeof(buf)) + return -E2BIG; - ret = -EFAULT; if (copy_from_user(buf, userbuf, count)) - goto freebuf; + return -EFAULT; + buf[count] = '\0'; ret = -ENODEV; rtnl_lock(); - if (sdata->dev->reg_state == NETREG_REGISTERED) - ret = (*write)(sdata, buf, count); + ret = (*write)(sdata, buf, count); rtnl_unlock(); -freebuf: - kfree(buf); return ret; } @@ -86,6 +82,21 @@ static ssize_t ieee80211_if_fmt_##name( \ #define IEEE80211_IF_FMT_SIZE(name, field) \ IEEE80211_IF_FMT(name, field, "%zd\n") +#define IEEE80211_IF_FMT_HEXARRAY(name, field) \ +static ssize_t ieee80211_if_fmt_##name( \ + const struct ieee80211_sub_if_data *sdata, \ + char *buf, int buflen) \ +{ \ + char *p = buf; \ + int i; \ + for (i = 0; i < sizeof(sdata->field); i++) { \ + p += scnprintf(p, buflen + buf - p, "%.2x ", \ + sdata->field[i]); \ + } \ + p += scnprintf(p, buflen + buf - p, "\n"); \ + return p - buf; \ +} + #define IEEE80211_IF_FMT_ATOMIC(name, field) \ static ssize_t ieee80211_if_fmt_##name( \ const struct ieee80211_sub_if_data *sdata, \ @@ -122,7 +133,7 @@ static ssize_t ieee80211_if_read_##name(struct file *file, \ static const struct file_operations name##_ops = { \ .read = ieee80211_if_read_##name, \ .write = (_write), \ - .open = mac80211_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ } @@ -147,6 +158,11 @@ IEEE80211_IF_FILE(rc_rateidx_mask_2ghz, rc_rateidx_mask[IEEE80211_BAND_2GHZ], HEX); IEEE80211_IF_FILE(rc_rateidx_mask_5ghz, rc_rateidx_mask[IEEE80211_BAND_5GHZ], HEX); +IEEE80211_IF_FILE(rc_rateidx_mcs_mask_2ghz, + rc_rateidx_mcs_mask[IEEE80211_BAND_2GHZ], HEXARRAY); +IEEE80211_IF_FILE(rc_rateidx_mcs_mask_5ghz, + rc_rateidx_mcs_mask[IEEE80211_BAND_5GHZ], HEXARRAY); + IEEE80211_IF_FILE(flags, flags, HEX); IEEE80211_IF_FILE(state, state, LHEX); IEEE80211_IF_FILE(channel_type, vif.bss_conf.channel_type, DEC); @@ -319,7 +335,64 @@ static ssize_t ieee80211_if_parse_tkip_mic_test( __IEEE80211_IF_FILE_W(tkip_mic_test); +static ssize_t ieee80211_if_fmt_uapsd_queues( + const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) +{ + const struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + + return snprintf(buf, buflen, "0x%x\n", ifmgd->uapsd_queues); +} + +static ssize_t ieee80211_if_parse_uapsd_queues( + struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) +{ + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + u8 val; + int ret; + + ret = kstrtou8(buf, 0, &val); + if (ret) + return ret; + + if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) + return -ERANGE; + + ifmgd->uapsd_queues = val; + + return buflen; +} +__IEEE80211_IF_FILE_W(uapsd_queues); + +static ssize_t ieee80211_if_fmt_uapsd_max_sp_len( + const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) +{ + const struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + + return snprintf(buf, buflen, "0x%x\n", ifmgd->uapsd_max_sp_len); +} + +static ssize_t ieee80211_if_parse_uapsd_max_sp_len( + struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) +{ + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + unsigned long val; + int ret; + + ret = kstrtoul(buf, 0, &val); + if (ret) + return -EINVAL; + + if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) + return -ERANGE; + + ifmgd->uapsd_max_sp_len = val; + + return buflen; +} +__IEEE80211_IF_FILE_W(uapsd_max_sp_len); + /* AP attributes */ +IEEE80211_IF_FILE(num_sta_authorized, u.ap.num_sta_authorized, ATOMIC); IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC); IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC); @@ -331,6 +404,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 +453,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); @@ -362,6 +477,8 @@ IEEE80211_IF_FILE(dot11MeshHWMPactivePathTimeout, u.mesh.mshcfg.dot11MeshHWMPactivePathTimeout, DEC); IEEE80211_IF_FILE(dot11MeshHWMPpreqMinInterval, u.mesh.mshcfg.dot11MeshHWMPpreqMinInterval, DEC); +IEEE80211_IF_FILE(dot11MeshHWMPperrMinInterval, + u.mesh.mshcfg.dot11MeshHWMPperrMinInterval, DEC); IEEE80211_IF_FILE(dot11MeshHWMPnetDiameterTraversalTime, u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC); IEEE80211_IF_FILE(dot11MeshHWMPmaxPREQretries, @@ -372,6 +489,12 @@ 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); +IEEE80211_IF_FILE(dot11MeshForwarding, u.mesh.mshcfg.dot11MeshForwarding, DEC); +IEEE80211_IF_FILE(rssi_threshold, u.mesh.mshcfg.rssi_threshold, DEC); #endif @@ -391,6 +514,8 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(channel_type); DEBUGFS_ADD(rc_rateidx_mask_2ghz); DEBUGFS_ADD(rc_rateidx_mask_5ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz); DEBUGFS_ADD(bssid); DEBUGFS_ADD(aid); @@ -398,6 +523,8 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(ave_beacon); DEBUGFS_ADD_MODE(smps, 0600); DEBUGFS_ADD_MODE(tkip_mic_test, 0200); + DEBUGFS_ADD_MODE(uapsd_queues, 0600); + DEBUGFS_ADD_MODE(uapsd_max_sp_len, 0600); } static void add_ap_files(struct ieee80211_sub_if_data *sdata) @@ -408,13 +535,27 @@ static void add_ap_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(channel_type); DEBUGFS_ADD(rc_rateidx_mask_2ghz); DEBUGFS_ADD(rc_rateidx_mask_5ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz); + DEBUGFS_ADD(num_sta_authorized); DEBUGFS_ADD(num_sta_ps); DEBUGFS_ADD(dtim_count); DEBUGFS_ADD(num_buffered_multicast); DEBUGFS_ADD_MODE(tkip_mic_test, 0200); } +static void add_ibss_files(struct ieee80211_sub_if_data *sdata) +{ + DEBUGFS_ADD(channel_type); + DEBUGFS_ADD(rc_rateidx_mask_2ghz); + DEBUGFS_ADD(rc_rateidx_mask_5ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz); + + DEBUGFS_ADD_MODE(tsf, 0600); +} + static void add_wds_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_ADD(drop_unencrypted); @@ -423,6 +564,8 @@ static void add_wds_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(channel_type); DEBUGFS_ADD(rc_rateidx_mask_2ghz); DEBUGFS_ADD(rc_rateidx_mask_5ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz); DEBUGFS_ADD(peer); } @@ -435,6 +578,8 @@ static void add_vlan_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(channel_type); DEBUGFS_ADD(rc_rateidx_mask_2ghz); DEBUGFS_ADD(rc_rateidx_mask_5ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz); } static void add_monitor_files(struct ieee80211_sub_if_data *sdata) @@ -446,11 +591,15 @@ static void add_monitor_files(struct ieee80211_sub_if_data *sdata) #ifdef CONFIG_MAC80211_MESH +static void add_mesh_files(struct ieee80211_sub_if_data *sdata) +{ + DEBUGFS_ADD_MODE(tsf, 0600); +} + static void add_mesh_stats(struct ieee80211_sub_if_data *sdata) { struct dentry *dir = debugfs_create_dir("mesh_stats", sdata->debugfs.dir); - #define MESHSTATS_ADD(name)\ debugfs_create_file(#name, 0400, dir, sdata, &name##_ops); @@ -459,6 +608,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 } @@ -481,11 +631,15 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata) MESHPARAMS_ADD(dot11MeshMaxPeerLinks); MESHPARAMS_ADD(dot11MeshHWMPactivePathTimeout); MESHPARAMS_ADD(dot11MeshHWMPpreqMinInterval); + MESHPARAMS_ADD(dot11MeshHWMPperrMinInterval); MESHPARAMS_ADD(dot11MeshHWMPnetDiameterTraversalTime); MESHPARAMS_ADD(dot11MeshHWMPmaxPREQretries); MESHPARAMS_ADD(path_refresh_time); MESHPARAMS_ADD(min_discovery_timeout); - + MESHPARAMS_ADD(dot11MeshHWMPRootMode); + MESHPARAMS_ADD(dot11MeshHWMPRannInterval); + MESHPARAMS_ADD(dot11MeshGateAnnouncementProtocol); + MESHPARAMS_ADD(rssi_threshold); #undef MESHPARAMS_ADD } #endif @@ -498,6 +652,7 @@ static void add_files(struct ieee80211_sub_if_data *sdata) switch (sdata->vif.type) { case NL80211_IFTYPE_MESH_POINT: #ifdef CONFIG_MAC80211_MESH + add_mesh_files(sdata); add_mesh_stats(sdata); add_mesh_config(sdata); #endif @@ -506,7 +661,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 +700,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..832b2da 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -33,7 +33,7 @@ static ssize_t sta_ ##name## _read(struct file *file, \ #define STA_OPS(name) \ static const struct file_operations sta_ ##name## _ops = { \ .read = sta_##name##_read, \ - .open = mac80211_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ } @@ -41,7 +41,7 @@ static const struct file_operations sta_ ##name## _ops = { \ static const struct file_operations sta_ ##name## _ops = { \ .read = sta_##name##_read, \ .write = sta_##name##_write, \ - .open = mac80211_open_file_generic, \ + .open = simple_open, \ .llseek = generic_file_llseek, \ } @@ -56,19 +56,23 @@ 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%s%s", + TEST(AUTH), TEST(ASSOC), TEST(PS_STA), + TEST(PS_DRIVER), TEST(AUTHORIZED), + TEST(SHORT_PREAMBLE), + 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), TEST(4ADDR_EVENT), + TEST(INSERTED), TEST(RATE_CONTROL)); +#undef TEST return simple_read_from_buffer(userbuf, count, ppos, buf, res); } STA_OPS(flags); @@ -78,8 +82,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 +275,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..af4691f 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -5,11 +5,34 @@ #include "ieee80211_i.h" #include "driver-trace.h" +static inline void check_sdata_in_driver(struct ieee80211_sub_if_data *sdata) +{ + WARN_ON(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER)); +} + +static inline struct ieee80211_sub_if_data * +get_bss_sdata(struct ieee80211_sub_if_data *sdata) +{ + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, + u.ap); + + return sdata; +} + static inline void drv_tx(struct ieee80211_local *local, struct sk_buff *skb) { local->ops->tx(&local->hw, skb); } +static inline void drv_tx_frags(struct ieee80211_local *local, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct sk_buff_head *skbs) +{ + local->ops->tx_frags(&local->hw, vif, sta, skbs); +} + static inline int drv_start(struct ieee80211_local *local) { int ret; @@ -69,15 +92,23 @@ static inline int drv_resume(struct ieee80211_local *local) #endif static inline int drv_add_interface(struct ieee80211_local *local, - struct ieee80211_vif *vif) + struct ieee80211_sub_if_data *sdata) { int ret; might_sleep(); - trace_drv_add_interface(local, vif_to_sdata(vif)); - ret = local->ops->add_interface(&local->hw, vif); + if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN || + sdata->vif.type == NL80211_IFTYPE_MONITOR)) + return -EINVAL; + + trace_drv_add_interface(local, sdata); + ret = local->ops->add_interface(&local->hw, &sdata->vif); trace_drv_return_int(local, ret); + + if (ret == 0) + sdata->flags |= IEEE80211_SDATA_IN_DRIVER; + return ret; } @@ -89,6 +120,8 @@ static inline int drv_change_interface(struct ieee80211_local *local, might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_change_interface(local, sdata, type, p2p); ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p); trace_drv_return_int(local, ret); @@ -96,12 +129,15 @@ static inline int drv_change_interface(struct ieee80211_local *local, } static inline void drv_remove_interface(struct ieee80211_local *local, - struct ieee80211_vif *vif) + struct ieee80211_sub_if_data *sdata) { might_sleep(); - trace_drv_remove_interface(local, vif_to_sdata(vif)); - local->ops->remove_interface(&local->hw, vif); + check_sdata_in_driver(sdata); + + trace_drv_remove_interface(local, sdata); + local->ops->remove_interface(&local->hw, &sdata->vif); + sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER; trace_drv_return_void(local); } @@ -124,6 +160,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, { might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_bss_info_changed(local, sdata, info, changed); if (local->ops->bss_info_changed) local->ops->bss_info_changed(&local->hw, &sdata->vif, info, changed); @@ -180,6 +218,9 @@ static inline int drv_set_key(struct ieee80211_local *local, might_sleep(); + sdata = get_bss_sdata(sdata); + check_sdata_in_driver(sdata); + trace_drv_set_key(local, cmd, sdata, sta, key); ret = local->ops->set_key(&local->hw, cmd, &sdata->vif, sta, key); trace_drv_return_int(local, ret); @@ -197,6 +238,9 @@ static inline void drv_update_tkip_key(struct ieee80211_local *local, if (sta) ista = &sta->sta; + sdata = get_bss_sdata(sdata); + check_sdata_in_driver(sdata); + trace_drv_update_tkip_key(local, sdata, conf, ista, iv32); if (local->ops->update_tkip_key) local->ops->update_tkip_key(&local->hw, &sdata->vif, conf, @@ -212,12 +256,26 @@ static inline int drv_hw_scan(struct ieee80211_local *local, might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_hw_scan(local, sdata); ret = local->ops->hw_scan(&local->hw, &sdata->vif, req); trace_drv_return_int(local, ret); return ret; } +static inline void drv_cancel_hw_scan(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + might_sleep(); + + check_sdata_in_driver(sdata); + + 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, @@ -228,6 +286,8 @@ drv_sched_scan_start(struct ieee80211_local *local, might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_sched_scan_start(local, sdata); ret = local->ops->sched_scan_start(&local->hw, &sdata->vif, req, ies); @@ -240,6 +300,8 @@ static inline void drv_sched_scan_stop(struct ieee80211_local *local, { might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_sched_scan_stop(local, sdata); local->ops->sched_scan_stop(&local->hw, &sdata->vif); trace_drv_return_void(local); @@ -336,6 +398,9 @@ static inline void drv_sta_notify(struct ieee80211_local *local, enum sta_notify_cmd cmd, struct ieee80211_sta *sta) { + sdata = get_bss_sdata(sdata); + check_sdata_in_driver(sdata); + trace_drv_sta_notify(local, sdata, cmd, sta); if (local->ops->sta_notify) local->ops->sta_notify(&local->hw, &sdata->vif, cmd, sta); @@ -350,6 +415,9 @@ static inline int drv_sta_add(struct ieee80211_local *local, might_sleep(); + sdata = get_bss_sdata(sdata); + check_sdata_in_driver(sdata); + trace_drv_sta_add(local, sdata, sta); if (local->ops->sta_add) ret = local->ops->sta_add(&local->hw, &sdata->vif, sta); @@ -365,6 +433,9 @@ static inline void drv_sta_remove(struct ieee80211_local *local, { might_sleep(); + sdata = get_bss_sdata(sdata); + check_sdata_in_driver(sdata); + trace_drv_sta_remove(local, sdata, sta); if (local->ops->sta_remove) local->ops->sta_remove(&local->hw, &sdata->vif, sta); @@ -372,50 +443,95 @@ 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 __must_check +int drv_sta_state(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + int ret = 0; + + might_sleep(); + + sdata = get_bss_sdata(sdata); + check_sdata_in_driver(sdata); + + trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state); + if (local->ops->sta_state) { + ret = local->ops->sta_state(&local->hw, &sdata->vif, &sta->sta, + old_state, new_state); + } else if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC) { + ret = drv_sta_add(local, sdata, &sta->sta); + if (ret == 0) + sta->uploaded = true; + } else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTH) { + drv_sta_remove(local, sdata, &sta->sta); + } + trace_drv_return_int(local, ret); + return ret; +} + +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); + check_sdata_in_driver(sdata); + + 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); + check_sdata_in_driver(sdata); + + 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); + check_sdata_in_driver(sdata); + + 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); + check_sdata_in_driver(sdata); + + 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); } @@ -442,6 +558,9 @@ static inline int drv_ampdu_action(struct ieee80211_local *local, might_sleep(); + sdata = get_bss_sdata(sdata); + check_sdata_in_driver(sdata); + trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn, buf_size); if (local->ops->ampdu_action) @@ -549,37 +668,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) { @@ -628,6 +716,8 @@ static inline int drv_set_bitrate_mask(struct ieee80211_local *local, might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_set_bitrate_mask(local, sdata, mask); if (local->ops->set_bitrate_mask) ret = local->ops->set_bitrate_mask(&local->hw, @@ -637,4 +727,54 @@ 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) +{ + check_sdata_in_driver(sdata); + + 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..21d6f52 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -5,17 +5,6 @@ #include #include "ieee80211_i.h" -#if !defined(CONFIG_MAC80211_DRIVER_API_TRACER) || defined(__CHECKER__) -#undef TRACE_EVENT -#define TRACE_EVENT(name, proto, ...) \ -static inline void trace_ ## name(proto) {} -#undef DECLARE_EVENT_CLASS -#define DECLARE_EVENT_CLASS(...) -#undef DEFINE_EVENT -#define DEFINE_EVENT(evt_class, name, proto, ...) \ -static inline void trace_ ## name(proto) {} -#endif - #undef TRACE_SYSTEM #define TRACE_SYSTEM mac80211 @@ -307,7 +296,7 @@ TRACE_EVENT(drv_bss_info_changed, __entry->dtimper = info->dtim_period; __entry->bcnint = info->beacon_int; __entry->assoc_cap = info->assoc_capability; - __entry->timestamp = info->timestamp; + __entry->timestamp = info->last_tsf; __entry->basic_rates = info->basic_rates; __entry->enable_beacon = info->enable_beacon; __entry->ht_operation_mode = info->ht_operation_mode; @@ -460,6 +449,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), @@ -597,6 +592,38 @@ TRACE_EVENT(drv_sta_notify, ) ); +TRACE_EVENT(drv_sta_state, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state), + + TP_ARGS(local, sdata, sta, old_state, new_state), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + STA_ENTRY + __field(u32, old_state) + __field(u32, new_state) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + STA_ASSIGN; + __entry->old_state = old_state; + __entry->new_state = new_state; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " state: %d->%d", + LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, + __entry->old_state, __entry->new_state + ) +); + TRACE_EVENT(drv_sta_add, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, @@ -648,64 +675,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 +1057,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 +1431,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..f25fff7 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -14,11 +14,89 @@ */ #include +#include #include #include "ieee80211_i.h" #include "rate.h" -void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband, +bool ieee80111_cfg_override_disables_ht40(struct ieee80211_sub_if_data *sdata) +{ + const __le16 flg = cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40); + if ((sdata->u.mgd.ht_capa_mask.cap_info & flg) && + !(sdata->u.mgd.ht_capa.cap_info & flg)) + return true; + return false; +} + +static void __check_htcap_disable(struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta_ht_cap *ht_cap, + u16 flag) +{ + __le16 le_flag = cpu_to_le16(flag); + if (sdata->u.mgd.ht_capa_mask.cap_info & le_flag) { + if (!(sdata->u.mgd.ht_capa.cap_info & le_flag)) + ht_cap->cap &= ~flag; + } +} + +void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta_ht_cap *ht_cap) +{ + u8 *scaps = (u8 *)(&sdata->u.mgd.ht_capa.mcs.rx_mask); + u8 *smask = (u8 *)(&sdata->u.mgd.ht_capa_mask.mcs.rx_mask); + int i; + + if (sdata->vif.type != NL80211_IFTYPE_STATION) { + /* AP interfaces call this code when adding new stations, + * so just silently ignore non station interfaces. + */ + return; + } + + /* NOTE: If you add more over-rides here, update register_hw + * ht_capa_mod_msk logic in main.c as well. + * And, if this method can ever change ht_cap.ht_supported, fix + * the check in ieee80211_add_ht_ie. + */ + + /* check for HT over-rides, MCS rates first. */ + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { + u8 m = smask[i]; + ht_cap->mcs.rx_mask[i] &= ~m; /* turn off all masked bits */ + /* Add back rates that are supported */ + ht_cap->mcs.rx_mask[i] |= (m & scaps[i]); + } + + /* Force removal of HT-40 capabilities? */ + __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SUP_WIDTH_20_40); + __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_40); + + /* Allow user to disable the max-AMSDU bit. */ + __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_MAX_AMSDU); + + /* Allow user to decrease AMPDU factor */ + if (sdata->u.mgd.ht_capa_mask.ampdu_params_info & + IEEE80211_HT_AMPDU_PARM_FACTOR) { + u8 n = sdata->u.mgd.ht_capa.ampdu_params_info + & IEEE80211_HT_AMPDU_PARM_FACTOR; + if (n < ht_cap->ampdu_factor) + ht_cap->ampdu_factor = n; + } + + /* Allow the user to increase AMPDU density. */ + if (sdata->u.mgd.ht_capa_mask.ampdu_params_info & + IEEE80211_HT_AMPDU_PARM_DENSITY) { + u8 n = (sdata->u.mgd.ht_capa.ampdu_params_info & + IEEE80211_HT_AMPDU_PARM_DENSITY) + >> IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT; + if (n > ht_cap->ampdu_density) + ht_cap->ampdu_density = n; + } +} + + +void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, + struct ieee80211_supported_band *sband, struct ieee80211_ht_cap *ht_cap_ie, struct ieee80211_sta_ht_cap *ht_cap) { @@ -102,6 +180,12 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband, /* handle MCS rate 32 too */ if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1) ht_cap->mcs.rx_mask[32/8] |= 1; + + /* + * If user has specified capability over-rides, take care + * of that here. + */ + ieee80211_apply_htcap_overrides(sdata, ht_cap); } void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx) @@ -130,7 +214,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 +224,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 +270,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); @@ -193,10 +279,13 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); if (sdata->vif.type == NL80211_IFTYPE_AP || - sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + sdata->vif.type == NL80211_IFTYPE_AP_VLAN || + sdata->vif.type == NL80211_IFTYPE_MESH_POINT) memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); else if (sdata->vif.type == NL80211_IFTYPE_STATION) memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); + else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) + memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); @@ -211,7 +300,7 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, mgmt->u.action.u.delba.params = cpu_to_le16(params); mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code); - ieee80211_tx_skb(sdata, skb); + ieee80211_tx_skb_tid(sdata, skb, tid); } void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index c6399f6..50191a3 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -20,7 +20,6 @@ #include #include #include -#include #include "ieee80211_i.h" #include "driver-ops.h" @@ -36,31 +35,6 @@ #define IEEE80211_IBSS_MAX_STA_ENTRIES 128 -static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - size_t len) -{ - u16 auth_alg, auth_transaction; - - lockdep_assert_held(&sdata->u.ibss.mtx); - - if (len < 24 + 6) - return; - - auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); - auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); - - /* - * IEEE 802.11 standard does not require authentication in IBSS - * networks and most implementations do not seem to use it. - * However, try to reply to authentication attempts if someone - * has actually implemented this. - */ - if (auth_alg == WLAN_AUTH_OPEN && auth_transaction == 1) - ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, NULL, 0, - sdata->u.ibss.bssid, NULL, 0, 0); -} - static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, const u8 *bssid, const int beacon_int, struct ieee80211_channel *chan, @@ -77,26 +51,28 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, struct cfg80211_bss *bss; u32 bss_change; u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; + enum nl80211_channel_type channel_type; 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; skb_reset_tail_pointer(skb); skb_reserve(skb, sdata->local->hw.extra_tx_headroom); - if (memcmp(ifibss->bssid, bssid, ETH_ALEN)) + if (compare_ether_addr(ifibss->bssid, bssid)) sta_info_flush(sdata->local, sdata); /* if merging, indicate to driver that we leave the old IBSS */ if (sdata->vif.bss_conf.ibss_joined) { sdata->vif.bss_conf.ibss_joined = false; + netif_carrier_off(sdata->dev); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IBSS); } @@ -105,7 +81,16 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0; local->oper_channel = chan; - WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT)); + channel_type = ifibss->channel_type; + if (channel_type > NL80211_CHAN_HT20 && + !cfg80211_can_beacon_sec_chan(local->hw.wiphy, chan, channel_type)) + channel_type = NL80211_CHAN_HT20; + if (!ieee80211_set_channel_type(local, sdata, channel_type)) { + /* can only fail due to HT40+/- mismatch */ + channel_type = NL80211_CHAN_HT20; + WARN_ON(!ieee80211_set_channel_type(local, sdata, + NL80211_CHAN_HT20)); + } ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); sband = local->hw.wiphy->bands[chan->band]; @@ -171,6 +156,19 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, memcpy(skb_put(skb, ifibss->ie_len), ifibss->ie, ifibss->ie_len); + /* add HT capability and information IEs */ + if (channel_type && sband->ht_cap.ht_supported) { + pos = skb_put(skb, 4 + + sizeof(struct ieee80211_ht_cap) + + sizeof(struct ieee80211_ht_info)); + pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, + sband->ht_cap.cap); + pos = ieee80211_ie_build_ht_info(pos, + &sband->ht_cap, + chan, + channel_type); + } + if (local->hw.queues >= 4) { pos = skb_put(skb, 9); *pos++ = WLAN_EID_VENDOR_SPECIFIC; @@ -194,6 +192,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, bss_change |= BSS_CHANGED_BEACON; bss_change |= BSS_CHANGED_BEACON_ENABLED; bss_change |= BSS_CHANGED_BASIC_RATES; + bss_change |= BSS_CHANGED_HT; bss_change |= BSS_CHANGED_IBSS; sdata->vif.bss_conf.ibss_joined = true; ieee80211_bss_info_change_notify(sdata, bss_change); @@ -207,6 +206,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, bss = cfg80211_inform_bss_frame(local->hw.wiphy, local->hw.conf.channel, mgmt, skb->len, 0, GFP_KERNEL); cfg80211_put_bss(bss); + netif_carrier_on(sdata->dev); cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL); } @@ -250,6 +250,128 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, cbss->tsf); } +static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta, + bool auth) + __acquires(RCU) +{ + struct ieee80211_sub_if_data *sdata = sta->sdata; + u8 addr[ETH_ALEN]; + + memcpy(addr, sta->sta.addr, ETH_ALEN); + +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + wiphy_debug(sdata->local->hw.wiphy, + "Adding new IBSS station %pM (dev=%s)\n", + addr, sdata->name); +#endif + + sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); + sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); + /* authorize the station only if the network is not RSN protected. If + * not wait for the userspace to authorize it */ + if (!sta->sdata->u.ibss.control_port) + sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); + + rate_control_rate_init(sta); + + /* If it fails, maybe we raced another insertion? */ + if (sta_info_insert_rcu(sta)) + return sta_info_get(sdata, addr); + if (auth) { +#ifdef CONFIG_MAC80211_IBSS_DEBUG + printk(KERN_DEBUG "TX Auth SA=%pM DA=%pM BSSID=%pM" + "(auth_transaction=1)\n", sdata->vif.addr, + sdata->u.ibss.bssid, addr); +#endif + ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, NULL, 0, + addr, sdata->u.ibss.bssid, NULL, 0, 0); + } + return sta; +} + +static struct sta_info * +ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, + const u8 *bssid, const u8 *addr, + u32 supp_rates, bool auth) + __acquires(RCU) +{ + struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + struct ieee80211_local *local = sdata->local; + struct sta_info *sta; + int band = local->hw.conf.channel->band; + + /* + * XXX: Consider removing the least recently used entry and + * allow new one to be added. + */ + if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) { + if (net_ratelimit()) + printk(KERN_DEBUG "%s: No room for a new IBSS STA entry %pM\n", + sdata->name, addr); + rcu_read_lock(); + return NULL; + } + + if (ifibss->state == IEEE80211_IBSS_MLME_SEARCH) { + rcu_read_lock(); + return NULL; + } + + if (compare_ether_addr(bssid, sdata->u.ibss.bssid)) { + rcu_read_lock(); + return NULL; + } + + sta = sta_info_alloc(sdata, addr, GFP_KERNEL); + if (!sta) { + rcu_read_lock(); + return NULL; + } + + sta->last_rx = jiffies; + + /* make sure mandatory rates are always added */ + sta->sta.supp_rates[band] = supp_rates | + ieee80211_mandatory_rates(local, band); + + return ieee80211_ibss_finish_sta(sta, auth); +} + +static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, + size_t len) +{ + u16 auth_alg, auth_transaction; + + lockdep_assert_held(&sdata->u.ibss.mtx); + + if (len < 24 + 6) + return; + + auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); + auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); + + if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) + return; +#ifdef CONFIG_MAC80211_IBSS_DEBUG + printk(KERN_DEBUG "%s: RX Auth SA=%pM DA=%pM BSSID=%pM." + "(auth_transaction=%d)\n", + sdata->name, mgmt->sa, mgmt->da, mgmt->bssid, auth_transaction); +#endif + sta_info_destroy_addr(sdata, mgmt->sa); + ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false); + rcu_read_unlock(); + + /* + * IEEE 802.11 standard does not require authentication in IBSS + * networks and most implementations do not seem to use it. + * However, try to reply to authentication attempts if someone + * has actually implemented this. + */ + ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, NULL, 0, + mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0); +} + static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, @@ -266,6 +388,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, u64 beacon_timestamp, rx_timestamp; u32 supp_rates = 0; enum ieee80211_band band = rx_status->band; + struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; + bool rates_updated = false; if (elems->ds_params && elems->ds_params_len == 1) freq = ieee80211_channel_to_frequency(elems->ds_params[0], @@ -279,7 +403,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, return; if (sdata->vif.type == NL80211_IFTYPE_ADHOC && - memcmp(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN) == 0) { + compare_ether_addr(mgmt->bssid, sdata->u.ibss.bssid) == 0) { rcu_read_lock(); sta = sta_info_get(sdata, mgmt->sa); @@ -305,16 +429,50 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, prev_rates, sta->sta.supp_rates[band]); #endif - rate_control_rate_init(sta); + rates_updated = true; } - } else + } else { + rcu_read_unlock(); sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, - mgmt->sa, supp_rates, - GFP_ATOMIC); + mgmt->sa, supp_rates, true); + } } if (sta && elems->wmm_info) - set_sta_flags(sta, WLAN_STA_WME); + set_sta_flag(sta, WLAN_STA_WME); + + if (sta && elems->ht_info_elem && elems->ht_cap_elem && + sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) { + /* we both use HT */ + struct ieee80211_sta_ht_cap sta_ht_cap_new; + enum nl80211_channel_type channel_type = + ieee80211_ht_info_to_channel_type( + elems->ht_info_elem); + + ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, + elems->ht_cap_elem, + &sta_ht_cap_new); + + /* + * fall back to HT20 if we don't use or use + * the other extension channel + */ + if (!(channel_type == NL80211_CHAN_HT40MINUS || + channel_type == NL80211_CHAN_HT40PLUS) || + channel_type != sdata->u.ibss.channel_type) + sta_ht_cap_new.cap &= + ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + + if (memcmp(&sta->sta.ht_cap, &sta_ht_cap_new, + sizeof(sta_ht_cap_new))) { + memcpy(&sta->sta.ht_cap, &sta_ht_cap_new, + sizeof(sta_ht_cap_new)); + rates_updated = true; + } + } + + if (sta && rates_updated) + rate_control_rate_init(sta); rcu_read_unlock(); } @@ -350,7 +508,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, goto put_bss; /* same BSSID */ - if (memcmp(cbss->bssid, sdata->u.ibss.bssid, ETH_ALEN) == 0) + if (compare_ether_addr(cbss->bssid, sdata->u.ibss.bssid) == 0) goto put_bss; if (rx_status->flag & RX_FLAG_MACTIME_MPDU) { @@ -382,7 +540,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 @@ -404,21 +562,17 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, ieee80211_sta_join_ibss(sdata, bss); supp_rates = ieee80211_sta_get_rates(local, elems, band); ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, - supp_rates, GFP_KERNEL); + supp_rates, true); + rcu_read_unlock(); } put_bss: ieee80211_rx_bss_put(local, bss); } -/* - * Add a new IBSS station, will also be called by the RX code when, - * in IBSS mode, receiving a frame from a yet-unknown station, hence - * 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, - gfp_t gfp) +void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, + const u8 *bssid, const u8 *addr, + u32 supp_rates) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_local *local = sdata->local; @@ -433,37 +587,29 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, if (net_ratelimit()) printk(KERN_DEBUG "%s: No room for a new IBSS STA entry %pM\n", sdata->name, addr); - return NULL; + return; } if (ifibss->state == IEEE80211_IBSS_MLME_SEARCH) - return NULL; + return; if (compare_ether_addr(bssid, sdata->u.ibss.bssid)) - return NULL; - -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - wiphy_debug(local->hw.wiphy, "Adding new IBSS station %pM (dev=%s)\n", - addr, sdata->name); -#endif + return; - sta = sta_info_alloc(sdata, addr, gfp); + sta = sta_info_alloc(sdata, addr, GFP_ATOMIC); if (!sta) - return NULL; + return; sta->last_rx = jiffies; - set_sta_flags(sta, WLAN_STA_AUTHORIZED); /* make sure mandatory rates are always added */ sta->sta.supp_rates[band] = supp_rates | ieee80211_mandatory_rates(local, band); - rate_control_rate_init(sta); - - /* If it fails, maybe we raced another insertion? */ - if (sta_info_insert(sta)) - return sta_info_get(sdata, addr); - return sta; + spin_lock(&ifibss->incomplete_lock); + list_add(&sta->list, &ifibss->incomplete_stations); + spin_unlock(&ifibss->incomplete_lock); + ieee80211_queue_work(&local->hw, &sdata->work); } static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata) @@ -518,9 +664,8 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata) printk(KERN_DEBUG "%s: No active IBSS STAs - trying to scan for other " "IBSS networks with same SSID (merge)\n", sdata->name); - ieee80211_request_internal_scan(sdata, - ifibss->ssid, ifibss->ssid_len, - ifibss->fixed_channel ? ifibss->channel : NULL); + ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len, + NULL); } static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) @@ -627,9 +772,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) printk(KERN_DEBUG "%s: Trigger new scan to find an IBSS to " "join\n", sdata->name); - ieee80211_request_internal_scan(sdata, - ifibss->ssid, ifibss->ssid_len, - ifibss->fixed_channel ? ifibss->channel : NULL); + ieee80211_request_ibss_scan(sdata, ifibss->ssid, + ifibss->ssid_len, chan); } else { int interval = IEEE80211_SCAN_INTERVAL; @@ -686,8 +830,8 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, if (!tx_last_beacon && is_multicast_ether_addr(mgmt->da)) return; - if (memcmp(mgmt->bssid, ifibss->bssid, ETH_ALEN) != 0 && - memcmp(mgmt->bssid, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) + if (compare_ether_addr(mgmt->bssid, ifibss->bssid) != 0 && + !is_broadcast_ether_addr(mgmt->bssid)) return; end = ((u8 *) mgmt) + len; @@ -731,9 +875,6 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, size_t baselen; struct ieee802_11_elems elems; - if (memcmp(mgmt->da, sdata->vif.addr, ETH_ALEN)) - return; /* ignore ProbeResp to foreign address */ - baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt; if (baselen > len) return; @@ -802,6 +943,7 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + struct sta_info *sta; mutex_lock(&ifibss->mtx); @@ -813,6 +955,19 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata) if (!ifibss->ssid_len) goto out; + spin_lock_bh(&ifibss->incomplete_lock); + while (!list_empty(&ifibss->incomplete_stations)) { + sta = list_first_entry(&ifibss->incomplete_stations, + struct sta_info, list); + list_del(&sta->list); + spin_unlock_bh(&ifibss->incomplete_lock); + + ieee80211_ibss_finish_sta(sta, true); + rcu_read_unlock(); + spin_lock_bh(&ifibss->incomplete_lock); + } + spin_unlock_bh(&ifibss->incomplete_lock); + switch (ifibss->state) { case IEEE80211_IBSS_MLME_SEARCH: ieee80211_sta_find_ibss(sdata); @@ -871,6 +1026,8 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata) setup_timer(&ifibss->timer, ieee80211_ibss_timer, (unsigned long) sdata); mutex_init(&ifibss->mtx); + INIT_LIST_HEAD(&ifibss->incomplete_stations); + spin_lock_init(&ifibss->incomplete_lock); } /* scan finished notification */ @@ -894,12 +1051,18 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, struct cfg80211_ibss_params *params) { struct sk_buff *skb; + u32 changed = 0; skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom + - 36 /* bitrates */ + - 34 /* SSID */ + - 3 /* DS params */ + - 4 /* IBSS params */ + + sizeof(struct ieee80211_hdr_3addr) + + 12 /* struct ieee80211_mgmt.u.beacon */ + + 2 + IEEE80211_MAX_SSID_LEN /* max SSID */ + + 2 + 8 /* max Supported Rates */ + + 3 /* max DS params */ + + 4 /* IBSS params */ + + 2 + (IEEE80211_MAX_SUPP_RATES - 8) + + 2 + sizeof(struct ieee80211_ht_cap) + + 2 + sizeof(struct ieee80211_ht_info) + params->ie_len); if (!skb) return -ENOMEM; @@ -913,6 +1076,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, sdata->u.ibss.fixed_bssid = false; sdata->u.ibss.privacy = params->privacy; + sdata->u.ibss.control_port = params->control_port; sdata->u.ibss.basic_rates = params->basic_rates; memcpy(sdata->vif.bss_conf.mcast_rate, params->mcast_rate, sizeof(params->mcast_rate)); @@ -920,13 +1084,18 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, sdata->vif.bss_conf.beacon_int = params->beacon_interval; sdata->u.ibss.channel = params->channel; + sdata->u.ibss.channel_type = params->channel_type; sdata->u.ibss.fixed_channel = params->channel_fixed; /* fix ourselves to that channel now already */ if (params->channel_fixed) { sdata->local->oper_channel = params->channel; - WARN_ON(!ieee80211_set_channel_type(sdata->local, sdata, - NL80211_CHAN_NO_HT)); + if (!ieee80211_set_channel_type(sdata->local, sdata, + params->channel_type)) { + mutex_unlock(&sdata->u.ibss.mtx); + kfree_skb(skb); + return -EINVAL; + } } if (params->ie) { @@ -949,6 +1118,23 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, ieee80211_recalc_idle(sdata->local); mutex_unlock(&sdata->local->mtx); + /* + * 802.11n-2009 9.13.3.1: In an IBSS, the HT Protection field is + * reserved, but an HT STA shall protect HT transmissions as though + * the HT Protection field were set to non-HT mixed mode. + * + * In an IBSS, the RIFS Mode field of the HT Operation element is + * also reserved, but an HT STA shall operate as though this field + * were set to 1. + */ + + sdata->vif.bss_conf.ht_operation_mode |= + IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED + | IEEE80211_HT_PARAM_RIFS_MODE; + + changed |= BSS_CHANGED_HT; + ieee80211_bss_info_change_notify(sdata, changed); + ieee80211_queue_work(&sdata->local->hw, &sdata->work); return 0; @@ -962,6 +1148,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) struct cfg80211_bss *cbss; u16 capability; int active_ibss; + struct sta_info *sta; mutex_lock(&sdata->u.ibss.mtx); @@ -991,11 +1178,25 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) sta_info_flush(sdata->local, sdata); + spin_lock_bh(&ifibss->incomplete_lock); + while (!list_empty(&ifibss->incomplete_stations)) { + sta = list_first_entry(&ifibss->incomplete_stations, + struct sta_info, list); + list_del(&sta->list); + spin_unlock_bh(&ifibss->incomplete_lock); + + sta_info_free(local, sta); + spin_lock_bh(&ifibss->incomplete_lock); + } + spin_unlock_bh(&ifibss->incomplete_lock); + + netif_carrier_off(sdata->dev); + /* remove beacon */ 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..ca07c5e 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -53,11 +54,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 @@ -104,6 +119,44 @@ struct ieee80211_bss { */ bool has_erp_value; u8 erp_value; + + /* Keep track of the corruption of the last beacon/probe response. */ + u8 corrupt_data; + + /* Keep track of what bits of information we have valid info for. */ + u8 valid_data; +}; + +/** + * enum ieee80211_corrupt_data_flags - BSS data corruption flags + * @IEEE80211_BSS_CORRUPT_BEACON: last beacon frame received was corrupted + * @IEEE80211_BSS_CORRUPT_PROBE_RESP: last probe response received was corrupted + * + * These are bss flags that are attached to a bss in the + * @corrupt_data field of &struct ieee80211_bss. + */ +enum ieee80211_bss_corrupt_data_flags { + IEEE80211_BSS_CORRUPT_BEACON = BIT(0), + IEEE80211_BSS_CORRUPT_PROBE_RESP = BIT(1) +}; + +/** + * enum ieee80211_valid_data_flags - BSS valid data flags + * @IEEE80211_BSS_VALID_DTIM: DTIM data was gathered from non-corrupt IE + * @IEEE80211_BSS_VALID_WMM: WMM/UAPSD data was gathered from non-corrupt IE + * @IEEE80211_BSS_VALID_RATES: Supported rates were gathered from non-corrupt IE + * @IEEE80211_BSS_VALID_ERP: ERP flag was gathered from non-corrupt IE + * + * These are bss flags that are attached to a bss in the + * @valid_data field of &struct ieee80211_bss. They show which parts + * of the data structure were recieved as a result of an un-corrupted + * beacon/probe response. + */ +enum ieee80211_bss_valid_data_flags { + IEEE80211_BSS_VALID_DTIM = BIT(0), + IEEE80211_BSS_VALID_WMM = BIT(1), + IEEE80211_BSS_VALID_RATES = BIT(2), + IEEE80211_BSS_VALID_ERP = BIT(3) }; static inline u8 *bss_mesh_cfg(struct ieee80211_bss *bss) @@ -136,12 +189,12 @@ 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) struct ieee80211_tx_data { struct sk_buff *skb; + struct sk_buff_head skbs; struct ieee80211_local *local; struct ieee80211_sub_if_data *sdata; struct sta_info *sta; @@ -149,7 +202,6 @@ struct ieee80211_tx_data { struct ieee80211_channel *channel; - u16 ethertype; unsigned int flags; }; @@ -186,12 +238,15 @@ enum ieee80211_packet_rx_flags { * enum ieee80211_rx_flags - RX data flags * * @IEEE80211_RX_CMNTR: received on cooked monitor already + * @IEEE80211_RX_BEACON_REPORTED: This frame was already reported + * to cfg80211_report_obss_beacon(). * * These flags are used across handling multiple interfaces * for a single frame. */ enum ieee80211_rx_flags { IEEE80211_RX_CMNTR = BIT(0), + IEEE80211_RX_BEACON_REPORTED = BIT(1), }; struct ieee80211_rx_data { @@ -202,7 +257,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; }; @@ -210,11 +280,12 @@ struct ieee80211_rx_data { struct beacon_data { u8 *head, *tail; int head_len, tail_len; - int dtim_period; + struct rcu_head rcu_head; }; struct ieee80211_if_ap { struct beacon_data __rcu *beacon; + struct sk_buff __rcu *probe_resp; struct list_head vlans; @@ -224,6 +295,7 @@ struct ieee80211_if_ap { u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)]; struct sk_buff_head ps_bc_buf; atomic_t num_sta_ps; /* number of stations in PS mode */ + atomic_t num_sta_authorized; /* number of authorized stations */ int dtim_count; bool dtim_bc_mc; }; @@ -246,6 +318,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; }; @@ -259,10 +332,6 @@ struct mesh_preq_queue { enum ieee80211_work_type { IEEE80211_WORK_ABORT, - IEEE80211_WORK_DIRECT_PROBE, - IEEE80211_WORK_AUTH, - IEEE80211_WORK_ASSOC_BEACON_WAIT, - IEEE80211_WORK_ASSOC, IEEE80211_WORK_REMAIN_ON_CHANNEL, IEEE80211_WORK_OFFCHANNEL_TX, }; @@ -295,34 +364,10 @@ struct ieee80211_work { unsigned long timeout; enum ieee80211_work_type type; - u8 filter_ta[ETH_ALEN]; - bool started; union { struct { - int tries; - u16 algorithm, transaction; - u8 ssid[IEEE80211_MAX_SSID_LEN]; - u8 ssid_len; - u8 key[WLAN_KEY_LEN_WEP104]; - u8 key_len, key_idx; - bool privacy; - } probe_auth; - struct { - struct cfg80211_bss *bss; - const u8 *supp_rates; - const u8 *ht_information_ie; - enum ieee80211_smps_mode smps; - int tries; - u16 capability; - u8 prev_bssid[ETH_ALEN]; - u8 ssid[IEEE80211_MAX_SSID_LEN]; - u8 ssid_len; - u8 supp_rates_len; - bool wmm_used, use_11n, uapsd_used; - } assoc; - struct { u32 duration; } remain; struct { @@ -332,9 +377,8 @@ struct ieee80211_work { } offchan_tx; }; - int ie_len; - /* must be last */ - u8 ie[0]; + size_t data_len; + u8 data[]; }; /* flags used in struct ieee80211_if_managed.flags */ @@ -350,6 +394,42 @@ enum ieee80211_sta_flags { IEEE80211_STA_RESET_SIGNAL_AVE = BIT(9), }; +struct ieee80211_mgd_auth_data { + struct cfg80211_bss *bss; + unsigned long timeout; + int tries; + u16 algorithm, expected_transaction; + + u8 key[WLAN_KEY_LEN_WEP104]; + u8 key_len, key_idx; + bool done; + + size_t ie_len; + u8 ie[]; +}; + +struct ieee80211_mgd_assoc_data { + struct cfg80211_bss *bss; + const u8 *supp_rates; + const u8 *ht_information_ie; + + unsigned long timeout; + int tries; + + u16 capability; + u8 prev_bssid[ETH_ALEN]; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 ssid_len; + u8 supp_rates_len; + bool wmm, uapsd; + bool have_beacon; + bool sent_assoc; + bool synced; + + size_t ie_len; + u8 ie[]; +}; + struct ieee80211_if_managed { struct timer_list timer; struct timer_list conn_mon_timer; @@ -366,6 +446,8 @@ struct ieee80211_if_managed { struct mutex mtx; struct cfg80211_bss *associated; + struct ieee80211_mgd_auth_data *auth_data; + struct ieee80211_mgd_assoc_data *assoc_data; u8 bssid[ETH_ALEN]; @@ -391,6 +473,20 @@ struct ieee80211_if_managed { IEEE80211_MFP_REQUIRED } mfp; /* management frame protection */ + /* + * Bitmask of enabled u-apsd queues, + * IEEE80211_WMM_IE_STA_QOSINFO_AC_BE & co. Needs a new association + * to take effect. + */ + unsigned int uapsd_queues; + + /* + * Maximum number of buffered frames AP can deliver during a + * service period, IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL or similar. + * Needs a new association to take effect. + */ + unsigned int uapsd_max_sp_len; + int wmm_last_param_set; u8 use_4addr; @@ -419,6 +515,17 @@ 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_ht_cap ht_capa; /* configured ht-cap over-rides */ + struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */ }; struct ieee80211_if_ibss { @@ -436,17 +543,23 @@ struct ieee80211_if_ibss { bool fixed_channel; bool privacy; - u8 bssid[ETH_ALEN]; + bool control_port; + + u8 bssid[ETH_ALEN] __aligned(2); u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 ssid_len, ie_len; u8 *ie; struct ieee80211_channel *channel; + enum nl80211_channel_type channel_type; unsigned long ibss_join_req; /* probe response/beacon for IBSS */ struct sk_buff __rcu *presp; struct sk_buff *skb; + spinlock_t incomplete_lock; + struct list_head incomplete_stations; + enum { IEEE80211_IBSS_MLME_SEARCH, IEEE80211_IBSS_MLME_JOINED, @@ -481,7 +594,9 @@ struct ieee80211_if_mesh { atomic_t mpaths; /* Timestamp of last SN update */ unsigned long last_sn_update; - /* Timestamp of last SN sent */ + /* Time when it's ok to send next PERR */ + unsigned long next_perr; + /* Timestamp of last PREQ sent */ unsigned long last_preq; struct mesh_rmc *rmc; spinlock_t mesh_preq_queue_lock; @@ -491,6 +606,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 +633,16 @@ 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. + * @IEEE80211_SDATA_IN_DRIVER: indicates interface was added to driver */ 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), + IEEE80211_SDATA_IN_DRIVER = BIT(5), }; /** @@ -546,6 +666,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; @@ -570,6 +693,9 @@ struct ieee80211_sub_if_data { struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX]; unsigned int fragment_next; + /* TID bitmap for NoAck policy */ + u16 noack_map; + struct ieee80211_key __rcu *keys[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; struct ieee80211_key __rcu *default_unicast_key; struct ieee80211_key __rcu *default_multicast_key; @@ -579,6 +705,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; @@ -593,6 +721,7 @@ struct ieee80211_sub_if_data { /* bitmap of allowed (non-MCS) rate indexes for rate control */ u32 rc_rateidx_mask[IEEE80211_NUM_BANDS]; + u8 rc_rateidx_mcs_mask[IEEE80211_NUM_BANDS][IEEE80211_HT_MCS_MASK_LEN]; union { struct ieee80211_if_ap ap; @@ -632,6 +761,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 +775,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 @@ -668,12 +803,15 @@ struct tpt_led_trigger { * 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_COMPLETED, SCAN_ABORTED, + SCAN_HW_CANCELLED, }; /** @@ -684,17 +822,16 @@ enum { * operating channel * @SCAN_SET_CHANNEL: Set the next channel to be scanned * @SCAN_SEND_PROBE: Send probe requests and wait for probe responses - * @SCAN_LEAVE_OPER_CHANNEL: Leave the operating channel, notify the AP - * about us leaving the channel and stop all associated STA interfaces - * @SCAN_ENTER_OPER_CHANNEL: Enter the operating channel again, notify the - * AP about us being back and restart all associated STA interfaces + * @SCAN_SUSPEND: Suspend the scan and go back to operating channel to + * send out data + * @SCAN_RESUME: Resume the scan and scan the next channel */ enum mac80211_scan_state { SCAN_DECISION, SCAN_SET_CHANNEL, SCAN_SEND_PROBE, - SCAN_LEAVE_OPER_CHANNEL, - SCAN_ENTER_OPER_CHANNEL, + SCAN_SUSPEND, + SCAN_RESUME, }; struct ieee80211_local { @@ -711,7 +848,6 @@ struct ieee80211_local { struct list_head work_list; struct timer_list work_timer; struct work_struct work_work; - struct sk_buff_head work_skb_queue; /* * private workqueue to mac80211. mac80211 makes this accessible @@ -797,18 +933,15 @@ struct ieee80211_local { /* Station data */ /* - * The mutex only protects the list and counter, - * reads are done in RCU. - * Additionally, the lock protects the hash table, - * the pending list and each BSS's TIM bitmap. + * The mutex only protects the list, hash table and + * counter, reads are done with RCU. */ struct mutex sta_mtx; - spinlock_t sta_lock; + spinlock_t tim_lock; unsigned long num_sta; - struct list_head sta_list, sta_pending_list; + struct list_head sta_list; struct sta_info __rcu *sta_hash[STA_HASH_SIZE]; struct timer_list sta_cleanup; - struct work_struct sta_finish_work; int sta_generation; struct sk_buff_head pending[IEEE80211_MAX_QUEUES]; @@ -913,23 +1046,8 @@ struct ieee80211_local { int total_ps_buffered; /* total number of all buffered unicast and * multicast packets for power saving stations */ - int wifi_wme_noack_test; unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */ - /* - * Bitmask of enabled u-apsd queues, - * IEEE80211_WMM_IE_STA_QOSINFO_AC_BE & co. Needs a new association - * to take effect. - */ - unsigned int uapsd_queues; - - /* - * Maximum number of buffered frames AP can deliver during a - * service period, IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL or similar. - * Needs a new association to take effect. - */ - unsigned int uapsd_max_sp_len; - bool pspolling; bool offchannel_ps_enabled; /* @@ -973,7 +1091,9 @@ struct ieee80211_local { unsigned int hw_roc_duration; u32 hw_roc_cookie; bool hw_roc_for_tx; - unsigned long hw_offchan_tx_cookie; + + struct idr ack_status_frames; + spinlock_t ack_status_lock; /* dummy netdev for use w/ NAPI */ struct net_device napi_dev; @@ -1017,7 +1137,7 @@ struct ieee802_11_elems { struct ieee80211_ht_info *ht_info_elem; struct ieee80211_meshconf_ie *mesh_config; u8 *mesh_id; - u8 *peer_link; + u8 *peering; u8 *preq; u8 *prep; u8 *perr; @@ -1025,7 +1145,7 @@ struct ieee802_11_elems { u8 *ch_switch_elem; u8 *country_elem; u8 *pwr_constr_elem; - u8 *quiet_elem; /* first quite element */ + u8 *quiet_elem; /* first quite element */ u8 *timeout_int; /* length of them, respectively */ @@ -1044,7 +1164,7 @@ struct ieee802_11_elems { u8 wmm_info_len; u8 wmm_param_len; u8 mesh_id_len; - u8 peer_link_len; + u8 peering_len; u8 preq_len; u8 prep_len; u8 perr_len; @@ -1054,6 +1174,9 @@ struct ieee802_11_elems { u8 quiet_elem_len; u8 num_of_quiet_elem; /* can be more the one */ u8 timeout_int_len; + + /* whether a parse error occurred while retrieving these elements */ + bool parse_error; }; static inline struct ieee80211_local *hw_to_local( @@ -1062,12 +1185,6 @@ static inline struct ieee80211_local *hw_to_local( return container_of(hw, struct ieee80211_local, hw); } -static inline struct ieee80211_hw *local_to_hw( - struct ieee80211_local *local) -{ - return &local->hw; -} - static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr) { @@ -1090,11 +1207,9 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, struct cfg80211_assoc_request *req); int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, - struct cfg80211_deauth_request *req, - void *cookie); + struct cfg80211_deauth_request *req); int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, - struct cfg80211_disassoc_request *req, - void *cookie); + struct cfg80211_disassoc_request *req); void ieee80211_send_pspoll(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata); void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency); @@ -1112,13 +1227,13 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata); void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata); +void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata); /* IBSS code */ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local); void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata); -struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, - u8 *bssid, u8 *addr, u32 supp_rates, - gfp_t gfp); +void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, + const u8 *bssid, const u8 *addr, u32 supp_rates); int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, struct cfg80211_ibss_params *params); int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata); @@ -1135,9 +1250,9 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, /* scan/BSS handling */ void ieee80211_scan_work(struct work_struct *work); -int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, - const u8 *ssid, u8 ssid_len, - struct ieee80211_channel *chan); +int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, + const u8 *ssid, u8 ssid_len, + struct ieee80211_channel *chan); int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, struct cfg80211_scan_request *req); void ieee80211_scan_cancel(struct ieee80211_local *local); @@ -1166,12 +1281,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_return(struct ieee80211_local *local, - bool enable_beaconing); +void ieee80211_offchannel_return(struct ieee80211_local *local); void ieee80211_hw_roc_setup(struct ieee80211_local *local); /* interface handling */ @@ -1201,24 +1312,17 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev); 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; - +void ieee80211_purge_tx_queue(struct ieee80211_hw *hw, + struct sk_buff_head *skbs); /* HT */ -void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband, +bool ieee80111_cfg_override_disables_ht40(struct ieee80211_sub_if_data *sdata); +void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta_ht_cap *ht_cap); +void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, + 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); @@ -1301,8 +1405,19 @@ int ieee80211_frame_duration(struct ieee80211_local *local, size_t len, void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx, struct ieee80211_hdr *hdr, const u8 *tsc, gfp_t gfp); -void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata); -void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); +void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, + bool bss_notify); +void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); + +void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, int tid); +static void inline ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + /* Send all internal mgmt frames on VO. Accordingly set TID to 7. */ + ieee80211_tx_skb_tid(sdata, skb, 7); +} + void ieee802_11_parse_elems(u8 *start, size_t len, struct ieee802_11_elems *elems); u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, @@ -1333,27 +1448,29 @@ 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, u8 *extra, size_t extra_len, const u8 *bssid, - const u8 *key, u8 key_len, u8 key_idx); + const u8 *da, const u8 *key, u8 key_len, u8 key_idx); int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, const u8 *ie, size_t ie_len, 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, @@ -1368,14 +1485,18 @@ void ieee80211_recalc_smps(struct ieee80211_local *local); size_t ieee80211_ie_split(const u8 *ies, size_t ielen, const u8 *ids, int n_ids, size_t offset); size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset); +u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, + u16 cap); +u8 *ieee80211_ie_build_ht_info(u8 *pos, + struct ieee80211_sta_ht_cap *ht_cap, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type); /* internal work items */ void ieee80211_work_init(struct ieee80211_local *local); void ieee80211_add_work(struct ieee80211_work *wk); void free_work(struct ieee80211_work *wk); void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata); -ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb); int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, @@ -1396,6 +1517,11 @@ ieee80211_get_channel_mode(struct ieee80211_local *local, bool ieee80211_set_channel_type(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, enum nl80211_channel_type chantype); +enum nl80211_channel_type +ieee80211_ht_info_to_channel_type(struct ieee80211_ht_info *ht_info); +enum nl80211_channel_type ieee80211_get_tx_channel_type( + struct ieee80211_local *local, + enum nl80211_channel_type channel_type); #ifdef CONFIG_MAC80211_NOINLINE #define debug_noinline noinline diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index bd1ef84..9f32756 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -188,11 +188,22 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) if (!is_valid_ether_addr(sdata->u.wds.remote_addr)) return -ENOLINK; break; - case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_AP_VLAN: { + struct ieee80211_sub_if_data *master; + if (!sdata->bss) return -ENOLINK; + list_add(&sdata->u.vlan.list, &sdata->bss->vlans); + + master = container_of(sdata->bss, + struct ieee80211_sub_if_data, u.ap); + sdata->control_port_protocol = + master->control_port_protocol; + sdata->control_port_no_encrypt = + master->control_port_no_encrypt; break; + } case NL80211_IFTYPE_AP: sdata->bss = &sdata->u.ap; break; @@ -265,7 +276,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) break; default: if (coming_up) { - res = drv_add_interface(local, &sdata->vif); + res = drv_add_interface(local, sdata); if (res) goto err_stop; } @@ -282,10 +293,18 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) changed |= ieee80211_reset_erp_info(sdata); ieee80211_bss_info_change_notify(sdata, changed); - if (sdata->vif.type == NL80211_IFTYPE_STATION) + if (sdata->vif.type == NL80211_IFTYPE_STATION || + sdata->vif.type == NL80211_IFTYPE_ADHOC) netif_carrier_off(dev); else netif_carrier_on(dev); + + /* + * set default queue parameters so drivers don't + * need to initialise the hardware if the hardware + * doesn't start up with sane defaults + */ + ieee80211_set_wmm_default(sdata, true); } set_bit(SDATA_STATE_RUNNING, &sdata->state); @@ -299,8 +318,9 @@ 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; + sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); + sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); + sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); res = sta_info_insert(sta); if (res) { @@ -329,15 +349,8 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) if (coming_up) local->open_count++; - if (hw_reconf_flags) { + if (hw_reconf_flags) ieee80211_hw_config(local, hw_reconf_flags); - /* - * set default queue parameters so drivers don't - * need to initialise the hardware if the hardware - * doesn't start up with sane defaults - */ - ieee80211_set_wmm_default(sdata); - } ieee80211_recalc_ps(local, -1); @@ -345,7 +358,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) return 0; err_del_interface: - drv_remove_interface(local, &sdata->vif); + drv_remove_interface(local, sdata); err_stop: if (!local->open_count) drv_stop(local); @@ -363,8 +376,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 +395,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); /* @@ -451,27 +465,31 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, struct ieee80211_sub_if_data *vlan, *tmpsdata; struct beacon_data *old_beacon = rtnl_dereference(sdata->u.ap.beacon); + struct sk_buff *old_probe_resp = + rtnl_dereference(sdata->u.ap.probe_resp); /* sdata_running will return false, so this will disable */ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); - /* remove beacon */ - rcu_assign_pointer(sdata->u.ap.beacon, NULL); + /* remove beacon and probe response */ + RCU_INIT_POINTER(sdata->u.ap.beacon, NULL); + RCU_INIT_POINTER(sdata->u.ap.probe_resp, 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); - } + kfree_skb(old_probe_resp); /* 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); + } else if (sdata->vif.type == NL80211_IFTYPE_STATION) { + ieee80211_mgd_stop(sdata); } if (going_down) @@ -535,7 +553,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, ieee80211_free_keys(sdata); if (going_down) - drv_remove_interface(local, &sdata->vif); + drv_remove_interface(local, sdata); } sdata->bss = NULL; @@ -546,6 +564,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 +679,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, @@ -671,7 +692,6 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev, struct ieee80211_local *local = sdata->local; struct ieee80211_hdr *hdr; struct ieee80211_radiotap_header *rtap = (void *)skb->data; - u8 *p; if (local->hw.queues < 4) return 0; @@ -682,19 +702,7 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev, hdr = (void *)((u8 *)skb->data + le16_to_cpu(rtap->it_len)); - if (!ieee80211_is_data(hdr->frame_control)) { - skb->priority = 7; - return ieee802_1d_to_ac[skb->priority]; - } - if (!ieee80211_is_data_qos(hdr->frame_control)) { - skb->priority = 0; - return ieee802_1d_to_ac[skb->priority]; - } - - p = ieee80211_get_qos_ctl(hdr); - skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK; - - return ieee80211_downgrade_queue(local, skb); + return ieee80211_select_queue_80211(local, skb, hdr); } static const struct net_device_ops ieee80211_monitorif_ops = { @@ -702,7 +710,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, @@ -865,6 +873,8 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, sdata->control_port_protocol = cpu_to_be16(ETH_P_PAE); sdata->control_port_no_encrypt = false; + sdata->noack_map = 0; + /* only monitor differs */ sdata->dev->type = ARPHRD_ETHER; @@ -1143,8 +1153,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)); @@ -1190,6 +1200,13 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, sband = local->hw.wiphy->bands[i]; sdata->rc_rateidx_mask[i] = sband ? (1 << sband->n_bitrates) - 1 : 0; + if (sband) + memcpy(sdata->rc_rateidx_mcs_mask[i], + sband->ht_cap.mcs.rx_mask, + sizeof(sdata->rc_rateidx_mcs_mask[i])); + else + memset(sdata->rc_rateidx_mcs_mask[i], 0, + sizeof(sdata->rc_rateidx_mcs_mask[i])); } /* setup type-dependent data */ @@ -1227,6 +1244,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 +1275,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); @@ -1315,7 +1338,9 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) /* do not count disabled managed interfaces */ if (sdata->vif.type == NL80211_IFTYPE_STATION && - !sdata->u.mgd.associated) { + !sdata->u.mgd.associated && + !sdata->u.mgd.auth_data && + !sdata->u.mgd.assoc_data) { sdata->vif.bss_conf.idle = true; continue; } @@ -1326,6 +1351,7 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) continue; } /* count everything else */ + sdata->vif.bss_conf.idle = false; count++; } @@ -1334,7 +1360,8 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) wk->sdata->vif.bss_conf.idle = false; } - if (local->scan_sdata) { + if (local->scan_sdata && + !(local->hw.flags & IEEE80211_HW_SCAN_WHILE_IDLE)) { scanning = true; local->scan_sdata->vif.bss_conf.idle = false; } @@ -1343,6 +1370,9 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) hw_roc = true; list_for_each_entry(sdata, &local->interfaces, list) { + if (sdata->vif.type == NL80211_IFTYPE_MONITOR || + sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + continue; if (sdata->old_idle == sdata->vif.bss_conf.idle) continue; if (!ieee80211_sdata_running(sdata)) diff --git a/net/mac80211/key.c b/net/mac80211/key.c index f825e2f..5bb600d 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -15,7 +15,9 @@ #include #include #include +#include #include +#include #include "ieee80211_i.h" #include "driver-ops.h" #include "debugfs_key.h" @@ -53,18 +55,40 @@ static void assert_key_lock(struct ieee80211_local *local) lockdep_assert_held(&local->key_mtx); } -static struct ieee80211_sta *get_sta_for_key(struct ieee80211_key *key) +static void increment_tailroom_need_count(struct ieee80211_sub_if_data *sdata) { - if (key->sta) - return &key->sta->sta; + /* + * 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 + */ - return NULL; + 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; - struct ieee80211_sta *sta; + struct sta_info *sta; int ret; might_sleep(); @@ -74,7 +98,7 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) assert_key_lock(key->local); - sta = get_sta_for_key(key); + sta = key->sta; /* * If this is a per-STA GTK, check if it @@ -84,6 +108,9 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) !(key->local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK)) goto out_unsupported; + if (sta && !sta->uploaded) + goto out_unsupported; + sdata = key->sdata; if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { /* @@ -92,22 +119,30 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) */ if (!(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE)) goto out_unsupported; - sdata = container_of(sdata->bss, - struct ieee80211_sub_if_data, - u.ap); } - ret = drv_set_key(key->local, SET_KEY, sdata, sta, &key->conf); + ret = drv_set_key(key->local, SET_KEY, sdata, + sta ? &sta->sta : NULL, &key->conf); 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) || + (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))) + sdata->crypto_tx_tailroom_needed_cnt--; + + WARN_ON((key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) && + (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)); + return 0; } if (ret != -ENOSPC && ret != -EOPNOTSUPP) wiphy_err(key->local->hw.wiphy, "failed to set key (%d, %pM) to hardware (%d)\n", - key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); + key->conf.keyidx, + sta ? sta->sta.addr : bcast_addr, ret); out_unsupported: switch (key->conf.cipher) { @@ -126,7 +161,7 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) { struct ieee80211_sub_if_data *sdata; - struct ieee80211_sta *sta; + struct sta_info *sta; int ret; might_sleep(); @@ -139,21 +174,22 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) return; - sta = get_sta_for_key(key); + sta = key->sta; sdata = key->sdata; - if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) - sdata = container_of(sdata->bss, - struct ieee80211_sub_if_data, - u.ap); + if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) || + (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) || + (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))) + increment_tailroom_need_count(sdata); ret = drv_set_key(key->local, DISABLE_KEY, sdata, - sta, &key->conf); + sta ? &sta->sta : NULL, &key->conf); if (ret) wiphy_err(key->local->hw.wiphy, "failed to remove key (%d, %pM) from hardware (%d)\n", - key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); + key->conf.keyidx, + sta ? sta->sta.addr : bcast_addr, ret); key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; } @@ -239,7 +275,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 +366,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 +431,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 +461,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 +475,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 +491,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 +539,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 +611,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 #include +#include #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..d62f47c 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -47,7 +47,7 @@ void ieee80211_configure_filter(struct ieee80211_local *local) if (atomic_read(&local->iff_allmultis)) new_flags |= FIF_ALLMULTI; - if (local->monitors || local->scanning) + if (local->monitors || test_bit(SCAN_SW_SCANNING, &local->scanning)) new_flags |= FIF_BCN_PRBRESP_PROMISC; if (local->fif_probe_req || local->probe_req_reg) @@ -92,50 +92,9 @@ 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; + struct ieee80211_channel *chan; int ret = 0; int power; enum nl80211_channel_type channel_type; @@ -143,14 +102,9 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) might_sleep(); - 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; + if (local->scan_channel) { + chan = local->scan_channel; /* If scanning on oper channel, use whatever channel-type * is currently in use. */ @@ -159,7 +113,7 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) else channel_type = NL80211_CHAN_NO_HT; } else if (local->tmp_channel) { - chan = scan_chan = local->tmp_channel; + chan = local->tmp_channel; channel_type = local->tmp_channel_type; } else { chan = local->oper_channel; @@ -193,12 +147,13 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) changed |= IEEE80211_CONF_CHANGE_SMPS; } - if ((local->scanning & SCAN_SW_SCANNING) || - (local->scanning & SCAN_HW_SCANNING)) + if (test_bit(SCAN_SW_SCANNING, &local->scanning) || + test_bit(SCAN_HW_SCANNING, &local->scanning)) power = chan->max_power; else power = local->power_constr_level ? - (chan->max_power - local->power_constr_level) : + min(chan->max_power, + (chan->max_reg_power - local->power_constr_level)) : chan->max_power; if (local->user_power_level >= 0) @@ -241,15 +196,7 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, return; if (sdata->vif.type == NL80211_IFTYPE_STATION) { - /* - * While not associated, claim a BSSID of all-zeroes - * so that drivers don't do any weird things with the - * BSSID at that time. - */ - if (sdata->vif.bss_conf.assoc) - sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid; - else - sdata->vif.bss_conf.bssid = zero; + sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid; } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid; else if (sdata->vif.type == NL80211_IFTYPE_AP) @@ -325,6 +272,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)) || @@ -334,11 +283,23 @@ static void ieee80211_tasklet_handler(unsigned long data) /* Clear skb->pkt_type in order to not confuse kernel * netstack. */ skb->pkt_type = 0; - ieee80211_rx(local_to_hw(local), skb); + ieee80211_rx(&local->hw, skb); break; case IEEE80211_TX_STATUS_MSG: skb->pkt_type = 0; - ieee80211_tx_status(local_to_hw(local), skb); + ieee80211_tx_status(&local->hw, 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", @@ -422,9 +383,6 @@ static int ieee80211_ifa_changed(struct notifier_block *nb, sdata = IEEE80211_DEV_TO_SUB_IF(ndev); bss_conf = &sdata->vif.bss_conf; - if (!ieee80211_sdata_running(sdata)) - return NOTIFY_DONE; - /* ARP filtering is only supported in managed mode */ if (sdata->vif.type != NL80211_IFTYPE_STATION) return NOTIFY_DONE; @@ -453,7 +411,7 @@ static int ieee80211_ifa_changed(struct notifier_block *nb, } bss_conf->arp_addr_cnt = c; - /* Configure driver only if associated */ + /* Configure driver only if associated (which also implies it is up) */ if (ifmgd->associated) { bss_conf->arp_filter_enabled = sdata->arp_filter_state; ieee80211_bss_info_change_notify(sdata, @@ -546,6 +504,19 @@ ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = { }, }; +static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = { + .ampdu_params_info = IEEE80211_HT_AMPDU_PARM_FACTOR | + IEEE80211_HT_AMPDU_PARM_DENSITY, + + .cap_info = cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_MAX_AMSDU | + IEEE80211_HT_CAP_SGI_40), + .mcs = { + .rx_mask = { 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, }, + }, +}; + struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, const struct ieee80211_ops *ops) { @@ -553,6 +524,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, int priv_size, i; struct wiphy *wiphy; + if (WARN_ON(ops->sta_state && (ops->sta_add || ops->sta_remove))) + return NULL; + /* Ensure 32-byte alignment of our private data and hw private data. * We use the wiphy priv data for both our ieee80211_local and for * the driver's private data @@ -581,7 +555,13 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, wiphy->flags |= WIPHY_FLAG_NETNS_OK | WIPHY_FLAG_4ADDR_AP | - WIPHY_FLAG_4ADDR_STATION; + WIPHY_FLAG_4ADDR_STATION | + WIPHY_FLAG_REPORTS_OBSS | + WIPHY_FLAG_OFFCHAN_TX | + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + + wiphy->features = NL80211_FEATURE_SK_TX_STATUS | + NL80211_FEATURE_HT_IBSS; if (!ops->set_key) wiphy->flags |= WIPHY_FLAG_IBSS_RSN; @@ -594,7 +574,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN); - BUG_ON(!ops->tx); + BUG_ON(!ops->tx && !ops->tx_frags); BUG_ON(!ops->start); BUG_ON(!ops->stop); BUG_ON(!ops->config); @@ -608,11 +588,11 @@ 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; - local->uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES; - local->uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN; + wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask; INIT_LIST_HEAD(&local->interfaces); @@ -655,6 +635,11 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, INIT_WORK(&local->sched_scan_stopped_work, ieee80211_sched_scan_stopped_work); + spin_lock_init(&local->ack_status_lock); + idr_init(&local->ack_status_frames); + /* preallocate at least one entry */ + idr_pre_get(&local->ack_status_frames, GFP_KERNEL); + sta_info_init(local); for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { @@ -678,7 +663,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, ieee80211_hw_roc_setup(local); - return local_to_hw(local); + return &local->hw; } EXPORT_SYMBOL(ieee80211_alloc_hw); @@ -707,6 +692,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) ) return -EINVAL; + if ((hw->flags & IEEE80211_HW_SCAN_WHILE_IDLE) && !local->ops->hw_scan) + return -EINVAL; + if (hw->max_report_rates == 0) hw->max_report_rates = hw->max_rates; @@ -742,6 +730,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 +856,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 +883,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 +1006,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,12 +1017,20 @@ 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); } EXPORT_SYMBOL(ieee80211_unregister_hw); +static int ieee80211_free_ack_frame(int id, void *p, void *data) +{ + WARN_ONCE(1, "Have pending ack frames!\n"); + kfree_skb(p); + return 0; +} + void ieee80211_free_hw(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); @@ -1040,6 +1041,10 @@ void ieee80211_free_hw(struct ieee80211_hw *hw) if (local->wiphy_ciphers_allocated) kfree(local->hw.wiphy->cipher_suites); + idr_for_each(&local->ack_status_frames, + ieee80211_free_ack_frame, NULL); + idr_destroy(&local->ack_status_frames); + wiphy_free(local->hw.wiphy); } EXPORT_SYMBOL(ieee80211_free_hw); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 370aa94..e80fa33 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(); @@ -69,6 +76,7 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data) bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct ieee80211_local *local = sdata->local; /* * As support for each feature is added, check for matching @@ -80,15 +88,23 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_dat * - MDA enabled * - Power management control on fc */ - if (ifmsh->mesh_id_len == ie->mesh_id_len && - memcmp(ifmsh->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 && - (ifmsh->mesh_pp_id == ie->mesh_config->meshconf_psel) && - (ifmsh->mesh_pm_id == ie->mesh_config->meshconf_pmetric) && - (ifmsh->mesh_cc_id == ie->mesh_config->meshconf_congest) && - (ifmsh->mesh_sp_id == ie->mesh_config->meshconf_synch) && - (ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth)) - return true; - + if (!(ifmsh->mesh_id_len == ie->mesh_id_len && + memcmp(ifmsh->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 && + (ifmsh->mesh_pp_id == ie->mesh_config->meshconf_psel) && + (ifmsh->mesh_pm_id == ie->mesh_config->meshconf_pmetric) && + (ifmsh->mesh_cc_id == ie->mesh_config->meshconf_congest) && + (ifmsh->mesh_sp_id == ie->mesh_config->meshconf_synch) && + (ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth))) + goto mismatch; + + /* disallow peering with mismatched channel types for now */ + if (ie->ht_info_elem && + (local->_oper_channel_type != + ieee80211_ht_info_to_channel_type(ie->ht_info_elem))) + goto mismatch; + + return true; +mismatch: return false; } @@ -188,15 +204,14 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr, kmem_cache_free(rm_cache, p); --entries; } else if ((seqnum == p->seqnum) && - (memcmp(sa, p->sa, ETH_ALEN) == 0)) + (compare_ether_addr(sa, p->sa) == 0)) return -1; } 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,36 +219,127 @@ 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; + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + u8 *pos, neighbors; + u8 meshconf_len = sizeof(struct ieee80211_meshconf_ie); + + if (skb_tailroom(skb) < 2 + meshconf_len) + return -ENOMEM; + + pos = skb_put(skb, 2 + meshconf_len); + *pos++ = WLAN_EID_MESH_CONFIG; + *pos++ = meshconf_len; + + /* Active path selection protocol ID */ + *pos++ = ifmsh->mesh_pp_id; + /* Active path selection metric ID */ + *pos++ = ifmsh->mesh_pm_id; + /* Congestion control mode identifier */ + *pos++ = ifmsh->mesh_cc_id; + /* Synchronization protocol identifier */ + *pos++ = ifmsh->mesh_sp_id; + /* Authentication Protocol identifier */ + *pos++ = ifmsh->mesh_auth_id; + /* Mesh Formation Info - number of neighbors */ + 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 */ + ifmsh->accepting_plinks = mesh_plink_availables(sdata); + *pos = MESHCONF_CAPAB_FORWARDING; + *pos++ |= ifmsh->accepting_plinks ? + MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; + *pos++ = 0x00; + + 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; - 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 (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); } - 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); + 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; @@ -241,53 +347,52 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) *pos++ = ieee80211_frequency_to_channel(local->hw.conf.channel->center_freq); } - 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); - - pos = skb_put(skb, 2 + sizeof(struct ieee80211_meshconf_ie)); - *pos++ = WLAN_EID_MESH_CONFIG; - *pos++ = sizeof(struct ieee80211_meshconf_ie); + return 0; +} - /* Active path selection protocol ID */ - *pos++ = sdata->u.mesh.mesh_pp_id; +int mesh_add_ht_cap_ie(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_supported_band *sband; + u8 *pos; - /* Active path selection metric ID */ - *pos++ = sdata->u.mesh.mesh_pm_id; + sband = local->hw.wiphy->bands[local->oper_channel->band]; + if (!sband->ht_cap.ht_supported || + local->_oper_channel_type == NL80211_CHAN_NO_HT) + return 0; - /* Congestion control mode identifier */ - *pos++ = sdata->u.mesh.mesh_cc_id; + if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap)) + return -ENOMEM; - /* Synchronization protocol identifier */ - *pos++ = sdata->u.mesh.mesh_sp_id; + pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_cap)); + ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, sband->ht_cap.cap); - /* Authentication Protocol identifier */ - *pos++ = sdata->u.mesh.mesh_auth_id; + return 0; +} - /* Mesh Formation Info - number of neighbors */ - neighbors = atomic_read(&sdata->u.mesh.mshstats.estab_plinks); - /* Number of neighbor mesh STAs or 15 whichever is smaller */ - neighbors = (neighbors > 15) ? 15 : neighbors; - *pos++ = neighbors << 1; +int mesh_add_ht_info_ie(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_channel *channel = local->oper_channel; + enum nl80211_channel_type channel_type = local->_oper_channel_type; + struct ieee80211_supported_band *sband = + local->hw.wiphy->bands[channel->band]; + struct ieee80211_sta_ht_cap *ht_cap = &sband->ht_cap; + u8 *pos; - /* Mesh capability */ - sdata->u.mesh.accepting_plinks = mesh_plink_availables(sdata); - *pos = MESHCONF_CAPAB_FORWARDING; - *pos++ |= sdata->u.mesh.accepting_plinks ? - MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; - *pos++ = 0x00; + if (!ht_cap->ht_supported || channel_type == NL80211_CHAN_NO_HT) + return 0; - 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); - } -} + if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_info)) + return -ENOMEM; + pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_info)); + ieee80211_ie_build_ht_info(pos, ht_cap, channel, channel_type); + return 0; +} static void ieee80211_mesh_path_timer(unsigned long data) { struct ieee80211_sub_if_data *sdata = @@ -352,8 +457,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 +529,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 +538,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 +663,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,9 +746,11 @@ 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; + ifmsh->next_perr = jiffies; /* Allocate all mesh structures when creating the first mesh interface. */ if (!mesh_allocated) ieee80211s_init(); diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 249e733..8d53b71 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -13,7 +13,6 @@ #include #include -#include #include "ieee80211_i.h" @@ -31,6 +30,8 @@ * @MESH_PATH_FIXED: the mesh path has been manually set and should not be * modified * @MESH_PATH_RESOLVED: the mesh path can has been resolved + * @MESH_PATH_REQ_QUEUED: there is an unsent path request for this destination + * already queued up, waiting for the discovery process to start. * * MESH_PATH_RESOLVED is used by the mesh path timer to * decide when to stop or cancel the mesh path discovery. @@ -41,6 +42,7 @@ enum mesh_path_flags { MESH_PATH_SN_VALID = BIT(2), MESH_PATH_FIXED = BIT(3), MESH_PATH_RESOLVED = BIT(4), + MESH_PATH_REQ_QUEUED = BIT(5), }; /** @@ -80,7 +82,12 @@ 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. + * @rann_snd_addr: the RANN sender address + * @is_root: the destination station of this path is a root node + * @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 +111,9 @@ struct mesh_path { u8 discovery_retries; enum mesh_path_flags flags; spinlock_t state_lock; + u8 rann_snd_addr[ETH_ALEN]; + bool is_root; + bool is_gate; }; /** @@ -120,6 +130,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 +146,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 +181,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 +194,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 +208,20 @@ 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); +int mesh_add_ht_cap_ie(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata); +int mesh_add_ht_info_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); @@ -213,6 +236,8 @@ void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh); /* Mesh paths */ int mesh_nexthop_lookup(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); +int mesh_nexthop_resolve(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata); void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata); struct mesh_path *mesh_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata); @@ -223,10 +248,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 +284,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..1c6f3d0 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -8,10 +8,14 @@ */ #include +#include +#include +#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 +61,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) @@ -111,20 +115,20 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags, struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; - struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400); + struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - u8 *pos; - int ie_len; + u8 *pos, ie_len; + int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.mesh_action) + + sizeof(mgmt->u.action.u.mesh_action); + skb = dev_alloc_skb(local->tx_headroom + + hdr_len + + 2 + 37); /* max HWMP IE */ if (!skb) return -1; - skb_reserve(skb, local->hw.extra_tx_headroom); - /* 25 is the size of the common mgmt part (24) plus the size of the - * common action part (1) - */ - mgmt = (struct ieee80211_mgmt *) - skb_put(skb, 25 + sizeof(mgmt->u.action.u.mesh_action)); - memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.mesh_action)); + skb_reserve(skb, local->tx_headroom); + mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); + memset(mgmt, 0, hdr_len); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); @@ -132,24 +136,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 +168,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,34 +232,44 @@ 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, struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; - struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400); + struct sk_buff *skb; + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_mgmt *mgmt; - u8 *pos; - int ie_len; + u8 *pos, ie_len; + int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.mesh_action) + + sizeof(mgmt->u.action.u.mesh_action); + if (time_before(jiffies, ifmsh->next_perr)) + return -EAGAIN; + + skb = dev_alloc_skb(local->tx_headroom + + hdr_len + + 2 + 15 /* PERR IE */); if (!skb) return -1; - skb_reserve(skb, local->hw.extra_tx_headroom); - /* 25 is the size of the common mgmt part (24) plus the size of the - * common action part (1) - */ - mgmt = (struct ieee80211_mgmt *) - skb_put(skb, 25 + sizeof(mgmt->u.action.u.mesh_action)); - memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.mesh_action)); + skb_reserve(skb, local->tx_headroom); + mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); + memset(mgmt, 0, hdr_len); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); 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 +294,11 @@ 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); + ifmsh->next_perr = TU_TO_EXP_TIME( + ifmsh->mshcfg.dot11MeshHWMPperrMinInterval); + ieee80211_add_pending_skb(local, skb); return 0; } @@ -277,6 +324,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local, struct sta_info *sta) { struct ieee80211_supported_band *sband; + struct rate_info rinfo; /* This should be adjusted for each device */ int device_constant = 1 << ARITH_SHIFT; int test_frame_len = TEST_FRAME_LEN << ARITH_SHIFT; @@ -290,7 +338,9 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local, if (sta->fail_avg >= 100) return MAX_METRIC; - if (sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS) + sta_set_rate_info_tx(sta, &sta->last_tx_rate, &rinfo); + rate = cfg80211_calculate_bitrate(&rinfo); + if (WARN_ON(!rate)) return MAX_METRIC; err = (sta->fail_avg << ARITH_SHIFT) / 100; @@ -298,7 +348,6 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local, /* bitrate is in units of 100 Kbps, while we need rate in units of * 1Mbps. This will be corrected on tx_time computation. */ - rate = sband->bitrates[sta->last_tx_rate.idx].bitrate; tx_time = (device_constant + 10 * test_frame_len / rate); estimated_retx = ((1 << (2 * ARITH_SHIFT)) / (s_unit - err)); result = (tx_time * estimated_retx) >> (2 * ARITH_SHIFT) ; @@ -354,15 +403,13 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, orig_metric = PREQ_IE_METRIC(hwmp_ie); break; case MPATH_PREP: - /* Originator here refers to the MP that was the destination in - * the Path Request. The draft refers to that MP as the - * destination address, even though usually it is the origin of - * the PREP frame. We divert from the nomenclature in the draft + /* Originator here refers to the MP that was the target in the + * Path Request. We divert from the nomenclature in the draft * so that we can easily use a single function to gather path * information from both PREQ and PREP frames. */ - orig_addr = PREP_IE_ORIG_ADDR(hwmp_ie); - orig_sn = PREP_IE_ORIG_SN(hwmp_ie); + orig_addr = PREP_IE_TARGET_ADDR(hwmp_ie); + orig_sn = PREP_IE_TARGET_SN(hwmp_ie); orig_lifetime = PREP_IE_LIFETIME(hwmp_ie); orig_metric = PREP_IE_METRIC(hwmp_ie); break; @@ -375,7 +422,7 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, new_metric = MAX_METRIC; exp_time = TU_TO_EXP_TIME(orig_lifetime); - if (memcmp(orig_addr, sdata->vif.addr, ETH_ALEN) == 0) { + if (compare_ether_addr(orig_addr, sdata->vif.addr) == 0) { /* This MP is the originator, we are not interested in this * frame, except for updating transmitter's path info. */ @@ -425,7 +472,7 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, /* Update and check transmitter routing info */ ta = mgmt->sa; - if (memcmp(orig_addr, ta, ETH_ALEN) == 0) + if (compare_ether_addr(orig_addr, ta) == 0) fresh_info = false; else { fresh_info = true; @@ -449,7 +496,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; @@ -470,8 +516,9 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, u8 *preq_elem, u32 metric) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; - struct mesh_path *mpath; + struct mesh_path *mpath = NULL; u8 *target_addr, *orig_addr; + const u8 *da; u8 target_flags, ttl; u32 orig_sn, target_sn, lifetime; bool reply = false; @@ -484,10 +531,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"); + if (compare_ether_addr(target_addr, sdata->vif.addr) == 0) { + mhwmp_dbg("PREQ is for us"); forward = false; reply = true; metric = 0; @@ -523,17 +570,17 @@ 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"); - 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, + mhwmp_dbg("replying to the PREQ"); + mesh_path_sel_frame_tx(MPATH_PREP, 0, orig_addr, + cpu_to_le32(orig_sn), 0, target_addr, + cpu_to_le32(target_sn), mgmt->sa, 0, ttl, cpu_to_le32(lifetime), cpu_to_le32(metric), 0, sdata); } else ifmsh->mshstats.dropped_frames_ttl++; } - if (forward) { + if (forward && ifmsh->mshcfg.dot11MeshForwarding) { u32 preq_id; u8 hopcount, flags; @@ -543,14 +590,16 @@ 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); hopcount = PREQ_IE_HOPCOUNT(preq_elem) + 1; + da = (mpath && mpath->is_root) ? + mpath->rann_snd_addr : broadcast_addr; mesh_path_sel_frame_tx(MPATH_PREQ, flags, orig_addr, cpu_to_le32(orig_sn), target_flags, target_addr, - cpu_to_le32(target_sn), broadcast_addr, + cpu_to_le32(target_sn), da, hopcount, ttl, cpu_to_le32(lifetime), cpu_to_le32(metric), cpu_to_le32(preq_id), sdata); @@ -572,25 +621,23 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, u8 *prep_elem, u32 metric) { + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct mesh_path *mpath; u8 *target_addr, *orig_addr; u8 ttl, hopcount, flags; 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 - * function destnation refers to the final destination of the PREP, - * which corresponds with the originator of the PREQ which this PREP - * replies - */ - target_addr = PREP_IE_TARGET_ADDR(prep_elem); - if (memcmp(target_addr, sdata->vif.addr, ETH_ALEN) == 0) + orig_addr = PREP_IE_ORIG_ADDR(prep_elem); + if (compare_ether_addr(orig_addr, sdata->vif.addr) == 0) /* destination, no forwarding required */ return; + if (!ifmsh->mshcfg.dot11MeshForwarding) + return; + ttl = PREP_IE_TTL(prep_elem); if (ttl <= 1) { sdata->u.mesh.mshstats.dropped_frames_ttl++; @@ -598,7 +645,7 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, } rcu_read_lock(); - mpath = mesh_path_lookup(target_addr, sdata); + mpath = mesh_path_lookup(orig_addr, sdata); if (mpath) spin_lock_bh(&mpath->state_lock); else @@ -613,7 +660,7 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, flags = PREP_IE_FLAGS(prep_elem); lifetime = PREP_IE_LIFETIME(prep_elem); hopcount = PREP_IE_HOPCOUNT(prep_elem) + 1; - orig_addr = PREP_IE_ORIG_ADDR(prep_elem); + target_addr = PREP_IE_TARGET_ADDR(prep_elem); target_sn = PREP_IE_TARGET_SN(prep_elem); orig_sn = PREP_IE_ORIG_SN(prep_elem); @@ -657,21 +704,26 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata, rcu_read_lock(); mpath = mesh_path_lookup(target_addr, sdata); if (mpath) { + struct sta_info *sta; + spin_lock_bh(&mpath->state_lock); + sta = next_hop_deref_protected(mpath); if (mpath->flags & MESH_PATH_ACTIVE && - memcmp(ta, next_hop_deref_protected(mpath)->sta.addr, - ETH_ALEN) == 0 && + compare_ether_addr(ta, sta->sta.addr) == 0 && (!(mpath->flags & MESH_PATH_SN_VALID) || SN_GT(target_sn, mpath->sn))) { mpath->flags &= ~MESH_PATH_ACTIVE; mpath->sn = target_sn; spin_unlock_bh(&mpath->state_lock); + if (!ifmsh->mshcfg.dot11MeshForwarding) + goto endperr; mesh_path_error_tx(ttl, target_addr, cpu_to_le32(target_sn), cpu_to_le16(target_rcode), broadcast_addr, sdata); } else spin_unlock_bh(&mpath->state_lock); } +endperr: rcu_read_unlock(); } @@ -684,6 +736,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 +746,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 (compare_ether_addr(orig_addr, sdata->vif.addr) == 0) + return; + + mhwmp_dbg("received RANN from %pM via neighbour %pM (is_gate=%d)", + orig_addr, mgmt->sa, root_is_gate); rcu_read_lock(); mpath = mesh_path_lookup(orig_addr, sdata); @@ -709,18 +770,33 @@ 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->sn < orig_sn) { + + 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 && ifmsh->mshcfg.dot11MeshForwarding) { 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; } + + /* Using individually addressed PREQ for root node */ + memcpy(mpath->rann_snd_addr, mgmt->sa, ETH_ALEN); + mpath->is_root = true; + + if (root_is_gate) + mesh_path_add_gate(mpath); + rcu_read_unlock(); } @@ -732,11 +808,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,25 +873,36 @@ 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; + } + + spin_lock(&mpath->state_lock); + if (mpath->flags & MESH_PATH_REQ_QUEUED) { + spin_unlock(&mpath->state_lock); + spin_unlock_bh(&ifmsh->mesh_preq_queue_lock); + kfree(preq_node); return; } memcpy(preq_node->dst, mpath->dst, ETH_ALEN); preq_node->flags = flags; + mpath->flags |= MESH_PATH_REQ_QUEUED; + spin_unlock(&mpath->state_lock); + 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); @@ -833,6 +929,7 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata) struct mesh_preq_queue *preq_node; struct mesh_path *mpath; u8 ttl, target_flags; + const u8 *da; u32 lifetime; spin_lock_bh(&ifmsh->mesh_preq_queue_lock); @@ -855,6 +952,7 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata) goto enddiscovery; spin_lock_bh(&mpath->state_lock); + mpath->flags &= ~MESH_PATH_REQ_QUEUED; if (preq_node->flags & PREQ_Q_F_START) { if (mpath->flags & MESH_PATH_RESOLVING) { spin_unlock_bh(&mpath->state_lock); @@ -894,9 +992,10 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata) target_flags = MP_F_RF; spin_unlock_bh(&mpath->state_lock); + da = (mpath->is_root) ? mpath->rann_snd_addr : broadcast_addr; mesh_path_sel_frame_tx(MPATH_PREQ, 0, sdata->vif.addr, cpu_to_le32(ifmsh->sn), target_flags, mpath->dst, - cpu_to_le32(mpath->sn), broadcast_addr, 0, + cpu_to_le32(mpath->sn), da, 0, ttl, cpu_to_le32(lifetime), 0, cpu_to_le32(ifmsh->preq_id++), sdata); mod_timer(&mpath->timer, jiffies + mpath->discovery_timeout); @@ -906,71 +1005,97 @@ enddiscovery: kfree(preq_node); } -/** - * mesh_nexthop_lookup - put the appropriate next hop on a mesh frame +/* mesh_nexthop_resolve - lookup next hop for given skb and start path + * discovery if no forwarding information is found. * * @skb: 802.11 frame to be sent * @sdata: network subif the frame will be sent through * - * Returns: 0 if the next hop was found. Nonzero otherwise. If no next hop is - * found, the function will start a path discovery and queue the frame so it is - * sent when the path is resolved. This means the caller must not free the skb - * in this case. + * Returns: 0 if the next hop was found and -ENOENT if the frame was queued. + * skb is freeed here if no mpath could be allocated. */ -int mesh_nexthop_lookup(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata) +int mesh_nexthop_resolve(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata) { - struct sk_buff *skb_to_free = NULL; - struct mesh_path *mpath; - struct sta_info *next_hop; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct mesh_path *mpath; + struct sk_buff *skb_to_free = NULL; u8 *target_addr = hdr->addr3; int err = 0; rcu_read_lock(); - mpath = mesh_path_lookup(target_addr, sdata); + err = mesh_nexthop_lookup(skb, sdata); + if (!err) + goto endlookup; + /* no nexthop found, start resolving */ + mpath = mesh_path_lookup(target_addr, sdata); if (!mpath) { mesh_path_add(target_addr, sdata); mpath = mesh_path_lookup(target_addr, sdata); if (!mpath) { - sdata->u.mesh.mshstats.dropped_frames_no_route++; + mesh_path_discard_frame(skb, sdata); err = -ENOSPC; goto endlookup; } } - if (mpath->flags & MESH_PATH_ACTIVE) { - if (time_after(jiffies, - mpath->exp_time - - msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time)) && - !memcmp(sdata->vif.addr, hdr->addr4, ETH_ALEN) && - !(mpath->flags & MESH_PATH_RESOLVING) && - !(mpath->flags & MESH_PATH_FIXED)) { - mesh_queue_preq(mpath, - PREQ_Q_F_START | PREQ_Q_F_REFRESH); - } - next_hop = rcu_dereference(mpath->next_hop); - if (next_hop) - memcpy(hdr->addr1, next_hop->sta.addr, ETH_ALEN); - else - err = -ENOENT; - } else { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - if (!(mpath->flags & MESH_PATH_RESOLVING)) { - /* Start discovery only if it is not running yet */ - mesh_queue_preq(mpath, PREQ_Q_F_START); - } + if (!(mpath->flags & MESH_PATH_RESOLVING)) + mesh_queue_preq(mpath, PREQ_Q_F_START); + + if (skb_queue_len(&mpath->frame_queue) >= MESH_FRAME_QUEUE_LEN) + skb_to_free = skb_dequeue(&mpath->frame_queue); - if (skb_queue_len(&mpath->frame_queue) >= - MESH_FRAME_QUEUE_LEN) - skb_to_free = skb_dequeue(&mpath->frame_queue); + info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; + ieee80211_set_qos_hdr(sdata, skb); + skb_queue_tail(&mpath->frame_queue, skb); + err = -ENOENT; + if (skb_to_free) + mesh_path_discard_frame(skb_to_free, sdata); - info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; - skb_queue_tail(&mpath->frame_queue, skb); - if (skb_to_free) - mesh_path_discard_frame(skb_to_free, sdata); - err = -ENOENT; +endlookup: + rcu_read_unlock(); + return err; +} +/** + * mesh_nexthop_lookup - put the appropriate next hop on a mesh frame. Calling + * this function is considered "using" the associated mpath, so preempt a path + * refresh if this mpath expires soon. + * + * @skb: 802.11 frame to be sent + * @sdata: network subif the frame will be sent through + * + * Returns: 0 if the next hop was found. Nonzero otherwise. + */ +int mesh_nexthop_lookup(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata) +{ + struct mesh_path *mpath; + struct sta_info *next_hop; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + u8 *target_addr = hdr->addr3; + int err = -ENOENT; + + rcu_read_lock(); + mpath = mesh_path_lookup(target_addr, sdata); + + if (!mpath || !(mpath->flags & MESH_PATH_ACTIVE)) + goto endlookup; + + if (time_after(jiffies, + mpath->exp_time - + msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time)) && + !compare_ether_addr(sdata->vif.addr, hdr->addr4) && + !(mpath->flags & MESH_PATH_RESOLVING) && + !(mpath->flags & MESH_PATH_FIXED)) + mesh_queue_preq(mpath, PREQ_Q_F_START | PREQ_Q_F_REFRESH); + + next_hop = rcu_dereference(mpath->next_hop); + if (next_hop) { + memcpy(hdr->addr1, next_hop->sta.addr, ETH_ALEN); + memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); + err = 0; } endlookup: @@ -982,35 +1107,47 @@ 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; + mpath->flags &= ~MESH_PATH_REQ_QUEUED; + 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..7ed81ee 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -14,9 +14,16 @@ #include #include #include +#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); @@ -103,6 +112,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 +128,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 +140,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 +168,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; @@ -198,6 +221,7 @@ 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); + memcpy(hdr->addr2, mpath->sdata->vif.addr, ETH_ALEN); __skb_queue_tail(&tmpq, skb); } @@ -205,62 +229,129 @@ 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->addr2, gate_mpath->sdata->vif.addr, ETH_ALEN); + 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 *mpath_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) { + compare_ether_addr(dst, mpath->dst) == 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 +360,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 mpath_lookup(rcu_dereference(mesh_paths), dst, sdata); +} + +struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) +{ + return mpath_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 +403,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; @@ -305,6 +414,88 @@ struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data } /** + * 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) +{ + struct mesh_table *tbl; + struct mpath_node *gate, *new_gate; + struct hlist_node *n; + int err; + + rcu_read_lock(); + tbl = rcu_dereference(mesh_paths); + + 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; + + 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); + kfree_rcu(gate, rcu); + 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_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) * @sdata: local subif @@ -326,7 +517,7 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) int err = 0; u32 hash_idx; - if (memcmp(dst, sdata->vif.addr, ETH_ALEN) == 0) + if (compare_ether_addr(dst, sdata->vif.addr) == 0) /* never add ourselves as neighbours */ return -ENOTSUPP; @@ -362,12 +553,13 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) hash_idx = mesh_table_hash(dst, sdata, tbl); bucket = &tbl->hash_buckets[hash_idx]; - spin_lock_bh(&tbl->hashwlock[hash_idx]); + spin_lock(&tbl->hashwlock[hash_idx]); err = -EEXIST; hlist_for_each_entry(node, n, bucket, list) { mpath = node->mpath; - if (mpath->sdata == sdata && memcmp(dst, mpath->dst, ETH_ALEN) == 0) + if (mpath->sdata == sdata && + compare_ether_addr(dst, mpath->dst) == 0) goto err_exists; } @@ -378,7 +570,7 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) mesh_paths_generation++; - spin_unlock_bh(&tbl->hashwlock[hash_idx]); + spin_unlock(&tbl->hashwlock[hash_idx]); read_unlock_bh(&pathtbl_resize_lock); if (grow) { set_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags); @@ -387,7 +579,7 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) return 0; err_exists: - spin_unlock_bh(&tbl->hashwlock[hash_idx]); + spin_unlock(&tbl->hashwlock[hash_idx]); read_unlock_bh(&pathtbl_resize_lock); kfree(new_node); err_node_alloc: @@ -458,7 +650,7 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) int err = 0; u32 hash_idx; - if (memcmp(dst, sdata->vif.addr, ETH_ALEN) == 0) + if (compare_ether_addr(dst, sdata->vif.addr) == 0) /* never add ourselves as neighbours */ return -ENOTSUPP; @@ -481,6 +673,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); @@ -489,12 +682,13 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) hash_idx = mesh_table_hash(dst, sdata, tbl); bucket = &tbl->hash_buckets[hash_idx]; - spin_lock_bh(&tbl->hashwlock[hash_idx]); + spin_lock(&tbl->hashwlock[hash_idx]); err = -EEXIST; hlist_for_each_entry(node, n, bucket, list) { mpath = node->mpath; - if (mpath->sdata == sdata && memcmp(dst, mpath->dst, ETH_ALEN) == 0) + if (mpath->sdata == sdata && + compare_ether_addr(dst, mpath->dst) == 0) goto err_exists; } @@ -503,7 +697,7 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) tbl->mean_chain_len * (tbl->hash_mask + 1)) grow = 1; - spin_unlock_bh(&tbl->hashwlock[hash_idx]); + spin_unlock(&tbl->hashwlock[hash_idx]); read_unlock_bh(&pathtbl_resize_lock); if (grow) { set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags); @@ -512,7 +706,7 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) return 0; err_exists: - spin_unlock_bh(&tbl->hashwlock[hash_idx]); + spin_unlock(&tbl->hashwlock[hash_idx]); read_unlock_bh(&pathtbl_resize_lock); kfree(new_node); err_node_alloc: @@ -539,28 +733,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); + + del_timer_sync(&node->mpath->timer); + 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 = node->mpath; + struct ieee80211_sub_if_data *sdata = node->mpath->sdata; + + 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(&sdata->u.mesh.mpaths); + atomic_dec(&tbl->entries); +} + /** * mesh_path_flush_by_nexthop - Deletes mesh paths if their next hop matches * @@ -581,42 +800,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(&tbl->hashwlock[i]); + __mesh_path_del(tbl, node); + spin_unlock(&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(); } /** @@ -642,17 +878,12 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) hash_idx = mesh_table_hash(addr, sdata, tbl); bucket = &tbl->hash_buckets[hash_idx]; - spin_lock_bh(&tbl->hashwlock[hash_idx]); + spin_lock(&tbl->hashwlock[hash_idx]); hlist_for_each_entry(node, n, bucket, list) { 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); + compare_ether_addr(addr, mpath->dst) == 0) { + __mesh_path_del(tbl, node); goto enddel; } } @@ -660,7 +891,7 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) err = -ENXIO; enddel: mesh_paths_generation++; - spin_unlock_bh(&tbl->hashwlock[hash_idx]); + spin_unlock(&tbl->hashwlock[hash_idx]); read_unlock_bh(&pathtbl_resize_lock); return err; } @@ -681,38 +912,68 @@ 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 * @sdata: network subif the frame was to be sent through * - * If the frame was being forwarded from another MP, a PERR frame will be sent - * to the precursor. The precursor's address (i.e. the previous hop) was saved - * in addr1 of the frame-to-be-forwarded, and would only be overwritten once - * the destination is successfully resolved. - * * Locking: the function must me called within a rcu_read_lock region */ void mesh_path_discard_frame(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - struct mesh_path *mpath; - u32 sn = 0; - - if (memcmp(hdr->addr4, sdata->vif.addr, ETH_ALEN) != 0) { - u8 *ra, *da; - - da = hdr->addr3; - ra = hdr->addr1; - mpath = mesh_path_lookup(da, sdata); - if (mpath) - sn = ++mpath->sn; - 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); - } - kfree_skb(skb); sdata->u.mesh.mshstats.dropped_frames_no_route++; } @@ -728,8 +989,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 +1050,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 +1058,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 +1108,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 +1119,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..4e53c4c 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -19,34 +19,23 @@ #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 -}; +/* We only need a valid sta if user configured a minimum rssi_threshold. */ +#define rssi_threshold_check(sta, sdata) \ + (sdata->u.mesh.mshcfg.rssi_threshold == 0 ||\ + (sta && (s8) -ewma_read(&sta->avg_signal) > \ + sdata->u.mesh.mshcfg.rssi_threshold)) enum plink_event { PLINK_UNDEFINED, @@ -60,6 +49,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) { @@ -93,11 +86,15 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta) * on it in the lifecycle management section! */ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, - u8 *hw_addr, u32 rates) + u8 *hw_addr, u32 rates, + struct ieee802_11_elems *elems) { struct ieee80211_local *local = sdata->local; + struct ieee80211_supported_band *sband; struct sta_info *sta; + sband = local->hw.wiphy->bands[local->oper_channel->band]; + if (local->num_sta >= MESH_MAX_PLINKS) return NULL; @@ -105,8 +102,17 @@ 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; + sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); + sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); + sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); + + set_sta_flag(sta, WLAN_STA_WME); + sta->sta.supp_rates[local->hw.conf.channel->band] = rates; + if (elems->ht_cap_elem) + ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, + elems->ht_cap_elem, + &sta->sta.ht_cap); rate_control_rate_init(sta); return sta; @@ -150,6 +156,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,82 +167,109 @@ 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 sk_buff *skb; struct ieee80211_mgmt *mgmt; bool include_plid = false; - static const u8 meshpeeringproto[] = { 0x00, 0x0F, 0xAC, 0x2A }; - u8 *pos; - int ie_len; - + u16 peering_proto = 0; + u8 *pos, ie_len = 4; + int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.self_prot) + + sizeof(mgmt->u.action.u.self_prot); + + skb = dev_alloc_skb(local->tx_headroom + + hdr_len + + 2 + /* capability info */ + 2 + /* AID */ + 2 + 8 + /* supported rates */ + 2 + (IEEE80211_MAX_SUPP_RATES - 8) + + 2 + sdata->u.mesh.mesh_id_len + + 2 + sizeof(struct ieee80211_meshconf_ie) + + 2 + sizeof(struct ieee80211_ht_cap) + + 2 + sizeof(struct ieee80211_ht_info) + + 2 + 8 + /* peering IE */ + sdata->u.mesh.ie_len); if (!skb) return -1; - skb_reserve(skb, local->hw.extra_tx_headroom); - /* 25 is the size of the common mgmt part (24) plus the size of the - * 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_reserve(skb, local->tx_headroom); + mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); + memset(mgmt, 0, hdr_len); 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 (action != WLAN_SP_MESH_PEERING_CLOSE) { + if (mesh_add_ht_cap_ie(skb, sdata) || + mesh_add_ht_info_ie(skb, sdata)) + return -1; + } + + if (mesh_add_vendor_ies(skb, sdata)) + return -1; + ieee80211_tx_skb(sdata, skb); return 0; } @@ -256,7 +293,7 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates, elems->ie_start, elems->total_len, GFP_KERNEL); else - sta = mesh_plink_alloc(sdata, hw_addr, rates); + sta = mesh_plink_alloc(sdata, hw_addr, rates, elems); if (!sta) return; if (sta_info_insert_rcu(sta)) { @@ -270,7 +307,8 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates, if (mesh_peer_accepts_plinks(elems) && sta->plink_state == NL80211_PLINK_LISTEN && sdata->u.mesh.accepting_plinks && - sdata->u.mesh.mshcfg.auto_open_plinks) + sdata->u.mesh.mshcfg.auto_open_plinks && + rssi_threshold_check(sta, sdata)) mesh_plink_open(sta); rcu_read_unlock(); @@ -322,21 +360,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 +418,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 +434,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 +460,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 +487,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 +505,48 @@ 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 (ftype == WLAN_SP_MESH_PEERING_OPEN && + !rssi_threshold_check(sta, sdata)) { + mpl_dbg("Mesh plink: %pM does not meet rssi threshold\n", + mgmt->sa); + rcu_read_unlock(); + return; + } + + 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 +559,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(); @@ -543,7 +593,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m } rates = ieee80211_sta_get_rates(local, &elems, rx_status->band); - sta = mesh_plink_alloc(sdata, mgmt->sa, rates); + sta = mesh_plink_alloc(sdata, mgmt->sa, rates, &elems); if (!sta) { mpl_dbg("Mesh plink error: plink table full\n"); return; @@ -557,21 +607,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 +670,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 +687,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 +699,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 +709,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 +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, @@ -689,14 +743,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 +772,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 +784,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 +796,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 +809,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 +818,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 +848,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..ae37644 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -16,10 +16,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -28,6 +30,12 @@ #include "rate.h" #include "led.h" +#define IEEE80211_AUTH_TIMEOUT (HZ / 5) +#define IEEE80211_AUTH_MAX_TRIES 3 +#define IEEE80211_AUTH_WAIT_ASSOC (HZ * 5) +#define IEEE80211_ASSOC_TIMEOUT (HZ / 5) +#define IEEE80211_ASSOC_MAX_TRIES 3 + static int max_nullfunc_tries = 2; module_param(max_nullfunc_tries, int, 0644); MODULE_PARM_DESC(max_nullfunc_tries, @@ -80,6 +88,8 @@ MODULE_PARM_DESC(probe_wait_ms, #define TMR_RUNNING_TIMER 0 #define TMR_RUNNING_CHANSW 1 +#define DEAUTH_DISASSOC_LEN (24 /* hdr */ + 2 /* reason */) + /* * All cfg80211 functions have to be called outside a locked * section so that they can acquire a lock themselves... This @@ -95,6 +105,15 @@ enum rx_mgmt_action { /* caller must call cfg80211_send_disassoc() */ RX_MGMT_CFG80211_DISASSOC, + + /* caller must call cfg80211_send_rx_auth() */ + RX_MGMT_CFG80211_RX_AUTH, + + /* caller must call cfg80211_send_rx_assoc() */ + RX_MGMT_CFG80211_RX_ASSOC, + + /* caller must call cfg80211_send_assoc_timeout() */ + RX_MGMT_CFG80211_ASSOC_TIMEOUT, }; /* utils */ @@ -113,8 +132,7 @@ static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd) * has happened -- the work that runs from this timer will * do that. */ -static void run_again(struct ieee80211_if_managed *ifmgd, - unsigned long timeout) +static void run_again(struct ieee80211_if_managed *ifmgd, unsigned long timeout) { ASSERT_MGD_MTX(ifmgd); @@ -125,7 +143,7 @@ static void run_again(struct ieee80211_if_managed *ifmgd, void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata) { - if (sdata->local->hw.flags & IEEE80211_HW_BEACON_FILTER) + if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER) return; mod_timer(&sdata->u.mgd.bcn_mon_timer, @@ -160,7 +178,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; @@ -168,89 +187,104 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, u32 changed = 0; int hti_cfreq; u16 ht_opmode; - bool enable_ht = true; + bool enable_ht = true, queues_stopped = false; enum nl80211_channel_type prev_chantype; - enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; + enum nl80211_channel_type rx_channel_type = NL80211_CHAN_NO_HT; + enum nl80211_channel_type tx_channel_type; sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; - prev_chantype = sdata->vif.bss_conf.channel_type; - /* HT is not supported */ - if (!sband->ht_cap.ht_supported) - enable_ht = false; - if (enable_ht) { - hti_cfreq = ieee80211_channel_to_frequency(hti->control_chan, - sband->band); - /* check that channel matches the right operating channel */ - if (local->hw.conf.channel->center_freq != hti_cfreq) { - /* Some APs mess this up, evidently. - * Netgear WNDR3700 sometimes reports 4 higher than - * the actual channel, for instance. - */ - printk(KERN_DEBUG - "%s: Wrong control channel in association" - " response: configured center-freq: %d" - " hti-cfreq: %d hti->control_chan: %d" - " band: %d. Disabling HT.\n", - sdata->name, - local->hw.conf.channel->center_freq, - hti_cfreq, hti->control_chan, - sband->band); - enable_ht = false; - } + hti_cfreq = ieee80211_channel_to_frequency(hti->control_chan, + sband->band); + /* check that channel matches the right operating channel */ + if (local->hw.conf.channel->center_freq != hti_cfreq) { + /* Some APs mess this up, evidently. + * Netgear WNDR3700 sometimes reports 4 higher than + * the actual channel, for instance. + */ + printk(KERN_DEBUG + "%s: Wrong control channel in association" + " response: configured center-freq: %d" + " hti-cfreq: %d hti->control_chan: %d" + " band: %d. Disabling HT.\n", + sdata->name, + local->hw.conf.channel->center_freq, + hti_cfreq, hti->control_chan, + sband->band); + enable_ht = false; } if (enable_ht) { - channel_type = NL80211_CHAN_HT20; + rx_channel_type = NL80211_CHAN_HT20; if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) && + !ieee80111_cfg_override_disables_ht40(sdata) && (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) && (hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) { switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: - if (!(local->hw.conf.channel->flags & - IEEE80211_CHAN_NO_HT40PLUS)) - channel_type = NL80211_CHAN_HT40PLUS; + rx_channel_type = NL80211_CHAN_HT40PLUS; break; case IEEE80211_HT_PARAM_CHA_SEC_BELOW: - if (!(local->hw.conf.channel->flags & - IEEE80211_CHAN_NO_HT40MINUS)) - channel_type = NL80211_CHAN_HT40MINUS; + rx_channel_type = NL80211_CHAN_HT40MINUS; break; } } } + tx_channel_type = ieee80211_get_tx_channel_type(local, rx_channel_type); + if (local->tmp_channel) - local->tmp_channel_type = channel_type; + local->tmp_channel_type = rx_channel_type; - if (!ieee80211_set_channel_type(local, sdata, channel_type)) { + if (!ieee80211_set_channel_type(local, sdata, rx_channel_type)) { /* can only fail due to HT40+/- mismatch */ - channel_type = NL80211_CHAN_HT20; - WARN_ON(!ieee80211_set_channel_type(local, sdata, channel_type)); + rx_channel_type = NL80211_CHAN_HT20; + WARN_ON(!ieee80211_set_channel_type(local, sdata, + rx_channel_type)); + } + + if (beacon_htcap_ie && (prev_chantype != rx_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); + queues_stopped = true; + + /* flush out all packets */ + synchronize_net(); + + drv_flush(local, false); } /* channel_type change automatically detected */ ieee80211_hw_config(local, 0); - if (prev_chantype != channel_type) { + if (prev_chantype != tx_channel_type) { rcu_read_lock(); sta = sta_info_get(sdata, bssid); if (sta) rate_control_rate_update(local, sband, sta, IEEE80211_RC_HT_CHANGED, - channel_type); + tx_channel_type); rcu_read_unlock(); } + if (queues_stopped) + ieee80211_wake_queues_by_reason(&sdata->local->hw, + IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE); + ht_opmode = le16_to_cpu(hti->operation_mode); /* if bss configuration changed store the new one */ if (sdata->ht_opmode_valid != enable_ht || sdata->vif.bss_conf.ht_operation_mode != ht_opmode || - prev_chantype != channel_type) { + prev_chantype != rx_channel_type) { changed |= BSS_CHANGED_HT; sdata->vif.bss_conf.ht_operation_mode = ht_opmode; sdata->ht_opmode_valid = enable_ht; @@ -261,50 +295,351 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, /* frame sending functions */ -static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, - const u8 *bssid, u16 stype, u16 reason, - void *cookie, bool send_frame) +static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len, + struct ieee80211_supported_band *sband, + u32 *rates) +{ + int i, j, count; + *rates = 0; + count = 0; + for (i = 0; i < supp_rates_len; i++) { + int rate = (supp_rates[i] & 0x7F) * 5; + + for (j = 0; j < sband->n_bitrates; j++) + if (sband->bitrates[j].bitrate == rate) { + *rates |= BIT(j); + count++; + break; + } + } + + return count; +} + +static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, const u8 *ht_info_ie, + struct ieee80211_supported_band *sband, + struct ieee80211_channel *channel, + enum ieee80211_smps_mode smps) +{ + struct ieee80211_ht_info *ht_info; + u8 *pos; + u32 flags = channel->flags; + u16 cap; + struct ieee80211_sta_ht_cap ht_cap; + + BUILD_BUG_ON(sizeof(ht_cap) != sizeof(sband->ht_cap)); + + if (!ht_info_ie) + return; + + if (ht_info_ie[1] < sizeof(struct ieee80211_ht_info)) + return; + + memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap)); + ieee80211_apply_htcap_overrides(sdata, &ht_cap); + + ht_info = (struct ieee80211_ht_info *)(ht_info_ie + 2); + + /* determine capability flags */ + cap = ht_cap.cap; + + switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + if (flags & IEEE80211_CHAN_NO_HT40PLUS) { + cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + cap &= ~IEEE80211_HT_CAP_SGI_40; + } + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + if (flags & IEEE80211_CHAN_NO_HT40MINUS) { + cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + cap &= ~IEEE80211_HT_CAP_SGI_40; + } + break; + } + + /* set SM PS mode properly */ + cap &= ~IEEE80211_HT_CAP_SM_PS; + switch (smps) { + case IEEE80211_SMPS_AUTOMATIC: + case IEEE80211_SMPS_NUM_MODES: + WARN_ON(1); + case IEEE80211_SMPS_OFF: + cap |= WLAN_HT_CAP_SM_PS_DISABLED << + IEEE80211_HT_CAP_SM_PS_SHIFT; + break; + case IEEE80211_SMPS_STATIC: + cap |= WLAN_HT_CAP_SM_PS_STATIC << + IEEE80211_HT_CAP_SM_PS_SHIFT; + break; + case IEEE80211_SMPS_DYNAMIC: + cap |= WLAN_HT_CAP_SM_PS_DYNAMIC << + IEEE80211_HT_CAP_SM_PS_SHIFT; + break; + } + + /* reserve and fill IE */ + pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); + ieee80211_ie_build_ht_cap(pos, &ht_cap, cap); +} + +static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; + u8 *pos, qos_info; + size_t offset = 0, noffset; + int i, count, rates_len, supp_rates_len; + u16 capab; + struct ieee80211_supported_band *sband; + u32 rates = 0; + + lockdep_assert_held(&ifmgd->mtx); + + sband = local->hw.wiphy->bands[local->oper_channel->band]; - 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 (assoc_data->supp_rates_len) { + /* + * Get all rates supported by the device and the AP as + * some APs don't like getting a superset of their rates + * in the association request (e.g. D-Link DAP 1353 in + * b-only mode)... + */ + rates_len = ieee80211_compatible_rates(assoc_data->supp_rates, + assoc_data->supp_rates_len, + sband, &rates); + } else { + /* + * In case AP not provide any supported rates information + * before association, we send information element(s) with + * all rates that we support. + */ + rates = ~0; + rates_len = sband->n_bitrates; + } + + skb = alloc_skb(local->hw.extra_tx_headroom + + sizeof(*mgmt) + /* bit too much but doesn't matter */ + 2 + assoc_data->ssid_len + /* SSID */ + 4 + rates_len + /* (extended) rates */ + 4 + /* power capability */ + 2 + 2 * sband->n_channels + /* supported channels */ + 2 + sizeof(struct ieee80211_ht_cap) + /* HT */ + assoc_data->ie_len + /* extra IEs */ + 9, /* WMM */ + GFP_KERNEL); + if (!skb) return; - } + skb_reserve(skb, local->hw.extra_tx_headroom); + capab = WLAN_CAPABILITY_ESS; + + if (sband->band == IEEE80211_BAND_2GHZ) { + 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; + } + + if (assoc_data->capability & WLAN_CAPABILITY_PRIVACY) + capab |= WLAN_CAPABILITY_PRIVACY; + + if ((assoc_data->capability & WLAN_CAPABILITY_SPECTRUM_MGMT) && + (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)) + capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; + mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); memset(mgmt, 0, 24); + memcpy(mgmt->da, assoc_data->bss->bssid, ETH_ALEN); + memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); + memcpy(mgmt->bssid, assoc_data->bss->bssid, ETH_ALEN); + + if (!is_zero_ether_addr(assoc_data->prev_bssid)) { + skb_put(skb, 10); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_REASSOC_REQ); + mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab); + mgmt->u.reassoc_req.listen_interval = + cpu_to_le16(local->hw.conf.listen_interval); + memcpy(mgmt->u.reassoc_req.current_ap, assoc_data->prev_bssid, + ETH_ALEN); + } else { + skb_put(skb, 4); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ASSOC_REQ); + mgmt->u.assoc_req.capab_info = cpu_to_le16(capab); + mgmt->u.assoc_req.listen_interval = + cpu_to_le16(local->hw.conf.listen_interval); + } + + /* SSID */ + pos = skb_put(skb, 2 + assoc_data->ssid_len); + *pos++ = WLAN_EID_SSID; + *pos++ = assoc_data->ssid_len; + memcpy(pos, assoc_data->ssid, assoc_data->ssid_len); + + /* add all rates which were marked to be used above */ + supp_rates_len = rates_len; + if (supp_rates_len > 8) + supp_rates_len = 8; + + pos = skb_put(skb, supp_rates_len + 2); + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = supp_rates_len; + + count = 0; + for (i = 0; i < sband->n_bitrates; i++) { + if (BIT(i) & rates) { + int rate = sband->bitrates[i].bitrate; + *pos++ = (u8) (rate / 5); + if (++count == 8) + break; + } + } + + if (rates_len > count) { + pos = skb_put(skb, rates_len - count + 2); + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = rates_len - count; + + for (i++; i < sband->n_bitrates; i++) { + if (BIT(i) & rates) { + int rate = sband->bitrates[i].bitrate; + *pos++ = (u8) (rate / 5); + } + } + } + + if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) { + /* 1. power capabilities */ + pos = skb_put(skb, 4); + *pos++ = WLAN_EID_PWR_CAPABILITY; + *pos++ = 2; + *pos++ = 0; /* min tx power */ + *pos++ = local->oper_channel->max_power; /* max tx power */ + + /* 2. supported channels */ + /* TODO: get this in reg domain format */ + pos = skb_put(skb, 2 * sband->n_channels + 2); + *pos++ = WLAN_EID_SUPPORTED_CHANNELS; + *pos++ = 2 * sband->n_channels; + for (i = 0; i < sband->n_channels; i++) { + *pos++ = ieee80211_frequency_to_channel( + sband->channels[i].center_freq); + *pos++ = 1; /* one channel in the subband*/ + } + } + + /* if present, add any custom IEs that go before HT */ + if (assoc_data->ie_len && assoc_data->ie) { + static const u8 before_ht[] = { + WLAN_EID_SSID, + WLAN_EID_SUPP_RATES, + WLAN_EID_EXT_SUPP_RATES, + WLAN_EID_PWR_CAPABILITY, + WLAN_EID_SUPPORTED_CHANNELS, + WLAN_EID_RSN, + WLAN_EID_QOS_CAPA, + WLAN_EID_RRM_ENABLED_CAPABILITIES, + WLAN_EID_MOBILITY_DOMAIN, + WLAN_EID_SUPPORTED_REGULATORY_CLASSES, + }; + noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len, + before_ht, ARRAY_SIZE(before_ht), + offset); + pos = skb_put(skb, noffset - offset); + memcpy(pos, assoc_data->ie + offset, noffset - offset); + offset = noffset; + } + + if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) + ieee80211_add_ht_ie(sdata, skb, assoc_data->ht_information_ie, + sband, local->oper_channel, ifmgd->ap_smps); + + /* if present, add any custom non-vendor IEs that go after HT */ + if (assoc_data->ie_len && assoc_data->ie) { + noffset = ieee80211_ie_split_vendor(assoc_data->ie, + assoc_data->ie_len, + offset); + pos = skb_put(skb, noffset - offset); + memcpy(pos, assoc_data->ie + offset, noffset - offset); + offset = noffset; + } + + if (assoc_data->wmm) { + if (assoc_data->uapsd) { + qos_info = ifmgd->uapsd_queues; + qos_info |= (ifmgd->uapsd_max_sp_len << + IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT); + } else { + qos_info = 0; + } + + pos = skb_put(skb, 9); + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = 7; /* len */ + *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ + *pos++ = 0x50; + *pos++ = 0xf2; + *pos++ = 2; /* WME */ + *pos++ = 0; /* WME info */ + *pos++ = 1; /* WME ver */ + *pos++ = qos_info; + } + + /* add any remaining custom (i.e. vendor specific here) IEs */ + if (assoc_data->ie_len && assoc_data->ie) { + noffset = assoc_data->ie_len; + pos = skb_put(skb, noffset - offset); + memcpy(pos, assoc_data->ie + offset, noffset - offset); + } + + IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + ieee80211_tx_skb(sdata, skb); +} + +static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, + const u8 *bssid, u16 stype, + u16 reason, bool send_frame, + u8 *frame_buf) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct sk_buff *skb; + struct ieee80211_mgmt *mgmt = (void *)frame_buf; + + /* build frame */ + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); + mgmt->duration = 0; /* initialize only */ + mgmt->seq_ctrl = 0; /* initialize only */ memcpy(mgmt->da, bssid, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); memcpy(mgmt->bssid, bssid, ETH_ALEN); - mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); - skb_put(skb, 2); /* u.deauth.reason_code == u.disassoc.reason_code */ mgmt->u.deauth.reason_code = cpu_to_le16(reason); - if (stype == IEEE80211_STYPE_DEAUTH) - if (cookie) - __cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len); - else - cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len); - else - if (cookie) - __cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len); - else - cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len); - if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED)) - IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + if (send_frame) { + skb = dev_alloc_skb(local->hw.extra_tx_headroom + + DEAUTH_DISASSOC_LEN); + if (!skb) + return; + + skb_reserve(skb, local->hw.extra_tx_headroom); + + /* copy in frame */ + memcpy(skb_put(skb, DEAUTH_DISASSOC_LEN), + mgmt, DEAUTH_DISASSOC_LEN); - if (send_frame) + if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED)) + IEEE80211_SKB_CB(skb)->flags |= + IEEE80211_TX_INTFL_DONT_ENCRYPT; ieee80211_tx_skb(sdata, skb); - else - kfree_skb(skb); + } } void ieee80211_send_pspoll(struct ieee80211_local *local, @@ -330,6 +665,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 +676,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 +694,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 +732,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! */ @@ -520,7 +861,7 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, if (pwr_constr_elem_len != 1) return; - if ((*pwr_constr_elem <= conf->channel->max_power) && + if ((*pwr_constr_elem <= conf->channel->max_reg_power) && (*pwr_constr_elem != sdata->local->power_constr_level)) { sdata->local->power_constr_level = *pwr_constr_elem; ieee80211_hw_config(sdata->local, 0); @@ -608,7 +949,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 +970,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 +1090,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,29 +1098,42 @@ 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))) { + !(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) { netif_tx_stop_all_queues(sdata->dev); if (drv_tx_frames_pending(local)) @@ -804,7 +1155,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) @@ -841,7 +1193,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, return; if (ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED) - uapsd_queues = local->uapsd_queues; + uapsd_queues = ifmgd->uapsd_queues; count = wmm_param[6] & 0x0f; if (count == ifmgd->wmm_last_param_set) @@ -906,7 +1258,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); @@ -914,7 +1267,6 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, /* enable WMM or activate new settings */ sdata->vif.bss_conf.qos = true; - ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS); } static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, @@ -967,7 +1319,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, bss_info_changed |= BSS_CHANGED_ASSOC; /* set timing information */ bss_conf->beacon_int = cbss->beacon_interval; - bss_conf->timestamp = cbss->tsf; + bss_conf->last_tsf = cbss->tsf; bss_info_changed |= BSS_CHANGED_BEACON_INT; bss_info_changed |= ieee80211_handle_bss_capability(sdata, @@ -993,18 +1345,9 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, bss_conf->dtim_period = 0; bss_conf->assoc = 1; - /* - * For now just always ask the driver to update the basic rateset - * when we have associated, we aren't checking whether it actually - * changed or not. - */ - bss_info_changed |= BSS_CHANGED_BASIC_RATES; - - /* And the BSSID changed - we're associated now */ - bss_info_changed |= BSS_CHANGED_BSSID; /* Tell the driver to monitor connection quality (if supported) */ - if ((local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) && + if (sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI && bss_conf->cqm_rssi_thold) bss_info_changed |= BSS_CHANGED_CQM; @@ -1026,23 +1369,23 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, } static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, - bool remove_sta, bool tx) + u16 stype, u16 reason, bool tx, + u8 *frame_buf) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; struct sta_info *sta; - u32 changed = 0, config_changed = 0; - u8 bssid[ETH_ALEN]; + u32 changed = 0; ASSERT_MGD_MTX(ifmgd); - if (WARN_ON(!ifmgd->associated)) + if (WARN_ON_ONCE(tx && !frame_buf)) return; - memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN); + if (WARN_ON(!ifmgd->associated)) + return; ifmgd->associated = NULL; - memset(ifmgd->bssid, 0, ETH_ALEN); /* * we need to commit the associated = NULL change because the @@ -1062,26 +1405,39 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, netif_carrier_off(sdata->dev); mutex_lock(&local->sta_mtx); - sta = sta_info_get(sdata, bssid); + sta = sta_info_get(sdata, ifmgd->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); + /* deauthenticate/disassociate now */ + if (tx || frame_buf) + ieee80211_send_deauth_disassoc(sdata, ifmgd->bssid, stype, + reason, tx, frame_buf); + + /* flush out frame */ + if (tx) + drv_flush(local, false); + + /* clear bssid only after building the needed mgmt frames */ + memset(ifmgd->bssid, 0, ETH_ALEN); + + /* remove AP and TDLS peers */ + sta_info_flush(local, sdata); + + /* finally reset all BSS / config parameters */ changed |= ieee80211_reset_erp_info(sdata); ieee80211_led_assoc(local, 0); changed |= BSS_CHANGED_ASSOC; sdata->vif.bss_conf.assoc = false; - ieee80211_set_wmm_default(sdata); - - /* channel(_type) changes are handled by ieee80211_hw_config */ - WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT)); - /* on the next assoc, re-program HT parameters */ sdata->ht_opmode_valid = false; + memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa)); + memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask)); local->power_constr_level = 0; @@ -1090,24 +1446,29 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, if (local->hw.conf.flags & IEEE80211_CONF_PS) { local->hw.conf.flags &= ~IEEE80211_CONF_PS; - config_changed |= IEEE80211_CONF_CHANGE_PS; + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } local->ps_sdata = NULL; - ieee80211_hw_config(local, config_changed); - /* Disable ARP filtering */ if (sdata->vif.bss_conf.arp_filter_enabled) { sdata->vif.bss_conf.arp_filter_enabled = false; changed |= BSS_CHANGED_ARP_FILTER; } + sdata->vif.bss_conf.qos = false; + changed |= BSS_CHANGED_QOS; + /* The BSSID (not really interesting) and HT changed */ changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT; ieee80211_bss_info_change_notify(sdata, changed); - if (remove_sta) - sta_info_destroy_addr(sdata, bssid); + /* channel(_type) changes are handled by ieee80211_hw_config */ + WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT)); + ieee80211_hw_config(local, 0); + + /* disassociated - set to defaults now */ + ieee80211_set_wmm_default(sdata, false); del_timer_sync(&sdata->u.mgd.conn_mon_timer); del_timer_sync(&sdata->u.mgd.bcn_mon_timer); @@ -1207,7 +1568,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 +1654,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; } @@ -1303,6 +1666,7 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; u8 bssid[ETH_ALEN]; + u8 frame_buf[DEAUTH_DISASSOC_LEN]; mutex_lock(&ifmgd->mtx); if (!ifmgd->associated) { @@ -1315,20 +1679,20 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) printk(KERN_DEBUG "%s: Connection to AP %pM lost.\n", sdata->name, bssid); - ieee80211_set_disassoc(sdata, true, true); + ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, + false, frame_buf); mutex_unlock(&ifmgd->mtx); - mutex_lock(&local->mtx); - ieee80211_recalc_idle(local); - mutex_unlock(&local->mtx); /* * must be outside lock due to cfg80211, * but that's not a problem. */ - ieee80211_send_deauth_disassoc(sdata, bssid, - IEEE80211_STYPE_DEAUTH, - WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, - NULL, true); + cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN); + + mutex_lock(&local->mtx); + ieee80211_recalc_idle(local); + mutex_unlock(&local->mtx); } void ieee80211_beacon_connection_loss_work(struct work_struct *work) @@ -1336,6 +1700,16 @@ void ieee80211_beacon_connection_loss_work(struct work_struct *work) struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, u.mgd.beacon_connection_loss_work); + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct sta_info *sta; + + if (ifmgd->associated) { + rcu_read_lock(); + sta = sta_info_get(sdata, ifmgd->bssid); + if (sta) + sta->beacon_loss_count++; + rcu_read_unlock(); + } if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) __ieee80211_connection_loss(sdata); @@ -1368,6 +1742,126 @@ void ieee80211_connection_loss(struct ieee80211_vif *vif) EXPORT_SYMBOL(ieee80211_connection_loss); +static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, + bool assoc) +{ + struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data; + + lockdep_assert_held(&sdata->u.mgd.mtx); + + if (!assoc) { + sta_info_destroy_addr(sdata, auth_data->bss->bssid); + + memset(sdata->u.mgd.bssid, 0, ETH_ALEN); + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); + } + + cfg80211_put_bss(auth_data->bss); + kfree(auth_data); + sdata->u.mgd.auth_data = NULL; +} + +static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len) +{ + struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data; + u8 *pos; + struct ieee802_11_elems elems; + + pos = mgmt->u.auth.variable; + ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); + if (!elems.challenge) + return; + auth_data->expected_transaction = 4; + ieee80211_send_auth(sdata, 3, auth_data->algorithm, + elems.challenge - 2, elems.challenge_len + 2, + auth_data->bss->bssid, auth_data->bss->bssid, + auth_data->key, auth_data->key_len, + auth_data->key_idx); +} + +static enum rx_mgmt_action __must_check +ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len) +{ + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + u8 bssid[ETH_ALEN]; + u16 auth_alg, auth_transaction, status_code; + struct sta_info *sta; + + lockdep_assert_held(&ifmgd->mtx); + + if (len < 24 + 6) + return RX_MGMT_NONE; + + if (!ifmgd->auth_data || ifmgd->auth_data->done) + return RX_MGMT_NONE; + + memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN); + + if (compare_ether_addr(bssid, mgmt->bssid)) + return RX_MGMT_NONE; + + auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); + auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); + status_code = le16_to_cpu(mgmt->u.auth.status_code); + + if (auth_alg != ifmgd->auth_data->algorithm || + auth_transaction != ifmgd->auth_data->expected_transaction) + return RX_MGMT_NONE; + + if (status_code != WLAN_STATUS_SUCCESS) { + printk(KERN_DEBUG "%s: %pM denied authentication (status %d)\n", + sdata->name, mgmt->sa, status_code); + ieee80211_destroy_auth_data(sdata, false); + return RX_MGMT_CFG80211_RX_AUTH; + } + + switch (ifmgd->auth_data->algorithm) { + case WLAN_AUTH_OPEN: + case WLAN_AUTH_LEAP: + case WLAN_AUTH_FT: + break; + case WLAN_AUTH_SHARED_KEY: + if (ifmgd->auth_data->expected_transaction != 4) { + ieee80211_auth_challenge(sdata, mgmt, len); + /* need another frame */ + return RX_MGMT_NONE; + } + break; + default: + WARN_ONCE(1, "invalid auth alg %d", + ifmgd->auth_data->algorithm); + return RX_MGMT_NONE; + } + + printk(KERN_DEBUG "%s: authenticated\n", sdata->name); + ifmgd->auth_data->done = true; + ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC; + run_again(ifmgd, ifmgd->auth_data->timeout); + + /* move station state to auth */ + mutex_lock(&sdata->local->sta_mtx); + sta = sta_info_get(sdata, bssid); + if (!sta) { + WARN_ONCE(1, "%s: STA %pM not found", sdata->name, bssid); + goto out_err; + } + if (sta_info_move_state(sta, IEEE80211_STA_AUTH)) { + printk(KERN_DEBUG "%s: failed moving %pM to auth\n", + sdata->name, bssid); + goto out_err; + } + mutex_unlock(&sdata->local->sta_mtx); + + return RX_MGMT_CFG80211_RX_AUTH; + out_err: + mutex_unlock(&sdata->local->sta_mtx); + /* ignore frame -- wait for timeout */ + return RX_MGMT_NONE; +} + + static enum rx_mgmt_action __must_check ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) @@ -1376,10 +1870,14 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, const u8 *bssid = NULL; u16 reason_code; + lockdep_assert_held(&ifmgd->mtx); + if (len < 24 + 2) return RX_MGMT_NONE; - ASSERT_MGD_MTX(ifmgd); + if (!ifmgd->associated || + compare_ether_addr(mgmt->bssid, ifmgd->associated->bssid)) + return RX_MGMT_NONE; bssid = ifmgd->associated->bssid; @@ -1388,7 +1886,8 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n", sdata->name, bssid, reason_code); - ieee80211_set_disassoc(sdata, true, false); + ieee80211_set_disassoc(sdata, 0, 0, false, NULL); + mutex_lock(&sdata->local->mtx); ieee80211_recalc_idle(sdata->local); mutex_unlock(&sdata->local->mtx); @@ -1404,15 +1903,13 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u16 reason_code; - if (len < 24 + 2) - return RX_MGMT_NONE; - - ASSERT_MGD_MTX(ifmgd); + lockdep_assert_held(&ifmgd->mtx); - if (WARN_ON(!ifmgd->associated)) + if (len < 24 + 2) return RX_MGMT_NONE; - if (WARN_ON(memcmp(ifmgd->associated->bssid, mgmt->sa, ETH_ALEN))) + if (!ifmgd->associated || + compare_ether_addr(mgmt->bssid, ifmgd->associated->bssid)) return RX_MGMT_NONE; reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); @@ -1420,31 +1917,89 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: disassociated from %pM (Reason: %u)\n", sdata->name, mgmt->sa, reason_code); - ieee80211_set_disassoc(sdata, true, false); + ieee80211_set_disassoc(sdata, 0, 0, false, NULL); + mutex_lock(&sdata->local->mtx); ieee80211_recalc_idle(sdata->local); mutex_unlock(&sdata->local->mtx); + return RX_MGMT_CFG80211_DISASSOC; } - -static bool ieee80211_assoc_success(struct ieee80211_work *wk, - struct ieee80211_mgmt *mgmt, size_t len) +static void ieee80211_get_rates(struct ieee80211_supported_band *sband, + u8 *supp_rates, unsigned int supp_rates_len, + u32 *rates, u32 *basic_rates, + bool *have_higher_than_11mbit, + int *min_rate, int *min_rate_index) { - struct ieee80211_sub_if_data *sdata = wk->sdata; - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - struct ieee80211_local *local = sdata->local; - struct ieee80211_supported_band *sband; - struct sta_info *sta; - struct cfg80211_bss *cbss = wk->assoc.bss; - u8 *pos; - u32 rates, basic_rates; - u16 capab_info, aid; - struct ieee802_11_elems elems; - struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; - u32 changed = 0; - int i, j, err; - bool have_higher_than_11mbit = false; + int i, j; + + for (i = 0; i < supp_rates_len; i++) { + int rate = (supp_rates[i] & 0x7f) * 5; + bool is_basic = !!(supp_rates[i] & 0x80); + + if (rate > 110) + *have_higher_than_11mbit = true; + + /* + * BSS_MEMBERSHIP_SELECTOR_HT_PHY is defined in 802.11n-2009 + * 7.3.2.2 as a magic value instead of a rate. Hence, skip it. + * + * Note: Even through the membership selector and the basic + * rate flag share the same bit, they are not exactly + * the same. + */ + if (!!(supp_rates[i] & 0x80) && + (supp_rates[i] & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY) + continue; + + for (j = 0; j < sband->n_bitrates; j++) { + if (sband->bitrates[j].bitrate == rate) { + *rates |= BIT(j); + if (is_basic) + *basic_rates |= BIT(j); + if (rate < *min_rate) { + *min_rate = rate; + *min_rate_index = j; + } + break; + } + } + } +} + +static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata, + bool assoc) +{ + struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data; + + lockdep_assert_held(&sdata->u.mgd.mtx); + + if (!assoc) { + sta_info_destroy_addr(sdata, assoc_data->bss->bssid); + + memset(sdata->u.mgd.bssid, 0, ETH_ALEN); + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); + } + + kfree(assoc_data); + sdata->u.mgd.assoc_data = NULL; +} + +static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, + struct cfg80211_bss *cbss, + struct ieee80211_mgmt *mgmt, size_t len) +{ + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_local *local = sdata->local; + struct ieee80211_supported_band *sband; + struct sta_info *sta; + u8 *pos; + u16 capab_info, aid; + struct ieee802_11_elems elems; + struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; + u32 changed = 0; + int err; u16 ap_ht_cap_flags; /* AssocResp and ReassocResp have identical structure */ @@ -1479,68 +2034,21 @@ 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(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); - if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) - set_sta_flags(sta, WLAN_STA_AUTHORIZED); - - rates = 0; - basic_rates = 0; - sband = local->hw.wiphy->bands[wk->chan->band]; - - for (i = 0; i < elems.supp_rates_len; i++) { - int rate = (elems.supp_rates[i] & 0x7f) * 5; - bool is_basic = !!(elems.supp_rates[i] & 0x80); - - if (rate > 110) - have_higher_than_11mbit = true; - - for (j = 0; j < sband->n_bitrates; j++) { - if (sband->bitrates[j].bitrate == rate) { - rates |= BIT(j); - if (is_basic) - basic_rates |= BIT(j); - break; - } - } - } - - for (i = 0; i < elems.ext_supp_rates_len; i++) { - int rate = (elems.ext_supp_rates[i] & 0x7f) * 5; - bool is_basic = !!(elems.ext_supp_rates[i] & 0x80); - - if (rate > 110) - have_higher_than_11mbit = true; - - for (j = 0; j < sband->n_bitrates; j++) { - if (sband->bitrates[j].bitrate == rate) { - rates |= BIT(j); - if (is_basic) - basic_rates |= BIT(j); - break; - } - } - } - - sta->sta.supp_rates[wk->chan->band] = rates; - sdata->vif.bss_conf.basic_rates = basic_rates; - - /* cf. IEEE 802.11 9.2.12 */ - if (wk->chan->band == IEEE80211_BAND_2GHZ && - have_higher_than_11mbit) - sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE; - else - sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; + sband = local->hw.wiphy->bands[local->oper_channel->band]; if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) - ieee80211_ht_cap_ie_to_sta_ht_cap(sband, + ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, elems.ht_cap_elem, &sta->sta.ht_cap); ap_ht_cap_flags = sta->sta.ht_cap.cap; @@ -1548,19 +2056,27 @@ 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 = NULL; + err = sta_info_move_state(sta, IEEE80211_STA_AUTH); + if (!err) + err = sta_info_move_state(sta, IEEE80211_STA_ASSOC); + if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) + err = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); if (err) { - printk(KERN_DEBUG "%s: failed to insert STA entry for" - " the AP (error %d)\n", sdata->name, err); + printk(KERN_DEBUG + "%s: failed to move station %pM to desired state\n", + sdata->name, sta->sta.addr); + WARN_ON(__sta_info_destroy(sta)); + mutex_unlock(&sdata->local->sta_mtx); return false; } + mutex_unlock(&sdata->local->sta_mtx); + /* * Always handle WMM once after association regardless * of the first value the AP uses. Setting -1 here has @@ -1573,15 +2089,14 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, elems.wmm_param_len); else - ieee80211_set_wmm_default(sdata); - - local->oper_channel = wk->chan; + ieee80211_set_wmm_default(sdata, false); + changed |= BSS_CHANGED_QOS; if (elems.ht_info_elem && elems.wmm_param && - (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 */ @@ -1606,7 +2121,86 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, return true; } +static enum rx_mgmt_action __must_check +ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len, + struct cfg80211_bss **bss) +{ + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; + u16 capab_info, status_code, aid; + struct ieee802_11_elems elems; + u8 *pos; + bool reassoc; + + lockdep_assert_held(&ifmgd->mtx); + + if (!assoc_data) + return RX_MGMT_NONE; + if (compare_ether_addr(assoc_data->bss->bssid, mgmt->bssid)) + return RX_MGMT_NONE; + + /* + * AssocResp and ReassocResp have identical structure, so process both + * of them in this function. + */ + + if (len < 24 + 6) + return RX_MGMT_NONE; + + reassoc = ieee80211_is_reassoc_req(mgmt->frame_control); + capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); + status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); + aid = le16_to_cpu(mgmt->u.assoc_resp.aid); + + printk(KERN_DEBUG "%s: RX %sssocResp from %pM (capab=0x%x " + "status=%d aid=%d)\n", + sdata->name, reassoc ? "Rea" : "A", mgmt->sa, + capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14)))); + + pos = mgmt->u.assoc_resp.variable; + ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); + + if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && + elems.timeout_int && elems.timeout_int_len == 5 && + elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) { + u32 tu, ms; + tu = get_unaligned_le32(elems.timeout_int + 1); + ms = tu * 1024 / 1000; + printk(KERN_DEBUG "%s: %pM rejected association temporarily; " + "comeback duration %u TU (%u ms)\n", + sdata->name, mgmt->sa, tu, ms); + assoc_data->timeout = jiffies + msecs_to_jiffies(ms); + if (ms > IEEE80211_ASSOC_TIMEOUT) + run_again(ifmgd, assoc_data->timeout); + return RX_MGMT_NONE; + } + + *bss = assoc_data->bss; + + if (status_code != WLAN_STATUS_SUCCESS) { + printk(KERN_DEBUG "%s: %pM denied association (code=%d)\n", + sdata->name, mgmt->sa, status_code); + ieee80211_destroy_assoc_data(sdata, false); + } else { + if (!ieee80211_assoc_success(sdata, *bss, mgmt, len)) { + /* oops -- internal error -- send timeout for now */ + ieee80211_destroy_assoc_data(sdata, false); + cfg80211_put_bss(*bss); + return RX_MGMT_CFG80211_ASSOC_TIMEOUT; + } + printk(KERN_DEBUG "%s: associated\n", sdata->name); + + /* + * destroy assoc_data afterwards, as otherwise an idle + * recalc after assoc_data is NULL but before associated + * is set can cause the interface to go idle + */ + ieee80211_destroy_assoc_data(sdata, true); + } + return RX_MGMT_CFG80211_RX_ASSOC; +} static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, @@ -1620,7 +2214,9 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, struct ieee80211_channel *channel; bool need_ps = false; - if (sdata->u.mgd.associated) { + if (sdata->u.mgd.associated && + compare_ether_addr(mgmt->bssid, sdata->u.mgd.associated->bssid) + == 0) { bss = (void *)sdata->u.mgd.associated->priv; /* not previously set so we may need to recalc */ need_ps = !bss->dtim_period; @@ -1675,7 +2271,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, ASSERT_MGD_MTX(ifmgd); - if (memcmp(mgmt->da, sdata->vif.addr, ETH_ALEN)) + if (compare_ether_addr(mgmt->da, sdata->vif.addr)) return; /* ignore ProbeResp to foreign address */ baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt; @@ -1688,8 +2284,18 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false); if (ifmgd->associated && - memcmp(mgmt->bssid, ifmgd->associated->bssid, ETH_ALEN) == 0) + compare_ether_addr(mgmt->bssid, ifmgd->associated->bssid) == 0) ieee80211_reset_ap_probe(sdata); + + if (ifmgd->auth_data && !ifmgd->auth_data->bss->proberesp_ies && + compare_ether_addr(mgmt->bssid, ifmgd->auth_data->bss->bssid) + == 0) { + /* got probe response, continue with auth */ + printk(KERN_DEBUG "%s: direct probe responded\n", sdata->name); + ifmgd->auth_data->tries = 0; + ifmgd->auth_data->timeout = jiffies; + run_again(ifmgd, ifmgd->auth_data->timeout); + } } /* @@ -1729,7 +2335,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, u32 ncrc; u8 *bssid; - ASSERT_MGD_MTX(ifmgd); + lockdep_assert_held(&ifmgd->mtx); /* Process beacon from the current BSS */ baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; @@ -1739,21 +2345,26 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (rx_status->freq != local->hw.conf.channel->center_freq) return; - /* - * We might have received a number of frames, among them a - * disassoc frame and a beacon... - */ - if (!ifmgd->associated) - return; + if (ifmgd->assoc_data && !ifmgd->assoc_data->have_beacon && + compare_ether_addr(mgmt->bssid, ifmgd->assoc_data->bss->bssid) + == 0) { + ieee802_11_parse_elems(mgmt->u.beacon.variable, + len - baselen, &elems); - bssid = ifmgd->associated->bssid; + ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, + false); + ifmgd->assoc_data->have_beacon = true; + ifmgd->assoc_data->sent_assoc = false; + /* continue assoc process */ + ifmgd->assoc_data->timeout = jiffies; + run_again(ifmgd, ifmgd->assoc_data->timeout); + return; + } - /* - * And in theory even frames from a different AP we were just - * associated to a split-second ago! - */ - if (memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0) + if (!ifmgd->associated || + compare_ether_addr(mgmt->bssid, ifmgd->associated->bssid)) return; + bssid = ifmgd->associated->bssid; /* Track average RSSI from the Beacon frames of the current AP */ ifmgd->last_beacon_signal = rx_status->signal; @@ -1762,6 +2373,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,9 +2381,31 @@ 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 / 16; + 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)) { + !(sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) { int sig = ifmgd->ave_beacon_signal / 16; int last_event = ifmgd->last_cqm_event_signal; int thold = bss_conf->cqm_rssi_thold; @@ -1884,7 +2518,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; - ieee80211_ht_cap_ie_to_sta_ht_cap(sband, + ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, elems.ht_cap_elem, &sta->sta.ht_cap); ap_ht_cap_flags = sta->sta.ht_cap.cap; @@ -1892,7 +2526,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 */ @@ -1914,6 +2548,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_rx_status *rx_status; struct ieee80211_mgmt *mgmt; + struct cfg80211_bss *bss = NULL; enum rx_mgmt_action rma = RX_MGMT_NONE; u16 fc; @@ -1923,92 +2558,59 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, mutex_lock(&ifmgd->mtx); - if (ifmgd->associated && - memcmp(ifmgd->associated->bssid, mgmt->bssid, ETH_ALEN) == 0) { - switch (fc & IEEE80211_FCTL_STYPE) { - case IEEE80211_STYPE_BEACON: - ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, - rx_status); - break; - case IEEE80211_STYPE_PROBE_RESP: - ieee80211_rx_mgmt_probe_resp(sdata, skb); - break; - case IEEE80211_STYPE_DEAUTH: - rma = ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len); - break; - case IEEE80211_STYPE_DISASSOC: - rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len); - break; - case IEEE80211_STYPE_ACTION: - switch (mgmt->u.action.category) { - case WLAN_CATEGORY_SPECTRUM_MGMT: - ieee80211_sta_process_chanswitch(sdata, - &mgmt->u.action.u.chan_switch.sw_elem, - (void *)ifmgd->associated->priv, - rx_status->mactime); - break; - } - } - mutex_unlock(&ifmgd->mtx); - - switch (rma) { - case RX_MGMT_NONE: - /* no action */ - break; - case RX_MGMT_CFG80211_DEAUTH: - cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len); - break; - case RX_MGMT_CFG80211_DISASSOC: - cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len); + switch (fc & IEEE80211_FCTL_STYPE) { + case IEEE80211_STYPE_BEACON: + ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, rx_status); + break; + case IEEE80211_STYPE_PROBE_RESP: + ieee80211_rx_mgmt_probe_resp(sdata, skb); + break; + case IEEE80211_STYPE_AUTH: + rma = ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len); + break; + case IEEE80211_STYPE_DEAUTH: + rma = ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len); + break; + case IEEE80211_STYPE_DISASSOC: + rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len); + break; + case IEEE80211_STYPE_ASSOC_RESP: + case IEEE80211_STYPE_REASSOC_RESP: + rma = ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, &bss); + break; + case IEEE80211_STYPE_ACTION: + switch (mgmt->u.action.category) { + case WLAN_CATEGORY_SPECTRUM_MGMT: + ieee80211_sta_process_chanswitch(sdata, + &mgmt->u.action.u.chan_switch.sw_elem, + (void *)ifmgd->associated->priv, + rx_status->mactime); break; - default: - WARN(1, "unexpected: %d", rma); } - return; } - mutex_unlock(&ifmgd->mtx); - if (skb->len >= 24 + 2 /* mgmt + deauth reason */ && - (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_DEAUTH) { - struct ieee80211_local *local = sdata->local; - struct ieee80211_work *wk; - - mutex_lock(&local->mtx); - list_for_each_entry(wk, &local->work_list, list) { - if (wk->sdata != sdata) - continue; - - if (wk->type != IEEE80211_WORK_ASSOC && - wk->type != IEEE80211_WORK_ASSOC_BEACON_WAIT) - continue; - - if (memcmp(mgmt->bssid, wk->filter_ta, ETH_ALEN)) - continue; - if (memcmp(mgmt->sa, wk->filter_ta, ETH_ALEN)) - continue; - - /* - * Printing the message only here means we can't - * spuriously print it, but it also means that it - * won't be printed when the frame comes in before - * we even tried to associate or in similar cases. - * - * Ultimately, I suspect cfg80211 should print the - * messages instead. - */ - printk(KERN_DEBUG - "%s: deauthenticated from %pM (Reason: %u)\n", - sdata->name, mgmt->bssid, - le16_to_cpu(mgmt->u.deauth.reason_code)); - - list_del_rcu(&wk->list); - free_work(wk); - break; - } - mutex_unlock(&local->mtx); - + switch (rma) { + case RX_MGMT_NONE: + /* no action */ + break; + case RX_MGMT_CFG80211_DEAUTH: cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len); + break; + case RX_MGMT_CFG80211_DISASSOC: + cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len); + break; + case RX_MGMT_CFG80211_RX_AUTH: + cfg80211_send_rx_auth(sdata->dev, (u8 *)mgmt, skb->len); + break; + case RX_MGMT_CFG80211_RX_ASSOC: + cfg80211_send_rx_assoc(sdata->dev, bss, (u8 *)mgmt, skb->len); + break; + case RX_MGMT_CFG80211_ASSOC_TIMEOUT: + cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid); + break; + default: + WARN(1, "unexpected: %d", rma); } } @@ -2028,38 +2630,170 @@ 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; + u8 frame_buf[DEAUTH_DISASSOC_LEN]; ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL | IEEE80211_STA_BEACON_POLL); - ieee80211_set_disassoc(sdata, true, true); + ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason, + false, frame_buf); mutex_unlock(&ifmgd->mtx); - mutex_lock(&local->mtx); - ieee80211_recalc_idle(local); - mutex_unlock(&local->mtx); + /* * must be outside lock due to cfg80211, * but that's not a problem. */ - ieee80211_send_deauth_disassoc(sdata, bssid, - IEEE80211_STYPE_DEAUTH, - WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, - NULL, true); + cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN); + + mutex_lock(&local->mtx); + ieee80211_recalc_idle(local); + mutex_unlock(&local->mtx); + mutex_lock(&ifmgd->mtx); } +static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_mgd_auth_data *auth_data = ifmgd->auth_data; + + lockdep_assert_held(&ifmgd->mtx); + + if (WARN_ON_ONCE(!auth_data)) + return -EINVAL; + + auth_data->tries++; + + if (auth_data->tries > IEEE80211_AUTH_MAX_TRIES) { + printk(KERN_DEBUG "%s: authentication with %pM timed out\n", + sdata->name, auth_data->bss->bssid); + + /* + * Most likely AP is not in the range so remove the + * bss struct for that AP. + */ + cfg80211_unlink_bss(local->hw.wiphy, auth_data->bss); + + return -ETIMEDOUT; + } + + if (auth_data->bss->proberesp_ies) { + printk(KERN_DEBUG "%s: send auth to %pM (try %d/%d)\n", + sdata->name, auth_data->bss->bssid, auth_data->tries, + IEEE80211_AUTH_MAX_TRIES); + + auth_data->expected_transaction = 2; + ieee80211_send_auth(sdata, 1, auth_data->algorithm, + auth_data->ie, auth_data->ie_len, + auth_data->bss->bssid, + auth_data->bss->bssid, NULL, 0, 0); + } else { + const u8 *ssidie; + + printk(KERN_DEBUG "%s: direct probe to %pM (try %d/%i)\n", + sdata->name, auth_data->bss->bssid, auth_data->tries, + IEEE80211_AUTH_MAX_TRIES); + + ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID); + if (!ssidie) + return -EINVAL; + /* + * Direct probe is sent to broadcast address as some APs + * will not answer to direct packet in unassociated state. + */ + ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1], + NULL, 0, (u32) -1, true, false); + } + + auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; + run_again(ifmgd, auth_data->timeout); + + return 0; +} + +static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data; + struct ieee80211_local *local = sdata->local; + + lockdep_assert_held(&sdata->u.mgd.mtx); + + assoc_data->tries++; + if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) { + printk(KERN_DEBUG "%s: association with %pM timed out\n", + sdata->name, assoc_data->bss->bssid); + + /* + * Most likely AP is not in the range so remove the + * bss struct for that AP. + */ + cfg80211_unlink_bss(local->hw.wiphy, assoc_data->bss); + + return -ETIMEDOUT; + } + + printk(KERN_DEBUG "%s: associate with %pM (try %d/%d)\n", + sdata->name, assoc_data->bss->bssid, assoc_data->tries, + IEEE80211_ASSOC_MAX_TRIES); + ieee80211_send_assoc(sdata); + + assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT; + run_again(&sdata->u.mgd, assoc_data->timeout); + + return 0; +} + void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - /* then process the rest of the work */ mutex_lock(&ifmgd->mtx); + if (ifmgd->auth_data && + time_after(jiffies, ifmgd->auth_data->timeout)) { + if (ifmgd->auth_data->done) { + /* + * ok ... we waited for assoc but userspace didn't, + * so let's just kill the auth data + */ + ieee80211_destroy_auth_data(sdata, false); + } else if (ieee80211_probe_auth(sdata)) { + u8 bssid[ETH_ALEN]; + + memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN); + + ieee80211_destroy_auth_data(sdata, false); + + mutex_unlock(&ifmgd->mtx); + cfg80211_send_auth_timeout(sdata->dev, bssid); + mutex_lock(&ifmgd->mtx); + } + } else if (ifmgd->auth_data) + run_again(ifmgd, ifmgd->auth_data->timeout); + + if (ifmgd->assoc_data && + time_after(jiffies, ifmgd->assoc_data->timeout)) { + if (!ifmgd->assoc_data->have_beacon || + ieee80211_do_assoc(sdata)) { + u8 bssid[ETH_ALEN]; + + memcpy(bssid, ifmgd->assoc_data->bss->bssid, ETH_ALEN); + + ieee80211_destroy_assoc_data(sdata, false); + + mutex_unlock(&ifmgd->mtx); + cfg80211_send_assoc_timeout(sdata->dev, bssid); + mutex_lock(&ifmgd->mtx); + } + } else if (ifmgd->assoc_data) + run_again(ifmgd, ifmgd->assoc_data->timeout); + if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | IEEE80211_STA_CONNECTION_POLL) && ifmgd->associated) { @@ -2093,7 +2827,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 +2840,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,11 +2863,16 @@ 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); } } mutex_unlock(&ifmgd->mtx); + + mutex_lock(&local->mtx); + ieee80211_recalc_idle(local); + mutex_unlock(&local->mtx); } static void ieee80211_sta_bcn_mon_timer(unsigned long data) @@ -2171,13 +2912,17 @@ static void ieee80211_sta_monitor_work(struct work_struct *work) static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata) { + u32 flags; + if (sdata->vif.type == NL80211_IFTYPE_STATION) { sdata->u.mgd.flags &= ~(IEEE80211_STA_BEACON_POLL | IEEE80211_STA_CONNECTION_POLL); /* let's probe the connection once */ - ieee80211_queue_work(&sdata->local->hw, - &sdata->u.mgd.monitor_work); + flags = sdata->local->hw.flags; + if (!(flags & IEEE80211_HW_CONNECTION_MONITOR)) + ieee80211_queue_work(&sdata->local->hw, + &sdata->u.mgd.monitor_work); /* and do all the other regular work too */ ieee80211_queue_work(&sdata->local->hw, &sdata->work); } @@ -2196,6 +2941,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 +2950,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,6 +2962,24 @@ 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)) @@ -2247,6 +3010,9 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) (unsigned long) sdata); ifmgd->flags = 0; + ifmgd->powersave = sdata->wdev.ps; + ifmgd->uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES; + ifmgd->uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN; mutex_init(&ifmgd->mtx); @@ -2283,46 +3049,119 @@ int ieee80211_max_network_latency(struct notifier_block *nb, return 0; } -/* config hooks */ -static enum work_done_result -ieee80211_probe_auth_done(struct ieee80211_work *wk, - struct sk_buff *skb) +static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, + struct cfg80211_bss *cbss, bool assoc) { - if (!skb) { - cfg80211_send_auth_timeout(wk->sdata->dev, wk->filter_ta); - return WORK_DONE_DESTROY; + struct ieee80211_local *local = sdata->local; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_bss *bss = (void *)cbss->priv; + struct sta_info *sta; + bool have_sta = false; + int err; + + if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data)) + return -EINVAL; + + if (assoc) { + rcu_read_lock(); + have_sta = sta_info_get(sdata, cbss->bssid); + rcu_read_unlock(); } - if (wk->type == IEEE80211_WORK_AUTH) { - cfg80211_send_rx_auth(wk->sdata->dev, skb->data, skb->len); - return WORK_DONE_DESTROY; + if (!have_sta) { + sta = sta_info_alloc(sdata, cbss->bssid, GFP_KERNEL); + if (!sta) + return -ENOMEM; } - mutex_lock(&wk->sdata->u.mgd.mtx); - ieee80211_rx_mgmt_probe_resp(wk->sdata, skb); - mutex_unlock(&wk->sdata->u.mgd.mtx); + mutex_lock(&local->mtx); + ieee80211_recalc_idle(sdata->local); + mutex_unlock(&local->mtx); + + /* switch to the right channel */ + local->oper_channel = cbss->channel; + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); + + if (!have_sta) { + struct ieee80211_supported_band *sband; + u32 rates = 0, basic_rates = 0; + bool have_higher_than_11mbit; + int min_rate = INT_MAX, min_rate_index = -1; + + sband = sdata->local->hw.wiphy->bands[cbss->channel->band]; + + ieee80211_get_rates(sband, bss->supp_rates, + bss->supp_rates_len, + &rates, &basic_rates, + &have_higher_than_11mbit, + &min_rate, &min_rate_index); + + /* + * This used to be a workaround for basic rates missing + * in the association response frame. Now that we no + * longer use the basic rates from there, it probably + * doesn't happen any more, but keep the workaround so + * in case some *other* APs are buggy in different ways + * we can connect -- with a warning. + */ + if (!basic_rates && min_rate_index >= 0) { + printk(KERN_DEBUG + "%s: No basic rates, using min rate instead.\n", + sdata->name); + basic_rates = BIT(min_rate_index); + } + + sta->sta.supp_rates[cbss->channel->band] = rates; + sdata->vif.bss_conf.basic_rates = basic_rates; - wk->type = IEEE80211_WORK_AUTH; - wk->probe_auth.tries = 0; - return WORK_DONE_REQUEUE; + /* cf. IEEE 802.11 9.2.12 */ + if (local->oper_channel->band == IEEE80211_BAND_2GHZ && + have_higher_than_11mbit) + sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE; + else + sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; + + memcpy(ifmgd->bssid, cbss->bssid, ETH_ALEN); + + /* tell driver about BSSID and basic rates */ + ieee80211_bss_info_change_notify(sdata, + BSS_CHANGED_BSSID | BSS_CHANGED_BASIC_RATES); + + if (assoc) + sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); + + err = sta_info_insert(sta); + sta = NULL; + if (err) { + printk(KERN_DEBUG + "%s: failed to insert STA entry for the AP (error %d)\n", + sdata->name, err); + return err; + } + } else + WARN_ON_ONCE(compare_ether_addr(ifmgd->bssid, cbss->bssid)); + + return 0; } +/* config hooks */ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, struct cfg80211_auth_request *req) { - const u8 *ssid; - struct ieee80211_work *wk; + struct ieee80211_local *local = sdata->local; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_mgd_auth_data *auth_data; u16 auth_alg; + int err; - if (req->local_state_change) - return 0; /* no need to update mac80211 state */ + /* prepare auth data structure */ switch (req->auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: auth_alg = WLAN_AUTH_OPEN; break; case NL80211_AUTHTYPE_SHARED_KEY: - if (IS_ERR(sdata->local->wep_tx_tfm)) + if (IS_ERR(local->wep_tx_tfm)) return -EOPNOTSUPP; auth_alg = WLAN_AUTH_SHARED_KEY; break; @@ -2336,145 +3175,156 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, return -EOPNOTSUPP; } - wk = kzalloc(sizeof(*wk) + req->ie_len, GFP_KERNEL); - if (!wk) + auth_data = kzalloc(sizeof(*auth_data) + req->ie_len, GFP_KERNEL); + if (!auth_data) return -ENOMEM; - memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN); + auth_data->bss = req->bss; if (req->ie && req->ie_len) { - memcpy(wk->ie, req->ie, req->ie_len); - wk->ie_len = req->ie_len; + memcpy(auth_data->ie, req->ie, req->ie_len); + auth_data->ie_len = req->ie_len; } if (req->key && req->key_len) { - wk->probe_auth.key_len = req->key_len; - wk->probe_auth.key_idx = req->key_idx; - memcpy(wk->probe_auth.key, req->key, req->key_len); + auth_data->key_len = req->key_len; + auth_data->key_idx = req->key_idx; + memcpy(auth_data->key, req->key, req->key_len); } - ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); - memcpy(wk->probe_auth.ssid, ssid + 2, ssid[1]); - wk->probe_auth.ssid_len = ssid[1]; - - wk->probe_auth.algorithm = auth_alg; - wk->probe_auth.privacy = req->bss->capability & WLAN_CAPABILITY_PRIVACY; + auth_data->algorithm = auth_alg; - /* if we already have a probe, don't probe again */ - if (req->bss->proberesp_ies) - wk->type = IEEE80211_WORK_AUTH; - else - wk->type = IEEE80211_WORK_DIRECT_PROBE; - wk->chan = req->bss->channel; - wk->chan_type = NL80211_CHAN_NO_HT; - wk->sdata = sdata; - wk->done = ieee80211_probe_auth_done; - - ieee80211_add_work(wk); - return 0; -} + /* try to authenticate/probe */ -static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk, - struct sk_buff *skb) -{ - struct ieee80211_mgmt *mgmt; - struct ieee80211_rx_status *rx_status; - struct ieee802_11_elems elems; - u16 status; + mutex_lock(&ifmgd->mtx); - if (!skb) { - cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta); - return WORK_DONE_DESTROY; + if ((ifmgd->auth_data && !ifmgd->auth_data->done) || + ifmgd->assoc_data) { + err = -EBUSY; + goto err_free; } - if (wk->type == IEEE80211_WORK_ASSOC_BEACON_WAIT) { - mutex_lock(&wk->sdata->u.mgd.mtx); - rx_status = (void *) skb->cb; - ieee802_11_parse_elems(skb->data + 24 + 12, skb->len - 24 - 12, &elems); - ieee80211_rx_bss_info(wk->sdata, (void *)skb->data, skb->len, rx_status, - &elems, true); - mutex_unlock(&wk->sdata->u.mgd.mtx); + if (ifmgd->auth_data) + ieee80211_destroy_auth_data(sdata, false); - wk->type = IEEE80211_WORK_ASSOC; - /* not really done yet */ - return WORK_DONE_REQUEUE; - } + /* prep auth_data so we don't go into idle on disassoc */ + ifmgd->auth_data = auth_data; - mgmt = (void *)skb->data; - status = le16_to_cpu(mgmt->u.assoc_resp.status_code); + if (ifmgd->associated) + ieee80211_set_disassoc(sdata, 0, 0, false, NULL); - if (status == WLAN_STATUS_SUCCESS) { - 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 */ - cfg80211_send_assoc_timeout(wk->sdata->dev, - wk->filter_ta); - return WORK_DONE_DESTROY; - } + printk(KERN_DEBUG "%s: authenticate with %pM\n", + sdata->name, req->bss->bssid); + + err = ieee80211_prep_connection(sdata, req->bss, false); + if (err) + goto err_clear; - mutex_unlock(&wk->sdata->u.mgd.mtx); + err = ieee80211_probe_auth(sdata); + if (err) { + sta_info_destroy_addr(sdata, req->bss->bssid); + goto err_clear; } - cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len); - return WORK_DONE_DESTROY; + /* hold our own reference */ + cfg80211_ref_bss(auth_data->bss); + err = 0; + goto out_unlock; + + err_clear: + memset(ifmgd->bssid, 0, ETH_ALEN); + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); + ifmgd->auth_data = NULL; + err_free: + kfree(auth_data); + out_unlock: + mutex_unlock(&ifmgd->mtx); + + return err; } int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, struct cfg80211_assoc_request *req) { + struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_bss *bss = (void *)req->bss->priv; - struct ieee80211_work *wk; - const u8 *ssid; - int i; + struct ieee80211_mgd_assoc_data *assoc_data; + struct ieee80211_supported_band *sband; + const u8 *ssidie; + int i, err; + + ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); + if (!ssidie) + return -EINVAL; + + assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL); + if (!assoc_data) + return -ENOMEM; mutex_lock(&ifmgd->mtx); - if (ifmgd->associated) { - if (!req->prev_bssid || - memcmp(req->prev_bssid, ifmgd->associated->bssid, - ETH_ALEN)) { - /* - * We are already associated and the request was not a - * reassociation request from the current BSS, so - * reject it. - */ - mutex_unlock(&ifmgd->mtx); - return -EALREADY; - } - /* Trying to reassociate - clear previous association state */ - ieee80211_set_disassoc(sdata, true, false); + if (ifmgd->associated) + ieee80211_set_disassoc(sdata, 0, 0, false, NULL); + + if (ifmgd->auth_data && !ifmgd->auth_data->done) { + err = -EBUSY; + goto err_free; } - mutex_unlock(&ifmgd->mtx); - wk = kzalloc(sizeof(*wk) + req->ie_len, GFP_KERNEL); - if (!wk) - return -ENOMEM; + if (ifmgd->assoc_data) { + err = -EBUSY; + goto err_free; + } + + if (ifmgd->auth_data) { + bool match; + + /* keep sta info, bssid if matching */ + match = compare_ether_addr(ifmgd->bssid, req->bss->bssid) == 0; + ieee80211_destroy_auth_data(sdata, match); + } + + /* prepare assoc data */ ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N; ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED; ifmgd->beacon_crc_valid = false; + /* + * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode. + * We still associate in non-HT mode (11a/b/g) if any one of these + * ciphers is configured as pairwise. + * We can set this to true for non-11n hardware, that'll be checked + * separately along with the peer capabilities. + */ for (i = 0; i < req->crypto.n_ciphers_pairwise; i++) if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 || req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP || req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) ifmgd->flags |= IEEE80211_STA_DISABLE_11N; + if (req->flags & ASSOC_REQ_DISABLE_HT) + ifmgd->flags |= IEEE80211_STA_DISABLE_11N; - if (req->ie && req->ie_len) { - memcpy(wk->ie, req->ie, req->ie_len); - wk->ie_len = req->ie_len; - } else - wk->ie_len = 0; + /* Also disable HT if we don't support it or the AP doesn't use WMM */ + sband = local->hw.wiphy->bands[req->bss->channel->band]; + if (!sband->ht_cap.ht_supported || + local->hw.queues < 4 || !bss->wmm_used) + ifmgd->flags |= IEEE80211_STA_DISABLE_11N; - wk->assoc.bss = req->bss; + memcpy(&ifmgd->ht_capa, &req->ht_capa, sizeof(ifmgd->ht_capa)); + memcpy(&ifmgd->ht_capa_mask, &req->ht_capa_mask, + sizeof(ifmgd->ht_capa_mask)); - memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN); + if (req->ie && req->ie_len) { + memcpy(assoc_data->ie, req->ie, req->ie_len); + assoc_data->ie_len = req->ie_len; + } + + assoc_data->bss = req->bss; - /* new association always uses requested smps mode */ if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) { if (ifmgd->powersave) ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC; @@ -2483,47 +3333,27 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, } else ifmgd->ap_smps = ifmgd->req_smps; - wk->assoc.smps = ifmgd->ap_smps; - /* - * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode. - * We still associate in non-HT mode (11a/b/g) if any one of these - * ciphers is configured as pairwise. - * We can set this to true for non-11n hardware, that'll be checked - * separately along with the peer capabilities. - */ - wk->assoc.use_11n = !(ifmgd->flags & IEEE80211_STA_DISABLE_11N); - wk->assoc.capability = req->bss->capability; - wk->assoc.wmm_used = bss->wmm_used; - wk->assoc.supp_rates = bss->supp_rates; - wk->assoc.supp_rates_len = bss->supp_rates_len; - wk->assoc.ht_information_ie = + assoc_data->capability = req->bss->capability; + assoc_data->wmm = bss->wmm_used && (local->hw.queues >= 4); + assoc_data->supp_rates = bss->supp_rates; + assoc_data->supp_rates_len = bss->supp_rates_len; + assoc_data->ht_information_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_INFORMATION); if (bss->wmm_used && bss->uapsd_supported && (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) { - wk->assoc.uapsd_used = true; + assoc_data->uapsd = true; ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED; } else { - wk->assoc.uapsd_used = false; + assoc_data->uapsd = false; ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED; } - ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); - memcpy(wk->assoc.ssid, ssid + 2, ssid[1]); - wk->assoc.ssid_len = ssid[1]; + memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]); + assoc_data->ssid_len = ssidie[1]; if (req->prev_bssid) - memcpy(wk->assoc.prev_bssid, req->prev_bssid, ETH_ALEN); - - wk->chan = req->bss->channel; - wk->chan_type = NL80211_CHAN_NO_HT; - wk->sdata = sdata; - wk->done = ieee80211_assoc_done; - if (!bss->dtim_period && - sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) - wk->type = IEEE80211_WORK_ASSOC_BEACON_WAIT; - else - wk->type = IEEE80211_WORK_ASSOC; + memcpy(assoc_data->prev_bssid, req->prev_bssid, ETH_ALEN); if (req->use_mfp) { ifmgd->mfp = IEEE80211_MFP_REQUIRED; @@ -2541,75 +3371,88 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, sdata->control_port_protocol = req->crypto.control_port_ethertype; sdata->control_port_no_encrypt = req->crypto.control_port_no_encrypt; - ieee80211_add_work(wk); - return 0; -} + /* kick off associate process */ -int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, - struct cfg80211_deauth_request *req, - void *cookie) -{ - 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; + ifmgd->assoc_data = assoc_data; - mutex_lock(&ifmgd->mtx); + err = ieee80211_prep_connection(sdata, req->bss, true); + if (err) + goto err_clear; - memcpy(bssid, req->bss->bssid, ETH_ALEN); - if (ifmgd->associated == req->bss) { - ieee80211_set_disassoc(sdata, false, true); - mutex_unlock(&ifmgd->mtx); - assoc_bss = true; + if (!bss->dtim_period && + sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) { + /* + * Wait up to one beacon interval ... + * should this be more if we miss one? + */ + printk(KERN_DEBUG "%s: waiting for beacon from %pM\n", + sdata->name, ifmgd->bssid); + assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval); } else { - bool not_auth_yet = false; - - mutex_unlock(&ifmgd->mtx); + assoc_data->have_beacon = true; + assoc_data->sent_assoc = false; + assoc_data->timeout = jiffies; + } + run_again(ifmgd, assoc_data->timeout); + + if (bss->corrupt_data) { + char *corrupt_type = "data"; + if (bss->corrupt_data & IEEE80211_BSS_CORRUPT_BEACON) { + if (bss->corrupt_data & + IEEE80211_BSS_CORRUPT_PROBE_RESP) + corrupt_type = "beacon and probe response"; + else + corrupt_type = "beacon"; + } else if (bss->corrupt_data & IEEE80211_BSS_CORRUPT_PROBE_RESP) + corrupt_type = "probe response"; + printk(KERN_DEBUG "%s: associating with AP with corrupt %s\n", + sdata->name, corrupt_type); + } - mutex_lock(&local->mtx); - list_for_each_entry(wk, &local->work_list, list) { - if (wk->sdata != sdata) - continue; + err = 0; + goto out; + err_clear: + memset(ifmgd->bssid, 0, ETH_ALEN); + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); + ifmgd->assoc_data = NULL; + err_free: + kfree(assoc_data); + out: + mutex_unlock(&ifmgd->mtx); - if (wk->type != IEEE80211_WORK_DIRECT_PROBE && - wk->type != IEEE80211_WORK_AUTH && - wk->type != IEEE80211_WORK_ASSOC && - wk->type != IEEE80211_WORK_ASSOC_BEACON_WAIT) - continue; + return err; +} - if (memcmp(req->bss->bssid, wk->filter_ta, ETH_ALEN)) - continue; +int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, + struct cfg80211_deauth_request *req) +{ + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + u8 frame_buf[DEAUTH_DISASSOC_LEN]; - not_auth_yet = wk->type == IEEE80211_WORK_DIRECT_PROBE; - list_del_rcu(&wk->list); - free_work(wk); - break; - } - mutex_unlock(&local->mtx); + mutex_lock(&ifmgd->mtx); - /* - * If somebody requests authentication and we haven't - * sent out an auth frame yet there's no need to send - * out a deauth frame either. If the state was PROBE, - * then this is the case. If it's AUTH we have sent a - * frame, and if it's IDLE we have completed the auth - * process already. - */ - if (not_auth_yet) { - __cfg80211_auth_canceled(sdata->dev, bssid); - return 0; - } + if (ifmgd->auth_data) { + ieee80211_destroy_auth_data(sdata, false); + mutex_unlock(&ifmgd->mtx); + return 0; } - printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n", - sdata->name, bssid, req->reason_code); + printk(KERN_DEBUG + "%s: deauthenticating from %pM by local choice (reason=%d)\n", + sdata->name, req->bssid, req->reason_code); + + if (ifmgd->associated && + compare_ether_addr(ifmgd->associated->bssid, req->bssid) == 0) + ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, + req->reason_code, true, frame_buf); + else + ieee80211_send_deauth_disassoc(sdata, req->bssid, + IEEE80211_STYPE_DEAUTH, + req->reason_code, true, + frame_buf); + mutex_unlock(&ifmgd->mtx); - ieee80211_send_deauth_disassoc(sdata, bssid, IEEE80211_STYPE_DEAUTH, - req->reason_code, cookie, - !req->local_state_change); - if (assoc_bss) - sta_info_destroy_addr(sdata, bssid); + __cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN); mutex_lock(&sdata->local->mtx); ieee80211_recalc_idle(sdata->local); @@ -2619,11 +3462,11 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, } int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, - struct cfg80211_disassoc_request *req, - void *cookie) + struct cfg80211_disassoc_request *req) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 bssid[ETH_ALEN]; + u8 frame_buf[DEAUTH_DISASSOC_LEN]; mutex_lock(&ifmgd->mtx); @@ -2642,14 +3485,12 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, sdata->name, req->bss->bssid, req->reason_code); memcpy(bssid, req->bss->bssid, ETH_ALEN); - ieee80211_set_disassoc(sdata, false, true); - + ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DISASSOC, + req->reason_code, !req->local_state_change, + frame_buf); mutex_unlock(&ifmgd->mtx); - 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); + __cfg80211_send_disassoc(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN); mutex_lock(&sdata->local->mtx); ieee80211_recalc_idle(sdata->local); @@ -2658,6 +3499,19 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, return 0; } +void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + + mutex_lock(&ifmgd->mtx); + if (ifmgd->assoc_data) + ieee80211_destroy_assoc_data(sdata, false); + if (ifmgd->auth_data) + ieee80211_destroy_auth_data(sdata, false); + del_timer_sync(&ifmgd->timer); + mutex_unlock(&ifmgd->mtx); +} + void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, enum nl80211_cqm_rssi_threshold_event rssi_event, gfp_t gfp) @@ -2669,3 +3523,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..c22f074 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -12,6 +12,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include #include #include "ieee80211_i.h" #include "driver-trace.h" @@ -135,30 +136,15 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local) mutex_unlock(&local->iflist_mtx); } -void ieee80211_offchannel_enable_all_ps(struct ieee80211_local *local, - bool tell_ap) +void ieee80211_offchannel_return(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; 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); - } - mutex_unlock(&local->iflist_mtx); -} - -void ieee80211_offchannel_return(struct ieee80211_local *local, - bool enable_beaconing) -{ - struct ieee80211_sub_if_data *sdata; + if (sdata->vif.type != NL80211_IFTYPE_MONITOR) + clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); - mutex_lock(&local->iflist_mtx); - list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) continue; @@ -168,7 +154,6 @@ void ieee80211_offchannel_return(struct ieee80211_local *local, ieee80211_offchannel_ps_disable(sdata); if (sdata->vif.type != NL80211_IFTYPE_MONITOR) { - clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); /* * This may wake up queues even though the driver * currently has them stopped. This is not very @@ -182,11 +167,9 @@ void ieee80211_offchannel_return(struct ieee80211_local *local, netif_tx_wake_all_queues(sdata->dev); } - /* Check to see if we should re-enable beaconing */ - if (enable_beaconing && - (sdata->vif.type == NL80211_IFTYPE_AP || - sdata->vif.type == NL80211_IFTYPE_ADHOC || - sdata->vif.type == NL80211_IFTYPE_MESH_POINT)) + 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); } @@ -206,8 +189,6 @@ static void ieee80211_hw_roc_start(struct work_struct *work) return; } - ieee80211_recalc_idle(local); - if (local->hw_roc_skb) { sdata = IEEE80211_DEV_TO_SUB_IF(local->hw_roc_dev); ieee80211_tx_skb(sdata, local->hw_roc_skb); @@ -221,6 +202,8 @@ static void ieee80211_hw_roc_start(struct work_struct *work) GFP_KERNEL); } + ieee80211_recalc_idle(local); + mutex_unlock(&local->mtx); } diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 730778a..ef8eba1 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 */ @@ -65,13 +98,12 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { if (sta->uploaded) { - sdata = sta->sdata; - if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) - sdata = container_of(sdata->bss, - struct ieee80211_sub_if_data, - u.ap); + enum ieee80211_sta_state state; - drv_sta_remove(local, sdata, &sta->sta); + state = sta->sta_state; + for (; state > IEEE80211_STA_NOTEXIST; state--) + WARN_ON(drv_sta_state(local, sta->sdata, sta, + state, state - 1)); } mesh_plink_quiesce(sta); @@ -82,23 +114,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; @@ -107,7 +124,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); - drv_remove_interface(local, &sdata->vif); + drv_remove_interface(local, sdata); } /* stop hardware - this must stop RX */ diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 816590b..3313c11 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "rate.h" #include "ieee80211_i.h" #include "debugfs.h" @@ -144,7 +145,7 @@ static ssize_t rcname_read(struct file *file, char __user *userbuf, static const struct file_operations rcname_ops = { .read = rcname_read, - .open = mac80211_open_file_generic, + .open = simple_open, .llseek = default_llseek, }; #endif @@ -158,7 +159,6 @@ static struct rate_control_ref *rate_control_alloc(const char *name, ref = kmalloc(sizeof(struct rate_control_ref), GFP_KERNEL); if (!ref) goto fail_ref; - kref_init(&ref->kref); ref->local = local; ref->ops = ieee80211_rate_control_ops_get(name); if (!ref->ops) @@ -183,11 +183,8 @@ fail_ref: return NULL; } -static void rate_control_release(struct kref *kref) +static void rate_control_free(struct rate_control_ref *ctrl_ref) { - struct rate_control_ref *ctrl_ref; - - ctrl_ref = container_of(kref, struct rate_control_ref, kref); ctrl_ref->ops->free(ctrl_ref->priv); #ifdef CONFIG_MAC80211_DEBUGFS @@ -199,7 +196,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 +205,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 +232,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 +261,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; @@ -263,8 +289,8 @@ bool rate_control_send_low(struct ieee80211_sta *sta, } EXPORT_SYMBOL(rate_control_send_low); -static void rate_idx_match_mask(struct ieee80211_tx_rate *rate, - int n_bitrates, u32 mask) +static bool rate_idx_match_legacy_mask(struct ieee80211_tx_rate *rate, + int n_bitrates, u32 mask) { int j; @@ -273,7 +299,7 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate, if (mask & (1 << j)) { /* Okay, found a suitable rate. Use it. */ rate->idx = j; - return; + return true; } } @@ -282,6 +308,112 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate, if (mask & (1 << j)) { /* Okay, found a suitable rate. Use it. */ rate->idx = j; + return true; + } + } + return false; +} + +static bool rate_idx_match_mcs_mask(struct ieee80211_tx_rate *rate, + u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN]) +{ + int i, j; + int ridx, rbit; + + ridx = rate->idx / 8; + rbit = rate->idx % 8; + + /* sanity check */ + if (ridx < 0 || ridx >= IEEE80211_HT_MCS_MASK_LEN) + return false; + + /* See whether the selected rate or anything below it is allowed. */ + for (i = ridx; i >= 0; i--) { + for (j = rbit; j >= 0; j--) + if (mcs_mask[i] & BIT(j)) { + rate->idx = i * 8 + j; + return true; + } + rbit = 7; + } + + /* Try to find a higher rate that would be allowed */ + ridx = (rate->idx + 1) / 8; + rbit = (rate->idx + 1) % 8; + + for (i = ridx; i < IEEE80211_HT_MCS_MASK_LEN; i++) { + for (j = rbit; j < 8; j++) + if (mcs_mask[i] & BIT(j)) { + rate->idx = i * 8 + j; + return true; + } + rbit = 0; + } + return false; +} + + + +static void rate_idx_match_mask(struct ieee80211_tx_rate *rate, + struct ieee80211_tx_rate_control *txrc, + u32 mask, + u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN]) +{ + struct ieee80211_tx_rate alt_rate; + + /* handle HT rates */ + if (rate->flags & IEEE80211_TX_RC_MCS) { + if (rate_idx_match_mcs_mask(rate, mcs_mask)) + return; + + /* also try the legacy rates. */ + alt_rate.idx = 0; + /* keep protection flags */ + alt_rate.flags = rate->flags & + (IEEE80211_TX_RC_USE_RTS_CTS | + IEEE80211_TX_RC_USE_CTS_PROTECT | + IEEE80211_TX_RC_USE_SHORT_PREAMBLE); + alt_rate.count = rate->count; + if (rate_idx_match_legacy_mask(&alt_rate, + txrc->sband->n_bitrates, + mask)) { + *rate = alt_rate; + return; + } + } else { + struct sk_buff *skb = txrc->skb; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + __le16 fc; + + /* handle legacy rates */ + if (rate_idx_match_legacy_mask(rate, txrc->sband->n_bitrates, + mask)) + return; + + /* if HT BSS, and we handle a data frame, also try HT rates */ + if (txrc->bss_conf->channel_type == NL80211_CHAN_NO_HT) + return; + + fc = hdr->frame_control; + if (!ieee80211_is_data(fc)) + return; + + alt_rate.idx = 0; + /* keep protection flags */ + alt_rate.flags = rate->flags & + (IEEE80211_TX_RC_USE_RTS_CTS | + IEEE80211_TX_RC_USE_CTS_PROTECT | + IEEE80211_TX_RC_USE_SHORT_PREAMBLE); + alt_rate.count = rate->count; + + alt_rate.flags |= IEEE80211_TX_RC_MCS; + + if ((txrc->bss_conf->channel_type == NL80211_CHAN_HT40MINUS) || + (txrc->bss_conf->channel_type == NL80211_CHAN_HT40PLUS)) + alt_rate.flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + + if (rate_idx_match_mcs_mask(&alt_rate, mcs_mask)) { + *rate = alt_rate; return; } } @@ -305,8 +437,9 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb); int i; u32 mask; + u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN]; - if (sta) { + if (sta && test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) { ista = &sta->sta; priv_sta = sta->rate_ctrl_priv; } @@ -328,10 +461,14 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata, * the common case. */ mask = sdata->rc_rateidx_mask[info->band]; + memcpy(mcs_mask, sdata->rc_rateidx_mcs_mask[info->band], + sizeof(mcs_mask)); if (mask != (1 << txrc->sband->n_bitrates) - 1) { if (sta) { /* Filter out rates that the STA does not support */ mask &= sta->sta.supp_rates[info->band]; + for (i = 0; i < sizeof(mcs_mask); i++) + mcs_mask[i] &= sta->sta.ht_cap.mcs.rx_mask[i]; } /* * Make sure the rate index selected for each TX rate is @@ -342,32 +479,18 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata, /* Skip invalid rates */ if (info->control.rates[i].idx < 0) break; - /* Rate masking supports only legacy rates for now */ - if (info->control.rates[i].flags & IEEE80211_TX_RC_MCS) - continue; - rate_idx_match_mask(&info->control.rates[i], - txrc->sband->n_bitrates, mask); + rate_idx_match_mask(&info->control.rates[i], txrc, + mask, mcs_mask); } } BUG_ON(info->control.rates[0].idx < 0); } -struct rate_control_ref *rate_control_get(struct rate_control_ref *ref) -{ - kref_get(&ref->kref); - return ref; -} - -void rate_control_put(struct rate_control_ref *ref) -{ - kref_put(&ref->kref, rate_control_release); -} - int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local, const char *name) { - struct rate_control_ref *ref, *old; + struct rate_control_ref *ref; ASSERT_RTNL(); @@ -387,12 +510,8 @@ int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local, return -ENOENT; } - old = local->rate_ctrl; + WARN_ON(local->rate_ctrl); local->rate_ctrl = ref; - if (old) { - rate_control_put(old); - sta_info_flush(local, NULL); - } wiphy_debug(local->hw.wiphy, "Selected rate control algorithm '%s'\n", ref->ops->name); @@ -410,6 +529,6 @@ void rate_control_deinitialize(struct ieee80211_local *local) return; local->rate_ctrl = NULL; - rate_control_put(ref); + rate_control_free(ref); } diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index 168427b..fbb1efd 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -14,7 +14,6 @@ #include #include #include -#include #include #include "ieee80211_i.h" #include "sta_info.h" @@ -23,14 +22,11 @@ struct rate_control_ref { struct ieee80211_local *local; struct rate_control_ops *ops; void *priv; - struct kref kref; }; void rate_control_get_rate(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct ieee80211_tx_rate_control *txrc); -struct rate_control_ref *rate_control_get(struct rate_control_ref *ref); -void rate_control_put(struct rate_control_ref *ref); static inline void rate_control_tx_status(struct ieee80211_local *local, struct ieee80211_supported_band *sband, @@ -41,7 +37,7 @@ static inline void rate_control_tx_status(struct ieee80211_local *local, struct ieee80211_sta *ista = &sta->sta; void *priv_sta = sta->rate_ctrl_priv; - if (!ref) + if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) return; ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb); @@ -62,6 +58,7 @@ static inline void rate_control_rate_init(struct sta_info *sta) sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; ref->ops->rate_init(ref->priv, sband, ista, priv_sta); + set_sta_flag(sta, WLAN_STA_RATE_CONTROL); } static inline void rate_control_rate_update(struct ieee80211_local *local, diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 8adac67..b39dda5 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -334,8 +334,8 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta, static void -calc_rate_durations(struct minstrel_sta_info *mi, struct ieee80211_local *local, - struct minstrel_rate *d, struct ieee80211_rate *rate) +calc_rate_durations(struct ieee80211_local *local, struct minstrel_rate *d, + struct ieee80211_rate *rate) { int erp = !!(rate->flags & IEEE80211_RATE_ERP_G); @@ -402,8 +402,7 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband, mr->rix = i; mr->bitrate = sband->bitrates[i].bitrate / 5; - calc_rate_durations(mi, local, mr, - &sband->bitrates[i]); + calc_rate_durations(local, mr, &sband->bitrates[i]); /* calculate maximum number of retransmissions before * fallback (based on maximum segment size) */ @@ -532,12 +531,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 #include #include +#include #include #include "rc80211_minstrel.h" diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 333b511..16e0b27 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -36,8 +36,17 @@ /* Transmit duration for the raw data part of an average sized packet */ #define MCS_DURATION(streams, sgi, bps) MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps))) +/* + * Define group sort order: HT40 -> SGI -> #streams + */ +#define GROUP_IDX(_streams, _sgi, _ht40) \ + MINSTREL_MAX_STREAMS * 2 * _ht40 + \ + MINSTREL_MAX_STREAMS * _sgi + \ + _streams - 1 + /* MCS rate information for an MCS group */ -#define MCS_GROUP(_streams, _sgi, _ht40) { \ +#define MCS_GROUP(_streams, _sgi, _ht40) \ + [GROUP_IDX(_streams, _sgi, _ht40)] = { \ .streams = _streams, \ .flags = \ (_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \ @@ -58,6 +67,9 @@ * To enable sufficiently targeted rate sampling, MCS rates are divided into * groups, based on the number of streams and flags (HT40, SGI) that they * use. + * + * Sortorder has to be fixed for GROUP_IDX macro to be applicable: + * HT40 -> SGI -> #streams */ const struct mcs_group minstrel_mcs_groups[] = { MCS_GROUP(1, 0, 0), @@ -102,21 +114,9 @@ minstrel_ewma(int old, int new, int weight) static int minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate) { - int streams = (rate->idx / MCS_GROUP_RATES) + 1; - u32 flags = IEEE80211_TX_RC_SHORT_GI | IEEE80211_TX_RC_40_MHZ_WIDTH; - int i; - - for (i = 0; i < ARRAY_SIZE(minstrel_mcs_groups); i++) { - if (minstrel_mcs_groups[i].streams != streams) - continue; - if (minstrel_mcs_groups[i].flags != (rate->flags & flags)) - continue; - - return i; - } - - WARN_ON(1); - return 0; + return GROUP_IDX((rate->idx / MCS_GROUP_RATES) + 1, + !!(rate->flags & IEEE80211_TX_RC_SHORT_GI), + !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)); } static inline struct minstrel_rate_stats * @@ -130,7 +130,7 @@ minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index) * Recalculate success probabilities and counters for a rate using EWMA */ static void -minstrel_calc_rate_ewma(struct minstrel_priv *mp, struct minstrel_rate_stats *mr) +minstrel_calc_rate_ewma(struct minstrel_rate_stats *mr) { if (unlikely(mr->attempts > 0)) { mr->sample_skipped = 0; @@ -156,8 +156,7 @@ minstrel_calc_rate_ewma(struct minstrel_priv *mp, struct minstrel_rate_stats *mr * the expected number of retransmissions and their expected length */ static void -minstrel_ht_calc_tp(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, - int group, int rate) +minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate) { struct minstrel_rate_stats *mr; unsigned int usecs; @@ -226,8 +225,8 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) mr = &mg->rates[i]; mr->retry_updated = false; index = MCS_GROUP_RATES * group + i; - minstrel_calc_rate_ewma(mp, mr); - minstrel_ht_calc_tp(mp, mi, group, i); + minstrel_calc_rate_ewma(mr); + minstrel_ht_calc_tp(mi, group, i); if (!mr->cur_tp) continue; @@ -281,6 +280,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; } @@ -298,10 +299,10 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) static bool minstrel_ht_txstat_valid(struct ieee80211_tx_rate *rate) { - if (!rate->count) + if (rate->idx < 0) return false; - if (rate->idx < 0) + if (!rate->count) return false; return !!(rate->flags & IEEE80211_TX_RC_MCS); @@ -355,7 +356,7 @@ minstrel_downgrade_rate(struct minstrel_ht_sta *mi, unsigned int *idx, } static void -minstrel_aggr_check(struct minstrel_priv *mp, struct ieee80211_sta *pubsta, struct sk_buff *skb) +minstrel_aggr_check(struct ieee80211_sta *pubsta, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct sta_info *sta = container_of(pubsta, struct sta_info, sta); @@ -452,7 +453,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(sta, skb); } } @@ -512,7 +514,6 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, static void minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, struct ieee80211_tx_rate *rate, int index, - struct ieee80211_tx_rate_control *txrc, bool sample, bool rtscts) { const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; @@ -567,6 +568,13 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) minstrel_next_sample_idx(mi); /* + * Sampling might add some overhead (RTS, no aggregation) + * to the frame. Hence, don't use sampling for the currently + * used max TP rate. + */ + if (sample_idx == mi->max_tp_rate) + return -1; + /* * When not using MRR, do not sample if the probability is already * higher than 95% to avoid wasting airtime */ @@ -608,15 +616,28 @@ 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, - txrc, true, false); + true, false); info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; } else { minstrel_ht_set_rate(mp, mi, &ar[0], mi->max_tp_rate, - txrc, false, false); + false, false); } if (mp->hw->max_rates >= 3) { @@ -627,13 +648,13 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, */ if (sample_idx >= 0) minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_tp_rate, - txrc, false, false); + false, false); else minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_tp_rate2, - txrc, false, true); + false, true); minstrel_ht_set_rate(mp, mi, &ar[2], mi->max_prob_rate, - txrc, false, !sample); + false, !sample); ar[3].count = 0; ar[3].idx = -1; @@ -644,7 +665,7 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, * max_tp_rate -> max_prob_rate by default. */ minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_prob_rate, - txrc, false, !sample); + false, !sample); ar[2].count = 0; ar[2].idx = -1; @@ -678,6 +699,7 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, int ack_dur; int stbc; int i; + unsigned int smps; /* fall back to the old minstrel for legacy stations */ if (!sta->ht_cap.ht_supported) @@ -717,6 +739,9 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, oper_chan_type != NL80211_CHAN_HT40PLUS) sta_cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + smps = (sta_cap & IEEE80211_HT_CAP_SM_PS) >> + IEEE80211_HT_CAP_SM_PS_SHIFT; + for (i = 0; i < ARRAY_SIZE(mi->groups); i++) { u16 req = 0; @@ -734,6 +759,11 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, if ((sta_cap & req) != req) continue; + /* Mark MCS > 7 as unsupported if STA is in static SMPS mode */ + if (smps == WLAN_HT_CAP_SM_PS_STATIC && + minstrel_mcs_groups[i].streams > 1) + continue; + mi->groups[i].supported = mcs->rx_mask[minstrel_mcs_groups[i].streams - 1]; 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 #include #include +#include #include #include "rc80211_minstrel.h" #include "rc80211_minstrel_ht.h" diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c index aeda654..502d3ec 100644 --- a/net/mac80211/rc80211_pid_algo.c +++ b/net/mac80211/rc80211_pid_algo.c @@ -318,7 +318,7 @@ rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband, rinfo[i].diff = i * pinfo->norm_offset; } for (i = 1; i < sband->n_bitrates; i++) { - s = 0; + s = false; for (j = 0; j < sband->n_bitrates - i; j++) if (unlikely(sband->bitrates[rinfo[j].index].bitrate > sband->bitrates[rinfo[j + 1].index].bitrate)) { @@ -327,7 +327,7 @@ rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband, rinfo[j + 1].index = tmp; rinfo[rinfo[j].index].rev_index = j; rinfo[rinfo[j + 1].index].rev_index = j + 1; - s = 1; + s = true; } if (!s) break; 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 #include #include +#include #include #include "rate.h" diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 10e8842..6202d05 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -16,8 +16,10 @@ #include #include #include +#include #include #include +#include #include "ieee80211_i.h" #include "driver-ops.h" @@ -27,6 +29,7 @@ #include "wpa.h" #include "tkip.h" #include "wme.h" +#include "rate.h" /* * monitor mode reception @@ -100,7 +103,7 @@ static void ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, struct sk_buff *skb, struct ieee80211_rate *rate, - int rtap_len) + int rtap_len, bool has_fcs) { struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_radiotap_header *rthdr; @@ -131,7 +134,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, } /* IEEE80211_RADIOTAP_FLAGS */ - if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) + if (has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)) *pos |= IEEE80211_RADIOTAP_F_FCS; if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC)) *pos |= IEEE80211_RADIOTAP_F_BADFCS; @@ -174,7 +177,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, pos += 2; /* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */ - if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) { + if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM && + !(status->flag & RX_FLAG_NO_SIGNAL_VAL)) { *pos = status->signal; rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL); @@ -224,7 +228,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, { struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb); struct ieee80211_sub_if_data *sdata; - int needed_headroom = 0; + int needed_headroom; struct sk_buff *skb, *skb2; struct net_device *prev_dev = NULL; int present_fcs_len = 0; @@ -290,7 +294,8 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, } /* prepend radiotap information */ - ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom); + ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom, + true); skb_reset_mac_header(skb); skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -334,15 +339,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 +363,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; @@ -471,7 +484,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)) { @@ -479,12 +491,12 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) if (ieee80211_has_tods(hdr->frame_control) || !ieee80211_has_fromds(hdr->frame_control)) return RX_DROP_MONITOR; - if (memcmp(hdr->addr3, dev_addr, ETH_ALEN) == 0) + if (compare_ether_addr(hdr->addr3, dev_addr) == 0) return RX_DROP_MONITOR; } else { if (!ieee80211_has_a4(hdr->frame_control)) return RX_DROP_MONITOR; - if (memcmp(hdr->addr4, dev_addr, ETH_ALEN) == 0) + if (compare_ether_addr(hdr->addr4, dev_addr) == 0) return RX_DROP_MONITOR; } } @@ -501,6 +513,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 +536,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 +668,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); } @@ -748,12 +758,14 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx) struct ieee80211_local *local = rx->local; struct ieee80211_hw *hw = &local->hw; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct sta_info *sta = rx->sta; struct tid_ampdu_rx *tid_agg_rx; u16 sc; - int tid; + u8 tid, ack_policy; - 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; /* @@ -764,6 +776,8 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx) if (!sta) goto dont_reorder; + ack_policy = *ieee80211_get_qos_ctl(hdr) & + IEEE80211_QOS_CTL_ACK_POLICY_MASK; tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]); @@ -774,6 +788,15 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx) if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC))) goto dont_reorder; + /* not part of a BA session */ + if (ack_policy != IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK && + ack_policy != IEEE80211_QOS_CTL_ACK_POLICY_NORMAL) + goto dont_reorder; + + /* not actually part of this BA session */ + if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) + goto dont_reorder; + /* new, potentially un-ordered, ampdu frame - process it */ /* reset session timer */ @@ -819,7 +842,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 +850,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 +874,35 @@ 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)))) { + /* + * accept port control frames from the AP even when it's not + * yet marked ASSOC to prevent a race where we don't set the + * assoc bit quickly enough before it sends the first frame + */ + if (rx->sta && rx->sdata->vif.type == NL80211_IFTYPE_STATION && + 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; + } + + if (rx->sdata->vif.type == NL80211_IFTYPE_AP && + cfg80211_rx_spurious_frame(rx->sdata->dev, + hdr->addr2, + GFP_ATOMIC)) + return RX_DROP_UNUSABLE; + return RX_DROP_MONITOR; + } return RX_CONTINUE; } @@ -1020,26 +1070,18 @@ 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 { return RX_DROP_MONITOR; } - if (skb_linearize(rx->skb)) - return RX_DROP_UNUSABLE; - /* the hdr variable is invalid now! */ - switch (rx->key->conf.cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: - /* Check for weak IVs if possible */ - if (rx->sta && ieee80211_is_data(fc) && - (!(status->flag & RX_FLAG_IV_STRIPPED) || - !(status->flag & RX_FLAG_DECRYPTED)) && - ieee80211_wep_is_weak_iv(rx->skb, rx->key)) - rx->sta->wep_weak_iv_count++; - result = ieee80211_crypto_wep_decrypt(rx); break; case WLAN_CIPHER_SUITE_TKIP: @@ -1059,6 +1101,8 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) return RX_DROP_UNUSABLE; } + /* the hdr variable is invalid after the decrypt handlers */ + /* either the frame has been decrypted or will be dropped */ status->flag |= RX_FLAG_DECRYPTED; @@ -1104,7 +1148,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 @@ -1115,19 +1159,15 @@ static void ap_sta_ps_start(struct sta_info *sta) static void ap_sta_ps_end(struct sta_info *sta) { - struct ieee80211_sub_if_data *sdata = sta->sdata; - - atomic_dec(&sdata->bss->num_sta_ps); - #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG printk(KERN_DEBUG "%s: STA %pM aid %d exits power save mode\n", - sdata->name, sta->sta.addr, sta->sta.aid); + 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); + sta->sdata->name, sta->sta.addr, sta->sta.aid); #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ return; } @@ -1143,7 +1183,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 +1197,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; @@ -1202,8 +1317,10 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) sta->rx_fragments++; sta->rx_bytes += rx->skb->len; - sta->last_signal = status->signal; - ewma_add(&sta->avg_signal, -status->signal); + if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) { + sta->last_signal = status->signal; + ewma_add(&sta->avg_signal, -status->signal); + } /* * Change STA power saving mode only at the end of a frame @@ -1214,7 +1331,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 @@ -1242,15 +1359,20 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) /* * If we receive a 4-addr nullfunc frame from a STA - * that was not moved to a 4-addr STA vlan yet, drop - * the frame to the monitor interface, to make sure - * that hostapd sees it + * that was not moved to a 4-addr STA vlan yet send + * the event to userspace and for older hostapd drop + * the frame to the monitor interface. */ if (ieee80211_has_a4(hdr->frame_control) && (rx->sdata->vif.type == NL80211_IFTYPE_AP || (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && - !rx->sdata->u.vlan.sta))) + !rx->sdata->u.vlan.sta))) { + if (!test_and_set_sta_flag(sta, WLAN_STA_4ADDR_EVENT)) + cfg80211_rx_unexpected_4addr_frame( + rx->sdata->dev, sta->sta.addr, + GFP_ATOMIC); return RX_DROP_MONITOR; + } /* * Update counter and free packet here to avoid * counting this as a dropped packed. @@ -1365,11 +1487,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 +1511,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 +1528,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 +1549,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,58 +1585,10 @@ 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; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)data; - - if (!ieee80211_is_data_qos(hdr->frame_control)) - return RX_CONTINUE; - - /* remove the qos control field, update frame type and meta-data */ - memmove(data + IEEE80211_QOS_CTL_LEN, data, - ieee80211_hdrlen(hdr->frame_control) - IEEE80211_QOS_CTL_LEN); - hdr = (struct ieee80211_hdr *)skb_pull(rx->skb, IEEE80211_QOS_CTL_LEN); - /* change frame type to non QOS */ - hdr->frame_control &= ~cpu_to_le16(IEEE80211_STYPE_QOS_DATA); - return RX_CONTINUE; } @@ -1520,7 +1596,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 +1639,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)) { @@ -1748,7 +1824,12 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx) } if (xmit_skb) { - /* send to wireless media */ + /* + * Send to wireless media and increase priority by 256 to + * keep the received priority instead of reclassifying + * the frame (see cfg80211_classify8021d). + */ + xmit_skb->priority += 256; xmit_skb->protocol = htons(ETH_P_802_3); skb_reset_network_header(xmit_skb); skb_reset_mac_header(xmit_skb); @@ -1817,23 +1898,49 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) static ieee80211_rx_result ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) { - struct ieee80211_hdr *hdr; + struct ieee80211_hdr *fwd_hdr, *hdr; + struct ieee80211_tx_info *info; struct ieee80211s_hdr *mesh_hdr; - unsigned int hdrlen; struct sk_buff *skb = rx->skb, *fwd_skb; struct ieee80211_local *local = rx->local; struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + __le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_NOFORWARD); + u16 q, hdrlen; 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 (mesh_hdr->flags & MESH_FLAGS_AE) { @@ -1844,9 +1951,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(); @@ -1867,60 +1977,51 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) compare_ether_addr(sdata->vif.addr, hdr->addr3) == 0) return RX_CONTINUE; - mesh_hdr->ttl--; + q = ieee80211_select_queue_80211(local, skb, hdr); + if (ieee80211_queue_stopped(&local->hw, q)) { + IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_congestion); + return RX_DROP_MONITOR; + } + skb_set_queue_mapping(skb, q); - if (status->rx_flags & IEEE80211_RX_RA_MATCH) { - if (!mesh_hdr->ttl) - IEEE80211_IFSTA_MESH_CTR_INC(&rx->sdata->u.mesh, - dropped_frames_ttl); - else { - struct ieee80211_hdr *fwd_hdr; - struct ieee80211_tx_info *info; - - fwd_skb = skb_copy(skb, GFP_ATOMIC); - - if (!fwd_skb && net_ratelimit()) - printk(KERN_DEBUG "%s: failed to clone mesh frame\n", - sdata->name); - if (!fwd_skb) - goto out; - - fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data; - memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN); - info = IEEE80211_SKB_CB(fwd_skb); - 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)) - IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, - fwded_mcast); - else { - int err; - /* - * Save TA to addr1 to send TA a path error if a - * suitable next hop is not found - */ - memcpy(fwd_hdr->addr1, fwd_hdr->addr2, - ETH_ALEN); - err = mesh_nexthop_lookup(fwd_skb, sdata); - /* Failed to immediately resolve next hop: - * fwded frame was dropped or will be added - * later to the pending skb queue. */ - if (err) - return RX_DROP_MONITOR; - - IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, - fwded_unicast); - } - IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, - fwded_frames); - ieee80211_add_pending_skb(local, fwd_skb); - } + if (!--mesh_hdr->ttl) { + IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_ttl); + return RX_DROP_MONITOR; + } + + if (!ifmsh->mshcfg.dot11MeshForwarding) + goto out; + + fwd_skb = skb_copy(skb, GFP_ATOMIC); + if (!fwd_skb) { + if (net_ratelimit()) + printk(KERN_DEBUG "%s: failed to clone mesh frame\n", + sdata->name); + goto out; + } + + fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data; + info = IEEE80211_SKB_CB(fwd_skb); + memset(info, 0, sizeof(*info)); + info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; + info->control.vif = &rx->sdata->vif; + info->control.jiffies = jiffies; + if (is_multicast_ether_addr(fwd_hdr->addr1)) { + IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_mcast); + memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN); + } else if (!mesh_nexthop_lookup(fwd_skb, sdata)) { + IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast); + } else { + /* unable to resolve next hop */ + mesh_path_error_tx(ifmsh->mshcfg.element_ttl, fwd_hdr->addr3, + 0, reason, fwd_hdr->addr2, sdata); + IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route); + kfree_skb(fwd_skb); + return RX_DROP_MONITOR; } + IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames); + ieee80211_add_pending_skb(local, fwd_skb); out: if (is_multicast_ether_addr(hdr->addr1) || sdata->dev->flags & IFF_PROMISC) @@ -1948,12 +2049,17 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx) return RX_DROP_MONITOR; /* - * Allow the cooked monitor interface of an AP to see 4-addr frames so - * that a 4-addr station can be detected and moved into a separate VLAN + * Send unexpected-4addr-frame event to hostapd. For older versions, + * also drop the frame to cooked monitor interfaces. */ if (ieee80211_has_a4(hdr->frame_control) && - sdata->vif.type == NL80211_IFTYPE_AP) + sdata->vif.type == NL80211_IFTYPE_AP) { + if (rx->sta && + !test_and_set_sta_flag(rx->sta, WLAN_STA_4ADDR_EVENT)) + cfg80211_rx_unexpected_4addr_frame( + rx->sdata->dev, rx->sta->sta.addr, GFP_ATOMIC); return RX_DROP_MONITOR; + } err = __ieee80211_data_to_8023(rx, &port_control); if (unlikely(err)) @@ -2108,6 +2214,20 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx) if (!ieee80211_is_mgmt(mgmt->frame_control)) return RX_DROP_MONITOR; + if (rx->sdata->vif.type == NL80211_IFTYPE_AP && + ieee80211_is_beacon(mgmt->frame_control) && + !(rx->flags & IEEE80211_RX_BEACON_REPORTED)) { + int sig = 0; + + if (rx->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) + sig = status->signal; + + cfg80211_report_obss_beacon(rx->local->hw.wiphy, + rx->skb->data, rx->skb->len, + status->freq, sig, GFP_ATOMIC); + rx->flags |= IEEE80211_RX_BEACON_REPORTED; + } + if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) return RX_DROP_MONITOR; @@ -2140,16 +2260,71 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) return RX_DROP_UNUSABLE; switch (mgmt->u.action.category) { + case WLAN_CATEGORY_HT: + /* reject HT action frames from stations not supporting HT */ + if (!rx->sta->sta.ht_cap.ht_supported) + goto invalid; + + if (sdata->vif.type != NL80211_IFTYPE_STATION && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT && + sdata->vif.type != NL80211_IFTYPE_AP_VLAN && + sdata->vif.type != NL80211_IFTYPE_AP && + sdata->vif.type != NL80211_IFTYPE_ADHOC) + break; + + /* verify action & smps_control are present */ + if (len < IEEE80211_MIN_ACTION_SIZE + 2) + goto invalid; + + switch (mgmt->u.action.u.ht_smps.action) { + case WLAN_HT_ACTION_SMPS: { + struct ieee80211_supported_band *sband; + u8 smps; + + /* convert to HT capability */ + switch (mgmt->u.action.u.ht_smps.smps_control) { + case WLAN_HT_SMPS_CONTROL_DISABLED: + smps = WLAN_HT_CAP_SM_PS_DISABLED; + break; + case WLAN_HT_SMPS_CONTROL_STATIC: + smps = WLAN_HT_CAP_SM_PS_STATIC; + break; + case WLAN_HT_SMPS_CONTROL_DYNAMIC: + smps = WLAN_HT_CAP_SM_PS_DYNAMIC; + break; + default: + goto invalid; + } + smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT; + + /* if no change do nothing */ + if ((rx->sta->sta.ht_cap.cap & + IEEE80211_HT_CAP_SM_PS) == smps) + goto handled; + + rx->sta->sta.ht_cap.cap &= ~IEEE80211_HT_CAP_SM_PS; + rx->sta->sta.ht_cap.cap |= smps; + + sband = rx->local->hw.wiphy->bands[status->band]; + + rate_control_rate_update( + local, sband, rx->sta, + IEEE80211_RC_SMPS_CHANGED, + ieee80211_get_tx_channel_type( + local, local->_oper_channel_type)); + goto handled; + } + default: + goto invalid; + } + + break; case WLAN_CATEGORY_BACK: - /* - * The aggregation code is not prepared to handle - * anything but STA/AP due to the BSSID handling; - * IBSS could work in the code but isn't supported - * by drivers or the standard. - */ if (sdata->vif.type != NL80211_IFTYPE_STATION && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT && sdata->vif.type != NL80211_IFTYPE_AP_VLAN && - sdata->vif.type != NL80211_IFTYPE_AP) + sdata->vif.type != NL80211_IFTYPE_AP && + sdata->vif.type != NL80211_IFTYPE_ADHOC) break; /* verify action_code is present */ @@ -2203,7 +2378,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) if (sdata->vif.type != NL80211_IFTYPE_STATION) break; - if (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN)) + if (compare_ether_addr(mgmt->bssid, sdata->u.mgd.bssid)) break; goto queue; @@ -2222,12 +2397,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; } @@ -2258,6 +2458,7 @@ static ieee80211_rx_result debug_noinline ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx) { struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); + int sig = 0; /* skip known-bad action frames and return them in the next handler */ if (status->rx_flags & IEEE80211_RX_MALFORMED_ACTION_FRM) @@ -2270,7 +2471,10 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx) * it transmitted were processed or returned. */ - if (cfg80211_rx_mgmt(rx->sdata->dev, status->freq, + if (rx->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) + sig = status->signal; + + if (cfg80211_rx_mgmt(rx->sdata->dev, status->freq, sig, rx->skb->data, rx->skb->len, GFP_ATOMIC)) { if (rx->sta) @@ -2338,14 +2542,9 @@ static ieee80211_rx_result debug_noinline ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) { struct ieee80211_sub_if_data *sdata = rx->sdata; - ieee80211_rx_result rxs; struct ieee80211_mgmt *mgmt = (void *)rx->skb->data; __le16 stype; - rxs = ieee80211_work_rx_mgmt(rx->sdata, rx->skb); - if (rxs != RX_CONTINUE) - return rxs; - stype = mgmt->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE); if (!ieee80211_vif_is_mesh(&sdata->vif) && @@ -2354,10 +2553,13 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) return RX_DROP_MONITOR; switch (stype) { + case cpu_to_le16(IEEE80211_STYPE_AUTH): case cpu_to_le16(IEEE80211_STYPE_BEACON): case cpu_to_le16(IEEE80211_STYPE_PROBE_RESP): /* process for all: mesh, mlme, ibss */ break; + case cpu_to_le16(IEEE80211_STYPE_ASSOC_RESP): + case cpu_to_le16(IEEE80211_STYPE_REASSOC_RESP): case cpu_to_le16(IEEE80211_STYPE_DEAUTH): case cpu_to_le16(IEEE80211_STYPE_DISASSOC): if (is_multicast_ether_addr(mgmt->da) && @@ -2369,7 +2571,6 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) return RX_DROP_MONITOR; break; case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ): - case cpu_to_le16(IEEE80211_STYPE_AUTH): /* process only for ibss */ if (sdata->vif.type != NL80211_IFTYPE_ADHOC) return RX_DROP_MONITOR; @@ -2394,16 +2595,10 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx, { struct ieee80211_sub_if_data *sdata; struct ieee80211_local *local = rx->local; - struct ieee80211_rtap_hdr { - struct ieee80211_radiotap_header hdr; - u8 flags; - u8 rate_or_pad; - __le16 chan_freq; - __le16 chan_flags; - } __packed *rthdr; struct sk_buff *skb = rx->skb, *skb2; struct net_device *prev_dev = NULL; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + int needed_headroom; /* * If cooked monitor has been processed already, then @@ -2413,30 +2608,20 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx, goto out_free_skb; rx->flags |= IEEE80211_RX_CMNTR; - if (skb_headroom(skb) < sizeof(*rthdr) && - pskb_expand_head(skb, sizeof(*rthdr), 0, GFP_ATOMIC)) + /* If there are no cooked monitor interfaces, just free the SKB */ + if (!local->cooked_mntrs) goto out_free_skb; - rthdr = (void *)skb_push(skb, sizeof(*rthdr)); - memset(rthdr, 0, sizeof(*rthdr)); - rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr)); - rthdr->hdr.it_present = - cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) | - (1 << IEEE80211_RADIOTAP_CHANNEL)); + /* room for the radiotap header based on driver features */ + needed_headroom = ieee80211_rx_radiotap_len(local, status); - if (rate) { - rthdr->rate_or_pad = rate->bitrate / 5; - rthdr->hdr.it_present |= - cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE); - } - rthdr->chan_freq = cpu_to_le16(status->freq); + if (skb_headroom(skb) < needed_headroom && + pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC)) + goto out_free_skb; - if (status->band == IEEE80211_BAND_5GHZ) - rthdr->chan_flags = cpu_to_le16(IEEE80211_CHAN_OFDM | - IEEE80211_CHAN_5GHZ); - else - rthdr->chan_flags = cpu_to_le16(IEEE80211_CHAN_DYN | - IEEE80211_CHAN_2GHZ); + /* prepend radiotap information */ + ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom, + false); skb_set_mac_header(skb, 0); skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -2539,17 +2724,16 @@ 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_amsdu) CALL_RXH(ieee80211_rx_h_data) CALL_RXH(ieee80211_rx_h_ctrl); CALL_RXH(ieee80211_rx_h_mgmt_check) @@ -2605,7 +2789,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 +2833,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; } @@ -2666,8 +2855,8 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx, rate_idx = 0; /* TODO: HT rates */ else rate_idx = status->rate_idx; - rx->sta = ieee80211_ibss_add_sta(sdata, bssid, - hdr->addr2, BIT(rate_idx), GFP_ATOMIC); + ieee80211_ibss_rx_no_sta(sdata, bssid, hdr->addr2, + BIT(rate_idx)); } break; case NL80211_IFTYPE_MESH_POINT: @@ -2688,6 +2877,15 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx, return 0; } else if (!ieee80211_bssid_match(bssid, sdata->vif.addr)) { + /* + * Accept public action frames even when the + * BSSID doesn't match, this is used for P2P + * and location updates. Note that mac80211 + * itself never looks at these frames. + */ + if (!(status->rx_flags & IEEE80211_RX_IN_SCAN) && + ieee80211_is_public_action(hdr, skb->len)) + return 1; if (!(status->rx_flags & IEEE80211_RX_IN_SCAN) && !ieee80211_is_beacon(hdr->frame_control)) return 0; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 7c75741..34b9280 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -13,10 +13,12 @@ */ #include +#include #include #include #include #include +#include #include #include "ieee80211_i.h" @@ -102,16 +104,35 @@ ieee80211_bss_info_update(struct ieee80211_local *local, cbss->free_priv = ieee80211_rx_bss_free; bss = (void *)cbss->priv; + if (elems->parse_error) { + if (beacon) + bss->corrupt_data |= IEEE80211_BSS_CORRUPT_BEACON; + else + bss->corrupt_data |= IEEE80211_BSS_CORRUPT_PROBE_RESP; + } else { + if (beacon) + bss->corrupt_data &= ~IEEE80211_BSS_CORRUPT_BEACON; + else + bss->corrupt_data &= ~IEEE80211_BSS_CORRUPT_PROBE_RESP; + } + /* save the ERP value so that it is available at association time */ - if (elems->erp_info && elems->erp_info_len >= 1) { + if (elems->erp_info && elems->erp_info_len >= 1 && + (!elems->parse_error || + !(bss->valid_data & IEEE80211_BSS_VALID_ERP))) { bss->erp_value = elems->erp_info[0]; - bss->has_erp_value = 1; + bss->has_erp_value = true; + if (!elems->parse_error) + bss->valid_data |= IEEE80211_BSS_VALID_ERP; } - if (elems->tim) { + if (elems->tim && (!elems->parse_error || + !(bss->valid_data & IEEE80211_BSS_VALID_DTIM))) { struct ieee80211_tim_ie *tim_ie = (struct ieee80211_tim_ie *)elems->tim; bss->dtim_period = tim_ie->dtim_period; + if (!elems->parse_error) + bss->valid_data |= IEEE80211_BSS_VALID_DTIM; } /* If the beacon had no TIM IE, or it was invalid, use 1 */ @@ -119,26 +140,38 @@ ieee80211_bss_info_update(struct ieee80211_local *local, bss->dtim_period = 1; /* replace old supported rates if we get new values */ - srlen = 0; - if (elems->supp_rates) { - clen = IEEE80211_MAX_SUPP_RATES; - if (clen > elems->supp_rates_len) - clen = elems->supp_rates_len; - memcpy(bss->supp_rates, elems->supp_rates, clen); - srlen += clen; - } - if (elems->ext_supp_rates) { - clen = IEEE80211_MAX_SUPP_RATES - srlen; - if (clen > elems->ext_supp_rates_len) - clen = elems->ext_supp_rates_len; - memcpy(bss->supp_rates + srlen, elems->ext_supp_rates, clen); - srlen += clen; + if (!elems->parse_error || + !(bss->valid_data & IEEE80211_BSS_VALID_RATES)) { + srlen = 0; + if (elems->supp_rates) { + clen = IEEE80211_MAX_SUPP_RATES; + if (clen > elems->supp_rates_len) + clen = elems->supp_rates_len; + memcpy(bss->supp_rates, elems->supp_rates, clen); + srlen += clen; + } + if (elems->ext_supp_rates) { + clen = IEEE80211_MAX_SUPP_RATES - srlen; + if (clen > elems->ext_supp_rates_len) + clen = elems->ext_supp_rates_len; + memcpy(bss->supp_rates + srlen, elems->ext_supp_rates, + clen); + srlen += clen; + } + if (srlen) { + bss->supp_rates_len = srlen; + if (!elems->parse_error) + bss->valid_data |= IEEE80211_BSS_VALID_RATES; + } } - if (srlen) - bss->supp_rates_len = srlen; - bss->wmm_used = elems->wmm_param || elems->wmm_info; - bss->uapsd_supported = is_uapsd_supported(elems); + if (!elems->parse_error || + !(bss->valid_data & IEEE80211_BSS_VALID_WMM)) { + bss->wmm_used = elems->wmm_param || elems->wmm_info; + bss->uapsd_supported = is_uapsd_supported(elems); + if (!elems->parse_error) + bss->valid_data |= IEEE80211_BSS_VALID_WMM; + } if (!beacon) bss->last_probe_resp = jiffies; @@ -175,7 +208,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) presp = ieee80211_is_probe_resp(fc); if (presp) { /* ignore ProbeResp to foreign address */ - if (memcmp(mgmt->da, sdata->vif.addr, ETH_ALEN)) + if (compare_ether_addr(mgmt->da, sdata->vif.addr)) return RX_DROP_MONITOR; presp = true; @@ -212,12 +245,7 @@ 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)) + if (channel == sdata->local->oper_channel) return RX_CONTINUE; dev_kfree_skb(skb); @@ -231,6 +259,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 +282,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 +294,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 +326,13 @@ 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); + /* Set power back to normal operating levels. */ + ieee80211_hw_config(local, 0); 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); } ieee80211_recalc_idle(local); @@ -355,15 +373,11 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) */ drv_sw_scan_start(local); - local->leave_oper_channel_time = 0; + local->leave_oper_channel_time = jiffies; 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); + ieee80211_offchannel_stop_vifs(local); ieee80211_configure_filter(local); @@ -371,8 +385,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) ieee80211_hw_config(local, 0); ieee80211_queue_delayed_work(&local->hw, - &local->scan_work, - IEEE80211_CHANNEL_TIME); + &local->scan_work, 0); return 0; } @@ -508,96 +521,39 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, 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 { - /* - * we're currently scanning a different channel, let's - * see if we can scan another channel without interfering - * with the current traffic situation. - * - * Since we don't know if the AP has pending frames for us - * we can only check for our tx queues and use the current - * pm_qos requirements for rx. Hence, if no tx traffic occurs - * at all we will scan as many channels in a row as the pm_qos - * latency allows us to. Additionally we also check for the - * currently negotiated listen interval to prevent losing - * frames unnecessarily. - * - * Otherwise switch back to the operating channel. - */ - - bad_latency = time_after(jiffies + - ieee80211_scan_get_channel_time(next_chan), - local->leave_oper_channel_time + - usecs_to_jiffies(pm_qos_request(PM_QOS_NETWORK_LATENCY))); - - listen_int_exceeded = time_after(jiffies + - ieee80211_scan_get_channel_time(next_chan), - local->leave_oper_channel_time + - usecs_to_jiffies(min_beacon_int * 1024) * - local->hw.conf.listen_interval); - - if (associated && ( !tx_empty || bad_latency || - listen_int_exceeded)) - local->next_scan_state = SCAN_ENTER_OPER_CHANNEL; - else - local->next_scan_state = SCAN_SET_CHANNEL; - } - - *next_delay = 0; -} - -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); - /* - * What if the nullfunc frames didn't arrive? + * we're currently scanning a different channel, let's + * see if we can scan another channel without interfering + * with the current traffic situation. + * + * Since we don't know if the AP has pending frames for us + * we can only check for our tx queues and use the current + * pm_qos requirements for rx. Hence, if no tx traffic occurs + * at all we will scan as many channels in a row as the pm_qos + * latency allows us to. Additionally we also check for the + * currently negotiated listen interval to prevent losing + * frames unnecessarily. + * + * Otherwise switch back to the operating channel. */ - drv_flush(local, false); - if (local->ops->flush) - *next_delay = 0; - else - *next_delay = HZ / 10; - /* remember when we left the operating channel */ - local->leave_oper_channel_time = jiffies; + bad_latency = time_after(jiffies + + ieee80211_scan_get_channel_time(next_chan), + local->leave_oper_channel_time + + usecs_to_jiffies(pm_qos_request(PM_QOS_NETWORK_LATENCY))); - /* advance to the next channel to be scanned */ - local->next_scan_state = SCAN_SET_CHANNEL; -} + listen_int_exceeded = time_after(jiffies + + ieee80211_scan_get_channel_time(next_chan), + local->leave_oper_channel_time + + usecs_to_jiffies(min_beacon_int * 1024) * + local->hw.conf.listen_interval); -static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *local, - unsigned long *next_delay) -{ - /* 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); - - /* - * Re-enable vifs and beaconing. Leave PS - * in off-channel state..will put that back - * on-channel at the end of scanning. - */ - ieee80211_offchannel_return(local, true); + if (associated && (!tx_empty || bad_latency || listen_int_exceeded)) + local->next_scan_state = SCAN_SUSPEND; + else + local->next_scan_state = SCAN_SET_CHANNEL; - *next_delay = HZ / 5; - local->next_scan_state = SCAN_DECISION; + *next_delay = 0; } static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, @@ -611,10 +567,8 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, local->scan_channel = chan; - /* Only call hw-config if we really need to change channels. */ - if (chan != local->hw.conf.channel) - if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL)) - skip = 1; + if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL)) + skip = 1; /* advance state machine to next channel/band */ local->scan_channel_idx++; @@ -652,13 +606,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 @@ -668,6 +625,39 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, local->next_scan_state = SCAN_DECISION; } +static void ieee80211_scan_state_suspend(struct ieee80211_local *local, + unsigned long *next_delay) +{ + /* switch back to the operating channel */ + local->scan_channel = NULL; + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); + + /* disable PS */ + ieee80211_offchannel_return(local); + + *next_delay = HZ / 5; + /* afterwards, resume scan & go to next channel */ + local->next_scan_state = SCAN_RESUME; +} + +static void ieee80211_scan_state_resume(struct ieee80211_local *local, + unsigned long *next_delay) +{ + ieee80211_offchannel_stop_vifs(local); + + if (local->ops->flush) { + drv_flush(local, false); + *next_delay = 0; + } else + *next_delay = HZ / 10; + + /* remember when we left the operating channel */ + local->leave_oper_channel_time = jiffies; + + /* advance to the next channel to be scanned */ + local->next_scan_state = SCAN_SET_CHANNEL; +} + void ieee80211_scan_work(struct work_struct *work) { struct ieee80211_local *local = @@ -738,11 +728,11 @@ void ieee80211_scan_work(struct work_struct *work) case SCAN_SEND_PROBE: ieee80211_scan_state_send_probe(local, &next_delay); break; - case SCAN_LEAVE_OPER_CHANNEL: - ieee80211_scan_state_leave_oper_channel(local, &next_delay); + case SCAN_SUSPEND: + ieee80211_scan_state_suspend(local, &next_delay); break; - case SCAN_ENTER_OPER_CHANNEL: - ieee80211_scan_state_enter_oper_channel(local, &next_delay); + case SCAN_RESUME: + ieee80211_scan_state_resume(local, &next_delay); break; } } while (next_delay == 0); @@ -769,9 +759,9 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, return res; } -int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, - const u8 *ssid, u8 ssid_len, - struct ieee80211_channel *chan) +int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, + const u8 *ssid, u8 ssid_len, + struct ieee80211_channel *chan) { struct ieee80211_local *local = sdata->local; int ret = -EBUSY; @@ -785,22 +775,36 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, /* fill internal scan request */ if (!chan) { - int i, nchan = 0; + int i, max_n; + int n_ch = 0; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!local->hw.wiphy->bands[band]) continue; - for (i = 0; - i < local->hw.wiphy->bands[band]->n_channels; - i++) { - local->int_scan_req->channels[nchan] = + + max_n = local->hw.wiphy->bands[band]->n_channels; + for (i = 0; i < max_n; i++) { + struct ieee80211_channel *tmp_ch = &local->hw.wiphy->bands[band]->channels[i]; - nchan++; + + if (tmp_ch->flags & (IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_DISABLED)) + continue; + + local->int_scan_req->channels[n_ch] = tmp_ch; + n_ch++; } } - local->int_scan_req->n_channels = nchan; + if (WARN_ON_ONCE(n_ch == 0)) + goto unlock; + + local->int_scan_req->n_channels = n_ch; } else { + if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_DISABLED))) + goto unlock; + local->int_scan_req->channels[0] = chan; local->int_scan_req->n_channels = 1; } @@ -821,10 +825,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 +838,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..569eb2c 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -24,6 +25,7 @@ #include "sta_info.h" #include "debugfs_sta.h" #include "mesh.h" +#include "wme.h" /** * DOC: STA information lifetime rules @@ -61,14 +63,14 @@ * freed before they are done using it. */ -/* Caller must hold local->sta_lock */ +/* Caller must hold local->sta_mtx */ static int sta_info_hash_del(struct ieee80211_local *local, struct sta_info *sta) { struct sta_info *s; s = rcu_dereference_protected(local->sta_hash[STA_HASH(sta->sta.addr)], - lockdep_is_held(&local->sta_lock)); + lockdep_is_held(&local->sta_mtx)); if (!s) return -ENOENT; if (s == sta) { @@ -80,7 +82,7 @@ static int sta_info_hash_del(struct ieee80211_local *local, while (rcu_access_pointer(s->hnext) && rcu_access_pointer(s->hnext) != sta) s = rcu_dereference_protected(s->hnext, - lockdep_is_held(&local->sta_lock)); + lockdep_is_held(&local->sta_mtx)); if (rcu_access_pointer(s->hnext)) { rcu_assign_pointer(s->hnext, sta->hnext); return 0; @@ -97,16 +99,12 @@ 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 && - memcmp(sta->sta.addr, addr, ETH_ALEN) == 0) + compare_ether_addr(sta->sta.addr, addr) == 0) break; sta = rcu_dereference_check(sta->hnext, - rcu_read_lock_held() || - lockdep_is_held(&local->sta_lock) || lockdep_is_held(&local->sta_mtx)); } return sta; @@ -123,17 +121,13 @@ 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)) && - memcmp(sta->sta.addr, addr, ETH_ALEN) == 0) + compare_ether_addr(sta->sta.addr, addr) == 0) break; sta = rcu_dereference_check(sta->hnext, - rcu_read_lock_held() || - lockdep_is_held(&local->sta_lock) || lockdep_is_held(&local->sta_mtx)); } return sta; @@ -160,21 +154,20 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata, } /** - * __sta_info_free - internal STA free helper + * sta_info_free - free STA * * @local: pointer to the global information * @sta: STA info to free * * This function must undo everything done by sta_info_alloc() - * that may happen before sta_info_insert(). + * that may happen before sta_info_insert(). It may only be + * called when sta_info_insert() has not been attempted (and + * if that fails, the station is freed anyway.) */ -static void __sta_info_free(struct ieee80211_local *local, - struct sta_info *sta) +void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) { - if (sta->rate_ctrl) { + if (sta->rate_ctrl) rate_control_free_sta(sta); - rate_control_put(sta->rate_ctrl); - } #ifdef CONFIG_MAC80211_VERBOSE_DEBUG wiphy_debug(local->hw.wiphy, "Destroyed STA %pM\n", sta->sta.addr); @@ -183,10 +176,11 @@ static void __sta_info_free(struct ieee80211_local *local, kfree(sta); } -/* Caller must hold local->sta_lock */ +/* Caller must hold local->sta_mtx */ static void sta_info_hash_add(struct ieee80211_local *local, struct sta_info *sta) { + lockdep_assert_held(&local->sta_mtx); sta->hnext = local->sta_hash[STA_HASH(sta->sta.addr)]; rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta); } @@ -200,13 +194,24 @@ 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)) { + local_bh_disable(); 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); + local_bh_enable(); + } 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, @@ -215,19 +220,17 @@ static int sta_prepare_rate_control(struct ieee80211_local *local, if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) return 0; - sta->rate_ctrl = rate_control_get(local->rate_ctrl); + sta->rate_ctrl = local->rate_ctrl; sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl, &sta->sta, gfp); - if (!sta->rate_ctrl_priv) { - rate_control_put(sta->rate_ctrl); + if (!sta->rate_ctrl_priv) return -ENOMEM; - } return 0; } struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, - u8 *addr, gfp_t gfp) + const u8 *addr, gfp_t gfp) { struct ieee80211_local *local = sdata->local; struct sta_info *sta; @@ -239,7 +242,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); @@ -249,6 +252,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, sta->sdata = sdata; sta->last_rx = jiffies; + sta->sta_state = IEEE80211_STA_NONE; + do_posix_clock_monotonic_gettime(&uptime); sta->last_connected = uptime.tv_sec; ewma_init(&sta->avg_signal, 1024, 8); @@ -266,8 +271,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,207 +291,147 @@ 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_insert_check(struct sta_info *sta) { - struct ieee80211_local *local = sta->local; struct ieee80211_sub_if_data *sdata = sta->sdata; - struct station_info sinfo; - unsigned long flags; - int err = 0; - - 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; -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - if (async) - wiphy_debug(local->hw.wiphy, - "Finished adding IBSS STA %pM\n", - sta->sta.addr); -#endif - } - sdata = sta->sdata; - - 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); - } - - 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); + /* + * 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))) + return -ENETDOWN; + if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->vif.addr) == 0 || + is_multicast_ether_addr(sta->sta.addr))) + return -EINVAL; return 0; } -static void sta_info_finish_pending(struct ieee80211_local *local) +static int sta_info_insert_drv_state(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct sta_info *sta) { - struct sta_info *sta; - unsigned long flags; + enum ieee80211_sta_state state; + int err = 0; - spin_lock_irqsave(&local->sta_lock, flags); - while (!list_empty(&local->sta_pending_list)) { - sta = list_first_entry(&local->sta_pending_list, - struct sta_info, list); - list_del(&sta->list); - spin_unlock_irqrestore(&local->sta_lock, flags); + for (state = IEEE80211_STA_NOTEXIST; state < sta->sta_state; state++) { + err = drv_sta_state(local, sdata, sta, state, state + 1); + if (err) + break; + } - sta_info_finish_insert(sta, true); + if (!err) { + /* + * Drivers using legacy sta_add/sta_remove callbacks only + * get uploaded set to true after sta_add is called. + */ + if (!local->ops->sta_add) + sta->uploaded = true; + return 0; + } - spin_lock_irqsave(&local->sta_lock, flags); + if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { + printk(KERN_DEBUG + "%s: failed to move IBSS STA %pM to state %d (%d) - keeping it anyway.\n", + sdata->name, sta->sta.addr, state + 1, err); + err = 0; } - spin_unlock_irqrestore(&local->sta_lock, flags); -} -static void sta_info_finish_work(struct work_struct *work) -{ - struct ieee80211_local *local = - container_of(work, struct ieee80211_local, sta_finish_work); + /* unwind on error */ + for (; state > IEEE80211_STA_NOTEXIST; state--) + WARN_ON(drv_sta_state(local, sdata, sta, state, state - 1)); - mutex_lock(&local->sta_mtx); - sta_info_finish_pending(local); - mutex_unlock(&local->sta_mtx); + return err; } -int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU) +/* + * should be called with sta_mtx locked + * this function replaces the mutex lock + * with a RCU lock + */ +static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) { struct ieee80211_local *local = sta->local; struct ieee80211_sub_if_data *sdata = sta->sdata; - unsigned long flags; + struct station_info sinfo; 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; - } + lockdep_assert_held(&local->sta_mtx); - if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->vif.addr) == 0 || - is_multicast_ether_addr(sta->sta.addr))) { - err = -EINVAL; - rcu_read_lock(); - goto out_free; + /* check if STA exists already */ + if (sta_info_get_bss(sdata, sta->sta.addr)) { + err = -EEXIST; + goto out_err; } - /* - * 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; - } + /* notify driver */ + err = sta_info_insert_drv_state(local, sdata, sta); + if (err) + goto out_err; - local->num_sta++; - local->sta_generation++; - smp_mb(); - sta_info_hash_add(local, sta); + local->num_sta++; + local->sta_generation++; + smp_mb(); - list_add_tail(&sta->list, &local->sta_pending_list); + /* make the station visible */ + sta_info_hash_add(local, sta); - rcu_read_lock(); - spin_unlock_irqrestore(&local->sta_lock, flags); + list_add_rcu(&sta->list, &local->sta_list); -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - wiphy_debug(local->hw.wiphy, "Added IBSS STA %pM\n", - sta->sta.addr); -#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ + set_sta_flag(sta, WLAN_STA_INSERTED); + + ieee80211_sta_debugfs_add(sta); + rate_control_add_sta_debugfs(sta); - ieee80211_queue_work(&local->hw, &local->sta_finish_work); + 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; - } +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + wiphy_debug(local->hw.wiphy, "Inserted STA %pM\n", sta->sta.addr); +#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ - /* - * On first glance, this will look racy, because the code - * below this point, 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. - */ + /* move reference to rcu-protected */ + rcu_read_lock(); + mutex_unlock(&local->sta_mtx); - might_sleep(); + if (ieee80211_vif_is_mesh(&sdata->vif)) + mesh_accept_plinks_update(sdata); - mutex_lock(&local->sta_mtx); + return 0; + out_err: + mutex_unlock(&local->sta_mtx); + rcu_read_lock(); + return err; +} - 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; - } +int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU) +{ + struct ieee80211_local *local = sta->local; + int err = 0; - spin_unlock_irqrestore(&local->sta_lock, flags); + might_sleep(); - err = sta_info_finish_insert(sta, false); + err = sta_info_insert_check(sta); if (err) { - mutex_unlock(&local->sta_mtx); rcu_read_lock(); goto out_free; } -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - wiphy_debug(local->hw.wiphy, "Inserted STA %pM\n", sta->sta.addr); -#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ - - /* move reference to rcu-protected */ - rcu_read_lock(); - mutex_unlock(&local->sta_mtx); + mutex_lock(&local->sta_mtx); - if (ieee80211_vif_is_mesh(&sdata->vif)) - mesh_accept_plinks_update(sdata); + err = sta_info_insert_finish(sta); + if (err) + goto out_free; return 0; out_free: BUG_ON(!err); - __sta_info_free(local, sta); + sta_info_free(local, sta); return err; } @@ -515,64 +462,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; + + /* + * 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; - __bss_tim_clear(bss, sta->sta.aid); + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + unsigned long tids; - 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; + 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; + + 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->tim_lock, flags); + + if (indicate_tim) + __bss_tim_set(bss, sta->sta.aid); + else + __bss_tim_clear(bss, sta->sta.aid); - BUG_ON(!sta->sdata->bss); + 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_lock_irqsave(&sta->local->sta_lock, flags); - __sta_info_clear_tim_bit(sta->sdata->bss, sta); - spin_unlock_irqrestore(&sta->local->sta_lock, flags); + spin_unlock_irqrestore(&local->tim_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 +562,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 +624,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) +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; + struct tid_ampdu_tx *tid_tx; might_sleep(); @@ -638,24 +674,23 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) local = sta->local; sdata = sta->sdata; + lockdep_assert_held(&local->sta_mtx); + /* * Before removing the station from the driver and * rate control, it might still start new aggregation * 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); ret = sta_info_hash_del(local, sta); - /* this might still be the pending list ... which is fine */ - if (!ret) - list_del(&sta->list); - spin_unlock_irqrestore(&local->sta_lock, flags); if (ret) return ret; + list_del_rcu(&sta->list); + mutex_lock(&local->key_mtx); for (i = 0; i < NUM_DEFAULT_KEYS; i++) __ieee80211_key_free(key_mtx_dereference(local, sta->gtk[i])); @@ -665,27 +700,24 @@ 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)) { - BUG_ON(!sdata->bss); - - atomic_dec(&sdata->bss->num_sta_ps); - sta_info_clear_tim_bit(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); + + while (sta->sta_state > IEEE80211_STA_NONE) { + ret = sta_info_move_state(sta, sta->sta_state - 1); + if (ret) { + WARN_ON_ONCE(1); + break; + } + } if (sta->uploaded) { - if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) - sdata = container_of(sdata->bss, - struct ieee80211_sub_if_data, - u.ap); - drv_sta_remove(local, sdata, &sta->sta); - sdata = sta->sdata; + ret = drv_sta_state(local, sdata, sta, IEEE80211_STA_NONE, + IEEE80211_STA_NOTEXIST); + WARN_ON_ONCE(ret != 0); } /* @@ -696,6 +728,21 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) */ synchronize_rcu(); + if (test_sta_flag(sta, WLAN_STA_PS_STA)) { + BUG_ON(!sdata->bss); + + clear_sta_flag(sta, WLAN_STA_PS_STA); + + atomic_dec(&sdata->bss->num_sta_ps); + sta_info_recalc_tim(sta); + } + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]); + ieee80211_purge_tx_queue(&local->hw, &sta->ps_tx_buf[ac]); + ieee80211_purge_tx_queue(&local->hw, &sta->tx_filtered[ac]); + } + #ifdef CONFIG_MAC80211_MESH if (ieee80211_vif_is_mesh(&sdata->vif)) mesh_accept_plinks_update(sdata); @@ -718,15 +765,21 @@ 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); + /* + * Destroy aggregation state here. It would be nice to wait for the + * driver to finish aggregation stop and then clean up, but for now + * drivers have to handle aggregation stop being requested, followed + * directly by station destruction. + */ + for (i = 0; i < STA_TID_NUM; i++) { + tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]); + if (!tid_tx) + continue; + ieee80211_purge_tx_queue(&local->hw, &tid_tx->pending); + kfree(tid_tx); } - while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) - dev_kfree_skb_any(skb); - - __sta_info_free(local, sta); + sta_info_free(local, sta); return 0; } @@ -782,11 +835,9 @@ static void sta_info_cleanup(unsigned long data) void sta_info_init(struct ieee80211_local *local) { - spin_lock_init(&local->sta_lock); + spin_lock_init(&local->tim_lock); mutex_init(&local->sta_mtx); INIT_LIST_HEAD(&local->sta_list); - INIT_LIST_HEAD(&local->sta_pending_list); - INIT_WORK(&local->sta_finish_work, sta_info_finish_work); setup_timer(&local->sta_cleanup, sta_info_cleanup, (unsigned long)local); @@ -815,12 +866,11 @@ int sta_info_flush(struct ieee80211_local *local, might_sleep(); mutex_lock(&local->sta_mtx); - - sta_info_finish_pending(local); - list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { - if (!sdata || sdata == sta->sdata) + if (!sdata || sdata == sta->sdata) { WARN_ON(__sta_info_destroy(sta)); + ret++; + } } mutex_unlock(&local->sta_mtx); @@ -834,7 +884,11 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, *tmp; mutex_lock(&local->sta_mtx); - list_for_each_entry_safe(sta, tmp, &local->sta_list, list) + + list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { + if (sdata != sta->sdata) + continue; + if (time_after(jiffies, sta->last_rx + exp_time)) { #ifdef CONFIG_MAC80211_IBSS_DEBUG printk(KERN_DEBUG "%s: expiring inactive STA %pM\n", @@ -842,6 +896,8 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, #endif WARN_ON(__sta_info_destroy(sta)); } + } + mutex_unlock(&local->sta_mtx); } @@ -890,8 +946,11 @@ EXPORT_SYMBOL(ieee80211_find_sta); static void clear_sta_ps_flags(void *_sta) { struct sta_info *sta = _sta; + struct ieee80211_sub_if_data *sdata = sta->sdata; - clear_sta_flags(sta, WLAN_STA_PS_DRIVER | WLAN_STA_PS_STA); + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + if (test_and_clear_sta_flag(sta, WLAN_STA_PS_STA)) + atomic_dec(&sdata->bss->num_sta_ps); } /* powersave support code */ @@ -899,88 +958,349 @@ 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); + + 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); + } + + 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_NO_PS_BUFFER | + 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; + } } - 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; + 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; + } - ieee80211_add_pending_skb(local, skb); + 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_NO_PS_BUFFER; + + /* + * 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); + } - if (no_pending_pkts) - sta_info_clear_tim_bit(sta); -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG + drv_allow_buffered_frames(local, sta, tids, num, + reason, more_data); + + 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 +1309,138 @@ 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); - set_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF); - sta_info_set_tim_bit(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); + + sta_info_recalc_tim(sta); +} +EXPORT_SYMBOL(ieee80211_sta_set_buffered); + +int sta_info_move_state(struct sta_info *sta, + enum ieee80211_sta_state new_state) +{ + might_sleep(); + + if (sta->sta_state == new_state) + return 0; + + /* check allowed transitions first */ + + switch (new_state) { + case IEEE80211_STA_NONE: + if (sta->sta_state != IEEE80211_STA_AUTH) + return -EINVAL; + break; + case IEEE80211_STA_AUTH: + if (sta->sta_state != IEEE80211_STA_NONE && + sta->sta_state != IEEE80211_STA_ASSOC) + return -EINVAL; + break; + case IEEE80211_STA_ASSOC: + if (sta->sta_state != IEEE80211_STA_AUTH && + sta->sta_state != IEEE80211_STA_AUTHORIZED) + return -EINVAL; + break; + case IEEE80211_STA_AUTHORIZED: + if (sta->sta_state != IEEE80211_STA_ASSOC) + return -EINVAL; + break; + default: + WARN(1, "invalid state %d", new_state); + return -EINVAL; + } + +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + printk(KERN_DEBUG "%s: moving STA %pM to state %d\n", + sta->sdata->name, sta->sta.addr, new_state); +#endif + + /* + * notify the driver before the actual changes so it can + * fail the transition + */ + if (test_sta_flag(sta, WLAN_STA_INSERTED)) { + int err = drv_sta_state(sta->local, sta->sdata, sta, + sta->sta_state, new_state); + if (err) + return err; + } + + /* reflect the change in all state variables */ + + switch (new_state) { + case IEEE80211_STA_NONE: + if (sta->sta_state == IEEE80211_STA_AUTH) + clear_bit(WLAN_STA_AUTH, &sta->_flags); + break; + case IEEE80211_STA_AUTH: + if (sta->sta_state == IEEE80211_STA_NONE) + set_bit(WLAN_STA_AUTH, &sta->_flags); + else if (sta->sta_state == IEEE80211_STA_ASSOC) + clear_bit(WLAN_STA_ASSOC, &sta->_flags); + break; + case IEEE80211_STA_ASSOC: + if (sta->sta_state == IEEE80211_STA_AUTH) { + set_bit(WLAN_STA_ASSOC, &sta->_flags); + } else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) { + if (sta->sdata->vif.type == NL80211_IFTYPE_AP) + atomic_dec(&sta->sdata->u.ap.num_sta_authorized); + clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags); + } + break; + case IEEE80211_STA_AUTHORIZED: + if (sta->sta_state == IEEE80211_STA_ASSOC) { + if (sta->sdata->vif.type == NL80211_IFTYPE_AP) + atomic_inc(&sta->sdata->u.ap.num_sta_authorized); + set_bit(WLAN_STA_AUTHORIZED, &sta->_flags); + } + break; + default: + break; + } + + sta->sta_state = new_state; + + return 0; } -EXPORT_SYMBOL(ieee80211_sta_set_tim); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index c6ae871..4e6ece8 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -14,12 +14,14 @@ #include #include #include +#include #include "key.h" /** * 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. @@ -29,7 +31,6 @@ * when virtual port control is not in use. * @WLAN_STA_SHORT_PREAMBLE: Station is capable of receiving short-preamble * frames. - * @WLAN_STA_ASSOC_AP: We're associated to that station, it is an AP. * @WLAN_STA_WME: Station is a QoS-STA. * @WLAN_STA_WDS: Station is one of our WDS peers. * @WLAN_STA_CLEAR_PS_FILT: Clear PS filter in hardware (using the @@ -43,29 +44,45 @@ * 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. + * @WLAN_STA_4ADDR_EVENT: 4-addr event was already sent for this frame. + * @WLAN_STA_INSERTED: This station is inserted into the hash table. + * @WLAN_STA_RATE_CONTROL: rate control was initialized for this station. */ 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_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, + WLAN_STA_4ADDR_EVENT, + WLAN_STA_INSERTED, + WLAN_STA_RATE_CONTROL, }; #define STA_TID_NUM 16 #define ADDBA_RESP_INTERVAL HZ -#define HT_AGG_MAX_RETRIES 0x3 +#define HT_AGG_MAX_RETRIES 15 +#define HT_AGG_BURST_RETRIES 3 +#define HT_AGG_RETRIES_PERIOD (15 * HZ) #define HT_AGG_STATE_DRV_READY 0 #define HT_AGG_STATE_RESPONSE_RECEIVED 1 @@ -78,6 +95,7 @@ enum ieee80211_sta_info_flags { * struct tid_ampdu_tx - TID aggregation information (Tx). * * @rcu_head: rcu head for freeing structure + * @session_timer: check if we keep Tx-ing on the TID (by timeout value) * @addba_resp_timer: timer for peer's response to addba request * @pending: pending frames queue -- use sta's spinlock to protect * @dialog_token: dialog token for aggregation session @@ -86,6 +104,9 @@ 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 + * @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. @@ -98,6 +119,7 @@ enum ieee80211_sta_info_flags { */ struct tid_ampdu_tx { struct rcu_head rcu_head; + struct timer_list session_timer; struct timer_list addba_resp_timer; struct sk_buff_head pending; unsigned long state; @@ -106,6 +128,9 @@ struct tid_ampdu_tx { u8 stop_initiator; bool tx_stop; u8 buf_size; + + u16 failed_bar_ssn; + bool bar_pending; }; /** @@ -145,6 +170,7 @@ struct tid_ampdu_rx { u16 buf_size; u16 timeout; u8 dialog_token; + bool removed; }; /** @@ -154,10 +180,13 @@ struct tid_ampdu_rx { * @tid_tx: aggregation info for Tx per TID * @tid_start_tx: sessions where start was requested * @addba_req_num: number of times addBA request has been sent. + * @last_addba_req_time: timestamp of the last addBA request. * @dialog_token_allocator: dialog token enumerator for each new session; * @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,10 +195,12 @@ 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]; struct tid_ampdu_tx *tid_start_tx[STA_TID_NUM]; + unsigned long last_addba_req_time[STA_TID_NUM]; u8 addba_req_num[STA_TID_NUM]; u8 dialog_token_allocator; }; @@ -195,15 +226,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 +268,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 + * @sta: station information we share with the driver + * @sta_state: duplicates information about station state (for debug) + * @beacon_loss_count: number of times beacon loss has triggered */ struct sta_info { /* General information, mostly static */ @@ -251,7 +286,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 +295,16 @@ struct sta_info { bool uploaded; - /* - * frequently updated, locked with own spinlock (flaglock), - * use the accessors defined below - */ - u32 flags; + enum ieee80211_sta_state sta_state; - /* - * STA powersave frame queues, no more than the internal - * locking required. - */ - struct sk_buff_head ps_tx_buf; - struct sk_buff_head tx_filtered; + /* use the accessors defined below */ + unsigned long _flags; + + /* 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 +316,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; @@ -331,6 +364,7 @@ struct sta_info { #endif unsigned int lost_packets; + unsigned int beacon_loss_count; /* keep last! */ struct ieee80211_sta sta; @@ -344,62 +378,63 @@ 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); + WARN_ON(flag == WLAN_STA_AUTH || + flag == WLAN_STA_ASSOC || + flag == WLAN_STA_AUTHORIZED); + 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); + WARN_ON(flag == WLAN_STA_AUTH || + flag == WLAN_STA_ASSOC || + flag == WLAN_STA_AUTHORIZED); + 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); + WARN_ON(flag == WLAN_STA_AUTH || + flag == WLAN_STA_ASSOC || + flag == WLAN_STA_AUTHORIZED); + return test_and_clear_bit(flag, &sta->_flags); +} - return ret; +static inline int test_and_set_sta_flag(struct sta_info *sta, + enum ieee80211_sta_info_flags flag) +{ + WARN_ON(flag == WLAN_STA_AUTH || + flag == WLAN_STA_ASSOC || + flag == WLAN_STA_AUTHORIZED); + return test_and_set_bit(flag, &sta->_flags); } -static inline u32 get_sta_flags(struct sta_info *sta) +int sta_info_move_state(struct sta_info *sta, + enum ieee80211_sta_state new_state); + +static inline void sta_info_pre_move_state(struct sta_info *sta, + enum ieee80211_sta_state new_state) { - u32 ret; - unsigned long irqfl; + int ret; - spin_lock_irqsave(&sta->flaglock, irqfl); - ret = sta->flags; - spin_unlock_irqrestore(&sta->flaglock, irqfl); + WARN_ON_ONCE(test_sta_flag(sta, WLAN_STA_INSERTED)); - return ret; + ret = sta_info_move_state(sta, new_state); + WARN_ON_ONCE(ret); } + void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, struct tid_ampdu_tx *tid_tx); @@ -415,8 +450,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. */ @@ -443,7 +478,7 @@ void for_each_sta_info_type_check(struct ieee80211_local *local, { } -#define for_each_sta_info(local, _addr, _sta, nxt) \ +#define for_each_sta_info(local, _addr, _sta, nxt) \ for ( /* initialise loop */ \ _sta = rcu_dereference(local->sta_hash[STA_HASH(_addr)]),\ nxt = _sta ? rcu_dereference(_sta->hnext) : NULL; \ @@ -456,7 +491,7 @@ void for_each_sta_info_type_check(struct ieee80211_local *local, 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) + if (compare_ether_addr(_sta->sta.addr, (_addr)) == 0) /* * Get STA info by index, BROKEN! @@ -468,7 +503,10 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata, * until sta_info_insert(). */ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, - u8 *addr, gfp_t gfp); + const u8 *addr, gfp_t gfp); + +void sta_info_free(struct ieee80211_local *local, struct sta_info *sta); + /* * Insert STA info into hash table/list, returns zero or a * -EEXIST if (if the same MAC address is already present). @@ -479,24 +517,27 @@ 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 __must_check __sta_info_destroy(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); int sta_info_flush(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata); +void sta_set_rate_info_tx(struct sta_info *sta, + const struct ieee80211_tx_rate *rate, + struct rate_info *rinfo); void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, unsigned long exp_time); 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..9e88897 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -9,11 +9,15 @@ * published by the Free Software Foundation. */ +#include +#include #include +#include #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 +47,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 +69,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 +135,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 +159,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 +222,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 ... @@ -176,10 +345,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(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 +354,8 @@ 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; + int rtap_len; for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { if (info->status.rates[i].idx < 0) { @@ -212,11 +381,14 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) for_each_sta_info(local, hdr->addr1, sta, tmp) { /* skip wrong virtual interface */ - if (memcmp(hdr->addr2, sta->sdata->vif.addr, ETH_ALEN)) + if (compare_ether_addr(hdr->addr2, sta->sdata->vif.addr)) 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 tid, 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(); @@ -286,12 +483,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) * Fragments are passed to low-level drivers as separate skbs, so these * are actually fragments, not frames. Update frame counters only for * the first fragment of the frame. */ - - frag = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG; - type = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_FTYPE; - if (info->flags & IEEE80211_TX_STAT_ACK) { - if (frag == 0) { + if (ieee80211_is_first_frag(hdr->seq_ctrl)) { local->dot11TransmittedFrameCount++; if (is_multicast_ether_addr(hdr->addr1)) local->dot11MulticastTransmittedFrameCount++; @@ -306,11 +499,11 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) * with a multicast address in the address 1 field of type Data * or Management. */ if (!is_multicast_ether_addr(hdr->addr1) || - type == IEEE80211_FTYPE_DATA || - type == IEEE80211_FTYPE_MGMT) + ieee80211_is_data(fc) || + ieee80211_is_mgmt(fc)) local->dot11TransmittedFragmentCount++; } else { - if (frag == 0) + if (ieee80211_is_first_frag(hdr->seq_ctrl)) local->dot11FailedCount++; } @@ -327,30 +520,55 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) } if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) { - struct ieee80211_work *wk; u64 cookie = (unsigned long)skb; - rcu_read_lock(); - list_for_each_entry_rcu(wk, &local->work_list, list) { - if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX) - continue; - if (wk->offchan_tx.frame != skb) - continue; - wk->offchan_tx.status = true; - break; - } - rcu_read_unlock(); - if (local->hw_roc_skb_for_status == skb) { - cookie = local->hw_roc_cookie ^ 2; - local->hw_roc_skb_for_status = NULL; - } + if (ieee80211_is_nullfunc(hdr->frame_control) || + ieee80211_is_qos_nullfunc(hdr->frame_control)) { + acked = info->flags & IEEE80211_TX_STAT_ACK; - if (cookie == local->hw_offchan_tx_cookie) - local->hw_offchan_tx_cookie = 0; + cfg80211_probe_status(skb->dev, hdr->addr1, + cookie, acked, GFP_ATOMIC); + } else { + struct ieee80211_work *wk; + + rcu_read_lock(); + list_for_each_entry_rcu(wk, &local->work_list, list) { + if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX) + continue; + if (wk->offchan_tx.frame != skb) + continue; + wk->offchan_tx.status = true; + break; + } + rcu_read_unlock(); + if (local->hw_roc_skb_for_status == skb) { + cookie = local->hw_roc_cookie ^ 2; + local->hw_roc_skb_for_status = NULL; + } - cfg80211_mgmt_tx_status( - skb->dev, cookie, skb->data, skb->len, - !!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC); + cfg80211_mgmt_tx_status( + skb->dev, cookie, skb->data, skb->len, + !!(info->flags & IEEE80211_TX_STAT_ACK), + GFP_ATOMIC); + } + } + + if (unlikely(info->ack_frame_id)) { + struct sk_buff *ack_skb; + unsigned long flags; + + spin_lock_irqsave(&local->ack_status_lock, flags); + ack_skb = idr_find(&local->ack_status_frames, + info->ack_frame_id); + if (ack_skb) + idr_remove(&local->ack_status_frames, + info->ack_frame_id); + spin_unlock_irqrestore(&local->ack_status_lock, flags); + + /* consumes ack_skb */ + if (ack_skb) + skb_complete_wifi_ack(ack_skb, + info->flags & IEEE80211_TX_STAT_ACK); } /* this was a transmitted frame, but now we want to reuse it */ @@ -358,7 +576,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) /* Need to make a copy before skb->cb gets cleared */ send_to_cooked = !!(info->flags & IEEE80211_TX_CTL_INJECTED) || - (type != IEEE80211_FTYPE_DATA); + !(ieee80211_is_data(fc)); /* * This is a bit racy but we can avoid a lot of work @@ -370,44 +588,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); @@ -454,3 +641,38 @@ void ieee80211_report_low_ack(struct ieee80211_sta *pubsta, u32 num_packets) num_packets, GFP_ATOMIC); } EXPORT_SYMBOL(ieee80211_report_low_ack); + +void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + if (unlikely(info->ack_frame_id)) { + struct sk_buff *ack_skb; + unsigned long flags; + + spin_lock_irqsave(&local->ack_status_lock, flags); + ack_skb = idr_find(&local->ack_status_frames, + info->ack_frame_id); + if (ack_skb) + idr_remove(&local->ack_status_frames, + info->ack_frame_id); + spin_unlock_irqrestore(&local->ack_status_lock, flags); + + /* consumes ack_skb */ + if (ack_skb) + dev_kfree_skb_any(ack_skb); + } + + dev_kfree_skb_any(skb); +} +EXPORT_SYMBOL(ieee80211_free_txskb); + +void ieee80211_purge_tx_queue(struct ieee80211_hw *hw, + struct sk_buff_head *skbs) +{ + struct sk_buff *skb; + + while ((skb = __skb_dequeue(skbs))) + ieee80211_free_txskb(hw, skb); +} 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 #include #include +#include #include #include @@ -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 #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..67cd0f1 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -35,7 +36,8 @@ /* misc utils */ -static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr, +static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, + struct sk_buff *skb, int group_addr, int next_frag_len) { int rate, mrate, erp, dur, i; @@ -43,7 +45,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr, struct ieee80211_local *local = tx->local; struct ieee80211_supported_band *sband; struct ieee80211_hdr *hdr; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); /* assume HW handles this */ if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS) @@ -75,7 +77,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr, * at the highest possible rate belonging to the PHY rates in the * BSSBasicRateSet */ - hdr = (struct ieee80211_hdr *)tx->skb->data; + hdr = (struct ieee80211_hdr *)skb->data; if (ieee80211_is_ctl(hdr->frame_control)) { /* TODO: These control frames are not currently sent by * mac80211, but should they be implemented, this function @@ -149,11 +151,15 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr, rate = mrate; } - /* Time needed to transmit ACK - * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up - * to closest integer */ - - dur = ieee80211_frame_duration(local, 10, rate, erp, + /* Don't calculate ACKs for QoS Frames with NoAck Policy set */ + if (ieee80211_is_data_qos(hdr->frame_control) && + *(ieee80211_get_qos_ctl(hdr)) | IEEE80211_QOS_CTL_ACK_POLICY_NOACK) + dur = 0; + else + /* Time needed to transmit ACK + * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up + * to closest integer */ + dur = ieee80211_frame_duration(local, 10, rate, erp, tx->sdata->vif.bss_conf.use_short_preamble); if (next_frag_len) { @@ -220,12 +226,12 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx) * have correct qos tag for some reason, due the network or the * peer application. * - * Note: local->uapsd_queues access is racy here. If the value is + * Note: ifmgd->uapsd_queues access is racy here. If the value is * changed via debugfs, user needs to reassociate manually to have * everything in sync. */ if ((ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED) - && (local->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) + && (ifmgd->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) && skb_get_queue_mapping(tx->skb) == 0) return TX_CONTINUE; @@ -253,7 +259,7 @@ 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; @@ -278,17 +284,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) && - tx->sdata->vif.type != NL80211_IFTYPE_ADHOC && + if (unlikely(!assoc && ieee80211_is_data(hdr->frame_control))) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG printk(KERN_DEBUG "%s: dropped data frame to not " @@ -298,17 +301,14 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) I802_DEBUG_INC(tx->local->tx_handlers_drop_not_assoc); return TX_DROP; } - } else { - if (unlikely(ieee80211_is_data(hdr->frame_control) && - tx->local->num_sta == 0 && - tx->sdata->vif.type != NL80211_IFTYPE_ADHOC)) { - /* - * No associated STAs - no need to send multicast - * frames. - */ - return TX_DROP; - } - return TX_CONTINUE; + } else if (unlikely(tx->sdata->vif.type == NL80211_IFTYPE_AP && + ieee80211_is_data(hdr->frame_control) && + !atomic_read(&tx->sdata->u.ap.num_sta_authorized))) { + /* + * No associated STAs - no need to send multicast + * frames. + */ + return TX_DROP; } return TX_CONTINUE; @@ -343,13 +343,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 +427,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,66 +444,80 @@ 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) || - ieee80211_is_auth(hdr->frame_control) || - ieee80211_is_assoc_resp(hdr->frame_control) || - ieee80211_is_reassoc_resp(hdr->frame_control))) + if (unlikely(!sta)) 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_NO_PS_BUFFER))) { + int ac = skb_get_queue_mapping(tx->skb); + + /* only deauth, disassoc and action are bufferable MMPDUs */ + if (ieee80211_is_mgmt(hdr->frame_control) && + !ieee80211_is_deauth(hdr->frame_control) && + !ieee80211_is_disassoc(hdr->frame_control) && + !ieee80211_is_action(hdr->frame_control)) { + info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER; + return TX_CONTINUE; + } - 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 +541,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 +577,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 @@ -567,8 +592,6 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) switch (tx->key->conf.cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: - if (ieee80211_is_auth(hdr->frame_control)) - break; case WLAN_CIPHER_SUITE_TKIP: if (!ieee80211_is_data_present(hdr->frame_control)) tx->key = NULL; @@ -589,6 +612,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 +634,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)); @@ -618,7 +644,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) tx->local->hw.wiphy->frag_threshold); /* set up the tx rate control struct we give the RC algo */ - txrc.hw = local_to_hw(tx->local); + txrc.hw = &tx->local->hw; txrc.sband = sband; txrc.bss_conf = &tx->sdata->vif.bss_conf; txrc.skb = tx->skb; @@ -628,7 +654,11 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) txrc.max_rate_idx = -1; else txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; + memcpy(txrc.rate_idx_mcs_mask, + tx->sdata->rc_rateidx_mcs_mask[tx->channel->band], + sizeof(txrc.rate_idx_mcs_mask)); txrc.bss = (tx->sdata->vif.type == NL80211_IFTYPE_AP || + tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT || tx->sdata->vif.type == NL80211_IFTYPE_ADHOC); /* set up RTS protection if desired */ @@ -644,17 +674,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 +827,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 @@ -833,11 +866,13 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) return TX_CONTINUE; } -static int ieee80211_fragment(struct ieee80211_local *local, +static int ieee80211_fragment(struct ieee80211_tx_data *tx, struct sk_buff *skb, int hdrlen, int frag_threshold) { - struct sk_buff *tail = skb, *tmp; + struct ieee80211_local *local = tx->local; + struct ieee80211_tx_info *info; + struct sk_buff *tmp; int per_fragm = frag_threshold - hdrlen - FCS_LEN; int pos = hdrlen + per_fragm; int rem = skb->len - hdrlen - per_fragm; @@ -845,6 +880,8 @@ static int ieee80211_fragment(struct ieee80211_local *local, if (WARN_ON(rem < 0)) return -EINVAL; + /* first fragment was already added to queue by caller */ + while (rem) { int fraglen = per_fragm; @@ -857,12 +894,21 @@ static int ieee80211_fragment(struct ieee80211_local *local, IEEE80211_ENCRYPT_TAILROOM); if (!tmp) return -ENOMEM; - tail->next = tmp; - tail = tmp; + + __skb_queue_tail(&tx->skbs, tmp); + skb_reserve(tmp, local->tx_headroom + IEEE80211_ENCRYPT_HEADROOM); /* copy control information */ memcpy(tmp->cb, skb->cb, sizeof(tmp->cb)); + + info = IEEE80211_SKB_CB(tmp); + info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT | + IEEE80211_TX_CTL_FIRST_FRAGMENT); + + if (rem) + info->flags |= IEEE80211_TX_CTL_MORE_FRAMES; + skb_copy_queue_mapping(tmp, skb); tmp->priority = skb->priority; tmp->dev = skb->dev; @@ -874,7 +920,8 @@ static int ieee80211_fragment(struct ieee80211_local *local, pos += fraglen; } - skb->len = hdrlen + per_fragm; + /* adjust first fragment's length */ + skb_trim(skb, hdrlen + per_fragm); return 0; } @@ -888,7 +935,14 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) int hdrlen; int fragnum; - if (!(tx->flags & IEEE80211_TX_FRAGMENTED)) + /* no matter what happens, tx->skb moves to tx->skbs */ + __skb_queue_tail(&tx->skbs, skb); + tx->skb = NULL; + + if (info->flags & IEEE80211_TX_CTL_DONTFRAG) + return TX_CONTINUE; + + if (tx->local->ops->set_frag_threshold) return TX_CONTINUE; /* @@ -901,7 +955,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; @@ -913,21 +967,21 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) * of the fragments then we will simply pretend to accept the skb * but store it away as pending. */ - if (ieee80211_fragment(tx->local, skb, hdrlen, frag_threshold)) + if (ieee80211_fragment(tx, skb, hdrlen, frag_threshold)) return TX_DROP; /* update duration/seq/flags of fragments */ fragnum = 0; - do { + + skb_queue_walk(&tx->skbs, skb) { int next_len; const __le16 morefrags = cpu_to_le16(IEEE80211_FCTL_MOREFRAGS); hdr = (void *)skb->data; info = IEEE80211_SKB_CB(skb); - if (skb->next) { + if (!skb_queue_is_last(&tx->skbs, skb)) { hdr->frame_control |= morefrags; - next_len = skb->next->len; /* * No multi-rate retries for fragmented frames, that * would completely throw off the NAV at other STAs. @@ -942,10 +996,9 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) hdr->frame_control &= ~morefrags; next_len = 0; } - hdr->duration_id = ieee80211_duration(tx, 0, next_len); hdr->seq_ctrl |= cpu_to_le16(fragnum & IEEE80211_SCTL_FRAG); fragnum++; - } while ((skb = skb->next)); + } return TX_CONTINUE; } @@ -953,16 +1006,16 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) static ieee80211_tx_result debug_noinline ieee80211_tx_h_stats(struct ieee80211_tx_data *tx) { - struct sk_buff *skb = tx->skb; + struct sk_buff *skb; if (!tx->sta) return TX_CONTINUE; tx->sta->tx_packets++; - do { + skb_queue_walk(&tx->skbs, skb) { tx->sta->tx_fragments++; tx->sta->tx_bytes += skb->len; - } while ((skb = skb->next)); + } return TX_CONTINUE; } @@ -970,8 +1023,6 @@ ieee80211_tx_h_stats(struct ieee80211_tx_data *tx) static ieee80211_tx_result debug_noinline ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); - if (!tx->key) return TX_CONTINUE; @@ -986,13 +1037,7 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx) case WLAN_CIPHER_SUITE_AES_CMAC: return ieee80211_crypto_aes_cmac_encrypt(tx); default: - /* handle hw-only algorithm */ - if (info->control.hw_key) { - ieee80211_tx_set_protected(tx); - return TX_CONTINUE; - } - break; - + return ieee80211_crypto_hw_encrypt(tx); } return TX_DROP; @@ -1001,121 +1046,31 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx) static ieee80211_tx_result debug_noinline ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx) { - struct sk_buff *skb = tx->skb; + struct sk_buff *skb; struct ieee80211_hdr *hdr; int next_len; bool group_addr; - do { + skb_queue_walk(&tx->skbs, skb) { hdr = (void *) skb->data; if (unlikely(ieee80211_is_pspoll(hdr->frame_control))) break; /* must not overwrite AID */ - next_len = skb->next ? skb->next->len : 0; + if (!skb_queue_is_last(&tx->skbs, skb)) { + struct sk_buff *next = skb_queue_next(&tx->skbs, skb); + next_len = next->len; + } else + next_len = 0; group_addr = is_multicast_ether_addr(hdr->addr1); hdr->duration_id = - ieee80211_duration(tx, group_addr, next_len); - } while ((skb = skb->next)); + ieee80211_duration(tx, skb, group_addr, next_len); + } return TX_CONTINUE; } /* 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, @@ -1123,9 +1078,12 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx, int tid) { bool queued = false; + bool reset_agg_timer = false; + struct sk_buff *purge_skb = NULL; if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { info->flags |= IEEE80211_TX_CTL_AMPDU; + reset_agg_timer = true; } else if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) { /* * nothing -- this aggregation session is being started @@ -1157,15 +1115,26 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx, /* do nothing, let packet pass through */ } else if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { info->flags |= IEEE80211_TX_CTL_AMPDU; + reset_agg_timer = true; } else { queued = true; info->control.vif = &tx->sdata->vif; info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; __skb_queue_tail(&tid_tx->pending, skb); + if (skb_queue_len(&tid_tx->pending) > STA_MAX_TX_BUFFER) + purge_skb = __skb_dequeue(&tid_tx->pending); } spin_unlock(&tx->sta->lock); + + if (purge_skb) + dev_kfree_skb(purge_skb); } + /* reset session timer */ + if (reset_agg_timer && tid_tx->timeout) + mod_timer(&tid_tx->session_timer, + TU_TO_EXP_TIME(tid_tx->timeout)); + return queued; } @@ -1180,7 +1149,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 +1157,7 @@ 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; - } + __skb_queue_head_init(&tx->skbs); /* * If this flag is set to true anywhere, and we get here, @@ -1230,7 +1180,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); @@ -1251,54 +1203,38 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, if (is_multicast_ether_addr(hdr->addr1)) { tx->flags &= ~IEEE80211_TX_UNICAST; info->flags |= IEEE80211_TX_CTL_NO_ACK; - } else { + } else 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; - } - 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; } -/* - * Returns false if the frame couldn't be transmitted but was queued instead. - */ -static bool __ieee80211_tx(struct ieee80211_local *local, struct sk_buff **skbp, - struct sta_info *sta, bool txpending) +static bool ieee80211_tx_frags(struct ieee80211_local *local, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct sk_buff_head *skbs, + bool txpending) { - struct sk_buff *skb = *skbp, *next; + struct sk_buff *skb, *tmp; struct ieee80211_tx_info *info; - struct ieee80211_sub_if_data *sdata; unsigned long flags; - int len; - bool fragm = false; - while (skb) { + skb_queue_walk_safe(skbs, skb, tmp) { int q = skb_get_queue_mapping(skb); - __le16 fc; spin_lock_irqsave(&local->queue_stop_reason_lock, flags); if (local->queue_stop_reasons[q] || @@ -1308,24 +1244,11 @@ static bool __ieee80211_tx(struct ieee80211_local *local, struct sk_buff **skbp, * transmission from the tx-pending tasklet when the * queue is woken again. */ - - do { - next = skb->next; - skb->next = NULL; - /* - * NB: If txpending is true, next must already - * be NULL since we must've gone through this - * loop before already; therefore we can just - * queue the frame to the head without worrying - * about reordering of fragments. - */ - if (unlikely(txpending)) - __skb_queue_head(&local->pending[q], - skb); - else - __skb_queue_tail(&local->pending[q], - skb); - } while ((skb = next)); + if (txpending) + skb_queue_splice_init(skbs, &local->pending[q]); + else + skb_queue_splice_tail_init(skbs, + &local->pending[q]); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); @@ -1334,47 +1257,72 @@ static bool __ieee80211_tx(struct ieee80211_local *local, struct sk_buff **skbp, spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); info = IEEE80211_SKB_CB(skb); + info->control.vif = vif; + info->control.sta = sta; - if (fragm) - info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT | - IEEE80211_TX_CTL_FIRST_FRAGMENT); - - next = skb->next; - len = skb->len; + __skb_unlink(skb, skbs); + drv_tx(local, skb); + } - if (next) - info->flags |= IEEE80211_TX_CTL_MORE_FRAMES; + return true; +} - sdata = vif_to_sdata(info->control.vif); +/* + * Returns false if the frame couldn't be transmitted but was queued instead. + */ +static bool __ieee80211_tx(struct ieee80211_local *local, + struct sk_buff_head *skbs, int led_len, + struct sta_info *sta, bool txpending) +{ + struct ieee80211_tx_info *info; + struct ieee80211_sub_if_data *sdata; + struct ieee80211_vif *vif; + struct ieee80211_sta *pubsta; + struct sk_buff *skb; + bool result = true; + __le16 fc; - switch (sdata->vif.type) { - case NL80211_IFTYPE_MONITOR: - info->control.vif = NULL; - break; - case NL80211_IFTYPE_AP_VLAN: - info->control.vif = &container_of(sdata->bss, - struct ieee80211_sub_if_data, u.ap)->vif; - break; - default: - /* keep */ - break; - } + if (WARN_ON(skb_queue_empty(skbs))) + return true; - if (sta && sta->uploaded) - info->control.sta = &sta->sta; - else - info->control.sta = NULL; + skb = skb_peek(skbs); + fc = ((struct ieee80211_hdr *)skb->data)->frame_control; + info = IEEE80211_SKB_CB(skb); + sdata = vif_to_sdata(info->control.vif); + if (sta && !sta->uploaded) + sta = NULL; - fc = ((struct ieee80211_hdr *)skb->data)->frame_control; - drv_tx(local, skb); + if (sta) + pubsta = &sta->sta; + else + pubsta = NULL; - ieee80211_tpt_led_trig_tx(local, fc, len); - *skbp = skb = next; - ieee80211_led_tx(local, 1); - fragm = true; + switch (sdata->vif.type) { + case NL80211_IFTYPE_MONITOR: + sdata = NULL; + vif = NULL; + break; + case NL80211_IFTYPE_AP_VLAN: + sdata = container_of(sdata->bss, + struct ieee80211_sub_if_data, u.ap); + /* fall through */ + default: + vif = &sdata->vif; + break; } - return true; + if (local->ops->tx_frags) + drv_tx_frags(local, vif, pubsta, skbs); + else + result = ieee80211_tx_frags(local, vif, pubsta, skbs, + txpending); + + ieee80211_tpt_led_trig_tx(local, fc, led_len); + ieee80211_led_tx(local, 1); + + WARN_ON_ONCE(!skb_queue_empty(skbs)); + + return result; } /* @@ -1383,8 +1331,7 @@ static bool __ieee80211_tx(struct ieee80211_local *local, struct sk_buff **skbp, */ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) { - struct sk_buff *skb = tx->skb; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); ieee80211_tx_result res = TX_DROP; #define CALL_TXH(txh) \ @@ -1402,8 +1349,11 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) if (!(tx->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)) CALL_TXH(ieee80211_tx_h_rate_ctrl); - if (unlikely(info->flags & IEEE80211_TX_INTFL_RETRANSMISSION)) + if (unlikely(info->flags & IEEE80211_TX_INTFL_RETRANSMISSION)) { + __skb_queue_tail(&tx->skbs, tx->skb); + tx->skb = NULL; goto txh_done; + } CALL_TXH(ieee80211_tx_h_michael_mic_add); CALL_TXH(ieee80211_tx_h_sequence); @@ -1418,13 +1368,10 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) txh_done: if (unlikely(res == TX_DROP)) { I802_DEBUG_INC(tx->local->tx_handlers_drop); - while (skb) { - struct sk_buff *next; - - next = skb->next; - dev_kfree_skb(skb); - skb = next; - } + if (tx->skb) + dev_kfree_skb(tx->skb); + else + ieee80211_purge_tx_queue(&tx->local->hw, &tx->skbs); return -1; } else if (unlikely(res == TX_QUEUED)) { I802_DEBUG_INC(tx->local->tx_handlers_queued); @@ -1445,6 +1392,7 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, ieee80211_tx_result res_prepare; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); bool result = true; + int led_len; if (unlikely(skb->len < 10)) { dev_kfree_skb(skb); @@ -1454,6 +1402,7 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, rcu_read_lock(); /* initialises tx */ + led_len = skb->len; res_prepare = ieee80211_tx_prepare(sdata, &tx, skb); if (unlikely(res_prepare == TX_DROP)) { @@ -1467,7 +1416,8 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, info->band = tx.channel->band; if (!invoke_tx_handlers(&tx)) - result = __ieee80211_tx(local, &tx.skb, tx.sta, txpending); + result = __ieee80211_tx(local, &tx.skbs, led_len, + tx.sta, txpending); out: rcu_read_unlock(); return result; @@ -1475,28 +1425,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 +1451,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 +1472,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; @@ -1591,17 +1484,100 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, if (ieee80211_vif_is_mesh(&sdata->vif) && ieee80211_is_data(hdr->frame_control) && !is_multicast_ether_addr(hdr->addr1)) - if (mesh_nexthop_lookup(skb, sdata)) { + if (mesh_nexthop_resolve(skb, sdata)) { /* skb queued: don't free */ rcu_read_unlock(); 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 +1586,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 +1640,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 +1737,11 @@ 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; - struct sk_buff *tmp_skb; + bool wme_sta = false, authorized = false, tdls_auth = false; + bool tdls_direct = false; + bool multicast; + u32 info_flags = 0; + u16 info_id = 0; if (unlikely(skb->len < ETH_HLEN)) { ret = NETDEV_TX_OK; @@ -1730,7 +1765,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 +1854,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 +1905,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; @@ -1852,16 +1928,23 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, * if it is a multicast address (which can only happen * in AP mode) */ - if (!is_multicast_ether_addr(hdr.addr1)) { + multicast = is_multicast_ether_addr(hdr.addr1); + if (!multicast) { 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 +1953,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" @@ -1889,11 +1970,54 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, goto fail; } + if (unlikely(!multicast && skb->sk && + skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) { + struct sk_buff *orig_skb = skb; + + skb = skb_clone(skb, GFP_ATOMIC); + if (skb) { + unsigned long flags; + int id, r; + + spin_lock_irqsave(&local->ack_status_lock, flags); + r = idr_get_new_above(&local->ack_status_frames, + orig_skb, 1, &id); + if (r == -EAGAIN) { + idr_pre_get(&local->ack_status_frames, + GFP_ATOMIC); + r = idr_get_new_above(&local->ack_status_frames, + orig_skb, 1, &id); + } + if (WARN_ON(!id) || id > 0xffff) { + idr_remove(&local->ack_status_frames, id); + r = -ERANGE; + } + spin_unlock_irqrestore(&local->ack_status_lock, flags); + + if (!r) { + info_id = id; + info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + } else if (skb_shared(skb)) { + kfree_skb(orig_skb); + } else { + kfree_skb(skb); + skb = orig_skb; + } + } else { + /* couldn't clone -- lose tx status ... */ + skb = orig_skb; + } + } + /* * If the skb is shared we need to obtain our own copy. */ if (skb_shared(skb)) { - tmp_skb = skb; + struct sk_buff *tmp_skb = skb; + + /* can't happen -- skb is a clone if info_id != 0 */ + WARN_ON(info_id); + skb = skb_clone(skb, GFP_ATOMIC); kfree_skb(tmp_skb); @@ -1946,7 +2070,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; } @@ -1994,6 +2118,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, memset(info, 0, sizeof(*info)); dev->trans_start = jiffies; + + info->flags = info_flags; + info->ack_frame_id = info_id; + ieee80211_xmit(sdata, skb); return NETDEV_TX_OK; @@ -2012,10 +2140,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, */ void ieee80211_clear_tx_pending(struct ieee80211_local *local) { + struct sk_buff *skb; int i; - for (i = 0; i < local->hw.queues; i++) - skb_queue_purge(&local->pending[i]); + for (i = 0; i < local->hw.queues; i++) { + while ((skb = skb_dequeue(&local->pending[i])) != NULL) + ieee80211_free_txskb(&local->hw, skb); + } } /* @@ -2037,10 +2168,15 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) { result = ieee80211_tx(sdata, skb, true); } else { + struct sk_buff_head skbs; + + __skb_queue_head_init(&skbs); + __skb_queue_tail(&skbs, skb); + hdr = (struct ieee80211_hdr *)skb->data; sta = sta_info_get(sdata, hdr->addr1); - result = __ieee80211_tx(local, &skb, sta, true); + result = __ieee80211_tx(local, &skbs, skb->len, sta, true); } return result; @@ -2099,7 +2235,8 @@ void ieee80211_tx_pending(unsigned long data) /* functions for drivers to get certain frames */ -static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss, +static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, + struct ieee80211_if_ap *bss, struct sk_buff *skb, struct beacon_data *beacon) { @@ -2116,7 +2253,7 @@ static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss, IEEE80211_MAX_AID+1); if (bss->dtim_count == 0) - bss->dtim_count = beacon->dtim_period - 1; + bss->dtim_count = sdata->vif.bss_conf.dtim_period - 1; else bss->dtim_count--; @@ -2124,7 +2261,7 @@ static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss, *pos++ = WLAN_EID_TIM; *pos++ = 4; *pos++ = bss->dtim_count; - *pos++ = beacon->dtim_period; + *pos++ = sdata->vif.bss_conf.dtim_period; if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf)) aid0 = 1; @@ -2153,10 +2290,10 @@ static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss, /* Bitmap control */ *pos++ = n1 | aid0; /* Part Virt Bitmap */ + skb_put(skb, n2 - n1); memcpy(pos, bss->tim + n1, n2 - n1 + 1); tim[1] = n2 - n1 + 4; - skb_put(skb, n2 - n1); } else { *pos++ = aid0; /* Bitmap control */ *pos++ = 0; /* Part Virt Bitmap */ @@ -2217,13 +2354,15 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, * of the tim bitmap in mac80211 and the driver. */ if (local->tim_in_locked_section) { - ieee80211_beacon_add_tim(ap, skb, beacon); + ieee80211_beacon_add_tim(sdata, ap, skb, + beacon); } else { unsigned long flags; - spin_lock_irqsave(&local->sta_lock, flags); - ieee80211_beacon_add_tim(ap, skb, beacon); - spin_unlock_irqrestore(&local->sta_lock, flags); + spin_lock_irqsave(&local->tim_lock, flags); + ieee80211_beacon_add_tim(sdata, ap, skb, + beacon); + spin_unlock_irqrestore(&local->tim_lock, flags); } if (tim_offset) @@ -2254,22 +2393,31 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, } else if (ieee80211_vif_is_mesh(&sdata->vif)) { struct ieee80211_mgmt *mgmt; u8 *pos; + int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) + + sizeof(mgmt->u.beacon); #ifdef CONFIG_MAC80211_MESH if (!sdata->u.mesh.mesh_id_len) goto out; #endif - /* headroom, head length, tail length and maximum TIM length */ - skb = dev_alloc_skb(local->tx_headroom + 400 + - sdata->u.mesh.ie_len); + skb = dev_alloc_skb(local->tx_headroom + + hdr_len + + 2 + /* NULL SSID */ + 2 + 8 + /* supported rates */ + 2 + 3 + /* DS params */ + 2 + (IEEE80211_MAX_SUPP_RATES - 8) + + 2 + sizeof(struct ieee80211_ht_cap) + + 2 + sizeof(struct ieee80211_ht_info) + + 2 + sdata->u.mesh.mesh_id_len + + 2 + sizeof(struct ieee80211_meshconf_ie) + + sdata->u.mesh.ie_len); if (!skb) goto out; skb_reserve(skb, local->hw.extra_tx_headroom); - mgmt = (struct ieee80211_mgmt *) - skb_put(skb, 24 + sizeof(mgmt->u.beacon)); - memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon)); + mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); + memset(mgmt, 0, hdr_len); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); memset(mgmt->da, 0xff, ETH_ALEN); @@ -2277,13 +2425,25 @@ 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_ht_cap_ie(skb, sdata) || + mesh_add_ht_info_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; @@ -2306,6 +2466,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, txrc.max_rate_idx = -1; else txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; + memcpy(txrc.rate_idx_mcs_mask, sdata->rc_rateidx_mcs_mask[band], + sizeof(txrc.rate_idx_mcs_mask)); txrc.bss = true; rate_control_get_rate(sdata, NULL, &txrc); @@ -2320,6 +2482,37 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_beacon_get_tim); +struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ieee80211_if_ap *ap = NULL; + struct sk_buff *presp = NULL, *skb = NULL; + struct ieee80211_hdr *hdr; + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + if (sdata->vif.type != NL80211_IFTYPE_AP) + return NULL; + + rcu_read_lock(); + + ap = &sdata->u.ap; + presp = rcu_dereference(ap->probe_resp); + if (!presp) + goto out; + + skb = skb_copy(presp, GFP_ATOMIC); + if (!skb) + goto out; + + hdr = (struct ieee80211_hdr *) skb->data; + memset(hdr->addr1, 0, sizeof(hdr->addr1)); + +out: + rcu_read_unlock(); + return skb; +} +EXPORT_SYMBOL(ieee80211_proberesp_get); + struct sk_buff *ieee80211_pspoll_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -2337,11 +2530,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 +2568,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 +2605,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); @@ -2539,15 +2725,15 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_get_buffered_bc); -void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) +void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, int tid) { 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; + skb_set_queue_mapping(skb, ieee802_1d_to_ac[tid]); + skb->priority = tid; /* * The other path calling ieee80211_xmit is from the tasklet, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 11d9d49..73ef163 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -96,13 +97,13 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx) { - struct sk_buff *skb = tx->skb; + struct sk_buff *skb; struct ieee80211_hdr *hdr; - do { + skb_queue_walk(&tx->skbs, skb) { hdr = (struct ieee80211_hdr *) skb->data; hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); - } while ((skb = skb->next)); + } } int ieee80211_frame_duration(struct ieee80211_local *local, size_t len, @@ -368,14 +369,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 +391,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 +402,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, @@ -567,12 +565,6 @@ void ieee80211_queue_delayed_work(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_queue_delayed_work); -void ieee802_11_parse_elems(u8 *start, size_t len, - struct ieee802_11_elems *elems) -{ - 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) @@ -580,24 +572,65 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, size_t left = len; u8 *pos = start; bool calc_crc = filter != 0; + DECLARE_BITMAP(seen_elems, 256); + bitmap_zero(seen_elems, 256); memset(elems, 0, sizeof(*elems)); elems->ie_start = start; elems->total_len = len; while (left >= 2) { u8 id, elen; + bool elem_parse_failed; id = *pos++; elen = *pos++; left -= 2; - if (elen > left) + if (elen > left) { + elems->parse_error = true; + break; + } + + switch (id) { + case WLAN_EID_SSID: + case WLAN_EID_SUPP_RATES: + case WLAN_EID_FH_PARAMS: + case WLAN_EID_DS_PARAMS: + case WLAN_EID_CF_PARAMS: + case WLAN_EID_TIM: + case WLAN_EID_IBSS_PARAMS: + case WLAN_EID_CHALLENGE: + case WLAN_EID_RSN: + case WLAN_EID_ERP_INFO: + case WLAN_EID_EXT_SUPP_RATES: + case WLAN_EID_HT_CAPABILITY: + case WLAN_EID_MESH_ID: + case WLAN_EID_MESH_CONFIG: + case WLAN_EID_PEER_MGMT: + case WLAN_EID_PREQ: + case WLAN_EID_PREP: + case WLAN_EID_PERR: + case WLAN_EID_RANN: + case WLAN_EID_CHANNEL_SWITCH: + case WLAN_EID_EXT_CHANSWITCH_ANN: + case WLAN_EID_COUNTRY: + case WLAN_EID_PWR_CONSTRAINT: + case WLAN_EID_TIMEOUT_INTERVAL: + if (test_bit(id, seen_elems)) { + elems->parse_error = true; + left -= elen; + pos += elen; + continue; + } break; + } if (calc_crc && id < 64 && (filter & (1ULL << id))) crc = crc32_be(crc, pos - 2, elen + 2); + elem_parse_failed = false; + switch (id) { case WLAN_EID_SSID: elems->ssid = pos; @@ -623,7 +656,8 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, if (elen >= sizeof(struct ieee80211_tim_ie)) { elems->tim = (void *)pos; elems->tim_len = elen; - } + } else + elem_parse_failed = true; break; case WLAN_EID_IBSS_PARAMS: elems->ibss_params = pos; @@ -672,10 +706,14 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, case WLAN_EID_HT_CAPABILITY: if (elen >= sizeof(struct ieee80211_ht_cap)) elems->ht_cap_elem = (void *)pos; + else + elem_parse_failed = true; break; case WLAN_EID_HT_INFORMATION: if (elen >= sizeof(struct ieee80211_ht_info)) elems->ht_info_elem = (void *)pos; + else + elem_parse_failed = true; break; case WLAN_EID_MESH_ID: elems->mesh_id = pos; @@ -684,10 +722,12 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, case WLAN_EID_MESH_CONFIG: if (elen >= sizeof(struct ieee80211_meshconf_ie)) elems->mesh_config = (void *)pos; + else + elem_parse_failed = true; break; - case WLAN_EID_PEER_LINK: - elems->peer_link = pos; - elems->peer_link_len = elen; + case WLAN_EID_PEER_MGMT: + elems->peering = pos; + elems->peering_len = elen; break; case WLAN_EID_PREQ: elems->preq = pos; @@ -704,6 +744,8 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, case WLAN_EID_RANN: if (elen >= sizeof(struct ieee80211_rann_ie)) elems->rann = (void *)pos; + else + elem_parse_failed = true; break; case WLAN_EID_CHANNEL_SWITCH: elems->ch_switch_elem = pos; @@ -732,14 +774,29 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, break; } + if (elem_parse_failed) + elems->parse_error = true; + else + set_bit(id, seen_elems); + left -= elen; pos += elen; } + if (left != 0) + elems->parse_error = true; + return crc; } -void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata) +void ieee802_11_parse_elems(u8 *start, size_t len, + struct ieee802_11_elems *elems) +{ + ieee802_11_parse_elems_crc(start, len, elems, 0, 0); +} + +void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, + bool bss_notify) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_queue_params qparam; @@ -755,7 +812,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata) use_11b = (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ) && !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE); - for (queue = 0; queue < local_to_hw(local)->queues; queue++) { + for (queue = 0; queue < local->hw.queues; queue++) { /* Set defaults according to 802.11-2007 Table 7-37 */ aCWmax = 1023; if (use_11b) @@ -799,7 +856,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, @@ -808,7 +866,9 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata) if (sdata->vif.type != NL80211_IFTYPE_MONITOR) { sdata->vif.bss_conf.qos = sdata->vif.type != NL80211_IFTYPE_STATION; - ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS); + if (bss_notify) + ieee80211_bss_info_change_notify(sdata, + BSS_CHANGED_QOS); } } @@ -830,7 +890,7 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, else sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; - ieee80211_set_wmm_default(sdata); + ieee80211_set_wmm_default(sdata, true); } u32 ieee80211_mandatory_rates(struct ieee80211_local *local, @@ -863,8 +923,8 @@ u32 ieee80211_mandatory_rates(struct ieee80211_local *local, void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, - u8 *extra, size_t extra_len, const u8 *bssid, - const u8 *key, u8 key_len, u8 key_idx) + u8 *extra, size_t extra_len, const u8 *da, + const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; @@ -873,18 +933,16 @@ 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); memset(mgmt, 0, 24 + 6); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); - memcpy(mgmt->da, bssid, ETH_ALEN); + memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); memcpy(mgmt->bssid, bssid, ETH_ALEN); mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg); @@ -982,23 +1040,9 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, offset = noffset; } - if (sband->ht_cap.ht_supported) { - u16 cap = sband->ht_cap.cap; - __le16 tmp; - - *pos++ = WLAN_EID_HT_CAPABILITY; - *pos++ = sizeof(struct ieee80211_ht_cap); - memset(pos, 0, sizeof(struct ieee80211_ht_cap)); - tmp = cpu_to_le16(cap); - memcpy(pos, &tmp, sizeof(u16)); - pos += sizeof(u16); - *pos++ = sband->ht_cap.ampdu_factor | - (sband->ht_cap.ampdu_density << - IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); - memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); - pos += sizeof(sband->ht_cap.mcs); - pos += 2 + 4 + 1; /* ext info, BF cap, antsel */ - } + if (sband->ht_cap.ht_supported) + pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, + sband->ht_cap.cap); /* * If adding more here, adjust code in main.c @@ -1016,9 +1060,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 +1074,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 +1114,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 +1181,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) @@ -1149,56 +1203,69 @@ int ieee80211_reconfig(struct ieee80211_local *local) */ } #endif + /* everything else happens only if HW was up & running */ + if (!local->open_count) + goto wake_up; - /* 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; - } - - ieee80211_led_radio(local, true); - ieee80211_mod_tpt_led_trig(local, - IEEE80211_TPT_LEDTRIG_FL_RADIO, 0); + /* + * 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); + + /* reset coverage class */ + drv_set_coverage_class(local, hw->wiphy->coverage_class); + + 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 && sdata->vif.type != NL80211_IFTYPE_MONITOR && ieee80211_sdata_running(sdata)) - res = drv_add_interface(local, &sdata->vif); + res = drv_add_interface(local, sdata); } /* add STAs back */ mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { if (sta->uploaded) { - sdata = sta->sdata; - if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) - sdata = container_of(sdata->bss, - struct ieee80211_sub_if_data, - u.ap); + enum ieee80211_sta_state state; - WARN_ON(drv_sta_add(local, sdata, &sta->sta)); + for (state = IEEE80211_STA_NOTEXIST; + state < sta->sta_state; state++) + WARN_ON(drv_sta_state(local, sta->sdata, sta, + state, state + 1)); } } 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); @@ -1221,11 +1288,13 @@ int ieee80211_reconfig(struct ieee80211_local *local) BSS_CHANGED_BEACON_INT | BSS_CHANGED_BSSID | BSS_CHANGED_CQM | - BSS_CHANGED_QOS; + BSS_CHANGED_QOS | + BSS_CHANGED_IDLE; switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: - changed |= BSS_CHANGED_ASSOC; + changed |= BSS_CHANGED_ASSOC | + BSS_CHANGED_ARP_FILTER; mutex_lock(&sdata->u.mgd.mtx); ieee80211_bss_info_change_notify(sdata, changed); mutex_unlock(&sdata->u.mgd.mtx); @@ -1234,6 +1303,12 @@ int ieee80211_reconfig(struct ieee80211_local *local) changed |= BSS_CHANGED_IBSS; /* fall through */ case NL80211_IFTYPE_AP: + changed |= BSS_CHANGED_SSID; + + if (sdata->vif.type == NL80211_IFTYPE_AP) + changed |= BSS_CHANGED_AP_PROBE_RESP; + + /* fall through */ case NL80211_IFTYPE_MESH_POINT: changed |= BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED; @@ -1254,6 +1329,25 @@ int ieee80211_reconfig(struct ieee80211_local *local) } } + ieee80211_recalc_ps(local, -1); + + /* + * The sta might be in psm against the ap (e.g. because + * this was the state before a hw restart), so we + * explicitly send a null packet in order to make sure + * it'll sync against the ap (and get out of psm). + */ + if (!(local->hw.conf.flags & IEEE80211_CONF_PS)) { + list_for_each_entry(sdata, &local->interfaces, list) { + if (sdata->vif.type != NL80211_IFTYPE_STATION) + continue; + if (!sdata->u.mgd.associated) + continue; + + ieee80211_send_nullfunc(local, sdata, 0); + } + } + /* add back keys */ list_for_each_entry(sdata, &local->interfaces, list) if (ieee80211_sdata_running(sdata)) @@ -1275,7 +1369,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 +1419,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 +1562,202 @@ 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); + +u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, + u16 cap) +{ + __le16 tmp; + + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_ht_cap); + memset(pos, 0, sizeof(struct ieee80211_ht_cap)); + + /* capability flags */ + tmp = cpu_to_le16(cap); + memcpy(pos, &tmp, sizeof(u16)); + pos += sizeof(u16); + + /* AMPDU parameters */ + *pos++ = ht_cap->ampdu_factor | + (ht_cap->ampdu_density << + IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); + + /* MCS set */ + memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs)); + pos += sizeof(ht_cap->mcs); + + /* extended capabilities */ + pos += sizeof(__le16); + + /* BF capabilities */ + pos += sizeof(__le32); + + /* antenna selection */ + pos += sizeof(u8); + + return pos; +} + +u8 *ieee80211_ie_build_ht_info(u8 *pos, + struct ieee80211_sta_ht_cap *ht_cap, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type) +{ + struct ieee80211_ht_info *ht_info; + /* Build HT Information */ + *pos++ = WLAN_EID_HT_INFORMATION; + *pos++ = sizeof(struct ieee80211_ht_info); + ht_info = (struct ieee80211_ht_info *)pos; + ht_info->control_chan = + ieee80211_frequency_to_channel(channel->center_freq); + switch (channel_type) { + case NL80211_CHAN_HT40MINUS: + ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW; + break; + case NL80211_CHAN_HT40PLUS: + ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + break; + case NL80211_CHAN_HT20: + default: + ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE; + break; + } + if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) + ht_info->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; + + /* + * Note: According to 802.11n-2009 9.13.3.1, HT Protection field and + * RIFS Mode are reserved in IBSS mode, therefore keep them at 0 + */ + ht_info->operation_mode = 0x0000; + ht_info->stbc_param = 0x0000; + + /* It seems that Basic MCS set and Supported MCS set + are identical for the first 10 bytes */ + memset(&ht_info->basic_set, 0, 16); + memcpy(&ht_info->basic_set, &ht_cap->mcs, 10); + + return pos + sizeof(struct ieee80211_ht_info); +} + +enum nl80211_channel_type +ieee80211_ht_info_to_channel_type(struct ieee80211_ht_info *ht_info) +{ + enum nl80211_channel_type channel_type; + + if (!ht_info) + return NL80211_CHAN_NO_HT; + + switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { + case IEEE80211_HT_PARAM_CHA_SEC_NONE: + channel_type = NL80211_CHAN_HT20; + break; + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + channel_type = NL80211_CHAN_HT40PLUS; + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + channel_type = NL80211_CHAN_HT40MINUS; + break; + default: + channel_type = NL80211_CHAN_NO_HT; + } + + return channel_type; +} + +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..9464f92 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; @@ -263,16 +265,14 @@ static int ieee80211_wep_decrypt(struct ieee80211_local *local, } -bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key) +static bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, + struct ieee80211_key *key) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; unsigned int hdrlen; u8 *ivpos; u32 iv; - if (!ieee80211_has_protected(hdr->frame_control)) - return false; - hdrlen = ieee80211_hdrlen(hdr->frame_control); ivpos = skb->data + hdrlen; iv = (ivpos[0] << 16) | (ivpos[1] << 8) | ivpos[2]; @@ -286,18 +286,27 @@ ieee80211_crypto_wep_decrypt(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; + __le16 fc = hdr->frame_control; - if (!ieee80211_is_data(hdr->frame_control) && - !ieee80211_is_auth(hdr->frame_control)) + if (!ieee80211_is_data(fc) && !ieee80211_is_auth(fc)) return RX_CONTINUE; if (!(status->flag & RX_FLAG_DECRYPTED)) { + if (skb_linearize(rx->skb)) + return RX_DROP_UNUSABLE; + if (rx->sta && ieee80211_wep_is_weak_iv(rx->skb, rx->key)) + rx->sta->wep_weak_iv_count++; if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key)) return RX_DROP_UNUSABLE; } else if (!(status->flag & RX_FLAG_IV_STRIPPED)) { + if (!pskb_may_pull(rx->skb, ieee80211_hdrlen(fc) + WEP_IV_LEN)) + return RX_DROP_UNUSABLE; + if (rx->sta && ieee80211_wep_is_weak_iv(rx->skb, rx->key)) + rx->sta->wep_weak_iv_count++; ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key); /* remove ICV */ - skb_trim(rx->skb, rx->skb->len - WEP_ICV_LEN); + if (pskb_trim(rx->skb, rx->skb->len - WEP_ICV_LEN)) + return RX_DROP_UNUSABLE; } return RX_CONTINUE; @@ -330,13 +339,12 @@ ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx) ieee80211_tx_set_protected(tx); - skb = tx->skb; - do { + skb_queue_walk(&tx->skbs, skb) { if (wep_encrypt_skb(tx, skb) < 0) { I802_DEBUG_INC(tx->local->tx_handlers_drop_wep); return TX_DROP; } - } while ((skb = skb->next)); + } return TX_CONTINUE; } diff --git a/net/mac80211/wep.h b/net/mac80211/wep.h index 01e5484..9615749 100644 --- a/net/mac80211/wep.h +++ b/net/mac80211/wep.h @@ -25,7 +25,6 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local, const u8 *key, int keylen, int keyidx); int ieee80211_wep_decrypt_data(struct crypto_cipher *tfm, u8 *rc4key, size_t klen, u8 *data, size_t data_len); -bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key); ieee80211_rx_result ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx); diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 28bc084..89511be 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -52,6 +52,30 @@ static int wme_downgrade_ac(struct sk_buff *skb) } } +/* Indicate which queue to use for this fully formed 802.11 frame */ +u16 ieee80211_select_queue_80211(struct ieee80211_local *local, + struct sk_buff *skb, + struct ieee80211_hdr *hdr) +{ + u8 *p; + + if (local->hw.queues < 4) + return 0; + + if (!ieee80211_is_data(hdr->frame_control)) { + skb->priority = 7; + return ieee802_1d_to_ac[skb->priority]; + } + if (!ieee80211_is_data_qos(hdr->frame_control)) { + skb->priority = 0; + return ieee802_1d_to_ac[skb->priority]; + } + + p = ieee80211_get_qos_ctl(hdr); + skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK; + + return ieee80211_downgrade_queue(local, skb); +} /* Indicate which queue to use. */ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, @@ -72,7 +96,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 +107,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. - */ + qos = true; break; #endif case NL80211_IFTYPE_STATION: @@ -103,7 +123,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,22 +159,31 @@ 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; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); /* Fill in the QoS header if there is one. */ if (ieee80211_is_data_qos(hdr->frame_control)) { u8 *p = ieee80211_get_qos_ctl(hdr); - u8 ack_policy = 0, tid; + u8 ack_policy, tid; 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 */ + /* preserve EOSP bit */ + ack_policy = *p & IEEE80211_QOS_CTL_EOSP; + + if (is_multicast_ether_addr(hdr->addr1) || + sdata->noack_map & BIT(tid)) { + ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK; + info->flags |= IEEE80211_TX_CTL_NO_ACK; + } + + /* 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..94edceb 100644 --- a/net/mac80211/wme.h +++ b/net/mac80211/wme.h @@ -13,16 +13,15 @@ #include #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_80211(struct ieee80211_local *local, + struct sk_buff *skb, + struct ieee80211_hdr *hdr); 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..a74f538 100644 --- a/net/mac80211/work.c +++ b/net/mac80211/work.c @@ -25,17 +25,11 @@ #include "ieee80211_i.h" #include "rate.h" - -#define IEEE80211_AUTH_TIMEOUT (HZ / 5) -#define IEEE80211_AUTH_MAX_TRIES 3 -#define IEEE80211_ASSOC_TIMEOUT (HZ / 5) -#define IEEE80211_ASSOC_MAX_TRIES 3 +#include "driver-ops.h" enum work_action { - WORK_ACT_MISMATCH, WORK_ACT_NONE, WORK_ACT_TIMEOUT, - WORK_ACT_DONE, }; @@ -70,460 +64,6 @@ void free_work(struct ieee80211_work *wk) kfree_rcu(wk, rcu_head); } -static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len, - struct ieee80211_supported_band *sband, - u32 *rates) -{ - int i, j, count; - *rates = 0; - count = 0; - for (i = 0; i < supp_rates_len; i++) { - int rate = (supp_rates[i] & 0x7F) * 5; - - for (j = 0; j < sband->n_bitrates; j++) - if (sband->bitrates[j].bitrate == rate) { - *rates |= BIT(j); - count++; - break; - } - } - - return count; -} - -/* frame sending functions */ - -static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie, - struct ieee80211_supported_band *sband, - struct ieee80211_channel *channel, - enum ieee80211_smps_mode smps) -{ - struct ieee80211_ht_info *ht_info; - u8 *pos; - u32 flags = channel->flags; - u16 cap = sband->ht_cap.cap; - __le16 tmp; - - if (!sband->ht_cap.ht_supported) - return; - - if (!ht_info_ie) - return; - - if (ht_info_ie[1] < sizeof(struct ieee80211_ht_info)) - return; - - ht_info = (struct ieee80211_ht_info *)(ht_info_ie + 2); - - /* determine capability flags */ - - switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { - case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: - if (flags & IEEE80211_CHAN_NO_HT40PLUS) { - cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; - cap &= ~IEEE80211_HT_CAP_SGI_40; - } - break; - case IEEE80211_HT_PARAM_CHA_SEC_BELOW: - if (flags & IEEE80211_CHAN_NO_HT40MINUS) { - cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; - cap &= ~IEEE80211_HT_CAP_SGI_40; - } - break; - } - - /* set SM PS mode properly */ - cap &= ~IEEE80211_HT_CAP_SM_PS; - switch (smps) { - case IEEE80211_SMPS_AUTOMATIC: - case IEEE80211_SMPS_NUM_MODES: - WARN_ON(1); - case IEEE80211_SMPS_OFF: - cap |= WLAN_HT_CAP_SM_PS_DISABLED << - IEEE80211_HT_CAP_SM_PS_SHIFT; - break; - case IEEE80211_SMPS_STATIC: - cap |= WLAN_HT_CAP_SM_PS_STATIC << - IEEE80211_HT_CAP_SM_PS_SHIFT; - break; - case IEEE80211_SMPS_DYNAMIC: - cap |= WLAN_HT_CAP_SM_PS_DYNAMIC << - IEEE80211_HT_CAP_SM_PS_SHIFT; - break; - } - - /* reserve and fill IE */ - - pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); - *pos++ = WLAN_EID_HT_CAPABILITY; - *pos++ = sizeof(struct ieee80211_ht_cap); - memset(pos, 0, sizeof(struct ieee80211_ht_cap)); - - /* capability flags */ - tmp = cpu_to_le16(cap); - memcpy(pos, &tmp, sizeof(u16)); - pos += sizeof(u16); - - /* AMPDU parameters */ - *pos++ = sband->ht_cap.ampdu_factor | - (sband->ht_cap.ampdu_density << - IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); - - /* MCS set */ - memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); - pos += sizeof(sband->ht_cap.mcs); - - /* extended capabilities */ - pos += sizeof(__le16); - - /* BF capabilities */ - pos += sizeof(__le32); - - /* antenna selection */ - pos += sizeof(u8); -} - -static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, - struct ieee80211_work *wk) -{ - struct ieee80211_local *local = sdata->local; - struct sk_buff *skb; - struct ieee80211_mgmt *mgmt; - u8 *pos, qos_info; - size_t offset = 0, noffset; - int i, count, rates_len, supp_rates_len; - u16 capab; - struct ieee80211_supported_band *sband; - u32 rates = 0; - - sband = local->hw.wiphy->bands[wk->chan->band]; - - if (wk->assoc.supp_rates_len) { - /* - * Get all rates supported by the device and the AP as - * some APs don't like getting a superset of their rates - * in the association request (e.g. D-Link DAP 1353 in - * b-only mode)... - */ - rates_len = ieee80211_compatible_rates(wk->assoc.supp_rates, - wk->assoc.supp_rates_len, - sband, &rates); - } else { - /* - * In case AP not provide any supported rates information - * before association, we send information element(s) with - * all rates that we support. - */ - rates = ~0; - rates_len = sband->n_bitrates; - } - - skb = alloc_skb(local->hw.extra_tx_headroom + - sizeof(*mgmt) + /* bit too much but doesn't matter */ - 2 + wk->assoc.ssid_len + /* SSID */ - 4 + rates_len + /* (extended) rates */ - 4 + /* power capability */ - 2 + 2 * sband->n_channels + /* supported channels */ - 2 + sizeof(struct ieee80211_ht_cap) + /* HT */ - 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); - return; - } - skb_reserve(skb, local->hw.extra_tx_headroom); - - capab = WLAN_CAPABILITY_ESS; - - if (sband->band == IEEE80211_BAND_2GHZ) { - 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; - } - - if (wk->assoc.capability & WLAN_CAPABILITY_PRIVACY) - capab |= WLAN_CAPABILITY_PRIVACY; - - if ((wk->assoc.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) && - (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)) - capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; - - mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); - memset(mgmt, 0, 24); - memcpy(mgmt->da, wk->filter_ta, ETH_ALEN); - memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); - memcpy(mgmt->bssid, wk->filter_ta, ETH_ALEN); - - if (!is_zero_ether_addr(wk->assoc.prev_bssid)) { - skb_put(skb, 10); - mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_REASSOC_REQ); - mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab); - mgmt->u.reassoc_req.listen_interval = - cpu_to_le16(local->hw.conf.listen_interval); - memcpy(mgmt->u.reassoc_req.current_ap, wk->assoc.prev_bssid, - ETH_ALEN); - } else { - skb_put(skb, 4); - mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_ASSOC_REQ); - mgmt->u.assoc_req.capab_info = cpu_to_le16(capab); - mgmt->u.assoc_req.listen_interval = - cpu_to_le16(local->hw.conf.listen_interval); - } - - /* SSID */ - pos = skb_put(skb, 2 + wk->assoc.ssid_len); - *pos++ = WLAN_EID_SSID; - *pos++ = wk->assoc.ssid_len; - memcpy(pos, wk->assoc.ssid, wk->assoc.ssid_len); - - /* add all rates which were marked to be used above */ - supp_rates_len = rates_len; - if (supp_rates_len > 8) - supp_rates_len = 8; - - pos = skb_put(skb, supp_rates_len + 2); - *pos++ = WLAN_EID_SUPP_RATES; - *pos++ = supp_rates_len; - - count = 0; - for (i = 0; i < sband->n_bitrates; i++) { - if (BIT(i) & rates) { - int rate = sband->bitrates[i].bitrate; - *pos++ = (u8) (rate / 5); - if (++count == 8) - break; - } - } - - if (rates_len > count) { - pos = skb_put(skb, rates_len - count + 2); - *pos++ = WLAN_EID_EXT_SUPP_RATES; - *pos++ = rates_len - count; - - for (i++; i < sband->n_bitrates; i++) { - if (BIT(i) & rates) { - int rate = sband->bitrates[i].bitrate; - *pos++ = (u8) (rate / 5); - } - } - } - - if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) { - /* 1. power capabilities */ - pos = skb_put(skb, 4); - *pos++ = WLAN_EID_PWR_CAPABILITY; - *pos++ = 2; - *pos++ = 0; /* min tx power */ - *pos++ = wk->chan->max_power; /* max tx power */ - - /* 2. supported channels */ - /* TODO: get this in reg domain format */ - pos = skb_put(skb, 2 * sband->n_channels + 2); - *pos++ = WLAN_EID_SUPPORTED_CHANNELS; - *pos++ = 2 * sband->n_channels; - for (i = 0; i < sband->n_channels; i++) { - *pos++ = ieee80211_frequency_to_channel( - sband->channels[i].center_freq); - *pos++ = 1; /* one channel in the subband*/ - } - } - - /* if present, add any custom IEs that go before HT */ - if (wk->ie_len && wk->ie) { - static const u8 before_ht[] = { - WLAN_EID_SSID, - WLAN_EID_SUPP_RATES, - WLAN_EID_EXT_SUPP_RATES, - WLAN_EID_PWR_CAPABILITY, - WLAN_EID_SUPPORTED_CHANNELS, - WLAN_EID_RSN, - WLAN_EID_QOS_CAPA, - WLAN_EID_RRM_ENABLED_CAPABILITIES, - WLAN_EID_MOBILITY_DOMAIN, - WLAN_EID_SUPPORTED_REGULATORY_CLASSES, - }; - noffset = ieee80211_ie_split(wk->ie, wk->ie_len, - before_ht, ARRAY_SIZE(before_ht), - offset); - pos = skb_put(skb, noffset - offset); - memcpy(pos, wk->ie + offset, noffset - offset); - offset = noffset; - } - - if (wk->assoc.use_11n && wk->assoc.wmm_used && - local->hw.queues >= 4) - ieee80211_add_ht_ie(skb, wk->assoc.ht_information_ie, - sband, wk->chan, wk->assoc.smps); - - /* if present, add any custom non-vendor IEs that go after HT */ - if (wk->ie_len && wk->ie) { - noffset = ieee80211_ie_split_vendor(wk->ie, wk->ie_len, - offset); - pos = skb_put(skb, noffset - offset); - memcpy(pos, wk->ie + offset, noffset - offset); - offset = noffset; - } - - if (wk->assoc.wmm_used && local->hw.queues >= 4) { - if (wk->assoc.uapsd_used) { - qos_info = local->uapsd_queues; - qos_info |= (local->uapsd_max_sp_len << - IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT); - } else { - qos_info = 0; - } - - pos = skb_put(skb, 9); - *pos++ = WLAN_EID_VENDOR_SPECIFIC; - *pos++ = 7; /* len */ - *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ - *pos++ = 0x50; - *pos++ = 0xf2; - *pos++ = 2; /* WME */ - *pos++ = 0; /* WME info */ - *pos++ = 1; /* WME ver */ - *pos++ = qos_info; - } - - /* add any remaining custom (i.e. vendor specific here) IEs */ - if (wk->ie_len && wk->ie) { - noffset = wk->ie_len; - pos = skb_put(skb, noffset - offset); - memcpy(pos, wk->ie + offset, noffset - offset); - } - - IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; - ieee80211_tx_skb(sdata, skb); -} - -static void ieee80211_remove_auth_bss(struct ieee80211_local *local, - struct ieee80211_work *wk) -{ - struct cfg80211_bss *cbss; - u16 capa_val = WLAN_CAPABILITY_ESS; - - if (wk->probe_auth.privacy) - capa_val |= WLAN_CAPABILITY_PRIVACY; - - cbss = cfg80211_get_bss(local->hw.wiphy, wk->chan, wk->filter_ta, - wk->probe_auth.ssid, wk->probe_auth.ssid_len, - WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, - capa_val); - if (!cbss) - return; - - cfg80211_unlink_bss(local->hw.wiphy, cbss); - cfg80211_put_bss(cbss); -} - -static enum work_action __must_check -ieee80211_direct_probe(struct ieee80211_work *wk) -{ - struct ieee80211_sub_if_data *sdata = wk->sdata; - struct ieee80211_local *local = sdata->local; - - wk->probe_auth.tries++; - if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) { - printk(KERN_DEBUG "%s: direct probe to %pM timed out\n", - sdata->name, wk->filter_ta); - - /* - * Most likely AP is not in the range so remove the - * bss struct for that AP. - */ - ieee80211_remove_auth_bss(local, wk); - - return WORK_ACT_TIMEOUT; - } - - printk(KERN_DEBUG "%s: direct probe to %pM (try %d/%i)\n", - sdata->name, wk->filter_ta, wk->probe_auth.tries, - IEEE80211_AUTH_MAX_TRIES); - - /* - * Direct probe is sent to broadcast address as some APs - * 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->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; - run_again(local, wk->timeout); - - return WORK_ACT_NONE; -} - - -static enum work_action __must_check -ieee80211_authenticate(struct ieee80211_work *wk) -{ - struct ieee80211_sub_if_data *sdata = wk->sdata; - struct ieee80211_local *local = sdata->local; - - wk->probe_auth.tries++; - if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) { - printk(KERN_DEBUG "%s: authentication with %pM" - " timed out\n", sdata->name, wk->filter_ta); - - /* - * Most likely AP is not in the range so remove the - * bss struct for that AP. - */ - ieee80211_remove_auth_bss(local, wk); - - return WORK_ACT_TIMEOUT; - } - - printk(KERN_DEBUG "%s: authenticate with %pM (try %d)\n", - sdata->name, wk->filter_ta, wk->probe_auth.tries); - - ieee80211_send_auth(sdata, 1, wk->probe_auth.algorithm, wk->ie, - wk->ie_len, wk->filter_ta, NULL, 0, 0); - wk->probe_auth.transaction = 2; - - wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; - run_again(local, wk->timeout); - - return WORK_ACT_NONE; -} - -static enum work_action __must_check -ieee80211_associate(struct ieee80211_work *wk) -{ - struct ieee80211_sub_if_data *sdata = wk->sdata; - struct ieee80211_local *local = sdata->local; - - wk->assoc.tries++; - if (wk->assoc.tries > IEEE80211_ASSOC_MAX_TRIES) { - printk(KERN_DEBUG "%s: association with %pM" - " timed out\n", - sdata->name, wk->filter_ta); - - /* - * Most likely AP is not in the range so remove the - * bss struct for that AP. - */ - if (wk->assoc.bss) - cfg80211_unlink_bss(local->hw.wiphy, wk->assoc.bss); - - return WORK_ACT_TIMEOUT; - } - - printk(KERN_DEBUG "%s: associate with %pM (try %d)\n", - sdata->name, wk->filter_ta, wk->assoc.tries); - ieee80211_send_assoc(sdata, wk); - - wk->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT; - run_again(local, wk->timeout); - - return WORK_ACT_NONE; -} - static enum work_action __must_check ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk) { @@ -563,338 +103,6 @@ ieee80211_offchannel_tx(struct ieee80211_work *wk) return WORK_ACT_TIMEOUT; } -static enum work_action __must_check -ieee80211_assoc_beacon_wait(struct ieee80211_work *wk) -{ - if (wk->started) - return WORK_ACT_TIMEOUT; - - /* - * Wait up to one beacon interval ... - * should this be more if we miss one? - */ - printk(KERN_DEBUG "%s: waiting for beacon from %pM\n", - wk->sdata->name, wk->filter_ta); - wk->timeout = TU_TO_EXP_TIME(wk->assoc.bss->beacon_interval); - return WORK_ACT_NONE; -} - -static void ieee80211_auth_challenge(struct ieee80211_work *wk, - struct ieee80211_mgmt *mgmt, - size_t len) -{ - struct ieee80211_sub_if_data *sdata = wk->sdata; - u8 *pos; - struct ieee802_11_elems elems; - - pos = mgmt->u.auth.variable; - ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); - if (!elems.challenge) - return; - ieee80211_send_auth(sdata, 3, wk->probe_auth.algorithm, - elems.challenge - 2, elems.challenge_len + 2, - wk->filter_ta, wk->probe_auth.key, - wk->probe_auth.key_len, wk->probe_auth.key_idx); - wk->probe_auth.transaction = 4; -} - -static enum work_action __must_check -ieee80211_rx_mgmt_auth(struct ieee80211_work *wk, - struct ieee80211_mgmt *mgmt, size_t len) -{ - u16 auth_alg, auth_transaction, status_code; - - if (wk->type != IEEE80211_WORK_AUTH) - return WORK_ACT_MISMATCH; - - if (len < 24 + 6) - return WORK_ACT_NONE; - - auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); - auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); - status_code = le16_to_cpu(mgmt->u.auth.status_code); - - if (auth_alg != wk->probe_auth.algorithm || - auth_transaction != wk->probe_auth.transaction) - return WORK_ACT_NONE; - - if (status_code != WLAN_STATUS_SUCCESS) { - printk(KERN_DEBUG "%s: %pM denied authentication (status %d)\n", - wk->sdata->name, mgmt->sa, status_code); - return WORK_ACT_DONE; - } - - switch (wk->probe_auth.algorithm) { - case WLAN_AUTH_OPEN: - case WLAN_AUTH_LEAP: - case WLAN_AUTH_FT: - break; - case WLAN_AUTH_SHARED_KEY: - if (wk->probe_auth.transaction != 4) { - ieee80211_auth_challenge(wk, mgmt, len); - /* need another frame */ - return WORK_ACT_NONE; - } - break; - default: - WARN_ON(1); - return WORK_ACT_NONE; - } - - printk(KERN_DEBUG "%s: authenticated\n", wk->sdata->name); - return WORK_ACT_DONE; -} - -static enum work_action __must_check -ieee80211_rx_mgmt_assoc_resp(struct ieee80211_work *wk, - struct ieee80211_mgmt *mgmt, size_t len, - bool reassoc) -{ - struct ieee80211_sub_if_data *sdata = wk->sdata; - struct ieee80211_local *local = sdata->local; - u16 capab_info, status_code, aid; - struct ieee802_11_elems elems; - u8 *pos; - - if (wk->type != IEEE80211_WORK_ASSOC) - return WORK_ACT_MISMATCH; - - /* - * AssocResp and ReassocResp have identical structure, so process both - * of them in this function. - */ - - if (len < 24 + 6) - return WORK_ACT_NONE; - - capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); - status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); - aid = le16_to_cpu(mgmt->u.assoc_resp.aid); - - printk(KERN_DEBUG "%s: RX %sssocResp from %pM (capab=0x%x " - "status=%d aid=%d)\n", - sdata->name, reassoc ? "Rea" : "A", mgmt->sa, - capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14)))); - - pos = mgmt->u.assoc_resp.variable; - ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); - - if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && - elems.timeout_int && elems.timeout_int_len == 5 && - elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) { - u32 tu, ms; - tu = get_unaligned_le32(elems.timeout_int + 1); - ms = tu * 1024 / 1000; - printk(KERN_DEBUG "%s: %pM rejected association temporarily; " - "comeback duration %u TU (%u ms)\n", - sdata->name, mgmt->sa, tu, ms); - wk->timeout = jiffies + msecs_to_jiffies(ms); - if (ms > IEEE80211_ASSOC_TIMEOUT) - run_again(local, wk->timeout); - return WORK_ACT_NONE; - } - - if (status_code != WLAN_STATUS_SUCCESS) - printk(KERN_DEBUG "%s: %pM denied association (code=%d)\n", - sdata->name, mgmt->sa, status_code); - else - printk(KERN_DEBUG "%s: associated\n", sdata->name); - - return WORK_ACT_DONE; -} - -static enum work_action __must_check -ieee80211_rx_mgmt_probe_resp(struct ieee80211_work *wk, - struct ieee80211_mgmt *mgmt, size_t len, - struct ieee80211_rx_status *rx_status) -{ - struct ieee80211_sub_if_data *sdata = wk->sdata; - struct ieee80211_local *local = sdata->local; - size_t baselen; - - ASSERT_WORK_MTX(local); - - if (wk->type != IEEE80211_WORK_DIRECT_PROBE) - return WORK_ACT_MISMATCH; - - if (len < 24 + 12) - return WORK_ACT_NONE; - - baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt; - if (baselen > len) - return WORK_ACT_NONE; - - printk(KERN_DEBUG "%s: direct probe responded\n", sdata->name); - return WORK_ACT_DONE; -} - -static enum work_action __must_check -ieee80211_rx_mgmt_beacon(struct ieee80211_work *wk, - struct ieee80211_mgmt *mgmt, size_t len) -{ - struct ieee80211_sub_if_data *sdata = wk->sdata; - struct ieee80211_local *local = sdata->local; - - ASSERT_WORK_MTX(local); - - if (wk->type != IEEE80211_WORK_ASSOC_BEACON_WAIT) - return WORK_ACT_MISMATCH; - - if (len < 24 + 12) - return WORK_ACT_NONE; - - printk(KERN_DEBUG "%s: beacon received\n", sdata->name); - return WORK_ACT_DONE; -} - -static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local, - struct sk_buff *skb) -{ - struct ieee80211_rx_status *rx_status; - struct ieee80211_mgmt *mgmt; - struct ieee80211_work *wk; - enum work_action rma = WORK_ACT_NONE; - u16 fc; - - rx_status = (struct ieee80211_rx_status *) skb->cb; - mgmt = (struct ieee80211_mgmt *) skb->data; - fc = le16_to_cpu(mgmt->frame_control); - - mutex_lock(&local->mtx); - - list_for_each_entry(wk, &local->work_list, list) { - const u8 *bssid = NULL; - - switch (wk->type) { - case IEEE80211_WORK_DIRECT_PROBE: - case IEEE80211_WORK_AUTH: - case IEEE80211_WORK_ASSOC: - case IEEE80211_WORK_ASSOC_BEACON_WAIT: - bssid = wk->filter_ta; - break; - default: - continue; - } - - /* - * Before queuing, we already verified mgmt->sa, - * so this is needed just for matching. - */ - if (compare_ether_addr(bssid, mgmt->bssid)) - continue; - - switch (fc & IEEE80211_FCTL_STYPE) { - case IEEE80211_STYPE_BEACON: - rma = ieee80211_rx_mgmt_beacon(wk, mgmt, skb->len); - break; - case IEEE80211_STYPE_PROBE_RESP: - rma = ieee80211_rx_mgmt_probe_resp(wk, mgmt, skb->len, - rx_status); - break; - case IEEE80211_STYPE_AUTH: - rma = ieee80211_rx_mgmt_auth(wk, mgmt, skb->len); - break; - case IEEE80211_STYPE_ASSOC_RESP: - rma = ieee80211_rx_mgmt_assoc_resp(wk, mgmt, - skb->len, false); - break; - case IEEE80211_STYPE_REASSOC_RESP: - rma = ieee80211_rx_mgmt_assoc_resp(wk, mgmt, - skb->len, true); - break; - default: - WARN_ON(1); - rma = WORK_ACT_NONE; - } - - /* - * We've either received an unexpected frame, or we have - * multiple work items and need to match the frame to the - * right one. - */ - if (rma == WORK_ACT_MISMATCH) - continue; - - /* - * We've processed this frame for that work, so it can't - * belong to another work struct. - * NB: this is also required for correctness for 'rma'! - */ - break; - } - - switch (rma) { - case WORK_ACT_MISMATCH: - /* ignore this unmatched frame */ - break; - case WORK_ACT_NONE: - break; - case WORK_ACT_DONE: - list_del_rcu(&wk->list); - break; - default: - WARN(1, "unexpected: %d", rma); - } - - mutex_unlock(&local->mtx); - - if (rma != WORK_ACT_DONE) - goto out; - - switch (wk->done(wk, skb)) { - case WORK_DONE_DESTROY: - free_work(wk); - break; - case WORK_DONE_REQUEUE: - synchronize_rcu(); - wk->started = false; /* restart */ - mutex_lock(&local->mtx); - list_add_tail(&wk->list, &local->work_list); - mutex_unlock(&local->mtx); - } - - out: - kfree_skb(skb); -} - -static bool ieee80211_work_ct_coexists(enum nl80211_channel_type wk_ct, - enum nl80211_channel_type oper_ct) -{ - switch (wk_ct) { - case NL80211_CHAN_NO_HT: - return true; - case NL80211_CHAN_HT20: - if (oper_ct != NL80211_CHAN_NO_HT) - return true; - return false; - case NL80211_CHAN_HT40MINUS: - case NL80211_CHAN_HT40PLUS: - return (wk_ct == oper_ct); - } - WARN_ON(1); /* shouldn't get here */ - 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; @@ -909,7 +117,6 @@ static void ieee80211_work_work(struct work_struct *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, work_work); - struct sk_buff *skb; struct ieee80211_work *wk, *tmp; LIST_HEAD(free_work); enum work_action rma; @@ -925,10 +132,6 @@ static void ieee80211_work_work(struct work_struct *work) if (WARN(local->suspended, "work scheduled while going to suspend\n")) return; - /* first process frames to avoid timing out while a frame is pending */ - while ((skb = skb_dequeue(&local->work_skb_queue))) - ieee80211_work_rx_queued_mgmt(local, skb); - mutex_lock(&local->mtx); ieee80211_recalc_idle(local); @@ -945,49 +148,12 @@ 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; + ieee80211_offchannel_stop_vifs(local); 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. - */ - 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); + local->tmp_channel_type = wk->chan_type; + + ieee80211_hw_config(local, 0); started = true; wk->timeout = jiffies; @@ -1016,24 +182,12 @@ static void ieee80211_work_work(struct work_struct *work) case IEEE80211_WORK_ABORT: rma = WORK_ACT_TIMEOUT; break; - case IEEE80211_WORK_DIRECT_PROBE: - rma = ieee80211_direct_probe(wk); - break; - case IEEE80211_WORK_AUTH: - rma = ieee80211_authenticate(wk); - break; - case IEEE80211_WORK_ASSOC: - rma = ieee80211_associate(wk); - break; case IEEE80211_WORK_REMAIN_ON_CHANNEL: rma = ieee80211_remain_on_channel_timeout(wk); break; case IEEE80211_WORK_OFFCHANNEL_TX: rma = ieee80211_offchannel_tx(wk); break; - case IEEE80211_WORK_ASSOC_BEACON_WAIT: - rma = ieee80211_assoc_beacon_wait(wk); - break; } wk->started = started; @@ -1056,34 +210,17 @@ static void ieee80211_work_work(struct work_struct *work) list_for_each_entry(wk, &local->work_list, list) { if (!wk->started) continue; - if (wk->chan != local->tmp_channel) - continue; - if (!ieee80211_work_ct_coexists(wk->chan_type, - local->tmp_channel_type)) + if (wk->chan != local->tmp_channel || + wk->chan_type != local->tmp_channel_type) continue; remain_off_channel = true; } if (!remain_off_channel && local->tmp_channel) { local->tmp_channel = NULL; - /* If tmp_channel wasn't operating channel, then - * we need to go back on-channel. - * NOTE: If we can ever be here while scannning, - * or if the hw_config() channel config logic changes, - * then we may need to do a more thorough check to see if - * 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 - * return logic here. We *could* skip enabling - * beaconing if we were already on-oper-channel - * as a future optimization. - */ - ieee80211_offchannel_return(local, true); + ieee80211_offchannel_return(local); /* give connection some time to breathe */ run_again(local, jiffies + HZ/2); @@ -1138,7 +275,6 @@ void ieee80211_work_init(struct ieee80211_local *local) setup_timer(&local->work_timer, ieee80211_work_timer, (unsigned long)local); INIT_WORK(&local->work_work, ieee80211_work_work); - skb_queue_head_init(&local->work_skb_queue); } void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata) @@ -1172,43 +308,6 @@ void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata) mutex_unlock(&local->mtx); } -ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb) -{ - struct ieee80211_local *local = sdata->local; - struct ieee80211_mgmt *mgmt; - struct ieee80211_work *wk; - u16 fc; - - if (skb->len < 24) - return RX_DROP_MONITOR; - - mgmt = (struct ieee80211_mgmt *) skb->data; - fc = le16_to_cpu(mgmt->frame_control); - - list_for_each_entry_rcu(wk, &local->work_list, list) { - if (sdata != wk->sdata) - continue; - if (compare_ether_addr(wk->filter_ta, mgmt->sa)) - continue; - if (compare_ether_addr(wk->filter_ta, mgmt->bssid)) - continue; - - switch (fc & IEEE80211_FCTL_STYPE) { - case IEEE80211_STYPE_AUTH: - case IEEE80211_STYPE_PROBE_RESP: - case IEEE80211_STYPE_ASSOC_RESP: - case IEEE80211_STYPE_REASSOC_RESP: - case IEEE80211_STYPE_BEACON: - skb_queue_tail(&local->work_skb_queue, skb); - ieee80211_queue_work(&local->hw, &local->work_work); - return RX_QUEUED; - } - } - - return RX_CONTINUE; -} - static enum work_done_result ieee80211_remain_done(struct ieee80211_work *wk, struct sk_buff *skb) { diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index d9e03cf..ea6d03b 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -15,6 +15,7 @@ #include #include #include +#include #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 @@ -142,6 +139,10 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) if (skb->len < hdrlen + MICHAEL_MIC_LEN) return RX_DROP_UNUSABLE; + if (skb_linearize(rx->skb)) + return RX_DROP_UNUSABLE; + hdr = (void *)skb->data; + data = skb->data + hdrlen; data_len = skb->len - hdrlen - MICHAEL_MIC_LEN; key = &rx->key->conf.key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]; @@ -154,8 +155,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 +178,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 +206,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,23 +220,22 @@ 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); } ieee80211_tx_result ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx) { - struct sk_buff *skb = tx->skb; + struct sk_buff *skb; ieee80211_tx_set_protected(tx); - do { + skb_queue_walk(&tx->skbs, skb) { if (tkip_encrypt_skb(tx, skb) < 0) return TX_DROP; - } while ((skb = skb->next)); + } return TX_CONTINUE; } @@ -247,11 +249,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); @@ -261,6 +258,11 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx) if (!rx->sta || skb->len - hdrlen < 12) return RX_DROP_UNUSABLE; + /* it may be possible to optimize this a bit more */ + if (skb_linearize(rx->skb)) + return RX_DROP_UNUSABLE; + hdr = (void *)skb->data; + /* * Let TKIP code verify IV, but skip decryption. * In the case where hardware checks the IV as well, @@ -272,7 +274,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 +302,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,11 +394,14 @@ 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)) { + !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) && + !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) { /* * hwaccel has no need for preallocated room for CCMP * header or MIC fields @@ -416,17 +423,23 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) pos = skb_push(skb, CCMP_HDR_LEN); memmove(pos, pos + CCMP_HDR_LEN, hdrlen); + + /* the HW only needs room for the IV, but not the actual IV */ + if (info->control.hw_key && + (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) + return 0; + 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 +448,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; @@ -446,14 +459,14 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) ieee80211_tx_result ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx) { - struct sk_buff *skb = tx->skb; + struct sk_buff *skb; ieee80211_tx_set_protected(tx); - do { + skb_queue_walk(&tx->skbs, skb) { if (ccmp_encrypt_skb(tx, skb) < 0) return TX_DROP; - } while ((skb = skb->next)); + } return TX_CONTINUE; } @@ -481,10 +494,17 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) if (!rx->sta || data_len < 0) return RX_DROP_UNUSABLE; + if (status->flag & RX_FLAG_DECRYPTED) { + if (!pskb_may_pull(rx->skb, hdrlen + CCMP_HDR_LEN)) + return RX_DROP_UNUSABLE; + } else { + if (skb_linearize(rx->skb)) + return RX_DROP_UNUSABLE; + } + 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 +512,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)) @@ -506,7 +527,8 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) memcpy(key->u.ccmp.rx_pn[queue], pn, CCMP_PN_LEN); /* Remove CCMP header and MIC */ - skb_trim(skb, skb->len - CCMP_MIC_LEN); + if (pskb_trim(skb, skb->len - CCMP_MIC_LEN)) + return RX_DROP_UNUSABLE; memmove(skb->data + CCMP_HDR_LEN, skb->data, hdrlen); skb_pull(skb, CCMP_HDR_LEN); @@ -527,6 +549,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]; @@ -541,15 +573,22 @@ static inline void bip_ipn_swap(u8 *d, const u8 *s) ieee80211_tx_result ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx) { - struct sk_buff *skb = tx->skb; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct sk_buff *skb; + struct ieee80211_tx_info *info; struct ieee80211_key *key = tx->key; struct ieee80211_mmie *mmie; - u8 *pn, aad[20]; - int i; + u8 aad[20]; + u64 pn64; + + if (WARN_ON(skb_queue_len(&tx->skbs) != 1)) + return TX_DROP; + + skb = skb_peek(&tx->skbs); + + info = IEEE80211_SKB_CB(skb); if (info->control.hw_key) - return 0; + return TX_CONTINUE; if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie))) return TX_DROP; @@ -560,22 +599,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; } @@ -594,6 +628,8 @@ ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx) if (!ieee80211_is_mgmt(hdr->frame_control)) return RX_CONTINUE; + /* management frames are already linear */ + if (skb->len < 24 + sizeof(*mmie)) return RX_DROP_UNUSABLE; @@ -613,8 +649,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++; @@ -629,3 +664,22 @@ ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx) return RX_CONTINUE; } + +ieee80211_tx_result +ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx) +{ + struct sk_buff *skb; + struct ieee80211_tx_info *info = NULL; + + skb_queue_walk(&tx->skbs, skb) { + info = IEEE80211_SKB_CB(skb); + + /* handle hw-only algorithm */ + if (!info->control.hw_key) + return TX_DROP; + } + + ieee80211_tx_set_protected(tx); + + return TX_CONTINUE; +} diff --git a/net/mac80211/wpa.h b/net/mac80211/wpa.h index baba060..07e33f8 100644 --- a/net/mac80211/wpa.h +++ b/net/mac80211/wpa.h @@ -32,5 +32,7 @@ ieee80211_tx_result ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx); ieee80211_rx_result ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx); +ieee80211_tx_result +ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx); #endif /* WPA_H */ -- cgit v1.1