From aff89a9b9084931e51b89d8f3ee3c547bea6c422 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:26:51 +0200 Subject: cfg80211: introduce nl80211 testmode command This introduces a new NL80211_CMD_TESTMODE for testing and calibration use with nl80211. There's no multiplexing like like iwpriv had, and the command is not available by default, it needs to be explicitly enabled in Kconfig and shouldn't be enabled in most kernels. The command requires a wiphy index or interface index to identify the device to operate on, and the new TESTDATA attribute. There also is API for sending replies to the command, and testmode multicast messages (on a testmode multicast group). I've also updated mac80211 to be able to pass through the command to the driver, since it itself doesn't implement the testmode command. Additionally, to give people an idea of how to use the command, I've added a little code to hwsim that makes use of the new command to set the powersave mode, this is currently done via debugfs and should remain there, and the testmode command only serves as an example of how to use this best -- with nested netlink attributes in the TESTDATA attribute. A hwsim testmode tool can be found at http://git.sipsolutions.net/hwsim.git/. This tool is BSD licensed so people can easily use it as a basis for their own internal fabrication and validation tools. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net/wireless/core.h') diff --git a/net/wireless/core.h b/net/wireless/core.h index bfa340c7abb5..bc084b68865c 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -58,6 +58,10 @@ struct cfg80211_registered_device { struct cfg80211_scan_request *scan_req; /* protected by RTNL */ unsigned long suspend_at; +#ifdef CONFIG_NL80211_TESTMODE + struct genl_info *testmode_info; +#endif + #ifdef CONFIG_CFG80211_DEBUGFS /* Debugfs entries */ struct wiphy_debugfsdentries { -- cgit v1.2.3 From b23aa676ab9d54469cda9f7151f51a2851c6f36e Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 1 Jul 2009 21:26:54 +0200 Subject: cfg80211: connect/disconnect API This patch introduces the cfg80211 connect/disconnect API. The goal here is to run the AUTH and ASSOC steps in one call. This is needed for some fullmac cards that run both steps directly from the target, after the host driver sends a connect command. Additionally, all the new crypto parameters for connect() are now also valid for associate() -- although associate requires the IEs to be used, the information can be useful for drivers and should be given. Signed-off-by: Samuel Ortiz Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 80 +++++++++++ include/net/cfg80211.h | 135 +++++++++++++++++- net/mac80211/cfg.c | 2 +- net/wireless/Makefile | 2 +- net/wireless/core.c | 16 ++- net/wireless/core.h | 7 + net/wireless/nl80211.c | 368 +++++++++++++++++++++++++++++++++++++++++++++++- net/wireless/nl80211.h | 13 ++ net/wireless/sme.c | 224 +++++++++++++++++++++++++++++ 9 files changed, 829 insertions(+), 18 deletions(-) create mode 100644 net/wireless/sme.c (limited to 'net/wireless/core.h') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 651b18839088..b34c17f52f3e 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -246,6 +246,22 @@ * to identify the device, and the TESTDATA blob attribute to pass through * to the driver. * + * @NL80211_CMD_CONNECT: connection request and notification; this command + * requests to connect to a specified network but without separating + * auth and assoc steps. For this, you need to specify the SSID in a + * %NL80211_ATTR_SSID attribute, and can optionally specify the association + * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC, + * %NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_CONTROL_PORT. + * It is also sent as an event, with the BSSID and response IEs when the + * connection is established or failed to be established. This can be + * determined by the STATUS_CODE attribute. + * @NL80211_CMD_ROAM: request that the card roam (currently not implemented), + * sent as an event when the card/driver roamed by itself. + * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify + * userspace that a connection was dropped by the AP or due to other + * reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and + * %NL80211_ATTR_REASON_CODE attributes are used. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -316,6 +332,10 @@ enum nl80211_commands { NL80211_CMD_TESTMODE, + NL80211_CMD_CONNECT, + NL80211_CMD_ROAM, + NL80211_CMD_DISCONNECT, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -520,6 +540,30 @@ enum nl80211_commands { * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver. * We recommend using nested, driver-specific attributes within this. * + * @NL80211_ATTR_DISCONNECTED_BY_AP: A flag indicating that the DISCONNECT + * event was due to the AP disconnecting the station, and not due to + * a local disconnect request. + * @NL80211_ATTR_STATUS_CODE: StatusCode for the %NL80211_CMD_CONNECT + * event (u16) + * @NL80211_ATTR_PRIVACY: Flag attribute, used with connect(), indicating + * that protected APs should be used. + * + * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT and ASSOCIATE to + * indicate which unicast key ciphers will be used with the connection + * (an array of u32). + * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT and ASSOCIATE to indicate + * which group key cipher will be used with the connection (a u32). + * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT and ASSOCIATE to indicate + * which WPA version(s) the AP we want to associate with is using + * (a u32 with flags from &enum nl80211_wpa_versions). + * @NL80211_ATTR_AKM_SUITES: Used with CONNECT and ASSOCIATE to indicate + * which key management algorithm(s) to use (an array of u32). + * + * @NL80211_ATTR_REQ_IE: (Re)association request information elements as + * sent out by the card, for ROAM and successful CONNECT events. + * @NL80211_ATTR_RESP_IE: (Re)association response information elements as + * sent by peer, for ROAM and successful CONNECT events. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -630,6 +674,19 @@ enum nl80211_attrs { NL80211_ATTR_TESTDATA, + NL80211_ATTR_PRIVACY, + + NL80211_ATTR_DISCONNECTED_BY_AP, + NL80211_ATTR_STATUS_CODE, + + NL80211_ATTR_CIPHER_SUITES_PAIRWISE, + NL80211_ATTR_CIPHER_SUITE_GROUP, + NL80211_ATTR_WPA_VERSIONS, + NL80211_ATTR_AKM_SUITES, + + NL80211_ATTR_REQ_IE, + NL80211_ATTR_RESP_IE, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -640,6 +697,7 @@ enum nl80211_attrs { * Allow user space programs to use #ifdef on new attributes by defining them * here */ +#define NL80211_CMD_CONNECT NL80211_CMD_CONNECT #define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY #define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES #define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS @@ -653,6 +711,10 @@ enum nl80211_attrs { #define NL80211_ATTR_SSID NL80211_ATTR_SSID #define NL80211_ATTR_AUTH_TYPE NL80211_ATTR_AUTH_TYPE #define NL80211_ATTR_REASON_CODE NL80211_ATTR_REASON_CODE +#define NL80211_ATTR_CIPHER_SUITES_PAIRWISE NL80211_ATTR_CIPHER_SUITES_PAIRWISE +#define NL80211_ATTR_CIPHER_SUITE_GROUP NL80211_ATTR_CIPHER_SUITE_GROUP +#define NL80211_ATTR_WPA_VERSIONS NL80211_ATTR_WPA_VERSIONS +#define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_REG_RULES 32 @@ -661,6 +723,9 @@ enum nl80211_attrs { #define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 #define NL80211_HT_CAPABILITY_LEN 26 +#define NL80211_MAX_NR_CIPHER_SUITES 5 +#define NL80211_MAX_NR_AKM_SUITES 2 + /** * enum nl80211_iftype - (virtual) interface types * @@ -1205,12 +1270,22 @@ enum nl80211_bss { * @NL80211_AUTHTYPE_SHARED_KEY: Shared Key authentication (WEP only) * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r) * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP) + * @__NL80211_AUTHTYPE_NUM: internal + * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm + * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by + * trying multiple times); this is invalid in netlink -- leave out + * the attribute for this on CONNECT commands. */ enum nl80211_auth_type { NL80211_AUTHTYPE_OPEN_SYSTEM, NL80211_AUTHTYPE_SHARED_KEY, NL80211_AUTHTYPE_FT, NL80211_AUTHTYPE_NETWORK_EAP, + + /* keep last */ + __NL80211_AUTHTYPE_NUM, + NL80211_AUTHTYPE_MAX = __NL80211_AUTHTYPE_NUM - 1, + NL80211_AUTHTYPE_AUTOMATIC }; /** @@ -1235,4 +1310,9 @@ enum nl80211_mfp { NL80211_MFP_REQUIRED, }; +enum nl80211_wpa_versions { + NL80211_WPA_VERSION_1 = 1 << 0, + NL80211_WPA_VERSION_2 = 1 << 1, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 885d4e5bc4b5..68e11321ed74 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -604,6 +604,30 @@ struct cfg80211_bss { u8 priv[0] __attribute__((__aligned__(sizeof(void *)))); }; +/** + * struct cfg80211_crypto_settings - Crypto settings + * @wpa_versions: indicates which, if any, WPA versions are enabled + * (from enum nl80211_wpa_versions) + * @cipher_group: group key cipher suite (or 0 if unset) + * @n_ciphers_pairwise: number of AP supported unicast ciphers + * @ciphers_pairwise: unicast key cipher suites + * @n_akm_suites: number of AKM suites + * @akm_suites: AKM suites + * @control_port: Whether user space controls IEEE 802.1X port, i.e., + * sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is + * required to assume that the port is unauthorized until authorized by + * user space. Otherwise, port is marked authorized by default. + */ +struct cfg80211_crypto_settings { + u32 wpa_versions; + u32 cipher_group; + int n_ciphers_pairwise; + u32 ciphers_pairwise[NL80211_MAX_NR_CIPHER_SUITES]; + int n_akm_suites; + u32 akm_suites[NL80211_MAX_NR_AKM_SUITES]; + bool control_port; +}; + /** * struct cfg80211_auth_request - Authentication request data * @@ -658,10 +682,7 @@ struct cfg80211_auth_request { * @ie: Extra IEs to add to (Re)Association Request frame or %NULL * @ie_len: Length of ie buffer in octets * @use_mfp: Use management frame protection (IEEE 802.11w) in this association - * @control_port: Whether user space controls IEEE 802.1X port, i.e., - * sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is - * required to assume that the port is unauthorized until authorized by - * user space. Otherwise, port is marked authorized by default. + * @crypto: crypto settings */ struct cfg80211_assoc_request { struct ieee80211_channel *chan; @@ -671,7 +692,7 @@ struct cfg80211_assoc_request { const u8 *ie; size_t ie_len; bool use_mfp; - bool control_port; + struct cfg80211_crypto_settings crypto; }; /** @@ -737,6 +758,36 @@ struct cfg80211_ibss_params { bool channel_fixed; }; +/** + * struct cfg80211_connect_params - Connection parameters + * + * This structure provides information needed to complete IEEE 802.11 + * authentication and association. + * + * @channel: The channel to use or %NULL if not specified (auto-select based + * on scan results) + * @bssid: The AP BSSID or %NULL if not specified (auto-select based on scan + * results) + * @ssid: SSID + * @ssid_len: Length of ssid in octets + * @auth_type: Authentication type (algorithm) + * @assoc_ie: IEs for association request + * @assoc_ie_len: Length of assoc_ie in octets + * @privacy: indicates whether privacy-enabled APs should be used + * @crypto: crypto settings + */ +struct cfg80211_connect_params { + struct ieee80211_channel *channel; + u8 *bssid; + u8 *ssid; + size_t ssid_len; + enum nl80211_auth_type auth_type; + u8 *ie; + size_t ie_len; + bool privacy; + struct cfg80211_crypto_settings crypto; +}; + /** * enum wiphy_params_flags - set_wiphy_params bitfield values * WIPHY_PARAM_RETRY_SHORT: wiphy->retry_short has changed @@ -841,6 +892,12 @@ enum tx_power_setting { * @deauth: Request to deauthenticate from the specified peer * @disassoc: Request to disassociate from the specified peer * + * @connect: Connect to the ESS with the specified parameters. When connected, + * call cfg80211_connect_result() with status code %WLAN_STATUS_SUCCESS. + * If the connection fails for some reason, call cfg80211_connect_result() + * with the status from the AP. + * @disconnect: Disconnect from the BSS/ESS. + * * @join_ibss: Join the specified IBSS (or create if necessary). Once done, call * cfg80211_ibss_joined(), also call that function when changing BSSID due * to a merge. @@ -946,6 +1003,11 @@ struct cfg80211_ops { int (*disassoc)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_disassoc_request *req); + int (*connect)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme); + int (*disconnect)(struct wiphy *wiphy, struct net_device *dev, + u16 reason_code); + int (*join_ibss)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *params); int (*leave_ibss)(struct wiphy *wiphy, struct net_device *dev); @@ -1174,10 +1236,15 @@ struct wireless_dev { struct list_head list; struct net_device *netdev; - /* currently used for IBSS - might be rearranged in the future */ + /* currently used for IBSS and SME - might be rearranged later */ struct cfg80211_bss *current_bss; u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 ssid_len; + enum { + CFG80211_SME_IDLE, + CFG80211_SME_CONNECTING, /* ->connect called */ + CFG80211_SME_CONNECTED, + } sme_state; #ifdef CONFIG_WIRELESS_EXT /* wext data */ @@ -1788,4 +1855,60 @@ void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp); #define CFG80211_TESTMODE_CMD(cmd) #endif +/** + * cfg80211_connect_result - notify cfg80211 of connection result + * + * @dev: network device + * @bssid: the BSSID of the AP + * @req_ie: association request IEs (maybe be %NULL) + * @req_ie_len: association request IEs length + * @resp_ie: association response IEs (may be %NULL) + * @resp_ie_len: assoc response IEs length + * @status: status code, 0 for successful connection, use + * %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you + * the real status code for failures. + * @gfp: allocation flags + * + * It should be called by the underlying driver whenever connect() has + * succeeded. + */ +void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, gfp_t gfp); + +/** + * cfg80211_roamed - notify cfg80211 of roaming + * + * @dev: network device + * @bssid: the BSSID of the new AP + * @req_ie: association request IEs (maybe be %NULL) + * @req_ie_len: association request IEs length + * @resp_ie: association response IEs (may be %NULL) + * @resp_ie_len: assoc response IEs length + * @gfp: allocation flags + * + * It should be called by the underlying driver whenever it roamed + * from one AP to another while connected. + */ +void cfg80211_roamed(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp); + +/** + * cfg80211_disconnected - notify cfg80211 that connection was dropped + * + * @dev: network device + * @ie: information elements of the deauth/disassoc frame (may be %NULL) + * @ie_len: length of IEs + * @reason: reason code for the disconnection, set it to 0 if unknown + * @gfp: allocation flags + * + * After it calls this function, the driver should enter an idle state + * and not try to connect to any AP any more. + */ +void cfg80211_disconnected(struct net_device *dev, u16 reason, + u8 *ie, size_t ie_len, gfp_t gfp); + + #endif /* __NET_CFG80211_H */ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index c34c1a41019a..03de4024597a 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1262,7 +1262,7 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, sdata->u.mgd.flags &= ~IEEE80211_STA_MFP_ENABLED; } - if (req->control_port) + if (req->crypto.control_port) sdata->u.mgd.flags |= IEEE80211_STA_CONTROL_PORT; else sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT; diff --git a/net/wireless/Makefile b/net/wireless/Makefile index f78c4832a9ca..750c08e31b10 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o -cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o +cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o sme.o cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o diff --git a/net/wireless/core.c b/net/wireless/core.c index d41b7412b212..314e00f70e3b 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -546,6 +546,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, "symlink to netdev!\n"); } wdev->netdev = dev; + wdev->sme_state = CFG80211_SME_IDLE; #ifdef CONFIG_WIRELESS_EXT wdev->wext.default_key = -1; wdev->wext.default_mgmt_key = -1; @@ -553,11 +554,20 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, mutex_unlock(&rdev->devlist_mtx); break; case NETDEV_GOING_DOWN: - if (wdev->iftype != NL80211_IFTYPE_ADHOC) - break; if (!wdev->ssid_len) break; - cfg80211_leave_ibss(rdev, dev, true); + + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + cfg80211_leave_ibss(rdev, dev, true); + break; + case NL80211_IFTYPE_STATION: + cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING); + break; + default: + break; + } break; case NETDEV_UP: #ifdef CONFIG_WIRELESS_EXT diff --git a/net/wireless/core.h b/net/wireless/core.h index bc084b68865c..f93f96f85d2b 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -174,6 +174,13 @@ void cfg80211_clear_ibss(struct net_device *dev, bool nowext); int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext); +/* SME */ +int cfg80211_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *connect); +int cfg80211_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, u16 reason); + /* internal helpers */ int cfg80211_validate_key_settings(struct key_params *params, int key_idx, const u8 *mac_addr); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index bb8de268a6bf..89dd3793e03c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -128,6 +128,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { .len = sizeof(struct nl80211_sta_flag_update), }, [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG }, + [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG }, + [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, + [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, }; /* IE validation */ @@ -347,6 +350,17 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, CMD(join_ibss, JOIN_IBSS); #undef CMD + + if (dev->ops->connect) { + i++; + NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT); + } + + if (dev->ops->disconnect) { + i++; + NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT); + } + nla_nest_end(msg, nl_cmds); return genlmsg_end(msg, hdr); @@ -3001,12 +3015,31 @@ static int nl80211_dump_scan(struct sk_buff *skb, static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) { - return auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM || - auth_type == NL80211_AUTHTYPE_SHARED_KEY || - auth_type == NL80211_AUTHTYPE_FT || - auth_type == NL80211_AUTHTYPE_NETWORK_EAP; + return auth_type <= NL80211_AUTHTYPE_MAX; +} + +static bool nl80211_valid_wpa_versions(u32 wpa_versions) +{ + return !(wpa_versions & ~(NL80211_WPA_VERSION_1 | + NL80211_WPA_VERSION_2)); +} + +static bool nl80211_valid_akm_suite(u32 akm) +{ + return akm == WLAN_AKM_SUITE_8021X || + akm == WLAN_AKM_SUITE_PSK; +} + +static bool nl80211_valid_cipher_suite(u32 cipher) +{ + return cipher == WLAN_CIPHER_SUITE_WEP40 || + cipher == WLAN_CIPHER_SUITE_WEP104 || + cipher == WLAN_CIPHER_SUITE_TKIP || + cipher == WLAN_CIPHER_SUITE_CCMP || + cipher == WLAN_CIPHER_SUITE_AES_CMAC; } + static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *drv; @@ -3086,6 +3119,68 @@ unlock_rtnl: return err; } +static int nl80211_crypto_settings(struct genl_info *info, + struct cfg80211_crypto_settings *settings) +{ + settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT]; + + if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) { + void *data; + int len, i; + + data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); + len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); + settings->n_ciphers_pairwise = len / sizeof(u32); + + if (len % sizeof(u32)) + return -EINVAL; + + if (settings->n_ciphers_pairwise > NL80211_MAX_NR_CIPHER_SUITES) + return -EINVAL; + + memcpy(settings->ciphers_pairwise, data, len); + + for (i = 0; i < settings->n_ciphers_pairwise; i++) + if (!nl80211_valid_cipher_suite( + settings->ciphers_pairwise[i])) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) { + settings->cipher_group = + nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]); + if (!nl80211_valid_cipher_suite(settings->cipher_group)) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) { + settings->wpa_versions = + nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]); + if (!nl80211_valid_wpa_versions(settings->wpa_versions)) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_AKM_SUITES]) { + void *data; + int len, i; + + data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]); + len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]); + settings->n_akm_suites = len / sizeof(u32); + + if (len % sizeof(u32)) + return -EINVAL; + + memcpy(settings->akm_suites, data, len); + + for (i = 0; i < settings->n_ciphers_pairwise; i++) + if (!nl80211_valid_akm_suite(settings->akm_suites[i])) + return -EINVAL; + } + + return 0; +} + static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *drv; @@ -3156,9 +3251,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) } } - req.control_port = info->attrs[NL80211_ATTR_CONTROL_PORT]; - - err = drv->ops->assoc(&drv->wiphy, dev, &req); + err = nl80211_crypto_settings(info, &req.crypto); + if (!err) + err = drv->ops->assoc(&drv->wiphy, dev, &req); out: cfg80211_put_dev(drv); @@ -3538,6 +3633,130 @@ void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) EXPORT_SYMBOL(cfg80211_testmode_event); #endif +static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + struct cfg80211_connect_params connect; + struct wiphy *wiphy; + int err; + + memset(&connect, 0, sizeof(connect)); + + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_SSID] || + !nla_len(info->attrs[NL80211_ATTR_SSID])) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { + connect.auth_type = + nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); + if (!nl80211_valid_auth_type(connect.auth_type)) + return -EINVAL; + } else + connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; + + connect.privacy = info->attrs[NL80211_ATTR_PRIVACY]; + + err = nl80211_crypto_settings(info, &connect.crypto); + if (err) + return err; + rtnl_lock(); + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + goto unlock_rtnl; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { + err = -EOPNOTSUPP; + goto out; + } + + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + + wiphy = &drv->wiphy; + + connect.bssid = NULL; + connect.channel = NULL; + connect.auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; + + if (info->attrs[NL80211_ATTR_MAC]) + connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + + if (info->attrs[NL80211_ATTR_IE]) { + connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { + connect.channel = + ieee80211_get_channel(wiphy, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (!connect.channel || + connect.channel->flags & IEEE80211_CHAN_DISABLED) { + err = -EINVAL; + goto out; + } + } + + err = cfg80211_connect(drv, dev, &connect); + +out: + cfg80211_put_dev(drv); + dev_put(dev); +unlock_rtnl: + rtnl_unlock(); + return err; +} + +static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + int err; + u16 reason; + + if (!info->attrs[NL80211_ATTR_REASON_CODE]) + reason = WLAN_REASON_DEAUTH_LEAVING; + else + reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + + if (reason == 0) + return -EINVAL; + + rtnl_lock(); + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + goto unlock_rtnl; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { + err = -EOPNOTSUPP; + goto out; + } + + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + + err = cfg80211_disconnect(drv, dev, reason); + +out: + cfg80211_put_dev(drv); + dev_put(dev); +unlock_rtnl: + rtnl_unlock(); + return err; +} + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -3759,6 +3978,18 @@ static struct genl_ops nl80211_ops[] = { .flags = GENL_ADMIN_PERM, }, #endif + { + .cmd = NL80211_CMD_CONNECT, + .doit = nl80211_connect, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_DISCONNECT, + .doit = nl80211_disconnect, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { .name = "mlme", @@ -4077,6 +4308,129 @@ void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, addr, gfp); } +void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + if (bssid) + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); + NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status); + if (req_ie) + NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie); + if (resp_ie) + NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); + +} + +void nl80211_send_roamed(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); + if (req_ie) + NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie); + if (resp_ie) + NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); + +} + +void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u16 reason, + u8 *ie, size_t ie_len, bool from_ap, gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + if (from_ap && reason) + NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason); + if (from_ap) + NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP); + if (ie) + NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); + +} + void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, gfp_t gfp) diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 662c216e8d4f..cf3708b48c29 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -31,6 +31,19 @@ void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, gfp_t gfp); +void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, gfp_t gfp); +void nl80211_send_roamed(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp); +void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u16 reason, + u8 *ie, size_t ie_len, bool from_ap, gfp_t gfp); + void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, diff --git a/net/wireless/sme.c b/net/wireless/sme.c new file mode 100644 index 000000000000..fc117031d0bb --- /dev/null +++ b/net/wireless/sme.c @@ -0,0 +1,224 @@ +/* + * SME code for cfg80211's connect emulation. + * + * Copyright 2009 Johannes Berg + * Copyright (C) 2009 Intel Corporation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include "nl80211.h" + + +void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_bss *bss; +#ifdef CONFIG_WIRELESS_EXT + union iwreq_data wrqu; +#endif + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return; + + if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING)) + return; + + if (wdev->current_bss) { + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(wdev->current_bss); + wdev->current_bss = NULL; + } + + if (status == WLAN_STATUS_SUCCESS) { + bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, + wdev->ssid, wdev->ssid_len, + WLAN_CAPABILITY_ESS, + WLAN_CAPABILITY_ESS); + + if (WARN_ON(!bss)) + return; + + cfg80211_hold_bss(bss); + wdev->current_bss = bss; + + wdev->sme_state = CFG80211_SME_CONNECTED; + } else { + wdev->sme_state = CFG80211_SME_IDLE; + } + + nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid, + req_ie, req_ie_len, resp_ie, resp_ie_len, + status, gfp); + +#ifdef CONFIG_WIRELESS_EXT + if (req_ie && status == WLAN_STATUS_SUCCESS) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = req_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); + } + + if (resp_ie && status == WLAN_STATUS_SUCCESS) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = resp_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); + } + + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + if (bssid) + memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); +#endif +} +EXPORT_SYMBOL(cfg80211_connect_result); + +void cfg80211_roamed(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_bss *bss; +#ifdef CONFIG_WIRELESS_EXT + union iwreq_data wrqu; +#endif + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return; + + if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) + return; + + /* internal error -- how did we get to CONNECTED w/o BSS? */ + if (WARN_ON(!wdev->current_bss)) { + return; + } + + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(wdev->current_bss); + wdev->current_bss = NULL; + + bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, + wdev->ssid, wdev->ssid_len, + WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + + if (WARN_ON(!bss)) + return; + + cfg80211_hold_bss(bss); + wdev->current_bss = bss; + + nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, bssid, + req_ie, req_ie_len, resp_ie, resp_ie_len, gfp); + +#ifdef CONFIG_WIRELESS_EXT + if (req_ie) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = req_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); + } + + if (resp_ie) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = resp_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); + } + + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); +#endif +} +EXPORT_SYMBOL(cfg80211_roamed); + +static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, + u8 *ie, size_t ie_len, u16 reason, + bool from_ap) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; +#ifdef CONFIG_WIRELESS_EXT + union iwreq_data wrqu; +#endif + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return; + + if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) + return; + + if (wdev->current_bss) { + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(wdev->current_bss); + } + + wdev->current_bss = NULL; + wdev->sme_state = CFG80211_SME_IDLE; + + nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev, + reason, ie, ie_len, from_ap, gfp); + +#ifdef CONFIG_WIRELESS_EXT + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); +#endif +} + +void cfg80211_disconnected(struct net_device *dev, u16 reason, + u8 *ie, size_t ie_len, gfp_t gfp) +{ + __cfg80211_disconnected(dev, reason, ie, ie_len, true, gfp); +} +EXPORT_SYMBOL(cfg80211_disconnected); + +int cfg80211_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *connect) +{ + int err; + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (wdev->sme_state != CFG80211_SME_IDLE) + return -EALREADY; + + if (!rdev->ops->connect) { + return -EOPNOTSUPP; + } else { + wdev->sme_state = CFG80211_SME_CONNECTING; + err = rdev->ops->connect(&rdev->wiphy, dev, connect); + if (err) { + wdev->sme_state = CFG80211_SME_IDLE; + return err; + } + } + + memcpy(wdev->ssid, connect->ssid, connect->ssid_len); + wdev->ssid_len = connect->ssid_len; + + return 0; +} + +int cfg80211_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, u16 reason) +{ + int err; + + if (!rdev->ops->disconnect) { + return -EOPNOTSUPP; + } else { + err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); + if (err) + return err; + } + + __cfg80211_disconnected(dev, 0, NULL, 0, false, GFP_KERNEL); + + return 0; +} -- cgit v1.2.3 From 6829c878ecd24ff0ae41b4668c7e9d0f11b66942 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 2 Jul 2009 09:13:27 +0200 Subject: cfg80211: emulate connect with auth/assoc This adds code to cfg80211 so that drivers (mac80211 right now) that don't implement connect but rather auth/assoc can still be used with the nl80211 connect command. This will also be necessary for the wext compat code. Signed-off-by: Samuel Ortiz Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 6 +- net/wireless/core.c | 7 + net/wireless/core.h | 8 + net/wireless/mlme.c | 79 ++++++++-- net/wireless/nl80211.c | 4 +- net/wireless/scan.c | 7 + net/wireless/sme.c | 395 +++++++++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 474 insertions(+), 32 deletions(-) (limited to 'net/wireless/core.h') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 68e11321ed74..24fab439d415 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1209,6 +1209,9 @@ extern void wiphy_unregister(struct wiphy *wiphy); */ extern void wiphy_free(struct wiphy *wiphy); +/* internal struct */ +struct cfg80211_conn; + /** * struct wireless_dev - wireless per-netdev state * @@ -1242,9 +1245,10 @@ struct wireless_dev { u8 ssid_len; enum { CFG80211_SME_IDLE, - CFG80211_SME_CONNECTING, /* ->connect called */ + CFG80211_SME_CONNECTING, CFG80211_SME_CONNECTED, } sme_state; + struct cfg80211_conn *conn; #ifdef CONFIG_WIRELESS_EXT /* wext data */ diff --git a/net/wireless/core.c b/net/wireless/core.c index 314e00f70e3b..a0a679704612 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -321,6 +321,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) } INIT_WORK(&drv->rfkill_sync, cfg80211_rfkill_sync_work); + INIT_WORK(&drv->conn_work, cfg80211_conn_work); /* * Initialize wiphy parameters to IEEE 802.11 MIB default values. @@ -481,6 +482,8 @@ void wiphy_unregister(struct wiphy *wiphy) /* unlock again before freeing */ mutex_unlock(&drv->mtx); + cancel_work_sync(&drv->conn_work); + cfg80211_debugfs_drv_del(drv); /* If this device got a regulatory hint tell core its @@ -569,6 +572,10 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, break; } break; + case NETDEV_DOWN: + kfree(wdev->conn); + wdev->conn = NULL; + break; case NETDEV_UP: #ifdef CONFIG_WIRELESS_EXT if (wdev->iftype != NL80211_IFTYPE_ADHOC) diff --git a/net/wireless/core.h b/net/wireless/core.h index f93f96f85d2b..2c0f64252f3d 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -62,6 +62,8 @@ struct cfg80211_registered_device { struct genl_info *testmode_info; #endif + struct work_struct conn_work; + #ifdef CONFIG_CFG80211_DEBUGFS /* Debugfs entries */ struct wiphy_debugfsdentries { @@ -181,8 +183,14 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, int cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason); +void cfg80211_conn_work(struct work_struct *work); + /* internal helpers */ int cfg80211_validate_key_settings(struct key_params *params, int key_idx, const u8 *mac_addr); +void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, + size_t ie_len, u16 reason, bool from_ap); +void cfg80211_sme_scan_done(struct net_device *dev); +void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index c4e6d4b84a46..3427fe73d3c3 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -16,58 +16,105 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len, gf { struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + nl80211_send_rx_auth(rdev, dev, buf, len, gfp); + cfg80211_sme_rx_auth(dev, buf, len); } EXPORT_SYMBOL(cfg80211_send_rx_auth); void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + u16 status_code; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + u8 *ie = mgmt->u.assoc_resp.variable; + int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); + + status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); + nl80211_send_rx_assoc(rdev, dev, buf, len, gfp); + + cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, + status_code, gfp); } EXPORT_SYMBOL(cfg80211_send_rx_assoc); void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + nl80211_send_deauth(rdev, dev, buf, len, gfp); + + if (wdev->sme_state == CFG80211_SME_CONNECTED) { + u16 reason_code; + bool from_ap; + + reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); + + from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; + __cfg80211_disconnected(dev, gfp, NULL, 0, + reason_code, from_ap); + + wdev->sme_state = CFG80211_SME_IDLE; + } else if (wdev->sme_state == CFG80211_SME_CONNECTING) { + cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, gfp); + } } EXPORT_SYMBOL(cfg80211_send_deauth); void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + nl80211_send_disassoc(rdev, dev, buf, len, gfp); -} -EXPORT_SYMBOL(cfg80211_send_disassoc); -static void cfg80211_wext_disconnected(struct net_device *dev) -{ -#ifdef CONFIG_WIRELESS_EXT - union iwreq_data wrqu; - memset(&wrqu, 0, sizeof(wrqu)); - wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); -#endif + if (wdev->sme_state == CFG80211_SME_CONNECTED) { + u16 reason_code; + bool from_ap; + + reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); + + from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; + __cfg80211_disconnected(dev, gfp, NULL, 0, + reason_code, from_ap); + + wdev->sme_state = CFG80211_SME_IDLE; + } } +EXPORT_SYMBOL(cfg80211_send_disassoc); void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr, gfp_t gfp) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); nl80211_send_auth_timeout(rdev, dev, addr, gfp); - cfg80211_wext_disconnected(dev); + if (wdev->sme_state == CFG80211_SME_CONNECTING) + cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, gfp); + wdev->sme_state = CFG80211_SME_IDLE; } EXPORT_SYMBOL(cfg80211_send_auth_timeout); void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr, gfp_t gfp) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); nl80211_send_assoc_timeout(rdev, dev, addr, gfp); - cfg80211_wext_disconnected(dev); + if (wdev->sme_state == CFG80211_SME_CONNECTING) + cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, gfp); + wdev->sme_state = CFG80211_SME_IDLE; } EXPORT_SYMBOL(cfg80211_send_assoc_timeout); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 89dd3793e03c..89aa9e781d10 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -351,12 +351,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, #undef CMD - if (dev->ops->connect) { + if (dev->ops->connect || dev->ops->auth) { i++; NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT); } - if (dev->ops->disconnect) { + if (dev->ops->disconnect || dev->ops->deauth) { i++; NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT); } diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 261a06386822..82b33e708488 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -30,6 +30,13 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); + /* + * This must be before sending the other events! + * Otherwise, wpa_supplicant gets completely confused with + * wext events. + */ + cfg80211_sme_scan_done(dev); + if (aborted) nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev); else diff --git a/net/wireless/sme.c b/net/wireless/sme.c index fc117031d0bb..3abb04729873 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -12,6 +12,266 @@ #include #include "nl80211.h" +struct cfg80211_conn { + struct cfg80211_connect_params params; + /* these are sub-states of the _CONNECTING sme_state */ + enum { + CFG80211_CONN_IDLE, + CFG80211_CONN_SCANNING, + CFG80211_CONN_SCAN_AGAIN, + CFG80211_CONN_AUTHENTICATE_NEXT, + CFG80211_CONN_AUTHENTICATING, + CFG80211_CONN_ASSOCIATE_NEXT, + CFG80211_CONN_ASSOCIATING, + } state; + u8 bssid[ETH_ALEN]; + u8 *ie; + size_t ie_len; + bool auto_auth; +}; + + +static int cfg80211_conn_scan(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); + struct cfg80211_scan_request *request; + int n_channels, err; + + ASSERT_RTNL(); + + if (drv->scan_req) + return -EBUSY; + + if (wdev->conn->params.channel) { + n_channels = 1; + } else { + enum ieee80211_band band; + n_channels = 0; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!wdev->wiphy->bands[band]) + continue; + n_channels += wdev->wiphy->bands[band]->n_channels; + } + } + request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) + + sizeof(request->channels[0]) * n_channels, + GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->channels = (void *)((char *)request + sizeof(*request)); + if (wdev->conn->params.channel) + request->channels[0] = wdev->conn->params.channel; + else { + int i = 0, j; + enum ieee80211_band band; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!wdev->wiphy->bands[band]) + continue; + for (j = 0; j < wdev->wiphy->bands[band]->n_channels; + i++, j++) + request->channels[i] = + &wdev->wiphy->bands[band]->channels[j]; + } + } + request->n_channels = n_channels; + request->ssids = (void *)(request->channels + n_channels); + request->n_ssids = 1; + + memcpy(request->ssids[0].ssid, wdev->conn->params.ssid, + wdev->conn->params.ssid_len); + request->ssids[0].ssid_len = wdev->conn->params.ssid_len; + + request->ifidx = wdev->netdev->ifindex; + request->wiphy = &drv->wiphy; + + drv->scan_req = request; + + err = drv->ops->scan(wdev->wiphy, wdev->netdev, request); + if (!err) { + wdev->conn->state = CFG80211_CONN_SCANNING; + nl80211_send_scan_start(drv, wdev->netdev); + } else { + drv->scan_req = NULL; + kfree(request); + } + return err; +} + +static int cfg80211_conn_do_work(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); + union { + struct cfg80211_auth_request auth_req; + struct cfg80211_assoc_request assoc_req; + } u; + + memset(&u, 0, sizeof(u)); + + if (!wdev->conn) + return 0; + + switch (wdev->conn->state) { + case CFG80211_CONN_SCAN_AGAIN: + return cfg80211_conn_scan(wdev); + case CFG80211_CONN_AUTHENTICATE_NEXT: + u.auth_req.chan = wdev->conn->params.channel; + u.auth_req.peer_addr = wdev->conn->params.bssid; + u.auth_req.ssid = wdev->conn->params.ssid; + u.auth_req.ssid_len = wdev->conn->params.ssid_len; + u.auth_req.auth_type = wdev->conn->params.auth_type; + u.auth_req.ie = NULL; + u.auth_req.ie_len = 0; + wdev->conn->state = CFG80211_CONN_AUTHENTICATING; + BUG_ON(!drv->ops->auth); + return drv->ops->auth(wdev->wiphy, wdev->netdev, &u.auth_req); + case CFG80211_CONN_ASSOCIATE_NEXT: + u.assoc_req.chan = wdev->conn->params.channel; + u.assoc_req.peer_addr = wdev->conn->params.bssid; + u.assoc_req.ssid = wdev->conn->params.ssid; + u.assoc_req.ssid_len = wdev->conn->params.ssid_len; + u.assoc_req.ie = wdev->conn->params.ie; + u.assoc_req.ie_len = wdev->conn->params.ie_len; + u.assoc_req.use_mfp = false; + memcpy(&u.assoc_req.crypto, &wdev->conn->params.crypto, + sizeof(u.assoc_req.crypto)); + wdev->conn->state = CFG80211_CONN_ASSOCIATING; + BUG_ON(!drv->ops->assoc); + return drv->ops->assoc(wdev->wiphy, wdev->netdev, + &u.assoc_req); + default: + return 0; + } +} + +void cfg80211_conn_work(struct work_struct *work) +{ + struct cfg80211_registered_device *drv = + container_of(work, struct cfg80211_registered_device, conn_work); + struct wireless_dev *wdev; + + rtnl_lock(); + mutex_lock(&drv->devlist_mtx); + + list_for_each_entry(wdev, &drv->netdev_list, list) { + if (!netif_running(wdev->netdev)) + continue; + if (wdev->sme_state != CFG80211_SME_CONNECTING) + continue; + if (cfg80211_conn_do_work(wdev)) + cfg80211_connect_result(wdev->netdev, + wdev->conn->params.bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_ATOMIC); + } + + mutex_unlock(&drv->devlist_mtx); + rtnl_unlock(); +} + +static bool cfg80211_get_conn_bss(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); + struct cfg80211_bss *bss; + u16 capa = WLAN_CAPABILITY_ESS; + + if (wdev->conn->params.privacy) + capa |= WLAN_CAPABILITY_PRIVACY; + + bss = cfg80211_get_bss(wdev->wiphy, NULL, wdev->conn->params.bssid, + wdev->conn->params.ssid, + wdev->conn->params.ssid_len, + WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, + capa); + + if (!bss) + return false; + + memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN); + wdev->conn->params.bssid = wdev->conn->bssid; + wdev->conn->params.channel = bss->channel; + wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; + schedule_work(&drv->conn_work); + + cfg80211_put_bss(bss); + return true; +} + +void cfg80211_sme_scan_done(struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); + + if (wdev->sme_state != CFG80211_SME_CONNECTING) + return; + + if (WARN_ON(!wdev->conn)) + return; + + if (wdev->conn->state != CFG80211_CONN_SCANNING && + wdev->conn->state != CFG80211_CONN_SCAN_AGAIN) + return; + + if (!cfg80211_get_conn_bss(wdev)) { + /* not found */ + if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) + schedule_work(&drv->conn_work); + else + cfg80211_connect_result(dev, wdev->conn->params.bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_ATOMIC); + return; + } +} + +void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); + + /* should only RX auth frames when connecting */ + if (wdev->sme_state != CFG80211_SME_CONNECTING) + return; + + if (WARN_ON(!wdev->conn)) + return; + + if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && + wdev->conn->auto_auth && + wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) { + /* select automatically between only open, shared, leap */ + switch (wdev->conn->params.auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_SHARED_KEY; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_NETWORK_EAP; + break; + default: + /* huh? */ + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_OPEN_SYSTEM; + break; + } + wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; + schedule_work(&rdev->conn_work); + } else if (status_code != WLAN_STATUS_SUCCESS) + wdev->sme_state = CFG80211_SME_IDLE; + else if (wdev->sme_state == CFG80211_SME_CONNECTING && + wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { + wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; + schedule_work(&rdev->conn_work); + } +} void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, @@ -27,7 +287,7 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; - if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING)) + if (wdev->sme_state != CFG80211_SME_CONNECTING) return; if (wdev->current_bss) { @@ -53,6 +313,9 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, wdev->sme_state = CFG80211_SME_IDLE; } + if (wdev->conn) + wdev->conn->state = CFG80211_CONN_IDLE; + nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, status, gfp); @@ -72,7 +335,7 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; - if (bssid) + if (bssid && status == WLAN_STATUS_SUCCESS) memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); #endif @@ -138,9 +401,8 @@ void cfg80211_roamed(struct net_device *dev, const u8 *bssid, } EXPORT_SYMBOL(cfg80211_roamed); -static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, - u8 *ie, size_t ie_len, u16 reason, - bool from_ap) +void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, + size_t ie_len, u16 reason, bool from_ap) { struct wireless_dev *wdev = dev->ieee80211_ptr; #ifdef CONFIG_WIRELESS_EXT @@ -161,6 +423,11 @@ static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, wdev->current_bss = NULL; wdev->sme_state = CFG80211_SME_IDLE; + if (wdev->conn) { + kfree(wdev->conn->ie); + wdev->conn->ie = NULL; + } + nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev, reason, ie, ie_len, from_ap, gfp); @@ -174,7 +441,7 @@ static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, void cfg80211_disconnected(struct net_device *dev, u16 reason, u8 *ie, size_t ie_len, gfp_t gfp) { - __cfg80211_disconnected(dev, reason, ie, ie_len, true, gfp); + __cfg80211_disconnected(dev, gfp, ie, ie_len, reason, true); } EXPORT_SYMBOL(cfg80211_disconnected); @@ -189,7 +456,74 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, return -EALREADY; if (!rdev->ops->connect) { - return -EOPNOTSUPP; + if (!rdev->ops->auth || !rdev->ops->assoc) + return -EOPNOTSUPP; + + if (!wdev->conn) { + wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); + if (!wdev->conn) + return -ENOMEM; + } else + memset(wdev->conn, 0, sizeof(*wdev->conn)); + + /* + * Copy all parameters, and treat explicitly IEs, BSSID, SSID. + */ + memcpy(&wdev->conn->params, connect, sizeof(*connect)); + if (connect->bssid) { + wdev->conn->params.bssid = wdev->conn->bssid; + memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); + } + + if (connect->ie) { + wdev->conn->ie = kmemdup(connect->ie, connect->ie_len, + GFP_KERNEL); + wdev->conn->params.ie = wdev->conn->ie; + if (!wdev->conn->ie) + return -ENOMEM; + } + + if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { + wdev->conn->auto_auth = true; + /* start with open system ... should mostly work */ + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_OPEN_SYSTEM; + } else { + wdev->conn->auto_auth = false; + } + + memcpy(wdev->ssid, connect->ssid, connect->ssid_len); + wdev->ssid_len = connect->ssid_len; + wdev->conn->params.ssid = wdev->ssid; + wdev->conn->params.ssid_len = connect->ssid_len; + + /* don't care about result -- but fill bssid & channel */ + if (!wdev->conn->params.bssid || !wdev->conn->params.channel) + cfg80211_get_conn_bss(wdev); + + wdev->sme_state = CFG80211_SME_CONNECTING; + + /* we're good if we have both BSSID and channel */ + if (wdev->conn->params.bssid && wdev->conn->params.channel) { + wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; + err = cfg80211_conn_do_work(wdev); + } else { + /* otherwise we'll need to scan for the AP first */ + err = cfg80211_conn_scan(wdev); + /* + * If we can't scan right now, then we need to scan again + * after the current scan finished, since the parameters + * changed (unless we find a good AP anyway). + */ + if (err == -EBUSY) { + err = 0; + wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; + } + } + if (err) + wdev->sme_state = CFG80211_SME_IDLE; + + return err; } else { wdev->sme_state = CFG80211_SME_CONNECTING; err = rdev->ops->connect(&rdev->wiphy, dev, connect); @@ -197,28 +531,63 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, wdev->sme_state = CFG80211_SME_IDLE; return err; } - } - memcpy(wdev->ssid, connect->ssid, connect->ssid_len); - wdev->ssid_len = connect->ssid_len; + memcpy(wdev->ssid, connect->ssid, connect->ssid_len); + wdev->ssid_len = connect->ssid_len; - return 0; + return 0; + } } int cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason) { + struct wireless_dev *wdev = dev->ieee80211_ptr; int err; + if (wdev->sme_state == CFG80211_SME_IDLE) + return -EINVAL; + if (!rdev->ops->disconnect) { - return -EOPNOTSUPP; + struct cfg80211_deauth_request deauth; + u8 bssid[ETH_ALEN]; + + /* internal bug. */ + if (WARN_ON(!wdev->conn)) + return -EINVAL; + + if (wdev->sme_state == CFG80211_SME_CONNECTING && + (wdev->conn->state == CFG80211_CONN_SCANNING || + wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) { + wdev->sme_state = CFG80211_SME_IDLE; + return 0; + } + + if (!rdev->ops->deauth) + return -EOPNOTSUPP; + + memset(&deauth, 0, sizeof(deauth)); + + /* wdev->conn->params.bssid must be set if > SCANNING */ + memcpy(bssid, wdev->conn->params.bssid, ETH_ALEN); + deauth.peer_addr = bssid; + deauth.reason_code = reason; + + err = rdev->ops->deauth(&rdev->wiphy, dev, &deauth); + if (err) + return err; } else { err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); if (err) return err; } - __cfg80211_disconnected(dev, 0, NULL, 0, false, GFP_KERNEL); + if (wdev->sme_state == CFG80211_SME_CONNECTED) + __cfg80211_disconnected(dev, GFP_KERNEL, NULL, 0, 0, false); + else if (wdev->sme_state == CFG80211_SME_CONNECTING) + cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); return 0; } -- cgit v1.2.3 From f21293549f60f88c74fcb9944737f11048896dc4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:26:56 +0200 Subject: cfg80211: managed mode wext compatibility This adds code to make it possible to use the cfg80211 connect() API with wireless extensions, and because the previous patch added emulation of that API with auth() and assoc(), by extension also supports wext on that. At the same time, removes code from mac80211 for wext, but doesn't yet clean up mac80211's mlme code more. Signed-off-by: Samuel Ortiz Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 36 ++++- net/mac80211/mlme.c | 69 ---------- net/mac80211/wext.c | 215 +++-------------------------- net/wireless/Makefile | 2 +- net/wireless/core.c | 23 +++- net/wireless/core.h | 3 +- net/wireless/nl80211.c | 2 +- net/wireless/sme.c | 55 +++++--- net/wireless/wext-compat.c | 229 +++++++++++++++++++++++++------ net/wireless/wext-sme.c | 329 +++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 625 insertions(+), 338 deletions(-) create mode 100644 net/wireless/wext-sme.c (limited to 'net/wireless/core.h') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 24fab439d415..07085216532d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1253,8 +1253,14 @@ struct wireless_dev { #ifdef CONFIG_WIRELESS_EXT /* wext data */ struct { - struct cfg80211_ibss_params ibss; + union { + struct cfg80211_ibss_params ibss; + struct cfg80211_connect_params connect; + }; + u8 *ie; + size_t ie_len; u8 bssid[ETH_ALEN]; + u8 ssid[IEEE80211_MAX_SSID_LEN]; s8 default_key, default_mgmt_key; } wext; #endif @@ -1535,6 +1541,34 @@ int cfg80211_ibss_wext_giwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra); +int cfg80211_mgd_wext_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra); +int cfg80211_mgd_wext_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra); +int cfg80211_mgd_wext_siwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid); +int cfg80211_mgd_wext_giwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid); +int cfg80211_mgd_wext_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra); +int cfg80211_mgd_wext_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra); +int cfg80211_wext_siwgenie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra); +int cfg80211_wext_siwauth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *data, char *extra); +int cfg80211_wext_giwauth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *data, char *extra); + struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 58135a5096af..fbb93a70ddc7 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -870,70 +870,6 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, return changed; } -static void ieee80211_sta_send_apinfo(struct ieee80211_sub_if_data *sdata) -{ - union iwreq_data wrqu; - - memset(&wrqu, 0, sizeof(wrqu)); - if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) - memcpy(wrqu.ap_addr.sa_data, sdata->u.mgd.bssid, ETH_ALEN); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - wireless_send_event(sdata->dev, SIOCGIWAP, &wrqu, NULL); -} - -static void ieee80211_sta_send_associnfo(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - char *buf; - size_t len; - int i; - union iwreq_data wrqu; - - if (!ifmgd->assocreq_ies && !ifmgd->assocresp_ies) - return; - - buf = kmalloc(50 + 2 * (ifmgd->assocreq_ies_len + - ifmgd->assocresp_ies_len), GFP_KERNEL); - if (!buf) - return; - - len = sprintf(buf, "ASSOCINFO("); - if (ifmgd->assocreq_ies) { - len += sprintf(buf + len, "ReqIEs="); - for (i = 0; i < ifmgd->assocreq_ies_len; i++) { - len += sprintf(buf + len, "%02x", - ifmgd->assocreq_ies[i]); - } - } - if (ifmgd->assocresp_ies) { - if (ifmgd->assocreq_ies) - len += sprintf(buf + len, " "); - len += sprintf(buf + len, "RespIEs="); - for (i = 0; i < ifmgd->assocresp_ies_len; i++) { - len += sprintf(buf + len, "%02x", - ifmgd->assocresp_ies[i]); - } - } - len += sprintf(buf + len, ")"); - - if (len > IW_CUSTOM_MAX) { - len = sprintf(buf, "ASSOCRESPIE="); - for (i = 0; i < ifmgd->assocresp_ies_len; i++) { - len += sprintf(buf + len, "%02x", - ifmgd->assocresp_ies[i]); - } - } - - if (len <= IW_CUSTOM_MAX) { - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = len; - wireless_send_event(sdata->dev, IWEVCUSTOM, &wrqu, buf); - } - - kfree(buf); -} - - static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, u32 bss_info_changed) { @@ -966,7 +902,6 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, ifmgd->flags |= IEEE80211_STA_PREV_BSSID_SET; memcpy(ifmgd->prev_bssid, sdata->u.mgd.bssid, ETH_ALEN); - ieee80211_sta_send_associnfo(sdata); ifmgd->last_probe = jiffies; ieee80211_led_assoc(local, 1); @@ -993,8 +928,6 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, netif_tx_start_all_queues(sdata->dev); netif_carrier_on(sdata->dev); - - ieee80211_sta_send_apinfo(sdata); } static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata) @@ -1147,8 +1080,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, changed |= BSS_CHANGED_ASSOC; sdata->vif.bss_conf.assoc = false; - ieee80211_sta_send_apinfo(sdata); - if (self_disconnected || reason == WLAN_REASON_DISASSOC_STA_HAS_LEFT) { ifmgd->state = IEEE80211_STA_MLME_DISABLED; ieee80211_rx_bss_remove(sdata, ifmgd->bssid, diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 1da81f456744..d4e61dc903e8 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -27,29 +27,6 @@ #include "aes_ccm.h" -static int ieee80211_ioctl_siwgenie(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct ieee80211_sub_if_data *sdata; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - int ret = ieee80211_sta_set_extra_ie(sdata, extra, data->length); - if (ret && ret != -EALREADY) - return ret; - sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; - sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; - sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT; - if (ret != -EALREADY) - ieee80211_sta_req_auth(sdata); - return 0; - } - - return -EOPNOTSUPP; -} - static int ieee80211_ioctl_siwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra) @@ -61,16 +38,13 @@ static int ieee80211_ioctl_siwfreq(struct net_device *dev, if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra); else if (sdata->vif.type == NL80211_IFTYPE_STATION) - sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_CHANNEL_SEL; + return cfg80211_mgd_wext_siwfreq(dev, info, freq, extra); /* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */ if (freq->e == 0) { - if (freq->m < 0) { - if (sdata->vif.type == NL80211_IFTYPE_STATION) - sdata->u.mgd.flags |= - IEEE80211_STA_AUTO_CHANNEL_SEL; - return 0; - } else + if (freq->m < 0) + return -EINVAL; + else chan = ieee80211_get_channel(local->hw.wiphy, ieee80211_channel_to_frequency(freq->m)); } else { @@ -95,9 +69,6 @@ static int ieee80211_ioctl_siwfreq(struct net_device *dev, if (local->oper_channel == chan) return 0; - if (sdata->vif.type == NL80211_IFTYPE_STATION) - ieee80211_sta_req_auth(sdata); - local->oper_channel = chan; local->oper_channel_type = NL80211_CHAN_NO_HT; ieee80211_hw_config(local, 0); @@ -115,6 +86,8 @@ static int ieee80211_ioctl_giwfreq(struct net_device *dev, if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); + else if (sdata->vif.type == NL80211_IFTYPE_STATION) + return cfg80211_mgd_wext_giwfreq(dev, info, freq, extra); freq->m = local->oper_channel->center_freq; freq->e = 6; @@ -128,31 +101,11 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev, struct iw_point *data, char *ssid) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - size_t len = data->length; - int ret; if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return cfg80211_ibss_wext_siwessid(dev, info, data, ssid); - - /* iwconfig uses nul termination in SSID.. */ - if (len > 0 && ssid[len - 1] == '\0') - len--; - - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - if (data->flags) - sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL; - else - sdata->u.mgd.flags |= IEEE80211_STA_AUTO_SSID_SEL; - - ret = ieee80211_sta_set_ssid(sdata, ssid, len); - if (ret) - return ret; - - sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; - sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT; - ieee80211_sta_req_auth(sdata); - return 0; - } + else if (sdata->vif.type == NL80211_IFTYPE_STATION) + return cfg80211_mgd_wext_siwessid(dev, info, data, ssid); return -EOPNOTSUPP; } @@ -162,23 +115,14 @@ static int ieee80211_ioctl_giwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid) { - size_t len; struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return cfg80211_ibss_wext_giwessid(dev, info, data, ssid); - - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - int res = ieee80211_sta_get_ssid(sdata, ssid, &len); - if (res == 0) { - data->length = len; - data->flags = 1; - } else - data->flags = 0; - return res; - } + else if (sdata->vif.type == NL80211_IFTYPE_STATION) + return cfg80211_mgd_wext_giwessid(dev, info, data, ssid); return -EOPNOTSUPP; } @@ -193,24 +137,10 @@ static int ieee80211_ioctl_siwap(struct net_device *dev, if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra); - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - int ret; + if (sdata->vif.type == NL80211_IFTYPE_STATION) + return cfg80211_mgd_wext_siwap(dev, info, ap_addr, extra); - if (is_zero_ether_addr((u8 *) &ap_addr->sa_data)) - sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL | - IEEE80211_STA_AUTO_CHANNEL_SEL; - else if (is_broadcast_ether_addr((u8 *) &ap_addr->sa_data)) - sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL; - else - sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; - ret = ieee80211_sta_set_bssid(sdata, (u8 *) &ap_addr->sa_data); - if (ret) - return ret; - sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; - sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT; - ieee80211_sta_req_auth(sdata); - return 0; - } else if (sdata->vif.type == NL80211_IFTYPE_WDS) { + if (sdata->vif.type == NL80211_IFTYPE_WDS) { /* * If it is necessary to update the WDS peer address * while the interface is running, then we need to do @@ -240,14 +170,10 @@ static int ieee80211_ioctl_giwap(struct net_device *dev, if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra); - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - if (sdata->u.mgd.state == IEEE80211_STA_MLME_ASSOCIATED) { - ap_addr->sa_family = ARPHRD_ETHER; - memcpy(&ap_addr->sa_data, sdata->u.mgd.bssid, ETH_ALEN); - } else - memset(&ap_addr->sa_data, 0, ETH_ALEN); - return 0; - } else if (sdata->vif.type == NL80211_IFTYPE_WDS) { + if (sdata->vif.type == NL80211_IFTYPE_STATION) + return cfg80211_mgd_wext_giwap(dev, info, ap_addr, extra); + + if (sdata->vif.type == NL80211_IFTYPE_WDS) { ap_addr->sa_family = ARPHRD_ETHER; memcpy(&ap_addr->sa_data, sdata->u.wds.remote_addr, ETH_ALEN); return 0; @@ -395,85 +321,6 @@ static int ieee80211_ioctl_giwpower(struct net_device *dev, return 0; } -static int ieee80211_ioctl_siwauth(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *data, char *extra) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - int ret = 0; - - switch (data->flags & IW_AUTH_INDEX) { - case IW_AUTH_WPA_VERSION: - case IW_AUTH_CIPHER_GROUP: - case IW_AUTH_WPA_ENABLED: - case IW_AUTH_RX_UNENCRYPTED_EAPOL: - case IW_AUTH_KEY_MGMT: - case IW_AUTH_CIPHER_GROUP_MGMT: - break; - case IW_AUTH_CIPHER_PAIRWISE: - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - if (data->value & (IW_AUTH_CIPHER_WEP40 | - IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_TKIP)) - sdata->u.mgd.flags |= - IEEE80211_STA_TKIP_WEP_USED; - else - sdata->u.mgd.flags &= - ~IEEE80211_STA_TKIP_WEP_USED; - } - break; - case IW_AUTH_DROP_UNENCRYPTED: - sdata->drop_unencrypted = !!data->value; - break; - case IW_AUTH_PRIVACY_INVOKED: - if (sdata->vif.type != NL80211_IFTYPE_STATION) - ret = -EINVAL; - else { - sdata->u.mgd.flags &= ~IEEE80211_STA_PRIVACY_INVOKED; - /* - * Privacy invoked by wpa_supplicant, store the - * value and allow associating to a protected - * network without having a key up front. - */ - if (data->value) - sdata->u.mgd.flags |= - IEEE80211_STA_PRIVACY_INVOKED; - } - break; - case IW_AUTH_80211_AUTH_ALG: - if (sdata->vif.type == NL80211_IFTYPE_STATION) - sdata->u.mgd.auth_algs = data->value; - else - ret = -EOPNOTSUPP; - break; - case IW_AUTH_MFP: - if (!(sdata->local->hw.flags & IEEE80211_HW_MFP_CAPABLE)) { - ret = -EOPNOTSUPP; - break; - } - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - switch (data->value) { - case IW_AUTH_MFP_DISABLED: - sdata->u.mgd.mfp = IEEE80211_MFP_DISABLED; - break; - case IW_AUTH_MFP_OPTIONAL: - sdata->u.mgd.mfp = IEEE80211_MFP_OPTIONAL; - break; - case IW_AUTH_MFP_REQUIRED: - sdata->u.mgd.mfp = IEEE80211_MFP_REQUIRED; - break; - default: - ret = -EINVAL; - } - } else - ret = -EOPNOTSUPP; - break; - default: - ret = -EOPNOTSUPP; - break; - } - return ret; -} - /* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev) { @@ -541,28 +388,6 @@ static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev return wstats; } -static int ieee80211_ioctl_giwauth(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *data, char *extra) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - int ret = 0; - - switch (data->flags & IW_AUTH_INDEX) { - case IW_AUTH_80211_AUTH_ALG: - if (sdata->vif.type == NL80211_IFTYPE_STATION) - data->value = sdata->u.mgd.auth_algs; - else - ret = -EOPNOTSUPP; - break; - default: - ret = -EOPNOTSUPP; - break; - } - return ret; -} - - /* Structures to export the Wireless Handlers */ static const iw_handler ieee80211_handler[] = @@ -615,10 +440,10 @@ static const iw_handler ieee80211_handler[] = (iw_handler) ieee80211_ioctl_giwpower, /* SIOCGIWPOWER */ (iw_handler) NULL, /* -- hole -- */ (iw_handler) NULL, /* -- hole -- */ - (iw_handler) ieee80211_ioctl_siwgenie, /* SIOCSIWGENIE */ + (iw_handler) cfg80211_wext_siwgenie, /* SIOCSIWGENIE */ (iw_handler) NULL, /* SIOCGIWGENIE */ - (iw_handler) ieee80211_ioctl_siwauth, /* SIOCSIWAUTH */ - (iw_handler) ieee80211_ioctl_giwauth, /* SIOCGIWAUTH */ + (iw_handler) cfg80211_wext_siwauth, /* SIOCSIWAUTH */ + (iw_handler) cfg80211_wext_giwauth, /* SIOCGIWAUTH */ (iw_handler) cfg80211_wext_siwencodeext, /* SIOCSIWENCODEEXT */ (iw_handler) NULL, /* SIOCGIWENCODEEXT */ (iw_handler) NULL, /* SIOCSIWPMKSA */ diff --git a/net/wireless/Makefile b/net/wireless/Makefile index 750c08e31b10..d74cc77fa57a 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -7,6 +7,6 @@ obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o sme.o cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o -cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o +cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o wext-sme.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/wireless/core.c b/net/wireless/core.c index a0a679704612..e2f80dd0e4a6 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -553,6 +553,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, #ifdef CONFIG_WIRELESS_EXT wdev->wext.default_key = -1; wdev->wext.default_mgmt_key = -1; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; #endif mutex_unlock(&rdev->devlist_mtx); break; @@ -565,8 +566,13 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, cfg80211_leave_ibss(rdev, dev, true); break; case NL80211_IFTYPE_STATION: +#ifdef CONFIG_WIRELESS_EXT + kfree(wdev->wext.ie); + wdev->wext.ie = NULL; + wdev->wext.ie_len = 0; +#endif cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING); + WLAN_REASON_DEAUTH_LEAVING, true); break; default: break; @@ -578,11 +584,20 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, break; case NETDEV_UP: #ifdef CONFIG_WIRELESS_EXT - if (wdev->iftype != NL80211_IFTYPE_ADHOC) + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + if (wdev->wext.ibss.ssid_len) + cfg80211_join_ibss(rdev, dev, + &wdev->wext.ibss); break; - if (!wdev->wext.ibss.ssid_len) + case NL80211_IFTYPE_STATION: + if (wdev->wext.connect.ssid_len) + cfg80211_connect(rdev, dev, + &wdev->wext.connect); + break; + default: break; - cfg80211_join_ibss(rdev, dev, &wdev->wext.ibss); + } #endif break; case NETDEV_UNREGISTER: diff --git a/net/wireless/core.h b/net/wireless/core.h index 2c0f64252f3d..5209acb0ff7e 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -181,7 +181,8 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect); int cfg80211_disconnect(struct cfg80211_registered_device *rdev, - struct net_device *dev, u16 reason); + struct net_device *dev, u16 reason, + bool wextev); void cfg80211_conn_work(struct work_struct *work); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 89aa9e781d10..0008144b354b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3747,7 +3747,7 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) goto out; } - err = cfg80211_disconnect(drv, dev, reason); + err = cfg80211_disconnect(drv, dev, reason, true); out: cfg80211_put_dev(drv); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 3abb04729873..f272ebf94303 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -273,10 +273,10 @@ void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len) } } -void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - u16 status, gfp_t gfp) +static void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, bool wextev, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_bss *bss; @@ -321,25 +321,36 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, status, gfp); #ifdef CONFIG_WIRELESS_EXT - if (req_ie && status == WLAN_STATUS_SUCCESS) { - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = req_ie_len; - wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); - } + if (wextev) { + if (req_ie && status == WLAN_STATUS_SUCCESS) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = req_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); + } + + if (resp_ie && status == WLAN_STATUS_SUCCESS) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = resp_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); + } - if (resp_ie && status == WLAN_STATUS_SUCCESS) { memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = resp_ie_len; - wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + if (bssid && status == WLAN_STATUS_SUCCESS) + memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); } - - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - if (bssid && status == WLAN_STATUS_SUCCESS) - memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); - wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); #endif } + +void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, gfp_t gfp) +{ + bool wextev = status == WLAN_STATUS_SUCCESS; + __cfg80211_connect_result(dev, bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, status, wextev, gfp); +} EXPORT_SYMBOL(cfg80211_connect_result); void cfg80211_roamed(struct net_device *dev, const u8 *bssid, @@ -540,7 +551,7 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, } int cfg80211_disconnect(struct cfg80211_registered_device *rdev, - struct net_device *dev, u16 reason) + struct net_device *dev, u16 reason, bool wextev) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; @@ -585,9 +596,9 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, if (wdev->sme_state == CFG80211_SME_CONNECTED) __cfg80211_disconnected(dev, GFP_KERNEL, NULL, 0, 0, false); else if (wdev->sme_state == CFG80211_SME_CONNECTING) - cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_KERNEL); + __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + wextev, GFP_KERNEL); return 0; } diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index cae3b52fba7f..02f052fc1808 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -261,50 +261,6 @@ int cfg80211_wext_giwrange(struct net_device *dev, } EXPORT_SYMBOL_GPL(cfg80211_wext_giwrange); -int cfg80211_wext_siwmlme(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct iw_mlme *mlme = (struct iw_mlme *)extra; - struct cfg80211_registered_device *rdev; - union { - struct cfg80211_disassoc_request disassoc; - struct cfg80211_deauth_request deauth; - } cmd; - - if (!wdev) - return -EOPNOTSUPP; - - rdev = wiphy_to_dev(wdev->wiphy); - - if (wdev->iftype != NL80211_IFTYPE_STATION) - return -EINVAL; - - if (mlme->addr.sa_family != ARPHRD_ETHER) - return -EINVAL; - - memset(&cmd, 0, sizeof(cmd)); - - switch (mlme->cmd) { - case IW_MLME_DEAUTH: - if (!rdev->ops->deauth) - return -EOPNOTSUPP; - cmd.deauth.peer_addr = mlme->addr.sa_data; - cmd.deauth.reason_code = mlme->reason_code; - return rdev->ops->deauth(wdev->wiphy, dev, &cmd.deauth); - case IW_MLME_DISASSOC: - if (!rdev->ops->disassoc) - return -EOPNOTSUPP; - cmd.disassoc.peer_addr = mlme->addr.sa_data; - cmd.disassoc.reason_code = mlme->reason_code; - return rdev->ops->disassoc(wdev->wiphy, dev, &cmd.disassoc); - default: - return -EOPNOTSUPP; - } -} -EXPORT_SYMBOL_GPL(cfg80211_wext_siwmlme); - /** * cfg80211_wext_freq - get wext frequency for non-"auto" @@ -846,3 +802,188 @@ int cfg80211_wext_giwtxpower(struct net_device *dev, return 0; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwtxpower); + +static int cfg80211_set_auth_alg(struct wireless_dev *wdev, + s32 auth_alg) +{ + int nr_alg = 0; + + if (!auth_alg) + return -EINVAL; + + if (auth_alg & ~(IW_AUTH_ALG_OPEN_SYSTEM | + IW_AUTH_ALG_SHARED_KEY | + IW_AUTH_ALG_LEAP)) + return -EINVAL; + + if (auth_alg & IW_AUTH_ALG_OPEN_SYSTEM) { + nr_alg++; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; + } + + if (auth_alg & IW_AUTH_ALG_SHARED_KEY) { + nr_alg++; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_SHARED_KEY; + } + + if (auth_alg & IW_AUTH_ALG_LEAP) { + nr_alg++; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_NETWORK_EAP; + } + + if (nr_alg > 1) + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; + + return 0; +} + +static int cfg80211_set_wpa_version(struct wireless_dev *wdev, u32 wpa_versions) +{ + wdev->wext.connect.crypto.wpa_versions = 0; + + if (wpa_versions & ~(IW_AUTH_WPA_VERSION_WPA | + IW_AUTH_WPA_VERSION_WPA2)) + return -EINVAL; + + if (wpa_versions & IW_AUTH_WPA_VERSION_WPA) + wdev->wext.connect.crypto.wpa_versions |= + NL80211_WPA_VERSION_1; + + if (wpa_versions & IW_AUTH_WPA_VERSION_WPA2) + wdev->wext.connect.crypto.wpa_versions |= + NL80211_WPA_VERSION_2; + + return 0; +} + +int cfg80211_set_cipher_group(struct wireless_dev *wdev, u32 cipher) +{ + wdev->wext.connect.crypto.cipher_group = 0; + + if (cipher & IW_AUTH_CIPHER_WEP40) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_WEP40; + else if (cipher & IW_AUTH_CIPHER_WEP104) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_WEP104; + else if (cipher & IW_AUTH_CIPHER_TKIP) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_TKIP; + else if (cipher & IW_AUTH_CIPHER_CCMP) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_CCMP; + else if (cipher & IW_AUTH_CIPHER_AES_CMAC) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_AES_CMAC; + else + return -EINVAL; + + return 0; +} + +int cfg80211_set_cipher_pairwise(struct wireless_dev *wdev, u32 cipher) +{ + int nr_ciphers = 0; + u32 *ciphers_pairwise = wdev->wext.connect.crypto.ciphers_pairwise; + + if (cipher & IW_AUTH_CIPHER_WEP40) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_WEP40; + nr_ciphers++; + } + + if (cipher & IW_AUTH_CIPHER_WEP104) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_WEP104; + nr_ciphers++; + } + + if (cipher & IW_AUTH_CIPHER_TKIP) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_TKIP; + nr_ciphers++; + } + + if (cipher & IW_AUTH_CIPHER_CCMP) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_CCMP; + nr_ciphers++; + } + + if (cipher & IW_AUTH_CIPHER_AES_CMAC) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_AES_CMAC; + nr_ciphers++; + } + + BUILD_BUG_ON(NL80211_MAX_NR_CIPHER_SUITES < 5); + + wdev->wext.connect.crypto.n_ciphers_pairwise = nr_ciphers; + + return 0; +} + + +int cfg80211_set_key_mgt(struct wireless_dev *wdev, u32 key_mgt) +{ + int nr_akm_suites = 0; + + if (key_mgt & ~(IW_AUTH_KEY_MGMT_802_1X | + IW_AUTH_KEY_MGMT_PSK)) + return -EINVAL; + + if (key_mgt & IW_AUTH_KEY_MGMT_802_1X) { + wdev->wext.connect.crypto.akm_suites[nr_akm_suites] = + WLAN_AKM_SUITE_8021X; + nr_akm_suites++; + } + + if (key_mgt & IW_AUTH_KEY_MGMT_PSK) { + wdev->wext.connect.crypto.akm_suites[nr_akm_suites] = + WLAN_AKM_SUITE_PSK; + nr_akm_suites++; + } + + wdev->wext.connect.crypto.n_akm_suites = nr_akm_suites; + + return 0; +} + +int cfg80211_wext_siwauth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *data, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (wdev->iftype != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + switch (data->flags & IW_AUTH_INDEX) { + case IW_AUTH_PRIVACY_INVOKED: + wdev->wext.connect.privacy = data->value; + return 0; + case IW_AUTH_WPA_VERSION: + return cfg80211_set_wpa_version(wdev, data->value); + case IW_AUTH_CIPHER_GROUP: + return cfg80211_set_cipher_group(wdev, data->value); + case IW_AUTH_KEY_MGMT: + return cfg80211_set_key_mgt(wdev, data->value); + case IW_AUTH_CIPHER_PAIRWISE: + return cfg80211_set_cipher_pairwise(wdev, data->value); + case IW_AUTH_80211_AUTH_ALG: + return cfg80211_set_auth_alg(wdev, data->value); + case IW_AUTH_WPA_ENABLED: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + case IW_AUTH_DROP_UNENCRYPTED: + case IW_AUTH_MFP: + return 0; + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwauth); + +int cfg80211_wext_giwauth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *data, char *extra) +{ + /* XXX: what do we need? */ + + return -EOPNOTSUPP; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_giwauth); diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c new file mode 100644 index 000000000000..3b531d572b69 --- /dev/null +++ b/net/wireless/wext-sme.c @@ -0,0 +1,329 @@ +/* + * cfg80211 wext compat for managed mode. + * + * Copyright 2009 Johannes Berg + * Copyright (C) 2009 Intel Corporation. All rights reserved. + */ + +#include +#include +#include +#include "nl80211.h" + +static int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) +{ + int err; + + if (!netif_running(wdev->netdev)) + return 0; + + wdev->wext.connect.ie = wdev->wext.ie; + wdev->wext.connect.ie_len = wdev->wext.ie_len; + wdev->wext.connect.privacy = wdev->wext.default_key != -1; + + err = 0; + if (wdev->wext.connect.ssid_len != 0) + err = cfg80211_connect(rdev, wdev->netdev, + &wdev->wext.connect); + + return err; +} + +int cfg80211_mgd_wext_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct ieee80211_channel *chan; + int err; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + chan = cfg80211_wext_freq(wdev->wiphy, freq); + if (chan && IS_ERR(chan)) + return PTR_ERR(chan); + + if (chan && (chan->flags & IEEE80211_CHAN_DISABLED)) + return -EINVAL; + + if (wdev->wext.connect.channel == chan) + return 0; + + if (wdev->sme_state != CFG80211_SME_IDLE) { + bool event = true; + /* if SSID set, we'll try right again, avoid event */ + if (wdev->wext.connect.ssid_len) + event = false; + err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), + dev, WLAN_REASON_DEAUTH_LEAVING, + event); + if (err) + return err; + } + + wdev->wext.connect.channel = chan; + + /* SSID is not set, we just want to switch channel */ + if (wdev->wext.connect.ssid_len && chan) { + if (!rdev->ops->set_channel) + return -EOPNOTSUPP; + + return rdev->ops->set_channel(wdev->wiphy, chan, + NL80211_CHAN_NO_HT); + } + + return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); +} +/* temporary symbol - mark GPL - in the future the handler won't be */ +EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwfreq); + +int cfg80211_mgd_wext_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct ieee80211_channel *chan = NULL; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + if (wdev->current_bss) + chan = wdev->current_bss->channel; + else if (wdev->wext.connect.channel) + chan = wdev->wext.connect.channel; + + if (chan) { + freq->m = chan->center_freq; + freq->e = 6; + return 0; + } + + /* no channel if not joining */ + return -EINVAL; +} +/* temporary symbol - mark GPL - in the future the handler won't be */ +EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwfreq); + +int cfg80211_mgd_wext_siwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + size_t len = data->length; + int err; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + if (!data->flags) + len = 0; + + /* iwconfig uses nul termination in SSID.. */ + if (len > 0 && ssid[len - 1] == '\0') + len--; + + if (wdev->wext.connect.ssid && len && + len == wdev->wext.connect.ssid_len && + memcmp(wdev->wext.connect.ssid, ssid, len)) + return 0; + + if (wdev->sme_state != CFG80211_SME_IDLE) { + bool event = true; + /* if SSID set now, we'll try to connect, avoid event */ + if (len) + event = false; + err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), + dev, WLAN_REASON_DEAUTH_LEAVING, + event); + if (err) + return err; + } + + wdev->wext.connect.ssid = wdev->wext.ssid; + memcpy(wdev->wext.ssid, ssid, len); + wdev->wext.connect.ssid_len = len; + + wdev->wext.connect.crypto.control_port = false; + + return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); +} +/* temporary symbol - mark GPL - in the future the handler won't be */ +EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwessid); + +int cfg80211_mgd_wext_giwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + data->flags = 0; + + if (wdev->ssid_len) { + data->flags = 1; + data->length = wdev->ssid_len; + memcpy(ssid, wdev->ssid, data->length); + } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) { + data->flags = 1; + data->length = wdev->wext.connect.ssid_len; + memcpy(ssid, wdev->wext.connect.ssid, data->length); + } else + data->flags = 0; + + return 0; +} +/* temporary symbol - mark GPL - in the future the handler won't be */ +EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwessid); + +int cfg80211_mgd_wext_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + u8 *bssid = ap_addr->sa_data; + int err; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + if (ap_addr->sa_family != ARPHRD_ETHER) + return -EINVAL; + + /* automatic mode */ + if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) + bssid = NULL; + + /* both automatic */ + if (!bssid && !wdev->wext.connect.bssid) + return 0; + + /* fixed already - and no change */ + if (wdev->wext.connect.bssid && bssid && + compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0) + return 0; + + if (wdev->sme_state != CFG80211_SME_IDLE) { + err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), + dev, WLAN_REASON_DEAUTH_LEAVING, + false); + if (err) + return err; + } + + if (bssid) { + memcpy(wdev->wext.bssid, bssid, ETH_ALEN); + wdev->wext.connect.bssid = wdev->wext.bssid; + } else + wdev->wext.connect.bssid = NULL; + + return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); +} +/* temporary symbol - mark GPL - in the future the handler won't be */ +EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwap); + +int cfg80211_mgd_wext_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + ap_addr->sa_family = ARPHRD_ETHER; + + if (wdev->current_bss) + memcpy(ap_addr->sa_data, wdev->current_bss->bssid, ETH_ALEN); + else if (wdev->wext.connect.bssid) + memcpy(ap_addr->sa_data, wdev->wext.connect.bssid, ETH_ALEN); + else + memset(ap_addr->sa_data, 0, ETH_ALEN); + + return 0; +} +/* temporary symbol - mark GPL - in the future the handler won't be */ +EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwap); + +int cfg80211_wext_siwgenie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + u8 *ie = extra; + int ie_len = data->length, err; + + if (wdev->iftype != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + if (!ie_len) + ie = NULL; + + /* no change */ + if (wdev->wext.ie_len == ie_len && + memcmp(wdev->wext.ie, ie, ie_len) == 0) + return 0; + + if (ie_len) { + ie = kmemdup(extra, ie_len, GFP_KERNEL); + if (!ie) + return -ENOMEM; + } else + ie = NULL; + + kfree(wdev->wext.ie); + wdev->wext.ie = ie; + wdev->wext.ie_len = ie_len; + + if (wdev->sme_state != CFG80211_SME_IDLE) { + err = cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, false); + if (err) + return err; + } + + /* userspace better not think we'll reconnect */ + return 0; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwgenie); + +int cfg80211_wext_siwmlme(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct iw_mlme *mlme = (struct iw_mlme *)extra; + struct cfg80211_registered_device *rdev; + + if (!wdev) + return -EOPNOTSUPP; + + rdev = wiphy_to_dev(wdev->wiphy); + + if (wdev->iftype != NL80211_IFTYPE_STATION) + return -EINVAL; + + if (mlme->addr.sa_family != ARPHRD_ETHER) + return -EINVAL; + + switch (mlme->cmd) { + case IW_MLME_DEAUTH: + case IW_MLME_DISASSOC: + return cfg80211_disconnect(rdev, dev, mlme->reason_code, + true); + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwmlme); -- cgit v1.2.3 From 19957bb399e2722719c0e20c9ae91cf8b6aaff04 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 2 Jul 2009 17:20:43 +0200 Subject: cfg80211: keep track of BSSes In order to avoid problems with BSS structs going away while they're in use, I've long wanted to make cfg80211 keep track of them. Without the SME, that wasn't doable but now that we have the SME we can do this too. It can keep track of up to four separate authentications and one association, regardless of whether it's controlled by the cfg80211 SME or the userspace SME. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 86 +++--------- net/mac80211/cfg.c | 22 ++- net/mac80211/mlme.c | 6 +- net/wireless/core.c | 5 +- net/wireless/core.h | 41 +++++- net/wireless/ibss.c | 12 +- net/wireless/mlme.c | 357 ++++++++++++++++++++++++++++++++++++++++++++++-- net/wireless/nl80211.c | 144 +++++++++---------- net/wireless/scan.c | 31 +---- net/wireless/sme.c | 156 ++++++++++++--------- net/wireless/wext-sme.c | 4 +- 11 files changed, 589 insertions(+), 275 deletions(-) (limited to 'net/wireless/core.h') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index eb026541f928..ca986cc91098 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -584,7 +584,6 @@ enum cfg80211_signal_type { * is no guarantee that these are well-formed!) * @len_information_elements: total length of the information elements * @signal: signal strength value (type depends on the wiphy's signal_type) - * @hold: BSS should not expire * @free_priv: function pointer to free private data * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes */ @@ -642,33 +641,17 @@ struct cfg80211_crypto_settings { * * This structure provides information needed to complete IEEE 802.11 * authentication. - * NOTE: This structure will likely change when more code from mac80211 is - * moved into cfg80211 so that non-mac80211 drivers can benefit from it, too. - * Before using this in a driver that does not use mac80211, it would be better - * to check the status of that work and better yet, volunteer to work on it. - * - * @chan: The channel to use or %NULL if not specified (auto-select based on - * scan results) - * @peer_addr: The address of the peer STA (AP BSSID in infrastructure case); - * this field is required to be present; if the driver wants to help with - * BSS selection, it should use (yet to be added) MLME event to allow user - * space SME to be notified of roaming candidate, so that the SME can then - * use the authentication request with the recommended BSSID and whatever - * other data may be needed for authentication/association - * @ssid: SSID or %NULL if not yet available - * @ssid_len: Length of ssid in octets + * + * @bss: The BSS to authenticate with. * @auth_type: Authentication type (algorithm) * @ie: Extra IEs to add to Authentication frame or %NULL * @ie_len: Length of ie buffer in octets */ struct cfg80211_auth_request { - struct ieee80211_channel *chan; - u8 *peer_addr; - const u8 *ssid; - size_t ssid_len; - enum nl80211_auth_type auth_type; + struct cfg80211_bss *bss; const u8 *ie; size_t ie_len; + enum nl80211_auth_type auth_type; }; /** @@ -676,32 +659,18 @@ struct cfg80211_auth_request { * * This structure provides information needed to complete IEEE 802.11 * (re)association. - * NOTE: This structure will likely change when more code from mac80211 is - * moved into cfg80211 so that non-mac80211 drivers can benefit from it, too. - * Before using this in a driver that does not use mac80211, it would be better - * to check the status of that work and better yet, volunteer to work on it. - * - * @chan: The channel to use or %NULL if not specified (auto-select based on - * scan results) - * @peer_addr: The address of the peer STA (AP BSSID); this field is required - * to be present and the STA must be in State 2 (authenticated) with the - * peer STA - * @ssid: SSID - * @ssid_len: Length of ssid in octets + * @bss: The BSS to associate with. * @ie: Extra IEs to add to (Re)Association Request frame or %NULL * @ie_len: Length of ie buffer in octets * @use_mfp: Use management frame protection (IEEE 802.11w) in this association * @crypto: crypto settings */ struct cfg80211_assoc_request { - struct ieee80211_channel *chan; - u8 *peer_addr; - const u8 *ssid; - size_t ssid_len; + struct cfg80211_bss *bss; const u8 *ie; size_t ie_len; - bool use_mfp; struct cfg80211_crypto_settings crypto; + bool use_mfp; }; /** @@ -710,16 +679,16 @@ struct cfg80211_assoc_request { * This structure provides information needed to complete IEEE 802.11 * deauthentication. * - * @peer_addr: The address of the peer STA (AP BSSID); this field is required - * to be present and the STA must be authenticated with the peer STA + * @bss: the BSS to deauthenticate from * @ie: Extra IEs to add to Deauthentication frame or %NULL * @ie_len: Length of ie buffer in octets + * @reason_code: The reason code for the deauthentication */ struct cfg80211_deauth_request { - u8 *peer_addr; - u16 reason_code; + struct cfg80211_bss *bss; const u8 *ie; size_t ie_len; + u16 reason_code; }; /** @@ -728,16 +697,16 @@ struct cfg80211_deauth_request { * This structure provides information needed to complete IEEE 802.11 * disassocation. * - * @peer_addr: The address of the peer STA (AP BSSID); this field is required - * to be present and the STA must be associated with the peer STA + * @bss: the BSS to disassociate from * @ie: Extra IEs to add to Disassociation frame or %NULL * @ie_len: Length of ie buffer in octets + * @reason_code: The reason code for the disassociation */ struct cfg80211_disassoc_request { - u8 *peer_addr; - u16 reason_code; + struct cfg80211_bss *bss; const u8 *ie; size_t ie_len; + u16 reason_code; }; /** @@ -1252,6 +1221,9 @@ extern void wiphy_free(struct wiphy *wiphy); /* internal struct */ struct cfg80211_conn; +struct cfg80211_internal_bss; + +#define MAX_AUTH_BSSES 4 /** * struct wireless_dev - wireless per-netdev state @@ -1281,7 +1253,6 @@ struct wireless_dev { struct net_device *netdev; /* currently used for IBSS and SME - might be rearranged later */ - struct cfg80211_bss *current_bss; u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 ssid_len; enum { @@ -1291,6 +1262,10 @@ struct wireless_dev { } sme_state; struct cfg80211_conn *conn; + struct cfg80211_internal_bss *authtry_bsses[MAX_AUTH_BSSES]; + struct cfg80211_internal_bss *auth_bsses[MAX_AUTH_BSSES]; + struct cfg80211_internal_bss *current_bss; /* associated / joined */ + #ifdef CONFIG_WIRELESS_EXT /* wext data */ struct { @@ -1812,23 +1787,6 @@ void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, gfp */ void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp); -/** - * cfg80211_hold_bss - exclude bss from expiration - * @bss: bss which should not expire - * - * In a case when the BSS is not updated but it shouldn't expire this - * function can be used to mark the BSS to be excluded from expiration. - */ -void cfg80211_hold_bss(struct cfg80211_bss *bss); - -/** - * cfg80211_unhold_bss - remove expiration exception from the BSS - * @bss: bss which can expire again - * - * This function marks the BSS to be expirable again. - */ -void cfg80211_unhold_bss(struct cfg80211_bss *bss); - /** * cfg80211_michael_mic_failure - notification of Michael MIC failure (TKIP) * @dev: network device diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 7606571d4581..0f29cd0580c9 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1173,6 +1173,7 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_auth_request *req) { struct ieee80211_sub_if_data *sdata; + const u8 *ssid; sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -1193,15 +1194,16 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, return -EOPNOTSUPP; } - memcpy(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN); + memcpy(sdata->u.mgd.bssid, req->bss->bssid, ETH_ALEN); - sdata->local->oper_channel = req->chan; + sdata->local->oper_channel = req->bss->channel; ieee80211_hw_config(sdata->local, 0); - if (!req->ssid) + ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); + if (!ssid) return -EINVAL; - memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len); - sdata->u.mgd.ssid_len = req->ssid_len; + sdata->u.mgd.ssid_len = *(ssid + 1); + memcpy(sdata->u.mgd.ssid, ssid + 2, sdata->u.mgd.ssid_len); kfree(sdata->u.mgd.sme_auth_ie); sdata->u.mgd.sme_auth_ie = NULL; @@ -1227,7 +1229,7 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (memcmp(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN) != 0 || + if (memcmp(sdata->u.mgd.bssid, req->bss->bssid, ETH_ALEN) != 0 || !(sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED)) return -ENOLINK; /* not authenticated */ @@ -1239,15 +1241,9 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) sdata->u.mgd.flags |= IEEE80211_STA_DISABLE_11N; - sdata->local->oper_channel = req->chan; + sdata->local->oper_channel = req->bss->channel; ieee80211_hw_config(sdata->local, 0); - if (!req->ssid) - return -EINVAL; - - memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len); - sdata->u.mgd.ssid_len = req->ssid_len; - ret = ieee80211_sta_set_extra_ie(sdata, req->ie, req->ie_len); if (ret && ret != -EALREADY) return ret; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 5748cda659c2..aa1829ae431d 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -876,8 +876,6 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, bss_info_changed |= ieee80211_handle_bss_capability(sdata, bss->cbss.capability, bss->has_erp_value, bss->erp_value); - cfg80211_hold_bss(&bss->cbss); - ieee80211_rx_bss_put(local, bss); } @@ -1031,10 +1029,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, conf->channel->center_freq, ifmgd->ssid, ifmgd->ssid_len); - if (bss) { - cfg80211_unhold_bss(&bss->cbss); + if (bss) ieee80211_rx_bss_put(local, bss); - } if (self_disconnected) { if (deauth) diff --git a/net/wireless/core.c b/net/wireless/core.c index 5f6a8322bcb3..7b66cf15349a 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -583,15 +583,12 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, #endif cfg80211_disconnect(rdev, dev, WLAN_REASON_DEAUTH_LEAVING, true); + cfg80211_mlme_down(rdev, dev); break; default: break; } break; - case NETDEV_DOWN: - kfree(wdev->conn); - wdev->conn = NULL; - break; case NETDEV_UP: #ifdef CONFIG_WIRELESS_EXT switch (wdev->iftype) { diff --git a/net/wireless/core.h b/net/wireless/core.h index 5209acb0ff7e..82918f5896a5 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -110,12 +110,30 @@ struct cfg80211_internal_bss { struct rb_node rbn; unsigned long ts; struct kref ref; - bool hold, ies_allocated; + atomic_t hold; + bool ies_allocated; /* must be last because of priv member */ struct cfg80211_bss pub; }; +static inline struct cfg80211_internal_bss *bss_from_pub(struct cfg80211_bss *pub) +{ + return container_of(pub, struct cfg80211_internal_bss, pub); +} + +static inline void cfg80211_hold_bss(struct cfg80211_internal_bss *bss) +{ + atomic_inc(&bss->hold); +} + +static inline void cfg80211_unhold_bss(struct cfg80211_internal_bss *bss) +{ + int r = atomic_dec_return(&bss->hold); + WARN_ON(r < 0); +} + + struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx); int get_wiphy_idx(struct wiphy *wiphy); @@ -176,6 +194,26 @@ void cfg80211_clear_ibss(struct net_device *dev, bool nowext); int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext); +/* MLME */ +int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, const u8 *bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len); +int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, struct ieee80211_channel *chan, + const u8 *bssid, const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, bool use_mfp, + struct cfg80211_crypto_settings *crypt); +int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason); +int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason); +void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, + struct net_device *dev); + /* SME */ int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, @@ -193,5 +231,6 @@ void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, size_t ie_len, u16 reason, bool from_ap); void cfg80211_sme_scan_done(struct net_device *dev); void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); +void cfg80211_sme_disassoc(struct net_device *dev, int idx); #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 34b11eae30c8..c92b542d54b0 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -33,11 +33,11 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); } - cfg80211_hold_bss(bss); - wdev->current_bss = bss; + cfg80211_hold_bss(bss_from_pub(bss)); + wdev->current_bss = bss_from_pub(bss); nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, gfp); #ifdef CONFIG_WIRELESS_EXT @@ -78,7 +78,7 @@ void cfg80211_clear_ibss(struct net_device *dev, bool nowext) if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); } wdev->current_bss = NULL; @@ -212,7 +212,7 @@ int cfg80211_ibss_wext_giwfreq(struct net_device *dev, return -EINVAL; if (wdev->current_bss) - chan = wdev->current_bss->channel; + chan = wdev->current_bss->pub.channel; else if (wdev->wext.ibss.channel) chan = wdev->wext.ibss.channel; @@ -352,7 +352,7 @@ int cfg80211_ibss_wext_giwap(struct net_device *dev, ap_addr->sa_family = ARPHRD_ETHER; if (wdev->current_bss) - memcpy(ap_addr->sa_data, wdev->current_bss->bssid, ETH_ALEN); + memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); else memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN); return 0; diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 3427fe73d3c3..1a92bf7597bf 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -14,8 +14,32 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + u8 *bssid = mgmt->bssid; + int i; + u16 status = le16_to_cpu(mgmt->u.auth.status_code); + bool done = false; + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->authtry_bsses[i] && + memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, + ETH_ALEN) == 0) { + if (status == WLAN_STATUS_SUCCESS) { + wdev->auth_bsses[i] = wdev->authtry_bsses[i]; + } else { + cfg80211_unhold_bss(wdev->authtry_bsses[i]); + cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); + } + wdev->authtry_bsses[i] = NULL; + done = true; + break; + } + } + + WARN_ON(!done); nl80211_send_rx_auth(rdev, dev, buf, len, gfp); cfg80211_sme_rx_auth(dev, buf, len); @@ -30,7 +54,8 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len, g struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; u8 *ie = mgmt->u.assoc_resp.variable; - int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); + int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); + bool done; status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); @@ -38,6 +63,20 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len, g cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, status_code, gfp); + + if (status_code == WLAN_STATUS_SUCCESS) { + for (i = 0; wdev->current_bss && i < MAX_AUTH_BSSES; i++) { + if (wdev->auth_bsses[i] == wdev->current_bss) { + cfg80211_unhold_bss(wdev->auth_bsses[i]); + cfg80211_put_bss(&wdev->auth_bsses[i]->pub); + wdev->auth_bsses[i] = NULL; + done = true; + break; + } + } + + WARN_ON(!done); + } } EXPORT_SYMBOL(cfg80211_send_rx_assoc); @@ -47,9 +86,45 @@ void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, gfp struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + const u8 *bssid = mgmt->bssid; + int i; + bool done = false; nl80211_send_deauth(rdev, dev, buf, len, gfp); + if (wdev->current_bss && + memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { + done = true; + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + wdev->current_bss = NULL; + } else for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->auth_bsses[i] && + memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) { + cfg80211_unhold_bss(wdev->auth_bsses[i]); + cfg80211_put_bss(&wdev->auth_bsses[i]->pub); + wdev->auth_bsses[i] = NULL; + done = true; + break; + } + if (wdev->authtry_bsses[i] && + memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) { + cfg80211_unhold_bss(wdev->authtry_bsses[i]); + cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); + wdev->authtry_bsses[i] = NULL; + done = true; + break; + } + } +/* + * mac80211 currently triggers this warning, + * so disable for now (it's harmless, just + * means that we got a spurious event) + + WARN_ON(!done); + + */ + if (wdev->sme_state == CFG80211_SME_CONNECTED) { u16 reason_code; bool from_ap; @@ -59,8 +134,6 @@ void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, gfp from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; __cfg80211_disconnected(dev, gfp, NULL, 0, reason_code, from_ap); - - wdev->sme_state = CFG80211_SME_IDLE; } else if (wdev->sme_state == CFG80211_SME_CONNECTING) { cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, gfp); @@ -74,21 +147,38 @@ void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, g struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + const u8 *bssid = mgmt->bssid; + int i; + u16 reason_code; + bool from_ap; + bool done = false; nl80211_send_disassoc(rdev, dev, buf, len, gfp); - if (wdev->sme_state == CFG80211_SME_CONNECTED) { - u16 reason_code; - bool from_ap; + if (!wdev->sme_state == CFG80211_SME_CONNECTED) + return; - reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); + if (wdev->current_bss && + memcmp(wdev->current_bss, bssid, ETH_ALEN) == 0) { + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->authtry_bsses[i] || wdev->auth_bsses[i]) + continue; + wdev->auth_bsses[i] = wdev->current_bss; + wdev->current_bss = NULL; + done = true; + cfg80211_sme_disassoc(dev, i); + break; + } + WARN_ON(!done); + } else + WARN_ON(1); - from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; - __cfg80211_disconnected(dev, gfp, NULL, 0, - reason_code, from_ap); - wdev->sme_state = CFG80211_SME_IDLE; - } + reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); + + from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; + __cfg80211_disconnected(dev, gfp, NULL, 0, + reason_code, from_ap); } EXPORT_SYMBOL(cfg80211_send_disassoc); @@ -97,11 +187,27 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr, gfp_t gf struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + int i; + bool done = false; + nl80211_send_auth_timeout(rdev, dev, addr, gfp); if (wdev->sme_state == CFG80211_SME_CONNECTING) cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, gfp); - wdev->sme_state = CFG80211_SME_IDLE; + + for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { + if (wdev->authtry_bsses[i] && + memcmp(wdev->authtry_bsses[i]->pub.bssid, + addr, ETH_ALEN) == 0) { + cfg80211_unhold_bss(wdev->authtry_bsses[i]); + cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); + wdev->authtry_bsses[i] = NULL; + done = true; + break; + } + } + + WARN_ON(!done); } EXPORT_SYMBOL(cfg80211_send_auth_timeout); @@ -110,11 +216,27 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr, gfp_t g struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + int i; + bool done = false; + nl80211_send_assoc_timeout(rdev, dev, addr, gfp); if (wdev->sme_state == CFG80211_SME_CONNECTING) cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, gfp); - wdev->sme_state = CFG80211_SME_IDLE; + + for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { + if (wdev->auth_bsses[i] && + memcmp(wdev->auth_bsses[i]->pub.bssid, + addr, ETH_ALEN) == 0) { + cfg80211_unhold_bss(wdev->auth_bsses[i]); + cfg80211_put_bss(&wdev->auth_bsses[i]->pub); + wdev->auth_bsses[i] = NULL; + done = true; + break; + } + } + + WARN_ON(!done); } EXPORT_SYMBOL(cfg80211_send_assoc_timeout); @@ -143,3 +265,208 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp); } EXPORT_SYMBOL(cfg80211_michael_mic_failure); + +/* some MLME handling for userspace SME */ +int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, const u8 *bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_auth_request req; + struct cfg80211_internal_bss *bss; + int i, err, slot = -1, nfree = 0; + + memset(&req, 0, sizeof(req)); + + req.ie = ie; + req.ie_len = ie_len; + req.auth_type = auth_type; + req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, + WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + if (!req.bss) + return -ENOENT; + + bss = bss_from_pub(req.bss); + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (bss == wdev->auth_bsses[i]) { + err = -EALREADY; + goto out; + } + } + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) { + slot = i; + nfree++; + } + } + + /* we need one free slot for disassoc and one for this auth */ + if (nfree < 2) { + err = -ENOSPC; + goto out; + } + + wdev->authtry_bsses[slot] = bss; + cfg80211_hold_bss(bss); + + err = rdev->ops->auth(&rdev->wiphy, dev, &req); + if (err) { + wdev->authtry_bsses[slot] = NULL; + cfg80211_unhold_bss(bss); + } + + out: + if (err) + cfg80211_put_bss(req.bss); + return err; +} + +int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, struct ieee80211_channel *chan, + const u8 *bssid, const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, bool use_mfp, + struct cfg80211_crypto_settings *crypt) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_assoc_request req; + struct cfg80211_internal_bss *bss; + int i, err, slot = -1; + + memset(&req, 0, sizeof(req)); + + if (wdev->current_bss) + return -EALREADY; + + req.ie = ie; + req.ie_len = ie_len; + memcpy(&req.crypto, crypt, sizeof(req.crypto)); + req.use_mfp = use_mfp; + req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, + WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + if (!req.bss) + return -ENOENT; + + bss = bss_from_pub(req.bss); + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (bss == wdev->auth_bsses[i]) { + slot = i; + break; + } + } + + if (slot < 0) { + err = -ENOTCONN; + goto out; + } + + err = rdev->ops->assoc(&rdev->wiphy, dev, &req); + out: + /* still a reference in wdev->auth_bsses[slot] */ + cfg80211_put_bss(req.bss); + return err; +} + +int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_deauth_request req; + int i; + + memset(&req, 0, sizeof(req)); + req.reason_code = reason; + req.ie = ie; + req.ie_len = ie_len; + if (wdev->current_bss && + memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { + req.bss = &wdev->current_bss->pub; + } else for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->auth_bsses[i] && + memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) { + req.bss = &wdev->auth_bsses[i]->pub; + break; + } + if (wdev->authtry_bsses[i] && + memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) { + req.bss = &wdev->authtry_bsses[i]->pub; + break; + } + } + + if (!req.bss) + return -ENOTCONN; + + return rdev->ops->deauth(&rdev->wiphy, dev, &req); +} + +int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_disassoc_request req; + + memset(&req, 0, sizeof(req)); + req.reason_code = reason; + req.ie = ie; + req.ie_len = ie_len; + if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) + req.bss = &wdev->current_bss->pub; + else + return -ENOTCONN; + + return rdev->ops->disassoc(&rdev->wiphy, dev, &req); +} + +void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_deauth_request req; + int i; + + if (!rdev->ops->deauth) + return; + + memset(&req, 0, sizeof(req)); + req.reason_code = WLAN_REASON_DEAUTH_LEAVING; + req.ie = NULL; + req.ie_len = 0; + + if (wdev->current_bss) { + req.bss = &wdev->current_bss->pub; + rdev->ops->deauth(&rdev->wiphy, dev, &req); + if (wdev->current_bss) { + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + wdev->current_bss = NULL; + } + } + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->auth_bsses[i]) { + req.bss = &wdev->auth_bsses[i]->pub; + rdev->ops->deauth(&rdev->wiphy, dev, &req); + if (wdev->auth_bsses[i]) { + cfg80211_unhold_bss(wdev->auth_bsses[i]); + cfg80211_put_bss(&wdev->auth_bsses[i]->pub); + wdev->auth_bsses[i] = NULL; + } + } + if (wdev->authtry_bsses[i]) { + req.bss = &wdev->authtry_bsses[i]->pub; + rdev->ops->deauth(&rdev->wiphy, dev, &req); + if (wdev->authtry_bsses[i]) { + cfg80211_unhold_bss(wdev->authtry_bsses[i]); + cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); + wdev->authtry_bsses[i] = NULL; + } + } + } +} diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0008144b354b..aa2b3f35cc48 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3044,9 +3044,10 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *drv; struct net_device *dev; - struct cfg80211_auth_request req; - struct wiphy *wiphy; - int err; + struct ieee80211_channel *chan; + const u8 *bssid, *ssid, *ie = NULL; + int err, ssid_len, ie_len = 0; + enum nl80211_auth_type auth_type; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; @@ -3057,6 +3058,12 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[NL80211_ATTR_AUTH_TYPE]) return -EINVAL; + if (!info->attrs[NL80211_ATTR_SSID]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) + return -EINVAL; + rtnl_lock(); err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); @@ -3078,38 +3085,30 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) goto out; } - wiphy = &drv->wiphy; - memset(&req, 0, sizeof(req)); - - req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - - if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { - req.chan = ieee80211_get_channel( - wiphy, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); - if (!req.chan) { - err = -EINVAL; - goto out; - } + bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + chan = ieee80211_get_channel(&drv->wiphy, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) { + err = -EINVAL; + goto out; } - if (info->attrs[NL80211_ATTR_SSID]) { - req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); - req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); - } + ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); if (info->attrs[NL80211_ATTR_IE]) { - req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); - req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + ie = nla_data(info->attrs[NL80211_ATTR_IE]); + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } - req.auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); - if (!nl80211_valid_auth_type(req.auth_type)) { + auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); + if (!nl80211_valid_auth_type(auth_type)) { err = -EINVAL; goto out; } - err = drv->ops->auth(&drv->wiphy, dev, &req); + err = cfg80211_mlme_auth(drv, dev, chan, auth_type, bssid, + ssid, ssid_len, ie, ie_len); out: cfg80211_put_dev(drv); @@ -3183,26 +3182,29 @@ static int nl80211_crypto_settings(struct genl_info *info, static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; - struct cfg80211_assoc_request req; - struct wiphy *wiphy; - int err; + struct cfg80211_crypto_settings crypto; + struct ieee80211_channel *chan; + const u8 *bssid, *ssid, *ie = NULL; + int err, ssid_len, ie_len = 0; + bool use_mfp = false; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; if (!info->attrs[NL80211_ATTR_MAC] || - !info->attrs[NL80211_ATTR_SSID]) + !info->attrs[NL80211_ATTR_SSID] || + !info->attrs[NL80211_ATTR_WIPHY_FREQ]) return -EINVAL; rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_drv_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->assoc) { + if (!rdev->ops->assoc) { err = -EOPNOTSUPP; goto out; } @@ -3217,46 +3219,42 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) goto out; } - wiphy = &drv->wiphy; - memset(&req, 0, sizeof(req)); - - req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { - req.chan = ieee80211_get_channel( - wiphy, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); - if (!req.chan) { - err = -EINVAL; - goto out; - } + chan = ieee80211_get_channel(&rdev->wiphy, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) { + err = -EINVAL; + goto out; } - req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); - req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); if (info->attrs[NL80211_ATTR_IE]) { - req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); - req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + ie = nla_data(info->attrs[NL80211_ATTR_IE]); + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } if (info->attrs[NL80211_ATTR_USE_MFP]) { enum nl80211_mfp use_mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]); if (use_mfp == NL80211_MFP_REQUIRED) - req.use_mfp = true; + use_mfp = true; else if (use_mfp != NL80211_MFP_NO) { err = -EINVAL; goto out; } } - err = nl80211_crypto_settings(info, &req.crypto); + err = nl80211_crypto_settings(info, &crypto); if (!err) - err = drv->ops->assoc(&drv->wiphy, dev, &req); + err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, ssid, + ssid_len, ie, ie_len, use_mfp, + &crypto); out: - cfg80211_put_dev(drv); + cfg80211_put_dev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3267,9 +3265,9 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *drv; struct net_device *dev; - struct cfg80211_deauth_request req; - struct wiphy *wiphy; - int err; + const u8 *ie = NULL, *bssid; + int err, ie_len = 0; + u16 reason_code; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; @@ -3301,24 +3299,21 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) goto out; } - wiphy = &drv->wiphy; - memset(&req, 0, sizeof(req)); - - req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); - if (req.reason_code == 0) { + reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + if (reason_code == 0) { /* Reason Code 0 is reserved */ err = -EINVAL; goto out; } if (info->attrs[NL80211_ATTR_IE]) { - req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); - req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + ie = nla_data(info->attrs[NL80211_ATTR_IE]); + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } - err = drv->ops->deauth(&drv->wiphy, dev, &req); + err = cfg80211_mlme_deauth(drv, dev, bssid, ie, ie_len, reason_code); out: cfg80211_put_dev(drv); @@ -3332,9 +3327,9 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *drv; struct net_device *dev; - struct cfg80211_disassoc_request req; - struct wiphy *wiphy; - int err; + const u8 *ie = NULL, *bssid; + int err, ie_len = 0; + u16 reason_code; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; @@ -3366,24 +3361,21 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) goto out; } - wiphy = &drv->wiphy; - memset(&req, 0, sizeof(req)); - - req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); - if (req.reason_code == 0) { + reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + if (reason_code == 0) { /* Reason Code 0 is reserved */ err = -EINVAL; goto out; } if (info->attrs[NL80211_ATTR_IE]) { - req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); - req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + ie = nla_data(info->attrs[NL80211_ATTR_IE]); + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } - err = drv->ops->disassoc(&drv->wiphy, dev, &req); + err = cfg80211_mlme_disassoc(drv, dev, bssid, ie, ie_len, reason_code); out: cfg80211_put_dev(drv); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 82b33e708488..925399462a79 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -70,6 +70,8 @@ static void bss_release(struct kref *ref) if (bss->ies_allocated) kfree(bss->pub.information_elements); + BUG_ON(atomic_read(&bss->hold)); + kfree(bss); } @@ -92,8 +94,9 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *dev) bool expired = false; list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { - if (bss->hold || - !time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) + if (atomic_read(&bss->hold)) + continue; + if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) continue; list_del(&bss->list); rb_erase(&bss->rbn, &dev->bss_tree); @@ -553,30 +556,6 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) } EXPORT_SYMBOL(cfg80211_unlink_bss); -void cfg80211_hold_bss(struct cfg80211_bss *pub) -{ - struct cfg80211_internal_bss *bss; - - if (!pub) - return; - - bss = container_of(pub, struct cfg80211_internal_bss, pub); - bss->hold = true; -} -EXPORT_SYMBOL(cfg80211_hold_bss); - -void cfg80211_unhold_bss(struct cfg80211_bss *pub) -{ - struct cfg80211_internal_bss *bss; - - if (!pub) - return; - - bss = container_of(pub, struct cfg80211_internal_bss, pub); - bss->hold = false; -} -EXPORT_SYMBOL(cfg80211_unhold_bss); - #ifdef CONFIG_WIRELESS_EXT int cfg80211_wext_siwscan(struct net_device *dev, struct iw_request_info *info, diff --git a/net/wireless/sme.c b/net/wireless/sme.c index d4e0b4065cbc..412161f7b08e 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -103,44 +103,37 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) static int cfg80211_conn_do_work(struct wireless_dev *wdev) { struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); - union { - struct cfg80211_auth_request auth_req; - struct cfg80211_assoc_request assoc_req; - } u; - - memset(&u, 0, sizeof(u)); + struct cfg80211_connect_params *params; + int err; if (!wdev->conn) return 0; + params = &wdev->conn->params; + switch (wdev->conn->state) { case CFG80211_CONN_SCAN_AGAIN: return cfg80211_conn_scan(wdev); case CFG80211_CONN_AUTHENTICATE_NEXT: - u.auth_req.chan = wdev->conn->params.channel; - u.auth_req.peer_addr = wdev->conn->params.bssid; - u.auth_req.ssid = wdev->conn->params.ssid; - u.auth_req.ssid_len = wdev->conn->params.ssid_len; - u.auth_req.auth_type = wdev->conn->params.auth_type; - u.auth_req.ie = NULL; - u.auth_req.ie_len = 0; - wdev->conn->state = CFG80211_CONN_AUTHENTICATING; BUG_ON(!drv->ops->auth); - return drv->ops->auth(wdev->wiphy, wdev->netdev, &u.auth_req); + wdev->conn->state = CFG80211_CONN_AUTHENTICATING; + return cfg80211_mlme_auth(drv, wdev->netdev, + params->channel, params->auth_type, + params->bssid, + params->ssid, params->ssid_len, + NULL, 0); case CFG80211_CONN_ASSOCIATE_NEXT: - u.assoc_req.chan = wdev->conn->params.channel; - u.assoc_req.peer_addr = wdev->conn->params.bssid; - u.assoc_req.ssid = wdev->conn->params.ssid; - u.assoc_req.ssid_len = wdev->conn->params.ssid_len; - u.assoc_req.ie = wdev->conn->params.ie; - u.assoc_req.ie_len = wdev->conn->params.ie_len; - u.assoc_req.use_mfp = false; - memcpy(&u.assoc_req.crypto, &wdev->conn->params.crypto, - sizeof(u.assoc_req.crypto)); - wdev->conn->state = CFG80211_CONN_ASSOCIATING; BUG_ON(!drv->ops->assoc); - return drv->ops->assoc(wdev->wiphy, wdev->netdev, - &u.assoc_req); + wdev->conn->state = CFG80211_CONN_ASSOCIATING; + err = cfg80211_mlme_assoc(drv, wdev->netdev, + params->channel, params->bssid, + params->ssid, params->ssid_len, + params->ie, params->ie_len, + false, ¶ms->crypto); + if (err) + cfg80211_mlme_deauth(drv, wdev->netdev, params->bssid, + NULL, 0, WLAN_REASON_DEAUTH_LEAVING); + return err; default: return 0; } @@ -186,7 +179,6 @@ static bool cfg80211_get_conn_bss(struct wireless_dev *wdev) wdev->conn->params.ssid_len, WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, capa); - if (!bss) return false; @@ -264,9 +256,11 @@ void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len) } wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; schedule_work(&rdev->conn_work); - } else if (status_code != WLAN_STATUS_SUCCESS) + } else if (status_code != WLAN_STATUS_SUCCESS) { wdev->sme_state = CFG80211_SME_IDLE; - else if (wdev->sme_state == CFG80211_SME_CONNECTING && + kfree(wdev->conn); + wdev->conn = NULL; + } else if (wdev->sme_state == CFG80211_SME_CONNECTING && wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; schedule_work(&rdev->conn_work); @@ -330,10 +324,13 @@ static void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); wdev->current_bss = NULL; } + if (wdev->conn) + wdev->conn->state = CFG80211_CONN_IDLE; + if (status == WLAN_STATUS_SUCCESS) { bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, wdev->ssid, wdev->ssid_len, @@ -343,16 +340,15 @@ static void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (WARN_ON(!bss)) return; - cfg80211_hold_bss(bss); - wdev->current_bss = bss; + cfg80211_hold_bss(bss_from_pub(bss)); + wdev->current_bss = bss_from_pub(bss); wdev->sme_state = CFG80211_SME_CONNECTED; } else { wdev->sme_state = CFG80211_SME_IDLE; + kfree(wdev->conn); + wdev->conn = NULL; } - - if (wdev->conn) - wdev->conn->state = CFG80211_CONN_IDLE; } void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, @@ -387,7 +383,7 @@ void cfg80211_roamed(struct net_device *dev, const u8 *bssid, } cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); wdev->current_bss = NULL; bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, @@ -397,8 +393,8 @@ void cfg80211_roamed(struct net_device *dev, const u8 *bssid, if (WARN_ON(!bss)) return; - cfg80211_hold_bss(bss); - wdev->current_bss = bss; + cfg80211_hold_bss(bss_from_pub(bss)); + wdev->current_bss = bss_from_pub(bss); nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, gfp); @@ -440,7 +436,7 @@ void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); } wdev->current_bss = NULL; @@ -449,6 +445,8 @@ void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, if (wdev->conn) { kfree(wdev->conn->ie); wdev->conn->ie = NULL; + kfree(wdev->conn); + wdev->conn = NULL; } nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev, @@ -482,12 +480,12 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, if (!rdev->ops->auth || !rdev->ops->assoc) return -EOPNOTSUPP; - if (!wdev->conn) { - wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); - if (!wdev->conn) - return -ENOMEM; - } else - memset(wdev->conn, 0, sizeof(*wdev->conn)); + if (WARN_ON(wdev->conn)) + return -EINPROGRESS; + + wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); + if (!wdev->conn) + return -ENOMEM; /* * Copy all parameters, and treat explicitly IEs, BSSID, SSID. @@ -502,8 +500,11 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, wdev->conn->ie = kmemdup(connect->ie, connect->ie_len, GFP_KERNEL); wdev->conn->params.ie = wdev->conn->ie; - if (!wdev->conn->ie) + if (!wdev->conn->ie) { + kfree(wdev->conn); + wdev->conn = NULL; return -ENOMEM; + } } if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { @@ -543,8 +544,11 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; } } - if (err) + if (err) { + kfree(wdev->conn); + wdev->conn = NULL; wdev->sme_state = CFG80211_SME_IDLE; + } return err; } else { @@ -572,31 +576,27 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, return -EINVAL; if (!rdev->ops->disconnect) { - struct cfg80211_deauth_request deauth; - u8 bssid[ETH_ALEN]; + if (!rdev->ops->deauth) + return -EOPNOTSUPP; - /* internal bug. */ - if (WARN_ON(!wdev->conn)) - return -EINVAL; + /* was it connected by userspace SME? */ + if (!wdev->conn) { + cfg80211_mlme_down(rdev, dev); + return 0; + } if (wdev->sme_state == CFG80211_SME_CONNECTING && (wdev->conn->state == CFG80211_CONN_SCANNING || wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) { wdev->sme_state = CFG80211_SME_IDLE; + kfree(wdev->conn); + wdev->conn = NULL; return 0; } - if (!rdev->ops->deauth) - return -EOPNOTSUPP; - - memset(&deauth, 0, sizeof(deauth)); - /* wdev->conn->params.bssid must be set if > SCANNING */ - memcpy(bssid, wdev->conn->params.bssid, ETH_ALEN); - deauth.peer_addr = bssid; - deauth.reason_code = reason; - - err = rdev->ops->deauth(&rdev->wiphy, dev, &deauth); + err = cfg80211_mlme_deauth(rdev, dev, wdev->conn->params.bssid, + NULL, 0, reason); if (err) return err; } else { @@ -614,3 +614,33 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, return 0; } + +void cfg80211_sme_disassoc(struct net_device *dev, int idx) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + u8 bssid[ETH_ALEN]; + + if (!wdev->conn) + return; + + if (wdev->conn->state == CFG80211_CONN_IDLE) + return; + + /* + * Ok, so the association was made by this SME -- we don't + * want it any more so deauthenticate too. + */ + + if (!wdev->auth_bsses[idx]) + return; + + memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN); + if (cfg80211_mlme_deauth(rdev, dev, bssid, + NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) { + /* whatever -- assume gone anyway */ + cfg80211_unhold_bss(wdev->auth_bsses[idx]); + cfg80211_put_bss(&wdev->auth_bsses[idx]->pub); + wdev->auth_bsses[idx] = NULL; + } +} diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 3b531d572b69..fe1987acb891 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -93,7 +93,7 @@ int cfg80211_mgd_wext_giwfreq(struct net_device *dev, return -EINVAL; if (wdev->current_bss) - chan = wdev->current_bss->channel; + chan = wdev->current_bss->pub.channel; else if (wdev->wext.connect.channel) chan = wdev->wext.connect.channel; @@ -244,7 +244,7 @@ int cfg80211_mgd_wext_giwap(struct net_device *dev, ap_addr->sa_family = ARPHRD_ETHER; if (wdev->current_bss) - memcpy(ap_addr->sa_data, wdev->current_bss->bssid, ETH_ALEN); + memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); else if (wdev->wext.connect.bssid) memcpy(ap_addr->sa_data, wdev->wext.connect.bssid, ETH_ALEN); else -- cgit v1.2.3 From 3e5d7649a64e558e4146ddfad4dfcf13fc65dd47 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 14:37:26 +0200 Subject: cfg80211: let SME control reassociation vs. association Since we don't really know that well in the kernel, let's let the SME control whether it wants to use reassociation or not, by allowing it to give the previous BSSID in the associate() parameters. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 5 +++++ include/net/cfg80211.h | 3 ++- net/mac80211/cfg.c | 6 ++++++ net/mac80211/mlme.c | 7 ------- net/wireless/core.h | 3 ++- net/wireless/mlme.c | 4 +++- net/wireless/nl80211.c | 10 +++++++--- net/wireless/sme.c | 8 +++++++- 8 files changed, 32 insertions(+), 14 deletions(-) (limited to 'net/wireless/core.h') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index b34c17f52f3e..e496a2daf7ef 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -564,6 +564,9 @@ enum nl80211_commands { * @NL80211_ATTR_RESP_IE: (Re)association response information elements as * sent by peer, for ROAM and successful CONNECT events. * + * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used by in ASSOCIATE + * commands to specify using a reassociate frame + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -687,6 +690,8 @@ enum nl80211_attrs { NL80211_ATTR_REQ_IE, NL80211_ATTR_RESP_IE, + NL80211_ATTR_PREV_BSSID, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ca986cc91098..71847d3c2640 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -664,10 +664,11 @@ struct cfg80211_auth_request { * @ie_len: Length of ie buffer in octets * @use_mfp: Use management frame protection (IEEE 802.11w) in this association * @crypto: crypto settings + * @prev_bssid: previous BSSID, if not %NULL use reassociate frame */ struct cfg80211_assoc_request { struct cfg80211_bss *bss; - const u8 *ie; + const u8 *ie, *prev_bssid; size_t ie_len; struct cfg80211_crypto_settings crypto; bool use_mfp; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 0f29cd0580c9..e6d8860f26f2 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1256,6 +1256,12 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, sdata->u.mgd.flags &= ~IEEE80211_STA_MFP_ENABLED; } + if (req->prev_bssid) { + sdata->u.mgd.flags |= IEEE80211_STA_PREV_BSSID_SET; + memcpy(sdata->u.mgd.prev_bssid, req->prev_bssid, ETH_ALEN); + } else + sdata->u.mgd.flags &= ~IEEE80211_STA_PREV_BSSID_SET; + if (req->crypto.control_port) sdata->u.mgd.flags |= IEEE80211_STA_CONTROL_PORT; else diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index aa1829ae431d..24486455e505 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -879,9 +879,6 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, ieee80211_rx_bss_put(local, bss); } - ifmgd->flags |= IEEE80211_STA_PREV_BSSID_SET; - memcpy(ifmgd->prev_bssid, sdata->u.mgd.bssid, ETH_ALEN); - ifmgd->last_probe = jiffies; ieee80211_led_assoc(local, 1); @@ -1470,10 +1467,6 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, if (status_code != WLAN_STATUS_SUCCESS) { printk(KERN_DEBUG "%s: AP denied association (code=%d)\n", sdata->dev->name, status_code); - /* if this was a reassociation, ensure we try a "full" - * association next time. This works around some broken APs - * which do not correctly reject reassociation requests. */ - ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET; cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len, GFP_KERNEL); /* Wait for SME to decide what to do next */ diff --git a/net/wireless/core.h b/net/wireless/core.h index 82918f5896a5..4554453c116a 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -202,7 +202,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, const u8 *ie, int ie_len); int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, - const u8 *bssid, const u8 *ssid, int ssid_len, + const u8 *bssid, const u8 *prev_bssid, + const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, bool use_mfp, struct cfg80211_crypto_settings *crypt); int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 020f33b38467..087d3377958f 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -335,7 +335,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, - const u8 *bssid, const u8 *ssid, int ssid_len, + const u8 *bssid, const u8 *prev_bssid, + const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, bool use_mfp, struct cfg80211_crypto_settings *crypt) { @@ -353,6 +354,7 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, req.ie_len = ie_len; memcpy(&req.crypto, crypt, sizeof(req.crypto)); req.use_mfp = use_mfp; + req.prev_bssid = prev_bssid; req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); if (!req.bss) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 723512b48f2e..44c520c264fc 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -71,6 +71,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 }, [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN }, + [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN }, [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, @@ -3187,7 +3188,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) struct net_device *dev; struct cfg80211_crypto_settings crypto; struct ieee80211_channel *chan; - const u8 *bssid, *ssid, *ie = NULL; + const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; int err, ssid_len, ie_len = 0; bool use_mfp = false; @@ -3248,10 +3249,13 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) } } + if (info->attrs[NL80211_ATTR_PREV_BSSID]) + prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]); + err = nl80211_crypto_settings(info, &crypto, 1); if (!err) - err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, ssid, - ssid_len, ie, ie_len, use_mfp, + err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, + ssid, ssid_len, ie, ie_len, use_mfp, &crypto); out: diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 412161f7b08e..066a19ef9d73 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -125,8 +125,14 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) case CFG80211_CONN_ASSOCIATE_NEXT: BUG_ON(!drv->ops->assoc); wdev->conn->state = CFG80211_CONN_ASSOCIATING; + /* + * We could, later, implement roaming here and then actually + * set prev_bssid to non-NULL. But then we need to be aware + * that some APs don't like that -- so we'd need to retry + * the association. + */ err = cfg80211_mlme_assoc(drv, wdev->netdev, - params->channel, params->bssid, + params->channel, params->bssid, NULL, params->ssid, params->ssid_len, params->ie, params->ie_len, false, ¶ms->crypto); -- cgit v1.2.3 From 4d0c8aead32ecdbe1310ad473b3637991f560865 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 03:56:09 +0200 Subject: cfg80211: properly name driver locking Currently we call that cfg80211_put_dev(), but that is misleading. With the new convention of using 'rdev' for registered_device variables, also call that function cfg80211_unlock_rdev(). Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 6 ---- net/wireless/core.h | 8 ++++-- net/wireless/nl80211.c | 74 +++++++++++++++++++++++++------------------------- net/wireless/scan.c | 6 ++-- 4 files changed, 45 insertions(+), 49 deletions(-) (limited to 'net/wireless/core.h') diff --git a/net/wireless/core.c b/net/wireless/core.c index 83ec7197bd7c..c6813beded06 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -171,12 +171,6 @@ cfg80211_get_dev_from_ifindex(int ifindex) return drv; } -void cfg80211_put_dev(struct cfg80211_registered_device *drv) -{ - BUG_ON(IS_ERR(drv)); - mutex_unlock(&drv->mtx); -} - /* requires cfg80211_mutex to be held */ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, char *newname) diff --git a/net/wireless/core.h b/net/wireless/core.h index 4554453c116a..92da612b3f9e 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -146,7 +146,7 @@ __cfg80211_drv_from_info(struct genl_info *info); * If successful, it returns non-NULL and also locks * the driver's mutex! * - * This means that you need to call cfg80211_put_dev() + * This means that you need to call cfg80211_unlock_rdev() * before being allowed to acquire &cfg80211_mutex! * * This is necessary because we need to lock the global @@ -170,7 +170,11 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx); extern struct cfg80211_registered_device * cfg80211_get_dev_from_ifindex(int ifindex); -extern void cfg80211_put_dev(struct cfg80211_registered_device *drv); +static inline void cfg80211_unlock_rdev(struct cfg80211_registered_device *drv) +{ + BUG_ON(IS_ERR(drv) || !drv); + mutex_unlock(&drv->mtx); +} /* free object */ extern void cfg80211_dev_free(struct cfg80211_registered_device *drv); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 44c520c264fc..df22048419e3 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -411,14 +411,14 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info) if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0) goto out_free; - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); return genlmsg_unicast(msg, info->snd_pid); out_free: nlmsg_free(msg); out_err: - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); return -ENOBUFS; } @@ -737,7 +737,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) goto out_free; dev_put(netdev); - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); return genlmsg_unicast(msg, info->snd_pid); @@ -745,7 +745,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) nlmsg_free(msg); out_err: dev_put(netdev); - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); return -ENOBUFS; } @@ -853,7 +853,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) unlock: dev_put(dev); - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); unlock_rtnl: rtnl_unlock(); return err; @@ -906,7 +906,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) type, err ? NULL : &flags, ¶ms); unlock: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); unlock_rtnl: rtnl_unlock(); return err; @@ -934,7 +934,7 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); unlock_rtnl: rtnl_unlock(); return err; @@ -1037,7 +1037,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) err = -ENOBUFS; nlmsg_free(msg); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1097,7 +1097,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) #endif out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: @@ -1163,7 +1163,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, ¶ms); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1211,7 +1211,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) #endif out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: @@ -1306,7 +1306,7 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) err = call(&drv->wiphy, dev, ¶ms); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1338,7 +1338,7 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) err = drv->ops->del_beacon(&drv->wiphy, dev); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1572,7 +1572,7 @@ static int nl80211_dump_station(struct sk_buff *skb, cb->args[1] = sta_idx; err = skb->len; out_err: - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); out_rtnl: rtnl_unlock(); @@ -1624,7 +1624,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) out_free: nlmsg_free(msg); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -1760,7 +1760,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) out: if (params.vlan) dev_put(params.vlan); - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -1865,7 +1865,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) out: if (params.vlan) dev_put(params.vlan); - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -1904,7 +1904,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) err = drv->ops->del_station(&drv->wiphy, dev, mac_addr); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2035,7 +2035,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb, cb->args[1] = path_idx; err = skb->len; out_err: - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); out_rtnl: rtnl_unlock(); @@ -2093,7 +2093,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) out_free: nlmsg_free(msg); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2142,7 +2142,7 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2190,7 +2190,7 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2222,7 +2222,7 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) err = drv->ops->del_mpath(&drv->wiphy, dev, dst); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2278,7 +2278,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) err = drv->ops->change_bss(&drv->wiphy, dev, ¶ms); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2444,7 +2444,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, err = -EMSGSIZE; out: /* Cleanup */ - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2550,7 +2550,7 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) out: /* cleanup */ - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2890,7 +2890,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) kfree(request); } out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -3007,7 +3007,7 @@ static int nl80211_dump_scan(struct sk_buff *skb, cb->args[1] = idx; err = skb->len; - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); out_put_netdev: dev_put(netdev); @@ -3112,7 +3112,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) ssid, ssid_len, ie, ie_len); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3259,7 +3259,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) &crypto); out: - cfg80211_put_dev(rdev); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3321,7 +3321,7 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) err = cfg80211_mlme_deauth(drv, dev, bssid, ie, ie_len, reason_code); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3383,7 +3383,7 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) err = cfg80211_mlme_disassoc(drv, dev, bssid, ie, ie_len, reason_code); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3464,7 +3464,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) err = cfg80211_join_ibss(drv, dev, &ibss); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3501,7 +3501,7 @@ static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info) err = cfg80211_leave_ibss(drv, dev, false); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3538,7 +3538,7 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) rdev->testmode_info = NULL; } - cfg80211_put_dev(rdev); + cfg80211_unlock_rdev(rdev); unlock_rtnl: rtnl_unlock(); @@ -3707,7 +3707,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) err = cfg80211_connect(drv, dev, &connect); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3748,7 +3748,7 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) err = cfg80211_disconnect(drv, dev, reason, true); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 925399462a79..1625faf1de57 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -28,8 +28,6 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) if (!dev) goto out; - WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); - /* * This must be before sending the other events! * Otherwise, wpa_supplicant gets completely confused with @@ -636,7 +634,7 @@ int cfg80211_wext_siwscan(struct net_device *dev, } else nl80211_send_scan_start(rdev, dev); out: - cfg80211_put_dev(rdev); + cfg80211_unlock_rdev(rdev); return err; } EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan); @@ -945,7 +943,7 @@ int cfg80211_wext_giwscan(struct net_device *dev, } out: - cfg80211_put_dev(rdev); + cfg80211_unlock_rdev(rdev); return res; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwscan); -- cgit v1.2.3 From 667503ddcb96f3b10211f997fe55907fa7509841 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 03:56:11 +0200 Subject: cfg80211: fix locking Over time, a lot of locking issues have crept into the smarts of cfg80211, so e.g. scan completion can race against a new scan, IBSS join can race against leaving an IBSS, etc. Introduce a new per-interface lock that protects most of the per-interface data that we need to keep track of, and sprinkle assertions about that lock everywhere. Some things now need to be offloaded to work structs so that we don't require being able to sleep in functions the drivers call. The exception to that are the MLME callbacks (rx_auth etc.) that currently only mac80211 calls because it was easier to do that there instead of in cfg80211, and future drivers implementing those calls will, if they ever exist, probably need to use a similar scheme like mac80211 anyway... In order to be able to handle _deauth and _disassoc properly, introduce a cookie passed to it that will determine locking requirements. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 24 ++++- net/mac80211/cfg.c | 12 ++- net/mac80211/ieee80211_i.h | 6 +- net/mac80211/mlme.c | 25 +++-- net/wireless/core.c | 92 +++++++++++++++-- net/wireless/core.h | 100 +++++++++++++++++- net/wireless/ibss.c | 133 +++++++++++++++++++----- net/wireless/mlme.c | 214 +++++++++++++++++++++++++++++--------- net/wireless/nl80211.c | 8 +- net/wireless/nl80211.h | 2 +- net/wireless/scan.c | 30 +++++- net/wireless/sme.c | 252 +++++++++++++++++++++++++++++++++++---------- net/wireless/wext-sme.c | 125 +++++++++++++++------- 13 files changed, 823 insertions(+), 200 deletions(-) (limited to 'net/wireless/core.h') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 60c1f11da45f..83c2c727d71e 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -555,6 +555,7 @@ struct cfg80211_scan_request { /* internal */ struct wiphy *wiphy; int ifidx; + bool aborted; }; /** @@ -998,9 +999,11 @@ struct cfg80211_ops { int (*assoc)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_assoc_request *req); int (*deauth)(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_deauth_request *req); + struct cfg80211_deauth_request *req, + void *cookie); int (*disassoc)(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_disassoc_request *req); + struct cfg80211_disassoc_request *req, + void *cookie); int (*connect)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme); @@ -1249,10 +1252,12 @@ struct wireless_dev { struct wiphy *wiphy; enum nl80211_iftype iftype; - /* private to the generic wireless code */ + /* the remainder of this struct should be private to cfg80211 */ struct list_head list; struct net_device *netdev; + struct mutex mtx; + /* currently used for IBSS and SME - might be rearranged later */ u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 ssid_len; @@ -1263,6 +1268,9 @@ struct wireless_dev { } sme_state; struct cfg80211_conn *conn; + struct list_head event_list; + spinlock_t event_lock; + struct cfg80211_internal_bss *authtry_bsses[MAX_AUTH_BSSES]; struct cfg80211_internal_bss *auth_bsses[MAX_AUTH_BSSES]; struct cfg80211_internal_bss *current_bss; /* associated / joined */ @@ -1765,24 +1773,30 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr); * @dev: network device * @buf: deauthentication frame (header + body) * @len: length of the frame data + * @cookie: cookie from ->deauth if called within that callback, + * %NULL otherwise * * This function is called whenever deauthentication has been processed in * station mode. This includes both received deauthentication frames and * locally generated ones. This function may sleep. */ -void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len); +void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, + void *cookie); /** * cfg80211_send_disassoc - notification of processed disassociation * @dev: network device * @buf: disassociation response frame (header + body) * @len: length of the frame data + * @cookie: cookie from ->disassoc if called within that callback, + * %NULL otherwise * * This function is called whenever disassociation has been processed in * station mode. This includes both received disassociation frames and locally * generated ones. This function may sleep. */ -void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len); +void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, + void *cookie); /** * cfg80211_michael_mic_failure - notification of Michael MIC failure (TKIP) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 7cfc14e4ca07..36f8f245fa4c 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1182,15 +1182,19 @@ 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) + struct cfg80211_deauth_request *req, + void *cookie) { - return ieee80211_mgd_deauth(IEEE80211_DEV_TO_SUB_IF(dev), req); + return ieee80211_mgd_deauth(IEEE80211_DEV_TO_SUB_IF(dev), + req, cookie); } static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_disassoc_request *req) + struct cfg80211_disassoc_request *req, + void *cookie) { - return ieee80211_mgd_disassoc(IEEE80211_DEV_TO_SUB_IF(dev), req); + return ieee80211_mgd_disassoc(IEEE80211_DEV_TO_SUB_IF(dev), + req, cookie); } static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 2e92bbd9b2d0..327aabc07abe 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -918,9 +918,11 @@ 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); + struct cfg80211_deauth_request *req, + void *cookie); int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, - struct cfg80211_disassoc_request *req); + struct cfg80211_disassoc_request *req, + void *cookie); ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); void ieee80211_send_pspoll(struct ieee80211_local *local, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 15dbb57ab55e..c9db9646025b 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -386,7 +386,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, - const u8 *bssid, u16 stype, u16 reason) + const u8 *bssid, u16 stype, u16 reason, + void *cookie) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -412,9 +413,9 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, mgmt->u.deauth.reason_code = cpu_to_le16(reason); if (stype == IEEE80211_STYPE_DEAUTH) - cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, skb->len); + cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len, cookie); else - cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, skb->len); + cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len, cookie); ieee80211_tx_skb(sdata, skb, ifmgd->flags & IEEE80211_STA_MFP_ENABLED); } @@ -1837,10 +1838,12 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, /* no action */ break; case RX_MGMT_CFG80211_DEAUTH: - cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len); + cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len, + NULL); break; case RX_MGMT_CFG80211_DISASSOC: - cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len); + cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len, + NULL); break; default: WARN(1, "unexpected: %d", rma); @@ -2273,7 +2276,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, } int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, - struct cfg80211_deauth_request *req) + struct cfg80211_deauth_request *req, + void *cookie) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_mgd_work *wk; @@ -2305,13 +2309,15 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, mutex_unlock(&ifmgd->mtx); ieee80211_send_deauth_disassoc(sdata, bssid, - IEEE80211_STYPE_DEAUTH, req->reason_code); + IEEE80211_STYPE_DEAUTH, req->reason_code, + cookie); return 0; } int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, - struct cfg80211_disassoc_request *req) + struct cfg80211_disassoc_request *req, + void *cookie) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -2331,6 +2337,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, mutex_unlock(&ifmgd->mtx); ieee80211_send_deauth_disassoc(sdata, req->bss->bssid, - IEEE80211_STYPE_DISASSOC, req->reason_code); + IEEE80211_STYPE_DISASSOC, req->reason_code, + cookie); return 0; } diff --git a/net/wireless/core.c b/net/wireless/core.c index c6813beded06..9c73769440a8 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -257,6 +257,71 @@ static void cfg80211_rfkill_sync_work(struct work_struct *work) cfg80211_rfkill_set_block(drv, rfkill_blocked(drv->rfkill)); } +static void cfg80211_process_events(struct wireless_dev *wdev) +{ + struct cfg80211_event *ev; + unsigned long flags; + + spin_lock_irqsave(&wdev->event_lock, flags); + while (!list_empty(&wdev->event_list)) { + ev = list_first_entry(&wdev->event_list, + struct cfg80211_event, list); + list_del(&ev->list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + + wdev_lock(wdev); + switch (ev->type) { + case EVENT_CONNECT_RESULT: + __cfg80211_connect_result( + wdev->netdev, ev->cr.bssid, + ev->cr.req_ie, ev->cr.req_ie_len, + ev->cr.resp_ie, ev->cr.resp_ie_len, + ev->cr.status, + ev->cr.status == WLAN_STATUS_SUCCESS); + break; + case EVENT_ROAMED: + __cfg80211_roamed(wdev, ev->rm.bssid, + ev->rm.req_ie, ev->rm.req_ie_len, + ev->rm.resp_ie, ev->rm.resp_ie_len); + break; + case EVENT_DISCONNECTED: + __cfg80211_disconnected(wdev->netdev, + ev->dc.ie, ev->dc.ie_len, + ev->dc.reason, true); + break; + case EVENT_IBSS_JOINED: + __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid); + break; + } + wdev_unlock(wdev); + + kfree(ev); + + spin_lock_irqsave(&wdev->event_lock, flags); + } + spin_unlock_irqrestore(&wdev->event_lock, flags); +} + +static void cfg80211_event_work(struct work_struct *work) +{ + struct cfg80211_registered_device *rdev; + struct wireless_dev *wdev; + + rdev = container_of(work, struct cfg80211_registered_device, + event_work); + + rtnl_lock(); + cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->devlist_mtx); + + list_for_each_entry(wdev, &rdev->netdev_list, list) + cfg80211_process_events(wdev); + + mutex_unlock(&rdev->devlist_mtx); + cfg80211_unlock_rdev(rdev); + rtnl_unlock(); +} + /* exported functions */ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) @@ -299,6 +364,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) INIT_LIST_HEAD(&drv->netdev_list); spin_lock_init(&drv->bss_lock); INIT_LIST_HEAD(&drv->bss_list); + INIT_WORK(&drv->scan_done_wk, __cfg80211_scan_done); device_initialize(&drv->wiphy.dev); drv->wiphy.dev.class = &ieee80211_class; @@ -316,6 +382,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) INIT_WORK(&drv->rfkill_sync, cfg80211_rfkill_sync_work); INIT_WORK(&drv->conn_work, cfg80211_conn_work); + INIT_WORK(&drv->event_work, cfg80211_event_work); /* * Initialize wiphy parameters to IEEE 802.11 MIB default values. @@ -477,6 +544,9 @@ void wiphy_unregister(struct wiphy *wiphy) mutex_unlock(&drv->mtx); cancel_work_sync(&drv->conn_work); + cancel_work_sync(&drv->scan_done_wk); + kfree(drv->scan_req); + flush_work(&drv->event_work); cfg80211_debugfs_drv_del(drv); @@ -535,6 +605,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, switch (state) { case NETDEV_REGISTER: + mutex_init(&wdev->mtx); + INIT_LIST_HEAD(&wdev->event_list); + spin_lock_init(&wdev->event_lock); mutex_lock(&rdev->devlist_mtx); list_add(&wdev->list, &rdev->netdev_list); if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj, @@ -566,15 +639,17 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, cfg80211_leave_ibss(rdev, dev, true); break; case NL80211_IFTYPE_STATION: + wdev_lock(wdev); #ifdef CONFIG_WIRELESS_EXT kfree(wdev->wext.ie); wdev->wext.ie = NULL; wdev->wext.ie_len = 0; wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; #endif - cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, true); + __cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, true); cfg80211_mlme_down(rdev, dev); + wdev_unlock(wdev); break; default: break; @@ -582,20 +657,24 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, break; case NETDEV_UP: #ifdef CONFIG_WIRELESS_EXT + cfg80211_lock_rdev(rdev); + wdev_lock(wdev); switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: if (wdev->wext.ibss.ssid_len) - cfg80211_join_ibss(rdev, dev, - &wdev->wext.ibss); + __cfg80211_join_ibss(rdev, dev, + &wdev->wext.ibss); break; case NL80211_IFTYPE_STATION: if (wdev->wext.connect.ssid_len) - cfg80211_connect(rdev, dev, - &wdev->wext.connect); + __cfg80211_connect(rdev, dev, + &wdev->wext.connect); break; default: break; } + wdev_unlock(wdev); + cfg80211_unlock_rdev(rdev); #endif break; case NETDEV_UNREGISTER: @@ -605,6 +684,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, list_del_init(&wdev->list); } mutex_unlock(&rdev->devlist_mtx); + mutex_destroy(&wdev->mtx); break; case NETDEV_PRE_UP: if (rfkill_blocked(rdev->rfkill)) diff --git a/net/wireless/core.h b/net/wireless/core.h index 92da612b3f9e..5ccd642e183b 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -57,12 +57,14 @@ struct cfg80211_registered_device { u32 bss_generation; struct cfg80211_scan_request *scan_req; /* protected by RTNL */ unsigned long suspend_at; + struct work_struct scan_done_wk; #ifdef CONFIG_NL80211_TESTMODE struct genl_info *testmode_info; #endif struct work_struct conn_work; + struct work_struct event_work; #ifdef CONFIG_CFG80211_DEBUGFS /* Debugfs entries */ @@ -170,12 +172,73 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx); extern struct cfg80211_registered_device * cfg80211_get_dev_from_ifindex(int ifindex); +static inline void cfg80211_lock_rdev(struct cfg80211_registered_device *drv) +{ + mutex_lock(&drv->mtx); +} + static inline void cfg80211_unlock_rdev(struct cfg80211_registered_device *drv) { BUG_ON(IS_ERR(drv) || !drv); mutex_unlock(&drv->mtx); } +static inline void wdev_lock(struct wireless_dev *wdev) + __acquires(wdev) +{ + mutex_lock(&wdev->mtx); + __acquire(wdev->mtx); +} + +static inline void wdev_unlock(struct wireless_dev *wdev) + __releases(wdev) +{ + __release(wdev->mtx); + mutex_unlock(&wdev->mtx); +} + +#define ASSERT_RDEV_LOCK(rdev) WARN_ON(!mutex_is_locked(&(rdev)->mtx)); +#define ASSERT_WDEV_LOCK(wdev) WARN_ON(!mutex_is_locked(&(wdev)->mtx)); + +enum cfg80211_event_type { + EVENT_CONNECT_RESULT, + EVENT_ROAMED, + EVENT_DISCONNECTED, + EVENT_IBSS_JOINED, +}; + +struct cfg80211_event { + struct list_head list; + enum cfg80211_event_type type; + + union { + struct { + u8 bssid[ETH_ALEN]; + const u8 *req_ie; + const u8 *resp_ie; + size_t req_ie_len; + size_t resp_ie_len; + u16 status; + } cr; + struct { + u8 bssid[ETH_ALEN]; + const u8 *req_ie; + const u8 *resp_ie; + size_t req_ie_len; + size_t resp_ie_len; + } rm; + struct { + const u8 *ie; + size_t ie_len; + u16 reason; + } dc; + struct { + u8 bssid[ETH_ALEN]; + } ij; + }; +}; + + /* free object */ extern void cfg80211_dev_free(struct cfg80211_registered_device *drv); @@ -191,25 +254,46 @@ void cfg80211_bss_age(struct cfg80211_registered_device *dev, unsigned long age_secs); /* IBSS */ +int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_ibss_params *params); int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_ibss_params *params); void cfg80211_clear_ibss(struct net_device *dev, bool nowext); int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext); +void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid); /* MLME */ +int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, + const u8 *bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len); int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len); +int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + const u8 *bssid, const u8 *prev_bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, bool use_mfp, + struct cfg80211_crypto_settings *crypt); int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, const u8 *bssid, const u8 *prev_bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, bool use_mfp, struct cfg80211_crypto_settings *crypt); +int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason); int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *bssid, const u8 *ie, int ie_len, u16 reason); @@ -218,24 +302,38 @@ int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, const u8 *ie, int ie_len, u16 reason); void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, struct net_device *dev); +void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, bool wextev); /* SME */ +int __cfg80211_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *connect); int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect); +int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, u16 reason, + bool wextev); int cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason, bool wextev); +void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len); void cfg80211_conn_work(struct work_struct *work); /* internal helpers */ int cfg80211_validate_key_settings(struct key_params *params, int key_idx, const u8 *mac_addr); -void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, +void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, size_t ie_len, u16 reason, bool from_ap); void cfg80211_sme_scan_done(struct net_device *dev); void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); void cfg80211_sme_disassoc(struct net_device *dev, int idx); +void __cfg80211_scan_done(struct work_struct *wk); #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index a5330c5a5477..99ef9364b7e8 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -10,7 +10,7 @@ #include "nl80211.h" -void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) +void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_bss *bss; @@ -39,22 +39,45 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) cfg80211_hold_bss(bss_from_pub(bss)); wdev->current_bss = bss_from_pub(bss); - nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, gfp); + nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, + GFP_KERNEL); #ifdef CONFIG_WIRELESS_EXT memset(&wrqu, 0, sizeof(wrqu)); memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); #endif } + +void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_event *ev; + unsigned long flags; + + ev = kzalloc(sizeof(*ev), gfp); + if (!ev) + return; + + ev->type = EVENT_IBSS_JOINED; + memcpy(ev->cr.bssid, bssid, ETH_ALEN); + + spin_lock_irqsave(&wdev->event_lock, flags); + list_add_tail(&ev->list, &wdev->event_list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + schedule_work(&rdev->event_work); +} EXPORT_SYMBOL(cfg80211_ibss_joined); -int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_ibss_params *params) +int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_ibss_params *params) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; + ASSERT_WDEV_LOCK(wdev); + if (wdev->ssid_len) return -EALREADY; @@ -72,10 +95,26 @@ int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, return 0; } -void cfg80211_clear_ibss(struct net_device *dev, bool nowext) +int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_ibss_params *params) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_join_ibss(rdev, dev, params); + wdev_unlock(wdev); + + return err; +} + +static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) { struct wireless_dev *wdev = dev->ieee80211_ptr; + ASSERT_WDEV_LOCK(wdev); + if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(&wdev->current_bss->pub); @@ -89,12 +128,23 @@ void cfg80211_clear_ibss(struct net_device *dev, bool nowext) #endif } -int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool nowext) +void cfg80211_clear_ibss(struct net_device *dev, bool nowext) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + wdev_lock(wdev); + __cfg80211_clear_ibss(dev, nowext); + wdev_unlock(wdev); +} + +static int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, bool nowext) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; + ASSERT_WDEV_LOCK(wdev); + if (!wdev->ssid_len) return -ENOLINK; @@ -103,11 +153,24 @@ int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, if (err) return err; - cfg80211_clear_ibss(dev, nowext); + __cfg80211_clear_ibss(dev, nowext); return 0; } +int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, bool nowext) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_leave_ibss(rdev, dev, nowext); + wdev_unlock(wdev); + + return err; +} + #ifdef CONFIG_WIRELESS_EXT static int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) @@ -184,12 +247,15 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, if (wdev->wext.ibss.channel == chan) return 0; - if (wdev->ssid_len) { - err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), - dev, true); - if (err) - return err; - } + wdev_lock(wdev); + err = 0; + if (wdev->ssid_len) + err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), + dev, true); + wdev_unlock(wdev); + + if (err) + return err; if (chan) { wdev->wext.ibss.channel = chan; @@ -215,10 +281,12 @@ int cfg80211_ibss_wext_giwfreq(struct net_device *dev, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) return -EINVAL; + wdev_lock(wdev); if (wdev->current_bss) chan = wdev->current_bss->pub.channel; else if (wdev->wext.ibss.channel) chan = wdev->wext.ibss.channel; + wdev_unlock(wdev); if (chan) { freq->m = chan->center_freq; @@ -247,12 +315,15 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev, if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss) return -EOPNOTSUPP; - if (wdev->ssid_len) { - err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), - dev, true); - if (err) - return err; - } + wdev_lock(wdev); + err = 0; + if (wdev->ssid_len) + err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), + dev, true); + wdev_unlock(wdev); + + if (err) + return err; /* iwconfig uses nul termination in SSID.. */ if (len > 0 && ssid[len - 1] == '\0') @@ -279,6 +350,7 @@ int cfg80211_ibss_wext_giwessid(struct net_device *dev, data->flags = 0; + wdev_lock(wdev); if (wdev->ssid_len) { data->flags = 1; data->length = wdev->ssid_len; @@ -288,6 +360,7 @@ int cfg80211_ibss_wext_giwessid(struct net_device *dev, data->length = wdev->wext.ibss.ssid_len; memcpy(ssid, wdev->wext.ibss.ssid, data->length); } + wdev_unlock(wdev); return 0; } @@ -325,12 +398,15 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev, compare_ether_addr(bssid, wdev->wext.ibss.bssid) == 0) return 0; - if (wdev->ssid_len) { - err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), - dev, true); - if (err) - return err; - } + wdev_lock(wdev); + err = 0; + if (wdev->ssid_len) + err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), + dev, true); + wdev_unlock(wdev); + + if (err) + return err; if (bssid) { memcpy(wdev->wext.bssid, bssid, ETH_ALEN); @@ -355,10 +431,13 @@ int cfg80211_ibss_wext_giwap(struct net_device *dev, ap_addr->sa_family = ARPHRD_ETHER; + wdev_lock(wdev); if (wdev->current_bss) memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); else memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN); + wdev_unlock(wdev); + return 0; } /* temporary symbol - mark GPL - in the future the handler won't be */ diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 960bf60e44e5..1b2ca1fea7a1 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -23,7 +23,7 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) u16 status = le16_to_cpu(mgmt->u.auth.status_code); bool done = false; - might_sleep(); + wdev_lock(wdev); for (i = 0; i < MAX_AUTH_BSSES; i++) { if (wdev->authtry_bsses[i] && @@ -45,6 +45,8 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL); cfg80211_sme_rx_auth(dev, buf, len); + + wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_rx_auth); @@ -59,14 +61,15 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); bool done; - might_sleep(); + wdev_lock(wdev); status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL); - cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, - status_code, GFP_KERNEL); + __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, + status_code, + status_code == WLAN_STATUS_SUCCESS); if (status_code == WLAN_STATUS_SUCCESS) { for (i = 0; wdev->current_bss && i < MAX_AUTH_BSSES; i++) { @@ -81,10 +84,13 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) WARN_ON(!done); } + + wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_rx_assoc); -void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len) +static void __cfg80211_send_deauth(struct net_device *dev, + const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; @@ -94,7 +100,7 @@ void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len) int i; bool done = false; - might_sleep(); + ASSERT_WDEV_LOCK(wdev); nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL); @@ -132,17 +138,35 @@ void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len) reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; - __cfg80211_disconnected(dev, GFP_KERNEL, NULL, 0, - reason_code, from_ap); + __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); } else if (wdev->sme_state == CFG80211_SME_CONNECTING) { - cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_KERNEL); + __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false); + } +} + + +void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, + void *cookie) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + BUG_ON(cookie && wdev != cookie); + + if (cookie) { + /* called within callback */ + __cfg80211_send_deauth(dev, buf, len); + } else { + wdev_lock(wdev); + __cfg80211_send_deauth(dev, buf, len); + wdev_unlock(wdev); } } EXPORT_SYMBOL(cfg80211_send_deauth); -void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len) +static void __cfg80211_send_disassoc(struct net_device *dev, + const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; @@ -154,12 +178,12 @@ void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len) bool from_ap; bool done = false; - might_sleep(); + wdev_lock(wdev); nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL); if (!wdev->sme_state == CFG80211_SME_CONNECTED) - return; + goto out; if (wdev->current_bss && memcmp(wdev->current_bss, bssid, ETH_ALEN) == 0) { @@ -180,8 +204,26 @@ void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len) reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; - __cfg80211_disconnected(dev, GFP_KERNEL, NULL, 0, - reason_code, from_ap); + __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); + out: + wdev_unlock(wdev); +} + +void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, + void *cookie) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + BUG_ON(cookie && wdev != cookie); + + if (cookie) { + /* called within callback */ + __cfg80211_send_disassoc(dev, buf, len); + } else { + wdev_lock(wdev); + __cfg80211_send_disassoc(dev, buf, len); + wdev_unlock(wdev); + } } EXPORT_SYMBOL(cfg80211_send_disassoc); @@ -193,13 +235,13 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) int i; bool done = false; - might_sleep(); + wdev_lock(wdev); nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL); if (wdev->sme_state == CFG80211_SME_CONNECTING) - cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_KERNEL); + __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false); for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { if (wdev->authtry_bsses[i] && @@ -214,6 +256,8 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) } WARN_ON(!done); + + wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_auth_timeout); @@ -225,13 +269,13 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) int i; bool done = false; - might_sleep(); + wdev_lock(wdev); nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL); if (wdev->sme_state == CFG80211_SME_CONNECTING) - cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_KERNEL); + __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false); for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { if (wdev->auth_bsses[i] && @@ -246,6 +290,8 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) } WARN_ON(!done); + + wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_assoc_timeout); @@ -276,17 +322,21 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, EXPORT_SYMBOL(cfg80211_michael_mic_failure); /* some MLME handling for userspace SME */ -int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, - struct net_device *dev, struct ieee80211_channel *chan, - enum nl80211_auth_type auth_type, const u8 *bssid, - const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len) +int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, + const u8 *bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_auth_request req; struct cfg80211_internal_bss *bss; int i, err, slot = -1, nfree = 0; + ASSERT_WDEV_LOCK(wdev); + if (wdev->current_bss && memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0) return -EALREADY; @@ -342,18 +392,37 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, return err; } -int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, struct ieee80211_channel *chan, - const u8 *bssid, const u8 *prev_bssid, - const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len, bool use_mfp, - struct cfg80211_crypto_settings *crypt) +int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, const u8 *bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len) +{ + int err; + + wdev_lock(dev->ieee80211_ptr); + err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, + ssid, ssid_len, ie, ie_len); + wdev_unlock(dev->ieee80211_ptr); + + return err; +} + +int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + const u8 *bssid, const u8 *prev_bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, bool use_mfp, + struct cfg80211_crypto_settings *crypt) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_assoc_request req; struct cfg80211_internal_bss *bss; int i, err, slot = -1; + ASSERT_WDEV_LOCK(wdev); + memset(&req, 0, sizeof(req)); if (wdev->current_bss) @@ -390,14 +459,35 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, return err; } -int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason) +int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + const u8 *bssid, const u8 *prev_bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, bool use_mfp, + struct cfg80211_crypto_settings *crypt) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, + ssid, ssid_len, ie, ie_len, use_mfp, crypt); + wdev_unlock(wdev); + + return err; +} + +int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_deauth_request req; int i; + ASSERT_WDEV_LOCK(wdev); + memset(&req, 0, sizeof(req)); req.reason_code = reason; req.ie = ie; @@ -421,16 +511,32 @@ int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, if (!req.bss) return -ENOTCONN; - return rdev->ops->deauth(&rdev->wiphy, dev, &req); + return rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); } -int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason) +int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason); + wdev_unlock(wdev); + + return err; +} + +static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_disassoc_request req; + ASSERT_WDEV_LOCK(wdev); + memset(&req, 0, sizeof(req)); req.reason_code = reason; req.ie = ie; @@ -440,7 +546,21 @@ int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, else return -ENOTCONN; - return rdev->ops->disassoc(&rdev->wiphy, dev, &req); + return rdev->ops->disassoc(&rdev->wiphy, dev, &req, wdev); +} + +int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason); + wdev_unlock(wdev); + + return err; } void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, @@ -450,6 +570,8 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, struct cfg80211_deauth_request req; int i; + ASSERT_WDEV_LOCK(wdev); + if (!rdev->ops->deauth) return; @@ -460,7 +582,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, if (wdev->current_bss) { req.bss = &wdev->current_bss->pub; - rdev->ops->deauth(&rdev->wiphy, dev, &req); + rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(&wdev->current_bss->pub); @@ -471,7 +593,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, for (i = 0; i < MAX_AUTH_BSSES; i++) { if (wdev->auth_bsses[i]) { req.bss = &wdev->auth_bsses[i]->pub; - rdev->ops->deauth(&rdev->wiphy, dev, &req); + rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); if (wdev->auth_bsses[i]) { cfg80211_unhold_bss(wdev->auth_bsses[i]); cfg80211_put_bss(&wdev->auth_bsses[i]->pub); @@ -480,7 +602,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, } if (wdev->authtry_bsses[i]) { req.bss = &wdev->authtry_bsses[i]->pub; - rdev->ops->deauth(&rdev->wiphy, dev, &req); + rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); if (wdev->authtry_bsses[i]) { cfg80211_unhold_bss(wdev->authtry_bsses[i]); cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 4976eac888a3..cf4ac786b20a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4029,6 +4029,8 @@ static int nl80211_add_scan_req(struct sk_buff *msg, struct nlattr *nest; int i; + ASSERT_RDEV_LOCK(rdev); + if (WARN_ON(!req)) return 0; @@ -4391,12 +4393,12 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, struct net_device *netdev, u16 reason, - u8 *ie, size_t ie_len, bool from_ap, gfp_t gfp) + const u8 *ie, size_t ie_len, bool from_ap) { struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); if (!msg) return; @@ -4420,7 +4422,7 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL); return; nla_put_failure: diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index cf3708b48c29..44cc2a76a1b0 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -42,7 +42,7 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp); void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, struct net_device *netdev, u16 reason, - u8 *ie, size_t ie_len, bool from_ap, gfp_t gfp); + const u8 *ie, size_t ie_len, bool from_ap); void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 1625faf1de57..4f552c3f29a3 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -17,13 +17,21 @@ #define IEEE80211_SCAN_RESULT_EXPIRE (10 * HZ) -void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) +void __cfg80211_scan_done(struct work_struct *wk) { + struct cfg80211_registered_device *rdev; + struct cfg80211_scan_request *request; struct net_device *dev; #ifdef CONFIG_WIRELESS_EXT union iwreq_data wrqu; #endif + rdev = container_of(wk, struct cfg80211_registered_device, + scan_done_wk); + + mutex_lock(&rdev->mtx); + request = rdev->scan_req; + dev = dev_get_by_index(&init_net, request->ifidx); if (!dev) goto out; @@ -35,7 +43,7 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) */ cfg80211_sme_scan_done(dev); - if (aborted) + if (request->aborted) nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev); else nl80211_send_scan_done(wiphy_to_dev(request->wiphy), dev); @@ -43,7 +51,7 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) wiphy_to_dev(request->wiphy)->scan_req = NULL; #ifdef CONFIG_WIRELESS_EXT - if (!aborted) { + if (!request->aborted) { memset(&wrqu, 0, sizeof(wrqu)); wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); @@ -53,8 +61,24 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) dev_put(dev); out: + cfg80211_unlock_rdev(rdev); kfree(request); } + +void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) +{ + struct net_device *dev = dev_get_by_index(&init_net, request->ifidx); + if (WARN_ON(!dev)) { + kfree(request); + return; + } + + WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); + + request->aborted = aborted; + schedule_work(&wiphy_to_dev(request->wiphy)->scan_done_wk); + dev_put(dev); +} EXPORT_SYMBOL(cfg80211_scan_done); static void bss_release(struct kref *ref) diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 066a19ef9d73..472e2412c781 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -38,6 +38,8 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) int n_channels, err; ASSERT_RTNL(); + ASSERT_RDEV_LOCK(drv); + ASSERT_WDEV_LOCK(wdev); if (drv->scan_req) return -EBUSY; @@ -106,6 +108,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) struct cfg80211_connect_params *params; int err; + ASSERT_WDEV_LOCK(wdev); + if (!wdev->conn) return 0; @@ -117,11 +121,11 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) case CFG80211_CONN_AUTHENTICATE_NEXT: BUG_ON(!drv->ops->auth); wdev->conn->state = CFG80211_CONN_AUTHENTICATING; - return cfg80211_mlme_auth(drv, wdev->netdev, - params->channel, params->auth_type, - params->bssid, - params->ssid, params->ssid_len, - NULL, 0); + return __cfg80211_mlme_auth(drv, wdev->netdev, + params->channel, params->auth_type, + params->bssid, + params->ssid, params->ssid_len, + NULL, 0); case CFG80211_CONN_ASSOCIATE_NEXT: BUG_ON(!drv->ops->assoc); wdev->conn->state = CFG80211_CONN_ASSOCIATING; @@ -131,14 +135,16 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) * that some APs don't like that -- so we'd need to retry * the association. */ - err = cfg80211_mlme_assoc(drv, wdev->netdev, - params->channel, params->bssid, NULL, - params->ssid, params->ssid_len, - params->ie, params->ie_len, - false, ¶ms->crypto); + err = __cfg80211_mlme_assoc(drv, wdev->netdev, + params->channel, params->bssid, + NULL, + params->ssid, params->ssid_len, + params->ie, params->ie_len, + false, ¶ms->crypto); if (err) - cfg80211_mlme_deauth(drv, wdev->netdev, params->bssid, - NULL, 0, WLAN_REASON_DEAUTH_LEAVING); + __cfg80211_mlme_deauth(drv, wdev->netdev, params->bssid, + NULL, 0, + WLAN_REASON_DEAUTH_LEAVING); return err; default: return 0; @@ -152,22 +158,31 @@ void cfg80211_conn_work(struct work_struct *work) struct wireless_dev *wdev; rtnl_lock(); + cfg80211_lock_rdev(drv); mutex_lock(&drv->devlist_mtx); list_for_each_entry(wdev, &drv->netdev_list, list) { - if (!netif_running(wdev->netdev)) + wdev_lock(wdev); + if (!netif_running(wdev->netdev)) { + wdev_unlock(wdev); continue; - if (wdev->sme_state != CFG80211_SME_CONNECTING) + } + if (wdev->sme_state != CFG80211_SME_CONNECTING) { + wdev_unlock(wdev); continue; + } if (cfg80211_conn_do_work(wdev)) - cfg80211_connect_result(wdev->netdev, - wdev->conn->params.bssid, - NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_ATOMIC); + __cfg80211_connect_result( + wdev->netdev, + wdev->conn->params.bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false); + wdev_unlock(wdev); } mutex_unlock(&drv->devlist_mtx); + cfg80211_unlock_rdev(drv); rtnl_unlock(); } @@ -177,6 +192,8 @@ static bool cfg80211_get_conn_bss(struct wireless_dev *wdev) struct cfg80211_bss *bss; u16 capa = WLAN_CAPABILITY_ESS; + ASSERT_WDEV_LOCK(wdev); + if (wdev->conn->params.privacy) capa |= WLAN_CAPABILITY_PRIVACY; @@ -198,11 +215,13 @@ static bool cfg80211_get_conn_bss(struct wireless_dev *wdev) return true; } -void cfg80211_sme_scan_done(struct net_device *dev) +static void __cfg80211_sme_scan_done(struct net_device *dev) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); + ASSERT_WDEV_LOCK(wdev); + if (wdev->sme_state != CFG80211_SME_CONNECTING) return; @@ -218,15 +237,26 @@ void cfg80211_sme_scan_done(struct net_device *dev) if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) schedule_work(&drv->conn_work); else - cfg80211_connect_result(dev, wdev->conn->params.bssid, - NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_ATOMIC); - return; + __cfg80211_connect_result( + wdev->netdev, + wdev->conn->params.bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false); } } -void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len) +void cfg80211_sme_scan_done(struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + wdev_lock(wdev); + __cfg80211_sme_scan_done(dev); + wdev_unlock(wdev); +} + +void cfg80211_sme_rx_auth(struct net_device *dev, + const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; @@ -234,6 +264,8 @@ void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len) struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); + ASSERT_WDEV_LOCK(wdev); + /* should only RX auth frames when connecting */ if (wdev->sme_state != CFG80211_SME_CONNECTING) return; @@ -273,10 +305,10 @@ void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len) } } -static void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - u16 status, bool wextev, gfp_t gfp) +void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, bool wextev) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_bss *bss; @@ -284,18 +316,20 @@ static void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, union iwreq_data wrqu; #endif + ASSERT_WDEV_LOCK(wdev); + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; if (wdev->sme_state == CFG80211_SME_CONNECTED) nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, bssid, req_ie, req_ie_len, - resp_ie, resp_ie_len, gfp); + resp_ie, resp_ie_len, GFP_KERNEL); else nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, - status, gfp); + status, GFP_KERNEL); #ifdef CONFIG_WIRELESS_EXT if (wextev) { @@ -362,21 +396,43 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, const u8 *resp_ie, size_t resp_ie_len, u16 status, gfp_t gfp) { - bool wextev = status == WLAN_STATUS_SUCCESS; - __cfg80211_connect_result(dev, bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, status, wextev, gfp); + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_event *ev; + unsigned long flags; + + ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); + if (!ev) + return; + + ev->type = EVENT_CONNECT_RESULT; + memcpy(ev->cr.bssid, bssid, ETH_ALEN); + ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev); + ev->cr.req_ie_len = req_ie_len; + memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len); + ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; + ev->cr.resp_ie_len = resp_ie_len; + memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len); + ev->cr.status = status; + + spin_lock_irqsave(&wdev->event_lock, flags); + list_add_tail(&ev->list, &wdev->event_list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + schedule_work(&rdev->event_work); } EXPORT_SYMBOL(cfg80211_connect_result); -void cfg80211_roamed(struct net_device *dev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) +void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len) { - struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_bss *bss; #ifdef CONFIG_WIRELESS_EXT union iwreq_data wrqu; #endif + ASSERT_WDEV_LOCK(wdev); + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; @@ -402,31 +458,62 @@ void cfg80211_roamed(struct net_device *dev, const u8 *bssid, cfg80211_hold_bss(bss_from_pub(bss)); wdev->current_bss = bss_from_pub(bss); - nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, bssid, - req_ie, req_ie_len, resp_ie, resp_ie_len, gfp); + nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bssid, + req_ie, req_ie_len, resp_ie, resp_ie_len, + GFP_KERNEL); #ifdef CONFIG_WIRELESS_EXT if (req_ie) { memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = req_ie_len; - wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); + wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, + &wrqu, req_ie); } if (resp_ie) { memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = resp_ie_len; - wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); + wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, + &wrqu, resp_ie); } memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); - wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); + wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); #endif } + +void cfg80211_roamed(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_event *ev; + unsigned long flags; + + ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); + if (!ev) + return; + + ev->type = EVENT_ROAMED; + memcpy(ev->rm.bssid, bssid, ETH_ALEN); + ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev); + ev->rm.req_ie_len = req_ie_len; + memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len); + ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; + ev->rm.resp_ie_len = resp_ie_len; + memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len); + + spin_lock_irqsave(&wdev->event_lock, flags); + list_add_tail(&ev->list, &wdev->event_list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + schedule_work(&rdev->event_work); +} EXPORT_SYMBOL(cfg80211_roamed); -void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, +void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, size_t ie_len, u16 reason, bool from_ap) { struct wireless_dev *wdev = dev->ieee80211_ptr; @@ -434,6 +521,8 @@ void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, union iwreq_data wrqu; #endif + ASSERT_WDEV_LOCK(wdev); + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; @@ -456,7 +545,7 @@ void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, } nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev, - reason, ie, ie_len, from_ap, gfp); + reason, ie, ie_len, from_ap); #ifdef CONFIG_WIRELESS_EXT memset(&wrqu, 0, sizeof(wrqu)); @@ -468,16 +557,36 @@ void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, void cfg80211_disconnected(struct net_device *dev, u16 reason, u8 *ie, size_t ie_len, gfp_t gfp) { - __cfg80211_disconnected(dev, gfp, ie, ie_len, reason, true); + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_event *ev; + unsigned long flags; + + ev = kzalloc(sizeof(*ev) + ie_len, gfp); + if (!ev) + return; + + ev->type = EVENT_DISCONNECTED; + ev->dc.ie = ((u8 *)ev) + sizeof(*ev); + ev->dc.ie_len = ie_len; + memcpy((void *)ev->dc.ie, ie, ie_len); + ev->dc.reason = reason; + + spin_lock_irqsave(&wdev->event_lock, flags); + list_add_tail(&ev->list, &wdev->event_list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + schedule_work(&rdev->event_work); } EXPORT_SYMBOL(cfg80211_disconnected); -int cfg80211_connect(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_connect_params *connect) +int __cfg80211_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *connect) { - int err; struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + ASSERT_WDEV_LOCK(wdev); if (wdev->sme_state != CFG80211_SME_IDLE) return -EALREADY; @@ -572,12 +681,27 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, } } -int cfg80211_disconnect(struct cfg80211_registered_device *rdev, - struct net_device *dev, u16 reason, bool wextev) +int cfg80211_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *connect) +{ + int err; + + wdev_lock(dev->ieee80211_ptr); + err = __cfg80211_connect(rdev, dev, connect); + wdev_unlock(dev->ieee80211_ptr); + + return err; +} + +int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, u16 reason, bool wextev) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; + ASSERT_WDEV_LOCK(wdev); + if (wdev->sme_state == CFG80211_SME_IDLE) return -EINVAL; @@ -601,8 +725,9 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, } /* wdev->conn->params.bssid must be set if > SCANNING */ - err = cfg80211_mlme_deauth(rdev, dev, wdev->conn->params.bssid, - NULL, 0, reason); + err = __cfg80211_mlme_deauth(rdev, dev, + wdev->conn->params.bssid, + NULL, 0, reason); if (err) return err; } else { @@ -612,21 +737,36 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, } if (wdev->sme_state == CFG80211_SME_CONNECTED) - __cfg80211_disconnected(dev, GFP_KERNEL, NULL, 0, 0, false); + __cfg80211_disconnected(dev, NULL, 0, 0, false); else if (wdev->sme_state == CFG80211_SME_CONNECTING) __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, - wextev, GFP_KERNEL); + wextev); return 0; } +int cfg80211_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + u16 reason, bool wextev) +{ + int err; + + wdev_lock(dev->ieee80211_ptr); + err = __cfg80211_disconnect(rdev, dev, reason, wextev); + wdev_unlock(dev->ieee80211_ptr); + + return err; +} + void cfg80211_sme_disassoc(struct net_device *dev, int idx) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); u8 bssid[ETH_ALEN]; + ASSERT_WDEV_LOCK(wdev); + if (!wdev->conn) return; diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index fe1987acb891..6f75aaa7f795 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -15,6 +15,9 @@ static int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, { int err; + ASSERT_RDEV_LOCK(rdev); + ASSERT_WDEV_LOCK(wdev); + if (!netif_running(wdev->netdev)) return 0; @@ -24,8 +27,8 @@ static int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, err = 0; if (wdev->wext.connect.ssid_len != 0) - err = cfg80211_connect(rdev, wdev->netdev, - &wdev->wext.connect); + err = __cfg80211_connect(rdev, wdev->netdev, + &wdev->wext.connect); return err; } @@ -50,33 +53,43 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, if (chan && (chan->flags & IEEE80211_CHAN_DISABLED)) return -EINVAL; - if (wdev->wext.connect.channel == chan) - return 0; + cfg80211_lock_rdev(rdev); + wdev_lock(wdev); + + if (wdev->wext.connect.channel == chan) { + err = 0; + goto out; + } if (wdev->sme_state != CFG80211_SME_IDLE) { bool event = true; /* if SSID set, we'll try right again, avoid event */ if (wdev->wext.connect.ssid_len) event = false; - err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), - dev, WLAN_REASON_DEAUTH_LEAVING, - event); + err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), + dev, WLAN_REASON_DEAUTH_LEAVING, + event); if (err) - return err; + goto out; } + wdev->wext.connect.channel = chan; /* SSID is not set, we just want to switch channel */ if (wdev->wext.connect.ssid_len && chan) { - if (!rdev->ops->set_channel) - return -EOPNOTSUPP; - - return rdev->ops->set_channel(wdev->wiphy, chan, - NL80211_CHAN_NO_HT); + err = -EOPNOTSUPP; + if (rdev->ops->set_channel) + err = rdev->ops->set_channel(wdev->wiphy, chan, + NL80211_CHAN_NO_HT); + goto out; } - return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + out: + wdev_unlock(wdev); + cfg80211_unlock_rdev(rdev); + return err; } /* temporary symbol - mark GPL - in the future the handler won't be */ EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwfreq); @@ -92,10 +105,12 @@ int cfg80211_mgd_wext_giwfreq(struct net_device *dev, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return -EINVAL; + wdev_lock(wdev); if (wdev->current_bss) chan = wdev->current_bss->pub.channel; else if (wdev->wext.connect.channel) chan = wdev->wext.connect.channel; + wdev_unlock(wdev); if (chan) { freq->m = chan->center_freq; @@ -128,21 +143,26 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, if (len > 0 && ssid[len - 1] == '\0') len--; + cfg80211_lock_rdev(wiphy_to_dev(wdev->wiphy)); + wdev_lock(wdev); + + err = 0; + if (wdev->wext.connect.ssid && len && len == wdev->wext.connect.ssid_len && memcmp(wdev->wext.connect.ssid, ssid, len)) - return 0; + goto out; if (wdev->sme_state != CFG80211_SME_IDLE) { bool event = true; /* if SSID set now, we'll try to connect, avoid event */ if (len) event = false; - err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), - dev, WLAN_REASON_DEAUTH_LEAVING, - event); + err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), + dev, WLAN_REASON_DEAUTH_LEAVING, + event); if (err) - return err; + goto out; } wdev->wext.connect.ssid = wdev->wext.ssid; @@ -151,7 +171,11 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, wdev->wext.connect.crypto.control_port = false; - return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + out: + wdev_unlock(wdev); + cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy)); + return err; } /* temporary symbol - mark GPL - in the future the handler won't be */ EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwessid); @@ -168,6 +192,7 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev, data->flags = 0; + wdev_lock(wdev); if (wdev->ssid_len) { data->flags = 1; data->length = wdev->ssid_len; @@ -178,6 +203,7 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev, memcpy(ssid, wdev->wext.connect.ssid, data->length); } else data->flags = 0; + wdev_unlock(wdev); return 0; } @@ -203,21 +229,25 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) bssid = NULL; + cfg80211_lock_rdev(wiphy_to_dev(wdev->wiphy)); + wdev_lock(wdev); + + err = 0; /* both automatic */ if (!bssid && !wdev->wext.connect.bssid) - return 0; + goto out; /* fixed already - and no change */ if (wdev->wext.connect.bssid && bssid && compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0) - return 0; + goto out; if (wdev->sme_state != CFG80211_SME_IDLE) { - err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), - dev, WLAN_REASON_DEAUTH_LEAVING, - false); + err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), + dev, WLAN_REASON_DEAUTH_LEAVING, + false); if (err) - return err; + goto out; } if (bssid) { @@ -226,7 +256,11 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, } else wdev->wext.connect.bssid = NULL; - return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + out: + wdev_unlock(wdev); + cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy)); + return err; } /* temporary symbol - mark GPL - in the future the handler won't be */ EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwap); @@ -243,12 +277,14 @@ int cfg80211_mgd_wext_giwap(struct net_device *dev, ap_addr->sa_family = ARPHRD_ETHER; + wdev_lock(wdev); if (wdev->current_bss) memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); else if (wdev->wext.connect.bssid) memcpy(ap_addr->sa_data, wdev->wext.connect.bssid, ETH_ALEN); else memset(ap_addr->sa_data, 0, ETH_ALEN); + wdev_unlock(wdev); return 0; } @@ -270,15 +306,20 @@ int cfg80211_wext_siwgenie(struct net_device *dev, if (!ie_len) ie = NULL; + wdev_lock(wdev); + /* no change */ + err = 0; if (wdev->wext.ie_len == ie_len && memcmp(wdev->wext.ie, ie, ie_len) == 0) - return 0; + goto out; if (ie_len) { ie = kmemdup(extra, ie_len, GFP_KERNEL); - if (!ie) - return -ENOMEM; + if (!ie) { + err = -ENOMEM; + goto out; + } } else ie = NULL; @@ -287,14 +328,17 @@ int cfg80211_wext_siwgenie(struct net_device *dev, wdev->wext.ie_len = ie_len; if (wdev->sme_state != CFG80211_SME_IDLE) { - err = cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, false); + err = __cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, false); if (err) - return err; + goto out; } /* userspace better not think we'll reconnect */ - return 0; + err = 0; + out: + wdev_unlock(wdev); + return err; } EXPORT_SYMBOL_GPL(cfg80211_wext_siwgenie); @@ -305,6 +349,7 @@ int cfg80211_wext_siwmlme(struct net_device *dev, struct wireless_dev *wdev = dev->ieee80211_ptr; struct iw_mlme *mlme = (struct iw_mlme *)extra; struct cfg80211_registered_device *rdev; + int err; if (!wdev) return -EOPNOTSUPP; @@ -317,13 +362,19 @@ int cfg80211_wext_siwmlme(struct net_device *dev, if (mlme->addr.sa_family != ARPHRD_ETHER) return -EINVAL; + wdev_lock(wdev); switch (mlme->cmd) { case IW_MLME_DEAUTH: case IW_MLME_DISASSOC: - return cfg80211_disconnect(rdev, dev, mlme->reason_code, - true); + err = __cfg80211_disconnect(rdev, dev, mlme->reason_code, + true); + break; default: - return -EOPNOTSUPP; + err = -EOPNOTSUPP; + break; } + wdev_unlock(wdev); + + return err; } EXPORT_SYMBOL_GPL(cfg80211_wext_siwmlme); -- cgit v1.2.3 From 79c97e97aed7f760d2826c7daf2d42d8eefe9838 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 03:56:12 +0200 Subject: cfg80211: clean up naming once and for all We've named the registered devices 'drv' sometimes, thinking of "driver", which is not what it is, it's the internal representation of a wiphy, i.e. a device. Let's clean up the naming once and and use 'rdev' aka 'registered device' everywhere. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 228 ++++++++++++++++----------------- net/wireless/core.h | 26 ++-- net/wireless/debugfs.c | 14 +- net/wireless/debugfs.h | 8 +- net/wireless/nl80211.c | 340 ++++++++++++++++++++++++------------------------- net/wireless/reg.c | 48 +++---- net/wireless/sme.c | 48 +++---- 7 files changed, 356 insertions(+), 356 deletions(-) (limited to 'net/wireless/core.h') diff --git a/net/wireless/core.c b/net/wireless/core.c index 9c73769440a8..b5e2f6dfa333 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -30,10 +30,10 @@ MODULE_DESCRIPTION("wireless configuration support"); /* RCU might be appropriate here since we usually * only read the list, and that can happen quite * often because we need to do it for each command */ -LIST_HEAD(cfg80211_drv_list); +LIST_HEAD(cfg80211_rdev_list); /* - * This is used to protect the cfg80211_drv_list, cfg80211_regdomain, + * This is used to protect the cfg80211_rdev_list, cfg80211_regdomain, * country_ie_regdomain, the reg_beacon_list and the the last regulatory * request receipt (last_request). */ @@ -43,18 +43,18 @@ DEFINE_MUTEX(cfg80211_mutex); static struct dentry *ieee80211_debugfs_dir; /* requires cfg80211_mutex to be held! */ -struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx) +struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) { - struct cfg80211_registered_device *result = NULL, *drv; + struct cfg80211_registered_device *result = NULL, *rdev; if (!wiphy_idx_valid(wiphy_idx)) return NULL; assert_cfg80211_lock(); - list_for_each_entry(drv, &cfg80211_drv_list, list) { - if (drv->wiphy_idx == wiphy_idx) { - result = drv; + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (rdev->wiphy_idx == wiphy_idx) { + result = rdev; break; } } @@ -64,32 +64,32 @@ struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx) int get_wiphy_idx(struct wiphy *wiphy) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; if (!wiphy) return WIPHY_IDX_STALE; - drv = wiphy_to_dev(wiphy); - return drv->wiphy_idx; + rdev = wiphy_to_dev(wiphy); + return rdev->wiphy_idx; } -/* requires cfg80211_drv_mutex to be held! */ +/* requires cfg80211_rdev_mutex to be held! */ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; if (!wiphy_idx_valid(wiphy_idx)) return NULL; assert_cfg80211_lock(); - drv = cfg80211_drv_by_wiphy_idx(wiphy_idx); - if (!drv) + rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx); + if (!rdev) return NULL; - return &drv->wiphy; + return &rdev->wiphy; } /* requires cfg80211_mutex to be held! */ struct cfg80211_registered_device * -__cfg80211_drv_from_info(struct genl_info *info) +__cfg80211_rdev_from_info(struct genl_info *info) { int ifindex; struct cfg80211_registered_device *bywiphyidx = NULL, *byifidx = NULL; @@ -99,7 +99,7 @@ __cfg80211_drv_from_info(struct genl_info *info) assert_cfg80211_lock(); if (info->attrs[NL80211_ATTR_WIPHY]) { - bywiphyidx = cfg80211_drv_by_wiphy_idx( + bywiphyidx = cfg80211_rdev_by_wiphy_idx( nla_get_u32(info->attrs[NL80211_ATTR_WIPHY])); err = -ENODEV; } @@ -134,26 +134,26 @@ __cfg80211_drv_from_info(struct genl_info *info) struct cfg80211_registered_device * cfg80211_get_dev_from_info(struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; mutex_lock(&cfg80211_mutex); - drv = __cfg80211_drv_from_info(info); + rdev = __cfg80211_rdev_from_info(info); /* if it is not an error we grab the lock on * it to assure it won't be going away while * we operate on it */ - if (!IS_ERR(drv)) - mutex_lock(&drv->mtx); + if (!IS_ERR(rdev)) + mutex_lock(&rdev->mtx); mutex_unlock(&cfg80211_mutex); - return drv; + return rdev; } struct cfg80211_registered_device * cfg80211_get_dev_from_ifindex(int ifindex) { - struct cfg80211_registered_device *drv = ERR_PTR(-ENODEV); + struct cfg80211_registered_device *rdev = ERR_PTR(-ENODEV); struct net_device *dev; mutex_lock(&cfg80211_mutex); @@ -161,21 +161,21 @@ cfg80211_get_dev_from_ifindex(int ifindex) if (!dev) goto out; if (dev->ieee80211_ptr) { - drv = wiphy_to_dev(dev->ieee80211_ptr->wiphy); - mutex_lock(&drv->mtx); + rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy); + mutex_lock(&rdev->mtx); } else - drv = ERR_PTR(-ENODEV); + rdev = ERR_PTR(-ENODEV); dev_put(dev); out: mutex_unlock(&cfg80211_mutex); - return drv; + return rdev; } /* requires cfg80211_mutex to be held */ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, char *newname) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev2; int wiphy_idx, taken = -1, result, digits; assert_cfg80211_lock(); @@ -201,8 +201,8 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, return 0; /* Ensure another device does not already have this name. */ - list_for_each_entry(drv, &cfg80211_drv_list, list) - if (strcmp(newname, dev_name(&drv->wiphy.dev)) == 0) + list_for_each_entry(rdev2, &cfg80211_rdev_list, list) + if (strcmp(newname, dev_name(&rdev2->wiphy.dev)) == 0) return -EINVAL; result = device_rename(&rdev->wiphy.dev, newname); @@ -224,26 +224,26 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data) { - struct cfg80211_registered_device *drv = data; + struct cfg80211_registered_device *rdev = data; - drv->ops->rfkill_poll(&drv->wiphy); + rdev->ops->rfkill_poll(&rdev->wiphy); } static int cfg80211_rfkill_set_block(void *data, bool blocked) { - struct cfg80211_registered_device *drv = data; + struct cfg80211_registered_device *rdev = data; struct wireless_dev *wdev; if (!blocked) return 0; rtnl_lock(); - mutex_lock(&drv->devlist_mtx); + mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &drv->netdev_list, list) + list_for_each_entry(wdev, &rdev->netdev_list, list) dev_close(wdev->netdev); - mutex_unlock(&drv->devlist_mtx); + mutex_unlock(&rdev->devlist_mtx); rtnl_unlock(); return 0; @@ -251,10 +251,10 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked) static void cfg80211_rfkill_sync_work(struct work_struct *work) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; - drv = container_of(work, struct cfg80211_registered_device, rfkill_sync); - cfg80211_rfkill_set_block(drv, rfkill_blocked(drv->rfkill)); + rdev = container_of(work, struct cfg80211_registered_device, rfkill_sync); + cfg80211_rfkill_set_block(rdev, rfkill_blocked(rdev->rfkill)); } static void cfg80211_process_events(struct wireless_dev *wdev) @@ -328,79 +328,79 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) { static int wiphy_counter; - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int alloc_size; WARN_ON(!ops->add_key && ops->del_key); WARN_ON(ops->add_key && !ops->del_key); - alloc_size = sizeof(*drv) + sizeof_priv; + alloc_size = sizeof(*rdev) + sizeof_priv; - drv = kzalloc(alloc_size, GFP_KERNEL); - if (!drv) + rdev = kzalloc(alloc_size, GFP_KERNEL); + if (!rdev) return NULL; - drv->ops = ops; + rdev->ops = ops; mutex_lock(&cfg80211_mutex); - drv->wiphy_idx = wiphy_counter++; + rdev->wiphy_idx = wiphy_counter++; - if (unlikely(!wiphy_idx_valid(drv->wiphy_idx))) { + if (unlikely(!wiphy_idx_valid(rdev->wiphy_idx))) { wiphy_counter--; mutex_unlock(&cfg80211_mutex); /* ugh, wrapped! */ - kfree(drv); + kfree(rdev); return NULL; } mutex_unlock(&cfg80211_mutex); /* give it a proper name */ - dev_set_name(&drv->wiphy.dev, PHY_NAME "%d", drv->wiphy_idx); - - mutex_init(&drv->mtx); - mutex_init(&drv->devlist_mtx); - INIT_LIST_HEAD(&drv->netdev_list); - spin_lock_init(&drv->bss_lock); - INIT_LIST_HEAD(&drv->bss_list); - INIT_WORK(&drv->scan_done_wk, __cfg80211_scan_done); - - device_initialize(&drv->wiphy.dev); - drv->wiphy.dev.class = &ieee80211_class; - drv->wiphy.dev.platform_data = drv; - - drv->rfkill_ops.set_block = cfg80211_rfkill_set_block; - drv->rfkill = rfkill_alloc(dev_name(&drv->wiphy.dev), - &drv->wiphy.dev, RFKILL_TYPE_WLAN, - &drv->rfkill_ops, drv); - - if (!drv->rfkill) { - kfree(drv); + dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx); + + mutex_init(&rdev->mtx); + mutex_init(&rdev->devlist_mtx); + INIT_LIST_HEAD(&rdev->netdev_list); + spin_lock_init(&rdev->bss_lock); + INIT_LIST_HEAD(&rdev->bss_list); + INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); + + device_initialize(&rdev->wiphy.dev); + rdev->wiphy.dev.class = &ieee80211_class; + rdev->wiphy.dev.platform_data = rdev; + + rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block; + rdev->rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev), + &rdev->wiphy.dev, RFKILL_TYPE_WLAN, + &rdev->rfkill_ops, rdev); + + if (!rdev->rfkill) { + kfree(rdev); return NULL; } - INIT_WORK(&drv->rfkill_sync, cfg80211_rfkill_sync_work); - INIT_WORK(&drv->conn_work, cfg80211_conn_work); - INIT_WORK(&drv->event_work, cfg80211_event_work); + INIT_WORK(&rdev->rfkill_sync, cfg80211_rfkill_sync_work); + INIT_WORK(&rdev->conn_work, cfg80211_conn_work); + INIT_WORK(&rdev->event_work, cfg80211_event_work); /* * Initialize wiphy parameters to IEEE 802.11 MIB default values. * Fragmentation and RTS threshold are disabled by default with the * special -1 value. */ - drv->wiphy.retry_short = 7; - drv->wiphy.retry_long = 4; - drv->wiphy.frag_threshold = (u32) -1; - drv->wiphy.rts_threshold = (u32) -1; + rdev->wiphy.retry_short = 7; + rdev->wiphy.retry_long = 4; + rdev->wiphy.frag_threshold = (u32) -1; + rdev->wiphy.rts_threshold = (u32) -1; - return &drv->wiphy; + return &rdev->wiphy; } EXPORT_SYMBOL(wiphy_new); int wiphy_register(struct wiphy *wiphy) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); int res; enum ieee80211_band band; struct ieee80211_supported_band *sband; @@ -454,11 +454,11 @@ int wiphy_register(struct wiphy *wiphy) /* check and set up bitrates */ ieee80211_set_bitrate_flags(wiphy); - res = device_add(&drv->wiphy.dev); + res = device_add(&rdev->wiphy.dev); if (res) return res; - res = rfkill_register(drv->rfkill); + res = rfkill_register(rdev->rfkill); if (res) goto out_rm_dev; @@ -467,16 +467,16 @@ int wiphy_register(struct wiphy *wiphy) /* set up regulatory info */ wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE); - list_add(&drv->list, &cfg80211_drv_list); + list_add(&rdev->list, &cfg80211_rdev_list); mutex_unlock(&cfg80211_mutex); /* add to debugfs */ - drv->wiphy.debugfsdir = - debugfs_create_dir(wiphy_name(&drv->wiphy), + rdev->wiphy.debugfsdir = + debugfs_create_dir(wiphy_name(&rdev->wiphy), ieee80211_debugfs_dir); - if (IS_ERR(drv->wiphy.debugfsdir)) - drv->wiphy.debugfsdir = NULL; + if (IS_ERR(rdev->wiphy.debugfsdir)) + rdev->wiphy.debugfsdir = NULL; if (wiphy->custom_regulatory) { struct regulatory_request request; @@ -489,48 +489,48 @@ int wiphy_register(struct wiphy *wiphy) nl80211_send_reg_change_event(&request); } - cfg80211_debugfs_drv_add(drv); + cfg80211_debugfs_rdev_add(rdev); return 0; out_rm_dev: - device_del(&drv->wiphy.dev); + device_del(&rdev->wiphy.dev); return res; } EXPORT_SYMBOL(wiphy_register); void wiphy_rfkill_start_polling(struct wiphy *wiphy) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - if (!drv->ops->rfkill_poll) + if (!rdev->ops->rfkill_poll) return; - drv->rfkill_ops.poll = cfg80211_rfkill_poll; - rfkill_resume_polling(drv->rfkill); + rdev->rfkill_ops.poll = cfg80211_rfkill_poll; + rfkill_resume_polling(rdev->rfkill); } EXPORT_SYMBOL(wiphy_rfkill_start_polling); void wiphy_rfkill_stop_polling(struct wiphy *wiphy) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - rfkill_pause_polling(drv->rfkill); + rfkill_pause_polling(rdev->rfkill); } EXPORT_SYMBOL(wiphy_rfkill_stop_polling); void wiphy_unregister(struct wiphy *wiphy) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - rfkill_unregister(drv->rfkill); + rfkill_unregister(rdev->rfkill); /* protect the device list */ mutex_lock(&cfg80211_mutex); - BUG_ON(!list_empty(&drv->netdev_list)); + BUG_ON(!list_empty(&rdev->netdev_list)); /* - * Try to grab drv->mtx. If a command is still in progress, + * Try to grab rdev->mtx. If a command is still in progress, * hopefully the driver will refuse it since it's tearing * down the device already. We wait for this command to complete * before unlinking the item from the list. @@ -539,38 +539,38 @@ void wiphy_unregister(struct wiphy *wiphy) * get to lock contention here if userspace issues a command * that identified the hardware by wiphy index. */ - mutex_lock(&drv->mtx); + mutex_lock(&rdev->mtx); /* unlock again before freeing */ - mutex_unlock(&drv->mtx); + mutex_unlock(&rdev->mtx); - cancel_work_sync(&drv->conn_work); - cancel_work_sync(&drv->scan_done_wk); - kfree(drv->scan_req); - flush_work(&drv->event_work); + cancel_work_sync(&rdev->conn_work); + cancel_work_sync(&rdev->scan_done_wk); + kfree(rdev->scan_req); + flush_work(&rdev->event_work); - cfg80211_debugfs_drv_del(drv); + cfg80211_debugfs_rdev_del(rdev); /* If this device got a regulatory hint tell core its * free to listen now to a new shiny device regulatory hint */ reg_device_remove(wiphy); - list_del(&drv->list); - device_del(&drv->wiphy.dev); - debugfs_remove(drv->wiphy.debugfsdir); + list_del(&rdev->list); + device_del(&rdev->wiphy.dev); + debugfs_remove(rdev->wiphy.debugfsdir); mutex_unlock(&cfg80211_mutex); } EXPORT_SYMBOL(wiphy_unregister); -void cfg80211_dev_free(struct cfg80211_registered_device *drv) +void cfg80211_dev_free(struct cfg80211_registered_device *rdev) { struct cfg80211_internal_bss *scan, *tmp; - rfkill_destroy(drv->rfkill); - mutex_destroy(&drv->mtx); - mutex_destroy(&drv->devlist_mtx); - list_for_each_entry_safe(scan, tmp, &drv->bss_list, list) + rfkill_destroy(rdev->rfkill); + mutex_destroy(&rdev->mtx); + mutex_destroy(&rdev->devlist_mtx); + list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) cfg80211_put_bss(&scan->pub); - kfree(drv); + kfree(rdev); } void wiphy_free(struct wiphy *wiphy) @@ -581,10 +581,10 @@ EXPORT_SYMBOL(wiphy_free); void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - if (rfkill_set_hw_state(drv->rfkill, blocked)) - schedule_work(&drv->rfkill_sync); + if (rfkill_set_hw_state(rdev->rfkill, blocked)) + schedule_work(&rdev->rfkill_sync); } EXPORT_SYMBOL(wiphy_rfkill_set_hw_state); diff --git a/net/wireless/core.h b/net/wireless/core.h index 5ccd642e183b..e46cd6eb61d7 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -97,13 +97,13 @@ bool wiphy_idx_valid(int wiphy_idx) } extern struct mutex cfg80211_mutex; -extern struct list_head cfg80211_drv_list; +extern struct list_head cfg80211_rdev_list; #define assert_cfg80211_lock() WARN_ON(!mutex_is_locked(&cfg80211_mutex)) /* * You can use this to mark a wiphy_idx as not having an associated wiphy. - * It guarantees cfg80211_drv_by_wiphy_idx(wiphy_idx) will return NULL + * It guarantees cfg80211_rdev_by_wiphy_idx(wiphy_idx) will return NULL */ #define WIPHY_IDX_STALE -1 @@ -136,11 +136,11 @@ static inline void cfg80211_unhold_bss(struct cfg80211_internal_bss *bss) } -struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx); +struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx); int get_wiphy_idx(struct wiphy *wiphy); struct cfg80211_registered_device * -__cfg80211_drv_from_info(struct genl_info *info); +__cfg80211_rdev_from_info(struct genl_info *info); /* * This function returns a pointer to the driver @@ -153,7 +153,7 @@ __cfg80211_drv_from_info(struct genl_info *info); * * This is necessary because we need to lock the global * mutex to get an item off the list safely, and then - * we lock the drv mutex so it doesn't go away under us. + * we lock the rdev mutex so it doesn't go away under us. * * We don't want to keep cfg80211_mutex locked * for all the time in order to allow requests on @@ -165,22 +165,22 @@ __cfg80211_drv_from_info(struct genl_info *info); extern struct cfg80211_registered_device * cfg80211_get_dev_from_info(struct genl_info *info); -/* requires cfg80211_drv_mutex to be held! */ +/* requires cfg80211_rdev_mutex to be held! */ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx); /* identical to cfg80211_get_dev_from_info but only operate on ifindex */ extern struct cfg80211_registered_device * cfg80211_get_dev_from_ifindex(int ifindex); -static inline void cfg80211_lock_rdev(struct cfg80211_registered_device *drv) +static inline void cfg80211_lock_rdev(struct cfg80211_registered_device *rdev) { - mutex_lock(&drv->mtx); + mutex_lock(&rdev->mtx); } -static inline void cfg80211_unlock_rdev(struct cfg80211_registered_device *drv) +static inline void cfg80211_unlock_rdev(struct cfg80211_registered_device *rdev) { - BUG_ON(IS_ERR(drv) || !drv); - mutex_unlock(&drv->mtx); + BUG_ON(IS_ERR(rdev) || !rdev); + mutex_unlock(&rdev->mtx); } static inline void wdev_lock(struct wireless_dev *wdev) @@ -240,9 +240,9 @@ struct cfg80211_event { /* free object */ -extern void cfg80211_dev_free(struct cfg80211_registered_device *drv); +extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev); -extern int cfg80211_dev_rename(struct cfg80211_registered_device *drv, +extern int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, char *newname); void ieee80211_set_bitrate_flags(struct wiphy *wiphy); diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c index 679ddfcec1ee..13d93d84f902 100644 --- a/net/wireless/debugfs.c +++ b/net/wireless/debugfs.c @@ -104,15 +104,15 @@ static const struct file_operations ht40allow_map_ops = { }; #define DEBUGFS_ADD(name) \ - drv->debugfs.name = debugfs_create_file(#name, S_IRUGO, phyd, \ - &drv->wiphy, &name## _ops); + rdev->debugfs.name = debugfs_create_file(#name, S_IRUGO, phyd, \ + &rdev->wiphy, &name## _ops); #define DEBUGFS_DEL(name) \ - debugfs_remove(drv->debugfs.name); \ - drv->debugfs.name = NULL; + debugfs_remove(rdev->debugfs.name); \ + rdev->debugfs.name = NULL; -void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv) +void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) { - struct dentry *phyd = drv->wiphy.debugfsdir; + struct dentry *phyd = rdev->wiphy.debugfsdir; DEBUGFS_ADD(rts_threshold); DEBUGFS_ADD(fragmentation_threshold); @@ -121,7 +121,7 @@ void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv) DEBUGFS_ADD(ht40allow_map); } -void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv) +void cfg80211_debugfs_rdev_del(struct cfg80211_registered_device *rdev) { DEBUGFS_DEL(rts_threshold); DEBUGFS_DEL(fragmentation_threshold); diff --git a/net/wireless/debugfs.h b/net/wireless/debugfs.h index c226983ae66b..6419b6d6ce3e 100644 --- a/net/wireless/debugfs.h +++ b/net/wireless/debugfs.h @@ -2,13 +2,13 @@ #define __CFG80211_DEBUGFS_H #ifdef CONFIG_CFG80211_DEBUGFS -void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv); -void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv); +void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev); +void cfg80211_debugfs_rdev_del(struct cfg80211_registered_device *rdev); #else static inline -void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv) {} +void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) {} static inline -void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv) {} +void cfg80211_debugfs_rdev_del(struct cfg80211_registered_device *rdev) {} #endif #endif /* __CFG80211_DEBUGFS_H */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index cf4ac786b20a..9deb12f73c44 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -29,9 +29,9 @@ static struct genl_family nl80211_fam = { .maxattr = NL80211_ATTR_MAX, }; -/* internal helper: get drv and dev */ -static int get_drv_dev_by_info_ifindex(struct nlattr **attrs, - struct cfg80211_registered_device **drv, +/* internal helper: get rdev and dev */ +static int get_rdev_dev_by_info_ifindex(struct nlattr **attrs, + struct cfg80211_registered_device **rdev, struct net_device **dev) { int ifindex; @@ -44,10 +44,10 @@ static int get_drv_dev_by_info_ifindex(struct nlattr **attrs, if (!*dev) return -ENODEV; - *drv = cfg80211_get_dev_from_ifindex(ifindex); - if (IS_ERR(*drv)) { + *rdev = cfg80211_get_dev_from_ifindex(ifindex); + if (IS_ERR(*rdev)) { dev_put(*dev); - return PTR_ERR(*drv); + return PTR_ERR(*rdev); } return 0; @@ -378,7 +378,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) struct cfg80211_registered_device *dev; mutex_lock(&cfg80211_mutex); - list_for_each_entry(dev, &cfg80211_drv_list, list) { + list_for_each_entry(dev, &cfg80211_rdev_list, list) { if (++idx <= start) continue; if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid, @@ -460,7 +460,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) mutex_lock(&cfg80211_mutex); - rdev = __cfg80211_drv_from_info(info); + rdev = __cfg80211_rdev_from_info(info); if (IS_ERR(rdev)) { mutex_unlock(&cfg80211_mutex); result = PTR_ERR(rdev); @@ -683,7 +683,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * struct wireless_dev *wdev; mutex_lock(&cfg80211_mutex); - list_for_each_entry(dev, &cfg80211_drv_list, list) { + list_for_each_entry(dev, &cfg80211_rdev_list, list) { if (wp_idx < wp_start) { wp_idx++; continue; @@ -724,7 +724,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) struct net_device *netdev; int err; - err = get_drv_dev_by_info_ifindex(info->attrs, &dev, &netdev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &dev, &netdev); if (err) return err; @@ -780,7 +780,7 @@ static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct vif_params params; int err; enum nl80211_iftype otype, ntype; @@ -792,7 +792,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; @@ -808,8 +808,8 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) } } - if (!drv->ops->change_virtual_intf || - !(drv->wiphy.interface_modes & (1 << ntype))) { + if (!rdev->ops->change_virtual_intf || + !(rdev->wiphy.interface_modes & (1 << ntype))) { err = -EOPNOTSUPP; goto unlock; } @@ -839,7 +839,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) } if (change) - err = drv->ops->change_virtual_intf(&drv->wiphy, dev, + err = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, ntype, flags, ¶ms); else err = 0; @@ -853,7 +853,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) unlock: dev_put(dev); - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); unlock_rtnl: rtnl_unlock(); return err; @@ -861,7 +861,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct vif_params params; int err; enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; @@ -880,14 +880,14 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - drv = cfg80211_get_dev_from_info(info); - if (IS_ERR(drv)) { - err = PTR_ERR(drv); + rdev = cfg80211_get_dev_from_info(info); + if (IS_ERR(rdev)) { + err = PTR_ERR(rdev); goto unlock_rtnl; } - if (!drv->ops->add_virtual_intf || - !(drv->wiphy.interface_modes & (1 << type))) { + if (!rdev->ops->add_virtual_intf || + !(rdev->wiphy.interface_modes & (1 << type))) { err = -EOPNOTSUPP; goto unlock; } @@ -901,12 +901,12 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, &flags); - err = drv->ops->add_virtual_intf(&drv->wiphy, + err = rdev->ops->add_virtual_intf(&rdev->wiphy, nla_data(info->attrs[NL80211_ATTR_IFNAME]), type, err ? NULL : &flags, ¶ms); unlock: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); unlock_rtnl: rtnl_unlock(); return err; @@ -914,27 +914,27 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int ifindex, err; struct net_device *dev; rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; ifindex = dev->ifindex; dev_put(dev); - if (!drv->ops->del_virtual_intf) { + if (!rdev->ops->del_virtual_intf) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex); + err = rdev->ops->del_virtual_intf(&rdev->wiphy, ifindex); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); unlock_rtnl: rtnl_unlock(); return err; @@ -968,7 +968,7 @@ static void get_key_callback(void *c, struct key_params *params) static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 key_idx = 0; @@ -990,11 +990,11 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->get_key) { + if (!rdev->ops->get_key) { err = -EOPNOTSUPP; goto out; } @@ -1020,7 +1020,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) if (mac_addr) NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); - err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr, + err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, mac_addr, &cookie, get_key_callback); if (err) @@ -1037,7 +1037,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) err = -ENOBUFS; nlmsg_free(msg); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1047,7 +1047,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 key_idx; @@ -1072,24 +1072,24 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; if (info->attrs[NL80211_ATTR_KEY_DEFAULT]) - func = drv->ops->set_default_key; + func = rdev->ops->set_default_key; else - func = drv->ops->set_default_mgmt_key; + func = rdev->ops->set_default_mgmt_key; if (!func) { err = -EOPNOTSUPP; goto out; } - err = func(&drv->wiphy, dev, key_idx); + err = func(&rdev->wiphy, dev, key_idx); #ifdef CONFIG_WIRELESS_EXT if (!err) { - if (func == drv->ops->set_default_key) + if (func == rdev->ops->set_default_key) dev->ieee80211_ptr->wext.default_key = key_idx; else dev->ieee80211_ptr->wext.default_mgmt_key = key_idx; @@ -1097,7 +1097,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) #endif out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: @@ -1108,7 +1108,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err, i; struct net_device *dev; struct key_params params; @@ -1143,27 +1143,27 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - for (i = 0; i < drv->wiphy.n_cipher_suites; i++) - if (params.cipher == drv->wiphy.cipher_suites[i]) + for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) + if (params.cipher == rdev->wiphy.cipher_suites[i]) break; - if (i == drv->wiphy.n_cipher_suites) { + if (i == rdev->wiphy.n_cipher_suites) { err = -EINVAL; goto out; } - if (!drv->ops->add_key) { + if (!rdev->ops->add_key) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, ¶ms); + err = rdev->ops->add_key(&rdev->wiphy, dev, key_idx, mac_addr, ¶ms); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1173,7 +1173,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 key_idx = 0; @@ -1190,16 +1190,16 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->del_key) { + if (!rdev->ops->del_key) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr); + err = rdev->ops->del_key(&rdev->wiphy, dev, key_idx, mac_addr); #ifdef CONFIG_WIRELESS_EXT if (!err) { @@ -1211,7 +1211,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) #endif out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: @@ -1224,7 +1224,7 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) { int (*call)(struct wiphy *wiphy, struct net_device *dev, struct beacon_parameters *info); - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; struct beacon_parameters params; @@ -1235,7 +1235,7 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1254,10 +1254,10 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) goto out; } - call = drv->ops->add_beacon; + call = rdev->ops->add_beacon; break; case NL80211_CMD_SET_BEACON: - call = drv->ops->set_beacon; + call = rdev->ops->set_beacon; break; default: WARN_ON(1); @@ -1303,10 +1303,10 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) goto out; } - err = call(&drv->wiphy, dev, ¶ms); + err = call(&rdev->wiphy, dev, ¶ms); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1316,17 +1316,17 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->del_beacon) { + if (!rdev->ops->del_beacon) { err = -EOPNOTSUPP; goto out; } @@ -1335,10 +1335,10 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) err = -EOPNOTSUPP; goto out; } - err = drv->ops->del_beacon(&drv->wiphy, dev); + err = rdev->ops->del_beacon(&rdev->wiphy, dev); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1581,7 +1581,7 @@ static int nl80211_dump_station(struct sk_buff *skb, static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; struct station_info sinfo; @@ -1597,16 +1597,16 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->get_station) { + if (!rdev->ops->get_station) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo); + err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo); if (err) goto out; @@ -1624,7 +1624,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) out_free: nlmsg_free(msg); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -1655,7 +1655,7 @@ static int get_vlan(struct nlattr *vlanattr, static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; struct station_parameters params; @@ -1697,11 +1697,11 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, ¶ms.vlan); + err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], rdev, ¶ms.vlan); if (err) goto out; @@ -1750,17 +1750,17 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) if (err) goto out; - if (!drv->ops->change_station) { + if (!rdev->ops->change_station) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, ¶ms); + err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, ¶ms); out: if (params.vlan) dev_put(params.vlan); - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -1770,7 +1770,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; struct station_parameters params; @@ -1810,11 +1810,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, ¶ms.vlan); + err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], rdev, ¶ms.vlan); if (err) goto out; @@ -1850,7 +1850,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) if (err) goto out; - if (!drv->ops->add_station) { + if (!rdev->ops->add_station) { err = -EOPNOTSUPP; goto out; } @@ -1860,12 +1860,12 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) goto out; } - err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, ¶ms); + err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, ¶ms); out: if (params.vlan) dev_put(params.vlan); - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -1875,7 +1875,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 *mac_addr = NULL; @@ -1885,7 +1885,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; @@ -1896,15 +1896,15 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) goto out; } - if (!drv->ops->del_station) { + if (!rdev->ops->del_station) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->del_station(&drv->wiphy, dev, mac_addr); + err = rdev->ops->del_station(&rdev->wiphy, dev, mac_addr); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2044,7 +2044,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb, static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; struct mpath_info pinfo; @@ -2061,11 +2061,11 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->get_mpath) { + if (!rdev->ops->get_mpath) { err = -EOPNOTSUPP; goto out; } @@ -2075,7 +2075,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) goto out; } - err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo); + err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo); if (err) goto out; @@ -2093,7 +2093,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) out_free: nlmsg_free(msg); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2103,7 +2103,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 *dst = NULL; @@ -2120,11 +2120,11 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->change_mpath) { + if (!rdev->ops->change_mpath) { err = -EOPNOTSUPP; goto out; } @@ -2139,10 +2139,10 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) goto out; } - err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop); + err = rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2151,7 +2151,7 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) } static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 *dst = NULL; @@ -2168,11 +2168,11 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->add_mpath) { + if (!rdev->ops->add_mpath) { err = -EOPNOTSUPP; goto out; } @@ -2187,10 +2187,10 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) goto out; } - err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop); + err = rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2200,7 +2200,7 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 *dst = NULL; @@ -2210,19 +2210,19 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->del_mpath) { + if (!rdev->ops->del_mpath) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->del_mpath(&drv->wiphy, dev, dst); + err = rdev->ops->del_mpath(&rdev->wiphy, dev, dst); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2232,7 +2232,7 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; struct bss_parameters params; @@ -2261,11 +2261,11 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->change_bss) { + if (!rdev->ops->change_bss) { err = -EOPNOTSUPP; goto out; } @@ -2275,10 +2275,10 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) goto out; } - err = drv->ops->change_bss(&drv->wiphy, dev, ¶ms); + err = rdev->ops->change_bss(&rdev->wiphy, dev, ¶ms); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2369,7 +2369,7 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) static int nl80211_get_mesh_params(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct mesh_config cur_params; int err; struct net_device *dev; @@ -2380,17 +2380,17 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, rtnl_lock(); /* Look up our device */ - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->get_mesh_params) { + if (!rdev->ops->get_mesh_params) { err = -EOPNOTSUPP; goto out; } /* Get the mesh params */ - err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params); + err = rdev->ops->get_mesh_params(&rdev->wiphy, dev, &cur_params); if (err) goto out; @@ -2444,7 +2444,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, err = -EMSGSIZE; out: /* Cleanup */ - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2482,7 +2482,7 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) { int err; u32 mask; - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; struct mesh_config cfg; struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1]; @@ -2497,11 +2497,11 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->set_mesh_params) { + if (!rdev->ops->set_mesh_params) { err = -EOPNOTSUPP; goto out; } @@ -2546,11 +2546,11 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) nla_get_u16); /* Apply changes */ - err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask); + err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask); out: /* cleanup */ - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2737,7 +2737,7 @@ static int validate_scan_freqs(struct nlattr *freqs) static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; struct cfg80211_scan_request *request; struct cfg80211_ssid *ssid; @@ -2753,13 +2753,13 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - wiphy = &drv->wiphy; + wiphy = &rdev->wiphy; - if (!drv->ops->scan) { + if (!rdev->ops->scan) { err = -EOPNOTSUPP; goto out; } @@ -2769,7 +2769,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) goto out; } - if (drv->scan_req) { + if (rdev->scan_req) { err = -EBUSY; goto out; } @@ -2876,21 +2876,21 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) } request->ifidx = dev->ifindex; - request->wiphy = &drv->wiphy; + request->wiphy = &rdev->wiphy; - drv->scan_req = request; - err = drv->ops->scan(&drv->wiphy, dev, request); + rdev->scan_req = request; + err = rdev->ops->scan(&rdev->wiphy, dev, request); if (!err) - nl80211_send_scan_start(drv, dev); + nl80211_send_scan_start(rdev, dev); out_free: if (err) { - drv->scan_req = NULL; + rdev->scan_req = NULL; kfree(request); } out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -3043,7 +3043,7 @@ static bool nl80211_valid_cipher_suite(u32 cipher) static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; struct ieee80211_channel *chan; const u8 *bssid, *ssid, *ie = NULL; @@ -3067,11 +3067,11 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->auth) { + if (!rdev->ops->auth) { err = -EOPNOTSUPP; goto out; } @@ -3087,7 +3087,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) } bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - chan = ieee80211_get_channel(&drv->wiphy, + chan = ieee80211_get_channel(&rdev->wiphy, nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) { err = -EINVAL; @@ -3108,11 +3108,11 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) goto out; } - err = cfg80211_mlme_auth(drv, dev, chan, auth_type, bssid, + err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, ssid, ssid_len, ie, ie_len); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3202,7 +3202,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3268,7 +3268,7 @@ unlock_rtnl: static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; const u8 *ie = NULL, *bssid; int err, ie_len = 0; @@ -3285,11 +3285,11 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->deauth) { + if (!rdev->ops->deauth) { err = -EOPNOTSUPP; goto out; } @@ -3318,10 +3318,10 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } - err = cfg80211_mlme_deauth(drv, dev, bssid, ie, ie_len, reason_code); + err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3330,7 +3330,7 @@ unlock_rtnl: static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; const u8 *ie = NULL, *bssid; int err, ie_len = 0; @@ -3347,11 +3347,11 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->disassoc) { + if (!rdev->ops->disassoc) { err = -EOPNOTSUPP; goto out; } @@ -3380,10 +3380,10 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } - err = cfg80211_mlme_disassoc(drv, dev, bssid, ie, ie_len, reason_code); + err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3392,7 +3392,7 @@ unlock_rtnl: static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; struct cfg80211_ibss_params ibss; struct wiphy *wiphy; @@ -3419,11 +3419,11 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->join_ibss) { + if (!rdev->ops->join_ibss) { err = -EOPNOTSUPP; goto out; } @@ -3438,7 +3438,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) goto out; } - wiphy = &drv->wiphy; + wiphy = &rdev->wiphy; if (info->attrs[NL80211_ATTR_MAC]) ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); @@ -3461,10 +3461,10 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; - err = cfg80211_join_ibss(drv, dev, &ibss); + err = cfg80211_join_ibss(rdev, dev, &ibss); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3473,17 +3473,17 @@ unlock_rtnl: static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; int err; rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->leave_ibss) { + if (!rdev->ops->leave_ibss) { err = -EOPNOTSUPP; goto out; } @@ -3498,10 +3498,10 @@ static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info) goto out; } - err = cfg80211_leave_ibss(drv, dev, false); + err = cfg80211_leave_ibss(rdev, dev, false); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3632,7 +3632,7 @@ EXPORT_SYMBOL(cfg80211_testmode_event); static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; struct cfg80211_connect_params connect; struct wiphy *wiphy; @@ -3663,7 +3663,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) return err; rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3677,7 +3677,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) goto out; } - wiphy = &drv->wiphy; + wiphy = &rdev->wiphy; connect.bssid = NULL; connect.channel = NULL; @@ -3704,10 +3704,10 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) } } - err = cfg80211_connect(drv, dev, &connect); + err = cfg80211_connect(rdev, dev, &connect); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3716,7 +3716,7 @@ unlock_rtnl: static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; int err; u16 reason; @@ -3731,7 +3731,7 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3745,10 +3745,10 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) goto out; } - err = cfg80211_disconnect(drv, dev, reason, true); + err = cfg80211_disconnect(rdev, dev, reason, true); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 5e14371cda70..2b4a6c66f5ae 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1061,10 +1061,10 @@ static bool ignore_reg_update(struct wiphy *wiphy, static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; - list_for_each_entry(drv, &cfg80211_drv_list, list) - wiphy_update_regulatory(&drv->wiphy, initiator); + list_for_each_entry(rdev, &cfg80211_rdev_list, list) + wiphy_update_regulatory(&rdev->wiphy, initiator); } static void handle_reg_beacon(struct wiphy *wiphy, @@ -1614,7 +1614,7 @@ static void reg_process_pending_hints(void) /* Processes beacon hints -- this has nothing to do with country IEs */ static void reg_process_pending_beacon_hints(void) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct reg_beacon *pending_beacon, *tmp; mutex_lock(&cfg80211_mutex); @@ -1633,8 +1633,8 @@ static void reg_process_pending_beacon_hints(void) list_del_init(&pending_beacon->list); /* Applies the beacon hint to current wiphys */ - list_for_each_entry(drv, &cfg80211_drv_list, list) - wiphy_update_new_beacon(&drv->wiphy, pending_beacon); + list_for_each_entry(rdev, &cfg80211_rdev_list, list) + wiphy_update_new_beacon(&rdev->wiphy, pending_beacon); /* Remembers the beacon hint for new wiphys or reg changes */ list_add_tail(&pending_beacon->list, ®_beacon_list); @@ -1814,23 +1814,23 @@ void regulatory_hint_11d(struct wiphy *wiphy, if (likely(last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && wiphy_idx_valid(last_request->wiphy_idx))) { - struct cfg80211_registered_device *drv_last_ie; + struct cfg80211_registered_device *rdev_last_ie; - drv_last_ie = - cfg80211_drv_by_wiphy_idx(last_request->wiphy_idx); + rdev_last_ie = + cfg80211_rdev_by_wiphy_idx(last_request->wiphy_idx); /* * Lets keep this simple -- we trust the first AP * after we intersect with CRDA */ - if (likely(&drv_last_ie->wiphy == wiphy)) { + if (likely(&rdev_last_ie->wiphy == wiphy)) { /* * Ignore IEs coming in on this wiphy with * the same alpha2 and environment cap */ - if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2, + if (likely(alpha2_equal(rdev_last_ie->country_ie_alpha2, alpha2) && - env == drv_last_ie->env)) { + env == rdev_last_ie->env)) { goto out; } /* @@ -1846,9 +1846,9 @@ void regulatory_hint_11d(struct wiphy *wiphy, * Ignore IEs coming in on two separate wiphys with * the same alpha2 and environment cap */ - if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2, + if (likely(alpha2_equal(rdev_last_ie->country_ie_alpha2, alpha2) && - env == drv_last_ie->env)) { + env == rdev_last_ie->env)) { goto out; } /* We could potentially intersect though */ @@ -1995,14 +1995,14 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { - struct cfg80211_registered_device *drv; - drv = cfg80211_drv_by_wiphy_idx( + struct cfg80211_registered_device *rdev; + rdev = cfg80211_rdev_by_wiphy_idx( last_request->wiphy_idx); - if (drv) { + if (rdev) { printk(KERN_INFO "cfg80211: Current regulatory " "domain updated by AP to: %c%c\n", - drv->country_ie_alpha2[0], - drv->country_ie_alpha2[1]); + rdev->country_ie_alpha2[0], + rdev->country_ie_alpha2[1]); } else printk(KERN_INFO "cfg80211: Current regulatory " "domain intersected: \n"); @@ -2063,7 +2063,7 @@ static inline void reg_country_ie_process_debug( static int __set_regdom(const struct ieee80211_regdomain *rd) { const struct ieee80211_regdomain *intersected_rd = NULL; - struct cfg80211_registered_device *drv = NULL; + struct cfg80211_registered_device *rdev = NULL; struct wiphy *request_wiphy; /* Some basic sanity checks first */ @@ -2202,11 +2202,11 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) if (!intersected_rd) return -EINVAL; - drv = wiphy_to_dev(request_wiphy); + rdev = wiphy_to_dev(request_wiphy); - drv->country_ie_alpha2[0] = rd->alpha2[0]; - drv->country_ie_alpha2[1] = rd->alpha2[1]; - drv->env = last_request->country_ie_env; + rdev->country_ie_alpha2[0] = rd->alpha2[0]; + rdev->country_ie_alpha2[1] = rd->alpha2[1]; + rdev->env = last_request->country_ie_env; BUG_ON(intersected_rd == rd); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 472e2412c781..df9173f73604 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -33,15 +33,15 @@ struct cfg80211_conn { static int cfg80211_conn_scan(struct wireless_dev *wdev) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_scan_request *request; int n_channels, err; ASSERT_RTNL(); - ASSERT_RDEV_LOCK(drv); + ASSERT_RDEV_LOCK(rdev); ASSERT_WDEV_LOCK(wdev); - if (drv->scan_req) + if (rdev->scan_req) return -EBUSY; if (wdev->conn->params.channel) { @@ -87,16 +87,16 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) request->ssids[0].ssid_len = wdev->conn->params.ssid_len; request->ifidx = wdev->netdev->ifindex; - request->wiphy = &drv->wiphy; + request->wiphy = &rdev->wiphy; - drv->scan_req = request; + rdev->scan_req = request; - err = drv->ops->scan(wdev->wiphy, wdev->netdev, request); + err = rdev->ops->scan(wdev->wiphy, wdev->netdev, request); if (!err) { wdev->conn->state = CFG80211_CONN_SCANNING; - nl80211_send_scan_start(drv, wdev->netdev); + nl80211_send_scan_start(rdev, wdev->netdev); } else { - drv->scan_req = NULL; + rdev->scan_req = NULL; kfree(request); } return err; @@ -104,7 +104,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) static int cfg80211_conn_do_work(struct wireless_dev *wdev) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_connect_params *params; int err; @@ -119,15 +119,15 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) case CFG80211_CONN_SCAN_AGAIN: return cfg80211_conn_scan(wdev); case CFG80211_CONN_AUTHENTICATE_NEXT: - BUG_ON(!drv->ops->auth); + BUG_ON(!rdev->ops->auth); wdev->conn->state = CFG80211_CONN_AUTHENTICATING; - return __cfg80211_mlme_auth(drv, wdev->netdev, + return __cfg80211_mlme_auth(rdev, wdev->netdev, params->channel, params->auth_type, params->bssid, params->ssid, params->ssid_len, NULL, 0); case CFG80211_CONN_ASSOCIATE_NEXT: - BUG_ON(!drv->ops->assoc); + BUG_ON(!rdev->ops->assoc); wdev->conn->state = CFG80211_CONN_ASSOCIATING; /* * We could, later, implement roaming here and then actually @@ -135,14 +135,14 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) * that some APs don't like that -- so we'd need to retry * the association. */ - err = __cfg80211_mlme_assoc(drv, wdev->netdev, + err = __cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel, params->bssid, NULL, params->ssid, params->ssid_len, params->ie, params->ie_len, false, ¶ms->crypto); if (err) - __cfg80211_mlme_deauth(drv, wdev->netdev, params->bssid, + __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, NULL, 0, WLAN_REASON_DEAUTH_LEAVING); return err; @@ -153,15 +153,15 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) void cfg80211_conn_work(struct work_struct *work) { - struct cfg80211_registered_device *drv = + struct cfg80211_registered_device *rdev = container_of(work, struct cfg80211_registered_device, conn_work); struct wireless_dev *wdev; rtnl_lock(); - cfg80211_lock_rdev(drv); - mutex_lock(&drv->devlist_mtx); + cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &drv->netdev_list, list) { + list_for_each_entry(wdev, &rdev->netdev_list, list) { wdev_lock(wdev); if (!netif_running(wdev->netdev)) { wdev_unlock(wdev); @@ -181,14 +181,14 @@ void cfg80211_conn_work(struct work_struct *work) wdev_unlock(wdev); } - mutex_unlock(&drv->devlist_mtx); - cfg80211_unlock_rdev(drv); + mutex_unlock(&rdev->devlist_mtx); + cfg80211_unlock_rdev(rdev); rtnl_unlock(); } static bool cfg80211_get_conn_bss(struct wireless_dev *wdev) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_bss *bss; u16 capa = WLAN_CAPABILITY_ESS; @@ -209,7 +209,7 @@ static bool cfg80211_get_conn_bss(struct wireless_dev *wdev) wdev->conn->params.bssid = wdev->conn->bssid; wdev->conn->params.channel = bss->channel; wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; - schedule_work(&drv->conn_work); + schedule_work(&rdev->conn_work); cfg80211_put_bss(bss); return true; @@ -218,7 +218,7 @@ static bool cfg80211_get_conn_bss(struct wireless_dev *wdev) static void __cfg80211_sme_scan_done(struct net_device *dev) { struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); ASSERT_WDEV_LOCK(wdev); @@ -235,7 +235,7 @@ static void __cfg80211_sme_scan_done(struct net_device *dev) if (!cfg80211_get_conn_bss(wdev)) { /* not found */ if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) - schedule_work(&drv->conn_work); + schedule_work(&rdev->conn_work); else __cfg80211_connect_result( wdev->netdev, -- cgit v1.2.3 From fffd0934b9390f34bec45762192b7edd3b12b4b5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 8 Jul 2009 14:22:54 +0200 Subject: cfg80211: rework key operation This reworks the key operation in cfg80211, and now only allows, from userspace, configuring keys (via nl80211) after the connection has been established (in managed mode), the IBSS been joined (in IBSS mode), at any time (in AP[_VLAN] modes) or never for all the other modes. In order to do shared key authentication correctly, it is now possible to give a WEP key to the AUTH command. To configure static WEP keys, these are given to the CONNECT or IBSS_JOIN command directly, for a userspace SME it is assumed it will configure it properly after the connection has been established. Since mac80211 used to check the default key in IBSS mode to see whether or not the network is protected, it needs an update in that area, as well as an update to make use of the WEP key passed to auth() for shared key authentication. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 5 ++ include/net/cfg80211.h | 18 ++++- net/mac80211/ibss.c | 9 +-- net/mac80211/ieee80211_i.h | 8 ++- net/mac80211/mlme.c | 11 ++- net/mac80211/util.c | 16 +++-- net/mac80211/wep.c | 6 +- net/mac80211/wep.h | 3 + net/wireless/core.c | 11 ++- net/wireless/core.h | 32 +++++++-- net/wireless/ibss.c | 79 +++++++++++++++++---- net/wireless/mlme.c | 16 ++++- net/wireless/nl80211.c | 170 ++++++++++++++++++++++++++++++++++++++++----- net/wireless/sme.c | 97 +++++++++++++++++++------- net/wireless/util.c | 41 ++++++++++- net/wireless/wext-compat.c | 163 ++++++++++++++++++++++++++----------------- net/wireless/wext-sme.c | 30 ++++++-- 17 files changed, 554 insertions(+), 161 deletions(-) (limited to 'net/wireless/core.h') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 48e0913c2209..b043b78dd2c3 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -569,6 +569,9 @@ enum nl80211_commands { * * @NL80211_ATTR_KEY: key information in a nested attribute with * %NL80211_KEY_* sub-attributes + * @NL80211_ATTR_KEYS: array of keys for static WEP keys for connect() + * and join_ibss(), key information is in a nested attribute each + * with %NL80211_KEY_* sub-attributes * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -696,6 +699,7 @@ enum nl80211_attrs { NL80211_ATTR_PREV_BSSID, NL80211_ATTR_KEY, + NL80211_ATTR_KEYS, /* add attributes here, update the policy in nl80211.c */ @@ -726,6 +730,7 @@ enum nl80211_attrs { #define NL80211_ATTR_WPA_VERSIONS NL80211_ATTR_WPA_VERSIONS #define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES #define NL80211_ATTR_KEY NL80211_ATTR_KEY +#define NL80211_ATTR_KEYS NL80211_ATTR_KEYS #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_REG_RULES 32 diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 83c2c727d71e..65a5cbcb5d14 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -647,12 +647,17 @@ struct cfg80211_crypto_settings { * @auth_type: Authentication type (algorithm) * @ie: Extra IEs to add to Authentication frame or %NULL * @ie_len: Length of ie buffer in octets + * @key_len: length of WEP key for shared key authentication + * @key_idx: index of WEP key for shared key authentication + * @key: WEP key for shared key authentication */ struct cfg80211_auth_request { struct cfg80211_bss *bss; const u8 *ie; size_t ie_len; enum nl80211_auth_type auth_type; + const u8 *key; + u8 key_len, key_idx; }; /** @@ -727,6 +732,8 @@ struct cfg80211_disassoc_request { * @ie: information element(s) to include in the beacon * @ie_len: length of that * @beacon_interval: beacon interval to use + * @privacy: this is a protected network, keys will be configured + * after joining */ struct cfg80211_ibss_params { u8 *ssid; @@ -736,6 +743,7 @@ struct cfg80211_ibss_params { u8 ssid_len, ie_len; u16 beacon_interval; bool channel_fixed; + bool privacy; }; /** @@ -755,6 +763,9 @@ struct cfg80211_ibss_params { * @assoc_ie_len: Length of assoc_ie in octets * @privacy: indicates whether privacy-enabled APs should be used * @crypto: crypto settings + * @key_len: length of WEP key for shared key authentication + * @key_idx: index of WEP key for shared key authentication + * @key: WEP key for shared key authentication */ struct cfg80211_connect_params { struct ieee80211_channel *channel; @@ -766,6 +777,8 @@ struct cfg80211_connect_params { size_t ie_len; bool privacy; struct cfg80211_crypto_settings crypto; + const u8 *key; + u8 key_len, key_idx; }; /** @@ -1223,9 +1236,10 @@ extern void wiphy_unregister(struct wiphy *wiphy); */ extern void wiphy_free(struct wiphy *wiphy); -/* internal struct */ +/* internal structs */ struct cfg80211_conn; struct cfg80211_internal_bss; +struct cfg80211_cached_keys; #define MAX_AUTH_BSSES 4 @@ -1267,6 +1281,7 @@ struct wireless_dev { CFG80211_SME_CONNECTED, } sme_state; struct cfg80211_conn *conn; + struct cfg80211_cached_keys *connect_keys; struct list_head event_list; spinlock_t event_lock; @@ -1280,6 +1295,7 @@ struct wireless_dev { struct { struct cfg80211_ibss_params ibss; struct cfg80211_connect_params connect; + struct cfg80211_cached_keys *keys; u8 *ie; size_t ie_len; u8 bssid[ETH_ALEN]; diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 15d5a53b59a8..8e2220000e5c 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -57,7 +57,7 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, */ if (auth_alg == WLAN_AUTH_OPEN && auth_transaction == 1) ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, NULL, 0, - sdata->u.ibss.bssid, 0); + sdata->u.ibss.bssid, NULL, 0, 0); } static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, @@ -494,7 +494,7 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) capability = WLAN_CAPABILITY_IBSS; - if (sdata->default_key) + if (ifibss->privacy) capability |= WLAN_CAPABILITY_PRIVACY; else sdata->drop_unencrypted = 0; @@ -524,9 +524,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) return; capability = WLAN_CAPABILITY_IBSS; - if (sdata->default_key) + if (ifibss->privacy) capability |= WLAN_CAPABILITY_PRIVACY; - if (ifibss->fixed_bssid) bssid = ifibss->bssid; if (ifibss->fixed_channel) @@ -872,6 +871,8 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, } else sdata->u.ibss.fixed_bssid = false; + sdata->u.ibss.privacy = params->privacy; + sdata->vif.bss_conf.beacon_int = params->beacon_interval; sdata->u.ibss.channel = params->channel; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 327aabc07abe..06b3411530f2 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -247,6 +247,9 @@ struct ieee80211_mgd_work { int tries; + u8 key[WLAN_KEY_LEN_WEP104]; + u8 key_len, key_idx; + /* must be last */ u8 ie[0]; /* for auth or assoc frame, not probe */ }; @@ -321,6 +324,7 @@ struct ieee80211_if_ibss { bool fixed_bssid; bool fixed_channel; + bool privacy; u8 bssid[ETH_ALEN]; u8 ssid[IEEE80211_MAX_SSID_LEN]; @@ -1093,8 +1097,8 @@ int ieee80211_add_pending_skbs(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, int encrypt); + u8 *extra, size_t extra_len, const u8 *bssid, + 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); void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index c9db9646025b..8e4a60497bba 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -954,7 +954,7 @@ ieee80211_authenticate(struct ieee80211_sub_if_data *sdata, sdata->dev->name, wk->bss->cbss.bssid, wk->tries); ieee80211_send_auth(sdata, 1, wk->auth_alg, wk->ie, wk->ie_len, - wk->bss->cbss.bssid, 0); + wk->bss->cbss.bssid, NULL, 0, 0); wk->auth_transaction = 2; wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; @@ -1176,7 +1176,8 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, return; ieee80211_send_auth(sdata, 3, wk->auth_alg, elems.challenge - 2, elems.challenge_len + 2, - wk->bss->cbss.bssid, 1); + wk->bss->cbss.bssid, + wk->key, wk->key_len, wk->key_idx); wk->auth_transaction = 4; } @@ -2175,6 +2176,12 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, wk->ie_len = req->ie_len; } + if (req->key && req->key_len) { + wk->key_len = req->key_len; + wk->key_idx = req->key_idx; + memcpy(wk->key, req->key, req->key_len); + } + ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); memcpy(wk->ssid, ssid + 2, ssid[1]); wk->ssid_len = ssid[1]; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 915e77769312..dbf66b52d38c 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -31,6 +31,7 @@ #include "mesh.h" #include "wme.h" #include "led.h" +#include "wep.h" /* privid for wiphys to determine whether they belong to us or not */ void *mac80211_wiphy_privid = &mac80211_wiphy_privid; @@ -804,12 +805,13 @@ 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, int encrypt) + u8 *extra, size_t extra_len, const u8 *bssid, + const u8 *key, u8 key_len, u8 key_idx) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; + int err; skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 6 + extra_len); @@ -824,8 +826,6 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, memset(mgmt, 0, 24 + 6); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); - if (encrypt) - mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); memcpy(mgmt->da, bssid, ETH_ALEN); memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN); memcpy(mgmt->bssid, bssid, ETH_ALEN); @@ -835,7 +835,13 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, if (extra) memcpy(skb_put(skb, extra_len), extra, extra_len); - ieee80211_tx_skb(sdata, skb, encrypt); + if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) { + mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); + err = ieee80211_wep_encrypt(local, skb, key, key_len, key_idx); + WARN_ON(err); + } + + ieee80211_tx_skb(sdata, skb, 0); } int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c index 4fafb2d27c84..8a980f136941 100644 --- a/net/mac80211/wep.c +++ b/net/mac80211/wep.c @@ -144,9 +144,9 @@ void ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, * * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) */ -static int ieee80211_wep_encrypt(struct ieee80211_local *local, - struct sk_buff *skb, - const u8 *key, int keylen, int keyidx) +int ieee80211_wep_encrypt(struct ieee80211_local *local, + struct sk_buff *skb, + const u8 *key, int keylen, int keyidx) { u8 *iv; size_t len; diff --git a/net/mac80211/wep.h b/net/mac80211/wep.h index 85219ded8703..fe29d7e5759f 100644 --- a/net/mac80211/wep.h +++ b/net/mac80211/wep.h @@ -20,6 +20,9 @@ int ieee80211_wep_init(struct ieee80211_local *local); void ieee80211_wep_free(struct ieee80211_local *local); void ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, size_t klen, u8 *data, size_t data_len); +int ieee80211_wep_encrypt(struct ieee80211_local *local, + struct sk_buff *skb, + const u8 *key, int keylen, int keyidx); int ieee80211_wep_decrypt_data(struct crypto_blkcipher *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); diff --git a/net/wireless/core.c b/net/wireless/core.c index 1a78b3c70cf2..97cc5968b7d6 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -666,14 +666,10 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, wdev_lock(wdev); switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: - if (wdev->wext.ibss.ssid_len) - __cfg80211_join_ibss(rdev, dev, - &wdev->wext.ibss); + cfg80211_ibss_wext_join(rdev, wdev); break; case NL80211_IFTYPE_STATION: - if (wdev->wext.connect.ssid_len) - __cfg80211_connect(rdev, dev, - &wdev->wext.connect); + cfg80211_mgd_wext_connect(rdev, wdev); break; default: break; @@ -690,6 +686,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, } mutex_unlock(&rdev->devlist_mtx); mutex_destroy(&wdev->mtx); +#ifdef CONFIG_WIRELESS_EXT + kfree(wdev->wext.keys); +#endif break; case NETDEV_PRE_UP: if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) diff --git a/net/wireless/core.h b/net/wireless/core.h index e46cd6eb61d7..2ec8ddbe57de 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -238,6 +238,12 @@ struct cfg80211_event { }; }; +struct cfg80211_cached_keys { + struct key_params params[6]; + u8 data[6][WLAN_MAX_KEY_LEN]; + int def, defmgmt; +}; + /* free object */ extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev); @@ -256,14 +262,18 @@ void cfg80211_bss_age(struct cfg80211_registered_device *dev, /* IBSS */ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_ibss_params *params); + struct cfg80211_ibss_params *params, + struct cfg80211_cached_keys *connkeys); int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_ibss_params *params); + struct cfg80211_ibss_params *params, + struct cfg80211_cached_keys *connkeys); void cfg80211_clear_ibss(struct net_device *dev, bool nowext); int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext); void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid); +int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev); /* MLME */ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, @@ -272,12 +282,14 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len); + const u8 *ie, int ie_len, + const u8 *key, int key_len, int key_idx); int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len); + const u8 *ie, int ie_len, + const u8 *key, int key_len, int key_idx); int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, @@ -310,10 +322,12 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, /* SME */ int __cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_connect_params *connect); + struct cfg80211_connect_params *connect, + struct cfg80211_cached_keys *connkeys); int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_connect_params *connect); + struct cfg80211_connect_params *connect, + struct cfg80211_cached_keys *connkeys); int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason, bool wextev); @@ -323,11 +337,14 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len); +int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev); void cfg80211_conn_work(struct work_struct *work); /* internal helpers */ -int cfg80211_validate_key_settings(struct key_params *params, int key_idx, +int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, + struct key_params *params, int key_idx, const u8 *mac_addr); void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, size_t ie_len, u16 reason, bool from_ap); @@ -335,5 +352,6 @@ void cfg80211_sme_scan_done(struct net_device *dev); void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); void cfg80211_sme_disassoc(struct net_device *dev, int idx); void __cfg80211_scan_done(struct work_struct *wk); +void cfg80211_upload_connect_keys(struct wireless_dev *wdev); #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 99ef9364b7e8..9394e78cd11f 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -39,6 +39,8 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) cfg80211_hold_bss(bss_from_pub(bss)); wdev->current_bss = bss_from_pub(bss); + cfg80211_upload_connect_keys(wdev); + nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, GFP_KERNEL); #ifdef CONFIG_WIRELESS_EXT @@ -71,7 +73,8 @@ EXPORT_SYMBOL(cfg80211_ibss_joined); int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_ibss_params *params) + struct cfg80211_ibss_params *params, + struct cfg80211_cached_keys *connkeys) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; @@ -81,13 +84,18 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, if (wdev->ssid_len) return -EALREADY; + if (WARN_ON(wdev->connect_keys)) + kfree(wdev->connect_keys); + wdev->connect_keys = connkeys; + #ifdef CONFIG_WIRELESS_EXT wdev->wext.ibss.channel = params->channel; #endif err = rdev->ops->join_ibss(&rdev->wiphy, dev, params); - - if (err) + if (err) { + wdev->connect_keys = NULL; return err; + } memcpy(wdev->ssid, params->ssid, params->ssid_len); wdev->ssid_len = params->ssid_len; @@ -97,13 +105,14 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_ibss_params *params) + struct cfg80211_ibss_params *params, + struct cfg80211_cached_keys *connkeys) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; wdev_lock(wdev); - err = __cfg80211_join_ibss(rdev, dev, params); + err = __cfg80211_join_ibss(rdev, dev, params, connkeys); wdev_unlock(wdev); return err; @@ -112,9 +121,22 @@ int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + int i; ASSERT_WDEV_LOCK(wdev); + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; + + /* + * Delete all the keys ... pairwise keys can't really + * exist any more anyway, but default keys might. + */ + if (rdev->ops->del_key) + for (i = 0; i < 6; i++) + rdev->ops->del_key(wdev->wiphy, dev, i, NULL); + if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(&wdev->current_bss->pub); @@ -172,11 +194,14 @@ int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, } #ifdef CONFIG_WIRELESS_EXT -static int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev) +int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) { + struct cfg80211_cached_keys *ck = NULL; enum ieee80211_band band; - int i; + int i, err; + + ASSERT_WDEV_LOCK(wdev); if (!wdev->wext.ibss.beacon_interval) wdev->wext.ibss.beacon_interval = 100; @@ -216,8 +241,24 @@ static int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, if (!netif_running(wdev->netdev)) return 0; - return cfg80211_join_ibss(wiphy_to_dev(wdev->wiphy), - wdev->netdev, &wdev->wext.ibss); + if (wdev->wext.keys) + wdev->wext.keys->def = wdev->wext.default_key; + + wdev->wext.ibss.privacy = wdev->wext.default_key != -1; + + if (wdev->wext.keys) { + ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL); + if (!ck) + return -ENOMEM; + for (i = 0; i < 6; i++) + ck->params[i].key = ck->data[i]; + } + err = __cfg80211_join_ibss(rdev, wdev->netdev, + &wdev->wext.ibss, ck); + if (err) + kfree(ck); + + return err; } int cfg80211_ibss_wext_siwfreq(struct net_device *dev, @@ -265,7 +306,11 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, wdev->wext.ibss.channel_fixed = false; } - return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + wdev_lock(wdev); + err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + wdev_unlock(wdev); + + return err; } /* temporary symbol - mark GPL - in the future the handler won't be */ EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwfreq); @@ -333,7 +378,11 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev, memcpy(wdev->wext.ibss.ssid, ssid, len); wdev->wext.ibss.ssid_len = len; - return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + wdev_lock(wdev); + err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + wdev_unlock(wdev); + + return err; } /* temporary symbol - mark GPL - in the future the handler won't be */ EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwessid); @@ -414,7 +463,11 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev, } else wdev->wext.ibss.bssid = NULL; - return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + wdev_lock(wdev); + err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + wdev_unlock(wdev); + + return err; } /* temporary symbol - mark GPL - in the future the handler won't be */ EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwap); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 1b2ca1fea7a1..8e4ce2fdf862 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -328,7 +328,8 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len) + const u8 *ie, int ie_len, + const u8 *key, int key_len, int key_idx) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_auth_request req; @@ -337,6 +338,10 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, ASSERT_WDEV_LOCK(wdev); + if (auth_type == NL80211_AUTHTYPE_SHARED_KEY) + if (!key || !key_len || key_idx < 0 || key_idx > 4) + return -EINVAL; + if (wdev->current_bss && memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0) return -EALREADY; @@ -359,6 +364,9 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, req.auth_type = auth_type; req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + req.key = key; + req.key_len = key_len; + req.key_idx = key_idx; if (!req.bss) return -ENOENT; @@ -396,13 +404,15 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len) + const u8 *ie, int ie_len, + const u8 *key, int key_len, int key_idx) { int err; wdev_lock(dev->ieee80211_ptr); err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, - ssid, ssid_len, ie, ie_len); + ssid, ssid_len, ie, ie_len, + key, key_len, key_idx); wdev_unlock(dev->ieee80211_ptr); return err; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 50cf59316292..45c5f9c8e51b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -138,8 +138,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { /* policy for the attributes */ static struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] __read_mostly = { - [NL80211_KEY_DATA] = { .type = NLA_BINARY, - .len = WLAN_MAX_KEY_LEN }, + [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, [NL80211_KEY_IDX] = { .type = NLA_U8 }, [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 }, @@ -305,6 +304,83 @@ static int nl80211_parse_key(struct genl_info *info, struct key_parse *k) return 0; } +static struct cfg80211_cached_keys * +nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, + struct nlattr *keys) +{ + struct key_parse parse; + struct nlattr *key; + struct cfg80211_cached_keys *result; + int rem, err, def = 0; + + result = kzalloc(sizeof(*result), GFP_KERNEL); + if (!result) + return ERR_PTR(-ENOMEM); + + result->def = -1; + result->defmgmt = -1; + + nla_for_each_nested(key, keys, rem) { + memset(&parse, 0, sizeof(parse)); + parse.idx = -1; + + err = nl80211_parse_key_new(key, &parse); + if (err) + goto error; + err = -EINVAL; + if (!parse.p.key) + goto error; + if (parse.idx < 0 || parse.idx > 4) + goto error; + if (parse.def) { + if (def) + goto error; + def = 1; + result->def = parse.idx; + } else if (parse.defmgmt) + goto error; + err = cfg80211_validate_key_settings(rdev, &parse.p, + parse.idx, NULL); + if (err) + goto error; + result->params[parse.idx].cipher = parse.p.cipher; + result->params[parse.idx].key_len = parse.p.key_len; + result->params[parse.idx].key = result->data[parse.idx]; + memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len); + } + + return result; + error: + kfree(result); + return ERR_PTR(err); +} + +static int nl80211_key_allowed(struct wireless_dev *wdev) +{ + ASSERT_WDEV_LOCK(wdev); + + if (!netif_running(wdev->netdev)) + return -ENETDOWN; + + switch (wdev->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + break; + case NL80211_IFTYPE_ADHOC: + if (!wdev->current_bss) + return -ENOLINK; + break; + case NL80211_IFTYPE_STATION: + if (wdev->sme_state != CFG80211_SME_CONNECTED) + return -ENOLINK; + break; + default: + return -EINVAL; + } + + return 0; +} + static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct cfg80211_registered_device *dev) { @@ -1212,7 +1288,11 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) goto out; } - err = func(&rdev->wiphy, dev, key.idx); + wdev_lock(dev->ieee80211_ptr); + err = nl80211_key_allowed(dev->ieee80211_ptr); + if (!err) + err = func(&rdev->wiphy, dev, key.idx); + #ifdef CONFIG_WIRELESS_EXT if (!err) { if (func == rdev->ops->set_default_key) @@ -1221,6 +1301,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) dev->ieee80211_ptr->wext.default_mgmt_key = key.idx; } #endif + wdev_unlock(dev->ieee80211_ptr); out: cfg80211_unlock_rdev(rdev); @@ -1235,7 +1316,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev; - int err, i; + int err; struct net_device *dev; struct key_parse key; u8 *mac_addr = NULL; @@ -1250,29 +1331,28 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_MAC]) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - if (cfg80211_validate_key_settings(&key.p, key.idx, mac_addr)) - return -EINVAL; - rtnl_lock(); err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) - if (key.p.cipher == rdev->wiphy.cipher_suites[i]) - break; - if (i == rdev->wiphy.n_cipher_suites) { - err = -EINVAL; + if (!rdev->ops->add_key) { + err = -EOPNOTSUPP; goto out; } - if (!rdev->ops->add_key) { - err = -EOPNOTSUPP; + if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, mac_addr)) { + err = -EINVAL; goto out; } - err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx, mac_addr, &key.p); + wdev_lock(dev->ieee80211_ptr); + err = nl80211_key_allowed(dev->ieee80211_ptr); + if (!err) + err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx, + mac_addr, &key.p); + wdev_unlock(dev->ieee80211_ptr); out: cfg80211_unlock_rdev(rdev); @@ -1309,7 +1389,10 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) goto out; } - err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr); + wdev_lock(dev->ieee80211_ptr); + err = nl80211_key_allowed(dev->ieee80211_ptr); + if (!err) + err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr); #ifdef CONFIG_WIRELESS_EXT if (!err) { @@ -1319,6 +1402,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) dev->ieee80211_ptr->wext.default_mgmt_key = -1; } #endif + wdev_unlock(dev->ieee80211_ptr); out: cfg80211_unlock_rdev(rdev); @@ -3159,6 +3243,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) const u8 *bssid, *ssid, *ie = NULL; int err, ssid_len, ie_len = 0; enum nl80211_auth_type auth_type; + struct key_parse key; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; @@ -3175,6 +3260,25 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) return -EINVAL; + err = nl80211_parse_key(info, &key); + if (err) + return err; + + if (key.idx >= 0) { + if (!key.p.key || !key.p.key_len) + return -EINVAL; + if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 || + key.p.key_len != WLAN_KEY_LEN_WEP40) && + (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 || + key.p.key_len != WLAN_KEY_LEN_WEP104)) + return -EINVAL; + if (key.idx > 4) + return -EINVAL; + } else { + key.p.key_len = 0; + key.p.key = NULL; + } + rtnl_lock(); err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); @@ -3219,7 +3323,8 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) } err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, - ssid, ssid_len, ie, ie_len); + ssid, ssid_len, ie, ie_len, + key.p.key, key.p.key_len, key.idx); out: cfg80211_unlock_rdev(rdev); @@ -3506,6 +3611,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) struct net_device *dev; struct cfg80211_ibss_params ibss; struct wiphy *wiphy; + struct cfg80211_cached_keys *connkeys = NULL; int err; memset(&ibss, 0, sizeof(ibss)); @@ -3570,13 +3676,26 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) } ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; + ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; + + if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) { + connkeys = nl80211_parse_connkeys(rdev, + info->attrs[NL80211_ATTR_KEYS]); + if (IS_ERR(connkeys)) { + err = PTR_ERR(connkeys); + connkeys = NULL; + goto out; + } + } - err = cfg80211_join_ibss(rdev, dev, &ibss); + err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys); out: cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: + if (err) + kfree(connkeys); rtnl_unlock(); return err; } @@ -3746,6 +3865,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) struct net_device *dev; struct cfg80211_connect_params connect; struct wiphy *wiphy; + struct cfg80211_cached_keys *connkeys = NULL; int err; memset(&connect, 0, sizeof(connect)); @@ -3810,12 +3930,24 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) } } - err = cfg80211_connect(rdev, dev, &connect); + if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) { + connkeys = nl80211_parse_connkeys(rdev, + info->attrs[NL80211_ATTR_KEYS]); + if (IS_ERR(connkeys)) { + err = PTR_ERR(connkeys); + connkeys = NULL; + goto out; + } + } + + err = cfg80211_connect(rdev, dev, &connect, connkeys); out: cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: + if (err) + kfree(connkeys); rtnl_unlock(); return err; } diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 79ca56cbfd36..d635a99dba51 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -125,7 +125,9 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) params->channel, params->auth_type, params->bssid, params->ssid, params->ssid_len, - NULL, 0); + NULL, 0, + params->key, params->key_len, + params->key_idx); case CFG80211_CONN_ASSOCIATE_NEXT: BUG_ON(!rdev->ops->assoc); wdev->conn->state = CFG80211_CONN_ASSOCIATING; @@ -279,8 +281,12 @@ void cfg80211_sme_rx_auth(struct net_device *dev, /* select automatically between only open, shared, leap */ switch (wdev->conn->params.auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: - wdev->conn->params.auth_type = - NL80211_AUTHTYPE_SHARED_KEY; + if (wdev->connect_keys) + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_SHARED_KEY; + else + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_NETWORK_EAP; break; case NL80211_AUTHTYPE_SHARED_KEY: wdev->conn->params.auth_type = @@ -353,10 +359,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, #endif if (status == WLAN_STATUS_SUCCESS && - wdev->sme_state == CFG80211_SME_IDLE) { - wdev->sme_state = CFG80211_SME_CONNECTED; - return; - } + wdev->sme_state == CFG80211_SME_IDLE) + goto success; if (wdev->sme_state != CFG80211_SME_CONNECTING) return; @@ -370,24 +374,29 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (wdev->conn) wdev->conn->state = CFG80211_CONN_IDLE; - if (status == WLAN_STATUS_SUCCESS) { - bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, - wdev->ssid, wdev->ssid_len, - WLAN_CAPABILITY_ESS, - WLAN_CAPABILITY_ESS); - - if (WARN_ON(!bss)) - return; - - cfg80211_hold_bss(bss_from_pub(bss)); - wdev->current_bss = bss_from_pub(bss); - - wdev->sme_state = CFG80211_SME_CONNECTED; - } else { + if (status != WLAN_STATUS_SUCCESS) { wdev->sme_state = CFG80211_SME_IDLE; kfree(wdev->conn); wdev->conn = NULL; + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; + return; } + + bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, + wdev->ssid, wdev->ssid_len, + WLAN_CAPABILITY_ESS, + WLAN_CAPABILITY_ESS); + + if (WARN_ON(!bss)) + return; + + cfg80211_hold_bss(bss_from_pub(bss)); + wdev->current_bss = bss_from_pub(bss); + + success: + wdev->sme_state = CFG80211_SME_CONNECTED; + cfg80211_upload_connect_keys(wdev); } void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, @@ -516,6 +525,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, size_t ie_len, u16 reason, bool from_ap) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + int i; #ifdef CONFIG_WIRELESS_EXT union iwreq_data wrqu; #endif @@ -543,8 +554,15 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, wdev->conn = NULL; } - nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev, - reason, ie, ie_len, from_ap); + nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); + + /* + * Delete all the keys ... pairwise keys can't really + * exist any more anyway, but default keys might. + */ + if (rdev->ops->del_key) + for (i = 0; i < 6; i++) + rdev->ops->del_key(wdev->wiphy, dev, i, NULL); #ifdef CONFIG_WIRELESS_EXT memset(&wrqu, 0, sizeof(wrqu)); @@ -580,7 +598,8 @@ EXPORT_SYMBOL(cfg80211_disconnected); int __cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_connect_params *connect) + struct cfg80211_connect_params *connect, + struct cfg80211_cached_keys *connkeys) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; @@ -590,6 +609,24 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, if (wdev->sme_state != CFG80211_SME_IDLE) return -EALREADY; + if (WARN_ON(wdev->connect_keys)) { + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; + } + + if (connkeys && connkeys->def >= 0) { + int idx; + + idx = connkeys->def; + /* If given a WEP key we may need it for shared key auth */ + if (connkeys->params[idx].cipher == WLAN_CIPHER_SUITE_WEP40 || + connkeys->params[idx].cipher == WLAN_CIPHER_SUITE_WEP104) { + connect->key_idx = idx; + connect->key = connkeys->params[idx].key; + connect->key_len = connkeys->params[idx].key_len; + } + } + if (!rdev->ops->connect) { if (!rdev->ops->auth || !rdev->ops->assoc) return -EOPNOTSUPP; @@ -640,6 +677,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, cfg80211_get_conn_bss(wdev); wdev->sme_state = CFG80211_SME_CONNECTING; + wdev->connect_keys = connkeys; /* we're good if we have both BSSID and channel */ if (wdev->conn->params.bssid && wdev->conn->params.channel) { @@ -662,13 +700,16 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, kfree(wdev->conn); wdev->conn = NULL; wdev->sme_state = CFG80211_SME_IDLE; + wdev->connect_keys = NULL; } return err; } else { wdev->sme_state = CFG80211_SME_CONNECTING; + wdev->connect_keys = connkeys; err = rdev->ops->connect(&rdev->wiphy, dev, connect); if (err) { + wdev->connect_keys = NULL; wdev->sme_state = CFG80211_SME_IDLE; return err; } @@ -682,12 +723,13 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_connect_params *connect) + struct cfg80211_connect_params *connect, + struct cfg80211_cached_keys *connkeys) { int err; wdev_lock(dev->ieee80211_ptr); - err = __cfg80211_connect(rdev, dev, connect); + err = __cfg80211_connect(rdev, dev, connect, connkeys); wdev_unlock(dev->ieee80211_ptr); return err; @@ -704,6 +746,9 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, if (wdev->sme_state == CFG80211_SME_IDLE) return -EINVAL; + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; + if (!rdev->ops->disconnect) { if (!rdev->ops->deauth) return -EOPNOTSUPP; diff --git a/net/wireless/util.c b/net/wireless/util.c index 28f8f96801d4..4bab380a1204 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -141,9 +141,12 @@ void ieee80211_set_bitrate_flags(struct wiphy *wiphy) set_mandatory_flags_band(wiphy->bands[band], band); } -int cfg80211_validate_key_settings(struct key_params *params, int key_idx, +int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, + struct key_params *params, int key_idx, const u8 *mac_addr) { + int i; + if (key_idx > 5) return -EINVAL; @@ -197,6 +200,12 @@ int cfg80211_validate_key_settings(struct key_params *params, int key_idx, } } + for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) + if (params->cipher == rdev->wiphy.cipher_suites[i]) + break; + if (i == rdev->wiphy.n_cipher_suites) + return -EINVAL; + return 0; } @@ -523,3 +532,33 @@ const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) return NULL; } EXPORT_SYMBOL(ieee80211_bss_get_ie); + +void cfg80211_upload_connect_keys(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct net_device *dev = wdev->netdev; + int i; + + if (!wdev->connect_keys) + return; + + for (i = 0; i < 6; i++) { + if (!wdev->connect_keys->params[i].cipher) + continue; + if (rdev->ops->add_key(wdev->wiphy, dev, i, NULL, + &wdev->connect_keys->params[i])) + printk(KERN_ERR "%s: failed to set key %d\n", + dev->name, i); + if (wdev->connect_keys->def == i) + if (rdev->ops->set_default_key(wdev->wiphy, dev, i)) + printk(KERN_ERR "%s: failed to set defkey %d\n", + dev->name, i); + if (wdev->connect_keys->defmgmt == i) + if (rdev->ops->set_default_mgmt_key(wdev->wiphy, dev, i)) + printk(KERN_ERR "%s: failed to set mgtdef %d\n", + dev->name, i); + } + + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; +} diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 5088d89a30fc..5d0176338539 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -453,15 +453,32 @@ int cfg80211_wext_giwretry(struct net_device *dev, } EXPORT_SYMBOL_GPL(cfg80211_wext_giwretry); -static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *addr, - bool remove, bool tx_key, int idx, - struct key_params *params) +static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *addr, + bool remove, bool tx_key, int idx, + struct key_params *params) { struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; + int err, i; + + if (!wdev->wext.keys) { + wdev->wext.keys = kzalloc(sizeof(*wdev->wext.keys), + GFP_KERNEL); + if (!wdev->wext.keys) + return -ENOMEM; + for (i = 0; i < 6; i++) + wdev->wext.keys->params[i].key = + wdev->wext.keys->data[i]; + } + + if (wdev->iftype != NL80211_IFTYPE_ADHOC && + wdev->iftype != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { + if (!wdev->current_bss) + return -ENOLINK; + if (!rdev->ops->set_default_mgmt_key) return -EOPNOTSUPP; @@ -471,8 +488,14 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, return -EINVAL; if (remove) { - err = rdev->ops->del_key(&rdev->wiphy, dev, idx, addr); + err = 0; + if (wdev->current_bss) + err = rdev->ops->del_key(&rdev->wiphy, dev, idx, addr); if (!err) { + if (!addr) { + wdev->wext.keys->params[idx].key_len = 0; + wdev->wext.keys->params[idx].cipher = 0; + } if (idx == wdev->wext.default_key) wdev->wext.default_key = -1; else if (idx == wdev->wext.default_mgmt_key) @@ -486,36 +509,64 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, return 0; return err; - } else { - if (addr) - tx_key = false; + } - if (cfg80211_validate_key_settings(params, idx, addr)) - return -EINVAL; + if (addr) + tx_key = false; + if (cfg80211_validate_key_settings(rdev, params, idx, addr)) + return -EINVAL; + + err = 0; + if (wdev->current_bss) err = rdev->ops->add_key(&rdev->wiphy, dev, idx, addr, params); - if (err) - return err; + if (err) + return err; + + if (!addr) { + wdev->wext.keys->params[idx] = *params; + memcpy(wdev->wext.keys->data[idx], + params->key, params->key_len); + wdev->wext.keys->params[idx].key = + wdev->wext.keys->data[idx]; + } - if (tx_key || (!addr && wdev->wext.default_key == -1)) { + if (params->cipher != WLAN_CIPHER_SUITE_AES_CMAC && + (tx_key || (!addr && wdev->wext.default_key == -1))) { + if (wdev->current_bss) err = rdev->ops->set_default_key(&rdev->wiphy, dev, idx); - if (!err) - wdev->wext.default_key = idx; - return err; - } + if (!err) + wdev->wext.default_key = idx; + return err; + } - if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC && - (tx_key || (!addr && wdev->wext.default_mgmt_key == -1))) { + if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC && + (tx_key || (!addr && wdev->wext.default_mgmt_key == -1))) { + if (wdev->current_bss) err = rdev->ops->set_default_mgmt_key(&rdev->wiphy, dev, idx); - if (!err) - wdev->wext.default_mgmt_key = idx; - return err; - } - - return 0; + if (!err) + wdev->wext.default_mgmt_key = idx; + return err; } + + return 0; +} + +static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *addr, + bool remove, bool tx_key, int idx, + struct key_params *params) +{ + int err; + + wdev_lock(dev->ieee80211_ptr); + err = __cfg80211_set_encryption(rdev, dev, addr, remove, + tx_key, idx, params); + wdev_unlock(dev->ieee80211_ptr); + + return err; } int cfg80211_wext_siwencode(struct net_device *dev, @@ -528,6 +579,10 @@ int cfg80211_wext_siwencode(struct net_device *dev, bool remove = false; struct key_params params; + if (wdev->iftype != NL80211_IFTYPE_STATION && + wdev->iftype != NL80211_IFTYPE_ADHOC) + return -EOPNOTSUPP; + /* no use -- only MFP (set_default_mgmt_key) is optional */ if (!rdev->ops->del_key || !rdev->ops->add_key || @@ -548,9 +603,14 @@ int cfg80211_wext_siwencode(struct net_device *dev, remove = true; else if (erq->length == 0) { /* No key data - just set the default TX key index */ - err = rdev->ops->set_default_key(&rdev->wiphy, dev, idx); + err = 0; + wdev_lock(wdev); + if (wdev->current_bss) + err = rdev->ops->set_default_key(&rdev->wiphy, + dev, idx); if (!err) wdev->wext.default_key = idx; + wdev_unlock(wdev); return err; } @@ -583,6 +643,10 @@ int cfg80211_wext_siwencodeext(struct net_device *dev, struct key_params params; u32 cipher; + if (wdev->iftype != NL80211_IFTYPE_STATION && + wdev->iftype != NL80211_IFTYPE_ADHOC) + return -EOPNOTSUPP; + /* no use -- only MFP (set_default_mgmt_key) is optional */ if (!rdev->ops->del_key || !rdev->ops->add_key || @@ -656,37 +720,15 @@ int cfg80211_wext_siwencodeext(struct net_device *dev, } EXPORT_SYMBOL_GPL(cfg80211_wext_siwencodeext); -struct giwencode_cookie { - size_t buflen; - char *keybuf; -}; - -static void giwencode_get_key_cb(void *cookie, struct key_params *params) -{ - struct giwencode_cookie *data = cookie; - - if (!params->key) { - data->buflen = 0; - return; - } - - data->buflen = min_t(size_t, data->buflen, params->key_len); - memcpy(data->keybuf, params->key, data->buflen); -} - int cfg80211_wext_giwencode(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *keybuf) { struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - int idx, err; - struct giwencode_cookie data = { - .keybuf = keybuf, - .buflen = erq->length, - }; + int idx; - if (!rdev->ops->get_key) + if (wdev->iftype != NL80211_IFTYPE_STATION && + wdev->iftype != NL80211_IFTYPE_ADHOC) return -EOPNOTSUPP; idx = erq->flags & IW_ENCODE_INDEX; @@ -701,21 +743,18 @@ int cfg80211_wext_giwencode(struct net_device *dev, erq->flags = idx + 1; - err = rdev->ops->get_key(&rdev->wiphy, dev, idx, NULL, &data, - giwencode_get_key_cb); - if (!err) { - erq->length = data.buflen; - erq->flags |= IW_ENCODE_ENABLED; - return 0; - } - - if (err == -ENOENT) { + if (!wdev->wext.keys || !wdev->wext.keys->params[idx].cipher) { erq->flags |= IW_ENCODE_DISABLED; erq->length = 0; return 0; } - return err; + erq->length = min_t(size_t, erq->length, + wdev->wext.keys->params[idx].key_len); + memcpy(keybuf, wdev->wext.keys->params[idx].key, erq->length); + erq->flags |= IW_ENCODE_ENABLED; + + return 0; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwencode); diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 6f75aaa7f795..c33ea9a5de78 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -10,10 +10,11 @@ #include #include "nl80211.h" -static int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev) +int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) { - int err; + struct cfg80211_cached_keys *ck = NULL; + int err, i; ASSERT_RDEV_LOCK(rdev); ASSERT_WDEV_LOCK(wdev); @@ -25,10 +26,25 @@ static int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, wdev->wext.connect.ie_len = wdev->wext.ie_len; wdev->wext.connect.privacy = wdev->wext.default_key != -1; - err = 0; - if (wdev->wext.connect.ssid_len != 0) - err = __cfg80211_connect(rdev, wdev->netdev, - &wdev->wext.connect); + if (wdev->wext.keys) { + wdev->wext.keys->def = wdev->wext.default_key; + wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key; + } + + if (!wdev->wext.connect.ssid_len) + return 0; + + if (wdev->wext.keys) { + ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL); + if (!ck) + return -ENOMEM; + for (i = 0; i < 6; i++) + ck->params[i].key = ck->data[i]; + } + err = __cfg80211_connect(rdev, wdev->netdev, + &wdev->wext.connect, ck); + if (err) + kfree(ck); return err; } -- cgit v1.2.3 From 463d018323851a608eef52a9427b0585005c647f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 14 Jul 2009 00:33:35 +0200 Subject: cfg80211: make aware of net namespaces In order to make cfg80211/nl80211 aware of network namespaces, we have to do the following things: * del_virtual_intf method takes an interface index rather than a netdev pointer - simply change this * nl80211 uses init_net a lot, it changes to use the sender's network namespace * scan requests use the interface index, hold a netdev pointer and reference instead * we want a wiphy and its associated virtual interfaces to be in one netns together, so - we need to be able to change ns for a given interface, so export dev_change_net_namespace() - for each virtual interface set the NETIF_F_NETNS_LOCAL flag, and clear that flag only when the wiphy changes ns, to disallow breaking this invariant * when a network namespace goes away, we need to reparent the wiphy to init_net * cfg80211 users that support creating virtual interfaces must create them in the wiphy's namespace, currently this affects only mac80211 The end result is that you can now switch an entire wiphy into a different network namespace with the new command iw phy# set netns and all virtual interfaces will follow (or the operation fails). Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 9 +++ include/net/cfg80211.h | 40 +++++++++- net/core/dev.c | 1 + net/mac80211/cfg.c | 14 +--- net/wireless/core.c | 75 ++++++++++++++++-- net/wireless/core.h | 5 +- net/wireless/nl80211.c | 202 +++++++++++++++++++++++++++++++++--------------- net/wireless/scan.c | 22 ++---- net/wireless/sme.c | 3 +- 9 files changed, 272 insertions(+), 99 deletions(-) (limited to 'net/wireless/core.h') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 962e2232a074..cb3dc6027fd9 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -262,6 +262,9 @@ * reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and * %NL80211_ATTR_REASON_CODE attributes are used. * + * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices + * associated with this wiphy must be down and will follow. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -336,6 +339,8 @@ enum nl80211_commands { NL80211_CMD_ROAM, NL80211_CMD_DISCONNECT, + NL80211_CMD_SET_WIPHY_NETNS, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -573,6 +578,8 @@ enum nl80211_commands { * and join_ibss(), key information is in a nested attribute each * with %NL80211_KEY_* sub-attributes * + * @NL80211_ATTR_PID: Process ID of a network namespace. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -701,6 +708,8 @@ enum nl80211_attrs { NL80211_ATTR_KEY, NL80211_ATTR_KEYS, + NL80211_ATTR_PID, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index a981ca8a5701..0d278777e39c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -542,7 +542,7 @@ struct cfg80211_ssid { * @ie: optional information element(s) to add into Probe Request or %NULL * @ie_len: length of ie in octets * @wiphy: the wiphy this was for - * @ifidx: the interface index + * @dev: the interface */ struct cfg80211_scan_request { struct cfg80211_ssid *ssids; @@ -554,7 +554,7 @@ struct cfg80211_scan_request { /* internal */ struct wiphy *wiphy; - int ifidx; + struct net_device *dev; bool aborted; }; @@ -845,7 +845,8 @@ struct cfg80211_bitrate_mask { * @resume: wiphy device needs to be resumed * * @add_virtual_intf: create a new virtual interface with the given name, - * must set the struct wireless_dev's iftype. + * must set the struct wireless_dev's iftype. Beware: You must create + * the new netdev in the wiphy's network namespace! * * @del_virtual_intf: remove the virtual interface determined by ifindex. * @@ -937,7 +938,7 @@ struct cfg80211_ops { int (*add_virtual_intf)(struct wiphy *wiphy, char *name, enum nl80211_iftype type, u32 *flags, struct vif_params *params); - int (*del_virtual_intf)(struct wiphy *wiphy, int ifindex); + int (*del_virtual_intf)(struct wiphy *wiphy, struct net_device *dev); int (*change_virtual_intf)(struct wiphy *wiphy, struct net_device *dev, enum nl80211_iftype type, u32 *flags, @@ -1088,6 +1089,9 @@ struct cfg80211_ops { * @frag_threshold: Fragmentation threshold (dot11FragmentationThreshold); * -1 = fragmentation disabled, only odd values >= 256 used * @rts_threshold: RTS threshold (dot11RTSThreshold); -1 = RTS/CTS disabled + * @net: the network namespace this wiphy currently lives in + * @netnsok: if set to false, do not allow changing the netns of this + * wiphy at all */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -1101,6 +1105,8 @@ struct wiphy { bool custom_regulatory; bool strict_regulatory; + bool netnsok; + enum cfg80211_signal_type signal_type; int bss_priv_size; @@ -1139,9 +1145,35 @@ struct wiphy { /* dir in debugfs: ieee80211/ */ struct dentry *debugfsdir; +#ifdef CONFIG_NET_NS + /* the network namespace this phy lives in currently */ + struct net *_net; +#endif + char priv[0] __attribute__((__aligned__(NETDEV_ALIGN))); }; +#ifdef CONFIG_NET_NS +static inline struct net *wiphy_net(struct wiphy *wiphy) +{ + return wiphy->_net; +} + +static inline void wiphy_net_set(struct wiphy *wiphy, struct net *net) +{ + wiphy->_net = net; +} +#else +static inline struct net *wiphy_net(struct wiphy *wiphy) +{ + return &init_net; +} + +static inline void wiphy_net_set(struct wiphy *wiphy, struct net *net) +{ +} +#endif + /** * wiphy_priv - return priv from wiphy * diff --git a/net/core/dev.c b/net/core/dev.c index d6c657ee413d..71347668c506 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5344,6 +5344,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char out: return err; } +EXPORT_SYMBOL_GPL(dev_change_net_namespace); static int dev_cpu_callback(struct notifier_block *nfb, unsigned long action, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 52928ad90570..4bbf5007799b 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -57,19 +57,9 @@ static int ieee80211_add_iface(struct wiphy *wiphy, char *name, return 0; } -static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex) +static int ieee80211_del_iface(struct wiphy *wiphy, struct net_device *dev) { - struct net_device *dev; - struct ieee80211_sub_if_data *sdata; - - /* we're under RTNL */ - dev = __dev_get_by_index(&init_net, ifindex); - if (!dev) - return -ENODEV; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - ieee80211_if_remove(sdata); + ieee80211_if_remove(IEEE80211_DEV_TO_SUB_IF(dev)); return 0; } diff --git a/net/wireless/core.c b/net/wireless/core.c index 6891cd0e38d5..442c9f389799 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -106,7 +106,7 @@ __cfg80211_rdev_from_info(struct genl_info *info) if (info->attrs[NL80211_ATTR_IFINDEX]) { ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); - dev = dev_get_by_index(&init_net, ifindex); + dev = dev_get_by_index(genl_info_net(info), ifindex); if (dev) { if (dev->ieee80211_ptr) byifidx = @@ -151,13 +151,13 @@ cfg80211_get_dev_from_info(struct genl_info *info) } struct cfg80211_registered_device * -cfg80211_get_dev_from_ifindex(int ifindex) +cfg80211_get_dev_from_ifindex(struct net *net, int ifindex) { struct cfg80211_registered_device *rdev = ERR_PTR(-ENODEV); struct net_device *dev; mutex_lock(&cfg80211_mutex); - dev = dev_get_by_index(&init_net, ifindex); + dev = dev_get_by_index(net, ifindex); if (!dev) goto out; if (dev->ieee80211_ptr) { @@ -222,6 +222,42 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, return 0; } +int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, + struct net *net) +{ + struct wireless_dev *wdev; + int err = 0; + + if (!rdev->wiphy.netnsok) + return -EOPNOTSUPP; + + list_for_each_entry(wdev, &rdev->netdev_list, list) { + wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; + err = dev_change_net_namespace(wdev->netdev, net, "wlan%d"); + if (err) + break; + wdev->netdev->features |= NETIF_F_NETNS_LOCAL; + } + + if (err) { + /* failed -- clean up to old netns */ + net = wiphy_net(&rdev->wiphy); + + list_for_each_entry_continue_reverse(wdev, &rdev->netdev_list, + list) { + wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; + err = dev_change_net_namespace(wdev->netdev, net, + "wlan%d"); + WARN_ON(err); + wdev->netdev->features |= NETIF_F_NETNS_LOCAL; + } + } + + wiphy_net_set(&rdev->wiphy, net); + + return err; +} + static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data) { struct cfg80211_registered_device *rdev = data; @@ -375,6 +411,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) rdev->wiphy.dev.class = &ieee80211_class; rdev->wiphy.dev.platform_data = rdev; + wiphy_net_set(&rdev->wiphy, &init_net); + rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block; rdev->rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev), &rdev->wiphy.dev, RFKILL_TYPE_WLAN, @@ -615,6 +653,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, spin_lock_init(&wdev->event_lock); mutex_lock(&rdev->devlist_mtx); list_add(&wdev->list, &rdev->netdev_list); + /* can only change netns with wiphy */ + dev->features |= NETIF_F_NETNS_LOCAL; + if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj, "phy80211")) { printk(KERN_ERR "wireless: failed to add phy80211 " @@ -705,10 +746,32 @@ static struct notifier_block cfg80211_netdev_notifier = { .notifier_call = cfg80211_netdev_notifier_call, }; -static int cfg80211_init(void) +static void __net_exit cfg80211_pernet_exit(struct net *net) +{ + struct cfg80211_registered_device *rdev; + + rtnl_lock(); + mutex_lock(&cfg80211_mutex); + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (net_eq(wiphy_net(&rdev->wiphy), net)) + WARN_ON(cfg80211_switch_netns(rdev, &init_net)); + } + mutex_unlock(&cfg80211_mutex); + rtnl_unlock(); +} + +static struct pernet_operations cfg80211_pernet_ops = { + .exit = cfg80211_pernet_exit, +}; + +static int __init cfg80211_init(void) { int err; + err = register_pernet_device(&cfg80211_pernet_ops); + if (err) + goto out_fail_pernet; + err = wiphy_sysfs_init(); if (err) goto out_fail_sysfs; @@ -736,9 +799,10 @@ out_fail_nl80211: out_fail_notifier: wiphy_sysfs_exit(); out_fail_sysfs: + unregister_pernet_device(&cfg80211_pernet_ops); +out_fail_pernet: return err; } - subsys_initcall(cfg80211_init); static void cfg80211_exit(void) @@ -748,5 +812,6 @@ static void cfg80211_exit(void) unregister_netdevice_notifier(&cfg80211_netdev_notifier); wiphy_sysfs_exit(); regulatory_exit(); + unregister_pernet_device(&cfg80211_pernet_ops); } module_exit(cfg80211_exit); diff --git a/net/wireless/core.h b/net/wireless/core.h index 2ec8ddbe57de..4276b70cd975 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -170,7 +170,10 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx); /* identical to cfg80211_get_dev_from_info but only operate on ifindex */ extern struct cfg80211_registered_device * -cfg80211_get_dev_from_ifindex(int ifindex); +cfg80211_get_dev_from_ifindex(struct net *net, int ifindex); + +int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, + struct net *net); static inline void cfg80211_lock_rdev(struct cfg80211_registered_device *rdev) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index da450ef1fc7e..7880a9c4cdda 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -14,8 +14,10 @@ #include #include #include +#include #include #include +#include #include "core.h" #include "nl80211.h" #include "reg.h" @@ -27,24 +29,26 @@ static struct genl_family nl80211_fam = { .hdrsize = 0, /* no private header */ .version = 1, /* no particular meaning now */ .maxattr = NL80211_ATTR_MAX, + .netnsok = true, }; /* internal helper: get rdev and dev */ -static int get_rdev_dev_by_info_ifindex(struct nlattr **attrs, +static int get_rdev_dev_by_info_ifindex(struct genl_info *info, struct cfg80211_registered_device **rdev, struct net_device **dev) { + struct nlattr **attrs = info->attrs; int ifindex; if (!attrs[NL80211_ATTR_IFINDEX]) return -EINVAL; ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]); - *dev = dev_get_by_index(&init_net, ifindex); + *dev = dev_get_by_index(genl_info_net(info), ifindex); if (!*dev) return -ENODEV; - *rdev = cfg80211_get_dev_from_ifindex(ifindex); + *rdev = cfg80211_get_dev_from_ifindex(genl_info_net(info), ifindex); if (IS_ERR(*rdev)) { dev_put(*dev); return PTR_ERR(*rdev); @@ -133,6 +137,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG }, [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, + [NL80211_ATTR_PID] = { .type = NLA_U32 }, }; /* policy for the attributes */ @@ -532,6 +537,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, CMD(deauth, DEAUTHENTICATE); CMD(disassoc, DISASSOCIATE); CMD(join_ibss, JOIN_IBSS); + if (dev->wiphy.netnsok) { + i++; + NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); + } #undef CMD @@ -562,6 +571,8 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) mutex_lock(&cfg80211_mutex); list_for_each_entry(dev, &cfg80211_rdev_list, list) { + if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk))) + continue; if (++idx <= start) continue; if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid, @@ -867,6 +878,8 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * mutex_lock(&cfg80211_mutex); list_for_each_entry(dev, &cfg80211_rdev_list, list) { + if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk))) + continue; if (wp_idx < wp_start) { wp_idx++; continue; @@ -907,7 +920,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) struct net_device *netdev; int err; - err = get_rdev_dev_by_info_ifindex(info->attrs, &dev, &netdev); + err = get_rdev_dev_by_info_ifindex(info, &dev, &netdev); if (err) return err; @@ -975,7 +988,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1098,26 +1111,25 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev; - int ifindex, err; + int err; struct net_device *dev; rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; - ifindex = dev->ifindex; - dev_put(dev); if (!rdev->ops->del_virtual_intf) { err = -EOPNOTSUPP; goto out; } - err = rdev->ops->del_virtual_intf(&rdev->wiphy, ifindex); + err = rdev->ops->del_virtual_intf(&rdev->wiphy, dev); out: cfg80211_unlock_rdev(rdev); + dev_put(dev); unlock_rtnl: rtnl_unlock(); return err; @@ -1195,7 +1207,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1274,7 +1286,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1333,7 +1345,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1380,7 +1392,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1429,7 +1441,7 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1516,7 +1528,7 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1726,13 +1738,13 @@ static int nl80211_dump_station(struct sk_buff *skb, rtnl_lock(); - netdev = __dev_get_by_index(&init_net, ifidx); + netdev = __dev_get_by_index(sock_net(skb->sk), ifidx); if (!netdev) { err = -ENODEV; goto out_rtnl; } - dev = cfg80211_get_dev_from_ifindex(ifidx); + dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx); if (IS_ERR(dev)) { err = PTR_ERR(dev); goto out_rtnl; @@ -1791,7 +1803,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -1829,14 +1841,16 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) /* * Get vlan interface making sure it is on the right wiphy. */ -static int get_vlan(struct nlattr *vlanattr, +static int get_vlan(struct genl_info *info, struct cfg80211_registered_device *rdev, struct net_device **vlan) { + struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN]; *vlan = NULL; if (vlanattr) { - *vlan = dev_get_by_index(&init_net, nla_get_u32(vlanattr)); + *vlan = dev_get_by_index(genl_info_net(info), + nla_get_u32(vlanattr)); if (!*vlan) return -ENODEV; if (!(*vlan)->ieee80211_ptr) @@ -1891,11 +1905,11 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; - err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], rdev, ¶ms.vlan); + err = get_vlan(info, rdev, ¶ms.vlan); if (err) goto out; @@ -2004,11 +2018,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; - err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], rdev, ¶ms.vlan); + err = get_vlan(info, rdev, ¶ms.vlan); if (err) goto out; @@ -2079,7 +2093,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2185,13 +2199,13 @@ static int nl80211_dump_mpath(struct sk_buff *skb, rtnl_lock(); - netdev = __dev_get_by_index(&init_net, ifidx); + netdev = __dev_get_by_index(sock_net(skb->sk), ifidx); if (!netdev) { err = -ENODEV; goto out_rtnl; } - dev = cfg80211_get_dev_from_ifindex(ifidx); + dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx); if (IS_ERR(dev)) { err = PTR_ERR(dev); goto out_rtnl; @@ -2255,7 +2269,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2314,7 +2328,7 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2362,7 +2376,7 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2404,7 +2418,7 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2455,7 +2469,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2574,7 +2588,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, rtnl_lock(); /* Look up our device */ - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2691,7 +2705,7 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2947,7 +2961,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -3069,14 +3083,16 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) request->ie_len); } - request->ifidx = dev->ifindex; + request->dev = dev; request->wiphy = &rdev->wiphy; rdev->scan_req = request; err = rdev->ops->scan(&rdev->wiphy, dev, request); - if (!err) + if (!err) { nl80211_send_scan_start(rdev, dev); + dev_hold(dev); + } out_free: if (err) { @@ -3198,11 +3214,11 @@ static int nl80211_dump_scan(struct sk_buff *skb, cb->args[0] = ifidx; } - dev = dev_get_by_index(&init_net, ifidx); + dev = dev_get_by_index(sock_net(skb->sk), ifidx); if (!dev) return -ENODEV; - rdev = cfg80211_get_dev_from_ifindex(ifidx); + rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx); if (IS_ERR(rdev)) { err = PTR_ERR(rdev); goto out_put_netdev; @@ -3312,7 +3328,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3448,7 +3464,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3531,7 +3547,7 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3593,7 +3609,7 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3666,7 +3682,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3739,7 +3755,7 @@ static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3924,7 +3940,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) return err; rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -4000,7 +4016,7 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -4024,6 +4040,47 @@ unlock_rtnl: return err; } +static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev; + struct net *net; + int err; + u32 pid; + + if (!info->attrs[NL80211_ATTR_PID]) + return -EINVAL; + + pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]); + + rtnl_lock(); + + rdev = cfg80211_get_dev_from_info(info); + if (IS_ERR(rdev)) { + err = PTR_ERR(rdev); + goto out; + } + + net = get_net_ns_by_pid(pid); + if (IS_ERR(net)) { + err = PTR_ERR(net); + goto out; + } + + err = 0; + + /* check if anything to do */ + if (net_eq(wiphy_net(&rdev->wiphy), net)) + goto out_put_net; + + err = cfg80211_switch_netns(rdev, net); + out_put_net: + put_net(net); + out: + cfg80211_unlock_rdev(rdev); + rtnl_unlock(); + return err; +} + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -4257,6 +4314,12 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, + { + .cmd = NL80211_CMD_SET_WIPHY_NETNS, + .doit = nl80211_wiphy_netns, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { .name = "mlme", @@ -4288,7 +4351,8 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev) return; } - genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_config_mcgrp.id, GFP_KERNEL); } static int nl80211_add_scan_req(struct sk_buff *msg, @@ -4365,7 +4429,8 @@ void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_scan_mcgrp.id, GFP_KERNEL); } void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, @@ -4383,7 +4448,8 @@ void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_scan_mcgrp.id, GFP_KERNEL); } void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, @@ -4401,7 +4467,8 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_scan_mcgrp.id, GFP_KERNEL); } /* @@ -4450,7 +4517,10 @@ void nl80211_send_reg_change_event(struct regulatory_request *request) return; } - genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_KERNEL); + rtnl_lock(); + genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id, + GFP_KERNEL); + rtnl_unlock(); return; @@ -4486,7 +4556,8 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -4553,7 +4624,8 @@ static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -4611,7 +4683,8 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -4651,7 +4724,8 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -4691,7 +4765,8 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, GFP_KERNEL); return; nla_put_failure: @@ -4726,7 +4801,8 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -4766,7 +4842,8 @@ void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -4819,7 +4896,10 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy, return; } - genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_ATOMIC); + rcu_read_lock(); + genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id, + GFP_ATOMIC); + rcu_read_unlock(); return; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index decc59fe0ee8..1b578b8cb1c9 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -32,9 +32,7 @@ void __cfg80211_scan_done(struct work_struct *wk) mutex_lock(&rdev->mtx); request = rdev->scan_req; - dev = dev_get_by_index(&init_net, request->ifidx); - if (!dev) - goto out; + dev = request->dev; /* * This must be before sending the other events! @@ -58,7 +56,6 @@ void __cfg80211_scan_done(struct work_struct *wk) dev_put(dev); - out: cfg80211_unlock_rdev(rdev); wiphy_to_dev(request->wiphy)->scan_req = NULL; kfree(request); @@ -66,17 +63,10 @@ void __cfg80211_scan_done(struct work_struct *wk) void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) { - struct net_device *dev = dev_get_by_index(&init_net, request->ifidx); - if (WARN_ON(!dev)) { - kfree(request); - return; - } - WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); request->aborted = aborted; schedule_work(&wiphy_to_dev(request->wiphy)->scan_done_wk); - dev_put(dev); } EXPORT_SYMBOL(cfg80211_scan_done); @@ -592,7 +582,7 @@ int cfg80211_wext_siwscan(struct net_device *dev, if (!netif_running(dev)) return -ENETDOWN; - rdev = cfg80211_get_dev_from_ifindex(dev->ifindex); + rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex); if (IS_ERR(rdev)) return PTR_ERR(rdev); @@ -617,7 +607,7 @@ int cfg80211_wext_siwscan(struct net_device *dev, } creq->wiphy = wiphy; - creq->ifidx = dev->ifindex; + creq->dev = dev; creq->ssids = (void *)(creq + 1); creq->channels = (void *)(creq->ssids + 1); creq->n_channels = n_channels; @@ -654,8 +644,10 @@ int cfg80211_wext_siwscan(struct net_device *dev, if (err) { rdev->scan_req = NULL; kfree(creq); - } else + } else { nl80211_send_scan_start(rdev, dev); + dev_hold(dev); + } out: cfg80211_unlock_rdev(rdev); return err; @@ -948,7 +940,7 @@ int cfg80211_wext_giwscan(struct net_device *dev, if (!netif_running(dev)) return -ENETDOWN; - rdev = cfg80211_get_dev_from_ifindex(dev->ifindex); + rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex); if (IS_ERR(rdev)) return PTR_ERR(rdev); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 82de2d9795f4..a19741097989 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -86,7 +86,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) wdev->conn->params.ssid_len); request->ssids[0].ssid_len = wdev->conn->params.ssid_len; - request->ifidx = wdev->netdev->ifindex; + request->dev = wdev->netdev; request->wiphy = &rdev->wiphy; rdev->scan_req = request; @@ -95,6 +95,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) if (!err) { wdev->conn->state = CFG80211_CONN_SCANNING; nl80211_send_scan_start(rdev, wdev->netdev); + dev_hold(wdev->netdev); } else { rdev->scan_req = NULL; kfree(request); -- cgit v1.2.3 From 0e82ffe3b90bcad72cfe80e4379946b8fb0691ca Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 27 Jul 2009 12:01:50 +0200 Subject: cfg80211: combine iwfreq implementations Until now we implemented iwfreq for managed mode, we needed to keep the implementations separate, but now that we have all versions implemented we can combine them and export just one handler. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwmc3200wifi/wext.c | 34 +-------------- include/net/cfg80211.h | 20 +++------ net/mac80211/wext.c | 73 +------------------------------- net/wireless/core.h | 3 ++ net/wireless/ibss.c | 5 +-- net/wireless/nl80211.c | 2 + net/wireless/wext-compat.c | 54 ++++++++++++++++++++++- net/wireless/wext-compat.h | 21 +++++++++ net/wireless/wext-sme.c | 5 +-- 9 files changed, 91 insertions(+), 126 deletions(-) create mode 100644 net/wireless/wext-compat.h (limited to 'net/wireless/core.h') diff --git a/drivers/net/wireless/iwmc3200wifi/wext.c b/drivers/net/wireless/iwmc3200wifi/wext.c index c3c90d5963bf..8058e9991c30 100644 --- a/drivers/net/wireless/iwmc3200wifi/wext.c +++ b/drivers/net/wireless/iwmc3200wifi/wext.c @@ -27,36 +27,6 @@ #include "iwm.h" #include "commands.h" -static int iwm_wext_siwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra) -{ - struct iwm_priv *iwm = ndev_to_iwm(dev); - - switch (iwm->conf.mode) { - case UMAC_MODE_IBSS: - return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra); - default: - return -EOPNOTSUPP; - } -} - -static int iwm_wext_giwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra) -{ - struct iwm_priv *iwm = ndev_to_iwm(dev); - - switch (iwm->conf.mode) { - case UMAC_MODE_IBSS: - return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); - case UMAC_MODE_BSS: - return cfg80211_mgd_wext_giwfreq(dev, info, freq, extra); - default: - return -EOPNOTSUPP; - } -} - static int iwm_wext_siwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) { @@ -125,8 +95,8 @@ static const iw_handler iwm_handlers[] = (iw_handler) cfg80211_wext_giwname, /* SIOCGIWNAME */ (iw_handler) NULL, /* SIOCSIWNWID */ (iw_handler) NULL, /* SIOCGIWNWID */ - (iw_handler) iwm_wext_siwfreq, /* SIOCSIWFREQ */ - (iw_handler) iwm_wext_giwfreq, /* SIOCGIWFREQ */ + (iw_handler) cfg80211_wext_siwfreq, /* SIOCSIWFREQ */ + (iw_handler) cfg80211_wext_giwfreq, /* SIOCGIWFREQ */ (iw_handler) cfg80211_wext_siwmode, /* SIOCSIWMODE */ (iw_handler) cfg80211_wext_giwmode, /* SIOCGIWMODE */ (iw_handler) NULL, /* SIOCSIWSENS */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0d278777e39c..5d249c4bf225 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1595,12 +1595,6 @@ int cfg80211_wext_siwmlme(struct net_device *dev, int cfg80211_wext_giwrange(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra); -int cfg80211_ibss_wext_siwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra); -int cfg80211_ibss_wext_giwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra); int cfg80211_ibss_wext_siwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid); @@ -1614,12 +1608,6 @@ int cfg80211_ibss_wext_giwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra); -int cfg80211_mgd_wext_siwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra); -int cfg80211_mgd_wext_giwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra); int cfg80211_mgd_wext_siwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid); @@ -1642,8 +1630,12 @@ int cfg80211_wext_giwauth(struct net_device *dev, struct iw_request_info *info, struct iw_param *data, char *extra); -struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy, - struct iw_freq *freq); +int cfg80211_wext_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra); +int cfg80211_wext_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra); int cfg80211_wext_siwrate(struct net_device *dev, struct iw_request_info *info, diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 5acb8140ee58..7cd9aa79ef52 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -27,75 +27,6 @@ #include "aes_ccm.h" -static int ieee80211_ioctl_siwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct ieee80211_local *local = sdata->local; - struct ieee80211_channel *chan; - - if (sdata->vif.type == NL80211_IFTYPE_ADHOC) - return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra); - else if (sdata->vif.type == NL80211_IFTYPE_STATION) - return cfg80211_mgd_wext_siwfreq(dev, info, freq, extra); - - /* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */ - if (freq->e == 0) { - if (freq->m < 0) - return -EINVAL; - else - chan = ieee80211_get_channel(local->hw.wiphy, - ieee80211_channel_to_frequency(freq->m)); - } else { - int i, div = 1000000; - for (i = 0; i < freq->e; i++) - div /= 10; - if (div <= 0) - return -EINVAL; - chan = ieee80211_get_channel(local->hw.wiphy, freq->m / div); - } - - if (!chan) - return -EINVAL; - - if (chan->flags & IEEE80211_CHAN_DISABLED) - return -EINVAL; - - /* - * no change except maybe auto -> fixed, ignore the HT - * setting so you can fix a channel you're on already - */ - if (local->oper_channel == chan) - return 0; - - local->oper_channel = chan; - local->oper_channel_type = NL80211_CHAN_NO_HT; - ieee80211_hw_config(local, 0); - - return 0; -} - - -static int ieee80211_ioctl_giwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra) -{ - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - if (sdata->vif.type == NL80211_IFTYPE_ADHOC) - return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); - else if (sdata->vif.type == NL80211_IFTYPE_STATION) - return cfg80211_mgd_wext_giwfreq(dev, info, freq, extra); - - freq->m = local->oper_channel->center_freq; - freq->e = 6; - - return 0; -} - - static int ieee80211_ioctl_siwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid) @@ -173,8 +104,8 @@ static const iw_handler ieee80211_handler[] = (iw_handler) cfg80211_wext_giwname, /* SIOCGIWNAME */ (iw_handler) NULL, /* SIOCSIWNWID */ (iw_handler) NULL, /* SIOCGIWNWID */ - (iw_handler) ieee80211_ioctl_siwfreq, /* SIOCSIWFREQ */ - (iw_handler) ieee80211_ioctl_giwfreq, /* SIOCGIWFREQ */ + (iw_handler) cfg80211_wext_siwfreq, /* SIOCSIWFREQ */ + (iw_handler) cfg80211_wext_giwfreq, /* SIOCGIWFREQ */ (iw_handler) cfg80211_wext_siwmode, /* SIOCSIWMODE */ (iw_handler) cfg80211_wext_giwmode, /* SIOCGIWMODE */ (iw_handler) NULL, /* SIOCSIWSENS */ diff --git a/net/wireless/core.h b/net/wireless/core.h index 4276b70cd975..6d903c1d721d 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -66,6 +66,9 @@ struct cfg80211_registered_device { struct work_struct conn_work; struct work_struct event_work; + /* current channel */ + struct ieee80211_channel *channel; + #ifdef CONFIG_CFG80211_DEBUGFS /* Debugfs entries */ struct wiphy_debugfsdentries { diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 8b65e212ae49..de9ac49cd907 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -7,6 +7,7 @@ #include #include #include +#include "wext-compat.h" #include "nl80211.h" @@ -312,8 +313,6 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, return err; } -/* temporary symbol - mark GPL - in the future the handler won't be */ -EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwfreq); int cfg80211_ibss_wext_giwfreq(struct net_device *dev, struct iw_request_info *info, @@ -342,8 +341,6 @@ int cfg80211_ibss_wext_giwfreq(struct net_device *dev, /* no channel if not joining */ return -EINVAL; } -/* temporary symbol - mark GPL - in the future the handler won't be */ -EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwfreq); int cfg80211_ibss_wext_siwessid(struct net_device *dev, struct iw_request_info *info, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c951eb2b07d5..0cd548267d4a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -757,6 +757,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) channel_type); if (result) goto bad_res; + + rdev->channel = chan; } changed = 0; diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index c7351a98e660..fc2e7768967d 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -14,6 +14,7 @@ #include #include #include +#include "wext-compat.h" #include "core.h" int cfg80211_wext_giwname(struct net_device *dev, @@ -300,7 +301,6 @@ struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy, return ERR_PTR(-EINVAL); return chan; } -EXPORT_SYMBOL_GPL(cfg80211_wext_freq); int cfg80211_wext_siwrts(struct net_device *dev, struct iw_request_info *info, @@ -759,6 +759,58 @@ int cfg80211_wext_giwencode(struct net_device *dev, } EXPORT_SYMBOL_GPL(cfg80211_wext_giwencode); +int cfg80211_wext_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct ieee80211_channel *chan; + int err; + + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + return cfg80211_mgd_wext_siwfreq(dev, info, freq, extra); + case NL80211_IFTYPE_ADHOC: + return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra); + default: + chan = cfg80211_wext_freq(wdev->wiphy, freq); + if (!chan) + return -EINVAL; + if (IS_ERR(chan)) + return PTR_ERR(chan); + err = rdev->ops->set_channel(wdev->wiphy, chan, + NL80211_CHAN_NO_HT); + if (err) + return err; + rdev->channel = chan; + return 0; + } +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwfreq); + +int cfg80211_wext_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + return cfg80211_mgd_wext_giwfreq(dev, info, freq, extra); + case NL80211_IFTYPE_ADHOC: + return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); + default: + if (!rdev->channel) + return -EINVAL; + freq->m = rdev->channel->center_freq; + freq->e = 6; + return 0; + } +} +EXPORT_SYMBOL_GPL(cfg80211_wext_giwfreq); + int cfg80211_wext_siwtxpower(struct net_device *dev, struct iw_request_info *info, union iwreq_data *data, char *extra) diff --git a/net/wireless/wext-compat.h b/net/wireless/wext-compat.h new file mode 100644 index 000000000000..23a6b5a83f2d --- /dev/null +++ b/net/wireless/wext-compat.h @@ -0,0 +1,21 @@ +#ifndef __WEXT_COMPAT +#define __WEXT_COMPAT + +int cfg80211_ibss_wext_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra); +int cfg80211_ibss_wext_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra); + +int cfg80211_mgd_wext_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra); +int cfg80211_mgd_wext_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra); + +struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy, + struct iw_freq *freq); + +#endif /* __WEXT_COMPAT */ diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 4c689fd865b0..509279a1cfb2 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -8,6 +8,7 @@ #include #include #include +#include "wext-compat.h" #include "nl80211.h" int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, @@ -108,8 +109,6 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, cfg80211_unlock_rdev(rdev); return err; } -/* temporary symbol - mark GPL - in the future the handler won't be */ -EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwfreq); int cfg80211_mgd_wext_giwfreq(struct net_device *dev, struct iw_request_info *info, @@ -138,8 +137,6 @@ int cfg80211_mgd_wext_giwfreq(struct net_device *dev, /* no channel if not joining */ return -EINVAL; } -/* temporary symbol - mark GPL - in the future the handler won't be */ -EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwfreq); int cfg80211_mgd_wext_siwessid(struct net_device *dev, struct iw_request_info *info, -- cgit v1.2.3 From df7fc0f9735085bb617fff774bfd71465edb448c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 29 Jul 2009 11:23:49 +0200 Subject: cfg80211: keep track of current_bss for userspace SME When a userspace SME is active, we're currently not keeping track of the BSS properly for reporting the current link and for internal use. Additionally, it looks like there is a possible BSS leak in that the BSS never gets removed from auth_bsses[]. To fix it, pass the BSS struct to __cfg80211_connect_result in this case. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 3 ++- net/wireless/core.h | 8 +++++++- net/wireless/mlme.c | 38 ++++++++++++++++++++++++-------------- net/wireless/sme.c | 35 ++++++++++++++++++----------------- 4 files changed, 51 insertions(+), 33 deletions(-) (limited to 'net/wireless/core.h') diff --git a/net/wireless/core.c b/net/wireless/core.c index f9fee65dc06a..755cdf1643c9 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -314,7 +314,8 @@ static void cfg80211_process_events(struct wireless_dev *wdev) ev->cr.req_ie, ev->cr.req_ie_len, ev->cr.resp_ie, ev->cr.resp_ie_len, ev->cr.status, - ev->cr.status == WLAN_STATUS_SUCCESS); + ev->cr.status == WLAN_STATUS_SUCCESS, + NULL); break; case EVENT_ROAMED: __cfg80211_roamed(wdev, ev->rm.bssid, diff --git a/net/wireless/core.h b/net/wireless/core.h index 6d903c1d721d..325c17e6198c 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -127,6 +127,11 @@ static inline struct cfg80211_internal_bss *bss_from_pub(struct cfg80211_bss *pu return container_of(pub, struct cfg80211_internal_bss, pub); } +static inline void cfg80211_ref_bss(struct cfg80211_internal_bss *bss) +{ + kref_get(&bss->ref); +} + static inline void cfg80211_hold_bss(struct cfg80211_internal_bss *bss) { atomic_inc(&bss->hold); @@ -323,7 +328,8 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, - u16 status, bool wextev); + u16 status, bool wextev, + struct cfg80211_bss *bss); /* SME */ int __cfg80211_connect(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 097a87d7bae1..525e8e247b30 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -61,7 +61,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; u8 *ie = mgmt->u.assoc_resp.variable; int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); - bool done; + struct cfg80211_internal_bss *bss = NULL; wdev_lock(wdev); @@ -69,22 +69,32 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL); - __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, - status_code, - status_code == WLAN_STATUS_SUCCESS); - if (status_code == WLAN_STATUS_SUCCESS) { - for (i = 0; wdev->current_bss && i < MAX_AUTH_BSSES; i++) { - if (wdev->auth_bsses[i] == wdev->current_bss) { - cfg80211_unhold_bss(wdev->auth_bsses[i]); - cfg80211_put_bss(&wdev->auth_bsses[i]->pub); + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (!wdev->auth_bsses[i]) + continue; + if (memcmp(wdev->auth_bsses[i]->pub.bssid, mgmt->bssid, + ETH_ALEN) == 0) { + bss = wdev->auth_bsses[i]; wdev->auth_bsses[i] = NULL; - done = true; + /* additional reference to drop hold */ + cfg80211_ref_bss(bss); break; } } - WARN_ON(!done); + WARN_ON(!bss); + } + + /* this consumes one bss reference (unless bss is NULL) */ + __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, + status_code, + status_code == WLAN_STATUS_SUCCESS, + bss ? &bss->pub : NULL); + /* drop hold now, and also reference acquired above */ + if (bss) { + cfg80211_unhold_bss(bss); + cfg80211_put_bss(&bss->pub); } wdev_unlock(wdev); @@ -144,7 +154,7 @@ static void __cfg80211_send_deauth(struct net_device *dev, } else if (wdev->sme_state == CFG80211_SME_CONNECTING) { __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, - false); + false, NULL); } } @@ -241,7 +251,7 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) if (wdev->sme_state == CFG80211_SME_CONNECTING) __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, - false); + false, NULL); for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { if (wdev->authtry_bsses[i] && @@ -275,7 +285,7 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) if (wdev->sme_state == CFG80211_SME_CONNECTING) __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, - false); + false, NULL); for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { if (wdev->auth_bsses[i] && diff --git a/net/wireless/sme.c b/net/wireless/sme.c index d2b5d4ce0a00..3728d2b88b25 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -182,7 +182,7 @@ void cfg80211_conn_work(struct work_struct *work) wdev->conn->params.bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, - false); + false, NULL); wdev_unlock(wdev); } @@ -247,7 +247,7 @@ static void __cfg80211_sme_scan_done(struct net_device *dev) wdev->conn->params.bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, - false); + false, NULL); } } @@ -305,7 +305,7 @@ void cfg80211_sme_rx_auth(struct net_device *dev, schedule_work(&rdev->conn_work); } else if (status_code != WLAN_STATUS_SUCCESS) { __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, - status_code, false); + status_code, false, NULL); } else if (wdev->sme_state == CFG80211_SME_CONNECTING && wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; @@ -316,10 +316,10 @@ void cfg80211_sme_rx_auth(struct net_device *dev, void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, - u16 status, bool wextev) + u16 status, bool wextev, + struct cfg80211_bss *bss) { struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_bss *bss; #ifdef CONFIG_WIRELESS_EXT union iwreq_data wrqu; #endif @@ -361,6 +361,12 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, } #endif + if (wdev->current_bss) { + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + wdev->current_bss = NULL; + } + if (status == WLAN_STATUS_SUCCESS && wdev->sme_state == CFG80211_SME_IDLE) goto success; @@ -368,12 +374,6 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (wdev->sme_state != CFG80211_SME_CONNECTING) return; - if (wdev->current_bss) { - cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); - wdev->current_bss = NULL; - } - if (wdev->conn) wdev->conn->state = CFG80211_CONN_IDLE; @@ -386,10 +386,12 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, return; } - bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, - wdev->ssid, wdev->ssid_len, - WLAN_CAPABILITY_ESS, - WLAN_CAPABILITY_ESS); + success: + if (!bss) + bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, + wdev->ssid, wdev->ssid_len, + WLAN_CAPABILITY_ESS, + WLAN_CAPABILITY_ESS); if (WARN_ON(!bss)) return; @@ -397,7 +399,6 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, cfg80211_hold_bss(bss_from_pub(bss)); wdev->current_bss = bss_from_pub(bss); - success: wdev->sme_state = CFG80211_SME_CONNECTED; cfg80211_upload_connect_keys(wdev); } @@ -788,7 +789,7 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, else if (wdev->sme_state == CFG80211_SME_CONNECTING) __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, - wextev); + wextev, NULL); return 0; } -- cgit v1.2.3 From 59bbb6f7574bc693ed8313b98eac641116c95b94 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 7 Aug 2009 17:22:35 +0200 Subject: cfg80211: validate channel settings across interfaces Currently, there's a problem that affects regulatory enforcement and connection stability, in that it is possible to switch the channel while connected to a network or joined to an IBSS. The problem comes from the fact that we only validate the channel against the current interface's type, not against any other interface. Thus, you have any type of interface up, additionally bring up a monitor mode interface and switch the channel on the monitor. This will obviously also switch the channel on the other interface. The problem now is that if you do that while sending beacons for IBSS mode, you can switch to a disabled channel or a channel that doesn't allow beaconing. Combined with a managed mode interface connected to an AP instead of an IBSS interface, you can easily break the connection that way. To fix this, this patch validates any channel change with all available interfaces, and disallows such changes on secondary interfaces if another interface is connected to an AP or joined to an IBSS. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/Makefile | 3 +- net/wireless/chan.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/core.h | 6 ++++ net/wireless/ibss.c | 61 ++++++++++++++++++++------------ net/wireless/nl80211.c | 54 +++++++--------------------- net/wireless/sme.c | 9 +++++ net/wireless/wext-compat.c | 55 ++++++++++------------------- net/wireless/wext-compat.h | 3 +- net/wireless/wext-sme.c | 65 +++++++++++++++++++--------------- 9 files changed, 213 insertions(+), 131 deletions(-) create mode 100644 net/wireless/chan.c (limited to 'net/wireless/core.h') diff --git a/net/wireless/Makefile b/net/wireless/Makefile index d74cc77fa57a..3ecaa9179977 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -5,7 +5,8 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o -cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o sme.o +cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o +cfg80211-y += mlme.o ibss.o sme.o chan.o cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o wext-sme.o diff --git a/net/wireless/chan.c b/net/wireless/chan.c new file mode 100644 index 000000000000..bc00c9a06b3d --- /dev/null +++ b/net/wireless/chan.c @@ -0,0 +1,88 @@ +/* + * This file contains helper code to handle channel + * settings and keeping track of what is possible at + * any point in time. + * + * Copyright 2009 Johannes Berg + */ + +#include +#include "core.h" + +struct ieee80211_channel * +rdev_fixed_channel(struct cfg80211_registered_device *rdev, + struct wireless_dev *for_wdev) +{ + struct wireless_dev *wdev; + struct ieee80211_channel *result = NULL; + + WARN_ON(!mutex_is_locked(&rdev->devlist_mtx)); + + list_for_each_entry(wdev, &rdev->netdev_list, list) { + if (wdev == for_wdev) + continue; + + /* + * Lock manually to tell lockdep about allowed + * nesting here if for_wdev->mtx is held already. + * This is ok as it's all under the rdev devlist + * mutex and as such can only be done once at any + * given time. + */ + mutex_lock_nested(&wdev->mtx, SINGLE_DEPTH_NESTING); + if (wdev->current_bss) + result = wdev->current_bss->pub.channel; + wdev_unlock(wdev); + + if (result) + break; + } + + return result; +} + +int rdev_set_freq(struct cfg80211_registered_device *rdev, + int freq, enum nl80211_channel_type channel_type) +{ + struct ieee80211_channel *chan; + struct ieee80211_sta_ht_cap *ht_cap; + int result; + + if (rdev_fixed_channel(rdev, NULL)) + return -EBUSY; + + if (!rdev->ops->set_channel) + return -EOPNOTSUPP; + + chan = ieee80211_get_channel(&rdev->wiphy, freq); + + /* Primary channel not allowed */ + if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) + return -EINVAL; + + if (channel_type == NL80211_CHAN_HT40MINUS && + chan->flags & IEEE80211_CHAN_NO_HT40MINUS) + return -EINVAL; + else if (channel_type == NL80211_CHAN_HT40PLUS && + chan->flags & IEEE80211_CHAN_NO_HT40PLUS) + return -EINVAL; + + ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; + + if (channel_type != NL80211_CHAN_NO_HT) { + if (!ht_cap->ht_supported) + return -EINVAL; + + if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || + ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) + return -EINVAL; + } + + result = rdev->ops->set_channel(&rdev->wiphy, chan, channel_type); + if (result) + return result; + + rdev->channel = chan; + + return 0; +} diff --git a/net/wireless/core.h b/net/wireless/core.h index 325c17e6198c..5696b95af9be 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -366,4 +366,10 @@ void cfg80211_sme_disassoc(struct net_device *dev, int idx); void __cfg80211_scan_done(struct work_struct *wk); void cfg80211_upload_connect_keys(struct wireless_dev *wdev); +struct ieee80211_channel * +rdev_fixed_channel(struct cfg80211_registered_device *rdev, + struct wireless_dev *for_wdev); +int rdev_set_freq(struct cfg80211_registered_device *rdev, + int freq, enum nl80211_channel_type channel_type); + #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 4d7a084b35e2..42840a01be74 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -78,10 +78,15 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct cfg80211_cached_keys *connkeys) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct ieee80211_channel *chan; int err; ASSERT_WDEV_LOCK(wdev); + chan = rdev_fixed_channel(rdev, wdev); + if (chan && chan != params->channel) + return -EBUSY; + if (wdev->ssid_len) return -EALREADY; @@ -112,9 +117,11 @@ int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev = dev->ieee80211_ptr; int err; + mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); err = __cfg80211_join_ibss(rdev, dev, params, connkeys); wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); return err; } @@ -264,27 +271,32 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, int cfg80211_ibss_wext_siwfreq(struct net_device *dev, struct iw_request_info *info, - struct iw_freq *freq, char *extra) + struct iw_freq *wextfreq, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; - struct ieee80211_channel *chan; - int err; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct ieee80211_channel *chan = NULL; + int err, freq; /* call only for ibss! */ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) return -EINVAL; - if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss) + if (!rdev->ops->join_ibss) return -EOPNOTSUPP; - chan = cfg80211_wext_freq(wdev->wiphy, freq); - if (chan && IS_ERR(chan)) - return PTR_ERR(chan); + freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); + if (freq < 0) + return freq; - if (chan && - (chan->flags & IEEE80211_CHAN_NO_IBSS || - chan->flags & IEEE80211_CHAN_DISABLED)) - return -EINVAL; + if (freq) { + chan = ieee80211_get_channel(wdev->wiphy, freq); + if (!chan) + return -EINVAL; + if (chan->flags & IEEE80211_CHAN_NO_IBSS || + chan->flags & IEEE80211_CHAN_DISABLED) + return -EINVAL; + } if (wdev->wext.ibss.channel == chan) return 0; @@ -292,8 +304,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, wdev_lock(wdev); err = 0; if (wdev->ssid_len) - err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), - dev, true); + err = __cfg80211_leave_ibss(rdev, dev, true); wdev_unlock(wdev); if (err) @@ -307,9 +318,11 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, wdev->wext.ibss.channel_fixed = false; } + mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); - err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + err = cfg80211_ibss_wext_join(rdev, wdev); wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); return err; } @@ -347,6 +360,7 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev, struct iw_point *data, char *ssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); size_t len = data->length; int err; @@ -354,14 +368,13 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) return -EINVAL; - if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss) + if (!rdev->ops->join_ibss) return -EOPNOTSUPP; wdev_lock(wdev); err = 0; if (wdev->ssid_len) - err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), - dev, true); + err = __cfg80211_leave_ibss(rdev, dev, true); wdev_unlock(wdev); if (err) @@ -375,9 +388,11 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev, memcpy(wdev->wext.ibss.ssid, ssid, len); wdev->wext.ibss.ssid_len = len; + mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); - err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + err = cfg80211_ibss_wext_join(rdev, wdev); wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); return err; } @@ -414,6 +429,7 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev, struct sockaddr *ap_addr, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); u8 *bssid = ap_addr->sa_data; int err; @@ -421,7 +437,7 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) return -EINVAL; - if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss) + if (!rdev->ops->join_ibss) return -EOPNOTSUPP; if (ap_addr->sa_family != ARPHRD_ETHER) @@ -443,8 +459,7 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev, wdev_lock(wdev); err = 0; if (wdev->ssid_len) - err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), - dev, true); + err = __cfg80211_leave_ibss(rdev, dev, true); wdev_unlock(wdev); if (err) @@ -456,9 +471,11 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev, } else wdev->wext.ibss.bssid = NULL; + mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); - err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + err = cfg80211_ibss_wext_join(rdev, wdev); wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); return err; } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0cd548267d4a..2ff7376f35a3 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -701,15 +701,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - struct ieee80211_channel *chan; - struct ieee80211_sta_ht_cap *ht_cap; u32 freq; - if (!rdev->ops->set_channel) { - result = -EOPNOTSUPP; - goto bad_res; - } - result = -EINVAL; if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { @@ -723,42 +716,12 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) } freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); - chan = ieee80211_get_channel(&rdev->wiphy, freq); - - /* Primary channel not allowed */ - if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) - goto bad_res; - - if (channel_type == NL80211_CHAN_HT40MINUS && - (chan->flags & IEEE80211_CHAN_NO_HT40MINUS)) - goto bad_res; - else if (channel_type == NL80211_CHAN_HT40PLUS && - (chan->flags & IEEE80211_CHAN_NO_HT40PLUS)) - goto bad_res; - - /* - * At this point we know if that if HT40 was requested - * we are allowed to use it and the extension channel - * exists. - */ - ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; - - /* no HT capabilities or intolerant */ - if (channel_type != NL80211_CHAN_NO_HT) { - if (!ht_cap->ht_supported) - goto bad_res; - if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || - (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)) - goto bad_res; - } - - result = rdev->ops->set_channel(&rdev->wiphy, chan, - channel_type); + mutex_lock(&rdev->devlist_mtx); + result = rdev_set_freq(rdev, freq, channel_type); + mutex_unlock(&rdev->devlist_mtx); if (result) goto bad_res; - - rdev->channel = chan; } changed = 0; @@ -3453,7 +3416,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev; struct net_device *dev; struct cfg80211_crypto_settings crypto; - struct ieee80211_channel *chan; + struct ieee80211_channel *chan, *fixedchan; const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; int err, ssid_len, ie_len = 0; bool use_mfp = false; @@ -3496,6 +3459,15 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) goto out; } + mutex_lock(&rdev->devlist_mtx); + fixedchan = rdev_fixed_channel(rdev, NULL); + if (fixedchan && chan != fixedchan) { + err = -EBUSY; + mutex_unlock(&rdev->devlist_mtx); + goto out; + } + mutex_unlock(&rdev->devlist_mtx); + ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 340934f714b2..219c3bc2c37d 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -256,9 +256,11 @@ void cfg80211_sme_scan_done(struct net_device *dev) { struct wireless_dev *wdev = dev->ieee80211_ptr; + mutex_lock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx); wdev_lock(wdev); __cfg80211_sme_scan_done(dev); wdev_unlock(wdev); + mutex_unlock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx); } void cfg80211_sme_rx_auth(struct net_device *dev, @@ -644,6 +646,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, struct cfg80211_cached_keys *connkeys) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct ieee80211_channel *chan; int err; ASSERT_WDEV_LOCK(wdev); @@ -651,6 +654,10 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, if (wdev->sme_state != CFG80211_SME_IDLE) return -EALREADY; + chan = rdev_fixed_channel(rdev, wdev); + if (chan && chan != connect->channel) + return -EBUSY; + if (WARN_ON(wdev->connect_keys)) { kfree(wdev->connect_keys); wdev->connect_keys = NULL; @@ -785,9 +792,11 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, { int err; + mutex_lock(&rdev->devlist_mtx); wdev_lock(dev->ieee80211_ptr); err = __cfg80211_connect(rdev, dev, connect, connkeys); wdev_unlock(dev->ieee80211_ptr); + mutex_unlock(&rdev->devlist_mtx); return err; } diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index e4e90e249bab..17648dc79867 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -267,39 +267,26 @@ EXPORT_SYMBOL_GPL(cfg80211_wext_giwrange); * @wiphy: the wiphy * @freq: the wext freq encoding * - * Returns a channel, %NULL for auto, or an ERR_PTR for errors! + * Returns a frequency, or a negative error code, or 0 for auto. */ -struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy, - struct iw_freq *freq) +int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq) { - struct ieee80211_channel *chan; - int f; - /* - * Parse frequency - return NULL for auto and + * Parse frequency - return 0 for auto and * -EINVAL for impossible things. */ if (freq->e == 0) { if (freq->m < 0) - return NULL; - f = ieee80211_channel_to_frequency(freq->m); + return 0; + return ieee80211_channel_to_frequency(freq->m); } else { int i, div = 1000000; for (i = 0; i < freq->e; i++) div /= 10; if (div <= 0) - return ERR_PTR(-EINVAL); - f = freq->m / div; + return -EINVAL; + return freq->m / div; } - - /* - * Look up channel struct and return -EINVAL when - * it cannot be found. - */ - chan = ieee80211_get_channel(wiphy, f); - if (!chan) - return ERR_PTR(-EINVAL); - return chan; } int cfg80211_wext_siwrts(struct net_device *dev, @@ -761,33 +748,29 @@ EXPORT_SYMBOL_GPL(cfg80211_wext_giwencode); int cfg80211_wext_siwfreq(struct net_device *dev, struct iw_request_info *info, - struct iw_freq *freq, char *extra) + struct iw_freq *wextfreq, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct ieee80211_channel *chan; - int err; + int freq, err; switch (wdev->iftype) { case NL80211_IFTYPE_STATION: - return cfg80211_mgd_wext_siwfreq(dev, info, freq, extra); + return cfg80211_mgd_wext_siwfreq(dev, info, wextfreq, extra); case NL80211_IFTYPE_ADHOC: - return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra); + return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra); default: - chan = cfg80211_wext_freq(wdev->wiphy, freq); - if (!chan) + freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); + if (freq < 0) + return freq; + if (freq == 0) return -EINVAL; - if (IS_ERR(chan)) - return PTR_ERR(chan); - err = rdev->ops->set_channel(wdev->wiphy, chan, - NL80211_CHAN_NO_HT); - if (err) - return err; - rdev->channel = chan; - return 0; + mutex_lock(&rdev->devlist_mtx); + err = rdev_set_freq(rdev, freq, NL80211_CHAN_NO_HT); + mutex_unlock(&rdev->devlist_mtx); + return err; } } -EXPORT_SYMBOL_GPL(cfg80211_wext_siwfreq); int cfg80211_wext_giwfreq(struct net_device *dev, struct iw_request_info *info, diff --git a/net/wireless/wext-compat.h b/net/wireless/wext-compat.h index 9a3774749589..20b3daef6964 100644 --- a/net/wireless/wext-compat.h +++ b/net/wireless/wext-compat.h @@ -42,8 +42,7 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid); -struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy, - struct iw_freq *freq); +int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq); extern const struct iw_handler_def cfg80211_wext_handler; diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index e4a054aceb5a..fe1a53639122 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -52,25 +52,31 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, int cfg80211_mgd_wext_siwfreq(struct net_device *dev, struct iw_request_info *info, - struct iw_freq *freq, char *extra) + struct iw_freq *wextfreq, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct ieee80211_channel *chan; - int err; + struct ieee80211_channel *chan = NULL; + int err, freq; /* call only for station! */ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return -EINVAL; - chan = cfg80211_wext_freq(wdev->wiphy, freq); - if (chan && IS_ERR(chan)) - return PTR_ERR(chan); + freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); + if (freq < 0) + return freq; - if (chan && (chan->flags & IEEE80211_CHAN_DISABLED)) - return -EINVAL; + if (freq) { + chan = ieee80211_get_channel(wdev->wiphy, freq); + if (!chan) + return -EINVAL; + if (chan->flags & IEEE80211_CHAN_DISABLED) + return -EINVAL; + } cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); if (wdev->sme_state != CFG80211_SME_IDLE) { @@ -84,9 +90,8 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, /* if SSID set, we'll try right again, avoid event */ if (wdev->wext.connect.ssid_len) event = false; - err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), - dev, WLAN_REASON_DEAUTH_LEAVING, - event); + err = __cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, event); if (err) goto out; } @@ -95,17 +100,15 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, wdev->wext.connect.channel = chan; /* SSID is not set, we just want to switch channel */ - if (wdev->wext.connect.ssid_len && chan) { - err = -EOPNOTSUPP; - if (rdev->ops->set_channel) - err = rdev->ops->set_channel(wdev->wiphy, chan, - NL80211_CHAN_NO_HT); + if (chan && !wdev->wext.connect.ssid_len) { + err = rdev_set_freq(rdev, freq, NL80211_CHAN_NO_HT); goto out; } - err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + err = cfg80211_mgd_wext_connect(rdev, wdev); out: wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); cfg80211_unlock_rdev(rdev); return err; } @@ -143,6 +146,7 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, struct iw_point *data, char *ssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); size_t len = data->length; int err; @@ -157,7 +161,8 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, if (len > 0 && ssid[len - 1] == '\0') len--; - cfg80211_lock_rdev(wiphy_to_dev(wdev->wiphy)); + cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); err = 0; @@ -173,9 +178,8 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, /* if SSID set now, we'll try to connect, avoid event */ if (len) event = false; - err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), - dev, WLAN_REASON_DEAUTH_LEAVING, - event); + err = __cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, event); if (err) goto out; } @@ -186,10 +190,11 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, wdev->wext.connect.crypto.control_port = false; - err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + err = cfg80211_mgd_wext_connect(rdev, wdev); out: wdev_unlock(wdev); - cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy)); + mutex_unlock(&rdev->devlist_mtx); + cfg80211_unlock_rdev(rdev); return err; } @@ -230,6 +235,7 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, struct sockaddr *ap_addr, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); u8 *bssid = ap_addr->sa_data; int err; @@ -244,7 +250,8 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) bssid = NULL; - cfg80211_lock_rdev(wiphy_to_dev(wdev->wiphy)); + cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); if (wdev->sme_state != CFG80211_SME_IDLE) { @@ -258,9 +265,8 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0) goto out; - err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), - dev, WLAN_REASON_DEAUTH_LEAVING, - false); + err = __cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, false); if (err) goto out; } @@ -271,10 +277,11 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, } else wdev->wext.connect.bssid = NULL; - err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + err = cfg80211_mgd_wext_connect(rdev, wdev); out: wdev_unlock(wdev); - cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy)); + mutex_unlock(&rdev->devlist_mtx); + cfg80211_unlock_rdev(rdev); return err; } -- cgit v1.2.3 From f401a6f7ede753e56b84025e7d2db0d5ef560ce6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 7 Aug 2009 14:51:05 +0200 Subject: cfg80211: use reassociation when possible With the move of everything related to the SME from mac80211 to cfg80211, we lost the ability to send reassociation frames. This adds them back, but only for wireless extensions. With the userspace SME, it shall control assoc vs. reassoc (it already can do so with the nl80211 interface). I haven't touched the connect() implementation, so it is not possible to reassociate with the nl80211 connect primitive. I think that should be done with the NL80211_CMD_ROAM command, but we'll have to see how that can be handled in the future, especially with fullmac chips. This patch addresses only the immediate regression we had in mac80211, which previously sent reassoc. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 4 ++-- net/wireless/core.h | 4 +++- net/wireless/mlme.c | 11 ++++++++++ net/wireless/sme.c | 54 ++++++++++++++++++++++++++++++++++++++----------- net/wireless/wext-sme.c | 8 +++++++- 5 files changed, 65 insertions(+), 16 deletions(-) (limited to 'net/wireless/core.h') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 64df51d9a89f..1ee30fcd6fdc 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1335,10 +1335,10 @@ struct wireless_dev { struct cfg80211_cached_keys *keys; u8 *ie; size_t ie_len; - u8 bssid[ETH_ALEN]; + u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; u8 ssid[IEEE80211_MAX_SSID_LEN]; s8 default_key, default_mgmt_key; - bool ps; + bool ps, prev_bssid_valid; int ps_timeout; } wext; #endif diff --git a/net/wireless/core.h b/net/wireless/core.h index 5696b95af9be..92e0492b0e4d 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -335,7 +335,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, int __cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect, - struct cfg80211_cached_keys *connkeys); + struct cfg80211_cached_keys *connkeys, + const u8 *prev_bssid); int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect, @@ -353,6 +354,7 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); void cfg80211_conn_work(struct work_struct *work); +bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev); /* internal helpers */ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 51d5df67c632..da64071ceb84 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -67,6 +67,16 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); + /* + * This is a bit of a hack, we don't notify userspace of + * a (re-)association reply if we tried to send a reassoc + * and got a reject -- we only try again with an assoc + * frame instead of reassoc. + */ + if (status_code != WLAN_STATUS_SUCCESS && wdev->conn && + cfg80211_sme_failed_reassoc(wdev)) + goto out; + nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL); if (status_code == WLAN_STATUS_SUCCESS) { @@ -97,6 +107,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) cfg80211_put_bss(&bss->pub); } + out: wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_rx_assoc); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 219c3bc2c37d..104b33e34d22 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -27,10 +27,10 @@ struct cfg80211_conn { CFG80211_CONN_ASSOCIATE_NEXT, CFG80211_CONN_ASSOCIATING, } state; - u8 bssid[ETH_ALEN]; + u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; u8 *ie; size_t ie_len; - bool auto_auth; + bool auto_auth, prev_bssid_valid; }; @@ -110,6 +110,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_connect_params *params; + const u8 *prev_bssid = NULL; int err; ASSERT_WDEV_LOCK(wdev); @@ -135,15 +136,11 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) case CFG80211_CONN_ASSOCIATE_NEXT: BUG_ON(!rdev->ops->assoc); wdev->conn->state = CFG80211_CONN_ASSOCIATING; - /* - * We could, later, implement roaming here and then actually - * set prev_bssid to non-NULL. But then we need to be aware - * that some APs don't like that -- so we'd need to retry - * the association. - */ + if (wdev->conn->prev_bssid_valid) + prev_bssid = wdev->conn->prev_bssid; err = __cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel, params->bssid, - NULL, + prev_bssid, params->ssid, params->ssid_len, params->ie, params->ie_len, false, ¶ms->crypto); @@ -316,6 +313,28 @@ void cfg80211_sme_rx_auth(struct net_device *dev, } } +bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev) +{ + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + if (WARN_ON(!wdev->conn)) + return false; + + if (!wdev->conn->prev_bssid_valid) + return false; + + /* + * Some stupid APs don't accept reassoc, so we + * need to fall back to trying regular assoc. + */ + wdev->conn->prev_bssid_valid = false; + wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; + schedule_work(&rdev->conn_work); + + return true; +} + void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, @@ -359,8 +378,11 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; - if (bssid && status == WLAN_STATUS_SUCCESS) + if (bssid && status == WLAN_STATUS_SUCCESS) { memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); + wdev->wext.prev_bssid_valid = true; + } wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); } #endif @@ -511,6 +533,8 @@ void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); + wdev->wext.prev_bssid_valid = true; wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); #endif } @@ -643,7 +667,8 @@ EXPORT_SYMBOL(cfg80211_disconnected); int __cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect, - struct cfg80211_cached_keys *connkeys) + struct cfg80211_cached_keys *connkeys, + const u8 *prev_bssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct ieee80211_channel *chan; @@ -742,6 +767,11 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, wdev->sme_state = CFG80211_SME_CONNECTING; wdev->connect_keys = connkeys; + if (prev_bssid) { + memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN); + wdev->conn->prev_bssid_valid = true; + } + /* we're good if we have both BSSID and channel */ if (wdev->conn->params.bssid && wdev->conn->params.channel) { wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; @@ -794,7 +824,7 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, mutex_lock(&rdev->devlist_mtx); wdev_lock(dev->ieee80211_ptr); - err = __cfg80211_connect(rdev, dev, connect, connkeys); + err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL); wdev_unlock(dev->ieee80211_ptr); mutex_unlock(&rdev->devlist_mtx); diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index fe1a53639122..907470063f22 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -15,6 +15,7 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { struct cfg80211_cached_keys *ck = NULL; + const u8 *prev_bssid = NULL; int err, i; ASSERT_RDEV_LOCK(rdev); @@ -42,8 +43,12 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, for (i = 0; i < 6; i++) ck->params[i].key = ck->data[i]; } + + if (wdev->wext.prev_bssid_valid) + prev_bssid = wdev->wext.prev_bssid; + err = __cfg80211_connect(rdev, wdev->netdev, - &wdev->wext.connect, ck); + &wdev->wext.connect, ck, prev_bssid); if (err) kfree(ck); @@ -184,6 +189,7 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, goto out; } + wdev->wext.prev_bssid_valid = false; wdev->wext.connect.ssid = wdev->wext.ssid; memcpy(wdev->wext.ssid, ssid, len); wdev->wext.connect.ssid_len = len; -- cgit v1.2.3 From f5ea9120be2e5d5c846243416cfdce01d02f5836 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 7 Aug 2009 16:17:38 +0200 Subject: nl80211: add generation number to all dumps In order for userspace to be able to figure out whether it obtained a consistent snapshot of data or not when using netlink dumps, we need to have a generation number in each dump message that indicates whether the list has changed or not -- its value is arbitrary. This patch adds such a number to all dumps, this needs some mac80211 involvement to keep track of a generation number to start with when adding/removing mesh paths or stations. The wiphy and netdev lists can be fully handled within cfg80211, of course, but generation numbers need to be stored there as well. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 17 ++++++++++++----- include/net/cfg80211.h | 12 ++++++++++++ net/mac80211/cfg.c | 4 ++++ net/mac80211/ieee80211_i.h | 1 + net/mac80211/mesh.h | 2 ++ net/mac80211/mesh_pathtbl.c | 5 +++++ net/mac80211/sta_info.c | 2 ++ net/wireless/core.c | 5 +++++ net/wireless/core.h | 2 ++ net/wireless/nl80211.c | 31 +++++++++++++++++++++---------- net/wireless/scan.c | 1 + 11 files changed, 67 insertions(+), 15 deletions(-) (limited to 'net/wireless/core.h') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index cb3dc6027fd9..a8d71ed43a0e 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -480,10 +480,6 @@ enum nl80211_commands { * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz) * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive * scanning and include a zero-length SSID (wildcard) for wildcard scan - * @NL80211_ATTR_SCAN_GENERATION: the scan generation increases whenever the - * scan result list changes (BSS expired or added) so that applications - * can verify that they got a single, consistent snapshot (when all dump - * messages carried the same generation number) * @NL80211_ATTR_BSS: scan result BSS * * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain @@ -580,6 +576,14 @@ enum nl80211_commands { * * @NL80211_ATTR_PID: Process ID of a network namespace. * + * @NL80211_ATTR_GENERATION: Used to indicate consistent snapshots for + * dumps. This number increases whenever the object list being + * dumped changes, and as such userspace can verify that it has + * obtained a complete and consistent snapshot by verifying that + * all dump messages contain the same generation number. If it + * changed then the list changed and the dump should be repeated + * completely from scratch. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -651,7 +655,7 @@ enum nl80211_attrs { NL80211_ATTR_SCAN_FREQUENCIES, NL80211_ATTR_SCAN_SSIDS, - NL80211_ATTR_SCAN_GENERATION, + NL80211_ATTR_GENERATION, /* replaces old SCAN_GENERATION */ NL80211_ATTR_BSS, NL80211_ATTR_REG_INITIATOR, @@ -716,6 +720,9 @@ enum nl80211_attrs { NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 }; +/* source-level API compatibility */ +#define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION + /* * Allow user space programs to use #ifdef on new attributes by defining them * here diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 1ee30fcd6fdc..de7d116acc3d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -372,6 +372,10 @@ struct rate_info { * @txrate: current unicast bitrate to this station * @rx_packets: packets received from this station * @tx_packets: packets transmitted to this station + * @generation: generation number for nl80211 dumps. + * This number should increase every time the list of stations + * changes, i.e. when a station is added or removed, so that + * userspace can tell whether it got a consistent snapshot. */ struct station_info { u32 filled; @@ -385,6 +389,8 @@ struct station_info { struct rate_info txrate; u32 rx_packets; u32 tx_packets; + + int generation; }; /** @@ -444,6 +450,10 @@ enum mpath_info_flags { * @flags: mesh path flags * @discovery_timeout: total mesh path discovery timeout, in msecs * @discovery_retries: mesh path discovery retries + * @generation: generation number for nl80211 dumps. + * This number should increase every time the list of mesh paths + * changes, i.e. when a station is added or removed, so that + * userspace can tell whether it got a consistent snapshot. */ struct mpath_info { u32 filled; @@ -454,6 +464,8 @@ struct mpath_info { u32 discovery_timeout; u8 discovery_retries; u8 flags; + + int generation; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 4bbf5007799b..5608f6c68413 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -323,6 +323,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = sta->sdata; + sinfo->generation = sdata->local->sta_generation; + sinfo->filled = STATION_INFO_INACTIVE_TIME | STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES | @@ -909,6 +911,8 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop, else memset(next_hop, 0, ETH_ALEN); + pinfo->generation = mesh_paths_generation; + pinfo->filled = MPATH_INFO_FRAME_QLEN | MPATH_INFO_DSN | MPATH_INFO_METRIC | diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 989591787aee..99433222bc5c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -678,6 +678,7 @@ struct ieee80211_local { struct list_head sta_list; struct sta_info *sta_hash[STA_HASH_SIZE]; struct timer_list sta_cleanup; + int sta_generation; struct sk_buff_head pending[IEEE80211_MAX_QUEUES]; struct tasklet_struct tx_pending_tasklet; diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 2a2ed182cb7e..ce538814b9bd 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -265,6 +265,8 @@ void mesh_path_discard_frame(struct sk_buff *skb, void mesh_path_quiesce(struct ieee80211_sub_if_data *sdata); void mesh_path_restart(struct ieee80211_sub_if_data *sdata); +extern int mesh_paths_generation; + #ifdef CONFIG_MAC80211_MESH extern int mesh_allocated; diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 04b9e4d61b8e..431865a58622 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -38,6 +38,8 @@ struct mpath_node { static struct mesh_table *mesh_paths; static struct mesh_table *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 @@ -243,6 +245,8 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) mesh_paths->mean_chain_len * (mesh_paths->hash_mask + 1)) grow = 1; + mesh_paths_generation++; + spin_unlock(&mesh_paths->hashwlock[hash_idx]); read_unlock(&pathtbl_resize_lock); if (grow) { @@ -484,6 +488,7 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) err = -ENXIO; enddel: + mesh_paths_generation++; spin_unlock(&mesh_paths->hashwlock[hash_idx]); read_unlock(&pathtbl_resize_lock); return err; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index a360bceeba59..eec001491e66 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -349,6 +349,7 @@ int sta_info_insert(struct sta_info *sta) goto out_free; } list_add(&sta->list, &local->sta_list); + local->sta_generation++; local->num_sta++; sta_info_hash_add(local, sta); @@ -485,6 +486,7 @@ static void __sta_info_unlink(struct sta_info **sta) } local->num_sta--; + local->sta_generation++; if (local->ops->sta_notify) { if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) diff --git a/net/wireless/core.c b/net/wireless/core.c index 1e189306560d..62e1ac00879b 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -32,6 +32,7 @@ MODULE_DESCRIPTION("wireless configuration support"); * only read the list, and that can happen quite * often because we need to do it for each command */ LIST_HEAD(cfg80211_rdev_list); +int cfg80211_rdev_list_generation; /* * This is used to protect the cfg80211_rdev_list @@ -511,6 +512,7 @@ int wiphy_register(struct wiphy *wiphy) wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE); list_add(&rdev->list, &cfg80211_rdev_list); + cfg80211_rdev_list_generation++; mutex_unlock(&cfg80211_mutex); @@ -593,6 +595,7 @@ void wiphy_unregister(struct wiphy *wiphy) reg_device_remove(wiphy); list_del(&rdev->list); + cfg80211_rdev_list_generation++; device_del(&rdev->wiphy.dev); debugfs_remove(rdev->wiphy.debugfsdir); @@ -653,6 +656,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, spin_lock_init(&wdev->event_lock); mutex_lock(&rdev->devlist_mtx); list_add(&wdev->list, &rdev->netdev_list); + rdev->devlist_generation++; /* can only change netns with wiphy */ dev->features |= NETIF_F_NETNS_LOCAL; @@ -733,6 +737,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, if (!list_empty(&wdev->list)) { sysfs_remove_link(&dev->dev.kobj, "phy80211"); list_del_init(&wdev->list); + rdev->devlist_generation++; mutex_destroy(&wdev->mtx); #ifdef CONFIG_WIRELESS_EXT kfree(wdev->wext.keys); diff --git a/net/wireless/core.h b/net/wireless/core.h index 92e0492b0e4d..639db52eeff7 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -49,6 +49,7 @@ struct cfg80211_registered_device { /* associate netdev list */ struct mutex devlist_mtx; struct list_head netdev_list; + int devlist_generation; /* BSSes/scanning */ spinlock_t bss_lock; @@ -101,6 +102,7 @@ bool wiphy_idx_valid(int wiphy_idx) extern struct mutex cfg80211_mutex; extern struct list_head cfg80211_rdev_list; +extern int cfg80211_rdev_list_generation; #define assert_cfg80211_lock() WARN_ON(!mutex_is_locked(&cfg80211_mutex)) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 2ff7376f35a3..b3d5c1df08dd 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -408,6 +408,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx); NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)); + NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, + cfg80211_rdev_list_generation); + NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT, dev->wiphy.retry_short); NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG, @@ -825,6 +828,11 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name); NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype); + + NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, + rdev->devlist_generation ^ + (cfg80211_rdev_list_generation << 2)); + return genlmsg_end(msg, hdr); nla_put_failure: @@ -838,12 +846,12 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * int if_idx = 0; int wp_start = cb->args[0]; int if_start = cb->args[1]; - struct cfg80211_registered_device *dev; + struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; mutex_lock(&cfg80211_mutex); - list_for_each_entry(dev, &cfg80211_rdev_list, list) { - if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk))) + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk))) continue; if (wp_idx < wp_start) { wp_idx++; @@ -851,21 +859,21 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * } if_idx = 0; - mutex_lock(&dev->devlist_mtx); - list_for_each_entry(wdev, &dev->netdev_list, list) { + mutex_lock(&rdev->devlist_mtx); + list_for_each_entry(wdev, &rdev->netdev_list, list) { if (if_idx < if_start) { if_idx++; continue; } if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, NLM_F_MULTI, - dev, wdev->netdev) < 0) { - mutex_unlock(&dev->devlist_mtx); + rdev, wdev->netdev) < 0) { + mutex_unlock(&rdev->devlist_mtx); goto out; } if_idx++; } - mutex_unlock(&dev->devlist_mtx); + mutex_unlock(&rdev->devlist_mtx); wp_idx++; } @@ -1616,6 +1624,8 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); + NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, sinfo->generation); + sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO); if (!sinfoattr) goto nla_put_failure; @@ -2101,6 +2111,8 @@ static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq, NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst); NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop); + NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, pinfo->generation); + pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO); if (!pinfoattr) goto nla_put_failure; @@ -3090,8 +3102,7 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, if (!hdr) return -1; - NLA_PUT_U32(msg, NL80211_ATTR_SCAN_GENERATION, - rdev->bss_generation); + NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex); bss = nla_nest_start(msg, NL80211_ATTR_BSS); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 0ccf3a07dc02..1bcb1312bd94 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -562,6 +562,7 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) spin_lock_bh(&dev->bss_lock); list_del(&bss->list); + dev->bss_generation++; rb_erase(&bss->rbn, &dev->bss_tree); spin_unlock_bh(&dev->bss_lock); -- cgit v1.2.3 From 4b181144e6c1c25aaba9b9fc7cc70c95495ecb92 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 8 Aug 2009 11:03:58 +0200 Subject: cfg80211: fix locking for SIWFREQ "cfg80211: validate channel settings across interfaces" contained a locking bug -- in the managed-mode SIWFREQ call it would end up running into a lock recursion. This fixes it by not checking that particular interface for a channel that it needs to stay on, which is as it should be as that's the interface we're setting the channel for. Reported-by: Reinette Chatre Reported-by: Kalle Valo Signed-off-by: Johannes Berg Tested-by: Kalle Valo Tested-by: Reinette Chatre Signed-off-by: John W. Linville --- net/wireless/chan.c | 3 ++- net/wireless/core.h | 1 + net/wireless/nl80211.c | 2 +- net/wireless/wext-compat.c | 2 +- net/wireless/wext-sme.c | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) (limited to 'net/wireless/core.h') diff --git a/net/wireless/chan.c b/net/wireless/chan.c index bc00c9a06b3d..a46ac6c9b365 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -42,13 +42,14 @@ rdev_fixed_channel(struct cfg80211_registered_device *rdev, } int rdev_set_freq(struct cfg80211_registered_device *rdev, + struct wireless_dev *for_wdev, int freq, enum nl80211_channel_type channel_type) { struct ieee80211_channel *chan; struct ieee80211_sta_ht_cap *ht_cap; int result; - if (rdev_fixed_channel(rdev, NULL)) + if (rdev_fixed_channel(rdev, for_wdev)) return -EBUSY; if (!rdev->ops->set_channel) diff --git a/net/wireless/core.h b/net/wireless/core.h index 639db52eeff7..f7be3a9b427e 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -374,6 +374,7 @@ struct ieee80211_channel * rdev_fixed_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *for_wdev); int rdev_set_freq(struct cfg80211_registered_device *rdev, + struct wireless_dev *for_wdev, int freq, enum nl80211_channel_type channel_type); #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 667a87d307da..a8aaadeb6773 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -721,7 +721,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); mutex_lock(&rdev->devlist_mtx); - result = rdev_set_freq(rdev, freq, channel_type); + result = rdev_set_freq(rdev, NULL, freq, channel_type); mutex_unlock(&rdev->devlist_mtx); if (result) goto bad_res; diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 17648dc79867..c44917492210 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -766,7 +766,7 @@ int cfg80211_wext_siwfreq(struct net_device *dev, if (freq == 0) return -EINVAL; mutex_lock(&rdev->devlist_mtx); - err = rdev_set_freq(rdev, freq, NL80211_CHAN_NO_HT); + err = rdev_set_freq(rdev, NULL, freq, NL80211_CHAN_NO_HT); mutex_unlock(&rdev->devlist_mtx); return err; } diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 907470063f22..d16cd9ea4d00 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -106,7 +106,7 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, /* SSID is not set, we just want to switch channel */ if (chan && !wdev->wext.connect.ssid_len) { - err = rdev_set_freq(rdev, freq, NL80211_CHAN_NO_HT); + err = rdev_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT); goto out; } -- cgit v1.2.3 From 36e6fea84905512ea776707e82b5b435220efc17 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Aug 2009 22:21:21 +0200 Subject: cfg80211: check for and abort dangling scan requests If you trigger a scan request on an interface and then take it down, or rmmod the module or unplug the device the driver might "forget" to cancel the scan request. That is a bug in the driver, but the current behaviour is that we just hang endlessly waiting for the netdev refcount to become 0 which it never will. To improve robustness, check for this situation in cfg80211, warn about it and clean up behind the driver. I don't just clean up silently because it's likely that the driver also has some internal state it has now leaked. Additionally, this fixes a locking bug, clearing the scan_req pointer should be done under the rdev lock. Finally, we also need to _wait_ for the scan work and not just abort it since it might be pending and wanting to do a cleanup. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 10 +++++++++- net/wireless/core.h | 1 + net/wireless/scan.c | 26 ++++++++++++++++---------- 3 files changed, 26 insertions(+), 11 deletions(-) (limited to 'net/wireless/core.h') diff --git a/net/wireless/core.c b/net/wireless/core.c index e630648fef79..35d83bedfe5b 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -601,8 +601,8 @@ void wiphy_unregister(struct wiphy *wiphy) mutex_unlock(&cfg80211_mutex); + flush_work(&rdev->scan_done_wk); cancel_work_sync(&rdev->conn_work); - cancel_work_sync(&rdev->scan_done_wk); kfree(rdev->scan_req); flush_work(&rdev->event_work); } @@ -728,6 +728,13 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, #endif break; case NETDEV_UNREGISTER: + cfg80211_lock_rdev(rdev); + + if (WARN_ON(rdev->scan_req && rdev->scan_req->dev == dev)) { + rdev->scan_req->aborted = true; + ___cfg80211_scan_done(rdev); + } + mutex_lock(&rdev->devlist_mtx); /* * It is possible to get NETDEV_UNREGISTER @@ -746,6 +753,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, #endif } mutex_unlock(&rdev->devlist_mtx); + cfg80211_unlock_rdev(rdev); break; case NETDEV_PRE_UP: if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) diff --git a/net/wireless/core.h b/net/wireless/core.h index f7be3a9b427e..c603f5286326 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -368,6 +368,7 @@ void cfg80211_sme_scan_done(struct net_device *dev); void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); void cfg80211_sme_disassoc(struct net_device *dev, int idx); void __cfg80211_scan_done(struct work_struct *wk); +void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev); void cfg80211_upload_connect_keys(struct wireless_dev *wdev); struct ieee80211_channel * diff --git a/net/wireless/scan.c b/net/wireless/scan.c index e6c1f11595da..fe575a24c95c 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -18,19 +18,14 @@ #define IEEE80211_SCAN_RESULT_EXPIRE (15 * HZ) -void __cfg80211_scan_done(struct work_struct *wk) +void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev) { - struct cfg80211_registered_device *rdev; struct cfg80211_scan_request *request; struct net_device *dev; #ifdef CONFIG_WIRELESS_EXT union iwreq_data wrqu; #endif - rdev = container_of(wk, struct cfg80211_registered_device, - scan_done_wk); - - mutex_lock(&rdev->mtx); request = rdev->scan_req; dev = request->dev; @@ -43,9 +38,9 @@ void __cfg80211_scan_done(struct work_struct *wk) cfg80211_sme_scan_done(dev); if (request->aborted) - nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev); + nl80211_send_scan_aborted(rdev, dev); else - nl80211_send_scan_done(wiphy_to_dev(request->wiphy), dev); + nl80211_send_scan_done(rdev, dev); #ifdef CONFIG_WIRELESS_EXT if (!request->aborted) { @@ -57,11 +52,22 @@ void __cfg80211_scan_done(struct work_struct *wk) dev_put(dev); - cfg80211_unlock_rdev(rdev); - wiphy_to_dev(request->wiphy)->scan_req = NULL; + rdev->scan_req = NULL; kfree(request); } +void __cfg80211_scan_done(struct work_struct *wk) +{ + struct cfg80211_registered_device *rdev; + + rdev = container_of(wk, struct cfg80211_registered_device, + scan_done_wk); + + cfg80211_lock_rdev(rdev); + ___cfg80211_scan_done(rdev); + cfg80211_unlock_rdev(rdev); +} + void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) { WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); -- cgit v1.2.3 From ad002395fd230528281083f4be71855ed7e35b04 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 18 Aug 2009 19:51:57 +0200 Subject: cfg80211: fix dangling scan request checking My patch "cfg80211: fix deadlock" broke the code it was supposed to fix, the scan request checking. But it's not trivial to put it back the way it was, since the original patch had a deadlock. Now do it in a completely new way: queue the check off to a work struct, where we can freely lock. But that has some more complications, like needing to wait for it to be done before the wiphy/rdev can be destroyed, so some code is required to handle that. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 2 ++ net/wireless/core.c | 76 +++++++++++++++++++++++++++++++++++++++++--------- net/wireless/core.h | 2 ++ 3 files changed, 67 insertions(+), 13 deletions(-) (limited to 'net/wireless/core.h') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0b146bb2dd14..3d874c620219 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1325,6 +1325,8 @@ struct wireless_dev { struct mutex mtx; + struct work_struct cleanup_work; + /* currently used for IBSS and SME - might be rearranged later */ u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 ssid_len; diff --git a/net/wireless/core.c b/net/wireless/core.c index 69a185ba9ff1..c150071b6f29 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -430,6 +430,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) INIT_WORK(&rdev->conn_work, cfg80211_conn_work); INIT_WORK(&rdev->event_work, cfg80211_event_work); + init_waitqueue_head(&rdev->dev_wait); + /* * Initialize wiphy parameters to IEEE 802.11 MIB default values. * Fragmentation and RTS threshold are disabled by default with the @@ -574,7 +576,23 @@ void wiphy_unregister(struct wiphy *wiphy) /* protect the device list */ mutex_lock(&cfg80211_mutex); + wait_event(rdev->dev_wait, ({ + int __count; + mutex_lock(&rdev->devlist_mtx); + __count = rdev->opencount; + mutex_unlock(&rdev->devlist_mtx); + __count == 0;})); + + mutex_lock(&rdev->devlist_mtx); BUG_ON(!list_empty(&rdev->netdev_list)); + mutex_unlock(&rdev->devlist_mtx); + + /* + * First remove the hardware from everywhere, this makes + * it impossible to find from userspace. + */ + cfg80211_debugfs_rdev_del(rdev); + list_del(&rdev->list); /* * Try to grab rdev->mtx. If a command is still in progress, @@ -582,26 +600,18 @@ void wiphy_unregister(struct wiphy *wiphy) * down the device already. We wait for this command to complete * before unlinking the item from the list. * Note: as codified by the BUG_ON above we cannot get here if - * a virtual interface is still associated. Hence, we can only - * get to lock contention here if userspace issues a command - * that identified the hardware by wiphy index. + * a virtual interface is still present. Hence, we can only get + * to lock contention here if userspace issues a command that + * identified the hardware by wiphy index. */ cfg80211_lock_rdev(rdev); - - if (WARN_ON(rdev->scan_req)) { - rdev->scan_req->aborted = true; - ___cfg80211_scan_done(rdev); - } - + /* nothing */ cfg80211_unlock_rdev(rdev); - cfg80211_debugfs_rdev_del(rdev); - /* If this device got a regulatory hint tell core its * free to listen now to a new shiny device regulatory hint */ reg_device_remove(wiphy); - list_del(&rdev->list); cfg80211_rdev_list_generation++; device_del(&rdev->wiphy.dev); debugfs_remove(rdev->wiphy.debugfsdir); @@ -640,6 +650,31 @@ void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked) } EXPORT_SYMBOL(wiphy_rfkill_set_hw_state); +static void wdev_cleanup_work(struct work_struct *work) +{ + struct wireless_dev *wdev; + struct cfg80211_registered_device *rdev; + + wdev = container_of(work, struct wireless_dev, cleanup_work); + rdev = wiphy_to_dev(wdev->wiphy); + + cfg80211_lock_rdev(rdev); + + if (WARN_ON(rdev->scan_req && rdev->scan_req->dev == wdev->netdev)) { + rdev->scan_req->aborted = true; + ___cfg80211_scan_done(rdev); + } + + cfg80211_unlock_rdev(rdev); + + mutex_lock(&rdev->devlist_mtx); + rdev->opencount--; + mutex_unlock(&rdev->devlist_mtx); + wake_up(&rdev->dev_wait); + + dev_put(wdev->netdev); +} + static int cfg80211_netdev_notifier_call(struct notifier_block * nb, unsigned long state, void *ndev) @@ -663,6 +698,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, * are added with nl80211. */ mutex_init(&wdev->mtx); + INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); INIT_LIST_HEAD(&wdev->event_list); spin_lock_init(&wdev->event_lock); mutex_lock(&rdev->devlist_mtx); @@ -717,8 +753,22 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, default: break; } + dev_hold(dev); + schedule_work(&wdev->cleanup_work); break; case NETDEV_UP: + /* + * If we have a really quick DOWN/UP succession we may + * have this work still pending ... cancel it and see + * if it was pending, in which case we need to account + * for some of the work it would have done. + */ + if (cancel_work_sync(&wdev->cleanup_work)) { + mutex_lock(&rdev->devlist_mtx); + rdev->opencount--; + mutex_unlock(&rdev->devlist_mtx); + dev_put(dev); + } #ifdef CONFIG_WIRELESS_EXT cfg80211_lock_rdev(rdev); mutex_lock(&rdev->devlist_mtx); @@ -734,6 +784,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, break; } wdev_unlock(wdev); + rdev->opencount++; mutex_unlock(&rdev->devlist_mtx); cfg80211_unlock_rdev(rdev); #endif @@ -756,7 +807,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, sysfs_remove_link(&dev->dev.kobj, "phy80211"); list_del_init(&wdev->list); rdev->devlist_generation++; - mutex_destroy(&wdev->mtx); #ifdef CONFIG_WIRELESS_EXT kfree(wdev->wext.keys); #endif diff --git a/net/wireless/core.h b/net/wireless/core.h index c603f5286326..f565432ae22f 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -50,6 +50,8 @@ struct cfg80211_registered_device { struct mutex devlist_mtx; struct list_head netdev_list; int devlist_generation; + int opencount; /* also protected by devlist_mtx */ + wait_queue_head_t dev_wait; /* BSSes/scanning */ spinlock_t bss_lock; -- cgit v1.2.3 From 01a0ac417ce9b4f1216a266f2fd454cffefc5aee Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 20 Aug 2009 21:36:16 +0200 Subject: cfg80211: check lost scans later, fix bug When we lose a scan, cfg80211 tries to clean up after the driver. However, it currently does this too early, it does this in GOING_DOWN already instead of DOWN, so it may happen with mac80211. Besides fixing this, also make it more robust by leaking the scan request so if the driver later actually finishes the scan, it won't crash. Also check in ___cfg80211_scan_done whether a scan request is still pending and exit if not. Reported-by: Felix Fietkau Signed-off-by: Johannes Berg Tested-by: Felix Fietkau Signed-off-by: John W. Linville --- net/wireless/core.c | 4 +++- net/wireless/core.h | 2 +- net/wireless/scan.c | 21 ++++++++++++++++++--- 3 files changed, 22 insertions(+), 5 deletions(-) (limited to 'net/wireless/core.h') diff --git a/net/wireless/core.c b/net/wireless/core.c index 154e1e294cb9..9b157caa74fd 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -664,7 +664,7 @@ static void wdev_cleanup_work(struct work_struct *work) if (WARN_ON(rdev->scan_req && rdev->scan_req->dev == wdev->netdev)) { rdev->scan_req->aborted = true; - ___cfg80211_scan_done(rdev); + ___cfg80211_scan_done(rdev, true); } cfg80211_unlock_rdev(rdev); @@ -755,6 +755,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, default: break; } + break; + case NETDEV_DOWN: dev_hold(dev); schedule_work(&wdev->cleanup_work); break; diff --git a/net/wireless/core.h b/net/wireless/core.h index f565432ae22f..68eaf340d613 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -370,7 +370,7 @@ void cfg80211_sme_scan_done(struct net_device *dev); void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); void cfg80211_sme_disassoc(struct net_device *dev, int idx); void __cfg80211_scan_done(struct work_struct *wk); -void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev); +void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); void cfg80211_upload_connect_keys(struct wireless_dev *wdev); struct ieee80211_channel * diff --git a/net/wireless/scan.c b/net/wireless/scan.c index fe575a24c95c..7043de6221ab 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -18,7 +18,7 @@ #define IEEE80211_SCAN_RESULT_EXPIRE (15 * HZ) -void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev) +void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) { struct cfg80211_scan_request *request; struct net_device *dev; @@ -26,8 +26,13 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev) union iwreq_data wrqu; #endif + ASSERT_RDEV_LOCK(rdev); + request = rdev->scan_req; + if (!request) + return; + dev = request->dev; /* @@ -53,7 +58,17 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev) dev_put(dev); rdev->scan_req = NULL; - kfree(request); + + /* + * OK. If this is invoked with "leak" then we can't + * free this ... but we've cleaned it up anyway. The + * driver failed to call the scan_done callback, so + * all bets are off, it might still be trying to use + * the scan request or not ... if it accesses the dev + * in there (it shouldn't anyway) then it may crash. + */ + if (!leak) + kfree(request); } void __cfg80211_scan_done(struct work_struct *wk) @@ -64,7 +79,7 @@ void __cfg80211_scan_done(struct work_struct *wk) scan_done_wk); cfg80211_lock_rdev(rdev); - ___cfg80211_scan_done(rdev); + ___cfg80211_scan_done(rdev, false); cfg80211_unlock_rdev(rdev); } -- cgit v1.2.3 From f7969969f416e593bcc7dc24abf3f9fd6c27136d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 21 Aug 2009 12:23:49 +0200 Subject: cfg80211: make spurious warnings less likely, configurable Bob reported that he got warnings in IBSS mode about the ssid_len being zero on a joined event, but only when kmemcheck was enabled. This appears to be due to a race condition between drivers and userspace, when the driver reports joined but the user in the meantime decided to leave the IBSS again, the warning would trigger. This was made more likely by kmemcheck delaying the code that does the check and sends the event. So first, make the warning trigger closer to the driver, which means it's not locked, but since only the warning depends on it that's ok. And secondly, users will not want to have spurious warnings at all, so make those that are known to be racy in such a way configurable. Reported-by: Bob Copeland Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/Kconfig | 17 +++++++++++++++++ net/wireless/core.h | 11 +++++++++++ net/wireless/ibss.c | 4 +++- net/wireless/sme.c | 12 +++++++++--- 4 files changed, 40 insertions(+), 4 deletions(-) (limited to 'net/wireless/core.h') diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index c6031d5b135f..aea7e6824af9 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -17,6 +17,23 @@ config NL80211_TESTMODE Say N. +config CFG80211_DEVELOPER_WARNINGS + bool "enable developer warnings" + depends on CFG80211 + default n + help + This option enables some additional warnings that help + cfg80211 developers and driver developers, but that can + trigger due to races with userspace. + + For example, when a driver reports that it was disconnected + from the AP, but the user disconnects manually at the same + time, the warning might trigger spuriously due to races. + + Say Y only if you are developing cfg80211 or a driver based + on it (or mac80211). + + config CFG80211_REG_DEBUG bool "cfg80211 regulatory debugging" depends on CFG80211 diff --git a/net/wireless/core.h b/net/wireless/core.h index 68eaf340d613..d262d42cbd5e 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -380,4 +380,15 @@ int rdev_set_freq(struct cfg80211_registered_device *rdev, struct wireless_dev *for_wdev, int freq, enum nl80211_channel_type channel_type); +#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS +#define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) +#else +/* + * Trick to enable using it as a condition, + * and also not give a warning when it's + * not used that way. + */ +#define CFG80211_DEV_WARN_ON(cond) ({bool __r = (cond); __r; }) +#endif + #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 42840a01be74..c88338911979 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -22,7 +22,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) return; - if (WARN_ON(!wdev->ssid_len)) + if (!wdev->ssid_len) return; bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, @@ -58,6 +58,8 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) struct cfg80211_event *ev; unsigned long flags; + CFG80211_DEV_WARN_ON(!wdev->ssid_len); + ev = kzalloc(sizeof(*ev), gfp); if (!ev) return; diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 4a8289f9b4f0..68307883ec87 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -351,7 +351,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; - if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING)) + if (wdev->sme_state != CFG80211_SME_CONNECTING) return; nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, @@ -445,6 +445,8 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, struct cfg80211_event *ev; unsigned long flags; + CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING); + ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); if (!ev) return; @@ -481,7 +483,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; - if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) + if (wdev->sme_state != CFG80211_SME_CONNECTED) return; /* internal error -- how did we get to CONNECTED w/o BSS? */ @@ -540,6 +542,8 @@ void cfg80211_roamed(struct net_device *dev, const u8 *bssid, struct cfg80211_event *ev; unsigned long flags; + CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); + ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); if (!ev) return; @@ -575,7 +579,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; - if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) + if (wdev->sme_state != CFG80211_SME_CONNECTED) return; if (wdev->current_bss) { @@ -639,6 +643,8 @@ void cfg80211_disconnected(struct net_device *dev, u16 reason, struct cfg80211_event *ev; unsigned long flags; + CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); + ev = kzalloc(sizeof(*ev) + ie_len, gfp); if (!ev) return; -- cgit v1.2.3 From 3d54d25515838543e56889aa7e48f40d00719368 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 21 Aug 2009 14:51:05 +0200 Subject: cfg80211: clean up properly on interface type change When the interface type changes while connected, and the driver does not require the interface to be down for a type change, it is currently possible to get very strange results unless the driver takes special care, which it shouldn't have to. To fix this, take care to disconnect/leave IBSS when changing the interface type -- even if the driver may fail the call. Also process all events that may be pending to avoid running into a situation where an event is reported but only processed after the type has already changed, which would lead to missing events and warnings. A side effect of this is that you will have disconnected or left the IBSS even if the mode change ultimately fails, but since the intention was to change it and thus leave or disconnect, this is not a problem. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 54 +---------------------- net/wireless/core.h | 4 ++ net/wireless/nl80211.c | 16 +------ net/wireless/util.c | 108 +++++++++++++++++++++++++++++++++++++++++++++ net/wireless/wext-compat.c | 16 ++----- 5 files changed, 117 insertions(+), 81 deletions(-) (limited to 'net/wireless/core.h') diff --git a/net/wireless/core.c b/net/wireless/core.c index 9b157caa74fd..45b2be3274db 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -294,69 +294,17 @@ static void cfg80211_rfkill_sync_work(struct work_struct *work) cfg80211_rfkill_set_block(rdev, rfkill_blocked(rdev->rfkill)); } -static void cfg80211_process_events(struct wireless_dev *wdev) -{ - struct cfg80211_event *ev; - unsigned long flags; - - spin_lock_irqsave(&wdev->event_lock, flags); - while (!list_empty(&wdev->event_list)) { - ev = list_first_entry(&wdev->event_list, - struct cfg80211_event, list); - list_del(&ev->list); - spin_unlock_irqrestore(&wdev->event_lock, flags); - - wdev_lock(wdev); - switch (ev->type) { - case EVENT_CONNECT_RESULT: - __cfg80211_connect_result( - wdev->netdev, is_zero_ether_addr(ev->cr.bssid) ? - NULL : ev->cr.bssid, - ev->cr.req_ie, ev->cr.req_ie_len, - ev->cr.resp_ie, ev->cr.resp_ie_len, - ev->cr.status, - ev->cr.status == WLAN_STATUS_SUCCESS, - NULL); - break; - case EVENT_ROAMED: - __cfg80211_roamed(wdev, ev->rm.bssid, - ev->rm.req_ie, ev->rm.req_ie_len, - ev->rm.resp_ie, ev->rm.resp_ie_len); - break; - case EVENT_DISCONNECTED: - __cfg80211_disconnected(wdev->netdev, - ev->dc.ie, ev->dc.ie_len, - ev->dc.reason, true); - break; - case EVENT_IBSS_JOINED: - __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid); - break; - } - wdev_unlock(wdev); - - kfree(ev); - - spin_lock_irqsave(&wdev->event_lock, flags); - } - spin_unlock_irqrestore(&wdev->event_lock, flags); -} - static void cfg80211_event_work(struct work_struct *work) { struct cfg80211_registered_device *rdev; - struct wireless_dev *wdev; rdev = container_of(work, struct cfg80211_registered_device, event_work); rtnl_lock(); cfg80211_lock_rdev(rdev); - mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &rdev->netdev_list, list) - cfg80211_process_events(wdev); - - mutex_unlock(&rdev->devlist_mtx); + cfg80211_process_rdev_events(rdev); cfg80211_unlock_rdev(rdev); rtnl_unlock(); } diff --git a/net/wireless/core.h b/net/wireless/core.h index d262d42cbd5e..2a33d8bc886b 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -372,6 +372,10 @@ void cfg80211_sme_disassoc(struct net_device *dev, int idx); void __cfg80211_scan_done(struct work_struct *wk); void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); void cfg80211_upload_connect_keys(struct wireless_dev *wdev); +int cfg80211_change_iface(struct cfg80211_registered_device *rdev, + struct net_device *dev, enum nl80211_iftype ntype, + u32 *flags, struct vif_params *params); +void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); struct ieee80211_channel * rdev_fixed_channel(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a8aaadeb6773..71bfc044a939 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -977,12 +977,6 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) } } - if (!rdev->ops->change_virtual_intf || - !(rdev->wiphy.interface_modes & (1 << ntype))) { - err = -EOPNOTSUPP; - goto unlock; - } - if (info->attrs[NL80211_ATTR_MESH_ID]) { if (ntype != NL80211_IFTYPE_MESH_POINT) { err = -EINVAL; @@ -1008,18 +1002,10 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) } if (change) - err = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, - ntype, flags, ¶ms); + err = cfg80211_change_iface(rdev, dev, ntype, flags, ¶ms); else err = 0; - WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype); - - if (!err && (ntype != otype)) { - if (otype == NL80211_IFTYPE_ADHOC) - cfg80211_clear_ibss(dev, false); - } - unlock: dev_put(dev); cfg80211_unlock_rdev(rdev); diff --git a/net/wireless/util.c b/net/wireless/util.c index 693275a16a26..3fc2df86278f 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -574,3 +574,111 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev) kfree(wdev->connect_keys); wdev->connect_keys = NULL; } + +static void cfg80211_process_wdev_events(struct wireless_dev *wdev) +{ + struct cfg80211_event *ev; + unsigned long flags; + const u8 *bssid = NULL; + + spin_lock_irqsave(&wdev->event_lock, flags); + while (!list_empty(&wdev->event_list)) { + ev = list_first_entry(&wdev->event_list, + struct cfg80211_event, list); + list_del(&ev->list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + + wdev_lock(wdev); + switch (ev->type) { + case EVENT_CONNECT_RESULT: + if (!is_zero_ether_addr(ev->cr.bssid)) + bssid = ev->cr.bssid; + __cfg80211_connect_result( + wdev->netdev, bssid, + ev->cr.req_ie, ev->cr.req_ie_len, + ev->cr.resp_ie, ev->cr.resp_ie_len, + ev->cr.status, + ev->cr.status == WLAN_STATUS_SUCCESS, + NULL); + break; + case EVENT_ROAMED: + __cfg80211_roamed(wdev, ev->rm.bssid, + ev->rm.req_ie, ev->rm.req_ie_len, + ev->rm.resp_ie, ev->rm.resp_ie_len); + break; + case EVENT_DISCONNECTED: + __cfg80211_disconnected(wdev->netdev, + ev->dc.ie, ev->dc.ie_len, + ev->dc.reason, true); + break; + case EVENT_IBSS_JOINED: + __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid); + break; + } + wdev_unlock(wdev); + + kfree(ev); + + spin_lock_irqsave(&wdev->event_lock, flags); + } + spin_unlock_irqrestore(&wdev->event_lock, flags); +} + +void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev) +{ + struct wireless_dev *wdev; + + ASSERT_RTNL(); + ASSERT_RDEV_LOCK(rdev); + + mutex_lock(&rdev->devlist_mtx); + + list_for_each_entry(wdev, &rdev->netdev_list, list) + cfg80211_process_wdev_events(wdev); + + mutex_unlock(&rdev->devlist_mtx); +} + +int cfg80211_change_iface(struct cfg80211_registered_device *rdev, + struct net_device *dev, enum nl80211_iftype ntype, + u32 *flags, struct vif_params *params) +{ + int err; + enum nl80211_iftype otype = dev->ieee80211_ptr->iftype; + + ASSERT_RDEV_LOCK(rdev); + + /* don't support changing VLANs, you just re-create them */ + if (otype == NL80211_IFTYPE_AP_VLAN) + return -EOPNOTSUPP; + + if (!rdev->ops->change_virtual_intf || + !(rdev->wiphy.interface_modes & (1 << ntype))) + return -EOPNOTSUPP; + + if (ntype != otype) { + switch (otype) { + case NL80211_IFTYPE_ADHOC: + cfg80211_leave_ibss(rdev, dev, false); + break; + case NL80211_IFTYPE_STATION: + cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, true); + break; + case NL80211_IFTYPE_MESH_POINT: + /* mesh should be handled? */ + break; + default: + break; + } + + cfg80211_process_rdev_events(rdev); + } + + err = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, + ntype, flags, params); + + WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype); + + return err; +} diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index c12029b1def0..429dd06a4ecc 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -70,18 +70,8 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, enum nl80211_iftype type; int ret; - if (!wdev) - return -EOPNOTSUPP; - rdev = wiphy_to_dev(wdev->wiphy); - if (!rdev->ops->change_virtual_intf) - return -EOPNOTSUPP; - - /* don't support changing VLANs, you just re-create them */ - if (wdev->iftype == NL80211_IFTYPE_AP_VLAN) - return -EOPNOTSUPP; - switch (*mode) { case IW_MODE_INFRA: type = NL80211_IFTYPE_STATION; @@ -104,9 +94,9 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, memset(&vifparams, 0, sizeof(vifparams)); - ret = rdev->ops->change_virtual_intf(wdev->wiphy, dev, type, - NULL, &vifparams); - WARN_ON(!ret && wdev->iftype != type); + cfg80211_lock_rdev(rdev); + ret = cfg80211_change_iface(rdev, dev, type, NULL, &vifparams); + cfg80211_unlock_rdev(rdev); return ret; } -- cgit v1.2.3