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