Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * spectrum management
0004  *
0005  * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
0006  * Copyright 2002-2005, Instant802 Networks, Inc.
0007  * Copyright 2005-2006, Devicescape Software, Inc.
0008  * Copyright 2006-2007  Jiri Benc <jbenc@suse.cz>
0009  * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
0010  * Copyright 2007-2008, Intel Corporation
0011  * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
0012  * Copyright (C) 2018, 2020, 2022 Intel Corporation
0013  */
0014 
0015 #include <linux/ieee80211.h>
0016 #include <net/cfg80211.h>
0017 #include <net/mac80211.h>
0018 #include "ieee80211_i.h"
0019 #include "sta_info.h"
0020 #include "wme.h"
0021 
0022 int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
0023                  struct ieee802_11_elems *elems,
0024                  enum nl80211_band current_band,
0025                  u32 vht_cap_info,
0026                  ieee80211_conn_flags_t conn_flags, u8 *bssid,
0027                  struct ieee80211_csa_ie *csa_ie)
0028 {
0029     enum nl80211_band new_band = current_band;
0030     int new_freq;
0031     u8 new_chan_no;
0032     struct ieee80211_channel *new_chan;
0033     struct cfg80211_chan_def new_vht_chandef = {};
0034     const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
0035     const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
0036     int secondary_channel_offset = -1;
0037 
0038     memset(csa_ie, 0, sizeof(*csa_ie));
0039 
0040     sec_chan_offs = elems->sec_chan_offs;
0041     wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
0042 
0043     if (conn_flags & (IEEE80211_CONN_DISABLE_HT |
0044               IEEE80211_CONN_DISABLE_40MHZ)) {
0045         sec_chan_offs = NULL;
0046         wide_bw_chansw_ie = NULL;
0047     }
0048 
0049     if (conn_flags & IEEE80211_CONN_DISABLE_VHT)
0050         wide_bw_chansw_ie = NULL;
0051 
0052     if (elems->ext_chansw_ie) {
0053         if (!ieee80211_operating_class_to_band(
0054                 elems->ext_chansw_ie->new_operating_class,
0055                 &new_band)) {
0056             sdata_info(sdata,
0057                    "cannot understand ECSA IE operating class, %d, ignoring\n",
0058                    elems->ext_chansw_ie->new_operating_class);
0059         }
0060         new_chan_no = elems->ext_chansw_ie->new_ch_num;
0061         csa_ie->count = elems->ext_chansw_ie->count;
0062         csa_ie->mode = elems->ext_chansw_ie->mode;
0063     } else if (elems->ch_switch_ie) {
0064         new_chan_no = elems->ch_switch_ie->new_ch_num;
0065         csa_ie->count = elems->ch_switch_ie->count;
0066         csa_ie->mode = elems->ch_switch_ie->mode;
0067     } else {
0068         /* nothing here we understand */
0069         return 1;
0070     }
0071 
0072     /* Mesh Channel Switch Parameters Element */
0073     if (elems->mesh_chansw_params_ie) {
0074         csa_ie->ttl = elems->mesh_chansw_params_ie->mesh_ttl;
0075         csa_ie->mode = elems->mesh_chansw_params_ie->mesh_flags;
0076         csa_ie->pre_value = le16_to_cpu(
0077                 elems->mesh_chansw_params_ie->mesh_pre_value);
0078 
0079         if (elems->mesh_chansw_params_ie->mesh_flags &
0080                 WLAN_EID_CHAN_SWITCH_PARAM_REASON)
0081             csa_ie->reason_code = le16_to_cpu(
0082                 elems->mesh_chansw_params_ie->mesh_reason);
0083     }
0084 
0085     new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
0086     new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
0087     if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
0088         sdata_info(sdata,
0089                "BSS %pM switches to unsupported channel (%d MHz), disconnecting\n",
0090                bssid, new_freq);
0091         return -EINVAL;
0092     }
0093 
0094     if (sec_chan_offs) {
0095         secondary_channel_offset = sec_chan_offs->sec_chan_offs;
0096     } else if (!(conn_flags & IEEE80211_CONN_DISABLE_HT)) {
0097         /* If the secondary channel offset IE is not present,
0098          * we can't know what's the post-CSA offset, so the
0099          * best we can do is use 20MHz.
0100         */
0101         secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
0102     }
0103 
0104     switch (secondary_channel_offset) {
0105     default:
0106         /* secondary_channel_offset was present but is invalid */
0107     case IEEE80211_HT_PARAM_CHA_SEC_NONE:
0108         cfg80211_chandef_create(&csa_ie->chandef, new_chan,
0109                     NL80211_CHAN_HT20);
0110         break;
0111     case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
0112         cfg80211_chandef_create(&csa_ie->chandef, new_chan,
0113                     NL80211_CHAN_HT40PLUS);
0114         break;
0115     case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
0116         cfg80211_chandef_create(&csa_ie->chandef, new_chan,
0117                     NL80211_CHAN_HT40MINUS);
0118         break;
0119     case -1:
0120         cfg80211_chandef_create(&csa_ie->chandef, new_chan,
0121                     NL80211_CHAN_NO_HT);
0122         /* keep width for 5/10 MHz channels */
0123         switch (sdata->vif.bss_conf.chandef.width) {
0124         case NL80211_CHAN_WIDTH_5:
0125         case NL80211_CHAN_WIDTH_10:
0126             csa_ie->chandef.width =
0127                 sdata->vif.bss_conf.chandef.width;
0128             break;
0129         default:
0130             break;
0131         }
0132         break;
0133     }
0134 
0135     if (wide_bw_chansw_ie) {
0136         u8 new_seg1 = wide_bw_chansw_ie->new_center_freq_seg1;
0137         struct ieee80211_vht_operation vht_oper = {
0138             .chan_width =
0139                 wide_bw_chansw_ie->new_channel_width,
0140             .center_freq_seg0_idx =
0141                 wide_bw_chansw_ie->new_center_freq_seg0,
0142             .center_freq_seg1_idx = new_seg1,
0143             /* .basic_mcs_set doesn't matter */
0144         };
0145         struct ieee80211_ht_operation ht_oper = {
0146             .operation_mode =
0147                 cpu_to_le16(new_seg1 <<
0148                         IEEE80211_HT_OP_MODE_CCFS2_SHIFT),
0149         };
0150 
0151         /* default, for the case of IEEE80211_VHT_CHANWIDTH_USE_HT,
0152          * to the previously parsed chandef
0153          */
0154         new_vht_chandef = csa_ie->chandef;
0155 
0156         /* ignore if parsing fails */
0157         if (!ieee80211_chandef_vht_oper(&sdata->local->hw,
0158                         vht_cap_info,
0159                         &vht_oper, &ht_oper,
0160                         &new_vht_chandef))
0161             new_vht_chandef.chan = NULL;
0162 
0163         if (conn_flags & IEEE80211_CONN_DISABLE_80P80MHZ &&
0164             new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80)
0165             ieee80211_chandef_downgrade(&new_vht_chandef);
0166         if (conn_flags & IEEE80211_CONN_DISABLE_160MHZ &&
0167             new_vht_chandef.width == NL80211_CHAN_WIDTH_160)
0168             ieee80211_chandef_downgrade(&new_vht_chandef);
0169     }
0170 
0171     /* if VHT data is there validate & use it */
0172     if (new_vht_chandef.chan) {
0173         if (!cfg80211_chandef_compatible(&new_vht_chandef,
0174                          &csa_ie->chandef)) {
0175             sdata_info(sdata,
0176                    "BSS %pM: CSA has inconsistent channel data, disconnecting\n",
0177                    bssid);
0178             return -EINVAL;
0179         }
0180         csa_ie->chandef = new_vht_chandef;
0181     }
0182 
0183     if (elems->max_channel_switch_time)
0184         csa_ie->max_switch_time =
0185             (elems->max_channel_switch_time[0] << 0) |
0186             (elems->max_channel_switch_time[1] <<  8) |
0187             (elems->max_channel_switch_time[2] << 16);
0188 
0189     return 0;
0190 }
0191 
0192 static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata,
0193                     struct ieee80211_msrment_ie *request_ie,
0194                     const u8 *da, const u8 *bssid,
0195                     u8 dialog_token)
0196 {
0197     struct ieee80211_local *local = sdata->local;
0198     struct sk_buff *skb;
0199     struct ieee80211_mgmt *msr_report;
0200 
0201     skb = dev_alloc_skb(sizeof(*msr_report) + local->hw.extra_tx_headroom +
0202                 sizeof(struct ieee80211_msrment_ie));
0203     if (!skb)
0204         return;
0205 
0206     skb_reserve(skb, local->hw.extra_tx_headroom);
0207     msr_report = skb_put_zero(skb, 24);
0208     memcpy(msr_report->da, da, ETH_ALEN);
0209     memcpy(msr_report->sa, sdata->vif.addr, ETH_ALEN);
0210     memcpy(msr_report->bssid, bssid, ETH_ALEN);
0211     msr_report->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
0212                         IEEE80211_STYPE_ACTION);
0213 
0214     skb_put(skb, 1 + sizeof(msr_report->u.action.u.measurement));
0215     msr_report->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
0216     msr_report->u.action.u.measurement.action_code =
0217                 WLAN_ACTION_SPCT_MSR_RPRT;
0218     msr_report->u.action.u.measurement.dialog_token = dialog_token;
0219 
0220     msr_report->u.action.u.measurement.element_id = WLAN_EID_MEASURE_REPORT;
0221     msr_report->u.action.u.measurement.length =
0222             sizeof(struct ieee80211_msrment_ie);
0223 
0224     memset(&msr_report->u.action.u.measurement.msr_elem, 0,
0225         sizeof(struct ieee80211_msrment_ie));
0226     msr_report->u.action.u.measurement.msr_elem.token = request_ie->token;
0227     msr_report->u.action.u.measurement.msr_elem.mode |=
0228             IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED;
0229     msr_report->u.action.u.measurement.msr_elem.type = request_ie->type;
0230 
0231     ieee80211_tx_skb(sdata, skb);
0232 }
0233 
0234 void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
0235                        struct ieee80211_mgmt *mgmt,
0236                        size_t len)
0237 {
0238     /*
0239      * Ignoring measurement request is spec violation.
0240      * Mandatory measurements must be reported optional
0241      * measurements might be refused or reported incapable
0242      * For now just refuse
0243      * TODO: Answer basic measurement as unmeasured
0244      */
0245     ieee80211_send_refuse_measurement_request(sdata,
0246             &mgmt->u.action.u.measurement.msr_elem,
0247             mgmt->sa, mgmt->bssid,
0248             mgmt->u.action.u.measurement.dialog_token);
0249 }