mac80211: read station mgmt keys via get_key call
[cascardo/linux.git] / net / mac80211 / cfg.c
index 9e7e68d..ac18528 100644 (file)
@@ -133,7 +133,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
                             struct key_params *params)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
        struct sta_info *sta = NULL;
+       const struct ieee80211_cipher_scheme *cs = NULL;
        struct ieee80211_key *key;
        int err;
 
@@ -145,22 +147,28 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
        case WLAN_CIPHER_SUITE_WEP40:
        case WLAN_CIPHER_SUITE_TKIP:
        case WLAN_CIPHER_SUITE_WEP104:
-               if (IS_ERR(sdata->local->wep_tx_tfm))
+               if (IS_ERR(local->wep_tx_tfm))
                        return -EINVAL;
                break;
+       case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_AES_CMAC:
+       case WLAN_CIPHER_SUITE_GCMP:
+               break;
        default:
+               cs = ieee80211_cs_get(local, params->cipher, sdata->vif.type);
                break;
        }
 
        key = ieee80211_key_alloc(params->cipher, key_idx, params->key_len,
-                                 params->key, params->seq_len, params->seq);
+                                 params->key, params->seq_len, params->seq,
+                                 cs);
        if (IS_ERR(key))
                return PTR_ERR(key);
 
        if (pairwise)
                key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;
 
-       mutex_lock(&sdata->local->sta_mtx);
+       mutex_lock(&local->sta_mtx);
 
        if (mac_addr) {
                if (ieee80211_vif_is_mesh(&sdata->vif))
@@ -216,10 +224,13 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
                break;
        }
 
+       if (sta)
+               sta->cipher_scheme = cs;
+
        err = ieee80211_key_link(key, sdata, sta);
 
  out_unlock:
-       mutex_unlock(&sdata->local->sta_mtx);
+       mutex_unlock(&local->sta_mtx);
 
        return err;
 }
@@ -244,7 +255,7 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
                        goto out_unlock;
 
                if (pairwise)
-                       key = key_mtx_dereference(local, sta->ptk);
+                       key = key_mtx_dereference(local, sta->ptk[key_idx]);
                else
                        key = key_mtx_dereference(local, sta->gtk[key_idx]);
        } else
@@ -290,9 +301,10 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
                if (!sta)
                        goto out;
 
-               if (pairwise)
-                       key = rcu_dereference(sta->ptk);
-               else if (key_idx < NUM_DEFAULT_KEYS)
+               if (pairwise && key_idx < NUM_DEFAULT_KEYS)
+                       key = rcu_dereference(sta->ptk[key_idx]);
+               else if (!pairwise &&
+                        key_idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)
                        key = rcu_dereference(sta->gtk[key_idx]);
        } else
                key = rcu_dereference(sdata->keys[key_idx]);
@@ -521,8 +533,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                                 STATION_INFO_PEER_PM |
                                 STATION_INFO_NONPEER_PM;
 
-               sinfo->llid = le16_to_cpu(sta->llid);
-               sinfo->plid = le16_to_cpu(sta->plid);
+               sinfo->llid = sta->llid;
+               sinfo->plid = sta->plid;
                sinfo->plink_state = sta->plink_state;
                if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
                        sinfo->filled |= STATION_INFO_T_OFFSET;
@@ -846,7 +858,7 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
        if (!resp || !resp_len)
                return 1;
 
-       old = rtnl_dereference(sdata->u.ap.probe_resp);
+       old = sdata_dereference(sdata->u.ap.probe_resp, sdata);
 
        new = kzalloc(sizeof(struct probe_resp) + resp_len, GFP_KERNEL);
        if (!new)
@@ -862,15 +874,16 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
        return 0;
 }
 
-int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
-                           struct cfg80211_beacon_data *params)
+static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+                                  struct cfg80211_beacon_data *params)
 {
        struct beacon_data *new, *old;
        int new_head_len, new_tail_len;
        int size, err;
        u32 changed = BSS_CHANGED_BEACON;
 
-       old = rtnl_dereference(sdata->u.ap.beacon);
+       old = sdata_dereference(sdata->u.ap.beacon, sdata);
+
 
        /* Need to have a beacon head if we don't have one yet */
        if (!params->head && !old)
@@ -947,7 +960,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
                      BSS_CHANGED_P2P_PS;
        int err;
 
-       old = rtnl_dereference(sdata->u.ap.beacon);
+       old = sdata_dereference(sdata->u.ap.beacon, sdata);
        if (old)
                return -EALREADY;
 
@@ -968,11 +981,19 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
         */
        sdata->control_port_protocol = params->crypto.control_port_ethertype;
        sdata->control_port_no_encrypt = params->crypto.control_port_no_encrypt;
+       sdata->encrypt_headroom = ieee80211_cs_headroom(sdata->local,
+                                                       &params->crypto,
+                                                       sdata->vif.type);
+
        list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
                vlan->control_port_protocol =
                        params->crypto.control_port_ethertype;
                vlan->control_port_no_encrypt =
                        params->crypto.control_port_no_encrypt;
+               vlan->encrypt_headroom =
+                       ieee80211_cs_headroom(sdata->local,
+                                             &params->crypto,
+                                             vlan->vif.type);
        }
 
        sdata->vif.bss_conf.beacon_int = params->beacon_interval;
