]> git.karo-electronics.de Git - linux-beck.git/blobdiff - net/mac80211/cfg.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[linux-beck.git] / net / mac80211 / cfg.c
index a6893602f87a753ebafeae0d0b8f449bdc7251f7..c34e6d78a592b302969a998777812cd7639e84c5 100644 (file)
@@ -254,7 +254,7 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
                goto out_unlock;
        }
 
-       __ieee80211_key_free(key);
+       __ieee80211_key_free(key, true);
 
        ret = 0;
  out_unlock:
@@ -1035,9 +1035,12 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
                sta_info_flush_defer(vlan);
        sta_info_flush_defer(sdata);
        rcu_barrier();
-       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+       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);
 
        sdata->vif.bss_conf.enable_beacon = false;
        clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
@@ -1177,6 +1180,18 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                        mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
                if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
                        set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+       } else if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+               /*
+                * TDLS -- everything follows authorized, but
+                * only becoming authorized is possible, not
+                * going back
+                */
+               if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
+                       set |= BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+                              BIT(NL80211_STA_FLAG_ASSOCIATED);
+                       mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+                               BIT(NL80211_STA_FLAG_ASSOCIATED);
+               }
        }
 
        ret = sta_apply_auth_flags(local, sta, mask, set);
@@ -1261,7 +1276,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
        if (ieee80211_vif_is_mesh(&sdata->vif)) {
 #ifdef CONFIG_MAC80211_MESH
                u32 changed = 0;
-               if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) {
+
+               if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
                        switch (params->plink_state) {
                        case NL80211_PLINK_ESTAB:
                                if (sta->plink_state != NL80211_PLINK_ESTAB)
@@ -1292,15 +1308,18 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                                /*  nothing  */
                                break;
                        }
-               } else {
-                       switch (params->plink_action) {
-                       case PLINK_ACTION_OPEN:
-                               changed |= mesh_plink_open(sta);
-                               break;
-                       case PLINK_ACTION_BLOCK:
-                               changed |= mesh_plink_block(sta);
-                               break;
-                       }
+               }
+
+               switch (params->plink_action) {
+               case NL80211_PLINK_ACTION_NO_ACTION:
+                       /* nothing */
+                       break;
+               case NL80211_PLINK_ACTION_OPEN:
+                       changed |= mesh_plink_open(sta);
+                       break;
+               case NL80211_PLINK_ACTION_BLOCK:
+                       changed |= mesh_plink_block(sta);
+                       break;
                }
 
                if (params->local_pm)
@@ -1346,8 +1365,10 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
         * defaults -- if userspace wants something else we'll
         * change it accordingly in sta_apply_parameters()
         */
-       sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
-       sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
+       if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) {
+               sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
+               sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
+       }
 
        err = sta_apply_parameters(local, sta, params);
        if (err) {
@@ -1356,8 +1377,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
        }
 
        /*
-        * for TDLS, rate control should be initialized only when supported
-        * rates are known.
+        * for TDLS, rate control should be initialized only when
+        * rates are known and station is marked authorized
         */
        if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER))
                rate_control_rate_init(sta);
@@ -1394,50 +1415,67 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
 }
 
 static int ieee80211_change_station(struct wiphy *wiphy,
-                                   struct net_device *dev,
-                                   u8 *mac,
+                                   struct net_device *dev, u8 *mac,
                                    struct station_parameters *params)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = wiphy_priv(wiphy);
        struct sta_info *sta;
        struct ieee80211_sub_if_data *vlansdata;
+       enum cfg80211_station_type statype;
        int err;
 
        mutex_lock(&local->sta_mtx);
 
        sta = sta_info_get_bss(sdata, mac);
        if (!sta) {
-               mutex_unlock(&local->sta_mtx);
-               return -ENOENT;
+               err = -ENOENT;
+               goto out_err;
        }
 
-       /* in station mode, some updates are only valid with TDLS */
-       if (sdata->vif.type == NL80211_IFTYPE_STATION &&
-           (params->supported_rates || params->ht_capa || params->vht_capa ||
-            params->sta_modify_mask ||
-            (params->sta_flags_mask & BIT(NL80211_STA_FLAG_WME))) &&
-           !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
-               mutex_unlock(&local->sta_mtx);
-               return -EINVAL;
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_MESH_POINT:
+               if (sdata->u.mesh.user_mpm)
+                       statype = CFG80211_STA_MESH_PEER_USER;
+               else
+                       statype = CFG80211_STA_MESH_PEER_KERNEL;
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               statype = CFG80211_STA_IBSS;
+               break;
+       case NL80211_IFTYPE_STATION:
+               if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+                       statype = CFG80211_STA_AP_STA;
+                       break;
+               }
+               if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+                       statype = CFG80211_STA_TDLS_PEER_ACTIVE;
+               else
+                       statype = CFG80211_STA_TDLS_PEER_SETUP;
+               break;
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_AP_VLAN:
+               statype = CFG80211_STA_AP_CLIENT;
+               break;
+       default:
+               err = -EOPNOTSUPP;
+               goto out_err;
        }
 
