1 /* 2 * spectrum management 3 * 4 * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi> 5 * Copyright 2002-2005, Instant802 Networks, Inc. 6 * Copyright 2005-2006, Devicescape Software, Inc. 7 * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 8 * Copyright 2007, Michael Wu <flamingice@sourmilk.net> 9 * Copyright 2007-2008, Intel Corporation 10 * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> 11 * 12 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License version 2 as 14 * published by the Free Software Foundation. 15 */ 16 17 #include <linux/ieee80211.h> 18 #include <net/cfg80211.h> 19 #include <net/mac80211.h> 20 #include "ieee80211_i.h" 21 #include "sta_info.h" 22 #include "wme.h" 23 24 int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, 25 struct ieee802_11_elems *elems, bool beacon, 26 enum ieee80211_band current_band, 27 u32 sta_flags, u8 *bssid, 28 struct ieee80211_csa_ie *csa_ie) 29 { 30 enum ieee80211_band new_band; 31 int new_freq; 32 u8 new_chan_no; 33 struct ieee80211_channel *new_chan; 34 struct cfg80211_chan_def new_vht_chandef = {}; 35 const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; 36 const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; 37 const struct ieee80211_ht_operation *ht_oper; 38 int secondary_channel_offset = -1; 39 40 sec_chan_offs = elems->sec_chan_offs; 41 wide_bw_chansw_ie = elems->wide_bw_chansw_ie; 42 ht_oper = elems->ht_operation; 43 44 if (sta_flags & (IEEE80211_STA_DISABLE_HT | 45 IEEE80211_STA_DISABLE_40MHZ)) { 46 sec_chan_offs = NULL; 47 wide_bw_chansw_ie = NULL; 48 /* only used for bandwidth here */ 49 ht_oper = NULL; 50 } 51 52 if (sta_flags & IEEE80211_STA_DISABLE_VHT) 53 wide_bw_chansw_ie = NULL; 54 55 if (elems->ext_chansw_ie) { 56 if (!ieee80211_operating_class_to_band( 57 elems->ext_chansw_ie->new_operating_class, 58 &new_band)) { 59 sdata_info(sdata, 60 "cannot understand ECSA IE operating class %d, disconnecting\n", 61 elems->ext_chansw_ie->new_operating_class); 62 return -EINVAL; 63 } 64 new_chan_no = elems->ext_chansw_ie->new_ch_num; 65 csa_ie->count = elems->ext_chansw_ie->count; 66 csa_ie->mode = elems->ext_chansw_ie->mode; 67 } else if (elems->ch_switch_ie) { 68 new_band = current_band; 69 new_chan_no = elems->ch_switch_ie->new_ch_num; 70 csa_ie->count = elems->ch_switch_ie->count; 71 csa_ie->mode = elems->ch_switch_ie->mode; 72 } else { 73 /* nothing here we understand */ 74 return 1; 75 } 76 77 /* Mesh Channel Switch Parameters Element */ 78 if (elems->mesh_chansw_params_ie) { 79 csa_ie->ttl = elems->mesh_chansw_params_ie->mesh_ttl; 80 csa_ie->mode = elems->mesh_chansw_params_ie->mesh_flags; 81 } 82 83 new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band); 84 new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); 85 if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) { 86 sdata_info(sdata, 87 "BSS %pM switches to unsupported channel (%d MHz), disconnecting\n", 88 bssid, new_freq); 89 return -EINVAL; 90 } 91 92 if (!beacon && sec_chan_offs) { 93 secondary_channel_offset = sec_chan_offs->sec_chan_offs; 94 } else if (beacon && ht_oper) { 95 secondary_channel_offset = 96 ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET; 97 } else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) { 98 /* If it's not a beacon, HT is enabled and the IE not present, 99 * it's 20 MHz, 802.11-2012 8.5.2.6: 100 * This element [the Secondary Channel Offset Element] is 101 * present when switching to a 40 MHz channel. It may be 102 * present when switching to a 20 MHz channel (in which 103 * case the secondary channel offset is set to SCN). 104 */ 105 secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; 106 } 107 108 switch (secondary_channel_offset) { 109 default: 110 /* secondary_channel_offset was present but is invalid */ 111 case IEEE80211_HT_PARAM_CHA_SEC_NONE: 112 cfg80211_chandef_create(&csa_ie->chandef, new_chan, 113 NL80211_CHAN_HT20); 114 break; 115 case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: 116 cfg80211_chandef_create(&csa_ie->chandef, new_chan, 117 NL80211_CHAN_HT40PLUS); 118 break; 119 case IEEE80211_HT_PARAM_CHA_SEC_BELOW: 120 cfg80211_chandef_create(&csa_ie->chandef, new_chan, 121 NL80211_CHAN_HT40MINUS); 122 break; 123 case -1: 124 cfg80211_chandef_create(&csa_ie->chandef, new_chan, 125 NL80211_CHAN_NO_HT); 126 /* keep width for 5/10 MHz channels */ 127 switch (sdata->vif.bss_conf.chandef.width) { 128 case NL80211_CHAN_WIDTH_5: 129 case NL80211_CHAN_WIDTH_10: 130 csa_ie->chandef.width = 131 sdata->vif.bss_conf.chandef.width; 132 break; 133 default: 134 break; 135 } 136 break; 137 } 138 139 if (wide_bw_chansw_ie) { 140 new_vht_chandef.chan = new_chan; 141 new_vht_chandef.center_freq1 = 142 ieee80211_channel_to_frequency( 143 wide_bw_chansw_ie->new_center_freq_seg0, 144 new_band); 145 146 switch (wide_bw_chansw_ie->new_channel_width) { 147 default: 148 /* hmmm, ignore VHT and use HT if present */ 149 case IEEE80211_VHT_CHANWIDTH_USE_HT: 150 new_vht_chandef.chan = NULL; 151 break; 152 case IEEE80211_VHT_CHANWIDTH_80MHZ: 153 new_vht_chandef.width = NL80211_CHAN_WIDTH_80; 154 break; 155 case IEEE80211_VHT_CHANWIDTH_160MHZ: 156 new_vht_chandef.width = NL80211_CHAN_WIDTH_160; 157 break; 158 case IEEE80211_VHT_CHANWIDTH_80P80MHZ: 159 /* field is otherwise reserved */ 160 new_vht_chandef.center_freq2 = 161 ieee80211_channel_to_frequency( 162 wide_bw_chansw_ie->new_center_freq_seg1, 163 new_band); 164 new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80; 165 break; 166 } 167 if (sta_flags & IEEE80211_STA_DISABLE_80P80MHZ && 168 new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80) 169 ieee80211_chandef_downgrade(&new_vht_chandef); 170 if (sta_flags & IEEE80211_STA_DISABLE_160MHZ && 171 new_vht_chandef.width == NL80211_CHAN_WIDTH_160) 172 ieee80211_chandef_downgrade(&new_vht_chandef); 173 if (sta_flags & IEEE80211_STA_DISABLE_40MHZ && 174 new_vht_chandef.width > NL80211_CHAN_WIDTH_20) 175 ieee80211_chandef_downgrade(&new_vht_chandef); 176 } 177 178 /* if VHT data is there validate & use it */ 179 if (new_vht_chandef.chan) { 180 if (!cfg80211_chandef_compatible(&new_vht_chandef, 181 &csa_ie->chandef)) { 182 sdata_info(sdata, 183 "BSS %pM: CSA has inconsistent channel data, disconnecting\n", 184 bssid); 185 return -EINVAL; 186 } 187 csa_ie->chandef = new_vht_chandef; 188 } 189 190 return 0; 191 } 192 193 static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata, 194 struct ieee80211_msrment_ie *request_ie, 195 const u8 *da, const u8 *bssid, 196 u8 dialog_token) 197 { 198 struct ieee80211_local *local = sdata->local; 199 struct sk_buff *skb; 200 struct ieee80211_mgmt *msr_report; 201 202 skb = dev_alloc_skb(sizeof(*msr_report) + local->hw.extra_tx_headroom + 203 sizeof(struct ieee80211_msrment_ie)); 204 if (!skb) 205 return; 206 207 skb_reserve(skb, local->hw.extra_tx_headroom); 208 msr_report = (struct ieee80211_mgmt *)skb_put(skb, 24); 209 memset(msr_report, 0, 24); 210 memcpy(msr_report->da, da, ETH_ALEN); 211 memcpy(msr_report->sa, sdata->vif.addr, ETH_ALEN); 212 memcpy(msr_report->bssid, bssid, ETH_ALEN); 213 msr_report->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 214 IEEE80211_STYPE_ACTION); 215 216 skb_put(skb, 1 + sizeof(msr_report->u.action.u.measurement)); 217 msr_report->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; 218 msr_report->u.action.u.measurement.action_code = 219 WLAN_ACTION_SPCT_MSR_RPRT; 220 msr_report->u.action.u.measurement.dialog_token = dialog_token; 221 222 msr_report->u.action.u.measurement.element_id = WLAN_EID_MEASURE_REPORT; 223 msr_report->u.action.u.measurement.length = 224 sizeof(struct ieee80211_msrment_ie); 225 226 memset(&msr_report->u.action.u.measurement.msr_elem, 0, 227 sizeof(struct ieee80211_msrment_ie)); 228 msr_report->u.action.u.measurement.msr_elem.token = request_ie->token; 229 msr_report->u.action.u.measurement.msr_elem.mode |= 230 IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED; 231 msr_report->u.action.u.measurement.msr_elem.type = request_ie->type; 232 233 ieee80211_tx_skb(sdata, skb); 234 } 235 236 void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, 237 struct ieee80211_mgmt *mgmt, 238 size_t len) 239 { 240 /* 241 * Ignoring measurement request is spec violation. 242 * Mandatory measurements must be reported optional 243 * measurements might be refused or reported incapable 244 * For now just refuse 245 * TODO: Answer basic measurement as unmeasured 246 */ 247 ieee80211_send_refuse_measurement_request(sdata, 248 &mgmt->u.action.u.measurement.msr_elem, 249 mgmt->sa, mgmt->bssid, 250 mgmt->u.action.u.measurement.dialog_token); 251 } 252