@@ -1001,7 +1022,8 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
 
        err = drv_start_ap(sdata->local, sdata);
        if (err) {
-               old = rtnl_dereference(sdata->u.ap.beacon);
+               old = sdata_dereference(sdata->u.ap.beacon, sdata);
+
                if (old)
                        kfree_rcu(old, rcu_head);
                RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
@@ -1032,7 +1054,7 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
        if (sdata->vif.csa_active)
                return -EBUSY;
 
-       old = rtnl_dereference(sdata->u.ap.beacon);
+       old = sdata_dereference(sdata->u.ap.beacon, sdata);
        if (!old)
                return -ENOENT;
 
@@ -1050,15 +1072,18 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        struct ieee80211_local *local = sdata->local;
        struct beacon_data *old_beacon;
        struct probe_resp *old_probe_resp;
+       struct cfg80211_chan_def chandef;
 
-       old_beacon = rtnl_dereference(sdata->u.ap.beacon);
+       old_beacon = sdata_dereference(sdata->u.ap.beacon, sdata);
        if (!old_beacon)
                return -ENOENT;
-       old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp);
+       old_probe_resp = sdata_dereference(sdata->u.ap.probe_resp, sdata);
 
        /* abort any running channel switch */
        sdata->vif.csa_active = false;
-       cancel_work_sync(&sdata->csa_finalize_work);
+       kfree(sdata->u.ap.next_beacon);
+       sdata->u.ap.next_beacon = NULL;
+
        cancel_work_sync(&sdata->u.ap.request_smps_work);
 
        /* turn off carrier for this interface and dependent VLANs */
@@ -1073,17 +1098,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        if (old_probe_resp)
                kfree_rcu(old_probe_resp, rcu_head);
 
-       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
-               sta_info_flush_defer(vlan);
-       sta_info_flush_defer(sdata);
-       synchronize_net();
-       rcu_barrier();
-       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
-               sta_info_flush_cleanup(vlan);
-               ieee80211_free_keys(vlan);
-       }
-       sta_info_flush_cleanup(sdata);
-       ieee80211_free_keys(sdata);
+       __sta_info_flush(sdata, true);
+       ieee80211_free_keys(sdata, true);
 
        sdata->vif.bss_conf.enable_beacon = false;
        sdata->vif.bss_conf.ssid_len = 0;
@@ -1091,8 +1107,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
 
        if (sdata->wdev.cac_started) {
+               chandef = sdata->vif.bss_conf.chandef;
                cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
-               cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_ABORTED,
+               cfg80211_cac_event(sdata->dev, &chandef,
+                                  NL80211_RADAR_CAC_ABORTED,
                                   GFP_KERNEL);
        }
 
@@ -1953,7 +1971,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
        enum ieee80211_band band;
        u32 changed = 0;
 
-       if (!rtnl_dereference(sdata->u.ap.beacon))
+       if (!sdata_dereference(sdata->u.ap.beacon, sdata))
                return -ENOENT;
 
        band = ieee80211_get_sdata_band(sdata);
@@ -2561,8 +2579,8 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
                int j;
 
                sdata->rc_rateidx_mask[i] = mask->control[i].legacy;
-               memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].mcs,
-                      sizeof(mask->control[i].mcs));
+               memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].ht_mcs,
+                      sizeof(mask->control[i].ht_mcs));
 
                sdata->rc_has_mcs_mask[i] = false;
                if (!sband)
@@ -2963,27 +2981,33 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
        struct ieee80211_local *local = sdata->local;
        int err, changed = 0;
 
+       sdata_lock(sdata);
+       /* AP might have been stopped while waiting for the lock. */
+       if (!sdata->vif.csa_active)
+               goto unlock;
+
        if (!ieee80211_sdata_running(sdata))
-               return;
+               goto unlock;
 
        sdata->radar_required = sdata->csa_radar_required;
-       err = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
-                                          &changed);
+       err = ieee80211_vif_change_channel(sdata, &changed);
        if (WARN_ON(err < 0))
-               return;
+               goto unlock;
 
        if (!local->use_chanctx) {
-               local->_oper_chandef = local->csa_chandef;
+               local->_oper_chandef = sdata->csa_chandef;
                ieee80211_hw_config(local, 0);
        }
 
        ieee80211_bss_info_change_notify(sdata, changed);
 
+       sdata->vif.csa_active = false;
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
                err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
                if (err < 0)
-                       return;
+                       goto unlock;
+
                changed |= err;
                kfree(sdata->u.ap.next_beacon);
                sdata->u.ap.next_beacon = NULL;
