mac80211: add generic cipher scheme support
[cascardo/linux.git] / net / mac80211 / cfg.c
index 2e7855a..f6b9265 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
@@ -291,7 +302,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
                        goto out;
 
                if (pairwise)
-                       key = rcu_dereference(sta->ptk);
+                       key = rcu_dereference(sta->ptk[key_idx]);
                else if (key_idx < NUM_DEFAULT_KEYS)
                        key = rcu_dereference(sta->gtk[key_idx]);
        } else
@@ -968,11 +979,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;
@@ -1050,6 +1069,7 @@ 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);
        if (!old_beacon)
@@ -1059,6 +1079,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        /* abort any running channel switch */
        sdata->vif.csa_active = false;
        cancel_work_sync(&sdata->csa_finalize_work);
+       cancel_work_sync(&sdata->u.ap.request_smps_work);
 
        /* turn off carrier for this interface and dependent VLANs */
        list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
@@ -1090,8 +1111,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);
        }
 
@@ -1342,8 +1365,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                                sta->plink_state = params->plink_state;
 
                                ieee80211_mps_sta_status_update(sta);
-                               changed |=
-                                     ieee80211_mps_local_status_update(sdata);
+                               changed |= ieee80211_mps_set_sta_local_pm(sta,
+                                               NL80211_MESH_POWER_UNKNOWN);
                                break;
                        default:
                                /*  nothing  */
@@ -1553,6 +1576,20 @@ static int ieee80211_change_station(struct wiphy *wiphy,
 
        mutex_unlock(&local->sta_mtx);
 
+       if ((sdata->vif.type == NL80211_IFTYPE_AP ||
+            sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
+           sta->known_smps_mode != sta->sdata->bss->req_smps &&
+           test_sta_flag(sta, WLAN_STA_AUTHORIZED) &&
+           sta_info_tx_streams(sta) != 1) {
+               ht_dbg(sta->sdata,
+                      "%pM just authorized and MIMO capable - update SMPS\n",
+                      sta->sta.addr);
+               ieee80211_send_smps_action(sta->sdata,
+                       sta->sdata->bss->req_smps,
+                       sta->sta.addr,
+                       sta->sdata->vif.bss_conf.bssid);
+       }
+
        if (sdata->vif.type == NL80211_IFTYPE_STATION &&
            params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
                ieee80211_recalc_ps(local, -1);
@@ -2337,8 +2374,92 @@ static int ieee80211_testmode_dump(struct wiphy *wiphy,
 }
 #endif
 
-int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
-                            enum ieee80211_smps_mode smps_mode)
+int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
+                               enum ieee80211_smps_mode smps_mode)
+{
+       struct sta_info *sta;
+       enum ieee80211_smps_mode old_req;
+       int i;
+
+       if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP))
+               return -EINVAL;
+
+       if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
+               return 0;
+
+       old_req = sdata->u.ap.req_smps;
+       sdata->u.ap.req_smps = smps_mode;
+
+       /* AUTOMATIC doesn't mean much for AP - don't allow it */
+       if (old_req == smps_mode ||
+           smps_mode == IEEE80211_SMPS_AUTOMATIC)
+               return 0;
+
+        /* If no associated stations, there's no need to do anything */
+       if (!atomic_read(&sdata->u.ap.num_mcast_sta)) {
+               sdata->smps_mode = smps_mode;
+               ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps);
+               return 0;
+       }
+
+       ht_dbg(sdata,
+              "SMSP %d requested in AP mode, sending Action frame to %d stations\n",
+              smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta));
+
+       mutex_lock(&sdata->local->sta_mtx);
+       for (i = 0; i < STA_HASH_SIZE; i++) {
+               for (sta = rcu_dereference_protected(sdata->local->sta_hash[i],
+                               lockdep_is_held(&sdata->local->sta_mtx));
+                    sta;
+                    sta = rcu_dereference_protected(sta->hnext,
+                               lockdep_is_held(&sdata->local->sta_mtx))) {
+                       /*
+                        * Only stations associated to our AP and
+                        * associated VLANs
+                        */
+                       if (sta->sdata->bss != &sdata->u.ap)
+                               continue;
+
+                       /* This station doesn't support MIMO - skip it */
+                       if (sta_info_tx_streams(sta) == 1)
+                               continue;
+
+                       /*
+                        * Don't wake up a STA just to send the action frame
+                        * unless we are getting more restrictive.
+                        */
+                       if (test_sta_flag(sta, WLAN_STA_PS_STA) &&
+                           !ieee80211_smps_is_restrictive(sta->known_smps_mode,
+                                                          smps_mode)) {
+                               ht_dbg(sdata,
+                                      "Won't send SMPS to sleeping STA %pM\n",
+                                      sta->sta.addr);
+                               continue;
+                       }
+
+                       /*
+                        * If the STA is not authorized, wait until it gets
+                        * authorized and the action frame will be sent then.
+                        */
+                       if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+                               continue;
+
+                       ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr);
+                       ieee80211_send_smps_action(sdata, smps_mode,
+                                                  sta->sta.addr,
+                                                  sdata->vif.bss_conf.bssid);
+               }
+       }
+       mutex_unlock(&sdata->local->sta_mtx);
+
+       sdata->smps_mode = smps_mode;
+       ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps);
+
+       return 0;
+}
+
+int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
+                                enum ieee80211_smps_mode smps_mode)
 {
        const u8 *ap;
        enum ieee80211_smps_mode old_req;
@@ -2346,6 +2467,9 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
 
        lockdep_assert_held(&sdata->wdev.mtx);
 
+       if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION))
+               return -EINVAL;
+
        old_req = sdata->u.mgd.req_smps;
        sdata->u.mgd.req_smps = smps_mode;
 
