diff options
author | John W. Linville <linville@tuxdriver.com> | 2012-12-06 14:58:41 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-12-06 14:58:41 -0500 |
commit | 403e16731ffddc097eae89f53f9a7b0f0c9769c4 (patch) | |
tree | 228d17985ad8ea22a7763457bf9641eb44781568 | |
parent | 55cb0797fa779e36f62876a8aa44cbf3984e8d59 (diff) | |
parent | da29d2a5780d80857773d7776b7603a449b0b6e0 (diff) |
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Conflicts:
drivers/net/wireless/mwifiex/sta_ioctl.c
net/mac80211/scan.c
35 files changed, 1138 insertions, 550 deletions
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index ec36868f6fc..ec6d5d6b452 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c | |||
@@ -298,6 +298,7 @@ static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss) | |||
298 | const u8 *rates_eid, *ext_rates_eid; | 298 | const u8 *rates_eid, *ext_rates_eid; |
299 | int n = 0; | 299 | int n = 0; |
300 | 300 | ||
301 | rcu_read_lock(); | ||
301 | rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); | 302 | rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); |
302 | ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); | 303 | ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); |
303 | 304 | ||
@@ -325,6 +326,7 @@ static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss) | |||
325 | *tlv++ = 0x96; | 326 | *tlv++ = 0x96; |
326 | n = 4; | 327 | n = 4; |
327 | } | 328 | } |
329 | rcu_read_unlock(); | ||
328 | 330 | ||
329 | rate_tlv->header.len = cpu_to_le16(n); | 331 | rate_tlv->header.len = cpu_to_le16(n); |
330 | return sizeof(rate_tlv->header) + n; | 332 | return sizeof(rate_tlv->header) + n; |
@@ -1140,11 +1142,13 @@ static int lbs_associate(struct lbs_private *priv, | |||
1140 | cmd->capability = cpu_to_le16(bss->capability); | 1142 | cmd->capability = cpu_to_le16(bss->capability); |
1141 | 1143 | ||
1142 | /* add SSID TLV */ | 1144 | /* add SSID TLV */ |
1145 | rcu_read_lock(); | ||
1143 | ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); | 1146 | ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); |
1144 | if (ssid_eid) | 1147 | if (ssid_eid) |
1145 | pos += lbs_add_ssid_tlv(pos, ssid_eid + 2, ssid_eid[1]); | 1148 | pos += lbs_add_ssid_tlv(pos, ssid_eid + 2, ssid_eid[1]); |
1146 | else | 1149 | else |
1147 | lbs_deb_assoc("no SSID\n"); | 1150 | lbs_deb_assoc("no SSID\n"); |
1151 | rcu_read_unlock(); | ||
1148 | 1152 | ||
1149 | /* add DS param TLV */ | 1153 | /* add DS param TLV */ |
1150 | if (bss->channel) | 1154 | if (bss->channel) |
@@ -1782,7 +1786,7 @@ static int lbs_ibss_join_existing(struct lbs_private *priv, | |||
1782 | struct cfg80211_ibss_params *params, | 1786 | struct cfg80211_ibss_params *params, |
1783 | struct cfg80211_bss *bss) | 1787 | struct cfg80211_bss *bss) |
1784 | { | 1788 | { |
1785 | const u8 *rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); | 1789 | const u8 *rates_eid; |
1786 | struct cmd_ds_802_11_ad_hoc_join cmd; | 1790 | struct cmd_ds_802_11_ad_hoc_join cmd; |
1787 | u8 preamble = RADIO_PREAMBLE_SHORT; | 1791 | u8 preamble = RADIO_PREAMBLE_SHORT; |
1788 | int ret = 0; | 1792 | int ret = 0; |
@@ -1841,6 +1845,8 @@ static int lbs_ibss_join_existing(struct lbs_private *priv, | |||
1841 | 1845 | ||
1842 | /* set rates to the intersection of our rates and the rates in the | 1846 | /* set rates to the intersection of our rates and the rates in the |
1843 | bss */ | 1847 | bss */ |
1848 | rcu_read_lock(); | ||
1849 | rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); | ||
1844 | if (!rates_eid) { | 1850 | if (!rates_eid) { |
1845 | lbs_add_rates(cmd.bss.rates); | 1851 | lbs_add_rates(cmd.bss.rates); |
1846 | } else { | 1852 | } else { |
@@ -1860,6 +1866,7 @@ static int lbs_ibss_join_existing(struct lbs_private *priv, | |||
1860 | } | 1866 | } |
1861 | } | 1867 | } |
1862 | } | 1868 | } |
1869 | rcu_read_unlock(); | ||
1863 | 1870 | ||
1864 | /* Only v8 and below support setting this */ | 1871 | /* Only v8 and below support setting this */ |
1865 | if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) { | 1872 | if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) { |
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 2aa8a1aa118..8a61dbd320e 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c | |||
@@ -1347,9 +1347,14 @@ static void hw_scan_work(struct work_struct *work) | |||
1347 | hwsim->hw_scan_vif, | 1347 | hwsim->hw_scan_vif, |
1348 | req->ssids[i].ssid, | 1348 | req->ssids[i].ssid, |
1349 | req->ssids[i].ssid_len, | 1349 | req->ssids[i].ssid_len, |
1350 | req->ie, req->ie_len); | 1350 | req->ie_len); |
1351 | if (!probe) | 1351 | if (!probe) |
1352 | continue; | 1352 | continue; |
1353 | |||
1354 | if (req->ie_len) | ||
1355 | memcpy(skb_put(probe, req->ie_len), req->ie, | ||
1356 | req->ie_len); | ||
1357 | |||
1353 | local_bh_disable(); | 1358 | local_bh_disable(); |
1354 | mac80211_hwsim_tx_frame(hwsim->hw, probe, | 1359 | mac80211_hwsim_tx_frame(hwsim->hw, probe, |
1355 | hwsim->tmp_chan); | 1360 | hwsim->tmp_chan); |
diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 95e3ab531c9..cb682561c43 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c | |||
@@ -160,11 +160,21 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, | |||
160 | { | 160 | { |
161 | int ret; | 161 | int ret; |
162 | u8 *beacon_ie; | 162 | u8 *beacon_ie; |
163 | size_t beacon_ie_len; | ||
163 | struct mwifiex_bss_priv *bss_priv = (void *)bss->priv; | 164 | struct mwifiex_bss_priv *bss_priv = (void *)bss->priv; |
164 | size_t beacon_ie_len = bss->len_information_elements; | 165 | const struct cfg80211_bss_ies *ies; |
166 | |||
167 | rcu_read_lock(); | ||
168 | ies = rcu_dereference(bss->ies); | ||
169 | if (WARN_ON(!ies)) { | ||
170 | /* should never happen */ | ||
171 | rcu_read_unlock(); | ||
172 | return -EINVAL; | ||
173 | } | ||
174 | beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC); | ||
175 | beacon_ie_len = ies->len; | ||
176 | rcu_read_unlock(); | ||
165 | 177 | ||
166 | beacon_ie = kmemdup(bss->information_elements, beacon_ie_len, | ||
167 | GFP_KERNEL); | ||
168 | if (!beacon_ie) { | 178 | if (!beacon_ie) { |
169 | dev_err(priv->adapter->dev, " failed to alloc beacon_ie\n"); | 179 | dev_err(priv->adapter->dev, " failed to alloc beacon_ie\n"); |
170 | return -ENOMEM; | 180 | return -ENOMEM; |
@@ -199,18 +209,23 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, | |||
199 | static int mwifiex_process_country_ie(struct mwifiex_private *priv, | 209 | static int mwifiex_process_country_ie(struct mwifiex_private *priv, |
200 | struct cfg80211_bss *bss) | 210 | struct cfg80211_bss *bss) |
201 | { | 211 | { |
202 | u8 *country_ie, country_ie_len; | 212 | const u8 *country_ie; |
213 | u8 country_ie_len; | ||
203 | struct mwifiex_802_11d_domain_reg *domain_info = | 214 | struct mwifiex_802_11d_domain_reg *domain_info = |
204 | &priv->adapter->domain_reg; | 215 | &priv->adapter->domain_reg; |
205 | 216 | ||
206 | country_ie = (u8 *)ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); | 217 | rcu_read_lock(); |
207 | 218 | country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); | |
208 | if (!country_ie) | 219 | if (!country_ie) { |
220 | rcu_read_unlock(); | ||
209 | return 0; | 221 | return 0; |
222 | } | ||
210 | 223 | ||
211 | country_ie_len = country_ie[1]; | 224 | country_ie_len = country_ie[1]; |
212 | if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) | 225 | if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) { |
226 | rcu_read_unlock(); | ||
213 | return 0; | 227 | return 0; |
228 | } | ||
214 | 229 | ||
215 | domain_info->country_code[0] = country_ie[2]; | 230 | domain_info->country_code[0] = country_ie[2]; |
216 | domain_info->country_code[1] = country_ie[3]; | 231 | domain_info->country_code[1] = country_ie[3]; |
@@ -224,6 +239,8 @@ static int mwifiex_process_country_ie(struct mwifiex_private *priv, | |||
224 | memcpy((u8 *)domain_info->triplet, | 239 | memcpy((u8 *)domain_info->triplet, |
225 | &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len); | 240 | &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len); |
226 | 241 | ||
242 | rcu_read_unlock(); | ||
243 | |||
227 | if (mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, | 244 | if (mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, |
228 | HostCmd_ACT_GEN_SET, 0, NULL)) { | 245 | HostCmd_ACT_GEN_SET, 0, NULL)) { |
229 | wiphy_err(priv->adapter->wiphy, | 246 | wiphy_err(priv->adapter->wiphy, |
diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index 441cbccbd38..f47e8b0482a 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c | |||
@@ -896,11 +896,13 @@ static int wl1251_op_hw_scan(struct ieee80211_hw *hw, | |||
896 | goto out; | 896 | goto out; |
897 | 897 | ||
898 | skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len, | 898 | skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len, |
899 | req->ie, req->ie_len); | 899 | req->ie_len); |
900 | if (!skb) { | 900 | if (!skb) { |
901 | ret = -ENOMEM; | 901 | ret = -ENOMEM; |
902 | goto out; | 902 | goto out; |
903 | } | 903 | } |
904 | if (req->ie_len) | ||
905 | memcpy(skb_put(skb, req->ie_len), req->ie, req->ie_len); | ||
904 | 906 | ||
905 | ret = wl1251_cmd_template_set(wl, CMD_PROBE_REQ, skb->data, | 907 | ret = wl1251_cmd_template_set(wl, CMD_PROBE_REQ, skb->data, |
906 | skb->len); | 908 | skb->len); |
diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index eaef3f41b25..27f83f72a93 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c | |||
@@ -1038,11 +1038,13 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, | |||
1038 | u16 template_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5; | 1038 | u16 template_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5; |
1039 | 1039 | ||
1040 | skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len, | 1040 | skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len, |
1041 | ie, ie_len); | 1041 | ie_len); |
1042 | if (!skb) { | 1042 | if (!skb) { |
1043 | ret = -ENOMEM; | 1043 | ret = -ENOMEM; |
1044 | goto out; | 1044 | goto out; |
1045 | } | 1045 | } |
1046 | if (ie_len) | ||
1047 | memcpy(skb_put(skb, ie_len), ie, ie_len); | ||
1046 | 1048 | ||
1047 | wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len); | 1049 | wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len); |
1048 | 1050 | ||
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index f9c5a787d35..8f690e53dd8 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h | |||
@@ -1213,6 +1213,21 @@ struct ieee80211_vht_cap { | |||
1213 | } __packed; | 1213 | } __packed; |
1214 | 1214 | ||
1215 | /** | 1215 | /** |
1216 | * enum ieee80211_vht_chanwidth - VHT channel width | ||
1217 | * @IEEE80211_VHT_CHANWIDTH_USE_HT: use the HT operation IE to | ||
1218 | * determine the channel width (20 or 40 MHz) | ||
1219 | * @IEEE80211_VHT_CHANWIDTH_80MHZ: 80 MHz bandwidth | ||
1220 | * @IEEE80211_VHT_CHANWIDTH_160MHZ: 160 MHz bandwidth | ||
1221 | * @IEEE80211_VHT_CHANWIDTH_80P80MHZ: 80+80 MHz bandwidth | ||
1222 | */ | ||
1223 | enum ieee80211_vht_chanwidth { | ||
1224 | IEEE80211_VHT_CHANWIDTH_USE_HT = 0, | ||
1225 | IEEE80211_VHT_CHANWIDTH_80MHZ = 1, | ||
1226 | IEEE80211_VHT_CHANWIDTH_160MHZ = 2, | ||
1227 | IEEE80211_VHT_CHANWIDTH_80P80MHZ = 3, | ||
1228 | }; | ||
1229 | |||
1230 | /** | ||
1216 | * struct ieee80211_vht_operation - VHT operation IE | 1231 | * struct ieee80211_vht_operation - VHT operation IE |
1217 | * | 1232 | * |
1218 | * This structure is the "VHT operation element" as | 1233 | * This structure is the "VHT operation element" as |
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e78db2cf3d1..8e6a6b73b9c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h | |||
@@ -58,6 +58,8 @@ | |||
58 | * structures here describe these capabilities in detail. | 58 | * structures here describe these capabilities in detail. |
59 | */ | 59 | */ |
60 | 60 | ||
61 | struct wiphy; | ||
62 | |||
61 | /* | 63 | /* |
62 | * wireless hardware capability structures | 64 | * wireless hardware capability structures |
63 | */ | 65 | */ |
@@ -388,6 +390,22 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *chandef1, | |||
388 | const struct cfg80211_chan_def *chandef2); | 390 | const struct cfg80211_chan_def *chandef2); |
389 | 391 | ||
390 | /** | 392 | /** |
393 | * cfg80211_chandef_valid - check if a channel definition is valid | ||
394 | * @chandef: the channel definition to check | ||
395 | */ | ||
396 | bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef); | ||
397 | |||
398 | /** | ||
399 | * cfg80211_chandef_usable - check if secondary channels can be used | ||
400 | * @wiphy: the wiphy to validate against | ||
401 | * @chandef: the channel definition to check | ||
402 | * @prohibited_flags: the regulatory chanenl flags that must not be set | ||
403 | */ | ||
404 | bool cfg80211_chandef_usable(struct wiphy *wiphy, | ||
405 | const struct cfg80211_chan_def *chandef, | ||
406 | u32 prohibited_flags); | ||
407 | |||
408 | /** | ||
391 | * enum survey_info_flags - survey information flags | 409 | * enum survey_info_flags - survey information flags |
392 | * | 410 | * |
393 | * @SURVEY_INFO_NOISE_DBM: noise (in dBm) was filled in | 411 | * @SURVEY_INFO_NOISE_DBM: noise (in dBm) was filled in |
@@ -520,6 +538,8 @@ struct cfg80211_beacon_data { | |||
520 | * @privacy: the BSS uses privacy | 538 | * @privacy: the BSS uses privacy |
521 | * @auth_type: Authentication type (algorithm) | 539 | * @auth_type: Authentication type (algorithm) |
522 | * @inactivity_timeout: time in seconds to determine station's inactivity. | 540 | * @inactivity_timeout: time in seconds to determine station's inactivity. |
541 | * @p2p_ctwindow: P2P CT Window | ||
542 | * @p2p_opp_ps: P2P opportunistic PS | ||
523 | */ | 543 | */ |
524 | struct cfg80211_ap_settings { | 544 | struct cfg80211_ap_settings { |
525 | struct cfg80211_chan_def chandef; | 545 | struct cfg80211_chan_def chandef; |
@@ -534,6 +554,8 @@ struct cfg80211_ap_settings { | |||
534 | bool privacy; | 554 | bool privacy; |
535 | enum nl80211_auth_type auth_type; | 555 | enum nl80211_auth_type auth_type; |
536 | int inactivity_timeout; | 556 | int inactivity_timeout; |
557 | u8 p2p_ctwindow; | ||
558 | bool p2p_opp_ps; | ||
537 | }; | 559 | }; |
538 | 560 | ||
539 | /** | 561 | /** |
@@ -895,6 +917,8 @@ struct mpath_info { | |||
895 | * @ap_isolate: do not forward packets between connected stations | 917 | * @ap_isolate: do not forward packets between connected stations |
896 | * @ht_opmode: HT Operation mode | 918 | * @ht_opmode: HT Operation mode |
897 | * (u16 = opmode, -1 = do not change) | 919 | * (u16 = opmode, -1 = do not change) |
920 | * @p2p_ctwindow: P2P CT Window (-1 = no change) | ||
921 | * @p2p_opp_ps: P2P opportunistic PS (-1 = no change) | ||
898 | */ | 922 | */ |
899 | struct bss_parameters { | 923 | struct bss_parameters { |
900 | int use_cts_prot; | 924 | int use_cts_prot; |
@@ -904,6 +928,7 @@ struct bss_parameters { | |||
904 | u8 basic_rates_len; | 928 | u8 basic_rates_len; |
905 | int ap_isolate; | 929 | int ap_isolate; |
906 | int ht_opmode; | 930 | int ht_opmode; |
931 | s8 p2p_ctwindow, p2p_opp_ps; | ||
907 | }; | 932 | }; |
908 | 933 | ||
909 | /** | 934 | /** |
@@ -1045,9 +1070,6 @@ struct ieee80211_txq_params { | |||
1045 | u8 aifs; | 1070 | u8 aifs; |
1046 | }; | 1071 | }; |
1047 | 1072 | ||
1048 | /* from net/wireless.h */ | ||
1049 | struct wiphy; | ||
1050 | |||
1051 | /** | 1073 | /** |
1052 | * DOC: Scanning and BSS list handling | 1074 | * DOC: Scanning and BSS list handling |
1053 | * | 1075 | * |
@@ -1184,6 +1206,18 @@ enum cfg80211_signal_type { | |||
1184 | }; | 1206 | }; |
1185 | 1207 | ||
1186 | /** | 1208 | /** |
1209 | * struct cfg80211_bss_ie_data - BSS entry IE data | ||
1210 | * @rcu_head: internal use, for freeing | ||
1211 | * @len: length of the IEs | ||
1212 | * @data: IE data | ||
1213 | */ | ||
1214 | struct cfg80211_bss_ies { | ||
1215 | struct rcu_head rcu_head; | ||
1216 | int len; | ||
1217 | u8 data[]; | ||
1218 | }; | ||
1219 | |||
1220 | /** | ||
1187 | * struct cfg80211_bss - BSS description | 1221 | * struct cfg80211_bss - BSS description |
1188 | * | 1222 | * |
1189 | * This structure describes a BSS (which may also be a mesh network) | 1223 | * This structure describes a BSS (which may also be a mesh network) |
@@ -1194,36 +1228,34 @@ enum cfg80211_signal_type { | |||
1194 | * @tsf: timestamp of last received update | 1228 | * @tsf: timestamp of last received update |
1195 | * @beacon_interval: the beacon interval as from the frame | 1229 | * @beacon_interval: the beacon interval as from the frame |
1196 | * @capability: the capability field in host byte order | 1230 | * @capability: the capability field in host byte order |
1197 | * @information_elements: the information elements (Note that there | 1231 | * @ies: the information elements (Note that there |
1198 | * is no guarantee that these are well-formed!); this is a pointer to | 1232 | * is no guarantee that these are well-formed!); this is a pointer to |
1199 | * either the beacon_ies or proberesp_ies depending on whether Probe | 1233 | * either the beacon_ies or proberesp_ies depending on whether Probe |
1200 | * Response frame has been received | 1234 | * Response frame has been received |
1201 | * @len_information_elements: total length of the information elements | ||
1202 | * @beacon_ies: the information elements from the last Beacon frame | 1235 | * @beacon_ies: the information elements from the last Beacon frame |
1203 | * @len_beacon_ies: total length of the beacon_ies | ||
1204 | * @proberesp_ies: the information elements from the last Probe Response frame | 1236 | * @proberesp_ies: the information elements from the last Probe Response frame |
1205 | * @len_proberesp_ies: total length of the proberesp_ies | ||
1206 | * @signal: signal strength value (type depends on the wiphy's signal_type) | 1237 | * @signal: signal strength value (type depends on the wiphy's signal_type) |
1207 | * @free_priv: function pointer to free private data | 1238 | * @free_priv: function pointer to free private data |
1208 | * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes | 1239 | * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes |
1209 | */ | 1240 | */ |
1210 | struct cfg80211_bss { | 1241 | struct cfg80211_bss { |
1242 | u64 tsf; | ||
1243 | |||
1211 | struct ieee80211_channel *channel; | 1244 | struct ieee80211_channel *channel; |
1212 | 1245 | ||
1213 | u8 bssid[ETH_ALEN]; | 1246 | const struct cfg80211_bss_ies __rcu *ies; |
1214 | u64 tsf; | 1247 | const struct cfg80211_bss_ies __rcu *beacon_ies; |
1248 | const struct cfg80211_bss_ies __rcu *proberesp_ies; | ||
1249 | |||
1250 | void (*free_priv)(struct cfg80211_bss *bss); | ||
1251 | |||
1252 | s32 signal; | ||
1253 | |||
1215 | u16 beacon_interval; | 1254 | u16 beacon_interval; |
1216 | u16 capability; | 1255 | u16 capability; |
1217 | u8 *information_elements; | ||
1218 | size_t len_information_elements; | ||
1219 | u8 *beacon_ies; | ||
1220 | size_t len_beacon_ies; | ||
1221 | u8 *proberesp_ies; | ||
1222 | size_t len_proberesp_ies; | ||
1223 | 1256 | ||
1224 | s32 signal; | 1257 | u8 bssid[ETH_ALEN]; |
1225 | 1258 | ||
1226 | void (*free_priv)(struct cfg80211_bss *bss); | ||
1227 | u8 priv[0] __attribute__((__aligned__(sizeof(void *)))); | 1259 | u8 priv[0] __attribute__((__aligned__(sizeof(void *)))); |
1228 | }; | 1260 | }; |
1229 | 1261 | ||
@@ -1231,6 +1263,9 @@ struct cfg80211_bss { | |||
1231 | * ieee80211_bss_get_ie - find IE with given ID | 1263 | * ieee80211_bss_get_ie - find IE with given ID |
1232 | * @bss: the bss to search | 1264 | * @bss: the bss to search |
1233 | * @ie: the IE ID | 1265 | * @ie: the IE ID |
1266 | * | ||
1267 | * Note that the return value is an RCU-protected pointer, so | ||
1268 | * rcu_read_lock() must be held when calling this function. | ||
1234 | * Returns %NULL if not found. | 1269 | * Returns %NULL if not found. |
1235 | */ | 1270 | */ |
1236 | const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie); | 1271 | const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie); |
diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index 7f0df133d11..c3999632e61 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h | |||
@@ -186,6 +186,10 @@ struct ieee80211_radiotap_header { | |||
186 | * IEEE80211_RADIOTAP_AMPDU_STATUS u32, u16, u8, u8 unitless | 186 | * IEEE80211_RADIOTAP_AMPDU_STATUS u32, u16, u8, u8 unitless |
187 | * | 187 | * |
188 | * Contains the AMPDU information for the subframe. | 188 | * Contains the AMPDU information for the subframe. |
189 | * | ||
190 | * IEEE80211_RADIOTAP_VHT u16, u8, u8, u8[4], u8, u8, u16 | ||
191 | * | ||
192 | * Contains VHT information about this frame. | ||
189 | */ | 193 | */ |
190 | enum ieee80211_radiotap_type { | 194 | enum ieee80211_radiotap_type { |
191 | IEEE80211_RADIOTAP_TSFT = 0, | 195 | IEEE80211_RADIOTAP_TSFT = 0, |
@@ -209,6 +213,7 @@ enum ieee80211_radiotap_type { | |||
209 | 213 | ||
210 | IEEE80211_RADIOTAP_MCS = 19, | 214 | IEEE80211_RADIOTAP_MCS = 19, |
211 | IEEE80211_RADIOTAP_AMPDU_STATUS = 20, | 215 | IEEE80211_RADIOTAP_AMPDU_STATUS = 20, |
216 | IEEE80211_RADIOTAP_VHT = 21, | ||
212 | 217 | ||
213 | /* valid in every it_present bitmap, even vendor namespaces */ | 218 | /* valid in every it_present bitmap, even vendor namespaces */ |
214 | IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29, | 219 | IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29, |
@@ -282,6 +287,25 @@ enum ieee80211_radiotap_type { | |||
282 | #define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR 0x0010 | 287 | #define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR 0x0010 |
283 | #define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN 0x0020 | 288 | #define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN 0x0020 |
284 | 289 | ||
290 | /* For IEEE80211_RADIOTAP_VHT */ | ||
291 | #define IEEE80211_RADIOTAP_VHT_KNOWN_STBC 0x0001 | ||
292 | #define IEEE80211_RADIOTAP_VHT_KNOWN_TXOP_PS_NA 0x0002 | ||
293 | #define IEEE80211_RADIOTAP_VHT_KNOWN_GI 0x0004 | ||
294 | #define IEEE80211_RADIOTAP_VHT_KNOWN_SGI_NSYM_DIS 0x0008 | ||
295 | #define IEEE80211_RADIOTAP_VHT_KNOWN_LDPC_EXTRA_OFDM_SYM 0x0010 | ||
296 | #define IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED 0x0020 | ||
297 | #define IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH 0x0040 | ||
298 | #define IEEE80211_RADIOTAP_VHT_KNOWN_GROUP_ID 0x0080 | ||
299 | #define IEEE80211_RADIOTAP_VHT_KNOWN_PARTIAL_AID 0x0100 | ||
300 | |||
301 | #define IEEE80211_RADIOTAP_VHT_FLAG_STBC 0x01 | ||
302 | #define IEEE80211_RADIOTAP_VHT_FLAG_TXOP_PS_NA 0x02 | ||
303 | #define IEEE80211_RADIOTAP_VHT_FLAG_SGI 0x04 | ||
304 | #define IEEE80211_RADIOTAP_VHT_FLAG_SGI_NSYM_M10_9 0x08 | ||
305 | #define IEEE80211_RADIOTAP_VHT_FLAG_LDPC_EXTRA_OFDM_SYM 0x10 | ||
306 | #define IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED 0x20 | ||
307 | |||
308 | |||
285 | /* helpers */ | 309 | /* helpers */ |
286 | static inline int ieee80211_get_radiotap_len(unsigned char *data) | 310 | static inline int ieee80211_get_radiotap_len(unsigned char *data) |
287 | { | 311 | { |
diff --git a/include/net/mac80211.h b/include/net/mac80211.h index db7680acd0d..1c02fb3b381 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h | |||
@@ -164,7 +164,7 @@ enum ieee80211_chanctx_change { | |||
164 | * active on the channel to receive MIMO transmissions | 164 | * active on the channel to receive MIMO transmissions |
165 | * @rx_chains_dynamic: The number of RX chains that must be enabled | 165 | * @rx_chains_dynamic: The number of RX chains that must be enabled |
166 | * after RTS/CTS handshake to receive SMPS MIMO transmissions; | 166 | * after RTS/CTS handshake to receive SMPS MIMO transmissions; |
167 | * this will always be >= @rx_chains_always. | 167 | * this will always be >= @rx_chains_static. |
168 | * @drv_priv: data area for driver use, will always be aligned to | 168 | * @drv_priv: data area for driver use, will always be aligned to |
169 | * sizeof(void *), size is determined in hw information. | 169 | * sizeof(void *), size is determined in hw information. |
170 | */ | 170 | */ |
@@ -1473,6 +1473,10 @@ enum ieee80211_hw_flags { | |||
1473 | * include _FMT. Use %IEEE80211_RADIOTAP_MCS_HAVE_* values, only | 1473 | * include _FMT. Use %IEEE80211_RADIOTAP_MCS_HAVE_* values, only |
1474 | * adding _BW is supported today. | 1474 | * adding _BW is supported today. |
1475 | * | 1475 | * |
1476 | * @radiotap_vht_details: lists which VHT MCS information the HW reports, | ||
1477 | * the default is _GI | _BANDWIDTH. | ||
1478 | * Use the %IEEE80211_RADIOTAP_VHT_KNOWN_* values. | ||
1479 | * | ||
1476 | * @netdev_features: netdev features to be set in each netdev created | 1480 | * @netdev_features: netdev features to be set in each netdev created |
1477 | * from this HW. Note only HW checksum features are currently | 1481 | * from this HW. Note only HW checksum features are currently |
1478 | * compatible with mac80211. Other feature bits will be rejected. | 1482 | * compatible with mac80211. Other feature bits will be rejected. |
@@ -1499,6 +1503,7 @@ struct ieee80211_hw { | |||
1499 | u8 max_tx_aggregation_subframes; | 1503 | u8 max_tx_aggregation_subframes; |
1500 | u8 offchannel_tx_hw_queue; | 1504 | u8 offchannel_tx_hw_queue; |
1501 | u8 radiotap_mcs_details; | 1505 | u8 radiotap_mcs_details; |
1506 | u16 radiotap_vht_details; | ||
1502 | netdev_features_t netdev_features; | 1507 | netdev_features_t netdev_features; |
1503 | }; | 1508 | }; |
1504 | 1509 | ||
@@ -3139,8 +3144,7 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw, | |||
3139 | * @vif: &struct ieee80211_vif pointer from the add_interface callback. | 3144 | * @vif: &struct ieee80211_vif pointer from the add_interface callback. |
3140 | * @ssid: SSID buffer | 3145 | * @ssid: SSID buffer |
3141 | * @ssid_len: length of SSID | 3146 | * @ssid_len: length of SSID |
3142 | * @ie: buffer containing all IEs except SSID for the template | 3147 | * @tailroom: tailroom to reserve at end of SKB for IEs |
3143 | * @ie_len: length of the IE buffer | ||
3144 | * | 3148 | * |
3145 | * Creates a Probe Request template which can, for example, be uploaded to | 3149 | * Creates a Probe Request template which can, for example, be uploaded to |
3146 | * hardware. | 3150 | * hardware. |
@@ -3148,7 +3152,7 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw, | |||
3148 | struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, | 3152 | struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, |
3149 | struct ieee80211_vif *vif, | 3153 | struct ieee80211_vif *vif, |
3150 | const u8 *ssid, size_t ssid_len, | 3154 | const u8 *ssid, size_t ssid_len, |
3151 | const u8 *ie, size_t ie_len); | 3155 | size_t tailroom); |
3152 | 3156 | ||
3153 | /** | 3157 | /** |
3154 | * ieee80211_rts_get - RTS frame generation function | 3158 | * ieee80211_rts_get - RTS frame generation function |
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 33a417481ad..e3e19f8b16f 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h | |||
@@ -1303,6 +1303,13 @@ enum nl80211_commands { | |||
1303 | * | 1303 | * |
1304 | * @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32) | 1304 | * @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32) |
1305 | * | 1305 | * |
1306 | * @NL80211_ATTR_P2P_CTWINDOW: P2P GO Client Traffic Window (u8), used with | ||
1307 | * the START_AP and SET_BSS commands | ||
1308 | * @NL80211_ATTR_P2P_OPPPS: P2P GO opportunistic PS (u8), used with the | ||
1309 | * START_AP and SET_BSS commands. This can have the values 0 or 1; | ||
1310 | * if not given in START_AP 0 is assumed, if not given in SET_BSS | ||
1311 | * no change is made. | ||
1312 | * | ||
1306 | * @NL80211_ATTR_MAX: highest attribute number currently defined | 1313 | * @NL80211_ATTR_MAX: highest attribute number currently defined |
1307 | * @__NL80211_ATTR_AFTER_LAST: internal use | 1314 | * @__NL80211_ATTR_AFTER_LAST: internal use |
1308 | */ | 1315 | */ |
@@ -1570,6 +1577,9 @@ enum nl80211_attrs { | |||
1570 | NL80211_ATTR_CENTER_FREQ1, | 1577 | NL80211_ATTR_CENTER_FREQ1, |
1571 | NL80211_ATTR_CENTER_FREQ2, | 1578 | NL80211_ATTR_CENTER_FREQ2, |
1572 | 1579 | ||
1580 | NL80211_ATTR_P2P_CTWINDOW, | ||
1581 | NL80211_ATTR_P2P_OPPPS, | ||
1582 | |||
1573 | /* add attributes here, update the policy in nl80211.c */ | 1583 | /* add attributes here, update the policy in nl80211.c */ |
1574 | 1584 | ||
1575 | __NL80211_ATTR_AFTER_LAST, | 1585 | __NL80211_ATTR_AFTER_LAST, |
@@ -3126,6 +3136,10 @@ enum nl80211_ap_sme_features { | |||
3126 | * @NL80211_FEATURE_NEED_OBSS_SCAN: The driver expects userspace to perform | 3136 | * @NL80211_FEATURE_NEED_OBSS_SCAN: The driver expects userspace to perform |
3127 | * OBSS scans and generate 20/40 BSS coex reports. This flag is used only | 3137 | * OBSS scans and generate 20/40 BSS coex reports. This flag is used only |
3128 | * for drivers implementing the CONNECT API, for AUTH/ASSOC it is implied. | 3138 | * for drivers implementing the CONNECT API, for AUTH/ASSOC it is implied. |
3139 | * @NL80211_FEATURE_P2P_GO_CTWIN: P2P GO implementation supports CT Window | ||
3140 | * setting | ||
3141 | * @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic | ||
3142 | * powersave | ||
3129 | */ | 3143 | */ |
3130 | enum nl80211_feature_flags { | 3144 | enum nl80211_feature_flags { |
3131 | NL80211_FEATURE_SK_TX_STATUS = 1 << 0, | 3145 | NL80211_FEATURE_SK_TX_STATUS = 1 << 0, |
@@ -3139,6 +3153,8 @@ enum nl80211_feature_flags { | |||
3139 | NL80211_FEATURE_AP_SCAN = 1 << 8, | 3153 | NL80211_FEATURE_AP_SCAN = 1 << 8, |
3140 | NL80211_FEATURE_VIF_TXPOWER = 1 << 9, | 3154 | NL80211_FEATURE_VIF_TXPOWER = 1 << 9, |
3141 | NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10, | 3155 | NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10, |
3156 | NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11, | ||
3157 | NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12, | ||
3142 | }; | 3158 | }; |
3143 | 3159 | ||
3144 | /** | 3160 | /** |
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 4965aa6424e..5c61677487c 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -398,6 +398,38 @@ void sta_set_rate_info_tx(struct sta_info *sta, | |||
398 | rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; | 398 | rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; |
399 | } | 399 | } |
400 | 400 | ||
401 | void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo) | ||
402 | { | ||
403 | rinfo->flags = 0; | ||
404 | |||
405 | if (sta->last_rx_rate_flag & RX_FLAG_HT) { | ||
406 | rinfo->flags |= RATE_INFO_FLAGS_MCS; | ||
407 | rinfo->mcs = sta->last_rx_rate_idx; | ||
408 | } else if (sta->last_rx_rate_flag & RX_FLAG_VHT) { | ||
409 | rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS; | ||
410 | rinfo->nss = sta->last_rx_rate_vht_nss; | ||
411 | rinfo->mcs = sta->last_rx_rate_idx; | ||
412 | } else { | ||
413 | struct ieee80211_supported_band *sband; | ||
414 | |||
415 | sband = sta->local->hw.wiphy->bands[ | ||
416 | ieee80211_get_sdata_band(sta->sdata)]; | ||
417 | rinfo->legacy = | ||
418 | sband->bitrates[sta->last_rx_rate_idx].bitrate; | ||
419 | } | ||
420 | |||
421 | if (sta->last_rx_rate_flag & RX_FLAG_40MHZ) | ||
422 | rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; | ||
423 | if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI) | ||
424 | rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; | ||
425 | if (sta->last_rx_rate_flag & RX_FLAG_80MHZ) | ||
426 | rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; | ||
427 | if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ) | ||
428 | rinfo->flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH; | ||
429 | if (sta->last_rx_rate_flag & RX_FLAG_160MHZ) | ||
430 | rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH; | ||
431 | } | ||
432 | |||
401 | static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) | 433 | static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) |
402 | { | 434 | { |
403 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 435 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
@@ -444,34 +476,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) | |||
444 | } | 476 | } |
445 | 477 | ||
446 | sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate); | 478 | sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate); |
447 | 479 | sta_set_rate_info_rx(sta, &sinfo->rxrate); | |
448 | sinfo->rxrate.flags = 0; | ||
449 | if (sta->last_rx_rate_flag & RX_FLAG_HT) { | ||
450 | sinfo->rxrate.flags |= RATE_INFO_FLAGS_MCS; | ||
451 | sinfo->rxrate.mcs = sta->last_rx_rate_idx; | ||
452 | } else if (sta->last_rx_rate_flag & RX_FLAG_VHT) { | ||
453 | sinfo->rxrate.flags |= RATE_INFO_FLAGS_VHT_MCS; | ||
454 | sinfo->rxrate.nss = sta->last_rx_rate_vht_nss; | ||
455 | sinfo->rxrate.mcs = sta->last_rx_rate_idx; | ||
456 | } else { | ||
457 | struct ieee80211_supported_band *sband; | ||
458 | |||
459 | sband = sta->local->hw.wiphy->bands[ | ||
460 | ieee80211_get_sdata_band(sta->sdata)]; | ||
461 | sinfo->rxrate.legacy = | ||
462 | sband->bitrates[sta->last_rx_rate_idx].bitrate; | ||
463 | } | ||
464 | |||
465 | if (sta->last_rx_rate_flag & RX_FLAG_40MHZ) | ||
466 | sinfo->rxrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; | ||
467 | if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI) | ||
468 | sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI; | ||
469 | if (sta->last_rx_rate_flag & RX_FLAG_80MHZ) | ||
470 | sinfo->rxrate.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; | ||
471 | if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ) | ||
472 | sinfo->rxrate.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH; | ||
473 | if (sta->last_rx_rate_flag & RX_FLAG_160MHZ) | ||
474 | sinfo->rxrate.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH; | ||
475 | 480 | ||
476 | if (ieee80211_vif_is_mesh(&sdata->vif)) { | 481 | if (ieee80211_vif_is_mesh(&sdata->vif)) { |
477 | #ifdef CONFIG_MAC80211_MESH | 482 | #ifdef CONFIG_MAC80211_MESH |
@@ -893,7 +898,8 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, | |||
893 | u32 changed = BSS_CHANGED_BEACON_INT | | 898 | u32 changed = BSS_CHANGED_BEACON_INT | |
894 | BSS_CHANGED_BEACON_ENABLED | | 899 | BSS_CHANGED_BEACON_ENABLED | |
895 | BSS_CHANGED_BEACON | | 900 | BSS_CHANGED_BEACON | |
896 | BSS_CHANGED_SSID; | 901 | BSS_CHANGED_SSID | |
902 | BSS_CHANGED_P2P_PS; | ||
897 | int err; | 903 | int err; |
898 | 904 | ||
899 | old = rtnl_dereference(sdata->u.ap.beacon); | 905 | old = rtnl_dereference(sdata->u.ap.beacon); |
@@ -932,6 +938,9 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, | |||
932 | sdata->vif.bss_conf.hidden_ssid = | 938 | sdata->vif.bss_conf.hidden_ssid = |
933 | (params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE); | 939 | (params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE); |
934 | 940 | ||
941 | sdata->vif.bss_conf.p2p_ctwindow = params->p2p_ctwindow; | ||
942 | sdata->vif.bss_conf.p2p_oppps = params->p2p_opp_ps; | ||
943 | |||
935 | err = ieee80211_assign_beacon(sdata, ¶ms->beacon); | 944 | err = ieee80211_assign_beacon(sdata, ¶ms->beacon); |
936 | if (err < 0) | 945 | if (err < 0) |
937 | return err; | 946 | return err; |
@@ -1807,6 +1816,16 @@ static int ieee80211_change_bss(struct wiphy *wiphy, | |||
1807 | changed |= BSS_CHANGED_HT; | 1816 | changed |= BSS_CHANGED_HT; |
1808 | } | 1817 | } |
1809 | 1818 | ||
1819 | if (params->p2p_ctwindow >= 0) { | ||
1820 | sdata->vif.bss_conf.p2p_ctwindow = params->p2p_ctwindow; | ||
1821 | changed |= BSS_CHANGED_P2P_PS; | ||
1822 | } | ||
1823 | |||
1824 | if (params->p2p_opp_ps >= 0) { | ||
1825 | sdata->vif.bss_conf.p2p_oppps = params->p2p_opp_ps; | ||
1826 | changed |= BSS_CHANGED_P2P_PS; | ||
1827 | } | ||
1828 | |||
1810 | ieee80211_bss_info_change_notify(sdata, changed); | 1829 | ieee80211_bss_info_change_notify(sdata, changed); |
1811 | 1830 | ||
1812 | return 0; | 1831 | return 0; |
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 89281d24b09..49a1c70bbd7 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c | |||
@@ -53,6 +53,7 @@ static const struct file_operations sta_ ##name## _ops = { \ | |||
53 | STA_FILE(aid, sta.aid, D); | 53 | STA_FILE(aid, sta.aid, D); |
54 | STA_FILE(dev, sdata->name, S); | 54 | STA_FILE(dev, sdata->name, S); |
55 | STA_FILE(last_signal, last_signal, D); | 55 | STA_FILE(last_signal, last_signal, D); |
56 | STA_FILE(last_ack_signal, last_ack_signal, D); | ||
56 | 57 | ||
57 | static ssize_t sta_flags_read(struct file *file, char __user *userbuf, | 58 | static ssize_t sta_flags_read(struct file *file, char __user *userbuf, |
58 | size_t count, loff_t *ppos) | 59 | size_t count, loff_t *ppos) |
@@ -321,6 +322,38 @@ static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf, | |||
321 | } | 322 | } |
322 | STA_OPS(ht_capa); | 323 | STA_OPS(ht_capa); |
323 | 324 | ||
325 | static ssize_t sta_current_tx_rate_read(struct file *file, char __user *userbuf, | ||
326 | size_t count, loff_t *ppos) | ||
327 | { | ||
328 | struct sta_info *sta = file->private_data; | ||
329 | struct rate_info rinfo; | ||
330 | u16 rate; | ||
331 | sta_set_rate_info_tx(sta, &sta->last_tx_rate, &rinfo); | ||
332 | rate = cfg80211_calculate_bitrate(&rinfo); | ||
333 | |||
334 | return mac80211_format_buffer(userbuf, count, ppos, | ||
335 | "%d.%d MBit/s\n", | ||
336 | rate/10, rate%10); | ||
337 | } | ||
338 | STA_OPS(current_tx_rate); | ||
339 | |||
340 | static ssize_t sta_last_rx_rate_read(struct file *file, char __user *userbuf, | ||
341 | size_t count, loff_t *ppos) | ||
342 | { | ||
343 | struct sta_info *sta = file->private_data; | ||
344 | struct rate_info rinfo; | ||
345 | u16 rate; | ||
346 | |||
347 | sta_set_rate_info_rx(sta, &rinfo); | ||
348 | |||
349 | rate = cfg80211_calculate_bitrate(&rinfo); | ||
350 | |||
351 | return mac80211_format_buffer(userbuf, count, ppos, | ||
352 | "%d.%d MBit/s\n", | ||
353 | rate/10, rate%10); | ||
354 | } | ||
355 | STA_OPS(last_rx_rate); | ||
356 | |||
324 | #define DEBUGFS_ADD(name) \ | 357 | #define DEBUGFS_ADD(name) \ |
325 | debugfs_create_file(#name, 0400, \ | 358 | debugfs_create_file(#name, 0400, \ |
326 | sta->debugfs.dir, sta, &sta_ ##name## _ops); | 359 | sta->debugfs.dir, sta, &sta_ ##name## _ops); |
@@ -369,6 +402,9 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) | |||
369 | DEBUGFS_ADD(dev); | 402 | DEBUGFS_ADD(dev); |
370 | DEBUGFS_ADD(last_signal); | 403 | DEBUGFS_ADD(last_signal); |
371 | DEBUGFS_ADD(ht_capa); | 404 | DEBUGFS_ADD(ht_capa); |
405 | DEBUGFS_ADD(last_ack_signal); | ||
406 | DEBUGFS_ADD(current_tx_rate); | ||
407 | DEBUGFS_ADD(last_rx_rate); | ||
372 | 408 | ||
373 | DEBUGFS_ADD_COUNTER(rx_packets, rx_packets); | 409 | DEBUGFS_ADD_COUNTER(rx_packets, rx_packets); |
374 | DEBUGFS_ADD_COUNTER(tx_packets, tx_packets); | 410 | DEBUGFS_ADD_COUNTER(tx_packets, tx_packets); |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 5c0d5a6946c..42d0d026773 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -371,6 +371,8 @@ enum ieee80211_sta_flags { | |||
371 | IEEE80211_STA_RESET_SIGNAL_AVE = BIT(9), | 371 | IEEE80211_STA_RESET_SIGNAL_AVE = BIT(9), |
372 | IEEE80211_STA_DISABLE_40MHZ = BIT(10), | 372 | IEEE80211_STA_DISABLE_40MHZ = BIT(10), |
373 | IEEE80211_STA_DISABLE_VHT = BIT(11), | 373 | IEEE80211_STA_DISABLE_VHT = BIT(11), |
374 | IEEE80211_STA_DISABLE_80P80MHZ = BIT(12), | ||
375 | IEEE80211_STA_DISABLE_160MHZ = BIT(13), | ||
374 | }; | 376 | }; |
375 | 377 | ||
376 | struct ieee80211_mgd_auth_data { | 378 | struct ieee80211_mgd_auth_data { |
@@ -1032,6 +1034,7 @@ struct ieee80211_local { | |||
1032 | enum ieee80211_band hw_scan_band; | 1034 | enum ieee80211_band hw_scan_band; |
1033 | int scan_channel_idx; | 1035 | int scan_channel_idx; |
1034 | int scan_ies_len; | 1036 | int scan_ies_len; |
1037 | int hw_scan_ies_bufsize; | ||
1035 | 1038 | ||
1036 | struct work_struct sched_scan_stopped_work; | 1039 | struct work_struct sched_scan_stopped_work; |
1037 | struct ieee80211_sub_if_data __rcu *sched_scan_sdata; | 1040 | struct ieee80211_sub_if_data __rcu *sched_scan_sdata; |
@@ -1573,7 +1576,7 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, | |||
1573 | const u8 *bssid, u16 stype, u16 reason, | 1576 | const u8 *bssid, u16 stype, u16 reason, |
1574 | bool send_frame, u8 *frame_buf); | 1577 | bool send_frame, u8 *frame_buf); |
1575 | int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, | 1578 | int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, |
1576 | const u8 *ie, size_t ie_len, | 1579 | size_t buffer_len, const u8 *ie, size_t ie_len, |
1577 | enum ieee80211_band band, u32 rate_mask, | 1580 | enum ieee80211_band band, u32 rate_mask, |
1578 | u8 channel); | 1581 | u8 channel); |
1579 | struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, | 1582 | struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, |
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 5331662489f..40c36d5d737 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -223,6 +223,47 @@ static int ieee80211_change_mtu(struct net_device *dev, int new_mtu) | |||
223 | return 0; | 223 | return 0; |
224 | } | 224 | } |
225 | 225 | ||
226 | static int ieee80211_verify_mac(struct ieee80211_local *local, u8 *addr) | ||
227 | { | ||
228 | struct ieee80211_sub_if_data *sdata; | ||
229 | u64 new, mask, tmp; | ||
230 | u8 *m; | ||
231 | int ret = 0; | ||
232 | |||
233 | if (is_zero_ether_addr(local->hw.wiphy->addr_mask)) | ||
234 | return 0; | ||
235 | |||
236 | m = addr; | ||
237 | new = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) | | ||
238 | ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) | | ||
239 | ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8); | ||
240 | |||
241 | m = local->hw.wiphy->addr_mask; | ||
242 | mask = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) | | ||
243 | ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) | | ||
244 | ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8); | ||
245 | |||
246 | |||
247 | mutex_lock(&local->iflist_mtx); | ||
248 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
249 | if (sdata->vif.type == NL80211_IFTYPE_MONITOR) | ||
250 | continue; | ||
251 | |||
252 | m = sdata->vif.addr; | ||
253 | tmp = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) | | ||
254 | ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) | | ||
255 | ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8); | ||
256 | |||
257 | if ((new & ~mask) != (tmp & ~mask)) { | ||
258 | ret = -EINVAL; | ||
259 | break; | ||
260 | } | ||
261 | } | ||
262 | mutex_unlock(&local->iflist_mtx); | ||
263 | |||
264 | return ret; | ||
265 | } | ||
266 | |||
226 | static int ieee80211_change_mac(struct net_device *dev, void *addr) | 267 | static int ieee80211_change_mac(struct net_device *dev, void *addr) |
227 | { | 268 | { |
228 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 269 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
@@ -232,6 +273,10 @@ static int ieee80211_change_mac(struct net_device *dev, void *addr) | |||
232 | if (ieee80211_sdata_running(sdata)) | 273 | if (ieee80211_sdata_running(sdata)) |
233 | return -EBUSY; | 274 | return -EBUSY; |
234 | 275 | ||
276 | ret = ieee80211_verify_mac(sdata->local, sa->sa_data); | ||
277 | if (ret) | ||
278 | return ret; | ||
279 | |||
235 | ret = eth_mac_addr(dev, sa); | 280 | ret = eth_mac_addr(dev, sa); |
236 | 281 | ||
237 | if (ret == 0) | 282 | if (ret == 0) |
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index f5e4c1f24bf..1b087fff93e 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -474,7 +474,8 @@ ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = { | |||
474 | .tx = 0xffff, | 474 | .tx = 0xffff, |
475 | .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | | 475 | .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | |
476 | BIT(IEEE80211_STYPE_AUTH >> 4) | | 476 | BIT(IEEE80211_STYPE_AUTH >> 4) | |
477 | BIT(IEEE80211_STYPE_DEAUTH >> 4), | 477 | BIT(IEEE80211_STYPE_DEAUTH >> 4) | |
478 | BIT(IEEE80211_STYPE_PROBE_REQ >> 4), | ||
478 | }, | 479 | }, |
479 | [NL80211_IFTYPE_STATION] = { | 480 | [NL80211_IFTYPE_STATION] = { |
480 | .tx = 0xffff, | 481 | .tx = 0xffff, |
@@ -638,6 +639,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
638 | local->hw.radiotap_mcs_details = IEEE80211_RADIOTAP_MCS_HAVE_MCS | | 639 | local->hw.radiotap_mcs_details = IEEE80211_RADIOTAP_MCS_HAVE_MCS | |
639 | IEEE80211_RADIOTAP_MCS_HAVE_GI | | 640 | IEEE80211_RADIOTAP_MCS_HAVE_GI | |
640 | IEEE80211_RADIOTAP_MCS_HAVE_BW; | 641 | IEEE80211_RADIOTAP_MCS_HAVE_BW; |
642 | local->hw.radiotap_vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI | | ||
643 | IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH; | ||
641 | local->user_power_level = IEEE80211_UNSET_POWER_LEVEL; | 644 | local->user_power_level = IEEE80211_UNSET_POWER_LEVEL; |
642 | wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask; | 645 | wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask; |
643 | 646 | ||
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index ca52dfdd537..4b274e9c91a 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c | |||
@@ -391,7 +391,8 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, | |||
391 | sta->ch_width = chandef.width; | 391 | sta->ch_width = chandef.width; |
392 | } | 392 | } |
393 | 393 | ||
394 | rate_control_rate_init(sta); | 394 | if (insert) |
395 | rate_control_rate_init(sta); | ||
395 | spin_unlock_bh(&sta->lock); | 396 | spin_unlock_bh(&sta->lock); |
396 | 397 | ||
397 | if (insert && sta_info_insert(sta)) | 398 | if (insert && sta_info_insert(sta)) |
diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c index 0f40086cce1..aa8d1e43738 100644 --- a/net/mac80211/mesh_sync.c +++ b/net/mac80211/mesh_sync.c | |||
@@ -195,11 +195,15 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata) | |||
195 | ifmsh->sync_offset_clockdrift_max); | 195 | ifmsh->sync_offset_clockdrift_max); |
196 | set_bit(MESH_WORK_DRIFT_ADJUST, | 196 | set_bit(MESH_WORK_DRIFT_ADJUST, |
197 | &ifmsh->wrkq_flags); | 197 | &ifmsh->wrkq_flags); |
198 | |||
199 | ifmsh->adjusting_tbtt = true; | ||
198 | } else { | 200 | } else { |
199 | msync_dbg(sdata, | 201 | msync_dbg(sdata, |
200 | "TBTT : max clockdrift=%lld; too small to adjust\n", | 202 | "TBTT : max clockdrift=%lld; too small to adjust\n", |
201 | (long long)ifmsh->sync_offset_clockdrift_max); | 203 | (long long)ifmsh->sync_offset_clockdrift_max); |
202 | ifmsh->sync_offset_clockdrift_max = 0; | 204 | ifmsh->sync_offset_clockdrift_max = 0; |
205 | |||
206 | ifmsh->adjusting_tbtt = false; | ||
203 | } | 207 | } |
204 | spin_unlock_bh(&ifmsh->sync_offset_lock); | 208 | spin_unlock_bh(&ifmsh->sync_offset_lock); |
205 | } | 209 | } |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d2a4f78b4b0..09556303c7e 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -354,6 +354,16 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, | |||
354 | /* determine capability flags */ | 354 | /* determine capability flags */ |
355 | cap = vht_cap.cap; | 355 | cap = vht_cap.cap; |
356 | 356 | ||
357 | if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_80P80MHZ) { | ||
358 | cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; | ||
359 | cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; | ||
360 | } | ||
361 | |||
362 | if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_160MHZ) { | ||
363 | cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160; | ||
364 | cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; | ||
365 | } | ||
366 | |||
357 | /* reserve and fill IE */ | 367 | /* reserve and fill IE */ |
358 | pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); | 368 | pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); |
359 | ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); | 369 | ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); |
@@ -543,6 +553,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) | |||
543 | offset = noffset; | 553 | offset = noffset; |
544 | } | 554 | } |
545 | 555 | ||
556 | if (WARN_ON_ONCE((ifmgd->flags & IEEE80211_STA_DISABLE_HT) && | ||
557 | !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))) | ||
558 | ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; | ||
559 | |||
546 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) | 560 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) |
547 | ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param, | 561 | ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param, |
548 | sband, chan, sdata->smps_mode); | 562 | sband, chan, sdata->smps_mode); |
@@ -775,6 +789,7 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
775 | "not handling channel switch with channel contexts\n"); | 789 | "not handling channel switch with channel contexts\n"); |
776 | ieee80211_queue_work(&sdata->local->hw, | 790 | ieee80211_queue_work(&sdata->local->hw, |
777 | &ifmgd->csa_connection_drop_work); | 791 | &ifmgd->csa_connection_drop_work); |
792 | return; | ||
778 | } | 793 | } |
779 | 794 | ||
780 | mutex_lock(&sdata->local->chanctx_mtx); | 795 | mutex_lock(&sdata->local->chanctx_mtx); |
@@ -1368,19 +1383,26 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, | |||
1368 | sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE; | 1383 | sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE; |
1369 | 1384 | ||
1370 | if (sdata->vif.p2p) { | 1385 | if (sdata->vif.p2p) { |
1371 | u8 noa[2]; | 1386 | const struct cfg80211_bss_ies *ies; |
1372 | int ret; | ||
1373 | 1387 | ||
1374 | ret = cfg80211_get_p2p_attr(cbss->information_elements, | 1388 | rcu_read_lock(); |
1375 | cbss->len_information_elements, | 1389 | ies = rcu_dereference(cbss->ies); |
1376 | IEEE80211_P2P_ATTR_ABSENCE_NOTICE, | 1390 | if (ies) { |
1377 | noa, sizeof(noa)); | 1391 | u8 noa[2]; |
1378 | if (ret >= 2) { | 1392 | int ret; |
1379 | bss_conf->p2p_oppps = noa[1] & 0x80; | 1393 | |
1380 | bss_conf->p2p_ctwindow = noa[1] & 0x7f; | 1394 | ret = cfg80211_get_p2p_attr( |
1381 | bss_info_changed |= BSS_CHANGED_P2P_PS; | 1395 | ies->data, ies->len, |
1382 | sdata->u.mgd.p2p_noa_index = noa[0]; | 1396 | IEEE80211_P2P_ATTR_ABSENCE_NOTICE, |
1397 | noa, sizeof(noa)); | ||
1398 | if (ret >= 2) { | ||
1399 | bss_conf->p2p_oppps = noa[1] & 0x80; | ||
1400 | bss_conf->p2p_ctwindow = noa[1] & 0x7f; | ||
1401 | bss_info_changed |= BSS_CHANGED_P2P_PS; | ||
1402 | sdata->u.mgd.p2p_noa_index = noa[0]; | ||
1403 | } | ||
1383 | } | 1404 | } |
1405 | rcu_read_unlock(); | ||
1384 | } | 1406 | } |
1385 | 1407 | ||
1386 | /* just to be sure */ | 1408 | /* just to be sure */ |
@@ -1645,6 +1667,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) | |||
1645 | } else { | 1667 | } else { |
1646 | int ssid_len; | 1668 | int ssid_len; |
1647 | 1669 | ||
1670 | rcu_read_lock(); | ||
1648 | ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID); | 1671 | ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID); |
1649 | if (WARN_ON_ONCE(ssid == NULL)) | 1672 | if (WARN_ON_ONCE(ssid == NULL)) |
1650 | ssid_len = 0; | 1673 | ssid_len = 0; |
@@ -1654,6 +1677,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) | |||
1654 | ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL, | 1677 | ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL, |
1655 | 0, (u32) -1, true, false, | 1678 | 0, (u32) -1, true, false, |
1656 | ifmgd->associated->channel, false); | 1679 | ifmgd->associated->channel, false); |
1680 | rcu_read_unlock(); | ||
1657 | } | 1681 | } |
1658 | 1682 | ||
1659 | ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms); | 1683 | ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms); |
@@ -1749,6 +1773,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, | |||
1749 | else | 1773 | else |
1750 | return NULL; | 1774 | return NULL; |
1751 | 1775 | ||
1776 | rcu_read_lock(); | ||
1752 | ssid = ieee80211_bss_get_ie(cbss, WLAN_EID_SSID); | 1777 | ssid = ieee80211_bss_get_ie(cbss, WLAN_EID_SSID); |
1753 | if (WARN_ON_ONCE(ssid == NULL)) | 1778 | if (WARN_ON_ONCE(ssid == NULL)) |
1754 | ssid_len = 0; | 1779 | ssid_len = 0; |
@@ -1759,6 +1784,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, | |||
1759 | (u32) -1, cbss->channel, | 1784 | (u32) -1, cbss->channel, |
1760 | ssid + 2, ssid_len, | 1785 | ssid + 2, ssid_len, |
1761 | NULL, 0, true); | 1786 | NULL, 0, true); |
1787 | rcu_read_unlock(); | ||
1762 | 1788 | ||
1763 | return skb; | 1789 | return skb; |
1764 | } | 1790 | } |
@@ -2844,9 +2870,12 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) | |||
2844 | auth_data->bss->bssid, auth_data->tries, | 2870 | auth_data->bss->bssid, auth_data->tries, |
2845 | IEEE80211_AUTH_MAX_TRIES); | 2871 | IEEE80211_AUTH_MAX_TRIES); |
2846 | 2872 | ||
2873 | rcu_read_lock(); | ||
2847 | ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID); | 2874 | ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID); |
2848 | if (!ssidie) | 2875 | if (!ssidie) { |
2876 | rcu_read_unlock(); | ||
2849 | return -EINVAL; | 2877 | return -EINVAL; |
2878 | } | ||
2850 | /* | 2879 | /* |
2851 | * Direct probe is sent to broadcast address as some APs | 2880 | * Direct probe is sent to broadcast address as some APs |
2852 | * will not answer to direct packet in unassociated state. | 2881 | * will not answer to direct packet in unassociated state. |
@@ -2854,6 +2883,7 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) | |||
2854 | ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1], | 2883 | ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1], |
2855 | NULL, 0, (u32) -1, true, false, | 2884 | NULL, 0, (u32) -1, true, false, |
2856 | auth_data->bss->channel, false); | 2885 | auth_data->bss->channel, false); |
2886 | rcu_read_unlock(); | ||
2857 | } | 2887 | } |
2858 | 2888 | ||
2859 | auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; | 2889 | auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; |
@@ -3183,106 +3213,313 @@ int ieee80211_max_network_latency(struct notifier_block *nb, | |||
3183 | return 0; | 3213 | return 0; |
3184 | } | 3214 | } |
3185 | 3215 | ||
3216 | static u32 chandef_downgrade(struct cfg80211_chan_def *c) | ||
3217 | { | ||
3218 | u32 ret; | ||
3219 | int tmp; | ||
3220 | |||
3221 | switch (c->width) { | ||
3222 | case NL80211_CHAN_WIDTH_20: | ||
3223 | c->width = NL80211_CHAN_WIDTH_20_NOHT; | ||
3224 | ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; | ||
3225 | break; | ||
3226 | case NL80211_CHAN_WIDTH_40: | ||
3227 | c->width = NL80211_CHAN_WIDTH_20; | ||
3228 | c->center_freq1 = c->chan->center_freq; | ||
3229 | ret = IEEE80211_STA_DISABLE_40MHZ | | ||
3230 | IEEE80211_STA_DISABLE_VHT; | ||
3231 | break; | ||
3232 | case NL80211_CHAN_WIDTH_80: | ||
3233 | tmp = (30 + c->chan->center_freq - c->center_freq1)/20; | ||
3234 | /* n_P40 */ | ||
3235 | tmp /= 2; | ||
3236 | /* freq_P40 */ | ||
3237 | c->center_freq1 = c->center_freq1 - 20 + 40 * tmp; | ||
3238 | c->width = NL80211_CHAN_WIDTH_40; | ||
3239 | ret = IEEE80211_STA_DISABLE_VHT; | ||
3240 | break; | ||
3241 | case NL80211_CHAN_WIDTH_80P80: | ||
3242 | c->center_freq2 = 0; | ||
3243 | c->width = NL80211_CHAN_WIDTH_80; | ||
3244 | ret = IEEE80211_STA_DISABLE_80P80MHZ | | ||
3245 | IEEE80211_STA_DISABLE_160MHZ; | ||
3246 | break; | ||
3247 | case NL80211_CHAN_WIDTH_160: | ||
3248 | /* n_P20 */ | ||
3249 | tmp = (70 + c->chan->center_freq - c->center_freq1)/20; | ||
3250 | /* n_P80 */ | ||
3251 | tmp /= 4; | ||
3252 | c->center_freq1 = c->center_freq1 - 40 + 80 * tmp; | ||
3253 | c->width = NL80211_CHAN_WIDTH_80; | ||
3254 | ret = IEEE80211_STA_DISABLE_80P80MHZ | | ||
3255 | IEEE80211_STA_DISABLE_160MHZ; | ||
3256 | break; | ||
3257 | default: | ||
3258 | case NL80211_CHAN_WIDTH_20_NOHT: | ||
3259 | WARN_ON_ONCE(1); | ||
3260 | c->width = NL80211_CHAN_WIDTH_20_NOHT; | ||
3261 | ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; | ||
3262 | break; | ||
3263 | } | ||
3264 | |||
3265 | WARN_ON_ONCE(!cfg80211_chandef_valid(c)); | ||
3266 | |||
3267 | return ret; | ||
3268 | } | ||
3269 | |||
3270 | static u32 | ||
3271 | ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, | ||
3272 | struct ieee80211_supported_band *sband, | ||
3273 | struct ieee80211_channel *channel, | ||
3274 | const struct ieee80211_ht_operation *ht_oper, | ||
3275 | const struct ieee80211_vht_operation *vht_oper, | ||
3276 | struct cfg80211_chan_def *chandef) | ||
3277 | { | ||
3278 | struct cfg80211_chan_def vht_chandef; | ||
3279 | u32 ht_cfreq, ret; | ||
3280 | |||
3281 | chandef->chan = channel; | ||
3282 | chandef->width = NL80211_CHAN_WIDTH_20_NOHT; | ||
3283 | chandef->center_freq1 = channel->center_freq; | ||
3284 | chandef->center_freq2 = 0; | ||
3285 | |||
3286 | if (!ht_oper || !sband->ht_cap.ht_supported) { | ||
3287 | ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; | ||
3288 | goto out; | ||
3289 | } | ||
3290 | |||
3291 | chandef->width = NL80211_CHAN_WIDTH_20; | ||
3292 | |||
3293 | ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan, | ||
3294 | channel->band); | ||
3295 | /* check that channel matches the right operating channel */ | ||
3296 | if (channel->center_freq != ht_cfreq) { | ||
3297 | /* | ||
3298 | * It's possible that some APs are confused here; | ||
3299 | * Netgear WNDR3700 sometimes reports 4 higher than | ||
3300 | * the actual channel in association responses, but | ||
3301 | * since we look at probe response/beacon data here | ||
3302 | * it should be OK. | ||
3303 | */ | ||
3304 | sdata_info(sdata, | ||
3305 | "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", | ||
3306 | channel->center_freq, ht_cfreq, | ||
3307 | ht_oper->primary_chan, channel->band); | ||
3308 | ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; | ||
3309 | goto out; | ||
3310 | } | ||
3311 | |||
3312 | /* check 40 MHz support, if we have it */ | ||
3313 | if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { | ||
3314 | switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { | ||
3315 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: | ||
3316 | chandef->width = NL80211_CHAN_WIDTH_40; | ||
3317 | chandef->center_freq1 += 10; | ||
3318 | break; | ||
3319 | case IEEE80211_HT_PARAM_CHA_SEC_BELOW: | ||
3320 | chandef->width = NL80211_CHAN_WIDTH_40; | ||
3321 | chandef->center_freq1 -= 10; | ||
3322 | break; | ||
3323 | } | ||
3324 | } else { | ||
3325 | /* 40 MHz (and 80 MHz) must be supported for VHT */ | ||
3326 | ret = IEEE80211_STA_DISABLE_VHT; | ||
3327 | goto out; | ||
3328 | } | ||
3329 | |||
3330 | if (!vht_oper || !sband->vht_cap.vht_supported) { | ||
3331 | ret = IEEE80211_STA_DISABLE_VHT; | ||
3332 | goto out; | ||
3333 | } | ||
3334 | |||
3335 | vht_chandef.chan = channel; | ||
3336 | vht_chandef.center_freq1 = | ||
3337 | ieee80211_channel_to_frequency(vht_oper->center_freq_seg1_idx, | ||
3338 | channel->band); | ||
3339 | vht_chandef.center_freq2 = 0; | ||
3340 | |||
3341 | if (vht_oper->center_freq_seg2_idx) | ||
3342 | vht_chandef.center_freq2 = | ||
3343 | ieee80211_channel_to_frequency( | ||
3344 | vht_oper->center_freq_seg2_idx, | ||
3345 | channel->band); | ||
3346 | |||
3347 | switch (vht_oper->chan_width) { | ||
3348 | case IEEE80211_VHT_CHANWIDTH_USE_HT: | ||
3349 | vht_chandef.width = chandef->width; | ||
3350 | break; | ||
3351 | case IEEE80211_VHT_CHANWIDTH_80MHZ: | ||
3352 | vht_chandef.width = NL80211_CHAN_WIDTH_80; | ||
3353 | break; | ||
3354 | case IEEE80211_VHT_CHANWIDTH_160MHZ: | ||
3355 | vht_chandef.width = NL80211_CHAN_WIDTH_160; | ||
3356 | break; | ||
3357 | case IEEE80211_VHT_CHANWIDTH_80P80MHZ: | ||
3358 | vht_chandef.width = NL80211_CHAN_WIDTH_80P80; | ||
3359 | break; | ||
3360 | default: | ||
3361 | sdata_info(sdata, | ||
3362 | "AP VHT operation IE has invalid channel width (%d), disable VHT\n", | ||
3363 | vht_oper->chan_width); | ||
3364 | ret = IEEE80211_STA_DISABLE_VHT; | ||
3365 | goto out; | ||
3366 | } | ||
3367 | |||
3368 | if (!cfg80211_chandef_valid(&vht_chandef)) { | ||
3369 | sdata_info(sdata, | ||
3370 | "AP VHT information is invalid, disable VHT\n"); | ||
3371 | ret = IEEE80211_STA_DISABLE_VHT; | ||
3372 | goto out; | ||
3373 | } | ||
3374 | |||
3375 | if (cfg80211_chandef_identical(chandef, &vht_chandef)) { | ||
3376 | ret = 0; | ||
3377 | goto out; | ||
3378 | } | ||
3379 | |||
3380 | if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) { | ||
3381 | sdata_info(sdata, | ||
3382 | "AP VHT information doesn't match HT, disable VHT\n"); | ||
3383 | ret = IEEE80211_STA_DISABLE_VHT; | ||
3384 | goto out; | ||
3385 | } | ||
3386 | |||
3387 | *chandef = vht_chandef; | ||
3388 | |||
3389 | ret = 0; | ||
3390 | |||
3391 | while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, | ||
3392 | IEEE80211_CHAN_DISABLED)) { | ||
3393 | if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) { | ||
3394 | ret = IEEE80211_STA_DISABLE_HT | | ||
3395 | IEEE80211_STA_DISABLE_VHT; | ||
3396 | goto out; | ||
3397 | } | ||
3398 | |||
3399 | ret = chandef_downgrade(chandef); | ||
3400 | } | ||
3401 | |||
3402 | if (chandef->width != vht_chandef.width) | ||
3403 | sdata_info(sdata, | ||
3404 | "local regulatory prevented using AP HT/VHT configuration, downgraded\n"); | ||
3405 | |||
3406 | out: | ||
3407 | WARN_ON_ONCE(!cfg80211_chandef_valid(chandef)); | ||
3408 | return ret; | ||
3409 | } | ||
3410 | |||
3411 | static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata, | ||
3412 | struct cfg80211_bss *cbss) | ||
3413 | { | ||
3414 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | ||
3415 | const u8 *ht_cap_ie, *vht_cap_ie; | ||
3416 | const struct ieee80211_ht_cap *ht_cap; | ||
3417 | const struct ieee80211_vht_cap *vht_cap; | ||
3418 | u8 chains = 1; | ||
3419 | |||
3420 | if (ifmgd->flags & IEEE80211_STA_DISABLE_HT) | ||
3421 | return chains; | ||
3422 | |||
3423 | ht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_CAPABILITY); | ||
3424 | if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) { | ||
3425 | ht_cap = (void *)(ht_cap_ie + 2); | ||
3426 | chains = ieee80211_mcs_to_chains(&ht_cap->mcs); | ||
3427 | /* | ||
3428 | * TODO: use "Tx Maximum Number Spatial Streams Supported" and | ||
3429 | * "Tx Unequal Modulation Supported" fields. | ||
3430 | */ | ||
3431 | } | ||
3432 | |||
3433 | if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT) | ||
3434 | return chains; | ||
3435 | |||
3436 | vht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_VHT_CAPABILITY); | ||
3437 | if (vht_cap_ie && vht_cap_ie[1] >= sizeof(*vht_cap)) { | ||
3438 | u8 nss; | ||
3439 | u16 tx_mcs_map; | ||
3440 | |||
3441 | vht_cap = (void *)(vht_cap_ie + 2); | ||
3442 | tx_mcs_map = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map); | ||
3443 | for (nss = 8; nss > 0; nss--) { | ||
3444 | if (((tx_mcs_map >> (2 * (nss - 1))) & 3) != | ||
3445 | IEEE80211_VHT_MCS_NOT_SUPPORTED) | ||
3446 | break; | ||
3447 | } | ||
3448 | /* TODO: use "Tx Highest Supported Long GI Data Rate" field? */ | ||
3449 | chains = max(chains, nss); | ||
3450 | } | ||
3451 | |||
3452 | return chains; | ||
3453 | } | ||
3454 | |||
3186 | static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, | 3455 | static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, |
3187 | struct cfg80211_bss *cbss) | 3456 | struct cfg80211_bss *cbss) |
3188 | { | 3457 | { |
3189 | struct ieee80211_local *local = sdata->local; | 3458 | struct ieee80211_local *local = sdata->local; |
3190 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 3459 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
3191 | int ht_cfreq; | ||
3192 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
3193 | const u8 *ht_oper_ie; | ||
3194 | const struct ieee80211_ht_operation *ht_oper = NULL; | 3460 | const struct ieee80211_ht_operation *ht_oper = NULL; |
3461 | const struct ieee80211_vht_operation *vht_oper = NULL; | ||
3195 | struct ieee80211_supported_band *sband; | 3462 | struct ieee80211_supported_band *sband; |
3196 | struct cfg80211_chan_def chandef; | 3463 | struct cfg80211_chan_def chandef; |
3464 | int ret; | ||
3197 | 3465 | ||
3198 | sband = local->hw.wiphy->bands[cbss->channel->band]; | 3466 | sband = local->hw.wiphy->bands[cbss->channel->band]; |
3199 | 3467 | ||
3200 | ifmgd->flags &= ~IEEE80211_STA_DISABLE_40MHZ; | 3468 | ifmgd->flags &= ~(IEEE80211_STA_DISABLE_40MHZ | |
3469 | IEEE80211_STA_DISABLE_80P80MHZ | | ||
3470 | IEEE80211_STA_DISABLE_160MHZ); | ||
3201 | 3471 | ||
3202 | if (sband->ht_cap.ht_supported) { | 3472 | rcu_read_lock(); |
3203 | ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION, | 3473 | |
3204 | cbss->information_elements, | 3474 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) && |
3205 | cbss->len_information_elements); | 3475 | sband->ht_cap.ht_supported) { |
3476 | const u8 *ht_oper_ie; | ||
3477 | |||
3478 | ht_oper_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_OPERATION); | ||
3206 | if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper)) | 3479 | if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper)) |
3207 | ht_oper = (void *)(ht_oper_ie + 2); | 3480 | ht_oper = (void *)(ht_oper_ie + 2); |
3208 | } | 3481 | } |
3209 | 3482 | ||
3210 | if (ht_oper) { | 3483 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) && |
3211 | ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan, | 3484 | sband->vht_cap.vht_supported) { |
3212 | cbss->channel->band); | 3485 | const u8 *vht_oper_ie; |
3213 | /* check that channel matches the right operating channel */ | 3486 | |
3214 | if (cbss->channel->center_freq != ht_cfreq) { | 3487 | vht_oper_ie = ieee80211_bss_get_ie(cbss, |
3215 | /* | 3488 | WLAN_EID_VHT_OPERATION); |
3216 | * It's possible that some APs are confused here; | 3489 | if (vht_oper_ie && vht_oper_ie[1] >= sizeof(*vht_oper)) |
3217 | * Netgear WNDR3700 sometimes reports 4 higher than | 3490 | vht_oper = (void *)(vht_oper_ie + 2); |
3218 | * the actual channel in association responses, but | 3491 | if (vht_oper && !ht_oper) { |
3219 | * since we look at probe response/beacon data here | 3492 | vht_oper = NULL; |
3220 | * it should be OK. | ||
3221 | */ | ||
3222 | sdata_info(sdata, | 3493 | sdata_info(sdata, |
3223 | "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", | 3494 | "AP advertised VHT without HT, disabling both\n"); |
3224 | cbss->channel->center_freq, | 3495 | sdata->flags |= IEEE80211_STA_DISABLE_HT; |
3225 | ht_cfreq, ht_oper->primary_chan, | 3496 | sdata->flags |= IEEE80211_STA_DISABLE_VHT; |
3226 | cbss->channel->band); | ||
3227 | ht_oper = NULL; | ||
3228 | } | 3497 | } |
3229 | } | 3498 | } |
3230 | 3499 | ||
3231 | if (ht_oper) { | 3500 | ifmgd->flags |= ieee80211_determine_chantype(sdata, sband, |
3232 | /* | 3501 | cbss->channel, |
3233 | * cfg80211 already verified that the channel itself can | 3502 | ht_oper, vht_oper, |
3234 | * be used, but it didn't check that we can do the right | 3503 | &chandef); |
3235 | * HT type, so do that here as well. If HT40 isn't allowed | ||
3236 | * on this channel, disable 40 MHz operation. | ||
3237 | */ | ||
3238 | const u8 *ht_cap_ie; | ||
3239 | const struct ieee80211_ht_cap *ht_cap; | ||
3240 | u8 chains = 1; | ||
3241 | |||
3242 | channel_type = NL80211_CHAN_HT20; | ||
3243 | |||
3244 | if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { | ||
3245 | switch (ht_oper->ht_param & | ||
3246 | IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { | ||
3247 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: | ||
3248 | if (cbss->channel->flags & | ||
3249 | IEEE80211_CHAN_NO_HT40PLUS) | ||
3250 | ifmgd->flags |= | ||
3251 | IEEE80211_STA_DISABLE_40MHZ; | ||
3252 | else | ||
3253 | channel_type = NL80211_CHAN_HT40PLUS; | ||
3254 | break; | ||
3255 | case IEEE80211_HT_PARAM_CHA_SEC_BELOW: | ||
3256 | if (cbss->channel->flags & | ||
3257 | IEEE80211_CHAN_NO_HT40MINUS) | ||
3258 | ifmgd->flags |= | ||
3259 | IEEE80211_STA_DISABLE_40MHZ; | ||
3260 | else | ||
3261 | channel_type = NL80211_CHAN_HT40MINUS; | ||
3262 | break; | ||
3263 | } | ||
3264 | } | ||
3265 | 3504 | ||
3266 | ht_cap_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, | 3505 | sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss), |
3267 | cbss->information_elements, | 3506 | local->rx_chains); |
3268 | cbss->len_information_elements); | 3507 | |
3269 | if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) { | 3508 | rcu_read_unlock(); |
3270 | ht_cap = (void *)(ht_cap_ie + 2); | ||
3271 | chains = ieee80211_mcs_to_chains(&ht_cap->mcs); | ||
3272 | } | ||
3273 | sdata->needed_rx_chains = min(chains, local->rx_chains); | ||
3274 | } else { | ||
3275 | sdata->needed_rx_chains = 1; | ||
3276 | sdata->u.mgd.flags |= IEEE80211_STA_DISABLE_HT; | ||
3277 | } | ||
3278 | 3509 | ||
3279 | /* will change later if needed */ | 3510 | /* will change later if needed */ |
3280 | sdata->smps_mode = IEEE80211_SMPS_OFF; | 3511 | sdata->smps_mode = IEEE80211_SMPS_OFF; |
3281 | 3512 | ||
3282 | ieee80211_vif_release_channel(sdata); | 3513 | /* |
3283 | cfg80211_chandef_create(&chandef, cbss->channel, channel_type); | 3514 | * If this fails (possibly due to channel context sharing |
3284 | return ieee80211_vif_use_channel(sdata, &chandef, | 3515 | * on incompatible channels, e.g. 80+80 and 160 sharing the |
3285 | IEEE80211_CHANCTX_SHARED); | 3516 | * same control channel) try to use a smaller bandwidth. |
3517 | */ | ||
3518 | ret = ieee80211_vif_use_channel(sdata, &chandef, | ||
3519 | IEEE80211_CHANCTX_SHARED); | ||
3520 | while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) | ||
3521 | ifmgd->flags |= chandef_downgrade(&chandef); | ||
3522 | return ret; | ||
3286 | } | 3523 | } |
3287 | 3524 | ||
3288 | static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, | 3525 | static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, |
@@ -3510,14 +3747,21 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
3510 | const u8 *ssidie, *ht_ie; | 3747 | const u8 *ssidie, *ht_ie; |
3511 | int i, err; | 3748 | int i, err; |
3512 | 3749 | ||
3513 | ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); | ||
3514 | if (!ssidie) | ||
3515 | return -EINVAL; | ||
3516 | |||
3517 | assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL); | 3750 | assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL); |
3518 | if (!assoc_data) | 3751 | if (!assoc_data) |
3519 | return -ENOMEM; | 3752 | return -ENOMEM; |
3520 | 3753 | ||
3754 | rcu_read_lock(); | ||
3755 | ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); | ||
3756 | if (!ssidie) { | ||
3757 | rcu_read_unlock(); | ||
3758 | kfree(assoc_data); | ||
3759 | return -EINVAL; | ||
3760 | } | ||
3761 | memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]); | ||
3762 | assoc_data->ssid_len = ssidie[1]; | ||
3763 | rcu_read_unlock(); | ||
3764 | |||
3521 | mutex_lock(&ifmgd->mtx); | 3765 | mutex_lock(&ifmgd->mtx); |
3522 | 3766 | ||
3523 | if (ifmgd->associated) | 3767 | if (ifmgd->associated) |
@@ -3612,12 +3856,14 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
3612 | assoc_data->supp_rates = bss->supp_rates; | 3856 | assoc_data->supp_rates = bss->supp_rates; |
3613 | assoc_data->supp_rates_len = bss->supp_rates_len; | 3857 | assoc_data->supp_rates_len = bss->supp_rates_len; |
3614 | 3858 | ||
3859 | rcu_read_lock(); | ||
3615 | ht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION); | 3860 | ht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION); |
3616 | if (ht_ie && ht_ie[1] >= sizeof(struct ieee80211_ht_operation)) | 3861 | if (ht_ie && ht_ie[1] >= sizeof(struct ieee80211_ht_operation)) |
3617 | assoc_data->ap_ht_param = | 3862 | assoc_data->ap_ht_param = |
3618 | ((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param; | 3863 | ((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param; |
3619 | else | 3864 | else |
3620 | ifmgd->flags |= IEEE80211_STA_DISABLE_HT; | 3865 | ifmgd->flags |= IEEE80211_STA_DISABLE_HT; |
3866 | rcu_read_unlock(); | ||
3621 | 3867 | ||
3622 | if (bss->wmm_used && bss->uapsd_supported && | 3868 | if (bss->wmm_used && bss->uapsd_supported && |
3623 | (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) { | 3869 | (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) { |
@@ -3628,9 +3874,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
3628 | ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED; | 3874 | ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED; |
3629 | } | 3875 | } |
3630 | 3876 | ||
3631 | memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]); | ||
3632 | assoc_data->ssid_len = ssidie[1]; | ||
3633 | |||
3634 | if (req->prev_bssid) | 3877 | if (req->prev_bssid) |
3635 | memcpy(assoc_data->prev_bssid, req->prev_bssid, ETH_ALEN); | 3878 | memcpy(assoc_data->prev_bssid, req->prev_bssid, ETH_ALEN); |
3636 | 3879 | ||
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index fb1d4aa65e8..9f9c453bc45 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c | |||
@@ -389,9 +389,9 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, | |||
389 | struct ieee80211_tx_rate *ar = info->status.rates; | 389 | struct ieee80211_tx_rate *ar = info->status.rates; |
390 | struct minstrel_rate_stats *rate, *rate2; | 390 | struct minstrel_rate_stats *rate, *rate2; |
391 | struct minstrel_priv *mp = priv; | 391 | struct minstrel_priv *mp = priv; |
392 | bool last = false; | 392 | bool last; |
393 | int group; | 393 | int group; |
394 | int i = 0; | 394 | int i; |
395 | 395 | ||
396 | if (!msp->is_ht) | 396 | if (!msp->is_ht) |
397 | return mac80211_minstrel.tx_status(priv, sband, sta, &msp->legacy, skb); | 397 | return mac80211_minstrel.tx_status(priv, sband, sta, &msp->legacy, skb); |
@@ -419,13 +419,11 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, | |||
419 | if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) | 419 | if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) |
420 | mi->sample_packets += info->status.ampdu_len; | 420 | mi->sample_packets += info->status.ampdu_len; |
421 | 421 | ||
422 | last = !minstrel_ht_txstat_valid(&ar[0]); | ||
422 | for (i = 0; !last; i++) { | 423 | for (i = 0; !last; i++) { |
423 | last = (i == IEEE80211_TX_MAX_RATES - 1) || | 424 | last = (i == IEEE80211_TX_MAX_RATES - 1) || |
424 | !minstrel_ht_txstat_valid(&ar[i + 1]); | 425 | !minstrel_ht_txstat_valid(&ar[i + 1]); |
425 | 426 | ||
426 | if (!minstrel_ht_txstat_valid(&ar[i])) | ||
427 | break; | ||
428 | |||
429 | group = minstrel_ht_get_group_idx(&ar[i]); | 427 | group = minstrel_ht_get_group_idx(&ar[i]); |
430 | rate = &mi->groups[group].rates[ar[i].idx % 8]; | 428 | rate = &mi->groups[group].rates[ar[i].idx % 8]; |
431 | 429 | ||
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 825f33cf7bb..db343fa8033 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c | |||
@@ -49,7 +49,7 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local, | |||
49 | /* driver bug */ | 49 | /* driver bug */ |
50 | WARN_ON(1); | 50 | WARN_ON(1); |
51 | dev_kfree_skb(skb); | 51 | dev_kfree_skb(skb); |
52 | skb = NULL; | 52 | return NULL; |
53 | } | 53 | } |
54 | } | 54 | } |
55 | 55 | ||
@@ -111,6 +111,11 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local, | |||
111 | len += 8; | 111 | len += 8; |
112 | } | 112 | } |
113 | 113 | ||
114 | if (status->flag & RX_FLAG_VHT) { | ||
115 | len = ALIGN(len, 2); | ||
116 | len += 12; | ||
117 | } | ||
118 | |||
114 | if (status->vendor_radiotap_len) { | 119 | if (status->vendor_radiotap_len) { |
115 | if (WARN_ON_ONCE(status->vendor_radiotap_align == 0)) | 120 | if (WARN_ON_ONCE(status->vendor_radiotap_align == 0)) |
116 | status->vendor_radiotap_align = 1; | 121 | status->vendor_radiotap_align = 1; |
@@ -297,6 +302,41 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, | |||
297 | *pos++ = 0; | 302 | *pos++ = 0; |
298 | } | 303 | } |
299 | 304 | ||
305 | if (status->flag & RX_FLAG_VHT) { | ||
306 | u16 known = local->hw.radiotap_vht_details; | ||
307 | |||
308 | rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT); | ||
309 | /* known field - how to handle 80+80? */ | ||
310 | if (status->flag & RX_FLAG_80P80MHZ) | ||
311 | known &= ~IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH; | ||
312 | put_unaligned_le16(known, pos); | ||
313 | pos += 2; | ||
314 | /* flags */ | ||
315 | if (status->flag & RX_FLAG_SHORT_GI) | ||
316 | *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI; | ||
317 | pos++; | ||
318 | /* bandwidth */ | ||
319 | if (status->flag & RX_FLAG_80MHZ) | ||
320 | *pos++ = 4; | ||
321 | else if (status->flag & RX_FLAG_80P80MHZ) | ||
322 | *pos++ = 0; /* marked not known above */ | ||
323 | else if (status->flag & RX_FLAG_160MHZ) | ||
324 | *pos++ = 11; | ||
325 | else if (status->flag & RX_FLAG_40MHZ) | ||
326 | *pos++ = 1; | ||
327 | else /* 20 MHz */ | ||
328 | *pos++ = 0; | ||
329 | /* MCS/NSS */ | ||
330 | *pos = (status->rate_idx << 4) | status->vht_nss; | ||
331 | pos += 4; | ||
332 | /* coding field */ | ||
333 | pos++; | ||
334 | /* group ID */ | ||
335 | pos++; | ||
336 | /* partial_aid */ | ||
337 | pos += 2; | ||
338 | } | ||
339 | |||
300 | if (status->vendor_radiotap_len) { | 340 | if (status->vendor_radiotap_len) { |
301 | /* ensure 2 byte alignment for the vendor field as required */ | 341 | /* ensure 2 byte alignment for the vendor field as required */ |
302 | if ((pos - (u8 *)rthdr) & 1) | 342 | if ((pos - (u8 *)rthdr) & 1) |
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index f3340279aba..f7176ac5a53 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -247,6 +247,7 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) | |||
247 | local->hw_scan_req->n_channels = n_chans; | 247 | local->hw_scan_req->n_channels = n_chans; |
248 | 248 | ||
249 | ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie, | 249 | ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie, |
250 | local->hw_scan_ies_bufsize, | ||
250 | req->ie, req->ie_len, band, | 251 | req->ie, req->ie_len, band, |
251 | req->rates[band], 0); | 252 | req->rates[band], 0); |
252 | local->hw_scan_req->ie_len = ielen; | 253 | local->hw_scan_req->ie_len = ielen; |
@@ -445,11 +446,13 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, | |||
445 | if (local->ops->hw_scan) { | 446 | if (local->ops->hw_scan) { |
446 | u8 *ies; | 447 | u8 *ies; |
447 | 448 | ||
449 | local->hw_scan_ies_bufsize = 2 + IEEE80211_MAX_SSID_LEN + | ||
450 | local->scan_ies_len + | ||
451 | req->ie_len; | ||
448 | local->hw_scan_req = kmalloc( | 452 | local->hw_scan_req = kmalloc( |
449 | sizeof(*local->hw_scan_req) + | 453 | sizeof(*local->hw_scan_req) + |
450 | req->n_channels * sizeof(req->channels[0]) + | 454 | req->n_channels * sizeof(req->channels[0]) + |
451 | 2 + IEEE80211_MAX_SSID_LEN + local->scan_ies_len + | 455 | local->hw_scan_ies_bufsize, GFP_KERNEL); |
452 | req->ie_len, GFP_KERNEL); | ||
453 | if (!local->hw_scan_req) | 456 | if (!local->hw_scan_req) |
454 | return -ENOMEM; | 457 | return -ENOMEM; |
455 | 458 | ||
@@ -928,7 +931,10 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, | |||
928 | { | 931 | { |
929 | struct ieee80211_local *local = sdata->local; | 932 | struct ieee80211_local *local = sdata->local; |
930 | struct ieee80211_sched_scan_ies sched_scan_ies = {}; | 933 | struct ieee80211_sched_scan_ies sched_scan_ies = {}; |
931 | int ret, i; | 934 | int ret, i, iebufsz; |
935 | |||
936 | iebufsz = 2 + IEEE80211_MAX_SSID_LEN + | ||
937 | local->scan_ies_len + req->ie_len; | ||
932 | 938 | ||
933 | mutex_lock(&local->mtx); | 939 | mutex_lock(&local->mtx); |
934 | 940 | ||
@@ -946,10 +952,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, | |||
946 | if (!local->hw.wiphy->bands[i]) | 952 | if (!local->hw.wiphy->bands[i]) |
947 | continue; | 953 | continue; |
948 | 954 | ||
949 | sched_scan_ies.ie[i] = kzalloc(2 + IEEE80211_MAX_SSID_LEN + | 955 | sched_scan_ies.ie[i] = kzalloc(iebufsz, GFP_KERNEL); |
950 | local->scan_ies_len + | ||
951 | req->ie_len, | ||
952 | GFP_KERNEL); | ||
953 | if (!sched_scan_ies.ie[i]) { | 956 | if (!sched_scan_ies.ie[i]) { |
954 | ret = -ENOMEM; | 957 | ret = -ENOMEM; |
955 | goto out_free; | 958 | goto out_free; |
@@ -957,8 +960,8 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, | |||
957 | 960 | ||
958 | sched_scan_ies.len[i] = | 961 | sched_scan_ies.len[i] = |
959 | ieee80211_build_preq_ies(local, sched_scan_ies.ie[i], | 962 | ieee80211_build_preq_ies(local, sched_scan_ies.ie[i], |
960 | req->ie, req->ie_len, i, | 963 | iebufsz, req->ie, req->ie_len, |
961 | (u32) -1, 0); | 964 | i, (u32) -1, 0); |
962 | } | 965 | } |
963 | 966 | ||
964 | ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies); | 967 | ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies); |
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 6835cea4e40..1489bca9ea9 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h | |||
@@ -250,6 +250,7 @@ struct sta_ampdu_mlme { | |||
250 | * @rx_dropped: number of dropped MPDUs from this STA | 250 | * @rx_dropped: number of dropped MPDUs from this STA |
251 | * @last_signal: signal of last received frame from this STA | 251 | * @last_signal: signal of last received frame from this STA |
252 | * @avg_signal: moving average of signal of received frames from this STA | 252 | * @avg_signal: moving average of signal of received frames from this STA |
253 | * @last_ack_signal: signal of last received Ack frame from this STA | ||
253 | * @last_seq_ctrl: last received seq/frag number from this STA (per RX queue) | 254 | * @last_seq_ctrl: last received seq/frag number from this STA (per RX queue) |
254 | * @tx_filtered_count: number of frames the hardware filtered for this STA | 255 | * @tx_filtered_count: number of frames the hardware filtered for this STA |
255 | * @tx_retry_failed: number of frames that failed retry | 256 | * @tx_retry_failed: number of frames that failed retry |
@@ -329,6 +330,7 @@ struct sta_info { | |||
329 | unsigned long rx_dropped; | 330 | unsigned long rx_dropped; |
330 | int last_signal; | 331 | int last_signal; |
331 | struct ewma avg_signal; | 332 | struct ewma avg_signal; |
333 | int last_ack_signal; | ||
332 | /* Plus 1 for non-QoS frames */ | 334 | /* Plus 1 for non-QoS frames */ |
333 | __le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1]; | 335 | __le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1]; |
334 | 336 | ||
@@ -552,6 +554,8 @@ int sta_info_flush(struct ieee80211_local *local, | |||
552 | void sta_set_rate_info_tx(struct sta_info *sta, | 554 | void sta_set_rate_info_tx(struct sta_info *sta, |
553 | const struct ieee80211_tx_rate *rate, | 555 | const struct ieee80211_tx_rate *rate, |
554 | struct rate_info *rinfo); | 556 | struct rate_info *rinfo); |
557 | void sta_set_rate_info_rx(struct sta_info *sta, | ||
558 | struct rate_info *rinfo); | ||
555 | void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, | 559 | void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, |
556 | unsigned long exp_time); | 560 | unsigned long exp_time); |
557 | 561 | ||
diff --git a/net/mac80211/status.c b/net/mac80211/status.c index ab63237107c..ab50285fcba 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c | |||
@@ -539,6 +539,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
539 | sta->lost_packets = 0; | 539 | sta->lost_packets = 0; |
540 | } | 540 | } |
541 | } | 541 | } |
542 | |||
543 | if (acked) | ||
544 | sta->last_ack_signal = info->status.ack_signal; | ||
542 | } | 545 | } |
543 | 546 | ||
544 | rcu_read_unlock(); | 547 | rcu_read_unlock(); |
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index d287a4f2c01..e9eadc40c09 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -2623,7 +2623,7 @@ EXPORT_SYMBOL(ieee80211_nullfunc_get); | |||
2623 | struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, | 2623 | struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, |
2624 | struct ieee80211_vif *vif, | 2624 | struct ieee80211_vif *vif, |
2625 | const u8 *ssid, size_t ssid_len, | 2625 | const u8 *ssid, size_t ssid_len, |
2626 | const u8 *ie, size_t ie_len) | 2626 | size_t tailroom) |
2627 | { | 2627 | { |
2628 | struct ieee80211_sub_if_data *sdata; | 2628 | struct ieee80211_sub_if_data *sdata; |
2629 | struct ieee80211_local *local; | 2629 | struct ieee80211_local *local; |
@@ -2637,7 +2637,7 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, | |||
2637 | ie_ssid_len = 2 + ssid_len; | 2637 | ie_ssid_len = 2 + ssid_len; |
2638 | 2638 | ||
2639 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*hdr) + | 2639 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*hdr) + |
2640 | ie_ssid_len + ie_len); | 2640 | ie_ssid_len + tailroom); |
2641 | if (!skb) | 2641 | if (!skb) |
2642 | return NULL; | 2642 | return NULL; |
2643 | 2643 | ||
@@ -2658,11 +2658,6 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, | |||
2658 | memcpy(pos, ssid, ssid_len); | 2658 | memcpy(pos, ssid, ssid_len); |
2659 | pos += ssid_len; | 2659 | pos += ssid_len; |
2660 | 2660 | ||
2661 | if (ie) { | ||
2662 | pos = skb_put(skb, ie_len); | ||
2663 | memcpy(pos, ie, ie_len); | ||
2664 | } | ||
2665 | |||
2666 | return skb; | 2661 | return skb; |
2667 | } | 2662 | } |
2668 | EXPORT_SYMBOL(ieee80211_probereq_get); | 2663 | EXPORT_SYMBOL(ieee80211_probereq_get); |
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 08132ff9815..f11e8c540db 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -1107,12 +1107,12 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, | |||
1107 | } | 1107 | } |
1108 | 1108 | ||
1109 | int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, | 1109 | int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, |
1110 | const u8 *ie, size_t ie_len, | 1110 | size_t buffer_len, const u8 *ie, size_t ie_len, |
1111 | enum ieee80211_band band, u32 rate_mask, | 1111 | enum ieee80211_band band, u32 rate_mask, |
1112 | u8 channel) | 1112 | u8 channel) |
1113 | { | 1113 | { |
1114 | struct ieee80211_supported_band *sband; | 1114 | struct ieee80211_supported_band *sband; |
1115 | u8 *pos; | 1115 | u8 *pos = buffer, *end = buffer + buffer_len; |
1116 | size_t offset = 0, noffset; | 1116 | size_t offset = 0, noffset; |
1117 | int supp_rates_len, i; | 1117 | int supp_rates_len, i; |
1118 | u8 rates[32]; | 1118 | u8 rates[32]; |
@@ -1123,8 +1123,6 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, | |||
1123 | if (WARN_ON_ONCE(!sband)) | 1123 | if (WARN_ON_ONCE(!sband)) |
1124 | return 0; | 1124 | return 0; |
1125 | 1125 | ||
1126 | pos = buffer; | ||
1127 | |||
1128 | num_rates = 0; | 1126 | num_rates = 0; |
1129 | for (i = 0; i < sband->n_bitrates; i++) { | 1127 | for (i = 0; i < sband->n_bitrates; i++) { |
1130 | if ((BIT(i) & rate_mask) == 0) | 1128 | if ((BIT(i) & rate_mask) == 0) |
@@ -1134,6 +1132,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, | |||
1134 | 1132 | ||
1135 | supp_rates_len = min_t(int, num_rates, 8); | 1133 | supp_rates_len = min_t(int, num_rates, 8); |
1136 | 1134 | ||
1135 | if (end - pos < 2 + supp_rates_len) | ||
1136 | goto out_err; | ||
1137 | *pos++ = WLAN_EID_SUPP_RATES; | 1137 | *pos++ = WLAN_EID_SUPP_RATES; |
1138 | *pos++ = supp_rates_len; | 1138 | *pos++ = supp_rates_len; |
1139 | memcpy(pos, rates, supp_rates_len); | 1139 | memcpy(pos, rates, supp_rates_len); |
@@ -1150,6 +1150,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, | |||
1150 | before_extrates, | 1150 | before_extrates, |
1151 | ARRAY_SIZE(before_extrates), | 1151 | ARRAY_SIZE(before_extrates), |
1152 | offset); | 1152 | offset); |
1153 | if (end - pos < noffset - offset) | ||
1154 | goto out_err; | ||
1153 | memcpy(pos, ie + offset, noffset - offset); | 1155 | memcpy(pos, ie + offset, noffset - offset); |
1154 | pos += noffset - offset; | 1156 | pos += noffset - offset; |
1155 | offset = noffset; | 1157 | offset = noffset; |
@@ -1157,6 +1159,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, | |||
1157 | 1159 | ||
1158 | ext_rates_len = num_rates - supp_rates_len; | 1160 | ext_rates_len = num_rates - supp_rates_len; |
1159 | if (ext_rates_len > 0) { | 1161 | if (ext_rates_len > 0) { |
1162 | if (end - pos < 2 + ext_rates_len) | ||
1163 | goto out_err; | ||
1160 | *pos++ = WLAN_EID_EXT_SUPP_RATES; | 1164 | *pos++ = WLAN_EID_EXT_SUPP_RATES; |
1161 | *pos++ = ext_rates_len; | 1165 | *pos++ = ext_rates_len; |
1162 | memcpy(pos, rates + supp_rates_len, ext_rates_len); | 1166 | memcpy(pos, rates + supp_rates_len, ext_rates_len); |
@@ -1164,6 +1168,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, | |||
1164 | } | 1168 | } |
1165 | 1169 | ||
1166 | if (channel && sband->band == IEEE80211_BAND_2GHZ) { | 1170 | if (channel && sband->band == IEEE80211_BAND_2GHZ) { |
1171 | if (end - pos < 3) | ||
1172 | goto out_err; | ||
1167 | *pos++ = WLAN_EID_DS_PARAMS; | 1173 | *pos++ = WLAN_EID_DS_PARAMS; |
1168 | *pos++ = 1; | 1174 | *pos++ = 1; |
1169 | *pos++ = channel; | 1175 | *pos++ = channel; |
@@ -1182,14 +1188,19 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, | |||
1182 | noffset = ieee80211_ie_split(ie, ie_len, | 1188 | noffset = ieee80211_ie_split(ie, ie_len, |
1183 | before_ht, ARRAY_SIZE(before_ht), | 1189 | before_ht, ARRAY_SIZE(before_ht), |
1184 | offset); | 1190 | offset); |
1191 | if (end - pos < noffset - offset) | ||
1192 | goto out_err; | ||
1185 | memcpy(pos, ie + offset, noffset - offset); | 1193 | memcpy(pos, ie + offset, noffset - offset); |
1186 | pos += noffset - offset; | 1194 | pos += noffset - offset; |
1187 | offset = noffset; | 1195 | offset = noffset; |
1188 | } | 1196 | } |
1189 | 1197 | ||
1190 | if (sband->ht_cap.ht_supported) | 1198 | if (sband->ht_cap.ht_supported) { |
1199 | if (end - pos < 2 + sizeof(struct ieee80211_ht_cap)) | ||
1200 | goto out_err; | ||
1191 | pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, | 1201 | pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, |
1192 | sband->ht_cap.cap); | 1202 | sband->ht_cap.cap); |
1203 | } | ||
1193 | 1204 | ||
1194 | /* | 1205 | /* |
1195 | * If adding more here, adjust code in main.c | 1206 | * If adding more here, adjust code in main.c |
@@ -1199,15 +1210,23 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, | |||
1199 | /* add any remaining custom IEs */ | 1210 | /* add any remaining custom IEs */ |
1200 | if (ie && ie_len) { | 1211 | if (ie && ie_len) { |
1201 | noffset = ie_len; | 1212 | noffset = ie_len; |
1213 | if (end - pos < noffset - offset) | ||
1214 | goto out_err; | ||
1202 | memcpy(pos, ie + offset, noffset - offset); | 1215 | memcpy(pos, ie + offset, noffset - offset); |
1203 | pos += noffset - offset; | 1216 | pos += noffset - offset; |
1204 | } | 1217 | } |
1205 | 1218 | ||
1206 | if (sband->vht_cap.vht_supported) | 1219 | if (sband->vht_cap.vht_supported) { |
1220 | if (end - pos < 2 + sizeof(struct ieee80211_vht_cap)) | ||
1221 | goto out_err; | ||
1207 | pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap, | 1222 | pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap, |
1208 | sband->vht_cap.cap); | 1223 | sband->vht_cap.cap); |
1224 | } | ||
1209 | 1225 | ||
1210 | return pos - buffer; | 1226 | return pos - buffer; |
1227 | out_err: | ||
1228 | WARN_ONCE(1, "not enough space for preq IEs\n"); | ||
1229 | return pos - buffer; | ||
1211 | } | 1230 | } |
1212 | 1231 | ||
1213 | struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, | 1232 | struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, |
@@ -1220,14 +1239,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, | |||
1220 | struct ieee80211_local *local = sdata->local; | 1239 | struct ieee80211_local *local = sdata->local; |
1221 | struct sk_buff *skb; | 1240 | struct sk_buff *skb; |
1222 | struct ieee80211_mgmt *mgmt; | 1241 | struct ieee80211_mgmt *mgmt; |
1223 | size_t buf_len; | ||
1224 | u8 *buf; | ||
1225 | u8 chan_no; | 1242 | u8 chan_no; |
1226 | 1243 | int ies_len; | |
1227 | /* FIXME: come up with a proper value */ | ||
1228 | buf = kmalloc(200 + ie_len, GFP_KERNEL); | ||
1229 | if (!buf) | ||
1230 | return NULL; | ||
1231 | 1244 | ||
1232 | /* | 1245 | /* |
1233 | * Do not send DS Channel parameter for directed probe requests | 1246 | * Do not send DS Channel parameter for directed probe requests |
@@ -1239,14 +1252,16 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, | |||
1239 | else | 1252 | else |
1240 | chan_no = ieee80211_frequency_to_channel(chan->center_freq); | 1253 | chan_no = ieee80211_frequency_to_channel(chan->center_freq); |
1241 | 1254 | ||
1242 | buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len, chan->band, | ||
1243 | ratemask, chan_no); | ||
1244 | |||
1245 | skb = ieee80211_probereq_get(&local->hw, &sdata->vif, | 1255 | skb = ieee80211_probereq_get(&local->hw, &sdata->vif, |
1246 | ssid, ssid_len, | 1256 | ssid, ssid_len, 100 + ie_len); |
1247 | buf, buf_len); | ||
1248 | if (!skb) | 1257 | if (!skb) |
1249 | goto out; | 1258 | return NULL; |
1259 | |||
1260 | ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb), | ||
1261 | skb_tailroom(skb), | ||
1262 | ie, ie_len, chan->band, | ||
1263 | ratemask, chan_no); | ||
1264 | skb_put(skb, ies_len); | ||
1250 | 1265 | ||
1251 | if (dst) { | 1266 | if (dst) { |
1252 | mgmt = (struct ieee80211_mgmt *) skb->data; | 1267 | mgmt = (struct ieee80211_mgmt *) skb->data; |
@@ -1256,9 +1271,6 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, | |||
1256 | 1271 | ||
1257 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; | 1272 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; |
1258 | 1273 | ||
1259 | out: | ||
1260 | kfree(buf); | ||
1261 | |||
1262 | return skb; | 1274 | return skb; |
1263 | } | 1275 | } |
1264 | 1276 | ||
@@ -1527,7 +1539,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
1527 | changed |= BSS_CHANGED_IBSS; | 1539 | changed |= BSS_CHANGED_IBSS; |
1528 | /* fall through */ | 1540 | /* fall through */ |
1529 | case NL80211_IFTYPE_AP: | 1541 | case NL80211_IFTYPE_AP: |
1530 | changed |= BSS_CHANGED_SSID; | 1542 | changed |= BSS_CHANGED_SSID | BSS_CHANGED_P2P_PS; |
1531 | 1543 | ||
1532 | if (sdata->vif.type == NL80211_IFTYPE_AP) { | 1544 | if (sdata->vif.type == NL80211_IFTYPE_AP) { |
1533 | changed |= BSS_CHANGED_AP_PROBE_RESP; | 1545 | changed |= BSS_CHANGED_AP_PROBE_RESP; |
diff --git a/net/wireless/chan.c b/net/wireless/chan.c index bf2dfd54ff3..b5f69831e31 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c | |||
@@ -44,7 +44,7 @@ void cfg80211_chandef_create(struct cfg80211_chan_def *chandef, | |||
44 | } | 44 | } |
45 | EXPORT_SYMBOL(cfg80211_chandef_create); | 45 | EXPORT_SYMBOL(cfg80211_chandef_create); |
46 | 46 | ||
47 | bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef) | 47 | bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) |
48 | { | 48 | { |
49 | u32 control_freq; | 49 | u32 control_freq; |
50 | 50 | ||
@@ -105,6 +105,7 @@ bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef) | |||
105 | 105 | ||
106 | return true; | 106 | return true; |
107 | } | 107 | } |
108 | EXPORT_SYMBOL(cfg80211_chandef_valid); | ||
108 | 109 | ||
109 | static void chandef_primary_freqs(const struct cfg80211_chan_def *c, | 110 | static void chandef_primary_freqs(const struct cfg80211_chan_def *c, |
110 | int *pri40, int *pri80) | 111 | int *pri40, int *pri80) |
@@ -187,9 +188,9 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, | |||
187 | } | 188 | } |
188 | EXPORT_SYMBOL(cfg80211_chandef_compatible); | 189 | EXPORT_SYMBOL(cfg80211_chandef_compatible); |
189 | 190 | ||
190 | bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, | 191 | static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, |
191 | u32 center_freq, u32 bandwidth, | 192 | u32 center_freq, u32 bandwidth, |
192 | u32 prohibited_flags) | 193 | u32 prohibited_flags) |
193 | { | 194 | { |
194 | struct ieee80211_channel *c; | 195 | struct ieee80211_channel *c; |
195 | u32 freq; | 196 | u32 freq; |
@@ -205,55 +206,88 @@ bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, | |||
205 | return true; | 206 | return true; |
206 | } | 207 | } |
207 | 208 | ||
208 | static bool cfg80211_check_beacon_chans(struct wiphy *wiphy, | 209 | bool cfg80211_chandef_usable(struct wiphy *wiphy, |
209 | u32 center_freq, u32 bw) | 210 | const struct cfg80211_chan_def *chandef, |
211 | u32 prohibited_flags) | ||
210 | { | 212 | { |
211 | return cfg80211_secondary_chans_ok(wiphy, center_freq, bw, | 213 | struct ieee80211_sta_ht_cap *ht_cap; |
212 | IEEE80211_CHAN_DISABLED | | 214 | struct ieee80211_sta_vht_cap *vht_cap; |
213 | IEEE80211_CHAN_PASSIVE_SCAN | | 215 | u32 width, control_freq; |
214 | IEEE80211_CHAN_NO_IBSS | | ||
215 | IEEE80211_CHAN_RADAR); | ||
216 | } | ||
217 | 216 | ||
218 | bool cfg80211_reg_can_beacon(struct wiphy *wiphy, | 217 | if (WARN_ON(!cfg80211_chandef_valid(chandef))) |
219 | struct cfg80211_chan_def *chandef) | 218 | return false; |
220 | { | ||
221 | u32 width; | ||
222 | bool res; | ||
223 | 219 | ||
224 | trace_cfg80211_reg_can_beacon(wiphy, chandef); | 220 | ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap; |
221 | vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap; | ||
225 | 222 | ||
226 | if (WARN_ON(!cfg80211_chan_def_valid(chandef))) { | 223 | control_freq = chandef->chan->center_freq; |
227 | trace_cfg80211_return_bool(false); | ||
228 | return false; | ||
229 | } | ||
230 | 224 | ||
231 | switch (chandef->width) { | 225 | switch (chandef->width) { |
232 | case NL80211_CHAN_WIDTH_20_NOHT: | ||
233 | case NL80211_CHAN_WIDTH_20: | 226 | case NL80211_CHAN_WIDTH_20: |
227 | if (!ht_cap->ht_supported) | ||
228 | return false; | ||
229 | case NL80211_CHAN_WIDTH_20_NOHT: | ||
234 | width = 20; | 230 | width = 20; |
235 | break; | 231 | break; |
236 | case NL80211_CHAN_WIDTH_40: | 232 | case NL80211_CHAN_WIDTH_40: |
237 | width = 40; | 233 | width = 40; |
234 | if (!ht_cap->ht_supported) | ||
235 | return false; | ||
236 | if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || | ||
237 | ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) | ||
238 | return false; | ||
239 | if (chandef->center_freq1 < control_freq && | ||
240 | chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS) | ||
241 | return false; | ||
242 | if (chandef->center_freq1 > control_freq && | ||
243 | chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS) | ||
244 | return false; | ||
238 | break; | 245 | break; |
239 | case NL80211_CHAN_WIDTH_80: | ||
240 | case NL80211_CHAN_WIDTH_80P80: | 246 | case NL80211_CHAN_WIDTH_80P80: |
247 | if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) | ||
248 | return false; | ||
249 | case NL80211_CHAN_WIDTH_80: | ||
250 | if (!vht_cap->vht_supported) | ||
251 | return false; | ||
241 | width = 80; | 252 | width = 80; |
242 | break; | 253 | break; |
243 | case NL80211_CHAN_WIDTH_160: | 254 | case NL80211_CHAN_WIDTH_160: |
255 | if (!vht_cap->vht_supported) | ||
256 | return false; | ||
257 | if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)) | ||
258 | return false; | ||
244 | width = 160; | 259 | width = 160; |
245 | break; | 260 | break; |
246 | default: | 261 | default: |
247 | WARN_ON_ONCE(1); | 262 | WARN_ON_ONCE(1); |
248 | trace_cfg80211_return_bool(false); | ||
249 | return false; | 263 | return false; |
250 | } | 264 | } |
251 | 265 | ||
252 | res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq1, width); | 266 | /* TODO: missing regulatory check on 80/160 bandwidth */ |
267 | |||
268 | if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1, | ||
269 | width, prohibited_flags)) | ||
270 | return false; | ||
271 | |||
272 | if (!chandef->center_freq2) | ||
273 | return true; | ||
274 | return cfg80211_secondary_chans_ok(wiphy, chandef->center_freq2, | ||
275 | width, prohibited_flags); | ||
276 | } | ||
277 | EXPORT_SYMBOL(cfg80211_chandef_usable); | ||
278 | |||
279 | bool cfg80211_reg_can_beacon(struct wiphy *wiphy, | ||
280 | struct cfg80211_chan_def *chandef) | ||
281 | { | ||
282 | bool res; | ||
283 | |||
284 | trace_cfg80211_reg_can_beacon(wiphy, chandef); | ||
253 | 285 | ||
254 | if (res && chandef->center_freq2) | 286 | res = cfg80211_chandef_usable(wiphy, chandef, |
255 | res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq2, | 287 | IEEE80211_CHAN_DISABLED | |
256 | width); | 288 | IEEE80211_CHAN_PASSIVE_SCAN | |
289 | IEEE80211_CHAN_NO_IBSS | | ||
290 | IEEE80211_CHAN_RADAR); | ||
257 | 291 | ||
258 | trace_cfg80211_return_bool(res); | 292 | trace_cfg80211_return_bool(res); |
259 | return res; | 293 | return res; |
diff --git a/net/wireless/core.h b/net/wireless/core.h index a0c8decf6a4..3563097169c 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -138,8 +138,6 @@ struct cfg80211_internal_bss { | |||
138 | unsigned long ts; | 138 | unsigned long ts; |
139 | struct kref ref; | 139 | struct kref ref; |
140 | atomic_t hold; | 140 | atomic_t hold; |
141 | bool beacon_ies_allocated; | ||
142 | bool proberesp_ies_allocated; | ||
143 | 141 | ||
144 | /* must be last because of priv member */ | 142 | /* must be last because of priv member */ |
145 | struct cfg80211_bss pub; | 143 | struct cfg80211_bss pub; |
@@ -483,12 +481,6 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, | |||
483 | void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, | 481 | void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, |
484 | enum nl80211_iftype iftype, int num); | 482 | enum nl80211_iftype iftype, int num); |
485 | 483 | ||
486 | bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef); | ||
487 | |||
488 | bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, | ||
489 | u32 center_freq, u32 bandwidth, | ||
490 | u32 prohibited_flags); | ||
491 | |||
492 | #define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10 | 484 | #define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10 |
493 | 485 | ||
494 | #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS | 486 | #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS |
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 3ee5a728228..f9d6ce5cfab 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c | |||
@@ -146,7 +146,8 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, | |||
146 | if (!setup->chandef.chan) | 146 | if (!setup->chandef.chan) |
147 | return -EINVAL; | 147 | return -EINVAL; |
148 | 148 | ||
149 | setup->chandef.width = NL80211_CHAN_WIDTH_20_NOHT;; | 149 | setup->chandef.width = NL80211_CHAN_WIDTH_20_NOHT; |
150 | setup->chandef.center_freq1 = setup->chandef.chan->center_freq; | ||
150 | } | 151 | } |
151 | 152 | ||
152 | if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef)) | 153 | if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef)) |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d038fa45ecd..f45706adaf3 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -363,6 +363,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { | |||
363 | [NL80211_ATTR_SAE_DATA] = { .type = NLA_BINARY, }, | 363 | [NL80211_ATTR_SAE_DATA] = { .type = NLA_BINARY, }, |
364 | [NL80211_ATTR_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN }, | 364 | [NL80211_ATTR_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN }, |
365 | [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 }, | 365 | [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 }, |
366 | [NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 }, | ||
367 | [NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 }, | ||
366 | }; | 368 | }; |
367 | 369 | ||
368 | /* policy for the key attributes */ | 370 | /* policy for the key attributes */ |
@@ -1369,9 +1371,7 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, | |||
1369 | struct genl_info *info, | 1371 | struct genl_info *info, |
1370 | struct cfg80211_chan_def *chandef) | 1372 | struct cfg80211_chan_def *chandef) |
1371 | { | 1373 | { |
1372 | struct ieee80211_sta_ht_cap *ht_cap; | 1374 | u32 control_freq; |
1373 | struct ieee80211_sta_vht_cap *vht_cap; | ||
1374 | u32 control_freq, width; | ||
1375 | 1375 | ||
1376 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) | 1376 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) |
1377 | return -EINVAL; | 1377 | return -EINVAL; |
@@ -1417,67 +1417,13 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, | |||
1417 | info->attrs[NL80211_ATTR_CENTER_FREQ2]); | 1417 | info->attrs[NL80211_ATTR_CENTER_FREQ2]); |
1418 | } | 1418 | } |
1419 | 1419 | ||
1420 | ht_cap = &rdev->wiphy.bands[chandef->chan->band]->ht_cap; | 1420 | if (!cfg80211_chandef_valid(chandef)) |
1421 | vht_cap = &rdev->wiphy.bands[chandef->chan->band]->vht_cap; | ||
1422 | |||
1423 | if (!cfg80211_chan_def_valid(chandef)) | ||
1424 | return -EINVAL; | 1421 | return -EINVAL; |
1425 | 1422 | ||
1426 | switch (chandef->width) { | 1423 | if (!cfg80211_chandef_usable(&rdev->wiphy, chandef, |
1427 | case NL80211_CHAN_WIDTH_20: | 1424 | IEEE80211_CHAN_DISABLED)) |
1428 | if (!ht_cap->ht_supported) | ||
1429 | return -EINVAL; | ||
1430 | case NL80211_CHAN_WIDTH_20_NOHT: | ||
1431 | width = 20; | ||
1432 | break; | ||
1433 | case NL80211_CHAN_WIDTH_40: | ||
1434 | width = 40; | ||
1435 | /* quick early regulatory check */ | ||
1436 | if (chandef->center_freq1 < control_freq && | ||
1437 | chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS) | ||
1438 | return -EINVAL; | ||
1439 | if (chandef->center_freq1 > control_freq && | ||
1440 | chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS) | ||
1441 | return -EINVAL; | ||
1442 | if (!ht_cap->ht_supported) | ||
1443 | return -EINVAL; | ||
1444 | if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || | ||
1445 | ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) | ||
1446 | return -EINVAL; | ||
1447 | break; | ||
1448 | case NL80211_CHAN_WIDTH_80: | ||
1449 | width = 80; | ||
1450 | if (!vht_cap->vht_supported) | ||
1451 | return -EINVAL; | ||
1452 | break; | ||
1453 | case NL80211_CHAN_WIDTH_80P80: | ||
1454 | width = 80; | ||
1455 | if (!vht_cap->vht_supported) | ||
1456 | return -EINVAL; | ||
1457 | if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) | ||
1458 | return -EINVAL; | ||
1459 | break; | ||
1460 | case NL80211_CHAN_WIDTH_160: | ||
1461 | width = 160; | ||
1462 | if (!vht_cap->vht_supported) | ||
1463 | return -EINVAL; | ||
1464 | if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)) | ||
1465 | return -EINVAL; | ||
1466 | break; | ||
1467 | default: | ||
1468 | return -EINVAL; | ||
1469 | } | ||
1470 | |||
1471 | if (!cfg80211_secondary_chans_ok(&rdev->wiphy, chandef->center_freq1, | ||
1472 | width, IEEE80211_CHAN_DISABLED)) | ||
1473 | return -EINVAL; | ||
1474 | if (chandef->center_freq2 && | ||
1475 | !cfg80211_secondary_chans_ok(&rdev->wiphy, chandef->center_freq2, | ||
1476 | width, IEEE80211_CHAN_DISABLED)) | ||
1477 | return -EINVAL; | 1425 | return -EINVAL; |
1478 | 1426 | ||
1479 | /* TODO: missing regulatory check on bandwidth */ | ||
1480 | |||
1481 | return 0; | 1427 | return 0; |
1482 | } | 1428 | } |
1483 | 1429 | ||
@@ -1841,7 +1787,7 @@ static inline u64 wdev_id(struct wireless_dev *wdev) | |||
1841 | static int nl80211_send_chandef(struct sk_buff *msg, | 1787 | static int nl80211_send_chandef(struct sk_buff *msg, |
1842 | struct cfg80211_chan_def *chandef) | 1788 | struct cfg80211_chan_def *chandef) |
1843 | { | 1789 | { |
1844 | WARN_ON(!cfg80211_chan_def_valid(chandef)); | 1790 | WARN_ON(!cfg80211_chandef_valid(chandef)); |
1845 | 1791 | ||
1846 | if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, | 1792 | if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, |
1847 | chandef->chan->center_freq)) | 1793 | chandef->chan->center_freq)) |
@@ -2732,6 +2678,32 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) | |||
2732 | info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]); | 2678 | info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]); |
2733 | } | 2679 | } |
2734 | 2680 | ||
2681 | if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) { | ||
2682 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) | ||
2683 | return -EINVAL; | ||
2684 | params.p2p_ctwindow = | ||
2685 | nla_get_u8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]); | ||
2686 | if (params.p2p_ctwindow > 127) | ||
2687 | return -EINVAL; | ||
2688 | if (params.p2p_ctwindow != 0 && | ||
2689 | !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN)) | ||
2690 | return -EINVAL; | ||
2691 | } | ||
2692 | |||
2693 | if (info->attrs[NL80211_ATTR_P2P_OPPPS]) { | ||
2694 | u8 tmp; | ||
2695 | |||
2696 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) | ||
2697 | return -EINVAL; | ||
2698 | tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]); | ||
2699 | if (tmp > 1) | ||
2700 | return -EINVAL; | ||
2701 | params.p2p_opp_ps = tmp; | ||
2702 | if (params.p2p_opp_ps != 0 && | ||
2703 | !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS)) | ||
2704 | return -EINVAL; | ||
2705 | } | ||
2706 | |||
2735 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { | 2707 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { |
2736 | err = nl80211_parse_chandef(rdev, info, ¶ms.chandef); | 2708 | err = nl80211_parse_chandef(rdev, info, ¶ms.chandef); |
2737 | if (err) | 2709 | if (err) |
@@ -3698,6 +3670,8 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) | |||
3698 | params.use_short_slot_time = -1; | 3670 | params.use_short_slot_time = -1; |
3699 | params.ap_isolate = -1; | 3671 | params.ap_isolate = -1; |
3700 | params.ht_opmode = -1; | 3672 | params.ht_opmode = -1; |
3673 | params.p2p_ctwindow = -1; | ||
3674 | params.p2p_opp_ps = -1; | ||
3701 | 3675 | ||
3702 | if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) | 3676 | if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) |
3703 | params.use_cts_prot = | 3677 | params.use_cts_prot = |
@@ -3720,6 +3694,32 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) | |||
3720 | params.ht_opmode = | 3694 | params.ht_opmode = |
3721 | nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]); | 3695 | nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]); |
3722 | 3696 | ||
3697 | if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) { | ||
3698 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) | ||
3699 | return -EINVAL; | ||
3700 | params.p2p_ctwindow = | ||
3701 | nla_get_s8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]); | ||
3702 | if (params.p2p_ctwindow < 0) | ||
3703 | return -EINVAL; | ||
3704 | if (params.p2p_ctwindow != 0 && | ||
3705 | !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN)) | ||
3706 | return -EINVAL; | ||
3707 | } | ||
3708 | |||
3709 | if (info->attrs[NL80211_ATTR_P2P_OPPPS]) { | ||
3710 | u8 tmp; | ||
3711 | |||
3712 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) | ||
3713 | return -EINVAL; | ||
3714 | tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]); | ||
3715 | if (tmp > 1) | ||
3716 | return -EINVAL; | ||
3717 | params.p2p_opp_ps = tmp; | ||
3718 | if (params.p2p_opp_ps && | ||
3719 | !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS)) | ||
3720 | return -EINVAL; | ||
3721 | } | ||
3722 | |||
3723 | if (!rdev->ops->change_bss) | 3723 | if (!rdev->ops->change_bss) |
3724 | return -EOPNOTSUPP; | 3724 | return -EOPNOTSUPP; |
3725 | 3725 | ||
@@ -4808,6 +4808,7 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, | |||
4808 | struct cfg80211_internal_bss *intbss) | 4808 | struct cfg80211_internal_bss *intbss) |
4809 | { | 4809 | { |
4810 | struct cfg80211_bss *res = &intbss->pub; | 4810 | struct cfg80211_bss *res = &intbss->pub; |
4811 | const struct cfg80211_bss_ies *ies; | ||
4811 | void *hdr; | 4812 | void *hdr; |
4812 | struct nlattr *bss; | 4813 | struct nlattr *bss; |
4813 | 4814 | ||
@@ -4828,16 +4829,24 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, | |||
4828 | if (!bss) | 4829 | if (!bss) |
4829 | goto nla_put_failure; | 4830 | goto nla_put_failure; |
4830 | if ((!is_zero_ether_addr(res->bssid) && | 4831 | if ((!is_zero_ether_addr(res->bssid) && |
4831 | nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)) || | 4832 | nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid))) |
4832 | (res->information_elements && res->len_information_elements && | 4833 | goto nla_put_failure; |
4833 | nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS, | 4834 | |
4834 | res->len_information_elements, | 4835 | rcu_read_lock(); |
4835 | res->information_elements)) || | 4836 | ies = rcu_dereference(res->ies); |
4836 | (res->beacon_ies && res->len_beacon_ies && | 4837 | if (ies && ies->len && nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS, |
4837 | res->beacon_ies != res->information_elements && | 4838 | ies->len, ies->data)) { |
4838 | nla_put(msg, NL80211_BSS_BEACON_IES, | 4839 | rcu_read_unlock(); |
4839 | res->len_beacon_ies, res->beacon_ies))) | 4840 | goto nla_put_failure; |
4841 | } | ||
4842 | ies = rcu_dereference(res->beacon_ies); | ||
4843 | if (ies && ies->len && nla_put(msg, NL80211_BSS_BEACON_IES, | ||
4844 | ies->len, ies->data)) { | ||
4845 | rcu_read_unlock(); | ||
4840 | goto nla_put_failure; | 4846 | goto nla_put_failure; |
4847 | } | ||
4848 | rcu_read_unlock(); | ||
4849 | |||
4841 | if (res->tsf && | 4850 | if (res->tsf && |
4842 | nla_put_u64(msg, NL80211_BSS_TSF, res->tsf)) | 4851 | nla_put_u64(msg, NL80211_BSS_TSF, res->tsf)) |
4843 | goto nla_put_failure; | 4852 | goto nla_put_failure; |
@@ -5502,6 +5511,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) | |||
5502 | return -EINVAL; | 5511 | return -EINVAL; |
5503 | if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT && | 5512 | if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT && |
5504 | !(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)) | 5513 | !(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)) |
5514 | return -EINVAL; | ||
5505 | 5515 | ||
5506 | ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; | 5516 | ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; |
5507 | ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; | 5517 | ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; |
@@ -6529,14 +6539,13 @@ nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = { | |||
6529 | }; | 6539 | }; |
6530 | 6540 | ||
6531 | static int nl80211_set_cqm_txe(struct genl_info *info, | 6541 | static int nl80211_set_cqm_txe(struct genl_info *info, |
6532 | u32 rate, u32 pkts, u32 intvl) | 6542 | u32 rate, u32 pkts, u32 intvl) |
6533 | { | 6543 | { |
6534 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 6544 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
6535 | struct wireless_dev *wdev; | 6545 | struct wireless_dev *wdev; |
6536 | struct net_device *dev = info->user_ptr[1]; | 6546 | struct net_device *dev = info->user_ptr[1]; |
6537 | 6547 | ||
6538 | if ((rate < 0 || rate > 100) || | 6548 | if (rate > 100 || intvl > NL80211_CQM_TXE_MAX_INTVL) |
6539 | (intvl < 0 || intvl > NL80211_CQM_TXE_MAX_INTVL)) | ||
6540 | return -EINVAL; | 6549 | return -EINVAL; |
6541 | 6550 | ||
6542 | wdev = dev->ieee80211_ptr; | 6551 | wdev = dev->ieee80211_ptr; |
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index b75756b05af..6e5308998e3 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -1796,7 +1796,7 @@ EXPORT_SYMBOL(regulatory_hint); | |||
1796 | */ | 1796 | */ |
1797 | void regulatory_hint_11d(struct wiphy *wiphy, | 1797 | void regulatory_hint_11d(struct wiphy *wiphy, |
1798 | enum ieee80211_band band, | 1798 | enum ieee80211_band band, |
1799 | u8 *country_ie, | 1799 | const u8 *country_ie, |
1800 | u8 country_ie_len) | 1800 | u8 country_ie_len) |
1801 | { | 1801 | { |
1802 | char alpha2[2]; | 1802 | char alpha2[2]; |
diff --git a/net/wireless/reg.h b/net/wireless/reg.h index f023c8a31c6..4c0a32ffd53 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h | |||
@@ -81,7 +81,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, | |||
81 | */ | 81 | */ |
82 | void regulatory_hint_11d(struct wiphy *wiphy, | 82 | void regulatory_hint_11d(struct wiphy *wiphy, |
83 | enum ieee80211_band band, | 83 | enum ieee80211_band band, |
84 | u8 *country_ie, | 84 | const u8 *country_ie, |
85 | u8 country_ie_len); | 85 | u8 country_ie_len); |
86 | 86 | ||
87 | /** | 87 | /** |
diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 9596015975d..01592d7d478 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c | |||
@@ -23,18 +23,23 @@ | |||
23 | 23 | ||
24 | static void bss_release(struct kref *ref) | 24 | static void bss_release(struct kref *ref) |
25 | { | 25 | { |
26 | struct cfg80211_bss_ies *ies; | ||
26 | struct cfg80211_internal_bss *bss; | 27 | struct cfg80211_internal_bss *bss; |
27 | 28 | ||
28 | bss = container_of(ref, struct cfg80211_internal_bss, ref); | 29 | bss = container_of(ref, struct cfg80211_internal_bss, ref); |
30 | |||
31 | if (WARN_ON(atomic_read(&bss->hold))) | ||
32 | return; | ||
33 | |||
29 | if (bss->pub.free_priv) | 34 | if (bss->pub.free_priv) |
30 | bss->pub.free_priv(&bss->pub); | 35 | bss->pub.free_priv(&bss->pub); |
31 | 36 | ||
32 | if (bss->beacon_ies_allocated) | 37 | ies = (void *)rcu_access_pointer(bss->pub.beacon_ies); |
33 | kfree(bss->pub.beacon_ies); | 38 | if (ies) |
34 | if (bss->proberesp_ies_allocated) | 39 | kfree_rcu(ies, rcu_head); |
35 | kfree(bss->pub.proberesp_ies); | 40 | ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies); |
36 | 41 | if (ies) | |
37 | BUG_ON(atomic_read(&bss->hold)); | 42 | kfree_rcu(ies, rcu_head); |
38 | 43 | ||
39 | kfree(bss); | 44 | kfree(bss); |
40 | } | 45 | } |
@@ -236,9 +241,8 @@ void cfg80211_bss_age(struct cfg80211_registered_device *dev, | |||
236 | struct cfg80211_internal_bss *bss; | 241 | struct cfg80211_internal_bss *bss; |
237 | unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); | 242 | unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); |
238 | 243 | ||
239 | list_for_each_entry(bss, &dev->bss_list, list) { | 244 | list_for_each_entry(bss, &dev->bss_list, list) |
240 | bss->ts -= age_jiffies; | 245 | bss->ts -= age_jiffies; |
241 | } | ||
242 | } | 246 | } |
243 | 247 | ||
244 | void cfg80211_bss_expire(struct cfg80211_registered_device *dev) | 248 | void cfg80211_bss_expire(struct cfg80211_registered_device *dev) |
@@ -287,7 +291,7 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, | |||
287 | } | 291 | } |
288 | EXPORT_SYMBOL(cfg80211_find_vendor_ie); | 292 | EXPORT_SYMBOL(cfg80211_find_vendor_ie); |
289 | 293 | ||
290 | static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) | 294 | static int cmp_ies(u8 num, const u8 *ies1, int len1, const u8 *ies2, int len2) |
291 | { | 295 | { |
292 | const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); | 296 | const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); |
293 | const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); | 297 | const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); |
@@ -307,10 +311,10 @@ static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) | |||
307 | return memcmp(ie1 + 2, ie2 + 2, ie1[1]); | 311 | return memcmp(ie1 + 2, ie2 + 2, ie1[1]); |
308 | } | 312 | } |
309 | 313 | ||
310 | static bool is_bss(struct cfg80211_bss *a, | 314 | static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, |
311 | const u8 *bssid, | ||
312 | const u8 *ssid, size_t ssid_len) | 315 | const u8 *ssid, size_t ssid_len) |
313 | { | 316 | { |
317 | const struct cfg80211_bss_ies *ies; | ||
314 | const u8 *ssidie; | 318 | const u8 *ssidie; |
315 | 319 | ||
316 | if (bssid && !ether_addr_equal(a->bssid, bssid)) | 320 | if (bssid && !ether_addr_equal(a->bssid, bssid)) |
@@ -319,9 +323,10 @@ static bool is_bss(struct cfg80211_bss *a, | |||
319 | if (!ssid) | 323 | if (!ssid) |
320 | return true; | 324 | return true; |
321 | 325 | ||
322 | ssidie = cfg80211_find_ie(WLAN_EID_SSID, | 326 | ies = rcu_access_pointer(a->ies); |
323 | a->information_elements, | 327 | if (!ies) |
324 | a->len_information_elements); | 328 | return false; |
329 | ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len); | ||
325 | if (!ssidie) | 330 | if (!ssidie) |
326 | return false; | 331 | return false; |
327 | if (ssidie[1] != ssid_len) | 332 | if (ssidie[1] != ssid_len) |
@@ -331,20 +336,21 @@ static bool is_bss(struct cfg80211_bss *a, | |||
331 | 336 | ||
332 | static bool is_mesh_bss(struct cfg80211_bss *a) | 337 | static bool is_mesh_bss(struct cfg80211_bss *a) |
333 | { | 338 | { |
339 | const struct cfg80211_bss_ies *ies; | ||
334 | const u8 *ie; | 340 | const u8 *ie; |
335 | 341 | ||
336 | if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) | 342 | if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) |
337 | return false; | 343 | return false; |
338 | 344 | ||
339 | ie = cfg80211_find_ie(WLAN_EID_MESH_ID, | 345 | ies = rcu_access_pointer(a->ies); |
340 | a->information_elements, | 346 | if (!ies) |
341 | a->len_information_elements); | 347 | return false; |
348 | |||
349 | ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len); | ||
342 | if (!ie) | 350 | if (!ie) |
343 | return false; | 351 | return false; |
344 | 352 | ||
345 | ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, | 353 | ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len); |
346 | a->information_elements, | ||
347 | a->len_information_elements); | ||
348 | if (!ie) | 354 | if (!ie) |
349 | return false; | 355 | return false; |
350 | 356 | ||
@@ -355,14 +361,17 @@ static bool is_mesh(struct cfg80211_bss *a, | |||
355 | const u8 *meshid, size_t meshidlen, | 361 | const u8 *meshid, size_t meshidlen, |
356 | const u8 *meshcfg) | 362 | const u8 *meshcfg) |
357 | { | 363 | { |
364 | const struct cfg80211_bss_ies *ies; | ||
358 | const u8 *ie; | 365 | const u8 *ie; |
359 | 366 | ||
360 | if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) | 367 | if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) |
361 | return false; | 368 | return false; |
362 | 369 | ||
363 | ie = cfg80211_find_ie(WLAN_EID_MESH_ID, | 370 | ies = rcu_access_pointer(a->ies); |
364 | a->information_elements, | 371 | if (!ies) |
365 | a->len_information_elements); | 372 | return false; |
373 | |||
374 | ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len); | ||
366 | if (!ie) | 375 | if (!ie) |
367 | return false; | 376 | return false; |
368 | if (ie[1] != meshidlen) | 377 | if (ie[1] != meshidlen) |
@@ -370,9 +379,7 @@ static bool is_mesh(struct cfg80211_bss *a, | |||
370 | if (memcmp(ie + 2, meshid, meshidlen)) | 379 | if (memcmp(ie + 2, meshid, meshidlen)) |
371 | return false; | 380 | return false; |
372 | 381 | ||
373 | ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, | 382 | ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len); |
374 | a->information_elements, | ||
375 | a->len_information_elements); | ||
376 | if (!ie) | 383 | if (!ie) |
377 | return false; | 384 | return false; |
378 | if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) | 385 | if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) |
@@ -384,30 +391,33 @@ static bool is_mesh(struct cfg80211_bss *a, | |||
384 | * part in the same mesh. | 391 | * part in the same mesh. |
385 | */ | 392 | */ |
386 | return memcmp(ie + 2, meshcfg, | 393 | return memcmp(ie + 2, meshcfg, |
387 | sizeof(struct ieee80211_meshconf_ie) - 2) == 0; | 394 | sizeof(struct ieee80211_meshconf_ie) - 2) == 0; |
388 | } | 395 | } |
389 | 396 | ||
390 | static int cmp_bss_core(struct cfg80211_bss *a, | 397 | static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b) |
391 | struct cfg80211_bss *b) | ||
392 | { | 398 | { |
399 | const struct cfg80211_bss_ies *a_ies, *b_ies; | ||
393 | int r; | 400 | int r; |
394 | 401 | ||
395 | if (a->channel != b->channel) | 402 | if (a->channel != b->channel) |
396 | return b->channel->center_freq - a->channel->center_freq; | 403 | return b->channel->center_freq - a->channel->center_freq; |
397 | 404 | ||
398 | if (is_mesh_bss(a) && is_mesh_bss(b)) { | 405 | if (is_mesh_bss(a) && is_mesh_bss(b)) { |
406 | a_ies = rcu_access_pointer(a->ies); | ||
407 | if (!a_ies) | ||
408 | return -1; | ||
409 | b_ies = rcu_access_pointer(b->ies); | ||
410 | if (!b_ies) | ||
411 | return 1; | ||
412 | |||
399 | r = cmp_ies(WLAN_EID_MESH_ID, | 413 | r = cmp_ies(WLAN_EID_MESH_ID, |
400 | a->information_elements, | 414 | a_ies->data, a_ies->len, |
401 | a->len_information_elements, | 415 | b_ies->data, b_ies->len); |
402 | b->information_elements, | ||
403 | b->len_information_elements); | ||
404 | if (r) | 416 | if (r) |
405 | return r; | 417 | return r; |
406 | return cmp_ies(WLAN_EID_MESH_CONFIG, | 418 | return cmp_ies(WLAN_EID_MESH_CONFIG, |
407 | a->information_elements, | 419 | a_ies->data, a_ies->len, |
408 | a->len_information_elements, | 420 | b_ies->data, b_ies->len); |
409 | b->information_elements, | ||
410 | b->len_information_elements); | ||
411 | } | 421 | } |
412 | 422 | ||
413 | /* | 423 | /* |
@@ -420,22 +430,28 @@ static int cmp_bss_core(struct cfg80211_bss *a, | |||
420 | static int cmp_bss(struct cfg80211_bss *a, | 430 | static int cmp_bss(struct cfg80211_bss *a, |
421 | struct cfg80211_bss *b) | 431 | struct cfg80211_bss *b) |
422 | { | 432 | { |
433 | const struct cfg80211_bss_ies *a_ies, *b_ies; | ||
423 | int r; | 434 | int r; |
424 | 435 | ||
425 | r = cmp_bss_core(a, b); | 436 | r = cmp_bss_core(a, b); |
426 | if (r) | 437 | if (r) |
427 | return r; | 438 | return r; |
428 | 439 | ||
440 | a_ies = rcu_access_pointer(a->ies); | ||
441 | if (!a_ies) | ||
442 | return -1; | ||
443 | b_ies = rcu_access_pointer(b->ies); | ||
444 | if (!b_ies) | ||
445 | return 1; | ||
446 | |||
429 | return cmp_ies(WLAN_EID_SSID, | 447 | return cmp_ies(WLAN_EID_SSID, |
430 | a->information_elements, | 448 | a_ies->data, a_ies->len, |
431 | a->len_information_elements, | 449 | b_ies->data, b_ies->len); |
432 | b->information_elements, | ||
433 | b->len_information_elements); | ||
434 | } | 450 | } |
435 | 451 | ||
436 | static int cmp_hidden_bss(struct cfg80211_bss *a, | 452 | static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b) |
437 | struct cfg80211_bss *b) | ||
438 | { | 453 | { |
454 | const struct cfg80211_bss_ies *a_ies, *b_ies; | ||
439 | const u8 *ie1; | 455 | const u8 *ie1; |
440 | const u8 *ie2; | 456 | const u8 *ie2; |
441 | int i; | 457 | int i; |
@@ -445,17 +461,26 @@ static int cmp_hidden_bss(struct cfg80211_bss *a, | |||
445 | if (r) | 461 | if (r) |
446 | return r; | 462 | return r; |
447 | 463 | ||
448 | ie1 = cfg80211_find_ie(WLAN_EID_SSID, | 464 | a_ies = rcu_access_pointer(a->ies); |
449 | a->information_elements, | 465 | if (!a_ies) |
450 | a->len_information_elements); | 466 | return -1; |
451 | ie2 = cfg80211_find_ie(WLAN_EID_SSID, | 467 | b_ies = rcu_access_pointer(b->ies); |
452 | b->information_elements, | 468 | if (!b_ies) |
453 | b->len_information_elements); | 469 | return 1; |
470 | |||
471 | ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len); | ||
472 | ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len); | ||
454 | 473 | ||
455 | /* Key comparator must use same algorithm in any rb-tree | 474 | /* |
475 | * Key comparator must use same algorithm in any rb-tree | ||
456 | * search function (order is important), otherwise ordering | 476 | * search function (order is important), otherwise ordering |
457 | * of items in the tree is broken and search gives incorrect | 477 | * of items in the tree is broken and search gives incorrect |
458 | * results. This code uses same order as cmp_ies() does. */ | 478 | * results. This code uses same order as cmp_ies() does. |
479 | * | ||
480 | * Note that due to the differring behaviour with hidden SSIDs | ||
481 | * this function only works when "b" is the tree element and | ||
482 | * "a" is the key we're looking for. | ||
483 | */ | ||
459 | 484 | ||
460 | /* sort missing IE before (left of) present IE */ | 485 | /* sort missing IE before (left of) present IE */ |
461 | if (!ie1) | 486 | if (!ie1) |
@@ -471,10 +496,14 @@ static int cmp_hidden_bss(struct cfg80211_bss *a, | |||
471 | if (ie1[1] != ie2[1]) | 496 | if (ie1[1] != ie2[1]) |
472 | return ie2[1] - ie1[1]; | 497 | return ie2[1] - ie1[1]; |
473 | 498 | ||
474 | /* zeroed SSID ie is another indication of a hidden bss */ | 499 | /* |
500 | * zeroed SSID ie is another indication of a hidden bss; | ||
501 | * if it isn't zeroed just return the regular sort value | ||
502 | * to find the next candidate | ||
503 | */ | ||
475 | for (i = 0; i < ie2[1]; i++) | 504 | for (i = 0; i < ie2[1]; i++) |
476 | if (ie2[i + 2]) | 505 | if (ie2[i + 2]) |
477 | return -1; | 506 | return memcmp(ie1 + 2, ie2 + 2, ie1[1]); |
478 | 507 | ||
479 | return 0; | 508 | return 0; |
480 | } | 509 | } |
@@ -600,7 +629,7 @@ rb_find_bss(struct cfg80211_registered_device *dev, | |||
600 | 629 | ||
601 | static struct cfg80211_internal_bss * | 630 | static struct cfg80211_internal_bss * |
602 | rb_find_hidden_bss(struct cfg80211_registered_device *dev, | 631 | rb_find_hidden_bss(struct cfg80211_registered_device *dev, |
603 | struct cfg80211_internal_bss *res) | 632 | struct cfg80211_internal_bss *res) |
604 | { | 633 | { |
605 | struct rb_node *n = dev->bss_tree.rb_node; | 634 | struct rb_node *n = dev->bss_tree.rb_node; |
606 | struct cfg80211_internal_bss *bss; | 635 | struct cfg80211_internal_bss *bss; |
@@ -623,127 +652,86 @@ rb_find_hidden_bss(struct cfg80211_registered_device *dev, | |||
623 | 652 | ||
624 | static void | 653 | static void |
625 | copy_hidden_ies(struct cfg80211_internal_bss *res, | 654 | copy_hidden_ies(struct cfg80211_internal_bss *res, |
626 | struct cfg80211_internal_bss *hidden) | 655 | struct cfg80211_internal_bss *hidden) |
627 | { | 656 | { |
628 | if (unlikely(res->pub.beacon_ies)) | 657 | const struct cfg80211_bss_ies *ies; |
629 | return; | 658 | |
630 | if (WARN_ON(!hidden->pub.beacon_ies)) | 659 | if (rcu_access_pointer(res->pub.beacon_ies)) |
631 | return; | 660 | return; |
632 | 661 | ||
633 | res->pub.beacon_ies = kmalloc(hidden->pub.len_beacon_ies, GFP_ATOMIC); | 662 | ies = rcu_access_pointer(hidden->pub.beacon_ies); |
634 | if (unlikely(!res->pub.beacon_ies)) | 663 | if (WARN_ON(!ies)) |
635 | return; | 664 | return; |
636 | 665 | ||
637 | res->beacon_ies_allocated = true; | 666 | ies = kmemdup(ies, sizeof(*ies) + ies->len, GFP_ATOMIC); |
638 | res->pub.len_beacon_ies = hidden->pub.len_beacon_ies; | 667 | if (unlikely(!ies)) |
639 | memcpy(res->pub.beacon_ies, hidden->pub.beacon_ies, | 668 | return; |
640 | res->pub.len_beacon_ies); | 669 | rcu_assign_pointer(res->pub.beacon_ies, ies); |
641 | } | 670 | } |
642 | 671 | ||
643 | static struct cfg80211_internal_bss * | 672 | static struct cfg80211_internal_bss * |
644 | cfg80211_bss_update(struct cfg80211_registered_device *dev, | 673 | cfg80211_bss_update(struct cfg80211_registered_device *dev, |
645 | struct cfg80211_internal_bss *res) | 674 | struct cfg80211_internal_bss *tmp) |
646 | { | 675 | { |
647 | struct cfg80211_internal_bss *found = NULL; | 676 | struct cfg80211_internal_bss *found = NULL; |
648 | 677 | ||
649 | /* | 678 | if (WARN_ON(!tmp->pub.channel)) |
650 | * The reference to "res" is donated to this function. | ||
651 | */ | ||
652 | |||
653 | if (WARN_ON(!res->pub.channel)) { | ||
654 | kref_put(&res->ref, bss_release); | ||
655 | return NULL; | 679 | return NULL; |
656 | } | ||
657 | 680 | ||
658 | res->ts = jiffies; | 681 | tmp->ts = jiffies; |
659 | 682 | ||
660 | spin_lock_bh(&dev->bss_lock); | 683 | spin_lock_bh(&dev->bss_lock); |
661 | 684 | ||
662 | found = rb_find_bss(dev, res); | 685 | if (WARN_ON(!rcu_access_pointer(tmp->pub.ies))) { |
686 | spin_unlock_bh(&dev->bss_lock); | ||
687 | return NULL; | ||
688 | } | ||
689 | |||
690 | found = rb_find_bss(dev, tmp); | ||
663 | 691 | ||
664 | if (found) { | 692 | if (found) { |
665 | found->pub.beacon_interval = res->pub.beacon_interval; | 693 | found->pub.beacon_interval = tmp->pub.beacon_interval; |
666 | found->pub.tsf = res->pub.tsf; | 694 | found->pub.tsf = tmp->pub.tsf; |
667 | found->pub.signal = res->pub.signal; | 695 | found->pub.signal = tmp->pub.signal; |
668 | found->pub.capability = res->pub.capability; | 696 | found->pub.capability = tmp->pub.capability; |
669 | found->ts = res->ts; | 697 | found->ts = tmp->ts; |
670 | 698 | ||
671 | /* Update IEs */ | 699 | /* Update IEs */ |
672 | if (res->pub.proberesp_ies) { | 700 | if (rcu_access_pointer(tmp->pub.proberesp_ies)) { |
673 | size_t used = dev->wiphy.bss_priv_size + sizeof(*res); | 701 | const struct cfg80211_bss_ies *old; |
674 | size_t ielen = res->pub.len_proberesp_ies; | ||
675 | |||
676 | if (found->pub.proberesp_ies && | ||
677 | !found->proberesp_ies_allocated && | ||
678 | ksize(found) >= used + ielen) { | ||
679 | memcpy(found->pub.proberesp_ies, | ||
680 | res->pub.proberesp_ies, ielen); | ||
681 | found->pub.len_proberesp_ies = ielen; | ||
682 | } else { | ||
683 | u8 *ies = found->pub.proberesp_ies; | ||
684 | |||
685 | if (found->proberesp_ies_allocated) | ||
686 | ies = krealloc(ies, ielen, GFP_ATOMIC); | ||
687 | else | ||
688 | ies = kmalloc(ielen, GFP_ATOMIC); | ||
689 | |||
690 | if (ies) { | ||
691 | memcpy(ies, res->pub.proberesp_ies, | ||
692 | ielen); | ||
693 | found->proberesp_ies_allocated = true; | ||
694 | found->pub.proberesp_ies = ies; | ||
695 | found->pub.len_proberesp_ies = ielen; | ||
696 | } | ||
697 | } | ||
698 | 702 | ||
703 | old = rcu_access_pointer(found->pub.proberesp_ies); | ||
704 | |||
705 | rcu_assign_pointer(found->pub.proberesp_ies, | ||
706 | tmp->pub.proberesp_ies); | ||
699 | /* Override possible earlier Beacon frame IEs */ | 707 | /* Override possible earlier Beacon frame IEs */ |
700 | found->pub.information_elements = | 708 | rcu_assign_pointer(found->pub.ies, |
701 | found->pub.proberesp_ies; | 709 | tmp->pub.proberesp_ies); |
702 | found->pub.len_information_elements = | 710 | if (old) |
703 | found->pub.len_proberesp_ies; | 711 | kfree_rcu((struct cfg80211_bss_ies *)old, |
704 | } | 712 | rcu_head); |
705 | if (res->pub.beacon_ies) { | 713 | } else if (rcu_access_pointer(tmp->pub.beacon_ies)) { |
706 | size_t used = dev->wiphy.bss_priv_size + sizeof(*res); | 714 | const struct cfg80211_bss_ies *old, *ies; |
707 | size_t ielen = res->pub.len_beacon_ies; | 715 | |
708 | bool information_elements_is_beacon_ies = | 716 | old = rcu_access_pointer(found->pub.beacon_ies); |
709 | (found->pub.information_elements == | 717 | ies = rcu_access_pointer(found->pub.ies); |
710 | found->pub.beacon_ies); | 718 | |
711 | 719 | rcu_assign_pointer(found->pub.beacon_ies, | |
712 | if (found->pub.beacon_ies && | 720 | tmp->pub.beacon_ies); |
713 | !found->beacon_ies_allocated && | ||
714 | ksize(found) >= used + ielen) { | ||
715 | memcpy(found->pub.beacon_ies, | ||
716 | res->pub.beacon_ies, ielen); | ||
717 | found->pub.len_beacon_ies = ielen; | ||
718 | } else { | ||
719 | u8 *ies = found->pub.beacon_ies; | ||
720 | |||
721 | if (found->beacon_ies_allocated) | ||
722 | ies = krealloc(ies, ielen, GFP_ATOMIC); | ||
723 | else | ||
724 | ies = kmalloc(ielen, GFP_ATOMIC); | ||
725 | |||
726 | if (ies) { | ||
727 | memcpy(ies, res->pub.beacon_ies, | ||
728 | ielen); | ||
729 | found->beacon_ies_allocated = true; | ||
730 | found->pub.beacon_ies = ies; | ||
731 | found->pub.len_beacon_ies = ielen; | ||
732 | } | ||
733 | } | ||
734 | 721 | ||
735 | /* Override IEs if they were from a beacon before */ | 722 | /* Override IEs if they were from a beacon before */ |
736 | if (information_elements_is_beacon_ies) { | 723 | if (old == ies) |
737 | found->pub.information_elements = | 724 | rcu_assign_pointer(found->pub.ies, |
738 | found->pub.beacon_ies; | 725 | tmp->pub.beacon_ies); |
739 | found->pub.len_information_elements = | ||
740 | found->pub.len_beacon_ies; | ||
741 | } | ||
742 | } | ||
743 | 726 | ||
744 | kref_put(&res->ref, bss_release); | 727 | if (old) |
728 | kfree_rcu((struct cfg80211_bss_ies *)old, | ||
729 | rcu_head); | ||
730 | } | ||
745 | } else { | 731 | } else { |
732 | struct cfg80211_internal_bss *new; | ||
746 | struct cfg80211_internal_bss *hidden; | 733 | struct cfg80211_internal_bss *hidden; |
734 | struct cfg80211_bss_ies *ies; | ||
747 | 735 | ||
748 | /* First check if the beacon is a probe response from | 736 | /* First check if the beacon is a probe response from |
749 | * a hidden bss. If so, copy beacon ies (with nullified | 737 | * a hidden bss. If so, copy beacon ies (with nullified |
@@ -754,14 +742,32 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
754 | /* TODO: The code is not trying to update existing probe | 742 | /* TODO: The code is not trying to update existing probe |
755 | * response bss entries when beacon ies are | 743 | * response bss entries when beacon ies are |
756 | * getting changed. */ | 744 | * getting changed. */ |
757 | hidden = rb_find_hidden_bss(dev, res); | 745 | hidden = rb_find_hidden_bss(dev, tmp); |
758 | if (hidden) | 746 | if (hidden) |
759 | copy_hidden_ies(res, hidden); | 747 | copy_hidden_ies(tmp, hidden); |
760 | 748 | ||
761 | /* this "consumes" the reference */ | 749 | /* |
762 | list_add_tail(&res->list, &dev->bss_list); | 750 | * create a copy -- the "res" variable that is passed in |
763 | rb_insert_bss(dev, res); | 751 | * is allocated on the stack since it's not needed in the |
764 | found = res; | 752 | * more common case of an update |
753 | */ | ||
754 | new = kzalloc(sizeof(*new) + dev->wiphy.bss_priv_size, | ||
755 | GFP_ATOMIC); | ||
756 | if (!new) { | ||
757 | ies = (void *)rcu_dereference(tmp->pub.beacon_ies); | ||
758 | if (ies) | ||
759 | kfree_rcu(ies, rcu_head); | ||
760 | ies = (void *)rcu_dereference(tmp->pub.proberesp_ies); | ||
761 | if (ies) | ||
762 | kfree_rcu(ies, rcu_head); | ||
763 | spin_unlock_bh(&dev->bss_lock); | ||
764 | return NULL; | ||
765 | } | ||
766 | memcpy(new, tmp, sizeof(*new)); | ||
767 | kref_init(&new->ref); | ||
768 | list_add_tail(&new->list, &dev->bss_list); | ||
769 | rb_insert_bss(dev, new); | ||
770 | found = new; | ||
765 | } | 771 | } |
766 | 772 | ||
767 | dev->bss_generation++; | 773 | dev->bss_generation++; |
@@ -810,14 +816,12 @@ cfg80211_inform_bss(struct wiphy *wiphy, | |||
810 | u16 beacon_interval, const u8 *ie, size_t ielen, | 816 | u16 beacon_interval, const u8 *ie, size_t ielen, |
811 | s32 signal, gfp_t gfp) | 817 | s32 signal, gfp_t gfp) |
812 | { | 818 | { |
813 | struct cfg80211_internal_bss *res; | 819 | struct cfg80211_bss_ies *ies; |
814 | size_t privsz; | 820 | struct cfg80211_internal_bss tmp = {}, *res; |
815 | 821 | ||
816 | if (WARN_ON(!wiphy)) | 822 | if (WARN_ON(!wiphy)) |
817 | return NULL; | 823 | return NULL; |
818 | 824 | ||
819 | privsz = wiphy->bss_priv_size; | ||
820 | |||
821 | if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC && | 825 | if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC && |
822 | (signal < 0 || signal > 100))) | 826 | (signal < 0 || signal > 100))) |
823 | return NULL; | 827 | return NULL; |
@@ -826,36 +830,33 @@ cfg80211_inform_bss(struct wiphy *wiphy, | |||
826 | if (!channel) | 830 | if (!channel) |
827 | return NULL; | 831 | return NULL; |
828 | 832 | ||
829 | res = kzalloc(sizeof(*res) + privsz + ielen, gfp); | 833 | memcpy(tmp.pub.bssid, bssid, ETH_ALEN); |
830 | if (!res) | 834 | tmp.pub.channel = channel; |
831 | return NULL; | 835 | tmp.pub.signal = signal; |
832 | 836 | tmp.pub.tsf = tsf; | |
833 | memcpy(res->pub.bssid, bssid, ETH_ALEN); | 837 | tmp.pub.beacon_interval = beacon_interval; |
834 | res->pub.channel = channel; | 838 | tmp.pub.capability = capability; |
835 | res->pub.signal = signal; | ||
836 | res->pub.tsf = tsf; | ||
837 | res->pub.beacon_interval = beacon_interval; | ||
838 | res->pub.capability = capability; | ||
839 | /* | 839 | /* |
840 | * Since we do not know here whether the IEs are from a Beacon or Probe | 840 | * Since we do not know here whether the IEs are from a Beacon or Probe |
841 | * Response frame, we need to pick one of the options and only use it | 841 | * Response frame, we need to pick one of the options and only use it |
842 | * with the driver that does not provide the full Beacon/Probe Response | 842 | * with the driver that does not provide the full Beacon/Probe Response |
843 | * frame. Use Beacon frame pointer to avoid indicating that this should | 843 | * frame. Use Beacon frame pointer to avoid indicating that this should |
844 | * override the information_elements pointer should we have received an | 844 | * override the iies pointer should we have received an earlier |
845 | * earlier indication of Probe Response data. | 845 | * indication of Probe Response data. |
846 | * | 846 | * |
847 | * The initial buffer for the IEs is allocated with the BSS entry and | 847 | * The initial buffer for the IEs is allocated with the BSS entry and |
848 | * is located after the private area. | 848 | * is located after the private area. |
849 | */ | 849 | */ |
850 | res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz; | 850 | ies = kmalloc(sizeof(*ies) + ielen, gfp); |
851 | memcpy(res->pub.beacon_ies, ie, ielen); | 851 | if (!ies) |
852 | res->pub.len_beacon_ies = ielen; | 852 | return NULL; |
853 | res->pub.information_elements = res->pub.beacon_ies; | 853 | ies->len = ielen; |
854 | res->pub.len_information_elements = res->pub.len_beacon_ies; | 854 | memcpy(ies->data, ie, ielen); |
855 | 855 | ||
856 | kref_init(&res->ref); | 856 | rcu_assign_pointer(tmp.pub.beacon_ies, ies); |
857 | rcu_assign_pointer(tmp.pub.ies, ies); | ||
857 | 858 | ||
858 | res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); | 859 | res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp); |
859 | if (!res) | 860 | if (!res) |
860 | return NULL; | 861 | return NULL; |
861 | 862 | ||
@@ -874,10 +875,10 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, | |||
874 | struct ieee80211_mgmt *mgmt, size_t len, | 875 | struct ieee80211_mgmt *mgmt, size_t len, |
875 | s32 signal, gfp_t gfp) | 876 | s32 signal, gfp_t gfp) |
876 | { | 877 | { |
877 | struct cfg80211_internal_bss *res; | 878 | struct cfg80211_internal_bss tmp = {}, *res; |
879 | struct cfg80211_bss_ies *ies; | ||
878 | size_t ielen = len - offsetof(struct ieee80211_mgmt, | 880 | size_t ielen = len - offsetof(struct ieee80211_mgmt, |
879 | u.probe_resp.variable); | 881 | u.probe_resp.variable); |
880 | size_t privsz; | ||
881 | 882 | ||
882 | BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) != | 883 | BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) != |
883 | offsetof(struct ieee80211_mgmt, u.beacon.variable)); | 884 | offsetof(struct ieee80211_mgmt, u.beacon.variable)); |
@@ -897,45 +898,31 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, | |||
897 | if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable))) | 898 | if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable))) |
898 | return NULL; | 899 | return NULL; |
899 | 900 | ||
900 | privsz = wiphy->bss_priv_size; | ||
901 | |||
902 | channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable, | 901 | channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable, |
903 | ielen, channel); | 902 | ielen, channel); |
904 | if (!channel) | 903 | if (!channel) |
905 | return NULL; | 904 | return NULL; |
906 | 905 | ||
907 | res = kzalloc(sizeof(*res) + privsz + ielen, gfp); | 906 | ies = kmalloc(sizeof(*ies) + ielen, gfp); |
908 | if (!res) | 907 | if (!ies) |
909 | return NULL; | 908 | return NULL; |
909 | ies->len = ielen; | ||
910 | memcpy(ies->data, mgmt->u.probe_resp.variable, ielen); | ||
910 | 911 | ||
911 | memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN); | 912 | if (ieee80211_is_probe_resp(mgmt->frame_control)) |
912 | res->pub.channel = channel; | 913 | rcu_assign_pointer(tmp.pub.proberesp_ies, ies); |
913 | res->pub.signal = signal; | 914 | else |
914 | res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); | 915 | rcu_assign_pointer(tmp.pub.beacon_ies, ies); |
915 | res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); | 916 | rcu_assign_pointer(tmp.pub.ies, ies); |
916 | res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); | 917 | |
917 | /* | 918 | memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN); |
918 | * The initial buffer for the IEs is allocated with the BSS entry and | 919 | tmp.pub.channel = channel; |
919 | * is located after the private area. | 920 | tmp.pub.signal = signal; |
920 | */ | 921 | tmp.pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); |
921 | if (ieee80211_is_probe_resp(mgmt->frame_control)) { | 922 | tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); |
922 | res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz; | 923 | tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); |
923 | memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable, | 924 | |
924 | ielen); | 925 | res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp); |
925 | res->pub.len_proberesp_ies = ielen; | ||
926 | res->pub.information_elements = res->pub.proberesp_ies; | ||
927 | res->pub.len_information_elements = res->pub.len_proberesp_ies; | ||
928 | } else { | ||
929 | res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz; | ||
930 | memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen); | ||
931 | res->pub.len_beacon_ies = ielen; | ||
932 | res->pub.information_elements = res->pub.beacon_ies; | ||
933 | res->pub.len_information_elements = res->pub.len_beacon_ies; | ||
934 | } | ||
935 | |||
936 | kref_init(&res->ref); | ||
937 | |||
938 | res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); | ||
939 | if (!res) | 926 | if (!res) |
940 | return NULL; | 927 | return NULL; |
941 | 928 | ||
@@ -1127,22 +1114,21 @@ int cfg80211_wext_siwscan(struct net_device *dev, | |||
1127 | EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan); | 1114 | EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan); |
1128 | 1115 | ||
1129 | static void ieee80211_scan_add_ies(struct iw_request_info *info, | 1116 | static void ieee80211_scan_add_ies(struct iw_request_info *info, |
1130 | struct cfg80211_bss *bss, | 1117 | const struct cfg80211_bss_ies *ies, |
1131 | char **current_ev, char *end_buf) | 1118 | char **current_ev, char *end_buf) |
1132 | { | 1119 | { |
1133 | u8 *pos, *end, *next; | 1120 | const u8 *pos, *end, *next; |
1134 | struct iw_event iwe; | 1121 | struct iw_event iwe; |
1135 | 1122 | ||
1136 | if (!bss->information_elements || | 1123 | if (!ies) |
1137 | !bss->len_information_elements) | ||
1138 | return; | 1124 | return; |
1139 | 1125 | ||
1140 | /* | 1126 | /* |
1141 | * If needed, fragment the IEs buffer (at IE boundaries) into short | 1127 | * If needed, fragment the IEs buffer (at IE boundaries) into short |
1142 | * enough fragments to fit into IW_GENERIC_IE_MAX octet messages. | 1128 | * enough fragments to fit into IW_GENERIC_IE_MAX octet messages. |
1143 | */ | 1129 | */ |
1144 | pos = bss->information_elements; | 1130 | pos = ies->data; |
1145 | end = pos + bss->len_information_elements; | 1131 | end = pos + ies->len; |
1146 | 1132 | ||
1147 | while (end - pos > IW_GENERIC_IE_MAX) { | 1133 | while (end - pos > IW_GENERIC_IE_MAX) { |
1148 | next = pos + 2 + pos[1]; | 1134 | next = pos + 2 + pos[1]; |
@@ -1153,7 +1139,8 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info, | |||
1153 | iwe.cmd = IWEVGENIE; | 1139 | iwe.cmd = IWEVGENIE; |
1154 | iwe.u.data.length = next - pos; | 1140 | iwe.u.data.length = next - pos; |
1155 | *current_ev = iwe_stream_add_point(info, *current_ev, | 1141 | *current_ev = iwe_stream_add_point(info, *current_ev, |
1156 | end_buf, &iwe, pos); | 1142 | end_buf, &iwe, |
1143 | (void *)pos); | ||
1157 | 1144 | ||
1158 | pos = next; | 1145 | pos = next; |
1159 | } | 1146 | } |
@@ -1163,7 +1150,8 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info, | |||
1163 | iwe.cmd = IWEVGENIE; | 1150 | iwe.cmd = IWEVGENIE; |
1164 | iwe.u.data.length = end - pos; | 1151 | iwe.u.data.length = end - pos; |
1165 | *current_ev = iwe_stream_add_point(info, *current_ev, | 1152 | *current_ev = iwe_stream_add_point(info, *current_ev, |
1166 | end_buf, &iwe, pos); | 1153 | end_buf, &iwe, |
1154 | (void *)pos); | ||
1167 | } | 1155 | } |
1168 | } | 1156 | } |
1169 | 1157 | ||
@@ -1182,10 +1170,11 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, | |||
1182 | struct cfg80211_internal_bss *bss, char *current_ev, | 1170 | struct cfg80211_internal_bss *bss, char *current_ev, |
1183 | char *end_buf) | 1171 | char *end_buf) |
1184 | { | 1172 | { |
1173 | const struct cfg80211_bss_ies *ies; | ||
1185 | struct iw_event iwe; | 1174 | struct iw_event iwe; |
1175 | const u8 *ie; | ||
1186 | u8 *buf, *cfg, *p; | 1176 | u8 *buf, *cfg, *p; |
1187 | u8 *ie = bss->pub.information_elements; | 1177 | int rem, i, sig; |
1188 | int rem = bss->pub.len_information_elements, i, sig; | ||
1189 | bool ismesh = false; | 1178 | bool ismesh = false; |
1190 | 1179 | ||
1191 | memset(&iwe, 0, sizeof(iwe)); | 1180 | memset(&iwe, 0, sizeof(iwe)); |
@@ -1250,7 +1239,17 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, | |||
1250 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | 1239 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, |
1251 | &iwe, ""); | 1240 | &iwe, ""); |
1252 | 1241 | ||
1253 | while (rem >= 2) { | 1242 | rcu_read_lock(); |
1243 | ies = rcu_dereference(bss->pub.ies); | ||
1244 | if (ies) { | ||
1245 | rem = ies->len; | ||
1246 | ie = ies->data; | ||
1247 | } else { | ||
1248 | rem = 0; | ||
1249 | ie = NULL; | ||
1250 | } | ||
1251 | |||
1252 | while (ies && rem >= 2) { | ||
1254 | /* invalid data */ | 1253 | /* invalid data */ |
1255 | if (ie[1] > rem - 2) | 1254 | if (ie[1] > rem - 2) |
1256 | break; | 1255 | break; |
@@ -1262,7 +1261,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, | |||
1262 | iwe.u.data.length = ie[1]; | 1261 | iwe.u.data.length = ie[1]; |
1263 | iwe.u.data.flags = 1; | 1262 | iwe.u.data.flags = 1; |
1264 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | 1263 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, |
1265 | &iwe, ie + 2); | 1264 | &iwe, (u8 *)ie + 2); |
1266 | break; | 1265 | break; |
1267 | case WLAN_EID_MESH_ID: | 1266 | case WLAN_EID_MESH_ID: |
1268 | memset(&iwe, 0, sizeof(iwe)); | 1267 | memset(&iwe, 0, sizeof(iwe)); |
@@ -1270,7 +1269,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, | |||
1270 | iwe.u.data.length = ie[1]; | 1269 | iwe.u.data.length = ie[1]; |
1271 | iwe.u.data.flags = 1; | 1270 | iwe.u.data.flags = 1; |
1272 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | 1271 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, |
1273 | &iwe, ie + 2); | 1272 | &iwe, (u8 *)ie + 2); |
1274 | break; | 1273 | break; |
1275 | case WLAN_EID_MESH_CONFIG: | 1274 | case WLAN_EID_MESH_CONFIG: |
1276 | ismesh = true; | 1275 | ismesh = true; |
@@ -1279,7 +1278,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, | |||
1279 | buf = kmalloc(50, GFP_ATOMIC); | 1278 | buf = kmalloc(50, GFP_ATOMIC); |
1280 | if (!buf) | 1279 | if (!buf) |
1281 | break; | 1280 | break; |
1282 | cfg = ie + 2; | 1281 | cfg = (u8 *)ie + 2; |
1283 | memset(&iwe, 0, sizeof(iwe)); | 1282 | memset(&iwe, 0, sizeof(iwe)); |
1284 | iwe.cmd = IWEVCUSTOM; | 1283 | iwe.cmd = IWEVCUSTOM; |
1285 | sprintf(buf, "Mesh Network Path Selection Protocol ID: " | 1284 | sprintf(buf, "Mesh Network Path Selection Protocol ID: " |
@@ -1377,7 +1376,8 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, | |||
1377 | kfree(buf); | 1376 | kfree(buf); |
1378 | } | 1377 | } |
1379 | 1378 | ||
1380 | ieee80211_scan_add_ies(info, &bss->pub, ¤t_ev, end_buf); | 1379 | ieee80211_scan_add_ies(info, ies, ¤t_ev, end_buf); |
1380 | rcu_read_unlock(); | ||
1381 | 1381 | ||
1382 | return current_ev; | 1382 | return current_ev; |
1383 | } | 1383 | } |
diff --git a/net/wireless/sme.c b/net/wireless/sme.c index c7490027237..f2431e41a37 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c | |||
@@ -417,7 +417,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
417 | struct cfg80211_bss *bss) | 417 | struct cfg80211_bss *bss) |
418 | { | 418 | { |
419 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 419 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
420 | u8 *country_ie; | 420 | const u8 *country_ie; |
421 | #ifdef CONFIG_CFG80211_WEXT | 421 | #ifdef CONFIG_CFG80211_WEXT |
422 | union iwreq_data wrqu; | 422 | union iwreq_data wrqu; |
423 | #endif | 423 | #endif |
@@ -501,7 +501,15 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
501 | wdev->sme_state = CFG80211_SME_CONNECTED; | 501 | wdev->sme_state = CFG80211_SME_CONNECTED; |
502 | cfg80211_upload_connect_keys(wdev); | 502 | cfg80211_upload_connect_keys(wdev); |
503 | 503 | ||
504 | country_ie = (u8 *) ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); | 504 | rcu_read_lock(); |
505 | country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); | ||
506 | if (!country_ie) { | ||
507 | rcu_read_unlock(); | ||
508 | return; | ||
509 | } | ||
510 | |||
511 | country_ie = kmemdup(country_ie, 2 + country_ie[1], GFP_ATOMIC); | ||
512 | rcu_read_unlock(); | ||
505 | 513 | ||
506 | if (!country_ie) | 514 | if (!country_ie) |
507 | return; | 515 | return; |
@@ -515,6 +523,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
515 | bss->channel->band, | 523 | bss->channel->band, |
516 | country_ie + 2, | 524 | country_ie + 2, |
517 | country_ie[1]); | 525 | country_ie[1]); |
526 | kfree(country_ie); | ||
518 | } | 527 | } |
519 | 528 | ||
520 | void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | 529 | void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, |
diff --git a/net/wireless/util.c b/net/wireless/util.c index 3cce6e48621..16d76a807c2 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
@@ -688,10 +688,13 @@ EXPORT_SYMBOL(cfg80211_classify8021d); | |||
688 | 688 | ||
689 | const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) | 689 | const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) |
690 | { | 690 | { |
691 | if (bss->information_elements == NULL) | 691 | const struct cfg80211_bss_ies *ies; |
692 | |||
693 | ies = rcu_dereference(bss->ies); | ||
694 | if (!ies) | ||
692 | return NULL; | 695 | return NULL; |
693 | return cfg80211_find_ie(ie, bss->information_elements, | 696 | |
694 | bss->len_information_elements); | 697 | return cfg80211_find_ie(ie, ies->data, ies->len); |
695 | } | 698 | } |
696 | EXPORT_SYMBOL(ieee80211_bss_get_ie); | 699 | EXPORT_SYMBOL(ieee80211_bss_get_ie); |
697 | 700 | ||
diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 873af63187c..fb9622f6d99 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c | |||
@@ -242,13 +242,17 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev, | |||
242 | 242 | ||
243 | wdev_lock(wdev); | 243 | wdev_lock(wdev); |
244 | if (wdev->current_bss) { | 244 | if (wdev->current_bss) { |
245 | const u8 *ie = ieee80211_bss_get_ie(&wdev->current_bss->pub, | 245 | const u8 *ie; |
246 | WLAN_EID_SSID); | 246 | |
247 | rcu_read_lock(); | ||
248 | ie = ieee80211_bss_get_ie(&wdev->current_bss->pub, | ||
249 | WLAN_EID_SSID); | ||
247 | if (ie) { | 250 | if (ie) { |
248 | data->flags = 1; | 251 | data->flags = 1; |
249 | data->length = ie[1]; | 252 | data->length = ie[1]; |
250 | memcpy(ssid, ie + 2, data->length); | 253 | memcpy(ssid, ie + 2, data->length); |
251 | } | 254 | } |
255 | rcu_read_unlock(); | ||
252 | } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) { | 256 | } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) { |
253 | data->flags = 1; | 257 | data->flags = 1; |
254 | data->length = wdev->wext.connect.ssid_len; | 258 | data->length = wdev->wext.connect.ssid_len; |