diff options
author | David S. Miller <davem@davemloft.net> | 2019-02-22 23:52:23 +0300 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-02-22 23:52:23 +0300 |
commit | 5328b633c9b3c3af38bec8cb70120658c0866e0a (patch) | |
tree | f76581f27784a5f2ce3d3ce7885f1603b2df48bb /net | |
parent | 2fce40a592daa92f1565152cb68d4c4ca7e97d52 (diff) | |
parent | 6c4128f658571b2dc7e01058ad09a8e947bc0159 (diff) | |
download | linux-5328b633c9b3c3af38bec8cb70120658c0866e0a.tar.xz |
Merge tag 'mac80211-next-for-davem-2019-02-22' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Johannes Berg says:
====================
This time we have, of note:
* the massive patch series for multi-BSSID support, I ended up
applying that through a side branch to record some details
* CSA improvements
* HE (802.11ax) updates to Draft 3.3
* strongly typed element iteration/etc. to make such code more
readable - this came up in particular in multi-BSSID
* rhashtable conversion patches from Herbert
Along, as usual, with various fixes and improvements.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv6/ila/ila_xlat.c | 15 | ||||
-rw-r--r-- | net/mac80211/debugfs.c | 4 | ||||
-rw-r--r-- | net/mac80211/debugfs_sta.c | 35 | ||||
-rw-r--r-- | net/mac80211/driver-ops.h | 31 | ||||
-rw-r--r-- | net/mac80211/ibss.c | 8 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 28 | ||||
-rw-r--r-- | net/mac80211/main.c | 13 | ||||
-rw-r--r-- | net/mac80211/mesh.c | 10 | ||||
-rw-r--r-- | net/mac80211/mesh_hwmp.c | 3 | ||||
-rw-r--r-- | net/mac80211/mesh_pathtbl.c | 24 | ||||
-rw-r--r-- | net/mac80211/mesh_plink.c | 4 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 269 | ||||
-rw-r--r-- | net/mac80211/rx.c | 21 | ||||
-rw-r--r-- | net/mac80211/scan.c | 160 | ||||
-rw-r--r-- | net/mac80211/spectmgmt.c | 6 | ||||
-rw-r--r-- | net/mac80211/tdls.c | 6 | ||||
-rw-r--r-- | net/mac80211/trace.h | 46 | ||||
-rw-r--r-- | net/mac80211/util.c | 137 | ||||
-rw-r--r-- | net/netlink/af_netlink.c | 10 | ||||
-rw-r--r-- | net/wireless/core.h | 13 | ||||
-rw-r--r-- | net/wireless/mlme.c | 8 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 82 | ||||
-rw-r--r-- | net/wireless/nl80211.h | 3 | ||||
-rw-r--r-- | net/wireless/pmsr.c | 2 | ||||
-rw-r--r-- | net/wireless/reg.c | 73 | ||||
-rw-r--r-- | net/wireless/reg.h | 2 | ||||
-rw-r--r-- | net/wireless/scan.c | 595 | ||||
-rw-r--r-- | net/wireless/util.c | 6 |
28 files changed, 1252 insertions, 362 deletions
diff --git a/net/ipv6/ila/ila_xlat.c b/net/ipv6/ila/ila_xlat.c index 17c455ff69ff..ae6cd4cef8db 100644 --- a/net/ipv6/ila/ila_xlat.c +++ b/net/ipv6/ila/ila_xlat.c @@ -385,10 +385,7 @@ int ila_xlat_nl_cmd_flush(struct sk_buff *skb, struct genl_info *info) spinlock_t *lock; int ret; - ret = rhashtable_walk_init(&ilan->xlat.rhash_table, &iter, GFP_KERNEL); - if (ret) - goto done; - + rhashtable_walk_enter(&ilan->xlat.rhash_table, &iter); rhashtable_walk_start(&iter); for (;;) { @@ -509,23 +506,17 @@ int ila_xlat_nl_dump_start(struct netlink_callback *cb) struct net *net = sock_net(cb->skb->sk); struct ila_net *ilan = net_generic(net, ila_net_id); struct ila_dump_iter *iter; - int ret; iter = kmalloc(sizeof(*iter), GFP_KERNEL); if (!iter) return -ENOMEM; - ret = rhashtable_walk_init(&ilan->xlat.rhash_table, &iter->rhiter, - GFP_KERNEL); - if (ret) { - kfree(iter); - return ret; - } + rhashtable_walk_enter(&ilan->xlat.rhash_table, &iter->rhiter); iter->skip = 0; cb->args[0] = (long)iter; - return ret; + return 0; } int ila_xlat_nl_dump_done(struct netlink_callback *cb) diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 343ad0a915e4..2d43bc127043 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -3,7 +3,7 @@ * * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright (C) 2018 Intel Corporation + * Copyright (C) 2018 - 2019 Intel Corporation * * GPLv2 * @@ -219,6 +219,8 @@ static const char *hw_flag_names[] = { FLAG(SUPPORTS_VHT_EXT_NSS_BW), FLAG(STA_MMPDU_TXQ), FLAG(TX_STATUS_NO_AMPDU_LEN), + FLAG(SUPPORTS_MULTI_BSSID), + FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID), #undef FLAG }; diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 3aa618dcc58e..8e921281e0d5 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -4,7 +4,7 @@ * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright(c) 2016 Intel Deutschland GmbH - * Copyright (C) 2018 Intel Corporation + * Copyright (C) 2018 - 2019 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -685,6 +685,9 @@ static ssize_t sta_he_capa_read(struct file *file, char __user *userbuf, "SUBCHAN-SELECVITE-TRANSMISSION"); PFLAG(MAC, 5, UL_2x996_TONE_RU, "UL-2x996-TONE-RU"); PFLAG(MAC, 5, OM_CTRL_UL_MU_DATA_DIS_RX, "OM-CTRL-UL-MU-DATA-DIS-RX"); + PFLAG(MAC, 5, HE_DYNAMIC_SM_PS, "HE-DYNAMIC-SM-PS"); + PFLAG(MAC, 5, PUNCTURED_SOUNDING, "PUNCTURED-SOUNDING"); + PFLAG(MAC, 5, HT_VHT_TRIG_FRAME_RX, "HT-VHT-TRIG-FRAME-RX"); cap = hec->he_cap_elem.phy_cap_info; p += scnprintf(p, buf_sz + buf - p, @@ -819,18 +822,18 @@ static ssize_t sta_he_capa_read(struct file *file, char __user *userbuf, PFLAG(PHY, 8, MIDAMBLE_RX_TX_2X_AND_1XLTF, "MIDAMBLE-RX-TX-2X-AND-1XLTF"); - switch (cap[8] & IEEE80211_HE_PHY_CAP8_DCM_MAX_BW_MASK) { - case IEEE80211_HE_PHY_CAP8_DCM_MAX_BW_20MHZ: - PRINT("DDCM-MAX-BW-20MHZ"); + switch (cap[8] & IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_MASK) { + case IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_242: + PRINT("DCM-MAX-RU-242"); break; - case IEEE80211_HE_PHY_CAP8_DCM_MAX_BW_40MHZ: - PRINT("DCM-MAX-BW-40MHZ"); + case IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484: + PRINT("DCM-MAX-RU-484"); break; - case IEEE80211_HE_PHY_CAP8_DCM_MAX_BW_80MHZ: - PRINT("DCM-MAX-BW-80MHZ"); + case IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_996: + PRINT("DCM-MAX-RU-996"); break; - case IEEE80211_HE_PHY_CAP8_DCM_MAX_BW_160_OR_80P80_MHZ: - PRINT("DCM-MAX-BW-160-OR-80P80-MHZ"); + case IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_2x996: + PRINT("DCM-MAX-RU-2x996"); break; } @@ -847,6 +850,18 @@ static ssize_t sta_he_capa_read(struct file *file, char __user *userbuf, PFLAG(PHY, 9, RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB, "RX-FULL-BW-SU-USING-MU-WITH-NON-COMP-SIGB"); + switch (cap[9] & IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_MASK) { + case IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_0US: + PRINT("NOMINAL-PACKET-PADDING-0US"); + break; + case IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_8US: + PRINT("NOMINAL-PACKET-PADDING-8US"); + break; + case IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_16US: + PRINT("NOMINAL-PACKET-PADDING-16US"); + break; + } + #undef PFLAG_RANGE_DEFAULT #undef PFLAG_RANGE #undef PFLAG diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index ba3c07b10cd0..28d022a3eee3 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -2,7 +2,7 @@ /* * Portions of this file * Copyright(c) 2016 Intel Deutschland GmbH -* Copyright (C) 2018 Intel Corporation +* Copyright (C) 2018 - 2019 Intel Corporation */ #ifndef __MAC80211_DRIVER_OPS @@ -1052,6 +1052,35 @@ drv_post_channel_switch(struct ieee80211_sub_if_data *sdata) return ret; } +static inline void +drv_abort_channel_switch(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + + if (!check_sdata_in_driver(sdata)) + return; + + trace_drv_abort_channel_switch(local, sdata); + + if (local->ops->abort_channel_switch) + local->ops->abort_channel_switch(&local->hw, &sdata->vif); +} + +static inline void +drv_channel_switch_rx_beacon(struct ieee80211_sub_if_data *sdata, + struct ieee80211_channel_switch *ch_switch) +{ + struct ieee80211_local *local = sdata->local; + + if (!check_sdata_in_driver(sdata)) + return; + + trace_drv_channel_switch_rx_beacon(local, sdata, ch_switch); + if (local->ops->channel_switch_rx_beacon) + local->ops->channel_switch_rx_beacon(&local->hw, &sdata->vif, + ch_switch); +} + static inline int drv_join_ibss(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 0d704e8d7078..4e4507115cf3 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -8,6 +8,7 @@ * Copyright 2009, Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2018-2019 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -1124,8 +1125,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, ieee80211_update_sta_info(sdata, mgmt, len, rx_status, elems, channel); - bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems, - channel); + bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, channel); if (!bss) return; @@ -1604,7 +1604,7 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata, return; ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, - false, &elems); + false, &elems, mgmt->bssid, NULL); ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); } @@ -1654,7 +1654,7 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ieee802_11_parse_elems( mgmt->u.action.u.chan_switch.variable, - ies_len, true, &elems); + ies_len, true, &elems, mgmt->bssid, NULL); if (elems.parse_error) break; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 056b16bce3b0..e170f986d226 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -4,7 +4,7 @@ * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> * Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2015 Intel Mobile Communications GmbH - * Copyright (C) 2018 Intel Corporation + * Copyright (C) 2018-2019 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -556,6 +556,12 @@ struct ieee80211_if_managed { * get stuck in a downgraded situation and flush takes forever. */ struct delayed_work tx_tspec_wk; + + /* Information elements from the last transmitted (Re)Association + * Request frame. + */ + u8 *assoc_req_ies; + size_t assoc_req_ies_len; }; struct ieee80211_if_ibss { @@ -1447,6 +1453,7 @@ struct ieee80211_csa_ie { u8 ttl; u16 pre_value; u16 reason_code; + u32 max_switch_time; }; /* Parsed Information Elements */ @@ -1487,6 +1494,7 @@ struct ieee802_11_elems { const struct ieee80211_channel_sw_ie *ch_switch_ie; const struct ieee80211_ext_chansw_ie *ext_chansw_ie; const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; + const u8 *max_channel_switch_time; const u8 *country_elem; const u8 *pwr_constr_elem; const u8 *cisco_dtpc_elem; @@ -1495,6 +1503,12 @@ struct ieee802_11_elems { const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie; const struct ieee80211_bss_max_idle_period_ie *max_idle_period_ie; + const struct ieee80211_multiple_bssid_configuration *mbssid_config_ie; + const struct ieee80211_bssid_index *bssid_index; + const u8 *nontransmitted_bssid_profile; + u8 max_bssid_indicator; + u8 dtim_count; + u8 dtim_period; /* length of them, respectively */ u8 ext_capab_len; @@ -1513,6 +1527,7 @@ struct ieee802_11_elems { u8 prep_len; u8 perr_len; u8 country_elem_len; + u8 bssid_index_len; /* whether a parse error occurred while retrieving these elements */ bool parse_error; @@ -1672,7 +1687,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local, struct ieee80211_rx_status *rx_status, struct ieee80211_mgmt *mgmt, size_t len, - struct ieee802_11_elems *elems, struct ieee80211_channel *channel); void ieee80211_rx_bss_put(struct ieee80211_local *local, struct ieee80211_bss *bss); @@ -1956,12 +1970,16 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, struct ieee802_11_elems *elems, - u64 filter, u32 crc); + u64 filter, u32 crc, u8 *transmitter_bssid, + u8 *bss_bssid); static inline void ieee802_11_parse_elems(const u8 *start, size_t len, bool action, - struct ieee802_11_elems *elems) + struct ieee802_11_elems *elems, + u8 *transmitter_bssid, + u8 *bss_bssid) { - ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0); + ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0, + transmitter_bssid, bss_bssid); } diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 71005b6dfcd1..5055aeba5c5a 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -4,7 +4,7 @@ * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018 Intel Corporation + * Copyright (C) 2018 - 2019 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -1112,6 +1112,17 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (ieee80211_hw_check(&local->hw, CHANCTX_STA_CSA)) local->ext_capa[0] |= WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING; + /* mac80211 supports multi BSSID, if the driver supports it */ + if (ieee80211_hw_check(&local->hw, SUPPORTS_MULTI_BSSID)) { + local->hw.wiphy->support_mbssid = true; + if (ieee80211_hw_check(&local->hw, + SUPPORTS_ONLY_HE_MULTI_BSSID)) + local->hw.wiphy->support_only_he_mbssid = true; + else + local->ext_capa[2] |= + WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT; + } + local->hw.wiphy->max_num_csa_counters = IEEE80211_MAX_CSA_COUNTERS_NUM; result = wiphy_register(local->hw.wiphy); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index c90452aa0c42..766e5e5bab8a 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2008, 2009 open80211s Ltd. - * Copyright (C) 2018 Intel Corporation + * Copyright (C) 2018 - 2019 Intel Corporation * Authors: Luis Carlos Cobo <luisca@cozybit.com> * Javier Cardona <javier@cozybit.com> * @@ -1106,7 +1106,8 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata, if (baselen > len) return; - ieee802_11_parse_elems(pos, len - baselen, false, &elems); + ieee802_11_parse_elems(pos, len - baselen, false, &elems, mgmt->bssid, + NULL); if (!elems.mesh_id) return; @@ -1170,7 +1171,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, return; ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, - false, &elems); + false, &elems, mgmt->bssid, NULL); /* ignore non-mesh or secure / unsecure mismatch */ if ((!elems.mesh_id || !elems.mesh_config) || @@ -1306,7 +1307,8 @@ static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata, pos = mgmt->u.action.u.chan_switch.variable; baselen = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch.variable); - ieee802_11_parse_elems(pos, len - baselen, true, &elems); + ieee802_11_parse_elems(pos, len - baselen, true, &elems, + mgmt->bssid, NULL); ifmsh->chsw_ttl = elems.mesh_chansw_params_ie->mesh_ttl; if (!--ifmsh->chsw_ttl) diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index e00284afdda5..f7517668e77a 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2008, 2009 open80211s Ltd. + * Copyright (C) 2019 Intel Corporation * Author: Luis Carlos Cobo <luisca@cozybit.com> * * This program is free software; you can redistribute it and/or modify @@ -926,7 +927,7 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt; ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable, - len - baselen, false, &elems); + len - baselen, false, &elems, mgmt->bssid, NULL); if (elems.preq) { if (elems.preq_len != 37) diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 88a6d5e18ccc..95eb5064fa91 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -404,7 +404,6 @@ struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata, { struct mesh_table *tbl; struct mesh_path *mpath, *new_mpath; - int ret; if (ether_addr_equal(dst, sdata->vif.addr)) /* never add ourselves as neighbours */ @@ -422,25 +421,18 @@ struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata, tbl = sdata->u.mesh.mesh_paths; spin_lock_bh(&tbl->walk_lock); - do { - ret = rhashtable_lookup_insert_fast(&tbl->rhead, - &new_mpath->rhash, - mesh_rht_params); - - if (ret == -EEXIST) - mpath = rhashtable_lookup_fast(&tbl->rhead, - dst, - mesh_rht_params); - else if (!ret) - hlist_add_head(&new_mpath->walk_list, &tbl->walk_head); - } while (unlikely(ret == -EEXIST && !mpath)); + mpath = rhashtable_lookup_get_insert_fast(&tbl->rhead, + &new_mpath->rhash, + mesh_rht_params); + if (!mpath) + hlist_add_head(&new_mpath->walk_list, &tbl->walk_head); spin_unlock_bh(&tbl->walk_lock); - if (ret) { + if (mpath) { kfree(new_mpath); - if (ret != -EEXIST) - return ERR_PTR(ret); + if (IS_ERR(mpath)) + return mpath; new_mpath = mpath; } diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 33055c8ed37e..8afd0ece94c9 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2008, 2009 open80211s Ltd. + * Copyright (C) 2019 Intel Corporation * Author: Luis Carlos Cobo <luisca@cozybit.com> * * This program is free software; you can redistribute it and/or modify @@ -1214,6 +1215,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, if (baselen > len) return; } - ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems); + ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems, + mgmt->bssid, NULL); mesh_process_plink_frame(sdata, mgmt, &elems, rx_status); } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 687821567287..2dbcf5d5512e 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -7,7 +7,7 @@ * Copyright 2007, Michael Wu <flamingice@sourmilk.net> * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015 - 2017 Intel Deutschland GmbH - * Copyright (C) 2018 Intel Corporation + * Copyright (C) 2018 - 2019 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -644,7 +644,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - u8 *pos, qos_info; + u8 *pos, qos_info, *ie_start; size_t offset = 0, noffset; int i, count, rates_len, supp_rates_len, shift; u16 capab; @@ -752,6 +752,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) /* SSID */ pos = skb_put(skb, 2 + assoc_data->ssid_len); + ie_start = pos; *pos++ = WLAN_EID_SSID; *pos++ = assoc_data->ssid_len; memcpy(pos, assoc_data->ssid, assoc_data->ssid_len); @@ -813,6 +814,21 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) } } + /* Set MBSSID support for HE AP if needed */ + if (ieee80211_hw_check(&local->hw, SUPPORTS_ONLY_HE_MULTI_BSSID) && + !(ifmgd->flags & IEEE80211_STA_DISABLE_HE) && assoc_data->ie_len) { + struct element *elem; + + /* we know it's writable, cast away the const */ + elem = (void *)cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY, + assoc_data->ie, + assoc_data->ie_len); + + /* We can probably assume both always true */ + if (elem && elem->datalen >= 3) + elem->data[2] |= WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT; + } + /* if present, add any custom IEs that go before HT */ if (assoc_data->ie_len) { static const u8 before_ht[] = { @@ -961,6 +977,11 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) return; } + pos = skb_tail_pointer(skb); + kfree(ifmgd->assoc_req_ies); + ifmgd->assoc_req_ies = kmemdup(ie_start, pos - ie_start, GFP_ATOMIC); + ifmgd->assoc_req_ies_len = pos - ie_start; + drv_mgd_prepare_tx(local, sdata, 0); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; @@ -1238,6 +1259,32 @@ static void ieee80211_chswitch_timer(struct timer_list *t) } static void +ieee80211_sta_abort_chanswitch(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + + if (!local->ops->abort_channel_switch) + return; + + mutex_lock(&local->mtx); + + mutex_lock(&local->chanctx_mtx); + ieee80211_vif_unreserve_chanctx(sdata); + mutex_unlock(&local->chanctx_mtx); + + if (sdata->csa_block_tx) + ieee80211_wake_vif_queues(local, sdata, + IEEE80211_QUEUE_STOP_REASON_CSA); + + sdata->csa_block_tx = false; + sdata->vif.csa_active = false; + + mutex_unlock(&local->mtx); + + drv_abort_channel_switch(sdata); +} + +static void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, u64 timestamp, u32 device_timestamp, struct ieee802_11_elems *elems, @@ -1261,19 +1308,36 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, if (local->scanning) return; - /* disregard subsequent announcements if we are already processing */ - if (sdata->vif.csa_active) - return; - current_band = cbss->channel->band; res = ieee80211_parse_ch_switch_ie(sdata, elems, current_band, ifmgd->flags, ifmgd->associated->bssid, &csa_ie); - if (res < 0) + + if (!res) { + ch_switch.timestamp = timestamp; + ch_switch.device_timestamp = device_timestamp; + ch_switch.block_tx = beacon ? csa_ie.mode : 0; + ch_switch.chandef = csa_ie.chandef; + ch_switch.count = csa_ie.count; + ch_switch.delay = csa_ie.max_switch_time; + } + + if (res < 0) { ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work); - if (res) return; + } + + if (beacon && sdata->vif.csa_active && !ifmgd->csa_waiting_bcn) { + if (res) + ieee80211_sta_abort_chanswitch(sdata); + else + drv_channel_switch_rx_beacon(sdata, &ch_switch); + return; + } else if (sdata->vif.csa_active || res) { + /* disregard subsequent announcements if already processing */ + return; + } if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chandef, IEEE80211_CHAN_DISABLED)) { @@ -1289,7 +1353,8 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, } if (cfg80211_chandef_identical(&csa_ie.chandef, - &sdata->vif.bss_conf.chandef)) { + &sdata->vif.bss_conf.chandef) && + (!csa_ie.mode || !beacon)) { if (ifmgd->csa_ignored_same_chan) return; sdata_info(sdata, @@ -1326,12 +1391,6 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, goto drop_connection; } - ch_switch.timestamp = timestamp; - ch_switch.device_timestamp = device_timestamp; - ch_switch.block_tx = csa_ie.mode; - ch_switch.chandef = csa_ie.chandef; - ch_switch.count = csa_ie.count; - if (drv_pre_channel_switch(sdata, &ch_switch)) { sdata_info(sdata, "preparing for channel switch failed, disconnecting\n"); @@ -1350,7 +1409,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, sdata->vif.csa_active = true; sdata->csa_chandef = csa_ie.chandef; - sdata->csa_block_tx = csa_ie.mode; + sdata->csa_block_tx = ch_switch.block_tx; ifmgd->csa_ignored_same_chan = false; if (sdata->csa_block_tx) @@ -1384,7 +1443,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, * reset when the disconnection worker runs. */ sdata->vif.csa_active = true; - sdata->csa_block_tx = csa_ie.mode; + sdata->csa_block_tx = ch_switch.block_tx; ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work); mutex_unlock(&local->chanctx_mtx); @@ -2762,7 +2821,8 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, u32 tx_flags = 0; pos = mgmt->u.auth.variable; - ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems); + ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, &elems, + mgmt->bssid, auth_data->bss->bssid); if (!elems.challenge) return; auth_data->expected_transaction = 4; @@ -3130,7 +3190,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, } pos = mgmt->u.assoc_resp.variable; - ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems); + ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, &elems, + mgmt->bssid, assoc_data->bss->bssid); if (!elems.supp_rates) { sdata_info(sdata, "no SuppRates element in AssocResp\n"); @@ -3167,7 +3228,9 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, return false; ieee802_11_parse_elems(bss_ies->data, bss_ies->len, - false, &bss_elems); + false, &bss_elems, + mgmt->bssid, + assoc_data->bss->bssid); if (assoc_data->wmm && !elems.wmm_param && bss_elems.wmm_param) { elems.wmm_param = bss_elems.wmm_param; @@ -3304,6 +3367,14 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, /* TODO: OPEN: what happens if BSS color disable is set? */ } + if (cbss->transmitted_bss) { + bss_conf->nontransmitted = true; + ether_addr_copy(bss_conf->transmitter_bssid, + cbss->transmitted_bss->bssid); + bss_conf->bssid_indicator = cbss->max_bssid_indicator; + bss_conf->bssid_index = cbss->bssid_index; + } + /* * Some APs, e.g. Netgear WNDR3700, report invalid HT operation data * in their association response, so ignore that data for our own @@ -3464,7 +3535,8 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, return; pos = mgmt->u.assoc_resp.variable; - ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems); + ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, &elems, + mgmt->bssid, assoc_data->bss->bssid); if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && elems.timeout_int && @@ -3516,13 +3588,13 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, uapsd_queues |= ieee80211_ac_to_qos_mask[ac]; } - cfg80211_rx_assoc_resp(sdata->dev, bss, (u8 *)mgmt, len, uapsd_queues); + cfg80211_rx_assoc_resp(sdata->dev, bss, (u8 *)mgmt, len, uapsd_queues, + ifmgd->assoc_req_ies, ifmgd->assoc_req_ies_len); } static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, - struct ieee80211_rx_status *rx_status, - struct ieee802_11_elems *elems) + struct ieee80211_rx_status *rx_status) { struct ieee80211_local *local = sdata->local; struct ieee80211_bss *bss; @@ -3534,8 +3606,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, if (!channel) return; - bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems, - channel); + bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, channel); if (bss) { sdata->vif.bss_conf.beacon_rate = bss->beacon_rate; ieee80211_rx_bss_put(local, bss); @@ -3550,7 +3621,6 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_managed *ifmgd; struct ieee80211_rx_status *rx_status = (void *) skb->cb; size_t baselen, len = skb->len; - struct ieee802_11_elems elems; ifmgd = &sdata->u.mgd; @@ -3563,10 +3633,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, if (baselen > len) return; - ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, - false, &elems); - - ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); + ieee80211_rx_bss_info(sdata, mgmt, len, rx_status); if (ifmgd->associated && ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) @@ -3693,6 +3760,16 @@ static void ieee80211_handle_beacon_sig(struct ieee80211_sub_if_data *sdata, } } +static bool ieee80211_rx_our_beacon(const u8 *tx_bssid, + struct cfg80211_bss *bss) +{ + if (ether_addr_equal(tx_bssid, bss->bssid)) + return true; + if (!bss->transmitted_bss) + return false; + return ether_addr_equal(tx_bssid, bss->transmitted_bss->bssid); +} + static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status) @@ -3734,15 +3811,16 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon && - ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) { + ieee80211_rx_our_beacon(mgmt->bssid, ifmgd->assoc_data->bss)) { ieee802_11_parse_elems(mgmt->u.beacon.variable, - len - baselen, false, &elems); + len - baselen, false, &elems, + mgmt->bssid, + ifmgd->assoc_data->bss->bssid); - ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); - if (elems.tim && !elems.parse_error) { - const struct ieee80211_tim_ie *tim_ie = elems.tim; - ifmgd->dtim_period = tim_ie->dtim_period; - } + ieee80211_rx_bss_info(sdata, mgmt, len, rx_status); + + if (elems.dtim_period) + ifmgd->dtim_period = elems.dtim_period; ifmgd->have_beacon = true; ifmgd->assoc_data->need_beacon = false; if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) { @@ -3750,12 +3828,17 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, le64_to_cpu(mgmt->u.beacon.timestamp); sdata->vif.bss_conf.sync_device_ts = rx_status->device_timestamp; - if (elems.tim) - sdata->vif.bss_conf.sync_dtim_count = - elems.tim->dtim_count; - else - sdata->vif.bss_conf.sync_dtim_count = 0; + sdata->vif.bss_conf.sync_dtim_count = elems.dtim_count; } + + if (elems.mbssid_config_ie) + bss_conf->profile_periodicity = + elems.mbssid_config_ie->profile_periodicity; + + if (elems.ext_capab_len >= 11 && + (elems.ext_capab[10] & WLAN_EXT_CAPA11_EMA_SUPPORT)) + bss_conf->ema_ap = true; + /* continue assoc process */ ifmgd->assoc_data->timeout = jiffies; ifmgd->assoc_data->timeout_started = true; @@ -3764,7 +3847,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, } if (!ifmgd->associated || - !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) + !ieee80211_rx_our_beacon(mgmt->bssid, ifmgd->associated)) return; bssid = ifmgd->associated->bssid; @@ -3787,7 +3870,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4); ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable, len - baselen, false, &elems, - care_about_ies, ncrc); + care_about_ies, ncrc, + mgmt->bssid, bssid); if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) && ieee80211_check_tim(elems.tim, elems.tim_len, ifmgd->aid)) { @@ -3859,11 +3943,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, le64_to_cpu(mgmt->u.beacon.timestamp); sdata->vif.bss_conf.sync_device_ts = rx_status->device_timestamp; - if (elems.tim) - sdata->vif.bss_conf.sync_dtim_count = - elems.tim->dtim_count; - else - sdata->vif.bss_conf.sync_dtim_count = 0; + sdata->vif.bss_conf.sync_dtim_count = elems.dtim_count; } if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid) @@ -3871,7 +3951,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ifmgd->beacon_crc = ncrc; ifmgd->beacon_crc_valid = true; - ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); + ieee80211_rx_bss_info(sdata, mgmt, len, rx_status); ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, rx_status->device_timestamp, @@ -3889,10 +3969,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, */ if (!ifmgd->have_beacon) { /* a few bogus AP send dtim_period = 0 or no TIM IE */ - if (elems.tim) - bss_conf->dtim_period = elems.tim->dtim_period ?: 1; - else - bss_conf->dtim_period = 1; + bss_conf->dtim_period = elems.dtim_period ?: 1; changed |= BSS_CHANGED_BEACON_INFO; ifmgd->have_beacon = true; @@ -3992,9 +4069,10 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, if (ies_len < 0) break; + /* CSA IE cannot be overridden, no need for BSSID */ ieee802_11_parse_elems( mgmt->u.action.u.chan_switch.variable, - ies_len, true, &elems); + ies_len, true, &elems, mgmt->bssid, NULL); if (elems.parse_error) break; @@ -4011,9 +4089,13 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, if (ies_len < 0) break; + /* + * extended CSA IE can't be overridden, no need for + * BSSID + */ ieee802_11_parse_elems( mgmt->u.action.u.ext_chan_switch.variable, - ies_len, true, &elems); + ies_len, true, &elems, mgmt->bssid, NULL); if (elems.parse_error) break; @@ -4754,6 +4836,40 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, return ret; } +static bool ieee80211_get_dtim(const struct cfg80211_bss_ies *ies, + u8 *dtim_count, u8 *dtim_period) +{ + const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM, ies->data, ies->len); + const u8 *idx_ie = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX, ies->data, + ies->len); + const struct ieee80211_tim_ie *tim = NULL; + const struct ieee80211_bssid_index *idx; + bool valid = tim_ie && tim_ie[1] >= 2; + + if (valid) + tim = (void *)(tim_ie + 2); + + if (dtim_count) + *dtim_count = valid ? tim->dtim_count : 0; + + if (dtim_period) + *dtim_period = valid ? tim->dtim_period : 0; + + /* Check if value is overridden by non-transmitted profile */ + if (!idx_ie || idx_ie[1] < 3) + return valid; + + idx = (void *)(idx_ie + 2); + + if (dtim_count) + *dtim_count = idx->dtim_count; + + if (dtim_period) + *dtim_period = idx->dtim_period; + + return true; +} + static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct cfg80211_bss *cbss, bool assoc, bool override) @@ -4845,17 +4961,13 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, rcu_read_lock(); ies = rcu_dereference(cbss->beacon_ies); if (ies) { - const u8 *tim_ie; - sdata->vif.bss_conf.sync_tsf = ies->tsf; sdata->vif.bss_conf.sync_device_ts = bss->device_ts_beacon; - tim_ie = cfg80211_find_ie(WLAN_EID_TIM, - ies->data, ies->len); - if (tim_ie && tim_ie[1] >= 2) - sdata->vif.bss_conf.sync_dtim_count = tim_ie[2]; - else - sdata->vif.bss_conf.sync_dtim_count = 0; + + ieee80211_get_dtim(ies, + &sdata->vif.bss_conf.sync_dtim_count, + NULL); } else if (!ieee80211_hw_check(&sdata->local->hw, TIMING_BEACON_ONLY)) { ies = rcu_dereference(cbss->proberesp_ies); @@ -5325,17 +5437,12 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, assoc_data->timeout_started = true; assoc_data->need_beacon = true; } else if (beacon_ies) { - const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM, - beacon_ies->data, - beacon_ies->len); + const u8 *ie; u8 dtim_count = 0; - if (tim_ie && tim_ie[1] >= sizeof(struct ieee80211_tim_ie)) { - const struct ieee80211_tim_ie *tim; - tim = (void *)(tim_ie + 2); - ifmgd->dtim_period = tim->dtim_period; - dtim_count = tim->dtim_count; - } + ieee80211_get_dtim(beacon_ies, &dtim_count, + &ifmgd->dtim_period); + ifmgd->have_beacon = true; assoc_data->timeout = jiffies; assoc_data->timeout_started = true; @@ -5346,6 +5453,17 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, bss->device_ts_beacon; sdata->vif.bss_conf.sync_dtim_count = dtim_count; } + + ie = cfg80211_find_ext_ie(WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION, + beacon_ies->data, beacon_ies->len); + if (ie && ie[1] >= 3) + sdata->vif.bss_conf.profile_periodicity = ie[4]; + + ie = cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, + beacon_ies->data, beacon_ies->len); + if (ie && ie[1] >= 11 && + (ie[10] & WLAN_EXT_CAPA11_EMA_SUPPORT)) + sdata->vif.bss_conf.ema_ap = true; } else { assoc_data->timeout = jiffies; assoc_data->timeout_started = true; @@ -5503,6 +5621,9 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata) ifmgd->teardown_skb = NULL; ifmgd->orig_teardown_skb = NULL; } + kfree(ifmgd->assoc_req_ies); + ifmgd->assoc_req_ies = NULL; + ifmgd->assoc_req_ies_len = 0; spin_unlock_bh(&ifmgd->teardown_lock); del_timer_sync(&ifmgd->timer); sdata_unlock(sdata); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index bb4d71efb6fb..c97018dd17fe 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -5,7 +5,7 @@ * Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright(c) 2015 - 2017 Intel Deutschland GmbH - * Copyright (C) 2018 Intel Corporation + * Copyright (C) 2018-2019 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -208,7 +208,24 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local, } if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) { - struct ieee80211_vendor_radiotap *rtap = (void *)skb->data; + struct ieee80211_vendor_radiotap *rtap; + int vendor_data_offset = 0; + + /* + * The position to look at depends on the existence (or non- + * existence) of other elements, so take that into account... + */ + if (status->flag & RX_FLAG_RADIOTAP_HE) + vendor_data_offset += + sizeof(struct ieee80211_radiotap_he); + if (status->flag & RX_FLAG_RADIOTAP_HE_MU) + vendor_data_offset += + sizeof(struct ieee80211_radiotap_he_mu); + if (status->flag & RX_FLAG_RADIOTAP_LSIG) + vendor_data_offset += + sizeof(struct ieee80211_radiotap_lsig); + + rtap = (void *)&skb->data[vendor_data_offset]; /* alignment for fixed 6-byte vendor data header */ len = ALIGN(len, 2); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 95413413f98c..0cf066700623 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -8,6 +8,7 @@ * Copyright 2007, Michael Wu <flamingice@sourmilk.net> * Copyright 2013-2015 Intel Mobile Communications GmbH * Copyright 2016-2017 Intel Deutschland GmbH + * Copyright (C) 2018-2019 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -57,62 +58,14 @@ static bool is_uapsd_supported(struct ieee802_11_elems *elems) return qos_info & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD; } -struct ieee80211_bss * -ieee80211_bss_info_update(struct ieee80211_local *local, - struct ieee80211_rx_status *rx_status, - struct ieee80211_mgmt *mgmt, size_t len, - struct ieee802_11_elems *elems, - struct ieee80211_channel *channel) +static void +ieee80211_update_bss_from_elems(struct ieee80211_local *local, + struct ieee80211_bss *bss, + struct ieee802_11_elems *elems, + struct ieee80211_rx_status *rx_status, + bool beacon) { - bool beacon = ieee80211_is_beacon(mgmt->frame_control); - struct cfg80211_bss *cbss; - struct ieee80211_bss *bss; int clen, srlen; - struct cfg80211_inform_bss bss_meta = { - .boottime_ns = rx_status->boottime_ns, - }; - bool signal_valid; - struct ieee80211_sub_if_data *scan_sdata; - - if (rx_status->flag & RX_FLAG_NO_SIGNAL_VAL) - bss_meta.signal = 0; /* invalid signal indication */ - else if (ieee80211_hw_check(&local->hw, SIGNAL_DBM)) - bss_meta.signal = rx_status->signal * 100; - else if (ieee80211_hw_check(&local->hw, SIGNAL_UNSPEC)) - bss_meta.signal = (rx_status->signal * 100) / local->hw.max_signal; - - bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_20; - if (rx_status->bw == RATE_INFO_BW_5) - bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_5; - else if (rx_status->bw == RATE_INFO_BW_10) - bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_10; - - bss_meta.chan = channel; - - rcu_read_lock(); - scan_sdata = rcu_dereference(local->scan_sdata); - if (scan_sdata && scan_sdata->vif.type == NL80211_IFTYPE_STATION && - scan_sdata->vif.bss_conf.assoc && - ieee80211_have_rx_timestamp(rx_status)) { - bss_meta.parent_tsf = - ieee80211_calculate_rx_timestamp(local, rx_status, - len + FCS_LEN, 24); - ether_addr_copy(bss_meta.parent_bssid, - scan_sdata->vif.bss_conf.bssid); - } - rcu_read_unlock(); - - cbss = cfg80211_inform_bss_frame_data(local->hw.wiphy, &bss_meta, - mgmt, len, GFP_ATOMIC); - if (!cbss) - return NULL; - /* In case the signal is invalid update the status */ - signal_valid = abs(channel->center_freq - cbss->channel->center_freq) - <= local->hw.wiphy->max_adj_channel_rssi_comp; - if (!signal_valid) - rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; - - bss = (void *)cbss->priv; if (beacon) bss->device_ts_beacon = rx_status->device_timestamp; @@ -182,6 +135,89 @@ ieee80211_bss_info_update(struct ieee80211_local *local, bss->beacon_rate = &sband->bitrates[rx_status->rate_idx]; } +} + +struct ieee80211_bss * +ieee80211_bss_info_update(struct ieee80211_local *local, + struct ieee80211_rx_status *rx_status, + struct ieee80211_mgmt *mgmt, size_t len, + struct ieee80211_channel *channel) +{ + bool beacon = ieee80211_is_beacon(mgmt->frame_control); + struct cfg80211_bss *cbss, *non_tx_cbss; + struct ieee80211_bss *bss, *non_tx_bss; + struct cfg80211_inform_bss bss_meta = { + .boottime_ns = rx_status->boottime_ns, + }; + bool signal_valid; + struct ieee80211_sub_if_data *scan_sdata; + struct ieee802_11_elems elems; + size_t baselen; + u8 *elements; + + if (rx_status->flag & RX_FLAG_NO_SIGNAL_VAL) + bss_meta.signal = 0; /* invalid signal indication */ + else if (ieee80211_hw_check(&local->hw, SIGNAL_DBM)) + bss_meta.signal = rx_status->signal * 100; + else if (ieee80211_hw_check(&local->hw, SIGNAL_UNSPEC)) + bss_meta.signal = (rx_status->signal * 100) / local->hw.max_signal; + + bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_20; + if (rx_status->bw == RATE_INFO_BW_5) + bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_5; + else if (rx_status->bw == RATE_INFO_BW_10) + bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_10; + + bss_meta.chan = channel; + + rcu_read_lock(); + scan_sdata = rcu_dereference(local->scan_sdata); + if (scan_sdata && scan_sdata->vif.type == NL80211_IFTYPE_STATION && + scan_sdata->vif.bss_conf.assoc && + ieee80211_have_rx_timestamp(rx_status)) { + bss_meta.parent_tsf = + ieee80211_calculate_rx_timestamp(local, rx_status, + len + FCS_LEN, 24); + ether_addr_copy(bss_meta.parent_bssid, + scan_sdata->vif.bss_conf.bssid); + } + rcu_read_unlock(); + + cbss = cfg80211_inform_bss_frame_data(local->hw.wiphy, &bss_meta, + mgmt, len, GFP_ATOMIC); + if (!cbss) + return NULL; + + if (ieee80211_is_probe_resp(mgmt->frame_control)) { + elements = mgmt->u.probe_resp.variable; + baselen = offsetof(struct ieee80211_mgmt, + u.probe_resp.variable); + } else { + baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable); + elements = mgmt->u.beacon.variable; + } + + if (baselen > len) + return NULL; + + ieee802_11_parse_elems(elements, len - baselen, false, &elems, + mgmt->bssid, cbss->bssid); + + /* In case the signal is invalid update the status */ + signal_valid = abs(channel->center_freq - cbss->channel->center_freq) + <= local->hw.wiphy->max_adj_channel_rssi_comp; + if (!signal_valid) + rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; + + bss = (void *)cbss->priv; + ieee80211_update_bss_from_elems(local, bss, &elems, rx_status, beacon); + + list_for_each_entry(non_tx_cbss, &cbss->nontrans_list, nontrans_list) { + non_tx_bss = (void *)non_tx_cbss->priv; + + ieee80211_update_bss_from_elems(local, non_tx_bss, &elems, + rx_status, beacon); + } return bss; } @@ -206,10 +242,7 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) struct ieee80211_sub_if_data *sdata1, *sdata2; struct ieee80211_mgmt *mgmt = (void *)skb->data; struct ieee80211_bss *bss; - u8 *elements; struct ieee80211_channel *channel; - size_t baselen; - struct ieee802_11_elems elems; if (skb->len < 24 || (!ieee80211_is_probe_resp(mgmt->frame_control) && @@ -244,26 +277,15 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) !ieee80211_scan_accept_presp(sdata2, sched_scan_req_flags, mgmt->da)) return; - - elements = mgmt->u.probe_resp.variable; - baselen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); - } else { - baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable); - elements = mgmt->u.beacon.variable; } - if (baselen > skb->len) - return; - - ieee802_11_parse_elems(elements, skb->len - baselen, false, &elems); - channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq); if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) return; bss = ieee80211_bss_info_update(local, rx_status, - mgmt, skb->len, &elems, + mgmt, skb->len, channel); if (bss) ieee80211_rx_bss_put(local, bss); diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index 4e4902bdbef8..3c644f14dd59 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -177,6 +177,12 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, csa_ie->chandef = new_vht_chandef; } + if (elems->max_channel_switch_time) + csa_ie->max_switch_time = + (elems->max_channel_switch_time[0] << 0) | + (elems->max_channel_switch_time[1] << 8) | + (elems->max_channel_switch_time[2] << 16); + return 0; } diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 6c647f425e05..d30690d79a58 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -5,6 +5,7 @@ * Copyright 2014, Intel Corporation * Copyright 2014 Intel Mobile Communications GmbH * Copyright 2015 - 2016 Intel Deutschland GmbH + * Copyright (C) 2019 Intel Corporation * * This file is GPLv2 as found in COPYING. */ @@ -1716,7 +1717,8 @@ ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata, } ieee802_11_parse_elems(tf->u.chan_switch_resp.variable, - skb->len - baselen, false, &elems); + skb->len - baselen, false, &elems, + NULL, NULL); if (elems.parse_error) { tdls_dbg(sdata, "Invalid IEs in TDLS channel switch resp\n"); ret = -EINVAL; @@ -1828,7 +1830,7 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata, } ieee802_11_parse_elems(tf->u.chan_switch_req.variable, - skb->len - baselen, false, &elems); + skb->len - baselen, false, &elems, NULL, NULL); if (elems.parse_error) { tdls_dbg(sdata, "Invalid IEs in TDLS channel switch req\n"); return -EINVAL; diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 35ea0dcb55e6..8ba70d26b82e 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Portions of this file -* Copyright(c) 2016 Intel Deutschland GmbH -* Copyright (C) 2018 Intel Corporation +* Copyright(c) 2016-2017 Intel Deutschland GmbH +* Copyright (C) 2018 - 2019 Intel Corporation */ #if !defined(__MAC80211_DRIVER_TRACE) || defined(TRACE_HEADER_MULTI_READ) @@ -2452,6 +2452,48 @@ DEFINE_EVENT(local_sdata_evt, drv_post_channel_switch, TP_ARGS(local, sdata) ); +DEFINE_EVENT(local_sdata_evt, drv_abort_channel_switch, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) +); + +TRACE_EVENT(drv_channel_switch_rx_beacon, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_channel_switch *ch_switch), + + TP_ARGS(local, sdata, ch_switch), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + CHANDEF_ENTRY + __field(u64, timestamp) + __field(u32, device_timestamp) + __field(bool, block_tx) + __field(u8, count) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + CHANDEF_ASSIGN(&ch_switch->chandef) + __entry->timestamp = ch_switch->timestamp; + __entry->device_timestamp = ch_switch->device_timestamp; + __entry->block_tx = ch_switch->block_tx; + __entry->count = ch_switch->count; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT + " received a channel switch beacon to " + CHANDEF_PR_FMT " count:%d block_tx:%d timestamp:%llu", + LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG, __entry->count, + __entry->block_tx, __entry->timestamp + ) +); + TRACE_EVENT(drv_get_txpower, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index ba950ae974fc..4c1655972565 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -891,33 +891,24 @@ void ieee80211_queue_delayed_work(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_queue_delayed_work); -u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, - struct ieee802_11_elems *elems, - u64 filter, u32 crc) +static u32 +_ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, + struct ieee802_11_elems *elems, + u64 filter, u32 crc, u8 *transmitter_bssid, + u8 *bss_bssid) { - size_t left = len; - const u8 *pos = start; + const struct element *elem, *sub; bool calc_crc = filter != 0; DECLARE_BITMAP(seen_elems, 256); const u8 *ie; bitmap_zero(seen_elems, 256); - memset(elems, 0, sizeof(*elems)); - elems->ie_start = start; - elems->total_len = len; - while (left >= 2) { - u8 id, elen; + for_each_element(elem, start, len) { bool elem_parse_failed; - - id = *pos++; - elen = *pos++; - left -= 2; - - if (elen > left) { - elems->parse_error = true; - break; - } + u8 id = elem->id; + u8 elen = elem->datalen; + const u8 *pos = elem->data; switch (id) { case WLAN_EID_SSID: @@ -960,8 +951,6 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, */ if (test_bit(id, seen_elems)) { elems->parse_error = true; - left -= elen; - pos += elen; continue; } break; @@ -1219,6 +1208,57 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, if (elen >= sizeof(*elems->max_idle_period_ie)) elems->max_idle_period_ie = (void *)pos; break; + case WLAN_EID_MULTIPLE_BSSID: + if (!bss_bssid || !transmitter_bssid || elen < 4) + break; + + elems->max_bssid_indicator = pos[0]; + + for_each_element(sub, pos + 1, elen - 1) { + u8 sub_len = sub->datalen; + u8 new_bssid[ETH_ALEN]; + const u8 *index; + + /* + * we only expect the "non-transmitted BSSID + * profile" subelement (subelement id 0) + */ + if (sub->id != 0 || sub->datalen < 4) { + /* not a valid BSS profile */ + continue; + } + + if (sub->data[0] != WLAN_EID_NON_TX_BSSID_CAP || + sub->data[1] != 2) { + /* The first element of the + * Nontransmitted BSSID Profile is not + * the Nontransmitted BSSID Capability + * element. + */ + continue; + } + + /* found a Nontransmitted BSSID Profile */ + index = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX, + sub->data, sub_len); + if (!index || index[1] < 1 || index[2] == 0) { + /* Invalid MBSSID Index element */ + continue; + } + + cfg80211_gen_new_bssid(transmitter_bssid, + pos[0], + index[2], + new_bssid); + if (ether_addr_equal(new_bssid, bss_bssid)) { + elems->nontransmitted_bssid_profile = + (void *)sub; + elems->bssid_index_len = index[1]; + elems->bssid_index = (void *)&index[2]; + break; + } + } + break; case WLAN_EID_EXTENSION: if (pos[0] == WLAN_EID_EXT_HE_MU_EDCA && elen >= (sizeof(*elems->mu_edca_param_set) + 1)) { @@ -1234,6 +1274,14 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, elems->he_operation = (void *)&pos[1]; } else if (pos[0] == WLAN_EID_EXT_UORA && elen >= 1) { elems->uora_element = (void *)&pos[1]; + } else if (pos[0] == + WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME && + elen == 4) { + elems->max_channel_switch_time = pos + 1; + } else if (pos[0] == + WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION && + elen == 3) { + elems->mbssid_config_ie = (void *)&pos[1]; } break; default: @@ -1244,17 +1292,56 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, elems->parse_error = true; else __set_bit(id, seen_elems); - - left -= elen; - pos += elen; } - if (left != 0) + if (!for_each_element_completed(elem, start, len)) elems->parse_error = true; return crc; } +u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, + struct ieee802_11_elems *elems, + u64 filter, u32 crc, u8 *transmitter_bssid, + u8 *bss_bssid) +{ + memset(elems, 0, sizeof(*elems)); + elems->ie_start = start; + elems->total_len = len; + + crc = _ieee802_11_parse_elems_crc(start, len, action, elems, filter, + crc, transmitter_bssid, bss_bssid); + + /* Override with nontransmitted profile, if found */ + if (transmitter_bssid && elems->nontransmitted_bssid_profile) { + const u8 *profile = elems->nontransmitted_bssid_profile; + + _ieee802_11_parse_elems_crc(&profile[2], profile[1], + action, elems, 0, 0, + transmitter_bssid, bss_bssid); + } + + if (elems->tim && !elems->parse_error) { + const struct ieee80211_tim_ie *tim_ie = elems->tim; + + elems->dtim_period = tim_ie->dtim_period; + elems->dtim_count = tim_ie->dtim_count; + } + + /* Override DTIM period and count if needed */ + if (elems->bssid_index && + elems->bssid_index_len >= + offsetofend(struct ieee80211_bssid_index, dtim_period)) + elems->dtim_period = elems->bssid_index->dtim_period; + + if (elems->bssid_index && + elems->bssid_index_len >= + offsetofend(struct ieee80211_bssid_index, dtim_count)) + elems->dtim_count = elems->bssid_index->dtim_count; + + return crc; +} + void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata, struct ieee80211_tx_queue_params *qparam, int ac) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 8fa35df94c07..f28e937320a3 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -2549,15 +2549,7 @@ struct nl_seq_iter { static int netlink_walk_start(struct nl_seq_iter *iter) { - int err; - - err = rhashtable_walk_init(&nl_table[iter->link].hash, &iter->hti, - GFP_KERNEL); - if (err) { - iter->link = MAX_LINKS; - return err; - } - + rhashtable_walk_enter(&nl_table[iter->link].hash, &iter->hti); rhashtable_walk_start(&iter->hti); return 0; diff --git a/net/wireless/core.h b/net/wireless/core.h index f6b40563dc63..84d36ca7a7ab 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -3,7 +3,7 @@ * Wireless configuration interface internals. * * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> - * Copyright (C) 2018 Intel Corporation + * Copyright (C) 2018-2019 Intel Corporation */ #ifndef __NET_WIRELESS_CORE_H #define __NET_WIRELESS_CORE_H @@ -182,12 +182,23 @@ static inline struct cfg80211_internal_bss *bss_from_pub(struct cfg80211_bss *pu static inline void cfg80211_hold_bss(struct cfg80211_internal_bss *bss) { atomic_inc(&bss->hold); + if (bss->pub.transmitted_bss) { + bss = container_of(bss->pub.transmitted_bss, + struct cfg80211_internal_bss, pub); + atomic_inc(&bss->hold); + } } static inline void cfg80211_unhold_bss(struct cfg80211_internal_bss *bss) { int r = atomic_dec_return(&bss->hold); WARN_ON(r < 0); + if (bss->pub.transmitted_bss) { + bss = container_of(bss->pub.transmitted_bss, + struct cfg80211_internal_bss, pub); + r = atomic_dec_return(&bss->hold); + WARN_ON(r < 0); + } } diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 1615e503f8e3..f9462010575f 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -21,7 +21,8 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, - const u8 *buf, size_t len, int uapsd_queues) + const u8 *buf, size_t len, int uapsd_queues, + const u8 *req_ies, size_t req_ies_len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; @@ -33,6 +34,8 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, cr.status = (int)le16_to_cpu(mgmt->u.assoc_resp.status_code); cr.bssid = mgmt->bssid; cr.bss = bss; + cr.req_ie = req_ies; + cr.req_ie_len = req_ies_len; cr.resp_ie = mgmt->u.assoc_resp.variable; cr.resp_ie_len = len - offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); @@ -52,7 +55,8 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, return; } - nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL, uapsd_queues); + nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL, uapsd_queues, + req_ies, req_ies_len); /* update current_bss etc., consumes the bss reference */ __cfg80211_connect_result(dev, &cr, cr.status == WLAN_STATUS_SUCCESS); } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e36437abc45a..25a9e3b5c154 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4,7 +4,7 @@ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018 Intel Corporation + * Copyright (C) 2018-2019 Intel Corporation */ #include <linux/if.h> @@ -203,29 +203,17 @@ cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info) static int validate_ie_attr(const struct nlattr *attr, struct netlink_ext_ack *extack) { - const u8 *pos; - int len; + const u8 *data = nla_data(attr); + unsigned int len = nla_len(attr); + const struct element *elem; - pos = nla_data(attr); - len = nla_len(attr); - - while (len) { - u8 elemlen; - - if (len < 2) - goto error; - len -= 2; - - elemlen = pos[1]; - if (elemlen > len) - goto error; - - len -= elemlen; - pos += 2 + elemlen; + for_each_element(elem, data, len) { + /* nothing */ } - return 0; -error: + if (for_each_element_completed(elem, data, len)) + return 0; + NL_SET_ERR_MSG_ATTR(extack, attr, "malformed information elements"); return -EINVAL; } @@ -9318,6 +9306,7 @@ struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, struct wireless_dev *wdev, enum nl80211_commands cmd, enum nl80211_attrs attr, + unsigned int portid, int vendor_event_idx, int approxlen, gfp_t gfp) { @@ -9341,7 +9330,7 @@ struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, return NULL; } - return __cfg80211_alloc_vendor_skb(rdev, wdev, approxlen, 0, 0, + return __cfg80211_alloc_vendor_skb(rdev, wdev, approxlen, portid, 0, cmd, attr, info, gfp); } EXPORT_SYMBOL(__cfg80211_alloc_event_skb); @@ -9350,6 +9339,7 @@ void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp) { struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; void *hdr = ((void **)skb->cb)[1]; + struct nlmsghdr *nlhdr = nlmsg_hdr(skb); struct nlattr *data = ((void **)skb->cb)[2]; enum nl80211_multicast_groups mcgrp = NL80211_MCGRP_TESTMODE; @@ -9359,11 +9349,16 @@ void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp) nla_nest_end(skb, data); genlmsg_end(skb, hdr); - if (data->nla_type == NL80211_ATTR_VENDOR_DATA) - mcgrp = NL80211_MCGRP_VENDOR; + if (nlhdr->nlmsg_pid) { + genlmsg_unicast(wiphy_net(&rdev->wiphy), skb, + nlhdr->nlmsg_pid); + } else { + if (data->nla_type == NL80211_ATTR_VENDOR_DATA) + mcgrp = NL80211_MCGRP_VENDOR; - genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), skb, 0, - mcgrp, gfp); + genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), + skb, 0, mcgrp, gfp); + } } EXPORT_SYMBOL(__cfg80211_send_event_skb); @@ -12748,6 +12743,17 @@ int cfg80211_vendor_cmd_reply(struct sk_buff *skb) } EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_reply); +unsigned int cfg80211_vendor_cmd_get_sender(struct wiphy *wiphy) +{ + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + + if (WARN_ON(!rdev->cur_cmd_info)) + return 0; + + return rdev->cur_cmd_info->snd_portid; +} +EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_get_sender); + static int nl80211_set_qos_map(struct sk_buff *skb, struct genl_info *info) { @@ -14503,12 +14509,13 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, enum nl80211_commands cmd, gfp_t gfp, - int uapsd_queues) + int uapsd_queues, const u8 *req_ies, + size_t req_ies_len) { struct sk_buff *msg; void *hdr; - msg = nlmsg_new(100 + len, gfp); + msg = nlmsg_new(100 + len + req_ies_len, gfp); if (!msg) return; @@ -14520,7 +14527,9 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || - nla_put(msg, NL80211_ATTR_FRAME, len, buf)) + nla_put(msg, NL80211_ATTR_FRAME, len, buf) || + (req_ies && + nla_put(msg, NL80211_ATTR_REQ_IE, req_ies_len, req_ies))) goto nla_put_failure; if (uapsd_queues >= 0) { @@ -14551,15 +14560,17 @@ void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_AUTHENTICATE, gfp, -1); + NL80211_CMD_AUTHENTICATE, gfp, -1, NULL, 0); } void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, - size_t len, gfp_t gfp, int uapsd_queues) + size_t len, gfp_t gfp, int uapsd_queues, + const u8 *req_ies, size_t req_ies_len) { nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_ASSOCIATE, gfp, uapsd_queues); + NL80211_CMD_ASSOCIATE, gfp, uapsd_queues, + req_ies, req_ies_len); } void nl80211_send_deauth(struct cfg80211_registered_device *rdev, @@ -14567,7 +14578,7 @@ void nl80211_send_deauth(struct cfg80211_registered_device *rdev, size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_DEAUTHENTICATE, gfp, -1); + NL80211_CMD_DEAUTHENTICATE, gfp, -1, NULL, 0); } void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, @@ -14575,7 +14586,7 @@ void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_DISASSOCIATE, gfp, -1); + NL80211_CMD_DISASSOCIATE, gfp, -1, NULL, 0); } void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf, @@ -14596,7 +14607,8 @@ void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf, cmd = NL80211_CMD_UNPROT_DISASSOCIATE; trace_cfg80211_rx_unprot_mlme_mgmt(dev, buf, len); - nl80211_send_mlme_event(rdev, dev, buf, len, cmd, GFP_ATOMIC, -1); + nl80211_send_mlme_event(rdev, dev, buf, len, cmd, GFP_ATOMIC, -1, + NULL, 0); } EXPORT_SYMBOL(cfg80211_rx_unprot_mlme_mgmt); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 531c82dcba6b..a41e94a49a89 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -67,7 +67,8 @@ void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp, - int uapsd_queues); + int uapsd_queues, + const u8 *req_ies, size_t req_ies_len); void nl80211_send_deauth(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp); diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index 0216ab555249..5e2ab01d325c 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -257,7 +257,7 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info) goto out_err; } else { memcpy(req->mac_addr, wdev_address(wdev), ETH_ALEN); - memset(req->mac_addr_mask, 0xff, ETH_ALEN); + eth_broadcast_addr(req->mac_addr_mask); } idx = 0; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index adfa58fa6536..2f1bf91eb226 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -5,7 +5,7 @@ * Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com> * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2017 Intel Deutschland GmbH - * Copyright (C) 2018 Intel Corporation + * Copyright (C) 2018 - 2019 Intel Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -131,7 +131,8 @@ static spinlock_t reg_indoor_lock; /* Used to track the userspace process controlling the indoor setting */ static u32 reg_is_indoor_portid; -static void restore_regulatory_settings(bool reset_user); +static void restore_regulatory_settings(bool reset_user, bool cached); +static void print_regdomain(const struct ieee80211_regdomain *rd); static const struct ieee80211_regdomain *get_cfg80211_regdom(void) { @@ -263,6 +264,7 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom = static char *ieee80211_regdom = "00"; static char user_alpha2[2]; +static const struct ieee80211_regdomain *cfg80211_user_regdom; module_param(ieee80211_regdom, charp, 0444); MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); @@ -445,6 +447,15 @@ reg_copy_regd(const struct ieee80211_regdomain *src_regd) return regd; } +static void cfg80211_save_user_regdom(const struct ieee80211_regdomain *rd) +{ + ASSERT_RTNL(); + + if (!IS_ERR(cfg80211_user_regdom)) + kfree(cfg80211_user_regdom); + cfg80211_user_regdom = reg_copy_regd(rd); +} + struct reg_regdb_apply_request { struct list_head list; const struct ieee80211_regdomain *regdom; @@ -510,7 +521,7 @@ static void crda_timeout_work(struct work_struct *work) pr_debug("Timeout while waiting for CRDA to reply, restoring regulatory settings\n"); rtnl_lock(); reg_crda_timeouts++; - restore_regulatory_settings(true); + restore_regulatory_settings(true, false); rtnl_unlock(); } @@ -1044,7 +1055,7 @@ static void regdb_fw_cb(const struct firmware *fw, void *context) } if (restore) - restore_regulatory_settings(true); + restore_regulatory_settings(true, false); rtnl_unlock(); @@ -3117,7 +3128,7 @@ static void restore_custom_reg_settings(struct wiphy *wiphy) * keep their own regulatory domain on wiphy->regd so that does does * not need to be remembered. */ -static void restore_regulatory_settings(bool reset_user) +static void restore_regulatory_settings(bool reset_user, bool cached) { char alpha2[2]; char world_alpha2[2]; @@ -3176,15 +3187,41 @@ static void restore_regulatory_settings(bool reset_user) restore_custom_reg_settings(&rdev->wiphy); } - regulatory_hint_core(world_alpha2); + if (cached && (!is_an_alpha2(alpha2) || + !IS_ERR_OR_NULL(cfg80211_user_regdom))) { + reset_regdomains(false, cfg80211_world_regdom); + update_all_wiphy_regulatory(NL80211_REGDOM_SET_BY_CORE); + print_regdomain(get_cfg80211_regdom()); + nl80211_send_reg_change_event(&core_request_world); + reg_set_request_processed(); - /* - * This restores the ieee80211_regdom module parameter - * preference or the last user requested regulatory - * settings, user regulatory settings takes precedence. - */ - if (is_an_alpha2(alpha2)) - regulatory_hint_user(alpha2, NL80211_USER_REG_HINT_USER); + if (is_an_alpha2(alpha2) && + !regulatory_hint_user(alpha2, NL80211_USER_REG_HINT_USER)) { + struct regulatory_request *ureq; + + spin_lock(®_requests_lock); + ureq = list_last_entry(®_requests_list, + struct regulatory_request, + list); + list_del(&ureq->list); + spin_unlock(®_requests_lock); + + notify_self_managed_wiphys(ureq); + reg_update_last_request(ureq); + set_regdom(reg_copy_regd(cfg80211_user_regdom), + REGD_SOURCE_CACHED); + } + } else { + regulatory_hint_core(world_alpha2); + + /* + * This restores the ieee80211_regdom module parameter + * preference or the last user requested regulatory + * settings, user regulatory settings takes precedence. + */ + if (is_an_alpha2(alpha2)) + regulatory_hint_user(alpha2, NL80211_USER_REG_HINT_USER); + } spin_lock(®_requests_lock); list_splice_tail_init(&tmp_reg_req_list, ®_requests_list); @@ -3244,7 +3281,7 @@ void regulatory_hint_disconnect(void) } pr_debug("All devices are disconnected, going to restore regulatory settings\n"); - restore_regulatory_settings(false); + restore_regulatory_settings(false, true); } static bool freq_is_chan_12_13_14(u32 freq) @@ -3561,6 +3598,9 @@ int set_regdom(const struct ieee80211_regdomain *rd, bool user_reset = false; int r; + if (IS_ERR_OR_NULL(rd)) + return -ENODATA; + if (!reg_is_valid_request(rd->alpha2)) { kfree(rd); return -EINVAL; @@ -3577,6 +3617,7 @@ int set_regdom(const struct ieee80211_regdomain *rd, r = reg_set_rd_core(rd); break; case NL80211_REGDOM_SET_BY_USER: + cfg80211_save_user_regdom(rd); r = reg_set_rd_user(rd, lr); user_reset = true; break; @@ -3599,7 +3640,7 @@ int set_regdom(const struct ieee80211_regdomain *rd, break; default: /* Back to world regulatory in case of errors */ - restore_regulatory_settings(user_reset); + restore_regulatory_settings(user_reset, false); } kfree(rd); @@ -3935,6 +3976,8 @@ void regulatory_exit(void) if (!IS_ERR_OR_NULL(regdb)) kfree(regdb); + if (!IS_ERR_OR_NULL(cfg80211_user_regdom)) + kfree(cfg80211_user_regdom); free_regdb_keyring(); } diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 9ceeb5f3a7cb..504133d76de4 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -5,6 +5,7 @@ /* * Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com> + * Copyright (C) 2019 Intel Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -22,6 +23,7 @@ enum ieee80211_regd_source { REGD_SOURCE_INTERNAL_DB, REGD_SOURCE_CRDA, + REGD_SOURCE_CACHED, }; extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 5123667f4569..287518c6caa4 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -5,6 +5,7 @@ * Copyright 2008 Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2016 Intel Deutschland GmbH + * Copyright (C) 2018-2019 Intel Corporation */ #include <linux/kernel.h> #include <linux/slab.h> @@ -109,6 +110,12 @@ static inline void bss_ref_get(struct cfg80211_registered_device *rdev, pub); bss->refcount++; } + if (bss->pub.transmitted_bss) { + bss = container_of(bss->pub.transmitted_bss, + struct cfg80211_internal_bss, + pub); + bss->refcount++; + } } static inline void bss_ref_put(struct cfg80211_registered_device *rdev, @@ -125,6 +132,18 @@ static inline void bss_ref_put(struct cfg80211_registered_device *rdev, if (hbss->refcount == 0) bss_free(hbss); } + + if (bss->pub.transmitted_bss) { + struct cfg80211_internal_bss *tbss; + + tbss = container_of(bss->pub.transmitted_bss, + struct cfg80211_internal_bss, + pub); + tbss->refcount--; + if (tbss->refcount == 0) + bss_free(tbss); + } + bss->refcount--; if (bss->refcount == 0) bss_free(bss); @@ -150,6 +169,7 @@ static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev, } list_del_init(&bss->list); + list_del_init(&bss->pub.nontrans_list); rb_erase(&bss->rbn, &rdev->bss_tree); rdev->bss_entries--; WARN_ONCE((rdev->bss_entries == 0) ^ list_empty(&rdev->bss_list), @@ -159,6 +179,162 @@ static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev, return true; } +static size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen, + const u8 *subelement, size_t subie_len, + u8 *new_ie, gfp_t gfp) +{ + u8 *pos, *tmp; + const u8 *tmp_old, *tmp_new; + u8 *sub_copy; + + /* copy subelement as we need to change its content to + * mark an ie after it is processed. + */ + sub_copy = kmalloc(subie_len, gfp); + if (!sub_copy) + return 0; + memcpy(sub_copy, subelement, subie_len); + + pos = &new_ie[0]; + + /* set new ssid */ + tmp_new = cfg80211_find_ie(WLAN_EID_SSID, sub_copy, subie_len); + if (tmp_new) { + memcpy(pos, tmp_new, tmp_new[1] + 2); + pos += (tmp_new[1] + 2); + } + + /* go through IEs in ie (skip SSID) and subelement, + * merge them into new_ie + */ + tmp_old = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen); + tmp_old = (tmp_old) ? tmp_old + tmp_old[1] + 2 : ie; + + while (tmp_old + tmp_old[1] + 2 - ie <= ielen) { + if (tmp_old[0] == 0) { + tmp_old++; + continue; + } + + if (tmp_old[0] == WLAN_EID_EXTENSION) + tmp = (u8 *)cfg80211_find_ext_ie(tmp_old[2], sub_copy, + subie_len); + else + tmp = (u8 *)cfg80211_find_ie(tmp_old[0], sub_copy, + subie_len); + + if (!tmp) { + /* ie in old ie but not in subelement */ + if (tmp_old[0] != WLAN_EID_MULTIPLE_BSSID) { + memcpy(pos, tmp_old, tmp_old[1] + 2); + pos += tmp_old[1] + 2; + } + } else { + /* ie in transmitting ie also in subelement, + * copy from subelement and flag the ie in subelement + * as copied (by setting eid field to WLAN_EID_SSID, + * which is skipped anyway). + * For vendor ie, compare OUI + type + subType to + * determine if they are the same ie. + */ + if (tmp_old[0] == WLAN_EID_VENDOR_SPECIFIC) { + if (!memcmp(tmp_old + 2, tmp + 2, 5)) { + /* same vendor ie, copy from + * subelement + */ + memcpy(pos, tmp, tmp[1] + 2); + pos += tmp[1] + 2; + tmp[0] = WLAN_EID_SSID; + } else { + memcpy(pos, tmp_old, tmp_old[1] + 2); + pos += tmp_old[1] + 2; + } + } else { + /* copy ie from subelement into new ie */ + memcpy(pos, tmp, tmp[1] + 2); + pos += tmp[1] + 2; + tmp[0] = WLAN_EID_SSID; + } + } + + if (tmp_old + tmp_old[1] + 2 - ie == ielen) + break; + + tmp_old += tmp_old[1] + 2; + } + + /* go through subelement again to check if there is any ie not + * copied to new ie, skip ssid, capability, bssid-index ie + */ + tmp_new = sub_copy; + while (tmp_new + tmp_new[1] + 2 - sub_copy <= subie_len) { + if (!(tmp_new[0] == WLAN_EID_NON_TX_BSSID_CAP || + tmp_new[0] == WLAN_EID_SSID || + tmp_new[0] == WLAN_EID_MULTI_BSSID_IDX)) { + memcpy(pos, tmp_new, tmp_new[1] + 2); + pos += tmp_new[1] + 2; + } + if (tmp_new + tmp_new[1] + 2 - sub_copy == subie_len) + break; + tmp_new += tmp_new[1] + 2; + } + + kfree(sub_copy); + return pos - new_ie; +} + +static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, + const u8 *ssid, size_t ssid_len) +{ + const struct cfg80211_bss_ies *ies; + const u8 *ssidie; + + if (bssid && !ether_addr_equal(a->bssid, bssid)) + return false; + + if (!ssid) + return true; + + ies = rcu_access_pointer(a->ies); + if (!ies) + return false; + ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len); + if (!ssidie) + return false; + if (ssidie[1] != ssid_len) + return false; + return memcmp(ssidie + 2, ssid, ssid_len) == 0; +} + +static int +cfg80211_add_nontrans_list(struct cfg80211_bss *trans_bss, + struct cfg80211_bss *nontrans_bss) +{ + const u8 *ssid; + size_t ssid_len; + struct cfg80211_bss *bss = NULL; + + rcu_read_lock(); + ssid = ieee80211_bss_get_ie(nontrans_bss, WLAN_EID_SSID); + if (!ssid) { + rcu_read_unlock(); + return -EINVAL; + } + ssid_len = ssid[1]; + ssid = ssid + 2; + rcu_read_unlock(); + + /* check if nontrans_bss is in the list */ + list_for_each_entry(bss, &trans_bss->nontrans_list, nontrans_list) { + if (is_bss(bss, nontrans_bss->bssid, ssid, ssid_len)) + return 0; + } + + /* add to the list */ + list_add_tail(&nontrans_bss->nontrans_list, &trans_bss->nontrans_list); + return 0; +} + static void __cfg80211_bss_expire(struct cfg80211_registered_device *rdev, unsigned long expire_time) { @@ -480,73 +656,43 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *rdev) __cfg80211_bss_expire(rdev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE); } -const u8 *cfg80211_find_ie_match(u8 eid, const u8 *ies, int len, - const u8 *match, int match_len, - int match_offset) +const struct element * +cfg80211_find_elem_match(u8 eid, const u8 *ies, unsigned int len, + const u8 *match, unsigned int match_len, + unsigned int match_offset) { - /* match_offset can't be smaller than 2, unless match_len is - * zero, in which case match_offset must be zero as well. - */ - if (WARN_ON((match_len && match_offset < 2) || - (!match_len && match_offset))) - return NULL; + const struct element *elem; - while (len >= 2 && len >= ies[1] + 2) { - if ((ies[0] == eid) && - (ies[1] + 2 >= match_offset + match_len) && - !memcmp(ies + match_offset, match, match_len)) - return ies; - - len -= ies[1] + 2; - ies += ies[1] + 2; + for_each_element_id(elem, eid, ies, len) { + if (elem->datalen >= match_offset + match_len && + !memcmp(elem->data + match_offset, match, match_len)) + return elem; } return NULL; } -EXPORT_SYMBOL(cfg80211_find_ie_match); +EXPORT_SYMBOL(cfg80211_find_elem_match); -const u8 *cfg80211_find_vendor_ie(unsigned int oui, int oui_type, - const u8 *ies, int len) +const struct element *cfg80211_find_vendor_elem(unsigned int oui, int oui_type, + const u8 *ies, + unsigned int len) { - const u8 *ie; + const struct element *elem; u8 match[] = { oui >> 16, oui >> 8, oui, oui_type }; int match_len = (oui_type < 0) ? 3 : sizeof(match); if (WARN_ON(oui_type > 0xff)) return NULL; - ie = cfg80211_find_ie_match(WLAN_EID_VENDOR_SPECIFIC, ies, len, - match, match_len, 2); + elem = cfg80211_find_elem_match(WLAN_EID_VENDOR_SPECIFIC, ies, len, + match, match_len, 0); - if (ie && (ie[1] < 4)) + if (!elem || elem->datalen < 4) return NULL; - return ie; -} -EXPORT_SYMBOL(cfg80211_find_vendor_ie); - -static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, - const u8 *ssid, size_t ssid_len) -{ - const struct cfg80211_bss_ies *ies; - const u8 *ssidie; - - if (bssid && !ether_addr_equal(a->bssid, bssid)) - return false; - - if (!ssid) - return true; - - ies = rcu_access_pointer(a->ies); - if (!ies) - return false; - ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len); - if (!ssidie) - return false; - if (ssidie[1] != ssid_len) - return false; - return memcmp(ssidie + 2, ssid, ssid_len) == 0; + return elem; } +EXPORT_SYMBOL(cfg80211_find_vendor_elem); /** * enum bss_compare_mode - BSS compare mode @@ -882,6 +1028,12 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *rdev, return true; } +struct cfg80211_non_tx_bss { + struct cfg80211_bss *tx_bss; + u8 max_bssid_indicator; + u8 bssid_index; +}; + /* Returned bss is reference counted and must be cleaned up appropriately. */ static struct cfg80211_internal_bss * cfg80211_bss_update(struct cfg80211_registered_device *rdev, @@ -985,6 +1137,8 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev, memcpy(found->pub.chain_signal, tmp->pub.chain_signal, IEEE80211_MAX_CHAINS); ether_addr_copy(found->parent_bssid, tmp->parent_bssid); + found->pub.max_bssid_indicator = tmp->pub.max_bssid_indicator; + found->pub.bssid_index = tmp->pub.bssid_index; } else { struct cfg80211_internal_bss *new; struct cfg80211_internal_bss *hidden; @@ -1009,6 +1163,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev, memcpy(new, tmp, sizeof(*new)); new->refcount = 1; INIT_LIST_HEAD(&new->hidden_list); + INIT_LIST_HEAD(&new->pub.nontrans_list); if (rcu_access_pointer(tmp->pub.proberesp_ies)) { hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN); @@ -1042,6 +1197,17 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev, goto drop; } + /* This must be before the call to bss_ref_get */ + if (tmp->pub.transmitted_bss) { + struct cfg80211_internal_bss *pbss = + container_of(tmp->pub.transmitted_bss, + struct cfg80211_internal_bss, + pub); + + new->pub.transmitted_bss = tmp->pub.transmitted_bss; + bss_ref_get(rdev, pbss); + } + list_add_tail(&new->list, &rdev->bss_list); rdev->bss_entries++; rb_insert_bss(rdev, new); @@ -1130,14 +1296,16 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen, } /* Returned bss is reference counted and must be cleaned up appropriately. */ -struct cfg80211_bss * -cfg80211_inform_bss_data(struct wiphy *wiphy, - struct cfg80211_inform_bss *data, - enum cfg80211_bss_frame_type ftype, - const u8 *bssid, u64 tsf, u16 capability, - u16 beacon_interval, const u8 *ie, size_t ielen, - gfp_t gfp) +static struct cfg80211_bss * +cfg80211_inform_single_bss_data(struct wiphy *wiphy, + struct cfg80211_inform_bss *data, + enum cfg80211_bss_frame_type ftype, + const u8 *bssid, u64 tsf, u16 capability, + u16 beacon_interval, const u8 *ie, size_t ielen, + struct cfg80211_non_tx_bss *non_tx_data, + gfp_t gfp) { + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct cfg80211_bss_ies *ies; struct ieee80211_channel *channel; struct cfg80211_internal_bss tmp = {}, *res; @@ -1163,6 +1331,11 @@ cfg80211_inform_bss_data(struct wiphy *wiphy, tmp.pub.beacon_interval = beacon_interval; tmp.pub.capability = capability; tmp.ts_boottime = data->boottime_ns; + if (non_tx_data) { + tmp.pub.transmitted_bss = non_tx_data->tx_bss; + tmp.pub.bssid_index = non_tx_data->bssid_index; + tmp.pub.max_bssid_indicator = non_tx_data->max_bssid_indicator; + } /* * If we do not know here whether the IEs are from a Beacon or Probe @@ -1209,19 +1382,247 @@ cfg80211_inform_bss_data(struct wiphy *wiphy, regulatory_hint_found_beacon(wiphy, channel, gfp); } + if (non_tx_data && non_tx_data->tx_bss) { + /* this is a nontransmitting bss, we need to add it to + * transmitting bss' list if it is not there + */ + if (cfg80211_add_nontrans_list(non_tx_data->tx_bss, + &res->pub)) { + if (__cfg80211_unlink_bss(rdev, res)) + rdev->bss_generation++; + } + } + trace_cfg80211_return_bss(&res->pub); /* cfg80211_bss_update gives us a referenced result */ return &res->pub; } -EXPORT_SYMBOL(cfg80211_inform_bss_data); -/* cfg80211_inform_bss_width_frame helper */ +static void cfg80211_parse_mbssid_data(struct wiphy *wiphy, + struct cfg80211_inform_bss *data, + enum cfg80211_bss_frame_type ftype, + const u8 *bssid, u64 tsf, + u16 beacon_interval, const u8 *ie, + size_t ielen, + struct cfg80211_non_tx_bss *non_tx_data, + gfp_t gfp) +{ + const u8 *mbssid_index_ie; + const struct element *elem, *sub; + size_t new_ie_len; + u8 new_bssid[ETH_ALEN]; + u8 *new_ie; + u16 capability; + struct cfg80211_bss *bss; + + if (!non_tx_data) + return; + if (!cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen)) + return; + if (!wiphy->support_mbssid) + return; + if (wiphy->support_only_he_mbssid && + !cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, ie, ielen)) + return; + + new_ie = kmalloc(IEEE80211_MAX_DATA_LEN, gfp); + if (!new_ie) + return; + + for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, ie, ielen) { + if (elem->datalen < 4) + continue; + for_each_element(sub, elem->data + 1, elem->datalen - 1) { + if (sub->id != 0 || sub->datalen < 4) { + /* not a valid BSS profile */ + continue; + } + + if (sub->data[0] != WLAN_EID_NON_TX_BSSID_CAP || + sub->data[1] != 2) { + /* The first element within the Nontransmitted + * BSSID Profile is not the Nontransmitted + * BSSID Capability element. + */ + continue; + } + + /* found a Nontransmitted BSSID Profile */ + mbssid_index_ie = cfg80211_find_ie + (WLAN_EID_MULTI_BSSID_IDX, + sub->data, sub->datalen); + if (!mbssid_index_ie || mbssid_index_ie[1] < 1 || + mbssid_index_ie[2] == 0) { + /* No valid Multiple BSSID-Index element */ + continue; + } + + non_tx_data->bssid_index = mbssid_index_ie[2]; + non_tx_data->max_bssid_indicator = elem->data[0]; + + cfg80211_gen_new_bssid(bssid, + non_tx_data->max_bssid_indicator, + non_tx_data->bssid_index, + new_bssid); + memset(new_ie, 0, IEEE80211_MAX_DATA_LEN); + new_ie_len = cfg80211_gen_new_ie(ie, ielen, sub->data, + sub->datalen, new_ie, + gfp); + if (!new_ie_len) + continue; + + capability = get_unaligned_le16(sub->data + 2); + bss = cfg80211_inform_single_bss_data(wiphy, data, + ftype, + new_bssid, tsf, + capability, + beacon_interval, + new_ie, + new_ie_len, + non_tx_data, + gfp); + if (!bss) + break; + cfg80211_put_bss(wiphy, bss); + } + } + + kfree(new_ie); +} + struct cfg80211_bss * -cfg80211_inform_bss_frame_data(struct wiphy *wiphy, - struct cfg80211_inform_bss *data, - struct ieee80211_mgmt *mgmt, size_t len, - gfp_t gfp) +cfg80211_inform_bss_data(struct wiphy *wiphy, + struct cfg80211_inform_bss *data, + enum cfg80211_bss_frame_type ftype, + const u8 *bssid, u64 tsf, u16 capability, + u16 beacon_interval, const u8 *ie, size_t ielen, + gfp_t gfp) +{ + struct cfg80211_bss *res; + struct cfg80211_non_tx_bss non_tx_data; + + res = cfg80211_inform_single_bss_data(wiphy, data, ftype, bssid, tsf, + capability, beacon_interval, ie, + ielen, NULL, gfp); + non_tx_data.tx_bss = res; + cfg80211_parse_mbssid_data(wiphy, data, ftype, bssid, tsf, + beacon_interval, ie, ielen, &non_tx_data, + gfp); + return res; +} +EXPORT_SYMBOL(cfg80211_inform_bss_data); + +static void +cfg80211_parse_mbssid_frame_data(struct wiphy *wiphy, + struct cfg80211_inform_bss *data, + struct ieee80211_mgmt *mgmt, size_t len, + struct cfg80211_non_tx_bss *non_tx_data, + gfp_t gfp) +{ + enum cfg80211_bss_frame_type ftype; + const u8 *ie = mgmt->u.probe_resp.variable; + size_t ielen = len - offsetof(struct ieee80211_mgmt, + u.probe_resp.variable); + ftype = ieee80211_is_beacon(mgmt->frame_control) ? + CFG80211_BSS_FTYPE_BEACON : CFG80211_BSS_FTYPE_PRESP; + + cfg80211_parse_mbssid_data(wiphy, data, ftype, mgmt->bssid, + le64_to_cpu(mgmt->u.probe_resp.timestamp), + le16_to_cpu(mgmt->u.probe_resp.beacon_int), + ie, ielen, non_tx_data, gfp); +} + +static void +cfg80211_update_notlisted_nontrans(struct wiphy *wiphy, + struct cfg80211_bss *nontrans_bss, + struct ieee80211_mgmt *mgmt, size_t len, + gfp_t gfp) +{ + u8 *ie, *new_ie, *pos; + const u8 *nontrans_ssid, *trans_ssid, *mbssid; + size_t ielen = len - offsetof(struct ieee80211_mgmt, + u.probe_resp.variable); + size_t new_ie_len; + struct cfg80211_bss_ies *new_ies; + const struct cfg80211_bss_ies *old; + u8 cpy_len; + + ie = mgmt->u.probe_resp.variable; + + new_ie_len = ielen; + trans_ssid = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen); + if (!trans_ssid) + return; + new_ie_len -= trans_ssid[1]; + mbssid = cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen); + if (!mbssid) + return; + new_ie_len -= mbssid[1]; + rcu_read_lock(); + nontrans_ssid = ieee80211_bss_get_ie(nontrans_bss, WLAN_EID_SSID); + if (!nontrans_ssid) { + rcu_read_unlock(); + return; + } + new_ie_len += nontrans_ssid[1]; + rcu_read_unlock(); + + /* generate new ie for nontrans BSS + * 1. replace SSID with nontrans BSS' SSID + * 2. skip MBSSID IE + */ + new_ie = kzalloc(new_ie_len, gfp); + if (!new_ie) + return; + new_ies = kzalloc(sizeof(*new_ies) + new_ie_len, gfp); + if (!new_ies) + goto out_free; + + pos = new_ie; + + /* copy the nontransmitted SSID */ + cpy_len = nontrans_ssid[1] + 2; + memcpy(pos, nontrans_ssid, cpy_len); + pos += cpy_len; + /* copy the IEs between SSID and MBSSID */ + cpy_len = trans_ssid[1] + 2; + memcpy(pos, (trans_ssid + cpy_len), (mbssid - (trans_ssid + cpy_len))); + pos += (mbssid - (trans_ssid + cpy_len)); + /* copy the IEs after MBSSID */ + cpy_len = mbssid[1] + 2; + memcpy(pos, mbssid + cpy_len, ((ie + ielen) - (mbssid + cpy_len))); + + /* update ie */ + new_ies->len = new_ie_len; + new_ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); + new_ies->from_beacon = ieee80211_is_beacon(mgmt->frame_control); + memcpy(new_ies->data, new_ie, new_ie_len); + if (ieee80211_is_probe_resp(mgmt->frame_control)) { + old = rcu_access_pointer(nontrans_bss->proberesp_ies); + rcu_assign_pointer(nontrans_bss->proberesp_ies, new_ies); + rcu_assign_pointer(nontrans_bss->ies, new_ies); + if (old) + kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head); + } else { + old = rcu_access_pointer(nontrans_bss->beacon_ies); + rcu_assign_pointer(nontrans_bss->beacon_ies, new_ies); + rcu_assign_pointer(nontrans_bss->ies, new_ies); + if (old) + kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head); + } + +out_free: + kfree(new_ie); +} + +/* cfg80211_inform_bss_width_frame helper */ +static struct cfg80211_bss * +cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy, + struct cfg80211_inform_bss *data, + struct ieee80211_mgmt *mgmt, size_t len, + struct cfg80211_non_tx_bss *non_tx_data, + gfp_t gfp) { struct cfg80211_internal_bss tmp = {}, *res; struct cfg80211_bss_ies *ies; @@ -1279,6 +1680,11 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy, tmp.pub.chains = data->chains; memcpy(tmp.pub.chain_signal, data->chain_signal, IEEE80211_MAX_CHAINS); ether_addr_copy(tmp.parent_bssid, data->parent_bssid); + if (non_tx_data) { + tmp.pub.transmitted_bss = non_tx_data->tx_bss; + tmp.pub.bssid_index = non_tx_data->bssid_index; + tmp.pub.max_bssid_indicator = non_tx_data->max_bssid_indicator; + } signal_valid = abs(data->chan->center_freq - channel->center_freq) <= wiphy->max_adj_channel_rssi_comp; @@ -1300,6 +1706,53 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy, /* cfg80211_bss_update gives us a referenced result */ return &res->pub; } + +struct cfg80211_bss * +cfg80211_inform_bss_frame_data(struct wiphy *wiphy, + struct cfg80211_inform_bss *data, + struct ieee80211_mgmt *mgmt, size_t len, + gfp_t gfp) +{ + struct cfg80211_bss *res, *tmp_bss; + const u8 *ie = mgmt->u.probe_resp.variable; + const struct cfg80211_bss_ies *ies1, *ies2; + size_t ielen = len - offsetof(struct ieee80211_mgmt, + u.probe_resp.variable); + struct cfg80211_non_tx_bss non_tx_data; + + res = cfg80211_inform_single_bss_frame_data(wiphy, data, mgmt, + len, NULL, gfp); + if (!res || !wiphy->support_mbssid || + !cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen)) + return res; + if (wiphy->support_only_he_mbssid && + !cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, ie, ielen)) + return res; + + non_tx_data.tx_bss = res; + /* process each non-transmitting bss */ + cfg80211_parse_mbssid_frame_data(wiphy, data, mgmt, len, + &non_tx_data, gfp); + + /* check if the res has other nontransmitting bss which is not + * in MBSSID IE + */ + ies1 = rcu_access_pointer(res->ies); + + /* go through nontrans_list, if the timestamp of the BSS is + * earlier than the timestamp of the transmitting BSS then + * update it + */ + list_for_each_entry(tmp_bss, &res->nontrans_list, + nontrans_list) { + ies2 = rcu_access_pointer(tmp_bss->ies); + if (ies2->tsf < ies1->tsf) + cfg80211_update_notlisted_nontrans(wiphy, tmp_bss, + mgmt, len, gfp); + } + + return res; +} EXPORT_SYMBOL(cfg80211_inform_bss_frame_data); void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) @@ -1337,7 +1790,8 @@ EXPORT_SYMBOL(cfg80211_put_bss); void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); - struct cfg80211_internal_bss *bss; + struct cfg80211_internal_bss *bss, *tmp1; + struct cfg80211_bss *nontrans_bss, *tmp; if (WARN_ON(!pub)) return; @@ -1345,10 +1799,21 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) bss = container_of(pub, struct cfg80211_internal_bss, pub); spin_lock_bh(&rdev->bss_lock); - if (!list_empty(&bss->list)) { - if (__cfg80211_unlink_bss(rdev, bss)) + if (list_empty(&bss->list)) + goto out; + + list_for_each_entry_safe(nontrans_bss, tmp, + &pub->nontrans_list, + nontrans_list) { + tmp1 = container_of(nontrans_bss, + struct cfg80211_internal_bss, pub); + if (__cfg80211_unlink_bss(rdev, tmp1)) rdev->bss_generation++; } + + if (__cfg80211_unlink_bss(rdev, bss)) + rdev->bss_generation++; +out: spin_unlock_bh(&rdev->bss_lock); } EXPORT_SYMBOL(cfg80211_unlink_bss); diff --git a/net/wireless/util.c b/net/wireless/util.c index ec30e3732c7b..e4b8db5e81ec 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -789,7 +789,7 @@ out: } EXPORT_SYMBOL(cfg80211_classify8021d); -const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) +const struct element *ieee80211_bss_get_elem(struct cfg80211_bss *bss, u8 id) { const struct cfg80211_bss_ies *ies; @@ -797,9 +797,9 @@ const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) if (!ies) return NULL; - return cfg80211_find_ie(ie, ies->data, ies->len); + return cfg80211_find_elem(id, ies->data, ies->len); } -EXPORT_SYMBOL(ieee80211_bss_get_ie); +EXPORT_SYMBOL(ieee80211_bss_get_elem); void cfg80211_upload_connect_keys(struct wireless_dev *wdev) { |