@@ -2997,24 +3021,26 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
        case NL80211_IFTYPE_MESH_POINT:
                err = ieee80211_mesh_finish_csa(sdata);
                if (err < 0)
-                       return;
+                       goto unlock;
                break;
 #endif
        default:
                WARN_ON(1);
-               return;
+               goto unlock;
        }
-       sdata->vif.csa_active = false;
 
        ieee80211_wake_queues_by_reason(&sdata->local->hw,
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
 
-       cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef);
+       cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
+
+unlock:
+       sdata_unlock(sdata);
 }
 
-static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
-                                   struct cfg80211_csa_settings *params)
+int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+                            struct cfg80211_csa_settings *params)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
@@ -3023,6 +3049,8 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_if_mesh __maybe_unused *ifmsh;
        int err, num_chanctx;
 
+       lockdep_assert_held(&sdata->wdev.mtx);
+
        if (!list_empty(&local->roc_list) || local->scanning)
                return -EBUSY;
 
@@ -3119,9 +3147,17 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                    params->chandef.chan->band)
                        return -EINVAL;
 
+               ifmsh->chsw_init = true;
+               if (!ifmsh->pre_value)
+                       ifmsh->pre_value = 1;
+               else
+                       ifmsh->pre_value++;
+
                err = ieee80211_mesh_csa_beacon(sdata, params, true);
-               if (err < 0)
+               if (err < 0) {
+                       ifmsh->chsw_init = false;
                        return err;
+               }
                break;
 #endif
        default:
@@ -3135,7 +3171,7 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                                IEEE80211_MAX_QUEUE_MAP,
                                IEEE80211_QUEUE_STOP_REASON_CSA);
 
-       local->csa_chandef = params->chandef;
+       sdata->csa_chandef = params->chandef;
        sdata->vif.csa_active = true;
 
        ieee80211_bss_info_change_notify(sdata, err);
@@ -3145,26 +3181,25 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
 }
 
 static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
-                            struct ieee80211_channel *chan, bool offchan,
-                            unsigned int wait, const u8 *buf, size_t len,
-                            bool no_cck, bool dont_wait_for_ack, u64 *cookie)
+                            struct cfg80211_mgmt_tx_params *params,
+                            u64 *cookie)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
        struct ieee80211_local *local = sdata->local;
        struct sk_buff *skb;
        struct sta_info *sta;
-       const struct ieee80211_mgmt *mgmt = (void *)buf;
+       const struct ieee80211_mgmt *mgmt = (void *)params->buf;
        bool need_offchan = false;
        u32 flags;
        int ret;
 
-       if (dont_wait_for_ack)
+       if (params->dont_wait_for_ack)
                flags = IEEE80211_TX_CTL_NO_ACK;
        else
                flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX |
                        IEEE80211_TX_CTL_REQ_TX_STATUS;
 
-       if (no_cck)
+       if (params->no_cck)
                flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
 
        switch (sdata->vif.type) {
@@ -3212,7 +3247,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
        /* configurations requiring offchan cannot work if no channel has been
         * specified
         */
-       if (need_offchan && !chan)
+       if (need_offchan && !params->chan)
                return -EINVAL;
 
        mutex_lock(&local->mtx);
@@ -3225,8 +3260,10 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 
                if (chanctx_conf) {
-                       need_offchan = chan && (chan != chanctx_conf->def.chan);
-               } else if (!chan) {
+                       need_offchan = params->chan &&
+                                      (params->chan !=
+                                       chanctx_conf->def.chan);
+               } else if (!params->chan) {
                        ret = -EINVAL;
                        rcu_read_unlock();
                        goto out_unlock;
@@ -3236,19 +3273,19 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                rcu_read_unlock();
        }
 
-       if (need_offchan && !offchan) {
+       if (need_offchan && !params->offchan) {
                ret = -EBUSY;
                goto out_unlock;
        }
 
-       skb = dev_alloc_skb(local->hw.extra_tx_headroom + len);
+       skb = dev_alloc_skb(local->hw.extra_tx_headroom + params->len);
        if (!skb) {
                ret = -ENOMEM;
                goto out_unlock;
        }
        skb_reserve(skb, local->hw.extra_tx_headroom);
 
-       memcpy(skb_put(skb, len), buf, len);
+       memcpy(skb_put(skb, params->len), params->buf, params->len);
 
        IEEE80211_SKB_CB(skb)->flags = flags;
 
@@ -3268,8 +3305,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                        local->hw.offchannel_tx_hw_queue;
 
        /* This will handle all kinds of coalescing and immediate TX */
-       ret = ieee80211_start_roc_work(local, sdata, chan,
-                                      wait, cookie, skb,
+       ret = ieee80211_start_roc_work(local, sdata, params->chan,
+                                      params->wait, cookie, skb,
                                       IEEE80211_ROC_TYPE_MGMT_TX);
        if (ret)
                kfree_skb(skb);