]> Pileus Git - ~andy/linux/blobdiff - net/mac80211/mlme.c
Linux 3.14
[~andy/linux] / net / mac80211 / mlme.c
index b3a3ce316656ce8406859a3cdc325fa262e024a5..245dce969b31165078c04fca9a5fc963457e9d68 100644 (file)
@@ -222,6 +222,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
        switch (vht_oper->chan_width) {
        case IEEE80211_VHT_CHANWIDTH_USE_HT:
                vht_chandef.width = chandef->width;
+               vht_chandef.center_freq1 = chandef->center_freq1;
                break;
        case IEEE80211_VHT_CHANWIDTH_80MHZ:
                vht_chandef.width = NL80211_CHAN_WIDTH_80;
@@ -271,6 +272,28 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
        ret = 0;
 
 out:
+       /*
+        * When tracking the current AP, don't do any further checks if the
+        * new chandef is identical to the one we're currently using for the
+        * connection. This keeps us from playing ping-pong with regulatory,
+        * without it the following can happen (for example):
+        *  - connect to an AP with 80 MHz, world regdom allows 80 MHz
+        *  - AP advertises regdom US
+        *  - CRDA loads regdom US with 80 MHz prohibited (old database)
+        *  - the code below detects an unsupported channel, downgrades, and
+        *    we disconnect from the AP in the caller
+        *  - disconnect causes CRDA to reload world regdomain and the game
+        *    starts anew.
+        * (see https://bugzilla.kernel.org/show_bug.cgi?id=70881)
+        *
+        * It seems possible that there are still scenarios with CSA or real
+        * bandwidth changes where a this could happen, but those cases are
+        * less common and wouldn't completely prevent using the AP.
+        */
+       if (tracking &&
+           cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef))
+               return ret;
+
        /* don't print the message below for VHT mismatch if VHT is disabled */
        if (ret & IEEE80211_STA_DISABLE_VHT)
                vht_chandef = *chandef;
@@ -330,6 +353,16 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
        if (WARN_ON_ONCE(!sta))
                return -EINVAL;
 
+       /*
+        * if bss configuration changed store the new one -
+        * this may be applicable even if channel is identical
+        */
+       ht_opmode = le16_to_cpu(ht_oper->operation_mode);
+       if (sdata->vif.bss_conf.ht_operation_mode != ht_opmode) {
+               *changed |= BSS_CHANGED_HT;
+               sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
+       }
+
        chan = sdata->vif.bss_conf.chandef.chan;
        sband = local->hw.wiphy->bands[chan->band];
 
@@ -416,14 +449,6 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
                                         IEEE80211_RC_BW_CHANGED);
        }
 
-       ht_opmode = le16_to_cpu(ht_oper->operation_mode);
-
-       /* if bss configuration changed store the new one */
-       if (sdata->vif.bss_conf.ht_operation_mode != ht_opmode) {
-               *changed |= BSS_CHANGED_HT;
-               sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
-       }
-
        return 0;
 }
 
@@ -714,7 +739,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
        }
 
        /* if present, add any custom IEs that go before HT */