+       err = cfg80211_check_station_change(wiphy, params, statype);
+       if (err)
+               goto out_err;
+
        if (params->vlan && params->vlan != sta->sdata->dev) {
                bool prev_4addr = false;
                bool new_4addr = false;
 
                vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
 
-               if (vlansdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
-                   vlansdata->vif.type != NL80211_IFTYPE_AP) {
-                       mutex_unlock(&local->sta_mtx);
-                       return -EINVAL;
-               }
-
                if (params->vlan->ieee80211_ptr->use_4addr) {
                        if (vlansdata->u.vlan.sta) {
-                               mutex_unlock(&local->sta_mtx);
-                               return -EBUSY;
+                               err = -EBUSY;
+                               goto out_err;
                        }
 
                        rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
@@ -1464,12 +1502,12 @@ static int ieee80211_change_station(struct wiphy *wiphy,
        }
 
        err = sta_apply_parameters(local, sta, params);
-       if (err) {
-               mutex_unlock(&local->sta_mtx);
-               return err;
-       }
+       if (err)
+               goto out_err;
 
-       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && params->supported_rates)
+       /* When peer becomes authorized, init rate control as well */
+       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
+           test_sta_flag(sta, WLAN_STA_AUTHORIZED))
                rate_control_rate_init(sta);
 
        mutex_unlock(&local->sta_mtx);
@@ -1479,7 +1517,11 @@ static int ieee80211_change_station(struct wiphy *wiphy,
                ieee80211_recalc_ps(local, -1);
                ieee80211_recalc_ps_vif(sdata);
        }
+
        return 0;
+out_err:
+       mutex_unlock(&local->sta_mtx);
+       return err;
 }
 
 #ifdef CONFIG_MAC80211_MESH
@@ -1687,6 +1729,7 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
        ifmsh->mesh_sp_id = setup->sync_method;
        ifmsh->mesh_pp_id = setup->path_sel_proto;
        ifmsh->mesh_pm_id = setup->path_metric;
+       ifmsh->user_mpm = setup->user_mpm;
        ifmsh->security = IEEE80211_MESH_SEC_NONE;
        if (setup->is_authenticated)
                ifmsh->security |= IEEE80211_MESH_SEC_AUTHED;
@@ -1730,8 +1773,11 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
                conf->dot11MeshTTL = nconf->dot11MeshTTL;
        if (_chg_mesh_attr(NL80211_MESHCONF_ELEMENT_TTL, mask))
                conf->element_ttl = nconf->element_ttl;
-       if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask))
+       if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask)) {
+               if (ifmsh->user_mpm)
+                       return -EBUSY;
                conf->auto_open_plinks = nconf->auto_open_plinks;
+       }
        if (_chg_mesh_attr(NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, mask))
                conf->dot11MeshNbrOffsetMaxNeighbor =
                        nconf->dot11MeshNbrOffsetMaxNeighbor;
@@ -2371,7 +2417,8 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
                                    struct ieee80211_sub_if_data *sdata,
                                    struct ieee80211_channel *channel,
                                    unsigned int duration, u64 *cookie,
-                                   struct sk_buff *txskb)
+                                   struct sk_buff *txskb,
+                                   enum ieee80211_roc_type type)
 {
        struct ieee80211_roc_work *roc, *tmp;
        bool queued = false;
@@ -2390,6 +2437,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
        roc->duration = duration;
        roc->req_duration = duration;
        roc->frame = txskb;
+       roc->type = type;
        roc->mgmt_tx_cookie = (unsigned long)txskb;
        roc->sdata = sdata;
        INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);
@@ -2420,7 +2468,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
        if (!duration)
                duration = 10;
 
-       ret = drv_remain_on_channel(local, sdata, channel, duration);
+       ret = drv_remain_on_channel(local, sdata, channel, duration, type);
        if (ret) {
                kfree(roc);
                return ret;
@@ -2439,10 +2487,13 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
                 *
                 * If it hasn't started yet, just increase the duration
                 * and add the new one to the list of dependents.
+                * If the type of the new ROC has higher priority, modify the
+                * type of the previous one to match that of the new one.
                 */
                if (!tmp->started) {
                        list_add_tail(&roc->list, &tmp->dependents);
                        tmp->duration = max(tmp->duration, roc->duration);
+                       tmp->type = max(tmp->type, roc->type);
                        queued = true;
                        break;
                }
@@ -2454,16 +2505,18 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
                        /*
                         * In the offloaded ROC case, if it hasn't begun, add
                         * this new one to the dependent list to be handled
-                        * when the the master one begins. If it has begun,
+                        * when the master one begins. If it has begun,
                         * check that there's still a minimum time left and
                         * if so, start this one, transmitting the frame, but
-                        * add it to the list directly after this one with a
+                        * add it to the list directly after this one with
                         * a reduced time so we'll ask the driver to execute
                         * it right after finishing the previous one, in the
                         * hope that it'll also be executed right afterwards,
                         * effectively extending the old one.
                         * If there's no minimum time left, just add it to the
                         * normal list.
+                        * TODO: the ROC type is ignored here, assuming that it
+                        * is better to immediately use the current ROC.
                         */
                        if (!tmp->hw_begun) {
                                list_add_tail(&roc->list, &tmp->dependents);
@@ -2557,7 +2610,8 @@ static int ieee80211_remain_on_channel(struct wiphy *wiphy,
 
        mutex_lock(&local->mtx);
        ret = ieee80211_start_roc_work(local, sdata, chan,
-                                      duration, cookie, NULL);
+                                      duration, cookie, NULL,
+                                      IEEE80211_ROC_TYPE_NORMAL);
        mutex_unlock(&local->mtx);
 
        return ret;
@@ -2792,7 +2846,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 
        /* This will handle all kinds of coalescing and immediate TX */
        ret = ieee80211_start_roc_work(local, sdata, chan,
-                                      wait, cookie, skb);
+                                      wait, cookie, skb,
+                                      IEEE80211_ROC_TYPE_MGMT_TX);
        if (ret)
                kfree_skb(skb);
  out_unlock: