139192c0bSJohannes Berg /* 239192c0bSJohannes Berg * spectrum management 339192c0bSJohannes Berg * 439192c0bSJohannes Berg * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi> 539192c0bSJohannes Berg * Copyright 2002-2005, Instant802 Networks, Inc. 639192c0bSJohannes Berg * Copyright 2005-2006, Devicescape Software, Inc. 739192c0bSJohannes Berg * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 839192c0bSJohannes Berg * Copyright 2007, Michael Wu <flamingice@sourmilk.net> 939192c0bSJohannes Berg * Copyright 2007-2008, Intel Corporation 1039192c0bSJohannes Berg * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> 1139192c0bSJohannes Berg * 1239192c0bSJohannes Berg * This program is free software; you can redistribute it and/or modify 1339192c0bSJohannes Berg * it under the terms of the GNU General Public License version 2 as 1439192c0bSJohannes Berg * published by the Free Software Foundation. 1539192c0bSJohannes Berg */ 1639192c0bSJohannes Berg 1739192c0bSJohannes Berg #include <linux/ieee80211.h> 18d3236553SJohannes Berg #include <net/cfg80211.h> 1939192c0bSJohannes Berg #include <net/mac80211.h> 2039192c0bSJohannes Berg #include "ieee80211_i.h" 2139192c0bSJohannes Berg #include "sta_info.h" 2239192c0bSJohannes Berg #include "wme.h" 2339192c0bSJohannes Berg 24e6b7cde4SSimon Wunderlich int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, 25e6b7cde4SSimon Wunderlich struct ieee802_11_elems *elems, bool beacon, 26e6b7cde4SSimon Wunderlich enum ieee80211_band current_band, 27c0f17eb9SChun-Yeow Yeoh u32 sta_flags, u8 *bssid, 28c0f17eb9SChun-Yeow Yeoh struct ieee80211_csa_ie *csa_ie) 29e6b7cde4SSimon Wunderlich { 30e6b7cde4SSimon Wunderlich enum ieee80211_band new_band; 31e6b7cde4SSimon Wunderlich int new_freq; 32e6b7cde4SSimon Wunderlich u8 new_chan_no; 33e6b7cde4SSimon Wunderlich struct ieee80211_channel *new_chan; 34e6b7cde4SSimon Wunderlich struct cfg80211_chan_def new_vht_chandef = {}; 35e6b7cde4SSimon Wunderlich const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; 36e6b7cde4SSimon Wunderlich const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; 37e6b7cde4SSimon Wunderlich const struct ieee80211_ht_operation *ht_oper; 38e6b7cde4SSimon Wunderlich int secondary_channel_offset = -1; 39e6b7cde4SSimon Wunderlich 40e6b7cde4SSimon Wunderlich sec_chan_offs = elems->sec_chan_offs; 41e6b7cde4SSimon Wunderlich wide_bw_chansw_ie = elems->wide_bw_chansw_ie; 42e6b7cde4SSimon Wunderlich ht_oper = elems->ht_operation; 43e6b7cde4SSimon Wunderlich 44e6b7cde4SSimon Wunderlich if (sta_flags & (IEEE80211_STA_DISABLE_HT | 45e6b7cde4SSimon Wunderlich IEEE80211_STA_DISABLE_40MHZ)) { 46e6b7cde4SSimon Wunderlich sec_chan_offs = NULL; 47e6b7cde4SSimon Wunderlich wide_bw_chansw_ie = NULL; 48e6b7cde4SSimon Wunderlich /* only used for bandwidth here */ 49e6b7cde4SSimon Wunderlich ht_oper = NULL; 50e6b7cde4SSimon Wunderlich } 51e6b7cde4SSimon Wunderlich 52e6b7cde4SSimon Wunderlich if (sta_flags & IEEE80211_STA_DISABLE_VHT) 53e6b7cde4SSimon Wunderlich wide_bw_chansw_ie = NULL; 54e6b7cde4SSimon Wunderlich 55e6b7cde4SSimon Wunderlich if (elems->ext_chansw_ie) { 56e6b7cde4SSimon Wunderlich if (!ieee80211_operating_class_to_band( 57e6b7cde4SSimon Wunderlich elems->ext_chansw_ie->new_operating_class, 58e6b7cde4SSimon Wunderlich &new_band)) { 59e6b7cde4SSimon Wunderlich sdata_info(sdata, 60e6b7cde4SSimon Wunderlich "cannot understand ECSA IE operating class %d, disconnecting\n", 61e6b7cde4SSimon Wunderlich elems->ext_chansw_ie->new_operating_class); 62e6b7cde4SSimon Wunderlich return -EINVAL; 63e6b7cde4SSimon Wunderlich } 64e6b7cde4SSimon Wunderlich new_chan_no = elems->ext_chansw_ie->new_ch_num; 65c0f17eb9SChun-Yeow Yeoh csa_ie->count = elems->ext_chansw_ie->count; 66c0f17eb9SChun-Yeow Yeoh csa_ie->mode = elems->ext_chansw_ie->mode; 67e6b7cde4SSimon Wunderlich } else if (elems->ch_switch_ie) { 68e6b7cde4SSimon Wunderlich new_band = current_band; 69e6b7cde4SSimon Wunderlich new_chan_no = elems->ch_switch_ie->new_ch_num; 70c0f17eb9SChun-Yeow Yeoh csa_ie->count = elems->ch_switch_ie->count; 71c0f17eb9SChun-Yeow Yeoh csa_ie->mode = elems->ch_switch_ie->mode; 72e6b7cde4SSimon Wunderlich } else { 73e6b7cde4SSimon Wunderlich /* nothing here we understand */ 74e6b7cde4SSimon Wunderlich return 1; 75e6b7cde4SSimon Wunderlich } 76e6b7cde4SSimon Wunderlich 7733a45867SChun-Yeow Yeoh /* Mesh Channel Switch Parameters Element */ 7833a45867SChun-Yeow Yeoh if (elems->mesh_chansw_params_ie) { 7933a45867SChun-Yeow Yeoh csa_ie->ttl = elems->mesh_chansw_params_ie->mesh_ttl; 8033a45867SChun-Yeow Yeoh csa_ie->mode = elems->mesh_chansw_params_ie->mesh_flags; 813f718fd8SChun-Yeow Yeoh csa_ie->pre_value = le16_to_cpu( 823f718fd8SChun-Yeow Yeoh elems->mesh_chansw_params_ie->mesh_pre_value); 8333a45867SChun-Yeow Yeoh } 8433a45867SChun-Yeow Yeoh 85e6b7cde4SSimon Wunderlich new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band); 86e6b7cde4SSimon Wunderlich new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); 87e6b7cde4SSimon Wunderlich if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) { 88e6b7cde4SSimon Wunderlich sdata_info(sdata, 89e6b7cde4SSimon Wunderlich "BSS %pM switches to unsupported channel (%d MHz), disconnecting\n", 90e6b7cde4SSimon Wunderlich bssid, new_freq); 91e6b7cde4SSimon Wunderlich return -EINVAL; 92e6b7cde4SSimon Wunderlich } 93e6b7cde4SSimon Wunderlich 94e6b7cde4SSimon Wunderlich if (!beacon && sec_chan_offs) { 95e6b7cde4SSimon Wunderlich secondary_channel_offset = sec_chan_offs->sec_chan_offs; 96e6b7cde4SSimon Wunderlich } else if (beacon && ht_oper) { 97e6b7cde4SSimon Wunderlich secondary_channel_offset = 98e6b7cde4SSimon Wunderlich ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET; 99e6b7cde4SSimon Wunderlich } else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) { 100e6b7cde4SSimon Wunderlich /* If it's not a beacon, HT is enabled and the IE not present, 101e6b7cde4SSimon Wunderlich * it's 20 MHz, 802.11-2012 8.5.2.6: 102e6b7cde4SSimon Wunderlich * This element [the Secondary Channel Offset Element] is 103e6b7cde4SSimon Wunderlich * present when switching to a 40 MHz channel. It may be 104e6b7cde4SSimon Wunderlich * present when switching to a 20 MHz channel (in which 105e6b7cde4SSimon Wunderlich * case the secondary channel offset is set to SCN). 106e6b7cde4SSimon Wunderlich */ 107e6b7cde4SSimon Wunderlich secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; 108e6b7cde4SSimon Wunderlich } 109e6b7cde4SSimon Wunderlich 110e6b7cde4SSimon Wunderlich switch (secondary_channel_offset) { 111e6b7cde4SSimon Wunderlich default: 112e6b7cde4SSimon Wunderlich /* secondary_channel_offset was present but is invalid */ 113e6b7cde4SSimon Wunderlich case IEEE80211_HT_PARAM_CHA_SEC_NONE: 114c0f17eb9SChun-Yeow Yeoh cfg80211_chandef_create(&csa_ie->chandef, new_chan, 115e6b7cde4SSimon Wunderlich NL80211_CHAN_HT20); 116e6b7cde4SSimon Wunderlich break; 117e6b7cde4SSimon Wunderlich case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: 118c0f17eb9SChun-Yeow Yeoh cfg80211_chandef_create(&csa_ie->chandef, new_chan, 119e6b7cde4SSimon Wunderlich NL80211_CHAN_HT40PLUS); 120e6b7cde4SSimon Wunderlich break; 121e6b7cde4SSimon Wunderlich case IEEE80211_HT_PARAM_CHA_SEC_BELOW: 122c0f17eb9SChun-Yeow Yeoh cfg80211_chandef_create(&csa_ie->chandef, new_chan, 123e6b7cde4SSimon Wunderlich NL80211_CHAN_HT40MINUS); 124e6b7cde4SSimon Wunderlich break; 125e6b7cde4SSimon Wunderlich case -1: 126c0f17eb9SChun-Yeow Yeoh cfg80211_chandef_create(&csa_ie->chandef, new_chan, 127e6b7cde4SSimon Wunderlich NL80211_CHAN_NO_HT); 128e6b7cde4SSimon Wunderlich /* keep width for 5/10 MHz channels */ 129e6b7cde4SSimon Wunderlich switch (sdata->vif.bss_conf.chandef.width) { 130e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_5: 131e6b7cde4SSimon Wunderlich case NL80211_CHAN_WIDTH_10: 132c0f17eb9SChun-Yeow Yeoh csa_ie->chandef.width = 133c0f17eb9SChun-Yeow Yeoh sdata->vif.bss_conf.chandef.width; 134e6b7cde4SSimon Wunderlich break; 135e6b7cde4SSimon Wunderlich default: 136e6b7cde4SSimon Wunderlich break; 137e6b7cde4SSimon Wunderlich } 138e6b7cde4SSimon Wunderlich break; 139e6b7cde4SSimon Wunderlich } 140e6b7cde4SSimon Wunderlich 141e6b7cde4SSimon Wunderlich if (wide_bw_chansw_ie) { 142e6b7cde4SSimon Wunderlich new_vht_chandef.chan = new_chan; 143e6b7cde4SSimon Wunderlich new_vht_chandef.center_freq1 = 144e6b7cde4SSimon Wunderlich ieee80211_channel_to_frequency( 145e6b7cde4SSimon Wunderlich wide_bw_chansw_ie->new_center_freq_seg0, 146e6b7cde4SSimon Wunderlich new_band); 147e6b7cde4SSimon Wunderlich 148e6b7cde4SSimon Wunderlich switch (wide_bw_chansw_ie->new_channel_width) { 149e6b7cde4SSimon Wunderlich default: 150e6b7cde4SSimon Wunderlich /* hmmm, ignore VHT and use HT if present */ 151e6b7cde4SSimon Wunderlich case IEEE80211_VHT_CHANWIDTH_USE_HT: 152e6b7cde4SSimon Wunderlich new_vht_chandef.chan = NULL; 153e6b7cde4SSimon Wunderlich break; 154e6b7cde4SSimon Wunderlich case IEEE80211_VHT_CHANWIDTH_80MHZ: 155e6b7cde4SSimon Wunderlich new_vht_chandef.width = NL80211_CHAN_WIDTH_80; 156e6b7cde4SSimon Wunderlich break; 157e6b7cde4SSimon Wunderlich case IEEE80211_VHT_CHANWIDTH_160MHZ: 158e6b7cde4SSimon Wunderlich new_vht_chandef.width = NL80211_CHAN_WIDTH_160; 159e6b7cde4SSimon Wunderlich break; 160e6b7cde4SSimon Wunderlich case IEEE80211_VHT_CHANWIDTH_80P80MHZ: 161e6b7cde4SSimon Wunderlich /* field is otherwise reserved */ 162e6b7cde4SSimon Wunderlich new_vht_chandef.center_freq2 = 163e6b7cde4SSimon Wunderlich ieee80211_channel_to_frequency( 164e6b7cde4SSimon Wunderlich wide_bw_chansw_ie->new_center_freq_seg1, 165e6b7cde4SSimon Wunderlich new_band); 166e6b7cde4SSimon Wunderlich new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80; 167e6b7cde4SSimon Wunderlich break; 168e6b7cde4SSimon Wunderlich } 169e6b7cde4SSimon Wunderlich if (sta_flags & IEEE80211_STA_DISABLE_80P80MHZ && 170e6b7cde4SSimon Wunderlich new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80) 171e6b7cde4SSimon Wunderlich ieee80211_chandef_downgrade(&new_vht_chandef); 172e6b7cde4SSimon Wunderlich if (sta_flags & IEEE80211_STA_DISABLE_160MHZ && 173e6b7cde4SSimon Wunderlich new_vht_chandef.width == NL80211_CHAN_WIDTH_160) 174e6b7cde4SSimon Wunderlich ieee80211_chandef_downgrade(&new_vht_chandef); 175e6b7cde4SSimon Wunderlich if (sta_flags & IEEE80211_STA_DISABLE_40MHZ && 176e6b7cde4SSimon Wunderlich new_vht_chandef.width > NL80211_CHAN_WIDTH_20) 177e6b7cde4SSimon Wunderlich ieee80211_chandef_downgrade(&new_vht_chandef); 178e6b7cde4SSimon Wunderlich } 179e6b7cde4SSimon Wunderlich 180e6b7cde4SSimon Wunderlich /* if VHT data is there validate & use it */ 181e6b7cde4SSimon Wunderlich if (new_vht_chandef.chan) { 182e6b7cde4SSimon Wunderlich if (!cfg80211_chandef_compatible(&new_vht_chandef, 183c0f17eb9SChun-Yeow Yeoh &csa_ie->chandef)) { 184e6b7cde4SSimon Wunderlich sdata_info(sdata, 185e6b7cde4SSimon Wunderlich "BSS %pM: CSA has inconsistent channel data, disconnecting\n", 186e6b7cde4SSimon Wunderlich bssid); 187e6b7cde4SSimon Wunderlich return -EINVAL; 188e6b7cde4SSimon Wunderlich } 189c0f17eb9SChun-Yeow Yeoh csa_ie->chandef = new_vht_chandef; 190e6b7cde4SSimon Wunderlich } 191e6b7cde4SSimon Wunderlich 192e6b7cde4SSimon Wunderlich return 0; 193e6b7cde4SSimon Wunderlich } 194e6b7cde4SSimon Wunderlich 19539192c0bSJohannes Berg static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata, 19639192c0bSJohannes Berg struct ieee80211_msrment_ie *request_ie, 19739192c0bSJohannes Berg const u8 *da, const u8 *bssid, 19839192c0bSJohannes Berg u8 dialog_token) 19939192c0bSJohannes Berg { 20039192c0bSJohannes Berg struct ieee80211_local *local = sdata->local; 20139192c0bSJohannes Berg struct sk_buff *skb; 20239192c0bSJohannes Berg struct ieee80211_mgmt *msr_report; 20339192c0bSJohannes Berg 20439192c0bSJohannes Berg skb = dev_alloc_skb(sizeof(*msr_report) + local->hw.extra_tx_headroom + 20539192c0bSJohannes Berg sizeof(struct ieee80211_msrment_ie)); 206d15b8459SJoe Perches if (!skb) 20739192c0bSJohannes Berg return; 20839192c0bSJohannes Berg 20939192c0bSJohannes Berg skb_reserve(skb, local->hw.extra_tx_headroom); 21039192c0bSJohannes Berg msr_report = (struct ieee80211_mgmt *)skb_put(skb, 24); 21139192c0bSJohannes Berg memset(msr_report, 0, 24); 21239192c0bSJohannes Berg memcpy(msr_report->da, da, ETH_ALEN); 21347846c9bSJohannes Berg memcpy(msr_report->sa, sdata->vif.addr, ETH_ALEN); 21439192c0bSJohannes Berg memcpy(msr_report->bssid, bssid, ETH_ALEN); 21539192c0bSJohannes Berg msr_report->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 21639192c0bSJohannes Berg IEEE80211_STYPE_ACTION); 21739192c0bSJohannes Berg 21839192c0bSJohannes Berg skb_put(skb, 1 + sizeof(msr_report->u.action.u.measurement)); 21939192c0bSJohannes Berg msr_report->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; 22039192c0bSJohannes Berg msr_report->u.action.u.measurement.action_code = 22139192c0bSJohannes Berg WLAN_ACTION_SPCT_MSR_RPRT; 22239192c0bSJohannes Berg msr_report->u.action.u.measurement.dialog_token = dialog_token; 22339192c0bSJohannes Berg 22439192c0bSJohannes Berg msr_report->u.action.u.measurement.element_id = WLAN_EID_MEASURE_REPORT; 22539192c0bSJohannes Berg msr_report->u.action.u.measurement.length = 22639192c0bSJohannes Berg sizeof(struct ieee80211_msrment_ie); 22739192c0bSJohannes Berg 22839192c0bSJohannes Berg memset(&msr_report->u.action.u.measurement.msr_elem, 0, 22939192c0bSJohannes Berg sizeof(struct ieee80211_msrment_ie)); 23039192c0bSJohannes Berg msr_report->u.action.u.measurement.msr_elem.token = request_ie->token; 23139192c0bSJohannes Berg msr_report->u.action.u.measurement.msr_elem.mode |= 23239192c0bSJohannes Berg IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED; 23339192c0bSJohannes Berg msr_report->u.action.u.measurement.msr_elem.type = request_ie->type; 23439192c0bSJohannes Berg 23562ae67beSJohannes Berg ieee80211_tx_skb(sdata, skb); 23639192c0bSJohannes Berg } 23739192c0bSJohannes Berg 23839192c0bSJohannes Berg void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, 23939192c0bSJohannes Berg struct ieee80211_mgmt *mgmt, 24039192c0bSJohannes Berg size_t len) 24139192c0bSJohannes Berg { 24239192c0bSJohannes Berg /* 24339192c0bSJohannes Berg * Ignoring measurement request is spec violation. 24439192c0bSJohannes Berg * Mandatory measurements must be reported optional 24539192c0bSJohannes Berg * measurements might be refused or reported incapable 24639192c0bSJohannes Berg * For now just refuse 24739192c0bSJohannes Berg * TODO: Answer basic measurement as unmeasured 24839192c0bSJohannes Berg */ 24939192c0bSJohannes Berg ieee80211_send_refuse_measurement_request(sdata, 25039192c0bSJohannes Berg &mgmt->u.action.u.measurement.msr_elem, 25139192c0bSJohannes Berg mgmt->sa, mgmt->bssid, 25239192c0bSJohannes Berg mgmt->u.action.u.measurement.dialog_token); 25339192c0bSJohannes Berg } 254