18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * spectrum management 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi> 68c2ecf20Sopenharmony_ci * Copyright 2002-2005, Instant802 Networks, Inc. 78c2ecf20Sopenharmony_ci * Copyright 2005-2006, Devicescape Software, Inc. 88c2ecf20Sopenharmony_ci * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 98c2ecf20Sopenharmony_ci * Copyright 2007, Michael Wu <flamingice@sourmilk.net> 108c2ecf20Sopenharmony_ci * Copyright 2007-2008, Intel Corporation 118c2ecf20Sopenharmony_ci * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> 128c2ecf20Sopenharmony_ci * Copyright (C) 2018, 2020 Intel Corporation 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/ieee80211.h> 168c2ecf20Sopenharmony_ci#include <net/cfg80211.h> 178c2ecf20Sopenharmony_ci#include <net/mac80211.h> 188c2ecf20Sopenharmony_ci#include "ieee80211_i.h" 198c2ecf20Sopenharmony_ci#include "sta_info.h" 208c2ecf20Sopenharmony_ci#include "wme.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ciint ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, 238c2ecf20Sopenharmony_ci struct ieee802_11_elems *elems, 248c2ecf20Sopenharmony_ci enum nl80211_band current_band, 258c2ecf20Sopenharmony_ci u32 vht_cap_info, 268c2ecf20Sopenharmony_ci u32 sta_flags, u8 *bssid, 278c2ecf20Sopenharmony_ci struct ieee80211_csa_ie *csa_ie) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci enum nl80211_band new_band = current_band; 308c2ecf20Sopenharmony_ci int new_freq; 318c2ecf20Sopenharmony_ci u8 new_chan_no; 328c2ecf20Sopenharmony_ci struct ieee80211_channel *new_chan; 338c2ecf20Sopenharmony_ci struct cfg80211_chan_def new_vht_chandef = {}; 348c2ecf20Sopenharmony_ci const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; 358c2ecf20Sopenharmony_ci const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; 368c2ecf20Sopenharmony_ci int secondary_channel_offset = -1; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci memset(csa_ie, 0, sizeof(*csa_ie)); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci sec_chan_offs = elems->sec_chan_offs; 418c2ecf20Sopenharmony_ci wide_bw_chansw_ie = elems->wide_bw_chansw_ie; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (sta_flags & (IEEE80211_STA_DISABLE_HT | 448c2ecf20Sopenharmony_ci IEEE80211_STA_DISABLE_40MHZ)) { 458c2ecf20Sopenharmony_ci sec_chan_offs = NULL; 468c2ecf20Sopenharmony_ci wide_bw_chansw_ie = NULL; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (sta_flags & IEEE80211_STA_DISABLE_VHT) 508c2ecf20Sopenharmony_ci wide_bw_chansw_ie = NULL; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (elems->ext_chansw_ie) { 538c2ecf20Sopenharmony_ci if (!ieee80211_operating_class_to_band( 548c2ecf20Sopenharmony_ci elems->ext_chansw_ie->new_operating_class, 558c2ecf20Sopenharmony_ci &new_band)) { 568c2ecf20Sopenharmony_ci sdata_info(sdata, 578c2ecf20Sopenharmony_ci "cannot understand ECSA IE operating class, %d, ignoring\n", 588c2ecf20Sopenharmony_ci elems->ext_chansw_ie->new_operating_class); 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci new_chan_no = elems->ext_chansw_ie->new_ch_num; 618c2ecf20Sopenharmony_ci csa_ie->count = elems->ext_chansw_ie->count; 628c2ecf20Sopenharmony_ci csa_ie->mode = elems->ext_chansw_ie->mode; 638c2ecf20Sopenharmony_ci } else if (elems->ch_switch_ie) { 648c2ecf20Sopenharmony_ci new_chan_no = elems->ch_switch_ie->new_ch_num; 658c2ecf20Sopenharmony_ci csa_ie->count = elems->ch_switch_ie->count; 668c2ecf20Sopenharmony_ci csa_ie->mode = elems->ch_switch_ie->mode; 678c2ecf20Sopenharmony_ci } else { 688c2ecf20Sopenharmony_ci /* nothing here we understand */ 698c2ecf20Sopenharmony_ci return 1; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* Mesh Channel Switch Parameters Element */ 738c2ecf20Sopenharmony_ci if (elems->mesh_chansw_params_ie) { 748c2ecf20Sopenharmony_ci csa_ie->ttl = elems->mesh_chansw_params_ie->mesh_ttl; 758c2ecf20Sopenharmony_ci csa_ie->mode = elems->mesh_chansw_params_ie->mesh_flags; 768c2ecf20Sopenharmony_ci csa_ie->pre_value = le16_to_cpu( 778c2ecf20Sopenharmony_ci elems->mesh_chansw_params_ie->mesh_pre_value); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (elems->mesh_chansw_params_ie->mesh_flags & 808c2ecf20Sopenharmony_ci WLAN_EID_CHAN_SWITCH_PARAM_REASON) 818c2ecf20Sopenharmony_ci csa_ie->reason_code = le16_to_cpu( 828c2ecf20Sopenharmony_ci elems->mesh_chansw_params_ie->mesh_reason); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band); 868c2ecf20Sopenharmony_ci new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); 878c2ecf20Sopenharmony_ci if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) { 888c2ecf20Sopenharmony_ci sdata_info(sdata, 898c2ecf20Sopenharmony_ci "BSS %pM switches to unsupported channel (%d MHz), disconnecting\n", 908c2ecf20Sopenharmony_ci bssid, new_freq); 918c2ecf20Sopenharmony_ci return -EINVAL; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (sec_chan_offs) { 958c2ecf20Sopenharmony_ci secondary_channel_offset = sec_chan_offs->sec_chan_offs; 968c2ecf20Sopenharmony_ci } else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) { 978c2ecf20Sopenharmony_ci /* If the secondary channel offset IE is not present, 988c2ecf20Sopenharmony_ci * we can't know what's the post-CSA offset, so the 998c2ecf20Sopenharmony_ci * best we can do is use 20MHz. 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci switch (secondary_channel_offset) { 1058c2ecf20Sopenharmony_ci default: 1068c2ecf20Sopenharmony_ci /* secondary_channel_offset was present but is invalid */ 1078c2ecf20Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_NONE: 1088c2ecf20Sopenharmony_ci cfg80211_chandef_create(&csa_ie->chandef, new_chan, 1098c2ecf20Sopenharmony_ci NL80211_CHAN_HT20); 1108c2ecf20Sopenharmony_ci break; 1118c2ecf20Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: 1128c2ecf20Sopenharmony_ci cfg80211_chandef_create(&csa_ie->chandef, new_chan, 1138c2ecf20Sopenharmony_ci NL80211_CHAN_HT40PLUS); 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_BELOW: 1168c2ecf20Sopenharmony_ci cfg80211_chandef_create(&csa_ie->chandef, new_chan, 1178c2ecf20Sopenharmony_ci NL80211_CHAN_HT40MINUS); 1188c2ecf20Sopenharmony_ci break; 1198c2ecf20Sopenharmony_ci case -1: 1208c2ecf20Sopenharmony_ci cfg80211_chandef_create(&csa_ie->chandef, new_chan, 1218c2ecf20Sopenharmony_ci NL80211_CHAN_NO_HT); 1228c2ecf20Sopenharmony_ci /* keep width for 5/10 MHz channels */ 1238c2ecf20Sopenharmony_ci switch (sdata->vif.bss_conf.chandef.width) { 1248c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_5: 1258c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_10: 1268c2ecf20Sopenharmony_ci csa_ie->chandef.width = 1278c2ecf20Sopenharmony_ci sdata->vif.bss_conf.chandef.width; 1288c2ecf20Sopenharmony_ci break; 1298c2ecf20Sopenharmony_ci default: 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (wide_bw_chansw_ie) { 1368c2ecf20Sopenharmony_ci u8 new_seg1 = wide_bw_chansw_ie->new_center_freq_seg1; 1378c2ecf20Sopenharmony_ci struct ieee80211_vht_operation vht_oper = { 1388c2ecf20Sopenharmony_ci .chan_width = 1398c2ecf20Sopenharmony_ci wide_bw_chansw_ie->new_channel_width, 1408c2ecf20Sopenharmony_ci .center_freq_seg0_idx = 1418c2ecf20Sopenharmony_ci wide_bw_chansw_ie->new_center_freq_seg0, 1428c2ecf20Sopenharmony_ci .center_freq_seg1_idx = new_seg1, 1438c2ecf20Sopenharmony_ci /* .basic_mcs_set doesn't matter */ 1448c2ecf20Sopenharmony_ci }; 1458c2ecf20Sopenharmony_ci struct ieee80211_ht_operation ht_oper = { 1468c2ecf20Sopenharmony_ci .operation_mode = 1478c2ecf20Sopenharmony_ci cpu_to_le16(new_seg1 << 1488c2ecf20Sopenharmony_ci IEEE80211_HT_OP_MODE_CCFS2_SHIFT), 1498c2ecf20Sopenharmony_ci }; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* default, for the case of IEEE80211_VHT_CHANWIDTH_USE_HT, 1528c2ecf20Sopenharmony_ci * to the previously parsed chandef 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci new_vht_chandef = csa_ie->chandef; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* ignore if parsing fails */ 1578c2ecf20Sopenharmony_ci if (!ieee80211_chandef_vht_oper(&sdata->local->hw, 1588c2ecf20Sopenharmony_ci vht_cap_info, 1598c2ecf20Sopenharmony_ci &vht_oper, &ht_oper, 1608c2ecf20Sopenharmony_ci &new_vht_chandef)) 1618c2ecf20Sopenharmony_ci new_vht_chandef.chan = NULL; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (sta_flags & IEEE80211_STA_DISABLE_80P80MHZ && 1648c2ecf20Sopenharmony_ci new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80) 1658c2ecf20Sopenharmony_ci ieee80211_chandef_downgrade(&new_vht_chandef); 1668c2ecf20Sopenharmony_ci if (sta_flags & IEEE80211_STA_DISABLE_160MHZ && 1678c2ecf20Sopenharmony_ci new_vht_chandef.width == NL80211_CHAN_WIDTH_160) 1688c2ecf20Sopenharmony_ci ieee80211_chandef_downgrade(&new_vht_chandef); 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* if VHT data is there validate & use it */ 1728c2ecf20Sopenharmony_ci if (new_vht_chandef.chan) { 1738c2ecf20Sopenharmony_ci if (!cfg80211_chandef_compatible(&new_vht_chandef, 1748c2ecf20Sopenharmony_ci &csa_ie->chandef)) { 1758c2ecf20Sopenharmony_ci sdata_info(sdata, 1768c2ecf20Sopenharmony_ci "BSS %pM: CSA has inconsistent channel data, disconnecting\n", 1778c2ecf20Sopenharmony_ci bssid); 1788c2ecf20Sopenharmony_ci return -EINVAL; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci csa_ie->chandef = new_vht_chandef; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (elems->max_channel_switch_time) 1848c2ecf20Sopenharmony_ci csa_ie->max_switch_time = 1858c2ecf20Sopenharmony_ci (elems->max_channel_switch_time[0] << 0) | 1868c2ecf20Sopenharmony_ci (elems->max_channel_switch_time[1] << 8) | 1878c2ecf20Sopenharmony_ci (elems->max_channel_switch_time[2] << 16); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata, 1938c2ecf20Sopenharmony_ci struct ieee80211_msrment_ie *request_ie, 1948c2ecf20Sopenharmony_ci const u8 *da, const u8 *bssid, 1958c2ecf20Sopenharmony_ci u8 dialog_token) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 1988c2ecf20Sopenharmony_ci struct sk_buff *skb; 1998c2ecf20Sopenharmony_ci struct ieee80211_mgmt *msr_report; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci skb = dev_alloc_skb(sizeof(*msr_report) + local->hw.extra_tx_headroom + 2028c2ecf20Sopenharmony_ci sizeof(struct ieee80211_msrment_ie)); 2038c2ecf20Sopenharmony_ci if (!skb) 2048c2ecf20Sopenharmony_ci return; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci skb_reserve(skb, local->hw.extra_tx_headroom); 2078c2ecf20Sopenharmony_ci msr_report = skb_put_zero(skb, 24); 2088c2ecf20Sopenharmony_ci memcpy(msr_report->da, da, ETH_ALEN); 2098c2ecf20Sopenharmony_ci memcpy(msr_report->sa, sdata->vif.addr, ETH_ALEN); 2108c2ecf20Sopenharmony_ci memcpy(msr_report->bssid, bssid, ETH_ALEN); 2118c2ecf20Sopenharmony_ci msr_report->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 2128c2ecf20Sopenharmony_ci IEEE80211_STYPE_ACTION); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci skb_put(skb, 1 + sizeof(msr_report->u.action.u.measurement)); 2158c2ecf20Sopenharmony_ci msr_report->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; 2168c2ecf20Sopenharmony_ci msr_report->u.action.u.measurement.action_code = 2178c2ecf20Sopenharmony_ci WLAN_ACTION_SPCT_MSR_RPRT; 2188c2ecf20Sopenharmony_ci msr_report->u.action.u.measurement.dialog_token = dialog_token; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci msr_report->u.action.u.measurement.element_id = WLAN_EID_MEASURE_REPORT; 2218c2ecf20Sopenharmony_ci msr_report->u.action.u.measurement.length = 2228c2ecf20Sopenharmony_ci sizeof(struct ieee80211_msrment_ie); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci memset(&msr_report->u.action.u.measurement.msr_elem, 0, 2258c2ecf20Sopenharmony_ci sizeof(struct ieee80211_msrment_ie)); 2268c2ecf20Sopenharmony_ci msr_report->u.action.u.measurement.msr_elem.token = request_ie->token; 2278c2ecf20Sopenharmony_ci msr_report->u.action.u.measurement.msr_elem.mode |= 2288c2ecf20Sopenharmony_ci IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED; 2298c2ecf20Sopenharmony_ci msr_report->u.action.u.measurement.msr_elem.type = request_ie->type; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci ieee80211_tx_skb(sdata, skb); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_civoid ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, 2358c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt, 2368c2ecf20Sopenharmony_ci size_t len) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci /* 2398c2ecf20Sopenharmony_ci * Ignoring measurement request is spec violation. 2408c2ecf20Sopenharmony_ci * Mandatory measurements must be reported optional 2418c2ecf20Sopenharmony_ci * measurements might be refused or reported incapable 2428c2ecf20Sopenharmony_ci * For now just refuse 2438c2ecf20Sopenharmony_ci * TODO: Answer basic measurement as unmeasured 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_ci ieee80211_send_refuse_measurement_request(sdata, 2468c2ecf20Sopenharmony_ci &mgmt->u.action.u.measurement.msr_elem, 2478c2ecf20Sopenharmony_ci mgmt->sa, mgmt->bssid, 2488c2ecf20Sopenharmony_ci mgmt->u.action.u.measurement.dialog_token); 2498c2ecf20Sopenharmony_ci} 250