@@ -2402,7 +2526,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
 
        /* no change, but if automatic follow powersave */
        sdata_lock(sdata);
-       __ieee80211_request_smps(sdata, sdata->u.mgd.req_smps);
+       __ieee80211_request_smps_mgd(sdata, sdata->u.mgd.req_smps);
        sdata_unlock(sdata);
 
        if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
@@ -2860,35 +2984,55 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
                container_of(work, struct ieee80211_sub_if_data,
                             csa_finalize_work);
        struct ieee80211_local *local = sdata->local;
-       int err, changed;
+       int err, changed = 0;
 
        if (!ieee80211_sdata_running(sdata))
                return;
 
-       if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
-               return;
-
        sdata->radar_required = sdata->csa_radar_required;
        err = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
                                           &changed);
        if (WARN_ON(err < 0))
                return;
 
-       err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
-       if (err < 0)
-               return;
+       if (!local->use_chanctx) {
+               local->_oper_chandef = local->csa_chandef;
+               ieee80211_hw_config(local, 0);
+       }
 
-       changed |= err;
-       kfree(sdata->u.ap.next_beacon);
-       sdata->u.ap.next_beacon = NULL;
+       ieee80211_bss_info_change_notify(sdata, changed);
+
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_AP:
+               err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
+               if (err < 0)
+                       return;
+               changed |= err;
+               kfree(sdata->u.ap.next_beacon);
+               sdata->u.ap.next_beacon = NULL;
+
+               ieee80211_bss_info_change_notify(sdata, err);
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               ieee80211_ibss_finish_csa(sdata);
+               break;
+#ifdef CONFIG_MAC80211_MESH
+       case NL80211_IFTYPE_MESH_POINT:
+               err = ieee80211_mesh_finish_csa(sdata);
+               if (err < 0)
+                       return;
+               break;
+#endif
+       default:
+               WARN_ON(1);
+               return;
+       }
        sdata->vif.csa_active = false;
 
        ieee80211_wake_queues_by_reason(&sdata->local->hw,
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
 
-       ieee80211_bss_info_change_notify(sdata, changed);
-
        cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef);
 }
 
@@ -2899,6 +3043,7 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_chanctx *chanctx;
+       struct ieee80211_if_mesh __maybe_unused *ifmsh;
        int err, num_chanctx;
 
        if (!list_empty(&local->roc_list) || local->scanning)
@@ -2936,20 +3081,76 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        if (sdata->vif.csa_active)
                return -EBUSY;
 
-       /* only handle AP for now. */
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
+               sdata->csa_counter_offset_beacon =
+                       params->counter_offset_beacon;
+               sdata->csa_counter_offset_presp = params->counter_offset_presp;
+               sdata->u.ap.next_beacon =
+                       cfg80211_beacon_dup(&params->beacon_after);
+               if (!sdata->u.ap.next_beacon)
+                       return -ENOMEM;
+
+               err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
+               if (err < 0) {
+                       kfree(sdata->u.ap.next_beacon);
+                       return err;
+               }
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               if (!sdata->vif.bss_conf.ibss_joined)
+                       return -EINVAL;
+
+               if (params->chandef.width != sdata->u.ibss.chandef.width)
+                       return -EINVAL;
+
+               switch (params->chandef.width) {
+               case NL80211_CHAN_WIDTH_40:
+                       if (cfg80211_get_chandef_type(&params->chandef) !=
+                           cfg80211_get_chandef_type(&sdata->u.ibss.chandef))
+                               return -EINVAL;
+               case NL80211_CHAN_WIDTH_5:
+               case NL80211_CHAN_WIDTH_10:
+               case NL80211_CHAN_WIDTH_20_NOHT:
+               case NL80211_CHAN_WIDTH_20:
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               /* changes into another band are not supported */
+               if (sdata->u.ibss.chandef.chan->band !=
+                   params->chandef.chan->band)
+                       return -EINVAL;
+
+               err = ieee80211_ibss_csa_beacon(sdata, params);
+               if (err < 0)
+                       return err;
+               break;
+#ifdef CONFIG_MAC80211_MESH
+       case NL80211_IFTYPE_MESH_POINT:
+               ifmsh = &sdata->u.mesh;
+
+               if (!ifmsh->mesh_id)
+                       return -EINVAL;
+
+               if (params->chandef.width != sdata->vif.bss_conf.chandef.width)
+                       return -EINVAL;
+
+               /* changes into another band are not supported */
+               if (sdata->vif.bss_conf.chandef.chan->band !=
+                   params->chandef.chan->band)
+                       return -EINVAL;
+
+               err = ieee80211_mesh_csa_beacon(sdata, params, true);
+               if (err < 0)
+                       return err;
                break;
+#endif
        default:
                return -EOPNOTSUPP;
        }
 
-       sdata->u.ap.next_beacon = cfg80211_beacon_dup(&params->beacon_after);
-       if (!sdata->u.ap.next_beacon)
-               return -ENOMEM;
-
-       sdata->csa_counter_offset_beacon = params->counter_offset_beacon;
-       sdata->csa_counter_offset_presp = params->counter_offset_presp;
        sdata->csa_radar_required = params->radar_required;
 
        if (params->block_tx)
@@ -2957,10 +3158,6 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                                IEEE80211_MAX_QUEUE_MAP,
                                IEEE80211_QUEUE_STOP_REASON_CSA);
 
-       err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
-       if (err < 0)
-               return err;
-
        local->csa_chandef = params->chandef;
        sdata->vif.csa_active = true;
 
@@ -3014,7 +3211,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                        need_offchan = true;
                if (!ieee80211_is_action(mgmt->frame_control) ||
                    mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
-                   mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED)
+                   mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED ||
+                   mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT)
                        break;
                rcu_read_lock();
                sta = sta_info_get(sdata, mgmt->da);
@@ -3518,7 +3716,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
                return -EINVAL;
        }
        band = chanctx_conf->def.chan->band;
-       sta = sta_info_get(sdata, peer);
+       sta = sta_info_get_bss(sdata, peer);
        if (sta) {
                qos = test_sta_flag(sta, WLAN_STA_WME);
        } else {