From decdb78ea0b4e8599e7975348e28cc5c0a4b71a6 Mon Sep 17 00:00:00 2001 From: kimocoder Date: Sat, 22 Jun 2019 15:16:29 +0200 Subject: [PATCH] Upload new v5.6.4.1 branch --- Kconfig | 6 - Makefile | 8 +- README.md | 6 - ReleaseNotes.pdf | Bin 103331 -> 104427 bytes core/mesh/rtw_mesh.c | 23 +- core/mesh/rtw_mesh.h | 2 +- core/rtw_ap.c | 40 ++- core/rtw_ieee80211.c | 153 +++++------- core/rtw_mlme.c | 7 +- core/rtw_mlme_ext.c | 178 ++++++++++++- core/rtw_wlan_util.c | 176 ++++++------- dkms-install.sh | 2 +- dkms-remove.sh | 2 +- dkms.conf | 2 +- hal/hal_mp.c | 3 +- hal/rtl8812a/usb/rtl8812au_xmit.c | 2 +- include/autoconf.h | 2 +- include/ieee80211.h | 42 +++- include/osdep_service.h | 2 + include/rtw_android.h | 1 + include/rtw_mlme.h | 14 +- include/rtw_mlme_ext.h | 10 +- include/rtw_security.h | 9 + include/rtw_version.h | 2 +- include/sta_info.h | 4 + include/wlan_bssdef.h | 1 - os_dep/linux/ioctl_cfg80211.c | 400 +++++++++++++++++++++--------- os_dep/linux/ioctl_cfg80211.h | 21 +- os_dep/linux/ioctl_mp.c | 13 - os_dep/linux/mlme_linux.c | 4 + os_dep/linux/os_intfs.c | 2 +- os_dep/linux/rtw_android.c | 7 + os_dep/linux/rtw_proc.c | 12 + os_dep/linux/usb_intf.c | 51 +--- os_dep/linux/wifi_regd.c | 204 ++++++++++++++- os_dep/osdep_service.c | 6 +- 36 files changed, 987 insertions(+), 430 deletions(-) delete mode 100644 Kconfig diff --git a/Kconfig b/Kconfig deleted file mode 100644 index f87653d..0000000 --- a/Kconfig +++ /dev/null @@ -1,6 +0,0 @@ -config RTL8812AU - tristate "Realtek 8812A USB WiFi" - depends on USB - ---help--- - Help message of RTL8812AU - diff --git a/Makefile b/Makefile index 7a9ce1d..1eff52e 100755 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ EXTRA_CFLAGS += $(USER_EXTRA_CFLAGS) EXTRA_CFLAGS += -O1 #EXTRA_CFLAGS += -O3 -EXTRA_CFLAGS += -Wall -EXTRA_CFLAGS += -Wextra +#EXTRA_CFLAGS += -Wall +#EXTRA_CFLAGS += -Wextra #EXTRA_CFLAGS += -Werror #EXTRA_CFLAGS += -pedantic #EXTRA_CFLAGS += -Wshadow -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes @@ -585,6 +585,10 @@ endif ########### HAL_RTL8814A ################################# ifeq ($(CONFIG_RTL8814A), y) +## ADD NEW VHT MP HW TX MODE ## +#EXTRA_CFLAGS += -DCONFIG_MP_VHT_HW_TX_MODE +#CONFIG_MP_VHT_HW_TX_MODE = y +########################################## RTL871X = rtl8814a ifeq ($(CONFIG_USB_HCI), y) MODULE_NAME = 8814au diff --git a/README.md b/README.md index cff7b8e..65a2015 100644 --- a/README.md +++ b/README.md @@ -143,9 +143,3 @@ at the end of file /etc/NetworkManager/NetworkManager.conf and restart NetworkMa sudo service NetworkManager restart ``` -### Other sources worth checking out -``` -new rtw88 mac80211 driver: https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/log/rtw88 -wireless git: https://git.kernel.org/pub/scm/linux/kernel/git/jh/wireless.git/log/ -wireless testing git: https://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git/log/ -wpa_supplicant git: https://w1.fi/cgit/hostap/log/ diff --git a/ReleaseNotes.pdf b/ReleaseNotes.pdf index 9c27e93e78f1a9096f05e282269b1f4168197099..e6e2b74b1bc6ae7527a170e256a7b91b666de37e 100644 GIT binary patch delta 40625 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 mmZ8CAnF-G036&*0s`JVscP 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 u8 *p = NULL, *dst_ie = NULL, *premainder_ie = NULL, *pbackup_remainder_ie = NULL; - u32 i, offset, ielen = 0, ie_offset, remainder_ielen = 0; + u32 i, offset, ielen, ie_offset, remainder_ielen = 0; for (i = sizeof(NDIS_802_11_FIXED_IEs); i < pnetwork->IELength;) { pIE = (PNDIS_802_11_VARIABLE_IEs)(pnetwork->IEs + i); @@ -1897,6 +1897,7 @@ int rtw_check_beacon_data(_adapter *padapter, u8 *pbuf, int len) u16 cap, ht_cap = _FALSE; uint ie_len = 0; int group_cipher, pairwise_cipher; + u32 akm; u8 mfp_opt = MFP_NO; u8 channel, network_type, supportRate[NDIS_802_11_LENGTH_RATES_EX]; int supportRateNum = 0; @@ -2057,13 +2058,14 @@ int rtw_check_beacon_data(_adapter *padapter, u8 *pbuf, int len) psecuritypriv->wpa_psk = 0; /* wpa2 */ + akm = 0; group_cipher = 0; pairwise_cipher = 0; psecuritypriv->wpa2_group_cipher = _NO_PRIVACY_; psecuritypriv->wpa2_pairwise_cipher = _NO_PRIVACY_; p = rtw_get_ie(ie + _BEACON_IE_OFFSET_, _RSN_IE_2_, &ie_len, (pbss_network->IELength - _BEACON_IE_OFFSET_)); if (p && ie_len > 0) { - if (rtw_parse_wpa2_ie(p, ie_len + 2, &group_cipher, &pairwise_cipher, NULL, &mfp_opt) == _SUCCESS) { + if (rtw_parse_wpa2_ie(p, ie_len + 2, &group_cipher, &pairwise_cipher, &akm, &mfp_opt) == _SUCCESS) { psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; psecuritypriv->ndisauthtype = Ndis802_11AuthModeWPA2PSK; psecuritypriv->dot8021xalg = 1;/* psk, todo:802.1x */ @@ -2071,6 +2073,15 @@ int rtw_check_beacon_data(_adapter *padapter, u8 *pbuf, int len) psecuritypriv->wpa2_group_cipher = group_cipher; psecuritypriv->wpa2_pairwise_cipher = pairwise_cipher; + + /* + Kernel < v5.1, the auth_type set as NL80211_AUTHTYPE_AUTOMATIC + in cfg80211_rtw_start_ap(). + if the AKM SAE in the RSN IE, we have to update the auth_type for SAE + in rtw_check_beacon_data(). + */ + if (CHECK_BIT(WLAN_AKM_TYPE_SAE, akm)) + psecuritypriv->auth_type = NL80211_AUTHTYPE_SAE; #if 0 switch (group_cipher) { case WPA_CIPHER_NONE: @@ -3858,6 +3869,12 @@ u8 ap_free_sta(_adapter *padapter, struct sta_info *psta, bool active, u16 reaso _enter_critical_bh(&psta->lock, &irqL); psta->state &= ~(_FW_LINKED | WIFI_UNDER_KEY_HANDSHAKE); + + if ((psta->auth_len != 0) && (psta->pauth_frame != NULL)) { + rtw_mfree(psta->pauth_frame, psta->auth_len); + psta->pauth_frame = NULL; + psta->auth_len = 0; + } _exit_critical_bh(&psta->lock, &irqL); if (!MLME_IS_MESH(padapter)) { @@ -5172,6 +5189,7 @@ u16 rtw_ap_parse_sta_security_ie(_adapter *adapter, struct sta_info *sta, struct u8 *wpa_ie; int wpa_ie_len; int group_cipher = 0, pairwise_cipher = 0; + u32 akm = 0; u8 mfp_opt = MFP_NO; u16 status = _STATS_SUCCESSFUL_; @@ -5187,13 +5205,17 @@ u16 rtw_ap_parse_sta_security_ie(_adapter *adapter, struct sta_info *sta, struct wpa_ie = elems->rsn_ie; wpa_ie_len = elems->rsn_ie_len; - if (rtw_parse_wpa2_ie(wpa_ie - 2, wpa_ie_len + 2, &group_cipher, &pairwise_cipher, NULL, &mfp_opt) == _SUCCESS) { + if (rtw_parse_wpa2_ie(wpa_ie - 2, wpa_ie_len + 2, &group_cipher, &pairwise_cipher, &akm, &mfp_opt) == _SUCCESS) { sta->dot8021xalg = 1;/* psk, todo:802.1x */ sta->wpa_psk |= BIT(1); sta->wpa2_group_cipher = group_cipher & sec->wpa2_group_cipher; sta->wpa2_pairwise_cipher = pairwise_cipher & sec->wpa2_pairwise_cipher; + sta->akm_suite_type = akm; + if ((CHECK_BIT(WLAN_AKM_TYPE_SAE, akm)) && (MFP_NO == mfp_opt)) + status = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; + if (!sta->wpa2_group_cipher) status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; @@ -5239,6 +5261,18 @@ u16 rtw_ap_parse_sta_security_ie(_adapter *adapter, struct sta_info *sta, struct else if (sec->mfp_opt >= MFP_OPTIONAL && mfp_opt >= MFP_OPTIONAL) sta->flags |= WLAN_STA_MFP; + if ((sec->auth_type == NL80211_AUTHTYPE_SAE) && + (CHECK_BIT(WLAN_AKM_TYPE_SAE, sta->akm_suite_type)) && + (WLAN_AUTH_OPEN == sta->authalg)) { + /* WPA3-SAE, PMK caching */ + if (rtw_cached_pmkid(adapter, sta->cmn.mac_addr) == -1) { + RTW_INFO("SAE: No PMKSA cache entry found\n"); + status = WLAN_STATUS_INVALID_PMKID; + } else { + RTW_INFO("SAE: PMKSA cache entry found\n"); + } + } + if (status != _STATS_SUCCESSFUL_) goto exit; diff --git a/core/rtw_ieee80211.c b/core/rtw_ieee80211.c index c867197..33b05c4 100644 --- a/core/rtw_ieee80211.c +++ b/core/rtw_ieee80211.c @@ -33,14 +33,28 @@ u8 WPA_CIPHER_SUITE_CCMP[] = { 0x00, 0x50, 0xf2, 4 }; u8 WPA_CIPHER_SUITE_WEP104[] = { 0x00, 0x50, 0xf2, 5 }; u16 RSN_VERSION_BSD = 1; -u8 RSN_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x0f, 0xac, 1 }; -u8 RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x0f, 0xac, 2 }; u8 RSN_CIPHER_SUITE_NONE[] = { 0x00, 0x0f, 0xac, 0 }; u8 RSN_CIPHER_SUITE_WEP40[] = { 0x00, 0x0f, 0xac, 1 }; u8 RSN_CIPHER_SUITE_TKIP[] = { 0x00, 0x0f, 0xac, 2 }; u8 RSN_CIPHER_SUITE_WRAP[] = { 0x00, 0x0f, 0xac, 3 }; u8 RSN_CIPHER_SUITE_CCMP[] = { 0x00, 0x0f, 0xac, 4 }; u8 RSN_CIPHER_SUITE_WEP104[] = { 0x00, 0x0f, 0xac, 5 }; + +u8 WLAN_AKM_8021X[] = {0x00, 0x0f, 0xac, 1}; +u8 WLAN_AKM_PSK[] = {0x00, 0x0f, 0xac, 2}; +u8 WLAN_AKM_FT_8021X[] = {0x00, 0x0f, 0xac, 3}; +u8 WLAN_AKM_FT_PSK[] = {0x00, 0x0f, 0xac, 4}; +u8 WLAN_AKM_8021X_SHA256[] = {0x00, 0x0f, 0xac, 5}; +u8 WLAN_AKM_PSK_SHA256[] = {0x00, 0x0f, 0xac, 6}; +u8 WLAN_AKM_TDLS[] = {0x00, 0x0f, 0xac, 7}; +u8 WLAN_AKM_SAE[] = {0x00, 0x0f, 0xac, 8}; +u8 WLAN_AKM_FT_OVER_SAE[] = {0x00, 0x0f, 0xac, 9}; +u8 WLAN_AKM_8021X_SUITE_B[] = {0x00, 0x0f, 0xac, 11}; +u8 WLAN_AKM_8021X_SUITE_B_192[] = {0x00, 0x0f, 0xac, 12}; +u8 WLAN_AKM_FILS_SHA256[] = {0x00, 0x0f, 0xac, 14}; +u8 WLAN_AKM_FILS_SHA384[] = {0x00, 0x0f, 0xac, 15}; +u8 WLAN_AKM_FT_FILS_SHA256[] = {0x00, 0x0f, 0xac, 16}; +u8 WLAN_AKM_FT_FILS_SHA384[] = {0x00, 0x0f, 0xac, 17}; /* ----------------------------------------------------------- * for adhoc-master to generate ie and provide supported-rate to fw * ----------------------------------------------------------- */ @@ -661,8 +675,44 @@ int rtw_get_wpa2_cipher_suite(u8 *s) return 0; } +u32 rtw_get_akm_suite_bitmap(u8 *s) +{ + if (_rtw_memcmp(s, WLAN_AKM_8021X, RSN_SELECTOR_LEN) == _TRUE) + return WLAN_AKM_TYPE_8021X; + if (_rtw_memcmp(s, WLAN_AKM_PSK, RSN_SELECTOR_LEN) == _TRUE) + return WLAN_AKM_TYPE_PSK; + if (_rtw_memcmp(s, WLAN_AKM_FT_8021X, RSN_SELECTOR_LEN) == _TRUE) + return WLAN_AKM_TYPE_FT_8021X; + if (_rtw_memcmp(s, WLAN_AKM_FT_PSK, RSN_SELECTOR_LEN) == _TRUE) + return WLAN_AKM_TYPE_FT_PSK; + if (_rtw_memcmp(s, WLAN_AKM_8021X_SHA256, RSN_SELECTOR_LEN) == _TRUE) + return WLAN_AKM_TYPE_8021X_SHA256; + if (_rtw_memcmp(s, WLAN_AKM_PSK_SHA256, RSN_SELECTOR_LEN) == _TRUE) + return WLAN_AKM_TYPE_PSK_SHA256; + if (_rtw_memcmp(s, WLAN_AKM_TDLS, RSN_SELECTOR_LEN) == _TRUE) + return WLAN_AKM_TYPE_TDLS; + if (_rtw_memcmp(s, WLAN_AKM_SAE, RSN_SELECTOR_LEN) == _TRUE) + return WLAN_AKM_TYPE_SAE; + if (_rtw_memcmp(s, WLAN_AKM_FT_OVER_SAE, RSN_SELECTOR_LEN) == _TRUE) + return WLAN_AKM_TYPE_FT_OVER_SAE; + if (_rtw_memcmp(s, WLAN_AKM_8021X_SUITE_B, RSN_SELECTOR_LEN) == _TRUE) + return WLAN_AKM_TYPE_8021X_SUITE_B; + if (_rtw_memcmp(s, WLAN_AKM_8021X_SUITE_B_192, RSN_SELECTOR_LEN) == _TRUE) + return WLAN_AKM_TYPE_8021X_SUITE_B_192; + if (_rtw_memcmp(s, WLAN_AKM_FILS_SHA256, RSN_SELECTOR_LEN) == _TRUE) + return WLAN_AKM_TYPE_FILS_SHA256; + if (_rtw_memcmp(s, WLAN_AKM_FILS_SHA384, RSN_SELECTOR_LEN) == _TRUE) + return WLAN_AKM_TYPE_FILS_SHA384; + if (_rtw_memcmp(s, WLAN_AKM_FT_FILS_SHA256, RSN_SELECTOR_LEN) == _TRUE) + return WLAN_AKM_TYPE_FT_FILS_SHA256; + if (_rtw_memcmp(s, WLAN_AKM_FT_FILS_SHA384, RSN_SELECTOR_LEN) == _TRUE) + return WLAN_AKM_TYPE_FT_FILS_SHA384; -int rtw_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher, int *pairwise_cipher, int *is_8021x) + return 0; +} + +int rtw_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher, + int *pairwise_cipher, u32 *akm) { int i, ret = _SUCCESS; int left, count; @@ -721,11 +771,11 @@ int rtw_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher, int *pairwis return _FAIL; } - if (is_8021x) { + if (akm) { if (left >= 6) { pos += 2; if (_rtw_memcmp(pos, SUITE_1X, 4) == 1) { - *is_8021x = 1; + *akm = WLAN_AKM_TYPE_8021X; } } } @@ -833,11 +883,11 @@ err: return _FAIL; } -int rtw_parse_wpa2_ie(u8 *rsn_ie, int rsn_ie_len, int *group_cipher, int *pairwise_cipher, int *is_8021x, u8 *mfp_opt) +int rtw_parse_wpa2_ie(u8 *rsn_ie, int rsn_ie_len, int *group_cipher, + int *pairwise_cipher, u32 *akm, u8 *mfp_opt) { struct rsne_info info; int i, ret = _SUCCESS; - u8 SUITE_1X[4] = {0x00, 0x0f, 0xac, 0x01}; ret = rtw_rsne_info_parse(rsn_ie, rsn_ie_len, &info); if (ret != _SUCCESS) @@ -856,11 +906,10 @@ int rtw_parse_wpa2_ie(u8 *rsn_ie, int rsn_ie_len, int *group_cipher, int *pairwi *pairwise_cipher |= rtw_get_wpa2_cipher_suite(info.pcs_list + 4 * i); } - if (is_8021x) { - *is_8021x = 0; - /* here only check the first AKM suite */ - if (info.akm_cnt && _rtw_memcmp(SUITE_1X, info.akm_list, 4) == _TRUE) - *is_8021x = 1; + if (akm) { + *akm = 0; + for (i = 0; i < info.akm_cnt; i++) + *akm |= rtw_get_akm_suite_bitmap(info.akm_list + 4 * i); } if (mfp_opt) { @@ -2660,86 +2709,6 @@ int ieee80211_get_hdrlen(u16 fc) return hdrlen; } -int rtw_get_cipher_info(struct wlan_network *pnetwork) -{ - u32 wpa_ielen; - unsigned char *pbuf; - int group_cipher = 0, pairwise_cipher = 0, is8021x = 0; - int ret = _FAIL; - pbuf = rtw_get_wpa_ie(&pnetwork->network.IEs[12], &wpa_ielen, pnetwork->network.IELength - 12); - - if (pbuf && (wpa_ielen > 0)) { - if (_SUCCESS == rtw_parse_wpa_ie(pbuf, wpa_ielen + 2, &group_cipher, &pairwise_cipher, &is8021x)) { - - pnetwork->BcnInfo.pairwise_cipher = pairwise_cipher; - pnetwork->BcnInfo.group_cipher = group_cipher; - pnetwork->BcnInfo.is_8021x = is8021x; - ret = _SUCCESS; - } - } else { - - pbuf = rtw_get_wpa2_ie(&pnetwork->network.IEs[12], &wpa_ielen, pnetwork->network.IELength - 12); - - if (pbuf && (wpa_ielen > 0)) { - if (_SUCCESS == rtw_parse_wpa2_ie(pbuf, wpa_ielen + 2, &group_cipher, &pairwise_cipher, &is8021x, NULL)) { - pnetwork->BcnInfo.pairwise_cipher = pairwise_cipher; - pnetwork->BcnInfo.group_cipher = group_cipher; - pnetwork->BcnInfo.is_8021x = is8021x; - ret = _SUCCESS; - } - } - } - - return ret; -} - -void rtw_get_bcn_info(struct wlan_network *pnetwork) -{ - unsigned short cap = 0; - u8 bencrypt = 0; - /* u8 wpa_ie[255],rsn_ie[255]; */ - u16 wpa_len = 0, rsn_len = 0; - struct HT_info_element *pht_info = NULL; - struct rtw_ieee80211_ht_cap *pht_cap = NULL; - unsigned int len; - unsigned char *p; - - _rtw_memcpy((u8 *)&cap, rtw_get_capability_from_ie(pnetwork->network.IEs), 2); - cap = le16_to_cpu(cap); - if (cap & WLAN_CAPABILITY_PRIVACY) { - bencrypt = 1; - pnetwork->network.Privacy = 1; - } else - pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_OPENSYS; - rtw_get_sec_ie(pnetwork->network.IEs , pnetwork->network.IELength, NULL, &rsn_len, NULL, &wpa_len); - - if (rsn_len > 0) - pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_WPA2; - else if (wpa_len > 0) - pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_WPA; - else { - if (bencrypt) - pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_WEP; - } - rtw_get_cipher_info(pnetwork); - - /* get bwmode and ch_offset */ - /* parsing HT_CAP_IE */ - p = rtw_get_ie(pnetwork->network.IEs + _FIXED_IE_LENGTH_, _HT_CAPABILITY_IE_, &len, pnetwork->network.IELength - _FIXED_IE_LENGTH_); - if (p && len > 0) { - pht_cap = (struct rtw_ieee80211_ht_cap *)(p + 2); - pnetwork->BcnInfo.ht_cap_info = pht_cap->cap_info; - } else - pnetwork->BcnInfo.ht_cap_info = 0; - /* parsing HT_INFO_IE */ - p = rtw_get_ie(pnetwork->network.IEs + _FIXED_IE_LENGTH_, _HT_ADD_INFO_IE_, &len, pnetwork->network.IELength - _FIXED_IE_LENGTH_); - if (p && len > 0) { - pht_info = (struct HT_info_element *)(p + 2); - pnetwork->BcnInfo.ht_info_infos_0 = pht_info->infos[0]; - } else - pnetwork->BcnInfo.ht_info_infos_0 = 0; -} - u8 rtw_ht_mcsset_to_nss(u8 *supp_mcs_set) { u8 nss = 1; diff --git a/core/rtw_mlme.c b/core/rtw_mlme.c index ca7ca56..a3567a2 100644 --- a/core/rtw_mlme.c +++ b/core/rtw_mlme.c @@ -4272,7 +4272,12 @@ static int SecIsInPMKIDList(_adapter *Adapter, u8 *bssid) } -static int rtw_rsn_sync_pmkid(_adapter *adapter, u8 *ie, uint ie_len, int i_ent) +int rtw_cached_pmkid(_adapter *Adapter, u8 *bssid) +{ + return SecIsInPMKIDList(Adapter, bssid); +} + +int rtw_rsn_sync_pmkid(_adapter *adapter, u8 *ie, uint ie_len, int i_ent) { struct security_priv *sec = &adapter->securitypriv; struct rsne_info info; diff --git a/core/rtw_mlme_ext.c b/core/rtw_mlme_ext.c index 2d90209..c4af4b9 100755 --- a/core/rtw_mlme_ext.c +++ b/core/rtw_mlme_ext.c @@ -1858,7 +1858,6 @@ unsigned int OnBeacon(_adapter *padapter, union recv_frame *precv_frame) struct beacon_keys recv_beacon; update_network(&(pmlmepriv->cur_network.network), pbss, padapter, _TRUE); - rtw_get_bcn_info(&(pmlmepriv->cur_network)); /* update bcn keys */ if (rtw_get_bcn_keys(padapter, pframe, len, &recv_beacon) == _TRUE) { @@ -2097,9 +2096,9 @@ unsigned int OnAuth(_adapter *padapter, union recv_frame *precv_frame) goto auth_fail; } - if (auth_mode == 2 && - psecuritypriv->dot11PrivacyAlgrthm != _WEP40_ && - psecuritypriv->dot11PrivacyAlgrthm != _WEP104_) + if ((auth_mode == 2) && (algorithm != WLAN_AUTH_SAE) && + (psecuritypriv->dot11PrivacyAlgrthm != _WEP40_) && + (psecuritypriv->dot11PrivacyAlgrthm != _WEP104_)) auth_mode = 0; if ((algorithm > 0 && auth_mode == 0) || /* rx a shared-key auth but shared not enabled */ @@ -2173,6 +2172,17 @@ unsigned int OnAuth(_adapter *padapter, union recv_frame *precv_frame) if (pstat->auth_seq == 0) pstat->expire_to = pstapriv->auth_to; +#ifdef CONFIG_IOCTL_CFG80211 + if (GET_CFG80211_REPORT_MGMT(adapter_wdev_data(padapter), IEEE80211_STYPE_AUTH) == _TRUE) { + if ((algorithm == WLAN_AUTH_SAE) && + (auth_mode == dot11AuthAlgrthm_8021X)) { + pstat->authalg = algorithm; + + rtw_cfg80211_rx_mframe(padapter, precv_frame, NULL); + return _SUCCESS; + } + } +#endif /* CONFIG_IOCTL_CFG80211 */ if ((pstat->auth_seq + 1) != seq) { RTW_INFO("(1)auth rejected because out of seq [rx_seq=%d, exp_seq=%d]!\n", @@ -2294,6 +2304,21 @@ unsigned int OnAuthClient(_adapter *padapter, union recv_frame *precv_frame) RTW_INFO("%s\n", __FUNCTION__); +#ifdef CONFIG_IOCTL_CFG80211 + if (GET_CFG80211_REPORT_MGMT(adapter_wdev_data(padapter), IEEE80211_STYPE_AUTH) == _TRUE) { + if (rtw_sec_chk_auth_type(padapter, NL80211_AUTHTYPE_SAE)) { + if (rtw_cached_pmkid(padapter, get_my_bssid(&pmlmeinfo->network)) != -1) { + RTW_INFO("SAE: PMKSA cache entry found\n"); + goto normal; + } + rtw_cfg80211_rx_mframe(padapter, precv_frame, NULL); + return _SUCCESS; + } + } + +normal: +#endif /* CONFIG_IOCTL_CFG80211 */ + /* check A1 matches or not */ if (!_rtw_memcmp(adapter_mac_addr(padapter), get_da(pframe), ETH_ALEN)) return _SUCCESS; @@ -2431,6 +2456,17 @@ unsigned int OnAssocReq(_adapter *padapter, union recv_frame *precv_frame) RTW_INFO("%s\n", __FUNCTION__); + if (pstat->authalg == WLAN_AUTH_SAE) { + /* WPA3-SAE */ + if (((pstat->state) & WIFI_FW_AUTH_NULL)) { + /* TODO: + Queue AssocReq and Proccess + by external auth trigger. */ + RTW_INFO("%s: wait external auth trigger\n", __func__); + return _SUCCESS; + } + } + /* check if this stat has been successfully authenticated/assocated */ if (!((pstat->state) & WIFI_FW_AUTH_SUCCESS)) { if (!((pstat->state) & WIFI_FW_ASSOC_SUCCESS)) { @@ -9152,7 +9188,18 @@ void _issue_assocreq(_adapter *padapter, u8 is_reassoc) rtw_ft_update_rsnie(padapter, _TRUE, pattrib, &pframe); } else #endif + { +#ifdef CONFIG_IOCTL_CFG80211 + if (rtw_sec_chk_auth_alg(padapter, WLAN_AUTH_OPEN) && + rtw_sec_chk_auth_type(padapter, NL80211_AUTHTYPE_SAE)) { + s32 entry = rtw_cached_pmkid(padapter, pmlmepriv->assoc_bssid); + + rtw_rsn_sync_pmkid(padapter, (u8 *)pIE, (pIE->Length + 2), entry); + } +#endif /* CONFIG_IOCTL_CFG80211 */ + pframe = rtw_set_ie(pframe, EID_WPA2, pIE->Length, pIE->data, &(pattrib->pktlen)); + } break; #ifdef CONFIG_80211N_HT case EID_HTCapability: @@ -11315,6 +11362,7 @@ void start_clnt_auth(_adapter *padapter) { struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info); + struct wifidirect_info *pwdinfo = &(padapter->wdinfo); _cancel_timer_ex(&pmlmeext->link_timer); @@ -11334,6 +11382,22 @@ void start_clnt_auth(_adapter *padapter) } else #endif RTW_PRINT("start auth\n"); + +#ifdef CONFIG_IOCTL_CFG80211 + if (rtw_sec_chk_auth_type(padapter, NL80211_AUTHTYPE_SAE)) { + if (rtw_cached_pmkid(padapter, get_my_bssid(&pmlmeinfo->network)) != -1) { + RTW_INFO("SAE: PMKSA cache entry found\n"); + padapter->securitypriv.auth_alg = WLAN_AUTH_OPEN; + goto no_external_auth; + } + + RTW_PRINT("SAE: start external auth\n"); + rtw_cfg80211_external_auth_request(padapter, NULL); + return; + } +no_external_auth: +#endif /* CONFIG_IOCTL_CFG80211 */ + issue_auth(padapter, NULL, 0); set_link_timer(pmlmeext, REAUTH_TO); @@ -13015,6 +13079,12 @@ void link_timer_hdl(void *ctx) pmlmeinfo->state = WIFI_FW_NULL_STATE; report_join_res(padapter, -3, WLAN_STATUS_UNSPECIFIED_FAILURE); } else if (pmlmeinfo->state & WIFI_FW_AUTH_STATE) { + +#ifdef CONFIG_IOCTL_CFG80211 + if (rtw_sec_chk_auth_type(padapter, NL80211_AUTHTYPE_SAE)) + return; +#endif /* CONFIG_IOCTL_CFG80211 */ + /* re-auth timer */ if (++pmlmeinfo->reauth_count > REAUTH_LIMIT) { /* if (pmlmeinfo->auth_algo != dot11AuthAlgrthm_Auto) */ @@ -13193,7 +13263,6 @@ void rtw_ft_update_bcn(_adapter *padapter, union recv_frame *precv_frame) struct beacon_keys recv_beacon; update_network(&(pmlmepriv->cur_network.network), pbss, padapter, _TRUE); - rtw_get_bcn_info(&(pmlmepriv->cur_network)); /* update bcn keys */ if (rtw_get_bcn_keys(padapter, pframe, len, &recv_beacon) == _TRUE) { @@ -16226,11 +16295,48 @@ exit: *ch = u_ch; *bw = u_bw; *offset = u_offset; + +#if defined(CONFIG_IOCTL_CFG80211) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) + { + u8 ht_option = 0; + +#ifdef CONFIG_80211N_HT + ht_option = adapter->mlmepriv.htpriv.ht_option; +#endif /* CONFIG_80211N_HT */ + + /* + when supplicant send the mlme frame, + the bss freq is updated by channel switch event. + */ + rtw_cfg80211_ch_switch_notify(adapter, + cur_ch, cur_bw, cur_ch_offset, ht_option); + } +#endif } return connect_allow == _TRUE ? _SUCCESS : _FAIL; } +void rtw_set_external_auth_status(_adapter *padapter, + const void *data, int len) +{ +#ifdef CONFIG_IOCTL_CFG80211 + struct net_device *dev = padapter->pnetdev; + struct wiphy *wiphy = adapter_to_wiphy(padapter); + struct rtw_external_auth_params params; + + /* convert data to external_auth_params */ + params.action = RTW_GET_BE32((u8 *)data); + _rtw_memcpy(¶ms.bssid, (u8 *)data + 4, ETH_ALEN); + _rtw_memcpy(¶ms.ssid.ssid, (u8 *)data + 10, WLAN_SSID_MAXLEN); + params.ssid.ssid_len = RTW_GET_BE64((u8 *)data + 42); + params.key_mgmt_suite = RTW_GET_BE32((u8 *)data + 58); + params.status = RTW_GET_BE16((u8 *)data + 62); + _rtw_memcpy(¶ms.pmkid, (u8 *)data + 64, PMKID_LEN); + + rtw_cfg80211_external_auth_status(wiphy, dev, ¶ms); +#endif /* CONFIG_IOCTL_CFG80211 */ +} u8 rtw_set_chbw_hdl(_adapter *padapter, u8 *pbuf) { @@ -16613,3 +16719,65 @@ u8 rtw_getmacreg_hdl(_adapter *padapter, u8 *pbuf) return H2C_SUCCESS; } + +int rtw_sae_preprocess(_adapter *adapter, const u8 *buf, u32 len, u8 tx) +{ +#ifdef CONFIG_IOCTL_CFG80211 + const u8 *frame_body = buf + sizeof(struct rtw_ieee80211_hdr_3addr); + u16 alg; + u16 seq; + u16 status; + int ret = _FAIL; + + alg = RTW_GET_LE16(frame_body); + if (alg != WLAN_AUTH_SAE) + goto exit; + + seq = RTW_GET_LE16(frame_body + 2); + status = RTW_GET_LE16(frame_body + 4); + + RTW_INFO("RTW_%s:AUTH alg:0x%04x, seq:0x%04x, status:0x%04x, mesg:%s\n", + (tx == _TRUE) ? "Tx" : "Rx", alg, seq, status, + (seq == 1) ? "Commit" : "Confirm"); + + ret = _SUCCESS; + +#ifdef CONFIG_RTW_MESH + if (MLME_IS_MESH(adapter)) { + rtw_mesh_sae_check_frames(adapter, buf, len, tx, alg, seq, status); + goto exit; + } +#endif + + if (tx && (seq == 2) && (status == 0)) { + /* quere commit frame until external auth statue update */ + struct sta_priv *pstapriv = &adapter->stapriv; + struct sta_info *psta = NULL; + _irqL irqL; + + psta = rtw_get_stainfo(pstapriv, GetAddr1Ptr(buf)); + if (psta) { + _enter_critical_bh(&psta->lock, &irqL); + if (psta->pauth_frame) { + rtw_mfree(psta->pauth_frame, psta->auth_len); + psta->pauth_frame = NULL; + psta->auth_len = 0; + } + + psta->pauth_frame = rtw_zmalloc(len); + if (psta->pauth_frame) { + _rtw_memcpy(psta->pauth_frame, buf, len); + psta->auth_len = len; + } + _exit_critical_bh(&psta->lock, &irqL); + + ret = 2; + } + } +exit: + return ret; +#else + return _SUCCESS; +#endif /* CONFIG_IOCTL_CFG80211 */ +} + diff --git a/core/rtw_wlan_util.c b/core/rtw_wlan_util.c index 9699697..fd9b7ca 100644 --- a/core/rtw_wlan_util.c +++ b/core/rtw_wlan_util.c @@ -2461,8 +2461,6 @@ int rtw_get_bcn_keys(ADAPTER *Adapter, u8 *pframe, u32 packet_len, u16 capability; unsigned char *pos; struct rtw_ieee802_11_elems elems; - struct rtw_ieee80211_ht_cap *pht_cap = NULL; - struct HT_info_element *pht_info = NULL; _rtw_memset(recv_beacon, 0, sizeof(*recv_beacon)); @@ -2475,34 +2473,48 @@ int rtw_get_bcn_keys(ADAPTER *Adapter, u8 *pframe, u32 packet_len, if (rtw_ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) return _FALSE; - /* check bw and channel offset */ if (elems.ht_capabilities) { - if (elems.ht_capabilities_len != sizeof(*pht_cap)) + if (elems.ht_capabilities_len != 26) return _FALSE; - - pht_cap = (struct rtw_ieee80211_ht_cap *) elems.ht_capabilities; - recv_beacon->ht_cap_info = pht_cap->cap_info; } if (elems.ht_operation) { - if (elems.ht_operation_len != sizeof(*pht_info)) + if (elems.ht_operation_len != 22) return _FALSE; - - pht_info = (struct HT_info_element *) elems.ht_operation; - recv_beacon->ht_info_infos_0_sco = pht_info->infos[0] & 0x03; } - /* Checking for channel */ - if (elems.ds_params && elems.ds_params_len == sizeof(recv_beacon->bcn_channel)) - _rtw_memcpy(&recv_beacon->bcn_channel, elems.ds_params, - sizeof(recv_beacon->bcn_channel)); - else if (pht_info) - /* In 5G, some ap do not have DSSET IE checking HT info for channel */ - recv_beacon->bcn_channel = pht_info->primary_channel; - else { + if (elems.vht_capabilities) { + if (elems.vht_capabilities_len != 12) + return _FALSE; + } + + if (elems.vht_operation) { + if (elems.vht_operation_len != 5) + return _FALSE; + } + + if (rtw_ies_get_supported_rate(pos, left, recv_beacon->rate_set, &recv_beacon->rate_num) == _FAIL) + return _FALSE; + + if (cckratesonly_included(recv_beacon->rate_set, recv_beacon->rate_num) == _TRUE) + recv_beacon->proto_cap |= PROTO_CAP_11B; + else if (cckrates_included(recv_beacon->rate_set, recv_beacon->rate_num) == _TRUE) + recv_beacon->proto_cap |= PROTO_CAP_11B | PROTO_CAP_11G; + else + recv_beacon->proto_cap |= PROTO_CAP_11G; + + if (elems.ht_capabilities && elems.ht_operation) + recv_beacon->proto_cap |= PROTO_CAP_11N; + + if (elems.vht_capabilities && elems.vht_operation) + recv_beacon->proto_cap |= PROTO_CAP_11AC; + + /* check bw and channel offset */ + rtw_ies_get_chbw(pos, left, &recv_beacon->ch, &recv_beacon->bw, &recv_beacon->offset, 1, 1); + if (!recv_beacon->ch) { /* we don't find channel IE, so don't check it */ /* RTW_INFO("Oops: %s we don't find channel IE, so don't check it\n", __func__); */ - recv_beacon->bcn_channel = Adapter->mlmeextpriv.cur_channel; + recv_beacon->ch = Adapter->mlmeextpriv.cur_channel; } /* checking SSID */ @@ -2512,22 +2524,21 @@ int rtw_get_bcn_keys(ADAPTER *Adapter, u8 *pframe, u32 packet_len, _rtw_memcpy(recv_beacon->ssid, elems.ssid, elems.ssid_len); recv_beacon->ssid_len = elems.ssid_len; - } else - ; /* means hidden ssid */ + } /* checking RSN first */ if (elems.rsn_ie && elems.rsn_ie_len) { recv_beacon->encryp_protocol = ENCRYP_PROTOCOL_WPA2; rtw_parse_wpa2_ie(elems.rsn_ie - 2, elems.rsn_ie_len + 2, &recv_beacon->group_cipher, &recv_beacon->pairwise_cipher, - &recv_beacon->is_8021x, NULL); + &recv_beacon->akm, NULL); } /* checking WPA secon */ else if (elems.wpa_ie && elems.wpa_ie_len) { recv_beacon->encryp_protocol = ENCRYP_PROTOCOL_WPA; rtw_parse_wpa_ie(elems.wpa_ie - 2, elems.wpa_ie_len + 2, &recv_beacon->group_cipher, &recv_beacon->pairwise_cipher, - &recv_beacon->is_8021x); + &recv_beacon->akm); } else if (capability & BIT(4)) recv_beacon->encryp_protocol = ENCRYP_PROTOCOL_WEP; @@ -2543,61 +2554,48 @@ int rtw_get_bcn_keys(ADAPTER *Adapter, u8 *pframe, u32 packet_len, return _TRUE; } -void rtw_dump_bcn_keys(struct beacon_keys *recv_beacon) +void rtw_dump_bcn_keys(void *sel, struct beacon_keys *recv_beacon) { u8 ssid[IW_ESSID_MAX_SIZE + 1]; _rtw_memcpy(ssid, recv_beacon->ssid, recv_beacon->ssid_len); ssid[recv_beacon->ssid_len] = '\0'; - RTW_INFO("%s: ssid = %s\n", __func__, ssid); - RTW_INFO("%s: channel = %d\n", __func__, recv_beacon->bcn_channel); - RTW_INFO("%s: ht_cap = 0x%04x\n", __func__, recv_beacon->ht_cap_info); - RTW_INFO("%s: ht_info_infos_0_sco = 0x%02x\n", __func__, recv_beacon->ht_info_infos_0_sco); - RTW_INFO("%s: sec=%d, group = %x, pair = %x, 8021X = %x\n", __func__, - recv_beacon->encryp_protocol, recv_beacon->group_cipher, - recv_beacon->pairwise_cipher, recv_beacon->is_8021x); + RTW_PRINT_SEL(sel, "ssid = %s (len = %u)\n", ssid, recv_beacon->ssid_len); + RTW_PRINT_SEL(sel, "ch = %u,%u,%u\n" + , recv_beacon->ch, recv_beacon->bw, recv_beacon->offset); + RTW_PRINT_SEL(sel, "proto_cap = 0x%02x\n", recv_beacon->proto_cap); + RTW_MAP_DUMP_SEL(sel, "rate_set = " + , recv_beacon->rate_set, recv_beacon->rate_num); + RTW_PRINT_SEL(sel, "sec = %d, group = 0x%x, pair = 0x%x, akm = 0x%08x\n" + , recv_beacon->encryp_protocol, recv_beacon->group_cipher + , recv_beacon->pairwise_cipher, recv_beacon->akm); } -#define DBG_BCN_CNT + int rtw_check_bcn_info(ADAPTER *Adapter, u8 *pframe, u32 packet_len) { - unsigned int len; +#define BCNKEY_VERIFY_PROTO_CAP 0 +#define BCNKEY_VERIFY_WHOLE_RATE_SET 0 + u8 *pbssid = GetAddr3Ptr(pframe); struct mlme_priv *pmlmepriv = &Adapter->mlmepriv; struct wlan_network *cur_network = &(Adapter->mlmepriv.cur_network); + struct beacon_keys *cur_beacon = &pmlmepriv->cur_beacon_keys; struct beacon_keys recv_beacon; + int ret = 0; if (is_client_associated_to_ap(Adapter) == _FALSE) - return _TRUE; - - len = packet_len - sizeof(struct rtw_ieee80211_hdr_3addr); - - if (len > MAX_IE_SZ) { - RTW_WARN("%s IE too long for survey event\n", __func__); - return _FAIL; - } - - if (_rtw_memcmp(cur_network->network.MacAddress, pbssid, 6) == _FALSE) { - RTW_WARN("Oops: rtw_check_network_encrypt linked but recv other bssid bcn\n" MAC_FMT MAC_FMT, - MAC_ARG(pbssid), MAC_ARG(cur_network->network.MacAddress)); - return _TRUE; - } + goto exit_success; if (rtw_get_bcn_keys(Adapter, pframe, packet_len, &recv_beacon) == _FALSE) - return _TRUE; /* parsing failed => broken IE */ + goto exit_success; /* parsing failed => broken IE */ #ifdef DBG_RX_BCN rtw_debug_bcn(Adapter, pframe, packet_len); #endif - /* don't care hidden ssid, use current beacon ssid directly */ - if (recv_beacon.ssid_len == 0) { - _rtw_memcpy(recv_beacon.ssid, pmlmepriv->cur_beacon_keys.ssid, - pmlmepriv->cur_beacon_keys.ssid_len); - recv_beacon.ssid_len = pmlmepriv->cur_beacon_keys.ssid_len; - } #ifdef CONFIG_BCN_CNT_CONFIRM_HDL - if (_rtw_memcmp(&recv_beacon, &pmlmepriv->cur_beacon_keys, sizeof(recv_beacon)) == _TRUE) + if (_rtw_memcmp(&recv_beacon, cur_beacon, sizeof(recv_beacon)) == _TRUE) pmlmepriv->new_beacon_cnts = 0; else if ((pmlmepriv->new_beacon_cnts == 0) || _rtw_memcmp(&recv_beacon, &pmlmepriv->new_beacon_keys, sizeof(recv_beacon)) == _FALSE) { @@ -2605,11 +2603,11 @@ int rtw_check_bcn_info(ADAPTER *Adapter, u8 *pframe, u32 packet_len) if (pmlmepriv->new_beacon_cnts == 0) { RTW_ERR("%s: cur beacon key\n", __func__); - RTW_DBG_EXPR(rtw_dump_bcn_keys(&pmlmepriv->cur_beacon_keys)); + RTW_DBG_EXPR(rtw_dump_bcn_keys(RTW_DBGDUMP, cur_beacon)); } RTW_DBG("%s: new beacon key\n", __func__); - RTW_DBG_EXPR(rtw_dump_bcn_keys(&recv_beacon)); + RTW_DBG_EXPR(rtw_dump_bcn_keys(RTW_DBGDUMP, &recv_beacon)); _rtw_memcpy(&pmlmepriv->new_beacon_keys, &recv_beacon, sizeof(recv_beacon)); pmlmepriv->new_beacon_cnts = 1; @@ -2621,45 +2619,47 @@ int rtw_check_bcn_info(ADAPTER *Adapter, u8 *pframe, u32 packet_len) /* if counter >= max, it means beacon is changed really */ if (pmlmepriv->new_beacon_cnts >= new_bcn_max) #else - if (_rtw_memcmp(&recv_beacon, &pmlmepriv->cur_beacon_keys, sizeof(recv_beacon)) == _FALSE) + if (_rtw_memcmp(&recv_beacon, cur_beacon, sizeof(recv_beacon)) == _FALSE) #endif { - /* check bw mode change only? */ - pmlmepriv->cur_beacon_keys.ht_cap_info = recv_beacon.ht_cap_info; - pmlmepriv->cur_beacon_keys.ht_info_infos_0_sco = recv_beacon.ht_info_infos_0_sco; - if (_rtw_memcmp(&recv_beacon, &pmlmepriv->cur_beacon_keys, - sizeof(recv_beacon)) == _FALSE) { - /* beacon is changed, have to do disconnect/connect */ - RTW_WARN("%s: new beacon occur!!\n", __func__); - #ifdef DBG_BCN_CNT - rtw_dump_bcn_keys(&recv_beacon); - #endif - return _FAIL; + struct beacon_keys tmp_beacon; + + RTW_INFO(FUNC_ADPT_FMT" new beacon occur!!\n", FUNC_ADPT_ARG(Adapter)); + RTW_INFO(FUNC_ADPT_FMT" cur beacon key:\n", FUNC_ADPT_ARG(Adapter)); + rtw_dump_bcn_keys(RTW_DBGDUMP, cur_beacon); + RTW_INFO(FUNC_ADPT_FMT" new beacon key:\n", FUNC_ADPT_ARG(Adapter)); + rtw_dump_bcn_keys(RTW_DBGDUMP, &recv_beacon); + + if (!rtw_is_chbw_grouped(cur_beacon->ch, cur_beacon->bw, cur_beacon->offset + , recv_beacon.ch, recv_beacon.bw, recv_beacon.offset)) + goto exit; + + _rtw_memcpy(&tmp_beacon, cur_beacon, sizeof(tmp_beacon)); + + /* check fields excluding below */ + tmp_beacon.ch = recv_beacon.ch; + tmp_beacon.bw = recv_beacon.bw; + tmp_beacon.offset = recv_beacon.offset; + if (!BCNKEY_VERIFY_PROTO_CAP) + tmp_beacon.proto_cap = recv_beacon.proto_cap; + if (!BCNKEY_VERIFY_WHOLE_RATE_SET) { + tmp_beacon.rate_num = recv_beacon.rate_num; + _rtw_memcpy(tmp_beacon.rate_set, recv_beacon.rate_set, 12); } - #ifdef DBG_BCN_CNT - RTW_INFO("%s bw mode change\n", __func__); - RTW_INFO("%s bcn now: ht_cap_info:%x ht_info_infos_0:%x\n", __func__, - cur_network->BcnInfo.ht_cap_info, - cur_network->BcnInfo.ht_info_infos_0); - #endif + if (_rtw_memcmp(&tmp_beacon, &recv_beacon, sizeof(recv_beacon)) == _FALSE) + goto exit; - cur_network->BcnInfo.ht_cap_info = recv_beacon.ht_cap_info; - cur_network->BcnInfo.ht_info_infos_0 = - (cur_network->BcnInfo.ht_info_infos_0 & (~0x03)) | - recv_beacon.ht_info_infos_0_sco; - - #ifdef DBG_BCN_CNT - RTW_INFO("%s bcn link: ht_cap_info:%x ht_info_infos_0:%x\n", __func__, - cur_network->BcnInfo.ht_cap_info, - cur_network->BcnInfo.ht_info_infos_0); - #endif - _rtw_memcpy(&pmlmepriv->cur_beacon_keys, &recv_beacon, sizeof(recv_beacon)); + _rtw_memcpy(cur_beacon, &recv_beacon, sizeof(recv_beacon)); #ifdef CONFIG_BCN_CNT_CONFIRM_HDL pmlmepriv->new_beacon_cnts = 0; #endif } - return _SUCCESS; +exit_success: + ret = 1; + +exit: + return ret; } void update_beacon_info(_adapter *padapter, u8 *pframe, uint pkt_len, struct sta_info *psta) diff --git a/dkms-install.sh b/dkms-install.sh index 656e866..7cf2e95 100755 --- a/dkms-install.sh +++ b/dkms-install.sh @@ -9,7 +9,7 @@ fi DRV_DIR="$(pwd)" DRV_NAME=rtl8812au -DRV_VERSION=5.6.4 +DRV_VERSION=5.6.4.1 cp -r ${DRV_DIR} /usr/src/${DRV_NAME}-${DRV_VERSION} diff --git a/dkms-remove.sh b/dkms-remove.sh index c0ae762..8f1d53e 100755 --- a/dkms-remove.sh +++ b/dkms-remove.sh @@ -9,7 +9,7 @@ fi DRV_DIR="$(pwd)" DRV_NAME=rtl8812au -DRV_VERSION=5.6.4 +DRV_VERSION=5.6.4.1 dkms remove ${DRV_NAME}/${DRV_VERSION} --all rm -rf /usr/src/${DRV_NAME}-${DRV_VERSION} diff --git a/dkms.conf b/dkms.conf index 38d3890..03d15a9 100644 --- a/dkms.conf +++ b/dkms.conf @@ -1,5 +1,5 @@ PACKAGE_NAME="realtek-rtl88xxau" -PACKAGE_VERSION="5.6.4~20190617" +PACKAGE_VERSION="5.6.4.1~20190622" CLEAN="'make' clean" BUILT_MODULE_NAME[0]=88XXau PROCS_NUM=`nproc` diff --git a/hal/hal_mp.c b/hal/hal_mp.c index c876a02..1af9de3 100644 --- a/hal/hal_mp.c +++ b/hal/hal_mp.c @@ -1009,8 +1009,7 @@ void mpt_SetRFPath_8812A(PADAPTER pAdapter) } switch (ulAntennaRx) { - // kimocoder edit below - u32 reg0xC50; + u32 reg0xC50 = 0; case ANTENNA_A: phy_set_bb_reg(pAdapter, rRxPath_Jaguar, bMaskByte0, 0x11); phy_set_rf_reg(pAdapter, RF_PATH_B, RF_AC_Jaguar, 0xF0000, 0x1); /*/ RF_B_0x0[19:16] = 1, Standby mode*/ diff --git a/hal/rtl8812a/usb/rtl8812au_xmit.c b/hal/rtl8812a/usb/rtl8812au_xmit.c index c62a13b..82e09bb 100644 --- a/hal/rtl8812a/usb/rtl8812au_xmit.c +++ b/hal/rtl8812a/usb/rtl8812au_xmit.c @@ -430,7 +430,7 @@ u32 upload_txpktbuf_8812au(_adapter *adapter, u8 *buf, u32 buflen) } rtw_write32(adapter, REG_PKTBUF_DBG_CTRL, 0xff800000+(beacon_head<<6) + qw_addr); loop_cnt = 0; - while ((rtw_read32(adapter, REG_PKTBUF_DBG_CTRL) & BIT23) != 0) { + while ((rtw_read32(adapter, REG_PKTBUF_DBG_CTRL) & BIT23) == 1) { rtw_udelay_os(10); if (loop_cnt++ == 100) return _FALSE; diff --git a/include/autoconf.h b/include/autoconf.h index b3c5ad3..d88036b 100644 --- a/include/autoconf.h +++ b/include/autoconf.h @@ -22,7 +22,7 @@ */ #define AUTOCONF_INCLUDED #define RTL871X_MODULE_NAME "8812AU" -//#define DRV_NAME "rtl8812au" +#define DRV_NAME "rtl8812au" #define CONFIG_USB_HCI diff --git a/include/ieee80211.h b/include/ieee80211.h index c87a5b2..6fac3d2 100644 --- a/include/ieee80211.h +++ b/include/ieee80211.h @@ -142,8 +142,6 @@ extern u8 WPA_CIPHER_SUITE_WEP104[]; #define RSN_SELECTOR_LEN 4 extern u16 RSN_VERSION_BSD; -extern u8 RSN_AUTH_KEY_MGMT_UNSPEC_802_1X[]; -extern u8 RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X[]; extern u8 RSN_CIPHER_SUITE_NONE[]; extern u8 RSN_CIPHER_SUITE_WEP40[]; extern u8 RSN_CIPHER_SUITE_TKIP[]; @@ -151,6 +149,39 @@ extern u8 RSN_CIPHER_SUITE_WRAP[]; extern u8 RSN_CIPHER_SUITE_CCMP[]; extern u8 RSN_CIPHER_SUITE_WEP104[]; +/* AKM suite type */ +extern u8 WLAN_AKM_8021X[]; +extern u8 WLAN_AKM_PSK[]; +extern u8 WLAN_AKM_FT_8021X[]; +extern u8 WLAN_AKM_FT_PSK[]; +extern u8 WLAN_AKM_8021X_SHA256[]; +extern u8 WLAN_AKM_PSK_SHA256[]; +extern u8 WLAN_AKM_TDLS[]; +extern u8 WLAN_AKM_SAE[]; +extern u8 WLAN_AKM_FT_OVER_SAE[]; +extern u8 WLAN_AKM_8021X_SUITE_B[]; +extern u8 WLAN_AKM_8021X_SUITE_B_192[]; +extern u8 WLAN_AKM_FILS_SHA256[]; +extern u8 WLAN_AKM_FILS_SHA384[]; +extern u8 WLAN_AKM_FT_FILS_SHA256[]; +extern u8 WLAN_AKM_FT_FILS_SHA384[]; + +#define WLAN_AKM_TYPE_8021X BIT(0) +#define WLAN_AKM_TYPE_PSK BIT(1) +#define WLAN_AKM_TYPE_FT_8021X BIT(2) +#define WLAN_AKM_TYPE_FT_PSK BIT(3) +#define WLAN_AKM_TYPE_8021X_SHA256 BIT(4) +#define WLAN_AKM_TYPE_PSK_SHA256 BIT(5) +#define WLAN_AKM_TYPE_TDLS BIT(6) +#define WLAN_AKM_TYPE_SAE BIT(7) +#define WLAN_AKM_TYPE_FT_OVER_SAE BIT(8) +#define WLAN_AKM_TYPE_8021X_SUITE_B BIT(9) +#define WLAN_AKM_TYPE_8021X_SUITE_B_192 BIT(10) +#define WLAN_AKM_TYPE_FILS_SHA256 BIT(11) +#define WLAN_AKM_TYPE_FILS_SHA384 BIT(12) +#define WLAN_AKM_TYPE_FT_FILS_SHA256 BIT(13) +#define WLAN_AKM_TYPE_FT_FILS_SHA384 BIT(14) + /* IEEE 802.11i */ #define PMKID_LEN 16 #define PMK_LEN 32 @@ -665,6 +696,7 @@ struct ieee80211_snap_hdr { /* Authentication algorithms */ #define WLAN_AUTH_OPEN 0 #define WLAN_AUTH_SHARED_KEY 1 +#define WLAN_AUTH_SAE 3 #define WLAN_AUTH_CHALLENGE_LEN 128 @@ -2070,8 +2102,8 @@ unsigned char *rtw_get_wpa2_ie(unsigned char *pie, int *rsn_ie_len, int limit); int rtw_get_wpa_cipher_suite(u8 *s); int rtw_get_wpa2_cipher_suite(u8 *s); int rtw_get_wapi_ie(u8 *in_ie, uint in_len, u8 *wapi_ie, u16 *wapi_len); -int rtw_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher, int *pairwise_cipher, int *is_8021x); -int rtw_parse_wpa2_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher, int *pairwise_cipher, int *is_8021x, u8 *mfp_opt); +int rtw_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher, int *pairwise_cipher, u32 *akm); +int rtw_parse_wpa2_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher, int *pairwise_cipher, u32 *akm, u8 *mfp_opt); int rtw_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, u8 *wpa_ie, u16 *wpa_len); @@ -2147,8 +2179,6 @@ uint rtw_is_cckratesonly_included(u8 *rate); uint rtw_get_cckrate_size(u8 *rate,u32 rate_length); int rtw_check_network_type(unsigned char *rate, int ratelen, int channel); -void rtw_get_bcn_info(struct wlan_network *pnetwork); - u8 rtw_check_invalid_mac_address(u8 *mac_addr, u8 check_local_bit); void rtw_macaddr_cfg(u8 *out, const u8 *hw_mac_addr); diff --git a/include/osdep_service.h b/include/osdep_service.h index 35988cc..7d13e01 100644 --- a/include/osdep_service.h +++ b/include/osdep_service.h @@ -64,6 +64,8 @@ #define BIT(x) (1 << (x)) #endif +#define CHECK_BIT(a, b) (!!((a) & (b))) + #define BIT0 0x00000001 #define BIT1 0x00000002 #define BIT2 0x00000004 diff --git a/include/rtw_android.h b/include/rtw_android.h index efc981a..f9d6b49 100644 --- a/include/rtw_android.h +++ b/include/rtw_android.h @@ -71,6 +71,7 @@ enum ANDROID_WIFI_CMD { #endif /* CONFIG_GTK_OL */ ANDROID_WIFI_CMD_P2P_DISABLE, ANDROID_WIFI_CMD_SET_AEK, + ANDROID_WIFI_CMD_EXT_AUTH_STATUS, ANDROID_WIFI_CMD_DRIVERVERSION, ANDROID_WIFI_CMD_MAX }; diff --git a/include/rtw_mlme.h b/include/rtw_mlme.h index 8f2646c..cdac712 100644 --- a/include/rtw_mlme.h +++ b/include/rtw_mlme.h @@ -529,13 +529,16 @@ enum { struct beacon_keys { u8 ssid[IW_ESSID_MAX_SIZE]; u32 ssid_len; - u8 bcn_channel; - u16 ht_cap_info; - u8 ht_info_infos_0_sco; /* bit0 & bit1 in infos[0] is second channel offset */ + u8 ch; + u8 bw; + u8 offset; + u8 proto_cap; /* PROTO_CAP_XXX */ + u8 rate_set[12]; + u8 rate_num; int encryp_protocol; int pairwise_cipher; int group_cipher; - int is_8021x; + u32 akm; }; #ifdef CONFIG_RTW_80211R #define RTW_FT_ACTION_REQ_LMT 4 @@ -1203,6 +1206,9 @@ void rtw_scan_abort_no_wait(_adapter *adapter); void rtw_scan_abort(_adapter *adapter); u32 rtw_join_abort_timeout(_adapter *adapter, u32 timeout_ms); +int rtw_cached_pmkid(_adapter *Adapter, u8 *bssid); +int rtw_rsn_sync_pmkid(_adapter *adapter, u8 *ie, uint ie_len, int i_ent); + extern int rtw_restruct_sec_ie(_adapter *adapter, u8 *out_ie); #ifdef CONFIG_WMMPS_STA void rtw_uapsd_use_default_setting(_adapter *padapter); diff --git a/include/rtw_mlme_ext.h b/include/rtw_mlme_ext.h index f198489..49fcaa9 100644 --- a/include/rtw_mlme_ext.h +++ b/include/rtw_mlme_ext.h @@ -276,6 +276,10 @@ enum TDLS_option { #endif /* CONFIG_TDLS */ +#ifndef NL80211_AUTHTYPE_SAE +#define NL80211_AUTHTYPE_SAE 4 +#endif + /* * Usage: * When one iface acted as AP mode and the other iface is STA mode and scanning, @@ -679,6 +683,8 @@ void change_band_update_ie(_adapter *padapter, WLAN_BSSID_EX *pnetwork, u8 ch); void Set_MSR(_adapter *padapter, u8 type); +void rtw_set_external_auth_status(_adapter *padapter, const void *data, int len); + u8 rtw_get_oper_ch(_adapter *adapter); void rtw_set_oper_ch(_adapter *adapter, u8 ch); u8 rtw_get_oper_bw(_adapter *adapter); @@ -765,7 +771,7 @@ void rtw_absorb_ssid_ifneed(_adapter *padapter, WLAN_BSSID_EX *bssid, u8 *pframe int rtw_get_bcn_keys(ADAPTER *Adapter, u8 *pframe, u32 packet_len, struct beacon_keys *recv_beacon); int validate_beacon_len(u8 *pframe, uint len); -void rtw_dump_bcn_keys(struct beacon_keys *recv_beacon); +void rtw_dump_bcn_keys(void *sel, struct beacon_keys *recv_beacon); int rtw_check_bcn_info(ADAPTER *Adapter, u8 *pframe, u32 packet_len); void update_beacon_info(_adapter *padapter, u8 *pframe, uint len, struct sta_info *psta); #ifdef CONFIG_DFS @@ -1115,6 +1121,8 @@ u8 tdls_hdl(_adapter *padapter, unsigned char *pbuf); u8 run_in_thread_hdl(_adapter *padapter, u8 *pbuf); u8 rtw_getmacreg_hdl(_adapter *padapter, u8 *pbuf); +int rtw_sae_preprocess(_adapter *adapter, const u8 *buf, u32 len, u8 tx); + #define GEN_DRV_CMD_HANDLER(size, cmd) {size, &cmd ## _hdl}, #define GEN_MLME_EXT_HANDLER(size, cmd) {size, cmd}, diff --git a/include/rtw_security.h b/include/rtw_security.h index ac8432e..36756e1 100644 --- a/include/rtw_security.h +++ b/include/rtw_security.h @@ -176,6 +176,9 @@ struct security_priv { u8 bcheck_grpkey; u8 bgrpkey_handshake; + u8 auth_alg; + u8 auth_type; + u8 extauth_status; /* u8 packet_cnt; */ /* unused, removed */ s32 sw_encrypt;/* from registry_priv */ @@ -494,4 +497,10 @@ u8 rtw_handle_tkip_countermeasure(_adapter *adapter, const char *caller); u16 rtw_calc_crc(u8 *pdata, int length); #endif /*CONFIG_WOWLAN*/ +#define rtw_sec_chk_auth_alg(a, s) \ + ((a)->securitypriv.auth_alg == (s)) + +#define rtw_sec_chk_auth_type(a, s) \ + ((a)->securitypriv.auth_type == (s)) + #endif /* __RTL871X_SECURITY_H_ */ diff --git a/include/rtw_version.h b/include/rtw_version.h index d540441..44aeb0e 100644 --- a/include/rtw_version.h +++ b/include/rtw_version.h @@ -1 +1 @@ -#define DRIVERVERSION "v5.6.4_33522.20190509" +#define DRIVERVERSION "v5.6.4.1_33916.20190619" diff --git a/include/sta_info.h b/include/sta_info.h index 9e100cb..762933a 100644 --- a/include/sta_info.h +++ b/include/sta_info.h @@ -401,6 +401,8 @@ struct sta_info { int wpa_pairwise_cipher; int wpa2_pairwise_cipher; + u32 akm_suite_type; + u8 bpairwise_key_installed; #ifdef CONFIG_RTW_80211R u8 ft_pairwise_key_installed; @@ -478,6 +480,8 @@ struct sta_info { #endif #ifdef CONFIG_IOCTL_CFG80211 + u8 *pauth_frame; + u32 auth_len; u8 *passoc_req; u32 assoc_req_len; #endif diff --git a/include/wlan_bssdef.h b/include/wlan_bssdef.h index b3296d5..167ae7f 100644 --- a/include/wlan_bssdef.h +++ b/include/wlan_bssdef.h @@ -627,7 +627,6 @@ struct wlan_network { int aid; /* will only be valid when a BSS is joinned. */ int join_res; WLAN_BSSID_EX network; /* must be the last item */ - WLAN_BCN_INFO BcnInfo; #ifdef PLATFORM_WINDOWS unsigned char iebuf[MAX_IE_SZ]; #endif diff --git a/os_dep/linux/ioctl_cfg80211.c b/os_dep/linux/ioctl_cfg80211.c index 9a7ba15..cead11b 100755 --- a/os_dep/linux/ioctl_cfg80211.c +++ b/os_dep/linux/ioctl_cfg80211.c @@ -407,6 +407,23 @@ static void rtw_get_chbw_from_cfg80211_chan_def(struct cfg80211_chan_def *chdef, #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)) */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) +bool rtw_cfg80211_allow_ch_switch_notify(_adapter *adapter) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)) + if ((!MLME_IS_AP(adapter)) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)) + && (!MLME_IS_ADHOC(adapter)) + && (!MLME_IS_ADHOC_MASTER(adapter)) + && (!MLME_IS_MESH(adapter)) +#elif defined(CONFIG_RTW_MESH) + && (!MLME_IS_MESH(adapter)) +#endif + ) + return 0; +#endif + return 1; +} + u8 rtw_cfg80211_ch_switch_notify(_adapter *adapter, u8 ch, u8 bw, u8 offset, u8 ht) { struct wiphy *wiphy = adapter_to_wiphy(adapter); @@ -415,6 +432,9 @@ u8 rtw_cfg80211_ch_switch_notify(_adapter *adapter, u8 ch, u8 bw, u8 offset, u8 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)) struct cfg80211_chan_def chdef; + if (!rtw_cfg80211_allow_ch_switch_notify(adapter)) + goto exit; + ret = rtw_chbw_to_cfg80211_chan_def(wiphy, &chdef, ch, bw, offset, ht); if (ret != _SUCCESS) goto exit; @@ -425,6 +445,9 @@ u8 rtw_cfg80211_ch_switch_notify(_adapter *adapter, u8 ch, u8 bw, u8 offset, u8 int freq = rtw_ch2freq(ch); enum nl80211_channel_type ctype; + if (!rtw_cfg80211_allow_ch_switch_notify(adapter)) + goto exit; + if (!freq) { ret = _FAIL; goto exit; @@ -537,6 +560,7 @@ static const struct ieee80211_txrx_stypes [NL80211_IFTYPE_STATION] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) }, [NL80211_IFTYPE_AP] = { @@ -3263,6 +3287,7 @@ static int rtw_cfg80211_set_auth_type(struct security_priv *psecuritypriv, { RTW_INFO("%s, nl80211_auth_type=%d\n", __func__, sme_auth_type); + psecuritypriv->auth_type = sme_auth_type; switch (sme_auth_type) { case NL80211_AUTHTYPE_AUTOMATIC: @@ -3290,6 +3315,9 @@ static int rtw_cfg80211_set_auth_type(struct security_priv *psecuritypriv, psecuritypriv->ndisencryptstatus = Ndis802_11Encryption1Enabled; + break; + case NL80211_AUTHTYPE_SAE: + psecuritypriv->auth_alg = WLAN_AUTH_SAE; break; default: psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_Open; @@ -3758,6 +3786,38 @@ static int _rtw_disconnect(struct wiphy *wiphy, struct net_device *ndev) return 0; } +#if (KERNEL_VERSION(4, 17, 0) > LINUX_VERSION_CODE) +static bool rtw_check_connect_sae_compat(struct cfg80211_connect_params *sme) +{ + struct rtw_ieee802_11_elems elems; + struct rsne_info info; + u8 AKM_SUITE_SAE[] = { 0x00, 0x0f, 0xac, 8 }; + int i; + + if (sme->auth_type != 1) + return false; + + if (rtw_ieee802_11_parse_elems((u8 *)sme->ie, sme->ie_len, &elems, 0) + == ParseFailed) + return false; + + if (!elems.rsn_ie) + return false; + + if (rtw_rsne_info_parse(elems.rsn_ie - 2, elems.rsn_ie_len + 2, &info) == _FAIL) + return false; + + for (i = 0; i < info.akm_cnt; i++) + if (memcmp(info.akm_list + i * RSN_SELECTOR_LEN, + AKM_SUITE_SAE, RSN_SELECTOR_LEN) == 0) + return true; + + return false; +} +#else +#define rtw_check_connect_sae_compat(sme) false +#endif + static int cfg80211_rtw_connect(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_connect_params *sme) { @@ -3780,6 +3840,11 @@ static int cfg80211_rtw_connect(struct wiphy *wiphy, struct net_device *ndev, RTW_INFO("privacy=%d, key=%p, key_len=%d, key_idx=%d, auth_type=%d\n", sme->privacy, sme->key, sme->key_len, sme->key_idx, sme->auth_type); + if (rtw_check_connect_sae_compat(sme)) { + sme->auth_type = NL80211_AUTHTYPE_SAE; + RTW_INFO("%s set sme->auth_type=4 for SAE compat\n", __FUNCTION__); + } + if (pwdev_priv->block == _TRUE) { ret = -EBUSY; RTW_INFO("%s wdev_priv.block is set\n", __FUNCTION__); @@ -3850,6 +3915,8 @@ static int cfg80211_rtw_connect(struct wiphy *wiphy, struct net_device *ndev, psecuritypriv->dot118021XGrpPrivacy = _NO_PRIVACY_; psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_Open; /* open system */ psecuritypriv->ndisauthtype = Ndis802_11AuthModeOpen; + psecuritypriv->auth_alg = WLAN_AUTH_OPEN; + psecuritypriv->extauth_status = WLAN_STATUS_UNSPECIFIED_FAILURE; #ifdef CONFIG_WAPI_SUPPORT padapter->wapiInfo.bWapiEnable = false; @@ -4116,36 +4183,20 @@ static int cfg80211_rtw_set_power_mgmt(struct wiphy *wiphy, return 0; } -static int cfg80211_rtw_set_pmksa(struct wiphy *wiphy, - struct net_device *ndev, - struct cfg80211_pmksa *pmksa) +static void _rtw_set_pmksa(struct net_device *ndev, + u8 *bssid, u8 *pmkid) { - u8 index, blInserted = _FALSE; _adapter *padapter = (_adapter *)rtw_netdev_priv(ndev); - struct mlme_priv *mlme = &padapter->mlmepriv; - struct security_priv *psecuritypriv = &padapter->securitypriv; - u8 strZeroMacAddress[ETH_ALEN] = { 0x00 }; - - RTW_INFO(FUNC_NDEV_FMT" "MAC_FMT" "KEY_FMT"\n", FUNC_NDEV_ARG(ndev) - , MAC_ARG(pmksa->bssid), KEY_ARG(pmksa->pmkid)); - - if (_rtw_memcmp((u8 *)pmksa->bssid, strZeroMacAddress, ETH_ALEN) == _TRUE) - return -EINVAL; - - if (check_fwstate(mlme, _FW_LINKED) == _FALSE) { - RTW_INFO(FUNC_NDEV_FMT" not set pmksa cause not in linked state\n", FUNC_NDEV_ARG(ndev)); - return -EINVAL; - } - - blInserted = _FALSE; + struct security_priv *psecuritypriv = &padapter->securitypriv; + u8 index, blInserted = _FALSE; /* overwrite PMKID */ for (index = 0 ; index < NUM_PMKID_CACHE; index++) { - if (_rtw_memcmp(psecuritypriv->PMKIDList[index].Bssid, (u8 *)pmksa->bssid, ETH_ALEN) == _TRUE) { + if (_rtw_memcmp(psecuritypriv->PMKIDList[index].Bssid, bssid, ETH_ALEN) == _TRUE) { /* BSSID is matched, the same AP => rewrite with new PMKID. */ - RTW_INFO(FUNC_NDEV_FMT" BSSID exists in the PMKList.\n", FUNC_NDEV_ARG(ndev)); + RTW_INFO("BSSID("MAC_FMT") exists in the PMKList.\n", MAC_ARG(bssid)); - _rtw_memcpy(psecuritypriv->PMKIDList[index].PMKID, (u8 *)pmksa->pmkid, WLAN_PMKID_LEN); + _rtw_memcpy(psecuritypriv->PMKIDList[index].PMKID, pmkid, WLAN_PMKID_LEN); psecuritypriv->PMKIDList[index].bUsed = _TRUE; psecuritypriv->PMKIDIndex = index + 1; blInserted = _TRUE; @@ -4155,17 +4206,48 @@ static int cfg80211_rtw_set_pmksa(struct wiphy *wiphy, if (!blInserted) { /* Find a new entry */ - RTW_INFO(FUNC_NDEV_FMT" Use the new entry index = %d for this PMKID.\n", - FUNC_NDEV_ARG(ndev), psecuritypriv->PMKIDIndex); + RTW_INFO("Use the new entry index = %d for this PMKID.\n", + psecuritypriv->PMKIDIndex); - _rtw_memcpy(psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].Bssid, (u8 *)pmksa->bssid, ETH_ALEN); - _rtw_memcpy(psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].PMKID, (u8 *)pmksa->pmkid, WLAN_PMKID_LEN); + _rtw_memcpy(psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].Bssid, bssid, ETH_ALEN); + _rtw_memcpy(psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].PMKID, pmkid, WLAN_PMKID_LEN); psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].bUsed = _TRUE; psecuritypriv->PMKIDIndex++ ; if (psecuritypriv->PMKIDIndex == 16) psecuritypriv->PMKIDIndex = 0; } +} + +static int cfg80211_rtw_set_pmksa(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_pmksa *pmksa) +{ + u8 index, blInserted = _FALSE; + _adapter *padapter = (_adapter *)rtw_netdev_priv(ndev); + struct mlme_priv *mlme = &padapter->mlmepriv; + struct security_priv *psecuritypriv = &padapter->securitypriv; + u8 strZeroMacAddress[ETH_ALEN] = { 0x00 }; + bool sae_auth = rtw_sec_chk_auth_type(padapter, NL80211_AUTHTYPE_SAE); + + RTW_INFO(FUNC_NDEV_FMT" "MAC_FMT" "KEY_FMT"\n", FUNC_NDEV_ARG(ndev) + , MAC_ARG(pmksa->bssid), KEY_ARG(pmksa->pmkid)); + + if (_rtw_memcmp((u8 *)pmksa->bssid, strZeroMacAddress, ETH_ALEN) == _TRUE) + return -EINVAL; + + if (check_fwstate(mlme, _FW_LINKED) == _FALSE && !sae_auth) { + RTW_INFO(FUNC_NDEV_FMT" not set pmksa cause not in linked state\n", FUNC_NDEV_ARG(ndev)); + return -EINVAL; + } + + _rtw_set_pmksa(ndev, (u8 *)pmksa->bssid, (u8 *)pmksa->pmkid); + + if (sae_auth && + (psecuritypriv->extauth_status == WLAN_STATUS_SUCCESS)) { + RTW_PRINT("SAE: auth success, start assoc\n"); + start_clnt_assoc(padapter); + } return 0; } @@ -4919,6 +5001,14 @@ static int cfg80211_rtw_start_ap(struct wiphy *wiphy, struct net_device *ndev, ret = -ENOTSUPP; goto exit; } + + /* + Kernel < v5.1, the auth_type set as NL80211_AUTHTYPE_AUTOMATIC. + if the AKM SAE in the RSN IE, we have to update the auth_type for SAE + in rtw_check_beacon_data(). + */ + rtw_cfg80211_set_auth_type(&adapter->securitypriv, settings->auth_type); + rtw_mi_scan_abort(adapter, _TRUE); rtw_mi_buddy_set_scan_deny(adapter, 300); ret = rtw_add_beacon(adapter, settings->beacon.head, settings->beacon.head_len, @@ -6166,10 +6256,7 @@ void rtw_cfg80211_rx_mframe(_adapter *adapter, union recv_frame *rframe, const c #endif RTW_INFO("RTW_Rx:ch=%d(%d), ta="MAC_FMT"\n", ch, sch, MAC_ARG(get_addr2_ptr(frame))); - #ifdef CONFIG_RTW_MESH - if (!rtw_sae_check_frames(adapter, frame, frame_len, _FALSE)) - #endif - { + if (!rtw_sae_preprocess(adapter, frame, frame_len, _FALSE)) { if (msg) RTW_INFO("RTW_Rx:%s\n", msg); else @@ -6441,6 +6528,54 @@ static s32 cfg80211_rtw_update_ft_ies(struct wiphy *wiphy, } #endif +void rtw_cfg80211_external_auth_request(_adapter *padapter, union recv_frame *rframe) +{ + struct rtw_external_auth_params params; + struct wireless_dev *wdev = padapter->rtw_wdev; + struct net_device *netdev = wdev_to_ndev(wdev); + struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv); + struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info); + + u8 frame[256] = { 0 }; + uint frame_len = 24; + s32 freq = 0; + + /* rframe, in this case is null point */ + + freq = rtw_ch2freq(pmlmeext->cur_channel); + +#ifdef CONFIG_DEBUG_CFG80211 + RTW_INFO(FUNC_ADPT_FMT": freq(%d, %d)\n", FUNC_ADPT_ARG(padapter), freq); +#endif + +#if (KERNEL_VERSION(4, 17, 0) <= LINUX_VERSION_CODE) + params.action = EXTERNAL_AUTH_START; + _rtw_memcpy(params.bssid, get_my_bssid(&pmlmeinfo->network), ETH_ALEN); + params.ssid.ssid_len = pmlmeinfo->network.Ssid.SsidLength; + _rtw_memcpy(params.ssid.ssid, pmlmeinfo->network.Ssid.Ssid, + pmlmeinfo->network.Ssid.SsidLength); + params.key_mgmt_suite = 0x8ac0f00; + + cfg80211_external_auth_request(netdev, + (struct cfg80211_external_auth_params *)¶ms, GFP_ATOMIC); +#elif (KERNEL_VERSION(2, 6, 37) <= LINUX_VERSION_CODE) + set_frame_sub_type(frame, WIFI_AUTH); + + _rtw_memcpy(frame + 4, get_my_bssid(&pmlmeinfo->network), ETH_ALEN); + _rtw_memcpy(frame + 10, adapter_mac_addr(padapter), ETH_ALEN); + _rtw_memcpy(frame + 16, get_my_bssid(&pmlmeinfo->network), ETH_ALEN); + RTW_PUT_LE32((frame + 18), 0x8ac0f00); + + if (pmlmeinfo->network.Ssid.SsidLength) { + *(frame + 23) = pmlmeinfo->network.Ssid.SsidLength; + _rtw_memcpy(frame + 24, pmlmeinfo->network.Ssid.Ssid, + pmlmeinfo->network.Ssid.SsidLength); + frame_len = 24 + pmlmeinfo->network.Ssid.SsidLength; + } + rtw_cfg80211_rx_mgmt(wdev, freq, 0, frame, frame_len, GFP_ATOMIC); +#endif +} + inline void rtw_cfg80211_set_is_roch(_adapter *adapter, bool val) { adapter->cfg80211_wdinfo.is_ro_ch = val; @@ -7187,15 +7322,19 @@ static int cfg80211_rtw_mgmt_tx(struct wiphy *wiphy, wait_ack = 0; goto dump; } -#ifdef CONFIG_RTW_MESH else if (frame_styp == RTW_IEEE80211_STYPE_AUTH) { + int retval = 0; + RTW_INFO("RTW_Tx:tx_ch=%d, no_cck=%u, da="MAC_FMT"\n", tx_ch, no_cck, MAC_ARG(GetAddr1Ptr(buf))); - if (!rtw_sae_check_frames(padapter, buf, len, _TRUE)) + + retval = rtw_sae_preprocess(padapter, buf, len, _TRUE); + if (retval == 2) + goto exit; + if (retval == 0) RTW_INFO("RTW_Tx:AUTH\n"); dump_limit = 1; goto dump; } -#endif if (rtw_action_frame_parse(buf, len, &category, &action) == _FALSE) { RTW_INFO(FUNC_ADPT_FMT" frame_control:0x%02x\n", FUNC_ADPT_ARG(padapter), @@ -7341,16 +7480,27 @@ static void cfg80211_rtw_mgmt_frame_register(struct wiphy *wiphy, frame_type, reg); #endif - /* Wait QC Verify */ - return; - switch (frame_type) { + case IEEE80211_STYPE_AUTH: /* 0x00B0 */ + if (reg > 0) + SET_CFG80211_REPORT_MGMT(pwdev_priv, IEEE80211_STYPE_AUTH, reg); + else + CLR_CFG80211_REPORT_MGMT(pwdev_priv, IEEE80211_STYPE_AUTH, reg); + break; +#ifdef not_yet case IEEE80211_STYPE_PROBE_REQ: /* 0x0040 */ - SET_CFG80211_REPORT_MGMT(pwdev_priv, IEEE80211_STYPE_PROBE_REQ, reg); + if (reg > 0) + SET_CFG80211_REPORT_MGMT(pwdev_priv, IEEE80211_STYPE_PROBE_REQ, reg); + else + CLR_CFG80211_REPORT_MGMT(pwdev_priv, IEEE80211_STYPE_PROBE_REQ, reg); break; case IEEE80211_STYPE_ACTION: /* 0x00D0 */ - SET_CFG80211_REPORT_MGMT(pwdev_priv, IEEE80211_STYPE_ACTION, reg); + if (reg > 0) + SET_CFG80211_REPORT_MGMT(pwdev_priv, IEEE80211_STYPE_ACTION, reg); + else + CLR_CFG80211_REPORT_MGMT(pwdev_priv, IEEE80211_STYPE_ACTION, reg); break; +#endif default: break; } @@ -9077,13 +9227,7 @@ static void rtw_cfg80211_init_ht_capab(_adapter *padapter ht_cap->ht_supported = 1; - /* According to the comment in rtw_ap.c: - * "Note: currently we switch to the MIXED op mode if HT non-greenfield - * station is associated. Probably it's a theoretical case, since - * it looks like all known HT STAs support greenfield." - * Therefore Greenfield is added to ht_cap - */ - ht_cap->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_GRN_FLD | + ht_cap->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_DSSSCCK40 | IEEE80211_HT_CAP_MAX_AMSDU; rtw_cfg80211_init_ht_capab_ex(padapter, ht_cap, band, rf_type); @@ -9161,75 +9305,6 @@ void rtw_cfg80211_init_wdev_data(_adapter *padapter) #endif } -static void rtw_cfg80211_init_vht_capab_ex(_adapter *padapter, struct ieee80211_sta_vht_cap *vht_cap, u8 rf_type) -{ -//todo: Support for other bandwidths -/* NSS = Number of Spatial Streams */ -#define MAX_BIT_RATE_80MHZ_NSS3 1300 /* Mbps */ -#define MAX_BIT_RATE_80MHZ_NSS2 867 /* Mbps */ -#define MAX_BIT_RATE_80MHZ_NSS1 434 /* Mbps */ - struct registry_priv *pregistrypriv = &padapter->registrypriv; - struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct vht_priv *pvhtpriv = &pmlmepriv->vhtpriv; - rtw_vht_use_default_setting(padapter); - /* RX LDPC */ - if (TEST_FLAG(pvhtpriv->ldpc_cap, LDPC_VHT_ENABLE_RX)) - vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC; - /* TX STBC */ - if (TEST_FLAG(pvhtpriv->stbc_cap, STBC_VHT_ENABLE_TX)) - vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC; - /* RX STBC */ - if (TEST_FLAG(pvhtpriv->stbc_cap, STBC_VHT_ENABLE_RX)) { - switch (rf_type) { - case RF_1T1R: - vht_cap->cap |= IEEE80211_VHT_CAP_RXSTBC_1;/*RX STBC One spatial stream*/ - break; - case RF_2T2R: - case RF_1T2R: - vht_cap->cap |= IEEE80211_VHT_CAP_RXSTBC_2;/*RX STBC Two spatial streams*/ - break; - case RF_3T3R: - case RF_3T4R: - case RF_4T4R: - vht_cap->cap |= IEEE80211_VHT_CAP_RXSTBC_3;/*RX STBC Three spatial streams*/ - break; - default: - /* DBG_871X("[warning] rf_type %d is not expected\n", rf_type); */ - break; - } - } - /* switch (rf_type) { - case RF_1T1R: - vht_cap->vht_mcs.tx_highest = MAX_BIT_RATE_80MHZ_NSS1; - vht_cap->vht_mcs.rx_highest = MAX_BIT_RATE_80MHZ_NSS1; - break; - case RF_2T2R: - case RF_1T2R: - vht_cap->vht_mcs.tx_highest = MAX_BIT_RATE_80MHZ_NSS2; - vht_cap->vht_mcs.rx_highest = MAX_BIT_RATE_80MHZ_NSS2; - break; - case RF_3T3R: - case RF_3T4R: - case RF_4T4R: - vht_cap->vht_mcs.tx_highest = MAX_BIT_RATE_80MHZ_NSS3; - vht_cap->vht_mcs.rx_highest = MAX_BIT_RATE_80MHZ_NSS3; - break; - default: - DBG_871X("[warning] rf_type %d is not expected\n", rf_type); - break; - } */ - /* MCS map */ - vht_cap->vht_mcs.tx_mcs_map = pvhtpriv->vht_mcs_map[0] | (pvhtpriv->vht_mcs_map[1] << 8); - vht_cap->vht_mcs.rx_mcs_map = vht_cap->vht_mcs.tx_mcs_map; - if (rf_type == RF_1T1R) { - vht_cap->vht_mcs.tx_highest = MAX_BIT_RATE_80MHZ_NSS1; - vht_cap->vht_mcs.rx_highest = MAX_BIT_RATE_80MHZ_NSS1; - } - if (pvhtpriv->sgi_80m) - vht_cap->cap |= IEEE80211_VHT_CAP_SHORT_GI_80; - vht_cap->cap |= (pvhtpriv->ampdu_len << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT); -} - void rtw_cfg80211_init_wiphy(_adapter *padapter) { u8 rf_type; @@ -9453,6 +9528,10 @@ static void rtw_cfg80211_preinit_wiphy(_adapter *adapter, struct wiphy *wiphy) ; #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)) */ #endif /* CONFIG_RTW_MESH */ + +#if (KERNEL_VERSION(3, 8, 0) <= LINUX_VERSION_CODE) + wiphy->features |= NL80211_FEATURE_SAE; +#endif } #ifdef CONFIG_RFKILL_POLL @@ -9626,6 +9705,94 @@ int rtw_hostapd_acs_dump_survey(struct wiphy *wiphy, struct net_device *netdev, } #endif /* defined(CONFIG_RTW_HOSTAPD_ACS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)) */ +#if (KERNEL_VERSION(4, 17, 0) <= LINUX_VERSION_CODE) +int cfg80211_rtw_external_auth(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_external_auth_params *params) +{ + PADAPTER padapter = (_adapter *)rtw_netdev_priv(dev); + + RTW_INFO(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(dev)); + + rtw_cfg80211_external_auth_status(wiphy, dev, + (struct rtw_external_auth_params *)params); + + return 0; +} +#endif + +void rtw_cfg80211_external_auth_status(struct wiphy *wiphy, struct net_device *dev, + struct rtw_external_auth_params *params) +{ + PADAPTER padapter = (_adapter *)rtw_netdev_priv(dev); + struct security_priv *psecuritypriv = &padapter->securitypriv; + struct sta_priv *pstapriv = &padapter->stapriv; + struct sta_info *psta = NULL; + u8 *buf = NULL; + u32 len = 0; + _irqL irqL; + + RTW_INFO(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(dev)); + + RTW_INFO("SAE: action: %u, status: %u\n", params->action, params->status); + if (params->status == WLAN_STATUS_SUCCESS) { + RTW_INFO("bssid: "MAC_FMT"\n", MAC_ARG(params->bssid)); + RTW_INFO("SSID: [%s]\n", + ((params->ssid.ssid_len == 0) ? "" : (char *)params->ssid.ssid)); + RTW_INFO("suite: 0x%08x\n", params->key_mgmt_suite); + } + + psta = rtw_get_stainfo(pstapriv, params->bssid); + if (psta && (params->status == WLAN_STATUS_SUCCESS)) { + /* AP mode */ + RTW_INFO("station match\n"); + + psta->state &= ~WIFI_FW_AUTH_NULL; + psta->state |= WIFI_FW_AUTH_SUCCESS; + psta->expire_to = padapter->stapriv.assoc_to; + + if (params->pmkid != NULL) { + /* RTW_INFO_DUMP("PMKID:", params->pmkid, PMKID_LEN); */ + _rtw_set_pmksa(dev, params->bssid, params->pmkid); + } + + _enter_critical_bh(&psta->lock, &irqL); + if ((psta->auth_len != 0) && (psta->pauth_frame != NULL)) { + buf = rtw_zmalloc(psta->auth_len); + if (buf) { + _rtw_memcpy(buf, psta->pauth_frame, psta->auth_len); + len = psta->auth_len; + } + + rtw_mfree(psta->pauth_frame, psta->auth_len); + psta->pauth_frame = NULL; + psta->auth_len = 0; + } + _exit_critical_bh(&psta->lock, &irqL); + + if (buf) { + struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv); + /* send the SAE auth Confirm */ + + rtw_ps_deny(padapter, PS_DENY_MGNT_TX); + if (_SUCCESS == rtw_pwr_wakeup(padapter)) { + rtw_mi_set_scan_deny(padapter, 1000); + rtw_mi_scan_abort(padapter, _TRUE); + + RTW_INFO("SAE: Tx auth Confirm\n"); + rtw_mgnt_tx_cmd(padapter, pmlmeext->cur_channel, 1, buf, len, 0, RTW_CMDF_DIRECTLY); + + rtw_mfree(buf, len); + buf = NULL; + len = 0; + } + rtw_ps_deny_cancel(padapter, PS_DENY_MGNT_TX); + } + } else { + /* STA mode */ + psecuritypriv->extauth_status = params->status; + } +} + static struct cfg80211_ops rtw_cfg80211_ops = { .change_virtual_intf = cfg80211_rtw_change_iface, .add_key = cfg80211_rtw_add_key, @@ -9742,6 +9909,9 @@ static struct cfg80211_ops rtw_cfg80211_ops = { #if defined(CONFIG_RTW_HOSTAPD_ACS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)) .dump_survey = rtw_hostapd_acs_dump_survey, #endif +#if (KERNEL_VERSION(4, 17, 0) <= LINUX_VERSION_CODE) + .external_auth = cfg80211_rtw_external_auth, +#endif }; struct wiphy *rtw_wiphy_alloc(_adapter *padapter, struct device *dev) diff --git a/os_dep/linux/ioctl_cfg80211.h b/os_dep/linux/ioctl_cfg80211.h index d16cfdd..b12ce56 100644 --- a/os_dep/linux/ioctl_cfg80211.h +++ b/os_dep/linux/ioctl_cfg80211.h @@ -193,6 +193,20 @@ struct rtw_wdev_priv { }; +enum external_auth_action { + EXTERNAL_AUTH_START, + EXTERNAL_AUTH_ABORT, +}; + +struct rtw_external_auth_params { + enum external_auth_action action; + u8 bssid[ETH_ALEN]__aligned(2); + struct cfg80211_ssid ssid; + unsigned int key_mgmt_suite; + u16 status; + u8 pmkid[PMKID_LEN]; +}; + bool rtw_cfg80211_is_connect_requested(_adapter *adapter); #if RTW_CFG80211_BLOCK_STA_DISCON_EVENT @@ -246,7 +260,8 @@ struct rtw_wiphy_data { #define FUNC_WIPHY_FMT "%s("WIPHY_FMT")" #define FUNC_WIPHY_ARG(wiphy) __func__, WIPHY_ARG(wiphy) -#define SET_CFG80211_REPORT_MGMT(w, t, v) (w->report_mgmt |= (v ? BIT(t >> 4) : 0)) +#define SET_CFG80211_REPORT_MGMT(w, t, v) (w->report_mgmt |= BIT(t >> 4)) +#define CLR_CFG80211_REPORT_MGMT(w, t, v) (w->report_mgmt &= (~BIT(t >> 4))) #define GET_CFG80211_REPORT_MGMT(w, t) ((w->report_mgmt & BIT(t >> 4)) > 0) struct wiphy *rtw_wiphy_alloc(_adapter *padapter, struct device *dev); @@ -320,6 +335,10 @@ void rtw_cfg80211_rx_action(_adapter *adapter, union recv_frame *rframe, const c void rtw_cfg80211_rx_mframe(_adapter *adapter, union recv_frame *rframe, const char *msg); void rtw_cfg80211_rx_probe_request(_adapter *padapter, union recv_frame *rframe); +void rtw_cfg80211_external_auth_request(_adapter *padapter, union recv_frame *rframe); +void rtw_cfg80211_external_auth_status(struct wiphy *wiphy, struct net_device *dev, + struct rtw_external_auth_params *params); + int rtw_cfg80211_set_mgnt_wpsp2pie(struct net_device *net, char *buf, int len, int type); bool rtw_cfg80211_pwr_mgmt(_adapter *adapter); diff --git a/os_dep/linux/ioctl_mp.c b/os_dep/linux/ioctl_mp.c index 65b91bf..06233dc 100644 --- a/os_dep/linux/ioctl_mp.c +++ b/os_dep/linux/ioctl_mp.c @@ -19,10 +19,6 @@ #include #include "../../hal/phydm/phydm_precomp.h" -#ifdef MARK_KERNEL_PFU - #include - #include -#endif #if defined(CONFIG_RTL8723B) #include @@ -1722,10 +1718,6 @@ int rtw_mp_tx(struct net_device *dev, PMAC_Get_Pkt_Param(&pMptCtx->PMacTxInfo, &pMptCtx->PMacPktInfo); - #ifdef MARK_KERNEL_PFU - kernel_fpu_begin(); - #endif - if (MPT_IS_CCK_RATE(pMptCtx->PMacTxInfo.TX_RATE)) CCK_generator(&pMptCtx->PMacTxInfo, &pMptCtx->PMacPktInfo); @@ -1734,11 +1726,6 @@ int rtw_mp_tx(struct net_device *dev, /* 24 BIT*/ L_SIG_generator(pMptCtx->PMacPktInfo.N_sym, &pMptCtx->PMacTxInfo, &pMptCtx->PMacPktInfo); } - - #ifdef MARK_KERNEL_PFU - kernel_fpu_end(); - #endif - /* 48BIT*/ if (MPT_IS_HT_RATE(pMptCtx->PMacTxInfo.TX_RATE)) HT_SIG_generator(&pMptCtx->PMacTxInfo, &pMptCtx->PMacPktInfo); diff --git a/os_dep/linux/mlme_linux.c b/os_dep/linux/mlme_linux.c index 39c0ac1..247e45e 100644 --- a/os_dep/linux/mlme_linux.c +++ b/os_dep/linux/mlme_linux.c @@ -130,6 +130,8 @@ void rtw_reset_securitypriv(_adapter *adapter) adapter->securitypriv.ndisauthtype = Ndis802_11AuthModeOpen; adapter->securitypriv.ndisencryptstatus = Ndis802_11WEPDisabled; + adapter->securitypriv.extauth_status = WLAN_STATUS_UNSPECIFIED_FAILURE; + } else { /* reset values in securitypriv */ /* if(adapter->mlmepriv.fw_state & WIFI_STATION_STATE) */ /* { */ @@ -145,6 +147,8 @@ void rtw_reset_securitypriv(_adapter *adapter) psec_priv->ndisauthtype = Ndis802_11AuthModeOpen; psec_priv->ndisencryptstatus = Ndis802_11WEPDisabled; /* } */ + + psec_priv->extauth_status = WLAN_STATUS_UNSPECIFIED_FAILURE; } /* add for CONFIG_IEEE80211W, none 11w also can use */ _exit_critical_bh(&adapter->security_key_mutex, &irqL); diff --git a/os_dep/linux/os_intfs.c b/os_dep/linux/os_intfs.c index 1acd713..45146e3 100644 --- a/os_dep/linux/os_intfs.c +++ b/os_dep/linux/os_intfs.c @@ -1364,7 +1364,7 @@ static u16 rtw_select_queue(struct net_device *dev, struct sk_buff *skb #else , void *accel_priv #endif - #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) && LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0)) + #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) , select_queue_fallback_t fallback #endif #endif diff --git a/os_dep/linux/rtw_android.c b/os_dep/linux/rtw_android.c index 51b5a65..e468574 100644 --- a/os_dep/linux/rtw_android.c +++ b/os_dep/linux/rtw_android.c @@ -95,6 +95,7 @@ const char *android_wifi_cmd_str[ANDROID_WIFI_CMD_MAX] = { /* Private command for P2P disable*/ "P2P_DISABLE", "SET_AEK", + "EXT_AUTH_STATUS", "DRIVER_VERSION" }; @@ -935,6 +936,12 @@ int rtw_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) break; #endif + case ANDROID_WIFI_CMD_EXT_AUTH_STATUS: { + rtw_set_external_auth_status(padapter, + command + strlen("EXT_AUTH_STATUS "), + priv_cmd.total_len - strlen("EXT_AUTH_STATUS ")); + break; + } case ANDROID_WIFI_CMD_DRIVERVERSION: { bytes_written = strlen(DRIVERVERSION); snprintf(command, bytes_written + 1, DRIVERVERSION); diff --git a/os_dep/linux/rtw_proc.c b/os_dep/linux/rtw_proc.c index 8b0d8e1..63be985 100644 --- a/os_dep/linux/rtw_proc.c +++ b/os_dep/linux/rtw_proc.c @@ -3742,6 +3742,17 @@ exit: } #endif /* CONFIG_RTW_TPT_MODE */ +int proc_get_cur_beacon_keys(struct seq_file *m, void *v) +{ + struct net_device *dev = m->private; + _adapter *adapter = rtw_netdev_priv(dev); + struct mlme_priv *mlme = &adapter->mlmepriv; + + rtw_dump_bcn_keys(m, &mlme->cur_beacon_keys); + + return 0; +} + /* * rtw_adapter_proc: * init/deinit when register/unregister net_device @@ -4108,6 +4119,7 @@ const struct rtw_proc_hdl adapter_proc_hdls[] = { #endif #endif + RTW_PROC_HDL_SSEQ("cur_beacon_keys", proc_get_cur_beacon_keys, NULL), }; const int adapter_proc_hdls_num = sizeof(adapter_proc_hdls) / sizeof(struct rtw_proc_hdl); diff --git a/os_dep/linux/usb_intf.c b/os_dep/linux/usb_intf.c index 3b92dc4..041e4dd 100644 --- a/os_dep/linux/usb_intf.c +++ b/os_dep/linux/usb_intf.c @@ -161,6 +161,7 @@ static struct usb_device_id rtw_usb_id_tbl[] = { {USB_DEVICE(USB_VENDER_ID_REALTEK, 0x881C), .driver_info = RTL8812}, /* Default ID */ /*=== Customer ID ===*/ {USB_DEVICE(0x050D, 0x1106), .driver_info = RTL8812}, /* Belkin - sercomm */ + {USB_DEVICE(0x2001, 0x330E), .driver_info = RTL8812}, /* D-Link - ALPHA */ {USB_DEVICE(0x7392, 0xA822), .driver_info = RTL8812}, /* Edimax - Edimax */ {USB_DEVICE(0x0DF6, 0x0074), .driver_info = RTL8812}, /* Sitecom - Edimax */ {USB_DEVICE(0x04BB, 0x0952), .driver_info = RTL8812}, /* I-O DATA - Edimax */ @@ -170,30 +171,12 @@ static struct usb_device_id rtw_usb_id_tbl[] = { {USB_DEVICE(0x0E66, 0x0022), .driver_info = RTL8812}, /* HAWKING - Edimax */ {USB_DEVICE(0x0586, 0x3426), .driver_info = RTL8812}, /* ZyXEL - */ {USB_DEVICE(0x2001, 0x3313), .driver_info = RTL8812}, /* D-Link - ALPHA */ - {USB_DEVICE(0x1058, 0x0632), .driver_info = RTL8812}, /* WD - Cybertan */ + {USB_DEVICE(0x1058, 0x0632), .driver_info = RTL8812}, /* WD - Cybertan*/ {USB_DEVICE(0x1740, 0x0100), .driver_info = RTL8812}, /* EnGenius - EnGenius */ {USB_DEVICE(0x2019, 0xAB30), .driver_info = RTL8812}, /* Planex - Abocom */ {USB_DEVICE(0x07B8, 0x8812), .driver_info = RTL8812}, /* Abocom - Abocom */ - {USB_DEVICE(0x0846, 0x9051), .driver_info = RTL8812}, /* Netgear A6200 v2 */ - {USB_DEVICE(0x2001, 0x330E), .driver_info = RTL8812}, /* D-Link - ALPHA */ - {USB_DEVICE(0x2001, 0x3313), .driver_info = RTL8812}, /* D-Link - ALPHA */ {USB_DEVICE(0x2001, 0x3315), .driver_info = RTL8812}, /* D-Link - Cameo */ {USB_DEVICE(0x2001, 0x3316), .driver_info = RTL8812}, /* D-Link - Cameo */ - {USB_DEVICE(0x13B1, 0x003F), .driver_info = RTL8812}, /* Linksys - WUSB6300 */ - {USB_DEVICE(0x2357, 0x0101), .driver_info = RTL8812}, /* TP-Link - Archer T4U AC1200 */ - {USB_DEVICE(0x2357, 0x0103), .driver_info = RTL8812}, /* TP-Link - T4UH */ - {USB_DEVICE(0x2357, 0x010D), .driver_info = RTL8812}, /* TP-Link - Archer T4U AC1300 */ - {USB_DEVICE(0x2357, 0x0115), .driver_info = RTL8812}, /* TP-Link - Archer T4U AC1300 */ - {USB_DEVICE(0x2357, 0x010E), .driver_info = RTL8812}, /* TP-Link - Archer T4UH AC1300 */ - {USB_DEVICE(0x2357, 0x010F), .driver_info = RTL8812}, /* TP-Link - T4UHP */ - {USB_DEVICE(0x2357, 0x0122), .driver_info = RTL8812}, /* TP-Link - T4UHP (other) */ - {USB_DEVICE(0x20F4, 0x805B), .driver_info = RTL8812}, /* TRENDnet - */ - {USB_DEVICE(0x0411, 0x025D), .driver_info = RTL8812}, /* Buffalo - WI-U3-866D */ - {USB_DEVICE(0x050D, 0x1109), .driver_info = RTL8812}, /* Belkin F9L1109 - SerComm */ - {USB_DEVICE(0x148F, 0x9097), .driver_info = RTL8812}, /* Amped Wireless ACA1 */ - {USB_DEVICE(0x0BDA, 0x8812), .driver_info = RTL8812}, /* Alfa - AWUS036AC, AWUS036ACH & AWUS036EAC */ - {USB_DEVICE(0x2604, 0x0012), .driver_info = RTL8812}, /* Tenda U12 */ - {USB_DEVICE(0x0BDA, 0x881A), .driver_info = RTL8812}, /* Unex DAUK-W8812 */ #endif #ifdef CONFIG_RTL8821A @@ -206,24 +189,13 @@ static struct usb_device_id rtw_usb_id_tbl[] = { {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDER_ID_REALTEK, 0x0823, 0xff, 0xff, 0xff), .driver_info = RTL8821}, /* 8821AU */ /*=== Customer ID ===*/ {USB_DEVICE(0x7392, 0xA811), .driver_info = RTL8821}, /* Edimax - Edimax */ - {USB_DEVICE(0x7392, 0xA812), .driver_info = RTL8821}, /* Edimax - EW-7811UTC */ - {USB_DEVICE(0x7392, 0xA813), .driver_info = RTL8821}, /* Edimax - EW-7811UAC */ {USB_DEVICE(0x04BB, 0x0953), .driver_info = RTL8821}, /* I-O DATA - Edimax */ {USB_DEVICE(0x2001, 0x3314), .driver_info = RTL8821}, /* D-Link - Cameo */ {USB_DEVICE(0x2001, 0x3318), .driver_info = RTL8821}, /* D-Link - Cameo */ {USB_DEVICE(0x0E66, 0x0023), .driver_info = RTL8821}, /* HAWKING - Edimax */ - {USB_DEVICE(0x056E, 0x400E), .driver_info = RTL8821}, /* ELECOM - ELECOM */ - {USB_DEVICE(0x056E, 0x400F), .driver_info = RTL8821}, /* ELECOM - ELECOM */ - {USB_DEVICE(0x0411, 0x0242), .driver_info = RTL8821}, /* ELECOM - WDC-433DU2H */ - {USB_DEVICE(0x2019, 0xAB32), .driver_info = RTL8821}, /* Planex - GW-450S */ - {USB_DEVICE(0x0846, 0x9052), .driver_info = RTL8821}, /* Netgear - A6100 */ - {USB_DEVICE(0x0411, 0x029B), .driver_info = RTL8821}, /* Buffalo - WI-U2-433DHP */ - {USB_DEVICE(0x056E, 0x4007), .driver_info = RTL8821}, /* Elecom - WDC-433DU2HBK */ - {USB_DEVICE(0x0BDA, 0xA811), .driver_info = RTL8821}, /* GMYLE - AC450 */ - {USB_DEVICE(0x3823, 0x6249), .driver_info = RTL8821}, /* Obihai - OBiWiFi */ - {USB_DEVICE(0x2357, 0x011E), .driver_info = RTL8821}, /* TP Link - T2U Nano */ - {USB_DEVICE(0x2357, 0x0122), .driver_info = RTL8821}, /* TP Link - T2U Nano */ - {USB_DEVICE(0x2357, 0x0120), .driver_info = RTL8821}, /* TP Link - T2U Plus */ + {USB_DEVICE(0x056E, 0x400E) , .driver_info = RTL8821}, /* ELECOM - ELECOM */ + {USB_DEVICE(0x056E, 0x400F) , .driver_info = RTL8821}, /* ELECOM - ELECOM */ + {USB_DEVICE(0x20f4, 0x804b), .driver_info = RTL8821}, /* TRENDnet */ #endif #ifdef CONFIG_RTL8192E @@ -245,20 +217,13 @@ static struct usb_device_id rtw_usb_id_tbl[] = { #endif /* CONFIG_RTL8703B */ #ifdef CONFIG_RTL8814A + {USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8813), .driver_info = RTL8814A}, - {USB_DEVICE(0x2001, 0x331A), .driver_info = RTL8814A}, /* D-Link - D-Link */ - {USB_DEVICE(0x0B05, 0x1817), .driver_info = RTL8814A}, /* ASUS - ASUSTeK */ - {USB_DEVICE(0x0B05, 0x1852), .driver_info = RTL8814A}, /* ASUS - ASUSTeK */ - {USB_DEVICE(0x0B05, 0x1853), .driver_info = RTL8814A}, /* ASUS - ASUSTeK */ + {USB_DEVICE(0x2001, 0x331a), .driver_info = RTL8814A}, /* D-Link - D-Link */ + {USB_DEVICE(0x0b05, 0x1817), .driver_info = RTL8814A}, /* ASUS - ASUSTeK */ {USB_DEVICE(0x056E, 0x400B), .driver_info = RTL8814A}, /* ELECOM - ELECOM */ {USB_DEVICE(0x056E, 0x400D), .driver_info = RTL8814A}, /* ELECOM - ELECOM */ {USB_DEVICE(0x7392, 0xA834), .driver_info = RTL8814A}, /* Edimax - Edimax */ - {USB_DEVICE(0x7392, 0xA833), .driver_info = RTL8814A}, /* Edimax - AC1750 */ - {USB_DEVICE(0x0BDA, 0x8813), .driver_info = RTL8814A}, /* Edimax - EDUP Adapters */ - {USB_DEVICE(0x2357, 0x0106), .driver_info = RTL8814A}, /* TP-LINK Archer T9UH */ - {USB_DEVICE(0x20F4, 0x809A), .driver_info = RTL8814A}, /* TRENDnet - TRENDnet */ - {USB_DEVICE(0x20F4, 0x809B), .driver_info = RTL8814A}, /* TRENDnet TEW-809UB */ - {USB_DEVICE(0x0846, 0x9054), .driver_info = RTL8814A}, /* Netgear A7000 */ #endif /* CONFIG_RTL8814A */ #ifdef CONFIG_RTL8188F diff --git a/os_dep/linux/wifi_regd.c b/os_dep/linux/wifi_regd.c index a7216c6..4c10f84 100644 --- a/os_dep/linux/wifi_regd.c +++ b/os_dep/linux/wifi_regd.c @@ -44,7 +44,7 @@ static struct country_code_to_enum_rd allCountries[] = { /* 2G chan 12 - chan 13, PASSIV SCAN */ #define RTW_2GHZ_CH12_13 \ REG_RULE(2467-10, 2472+10, 40, 0, 20, \ - 0) + NL80211_RRF_PASSIVE_SCAN) /* 2G chan 14, PASSIVS SCAN, NO OFDM (B only) */ #define RTW_2GHZ_CH14 \ @@ -69,7 +69,7 @@ static struct country_code_to_enum_rd allCountries[] = { /* 5G chan 36 - chan 165 */ #define RTW_5GHZ_5150_5850 \ REG_RULE(5150-10, 5850+10, 40, 0, 30, \ - 0) + NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) static const struct ieee80211_regdomain rtw_regdom_rd = { .n_reg_rules = 3, @@ -81,6 +81,176 @@ static const struct ieee80211_regdomain rtw_regdom_rd = { } }; +static const struct ieee80211_regdomain rtw_regdom_11 = { + .n_reg_rules = 1, + .alpha2 = "99", + .reg_rules = { + RTW_2GHZ_CH01_11, + } +}; + +static const struct ieee80211_regdomain rtw_regdom_12_13 = { + .n_reg_rules = 2, + .alpha2 = "99", + .reg_rules = { + RTW_2GHZ_CH01_11, + RTW_2GHZ_CH12_13, + } +}; + +static const struct ieee80211_regdomain rtw_regdom_no_midband = { + .n_reg_rules = 3, + .alpha2 = "99", + .reg_rules = { + RTW_2GHZ_CH01_11, + RTW_5GHZ_5150_5350, + RTW_5GHZ_5725_5850, + } +}; + +static const struct ieee80211_regdomain rtw_regdom_60_64 = { + .n_reg_rules = 3, + .alpha2 = "99", + .reg_rules = { + RTW_2GHZ_CH01_11, + RTW_2GHZ_CH12_13, + RTW_5GHZ_5725_5850, + } +}; + +static const struct ieee80211_regdomain rtw_regdom_14_60_64 = { + .n_reg_rules = 4, + .alpha2 = "99", + .reg_rules = { + RTW_2GHZ_CH01_11, + RTW_2GHZ_CH12_13, + RTW_2GHZ_CH14, + RTW_5GHZ_5725_5850, + } +}; + +static const struct ieee80211_regdomain rtw_regdom_14 = { + .n_reg_rules = 3, + .alpha2 = "99", + .reg_rules = { + RTW_2GHZ_CH01_11, + RTW_2GHZ_CH12_13, + RTW_2GHZ_CH14, + } +}; + +#if 0 +static struct rtw_regulatory *rtw_regd; +#endif + +#if 0 /* not_yet */ +static void _rtw_reg_apply_beaconing_flags(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator) +{ + enum nl80211_band band; + struct ieee80211_supported_band *sband; + const struct ieee80211_reg_rule *reg_rule; + struct ieee80211_channel *ch; + unsigned int i; + u32 bandwidth = 0; + int r; + + for (band = 0; band < NUM_NL80211_BANDS; band++) { + + if (!wiphy->bands[band]) + continue; + + sband = wiphy->bands[band]; + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + if (rtw_is_dfs_ch(ch->hw_value) || + (ch->flags & IEEE80211_CHAN_RADAR)) + continue; + if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { + r = freq_reg_info(wiphy, ch->center_freq, + bandwidth, ®_rule); + if (r) + continue; + + /* + *If 11d had a rule for this channel ensure + *we enable adhoc/beaconing if it allows us to + *use it. Note that we would have disabled it + *by applying our static world regdomain by + *default during init, prior to calling our + *regulatory_hint(). + */ + + if (!(reg_rule->flags & NL80211_RRF_NO_IBSS)) + ch->flags &= ~IEEE80211_CHAN_NO_IBSS; + if (! + (reg_rule->flags & + NL80211_RRF_PASSIVE_SCAN)) + ch->flags &= + ~IEEE80211_CHAN_PASSIVE_SCAN; + } else { + if (ch->beacon_found) + ch->flags &= ~(IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_PASSIVE_SCAN); + } + } + } +} + +/* Allows active scan scan on Ch 12 and 13 */ +static void _rtw_reg_apply_active_scan_flags(struct wiphy *wiphy, + enum nl80211_reg_initiator + initiator) +{ + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + const struct ieee80211_reg_rule *reg_rule; + u32 bandwidth = 0; + int r; + + if (!wiphy->bands[NL80211_BAND_2GHZ]) + return; + sband = wiphy->bands[NL80211_BAND_2GHZ]; + + /* + * If no country IE has been received always enable active scan + * on these channels. This is only done for specific regulatory SKUs + */ + if (initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { + ch = &sband->channels[11]; /* CH 12 */ + if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) + ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; + ch = &sband->channels[12]; /* CH 13 */ + if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) + ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; + return; + } + + /* + * If a country IE has been received check its rule for this + * channel first before enabling active scan. The passive scan + * would have been enforced by the initial processing of our + * custom regulatory domain. + */ + + ch = &sband->channels[11]; /* CH 12 */ + r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); + if (!r) { + if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) + if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) + ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; + } + + ch = &sband->channels[12]; /* CH 13 */ + r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); + if (!r) { + if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) + if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) + ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; + } +} +#endif void rtw_regd_apply_flags(struct wiphy *wiphy) { @@ -104,15 +274,11 @@ void rtw_regd_apply_flags(struct wiphy *wiphy) ch = &sband->channels[j]; if (ch) -#ifndef CONFIG_DISABLE_REGD_C ch->flags = IEEE80211_CHAN_DISABLED; -#else - ch->flags = 0; -#endif } } } -#ifndef CONFIG_DISABLE_REGD_C + /* channels apply by channel plans. */ for (i = 0; i < max_chan_nums; i++) { channel = channel_set[i].ChannelNum; @@ -150,14 +316,21 @@ void rtw_regd_apply_flags(struct wiphy *wiphy) } #endif /* CONFIG_DFS */ } -#endif } static const struct ieee80211_regdomain *_rtw_regdomain_select(struct rtw_regulatory *reg) { +#if 0 + switch (reg->country_code) { + case COUNTRY_CODE_USER: + default: + return &rtw_regdom_rd; + } +#else return &rtw_regdom_rd; +#endif } static void rtw_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) @@ -229,6 +402,21 @@ static struct country_code_to_enum_rd *_rtw_regd_find_country(u16 countrycode) int rtw_regd_init(struct wiphy *wiphy) { +#if 0 + if (rtw_regd == NULL) { + rtw_regd = (struct rtw_regulatory *) + rtw_malloc(sizeof(struct rtw_regulatory)); + + rtw_regd->alpha2[0] = '9'; + rtw_regd->alpha2[1] = '9'; + + rtw_regd->country_code = COUNTRY_CODE_USER; + } + + RTW_INFO("%s: Country alpha2 being used: %c%c\n", + __func__, rtw_regd->alpha2[0], rtw_regd->alpha2[1]); +#endif + _rtw_regd_init_wiphy(NULL, wiphy); return 0; diff --git a/os_dep/osdep_service.c b/os_dep/osdep_service.c index c4a2519..83c7640 100644 --- a/os_dep/osdep_service.c +++ b/os_dep/osdep_service.c @@ -2201,7 +2201,7 @@ static int isFileReadable(const char *path, u32 *sz) ret = PTR_ERR(fp); else { oldfs = get_fs(); - set_fs(KERNEL_DS); + set_fs(get_ds()); if (1 != readFile(fp, &buf, 1)) ret = PTR_ERR(fp); @@ -2239,7 +2239,7 @@ static int retriveFromFile(const char *path, u8 *buf, u32 sz) RTW_INFO("%s openFile path:%s fp=%p\n", __FUNCTION__, path , fp); oldfs = get_fs(); - set_fs(KERNEL_DS); + set_fs(get_ds()); ret = readFile(fp, buf, sz); set_fs(oldfs); closeFile(fp); @@ -2274,7 +2274,7 @@ static int storeToFile(const char *path, u8 *buf, u32 sz) RTW_INFO("%s openFile path:%s fp=%p\n", __FUNCTION__, path , fp); oldfs = get_fs(); - set_fs(KERNEL_DS); + set_fs(get_ds()); ret = writeFile(fp, buf, sz); set_fs(oldfs); closeFile(fp);