-       if (assoc_data->ie_len && assoc_data->ie) {
+       if (assoc_data->ie_len) {
                static const u8 before_ht[] = {
                        WLAN_EID_SSID,
                        WLAN_EID_SUPP_RATES,
@@ -748,7 +773,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                                     &assoc_data->ap_vht_cap);
 
        /* if present, add any custom non-vendor IEs that go after HT */
-       if (assoc_data->ie_len && assoc_data->ie) {
+       if (assoc_data->ie_len) {
                noffset = ieee80211_ie_split_vendor(assoc_data->ie,
                                                    assoc_data->ie_len,
                                                    offset);
@@ -779,7 +804,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
        }
 
        /* add any remaining custom (i.e. vendor specific here) IEs */
-       if (assoc_data->ie_len && assoc_data->ie) {
+       if (assoc_data->ie_len) {
                noffset = assoc_data->ie_len;
                pos = skb_put(skb, noffset - offset);
                memcpy(pos, assoc_data->ie + offset, noffset - offset);
@@ -886,8 +911,9 @@ static void ieee80211_chswitch_work(struct work_struct *work)
        if (!ifmgd->associated)
                goto out;
 
-       ret = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
-                                          &changed);
+       mutex_lock(&local->mtx);
+       ret = ieee80211_vif_change_channel(sdata, &changed);
+       mutex_unlock(&local->mtx);
        if (ret) {
                sdata_info(sdata,
                           "vif channel switch failed, disconnecting\n");
@@ -897,7 +923,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
        }
 
        if (!local->use_chanctx) {
-               local->_oper_chandef = local->csa_chandef;
+               local->_oper_chandef = sdata->csa_chandef;
                /* Call "hw_config" only if doing sw channel switch.
                 * Otherwise update the channel directly
                 */
@@ -908,7 +934,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
        }
 
        /* XXX: shouldn't really modify cfg80211-owned data! */
-       ifmgd->associated->channel = local->csa_chandef.chan;
+       ifmgd->associated->channel = sdata->csa_chandef.chan;
 
        /* XXX: wait for a beacon first? */
        ieee80211_wake_queues_by_reason(&local->hw,
@@ -1035,7 +1061,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        }
        mutex_unlock(&local->chanctx_mtx);
 
-       local->csa_chandef = csa_ie.chandef;
+       sdata->csa_chandef = csa_ie.chandef;
 
        if (csa_ie.mode)
                ieee80211_stop_queues_by_reason(&local->hw,
@@ -1398,10 +1424,16 @@ void ieee80211_dfs_cac_timer_work(struct work_struct *work)
        struct ieee80211_sub_if_data *sdata =
                container_of(delayed_work, struct ieee80211_sub_if_data,
                             dfs_cac_timer_work);
+       struct cfg80211_chan_def chandef = sdata->vif.bss_conf.chandef;
 
-       ieee80211_vif_release_channel(sdata);
-
-       cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
+       mutex_lock(&sdata->local->mtx);
+       if (sdata->wdev.cac_started) {
+               ieee80211_vif_release_channel(sdata);
+               cfg80211_cac_event(sdata->dev, &chandef,
+                                  NL80211_RADAR_CAC_FINISHED,
+                                  GFP_KERNEL);
+       }
+       mutex_unlock(&sdata->local->mtx);
 }
 
 /* MLME */
@@ -1695,7 +1727,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        memset(ifmgd->bssid, 0, ETH_ALEN);
 
        /* remove AP and TDLS peers */
-       sta_info_flush_defer(sdata);
+       sta_info_flush(sdata);
 
        /* finally reset all BSS / config parameters */
        changed |= ieee80211_reset_erp_info(sdata);
@@ -1744,7 +1776,11 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        ifmgd->have_beacon = false;
 
        ifmgd->flags = 0;
+       mutex_lock(&local->mtx);
        ieee80211_vif_release_channel(sdata);
+       mutex_unlock(&local->mtx);
+
+       sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
 }
 
 void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
@@ -2065,7 +2101,9 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
                memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
                ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
                sdata->u.mgd.flags = 0;
+               mutex_lock(&sdata->local->mtx);
                ieee80211_vif_release_channel(sdata);
+               mutex_unlock(&sdata->local->mtx);
        }
 
        cfg80211_put_bss(sdata->local->hw.wiphy, auth_data->bss);
@@ -2314,7 +2352,9 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
                memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
                ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
                sdata->u.mgd.flags = 0;
+               mutex_lock(&sdata->local->mtx);
                ieee80211_vif_release_channel(sdata);
+               mutex_unlock(&sdata->local->mtx);
        }
 
        kfree(assoc_data);
@@ -3665,6 +3705,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
        /* will change later if needed */
        sdata->smps_mode = IEEE80211_SMPS_OFF;
 
+       mutex_lock(&local->mtx);
        /*
         * If this fails (possibly due to channel context sharing
         * on incompatible channels, e.g. 80+80 and 160 sharing the
@@ -3676,13 +3717,15 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
        /* don't downgrade for 5 and 10 MHz channels, though. */
        if (chandef.width == NL80211_CHAN_WIDTH_5 ||
            chandef.width == NL80211_CHAN_WIDTH_10)
-               return ret;
+               goto out;
 
        while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
                ifmgd->flags |= ieee80211_chandef_downgrade(&chandef);
                ret = ieee80211_vif_use_channel(sdata, &chandef,
                                                IEEE80211_CHANCTX_SHARED);
        }
+ out:
+       mutex_unlock(&local->mtx);
        return ret;
 }
 
@@ -3733,6 +3776,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
                chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
                if (WARN_ON(!chanctx_conf)) {
                        rcu_read_unlock();
+                       sta_info_free(local, new_sta);
                        return -EINVAL;
                }
                rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
@@ -4191,6 +4235,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 
        sdata->control_port_protocol = req->crypto.control_port_ethertype;
        sdata->control_port_no_encrypt = req->crypto.control_port_no_encrypt;
+       sdata->encrypt_headroom = ieee80211_cs_headroom(local, &req->crypto,
+                                                       sdata->vif.type);
 
        /* kick off associate process */