]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - net/mac80211/cfg.c
xfrm_user: validate XFRM_MSG_NEWAE incoming ESN size harder
[mirror_ubuntu-bionic-kernel.git] / net / mac80211 / cfg.c
index 543b1d4fc33d563f7a4234c92ff7d22d92e01265..e91e503bf99257d7e8f3945c1500eebaa91f5e81 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2015  Intel Mobile Communications GmbH
+ * Copyright (C) 2015-2016 Intel Deutschland GmbH
  *
  * This file is GPLv2 as found in COPYING.
  */
@@ -39,7 +40,7 @@ static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
 
        if (type == NL80211_IFTYPE_MONITOR && flags) {
                sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
-               sdata->u.mntr_flags = *flags;
+               sdata->u.mntr.flags = *flags;
        }
 
        return wdev;
@@ -73,8 +74,29 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
                sdata->u.mgd.use_4addr = params->use_4addr;
        }
 
-       if (sdata->vif.type == NL80211_IFTYPE_MONITOR && flags) {
+       if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
                struct ieee80211_local *local = sdata->local;
+               struct ieee80211_sub_if_data *monitor_sdata;
+               u32 mu_mntr_cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER;
+
+               monitor_sdata = rtnl_dereference(local->monitor_sdata);
+               if (monitor_sdata &&
+                   wiphy_ext_feature_isset(wiphy, mu_mntr_cap_flag)) {
+                       memcpy(monitor_sdata->vif.bss_conf.mu_group.membership,
+                              params->vht_mumimo_groups, WLAN_MEMBERSHIP_LEN);
+                       memcpy(monitor_sdata->vif.bss_conf.mu_group.position,
+                              params->vht_mumimo_groups + WLAN_MEMBERSHIP_LEN,
+                              WLAN_USER_POSITION_LEN);
+                       monitor_sdata->vif.mu_mimo_owner = true;
+                       ieee80211_bss_info_change_notify(monitor_sdata,
+                                                        BSS_CHANGED_MU_GROUPS);
+
+                       ether_addr_copy(monitor_sdata->u.mntr.mu_follow_addr,
+                                       params->macaddr);
+               }
+
+               if (!flags)
+                       return 0;
 
                if (ieee80211_sdata_running(sdata)) {
                        u32 mask = MONITOR_FLAG_COOK_FRAMES |
@@ -89,11 +111,11 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
                         *      cooked_mntrs, monitor and all fif_* counters
                         *      reconfigure hardware
                         */
-                       if ((*flags & mask) != (sdata->u.mntr_flags & mask))
+                       if ((*flags & mask) != (sdata->u.mntr.flags & mask))
                                return -EBUSY;
 
                        ieee80211_adjust_monitor_flags(sdata, -1);
-                       sdata->u.mntr_flags = *flags;
+                       sdata->u.mntr.flags = *flags;
                        ieee80211_adjust_monitor_flags(sdata, 1);
 
                        ieee80211_configure_filter(local);
@@ -103,7 +125,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
                         * and ieee80211_do_open take care of "everything"
                         * mentioned in the comment above.
                         */
-                       sdata->u.mntr_flags = *flags;
+                       sdata->u.mntr.flags = *flags;
                }
        }
 
@@ -131,6 +153,149 @@ static void ieee80211_stop_p2p_device(struct wiphy *wiphy,
        ieee80211_sdata_stop(IEEE80211_WDEV_TO_SUB_IF(wdev));
 }
 
+static int ieee80211_start_nan(struct wiphy *wiphy,
+                              struct wireless_dev *wdev,
+                              struct cfg80211_nan_conf *conf)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+       int ret;
+
+       mutex_lock(&sdata->local->chanctx_mtx);
+       ret = ieee80211_check_combinations(sdata, NULL, 0, 0);
+       mutex_unlock(&sdata->local->chanctx_mtx);
+       if (ret < 0)
+               return ret;
+
+       ret = ieee80211_do_open(wdev, true);
+       if (ret)
+               return ret;
+
+       ret = drv_start_nan(sdata->local, sdata, conf);
+       if (ret)
+               ieee80211_sdata_stop(sdata);
+
+       sdata->u.nan.conf = *conf;
+
+       return ret;
+}
+
+static void ieee80211_stop_nan(struct wiphy *wiphy,
+                              struct wireless_dev *wdev)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+
+       drv_stop_nan(sdata->local, sdata);
+       ieee80211_sdata_stop(sdata);
+}
+
+static int ieee80211_nan_change_conf(struct wiphy *wiphy,
+                                    struct wireless_dev *wdev,
+                                    struct cfg80211_nan_conf *conf,
+                                    u32 changes)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+       struct cfg80211_nan_conf new_conf;
+       int ret = 0;
+
+       if (sdata->vif.type != NL80211_IFTYPE_NAN)
+               return -EOPNOTSUPP;
+
+       if (!ieee80211_sdata_running(sdata))
+               return -ENETDOWN;
+
+       new_conf = sdata->u.nan.conf;
+
+       if (changes & CFG80211_NAN_CONF_CHANGED_PREF)
+               new_conf.master_pref = conf->master_pref;
+
+       if (changes & CFG80211_NAN_CONF_CHANGED_DUAL)
+               new_conf.dual = conf->dual;
+
+       ret = drv_nan_change_conf(sdata->local, sdata, &new_conf, changes);
+       if (!ret)
+               sdata->u.nan.conf = new_conf;
+
+       return ret;
+}
+
+static int ieee80211_add_nan_func(struct wiphy *wiphy,
+                                 struct wireless_dev *wdev,
+                                 struct cfg80211_nan_func *nan_func)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+       int ret;
+
+       if (sdata->vif.type != NL80211_IFTYPE_NAN)
+               return -EOPNOTSUPP;
+
+       if (!ieee80211_sdata_running(sdata))
+               return -ENETDOWN;
+
+       spin_lock_bh(&sdata->u.nan.func_lock);
+
+       ret = idr_alloc(&sdata->u.nan.function_inst_ids,
+                       nan_func, 1, sdata->local->hw.max_nan_de_entries + 1,
+                       GFP_ATOMIC);
+       spin_unlock_bh(&sdata->u.nan.func_lock);
+
+       if (ret < 0)
+               return ret;
+
+       nan_func->instance_id = ret;
+
+       WARN_ON(nan_func->instance_id == 0);
+
+       ret = drv_add_nan_func(sdata->local, sdata, nan_func);
+       if (ret) {
+               spin_lock_bh(&sdata->u.nan.func_lock);
+               idr_remove(&sdata->u.nan.function_inst_ids,
+                          nan_func->instance_id);
+               spin_unlock_bh(&sdata->u.nan.func_lock);
+       }
+
+       return ret;
+}
+
+static struct cfg80211_nan_func *
+ieee80211_find_nan_func_by_cookie(struct ieee80211_sub_if_data *sdata,
+                                 u64 cookie)
+{
+       struct cfg80211_nan_func *func;
+       int id;
+
+       lockdep_assert_held(&sdata->u.nan.func_lock);
+
+       idr_for_each_entry(&sdata->u.nan.function_inst_ids, func, id) {
+               if (func->cookie == cookie)
+                       return func;
+       }
+
+       return NULL;
+}
+
+static void ieee80211_del_nan_func(struct wiphy *wiphy,
+                                 struct wireless_dev *wdev, u64 cookie)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+       struct cfg80211_nan_func *func;
+       u8 instance_id = 0;
+
+       if (sdata->vif.type != NL80211_IFTYPE_NAN ||
+           !ieee80211_sdata_running(sdata))
+               return;
+
+       spin_lock_bh(&sdata->u.nan.func_lock);
+
+       func = ieee80211_find_nan_func_by_cookie(sdata, cookie);
+       if (func)
+               instance_id = func->instance_id;
+
+       spin_unlock_bh(&sdata->u.nan.func_lock);
+
+       if (instance_id)
+               drv_del_nan_func(sdata->local, sdata, instance_id);
+}
+
 static int ieee80211_set_noack_map(struct wiphy *wiphy,
                                  struct net_device *dev,
                                  u16 noack_map)
@@ -192,10 +357,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
        mutex_lock(&local->sta_mtx);
 
        if (mac_addr) {
-               if (ieee80211_vif_is_mesh(&sdata->vif))
-                       sta = sta_info_get(sdata, mac_addr);
-               else
-                       sta = sta_info_get_bss(sdata, mac_addr);
+               sta = sta_info_get_bss(sdata, mac_addr);
                /*
                 * The ASSOC test makes sure the driver is ready to
                 * receive the key. When wpa_supplicant has roamed
@@ -236,6 +398,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
        case NL80211_IFTYPE_WDS:
        case NL80211_IFTYPE_MONITOR:
        case NL80211_IFTYPE_P2P_DEVICE:
+       case NL80211_IFTYPE_NAN:
        case NL80211_IFTYPE_UNSPECIFIED:
        case NUM_NL80211_IFTYPES:
        case NL80211_IFTYPE_P2P_CLIENT:
@@ -701,6 +864,8 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
        }
        sdata->needed_rx_chains = sdata->local->rx_chains;
 
+       sdata->vif.bss_conf.beacon_int = params->beacon_interval;
+
        mutex_lock(&local->mtx);
        err = ieee80211_vif_use_channel(sdata, &params->chandef,
                                        IEEE80211_CHANCTX_SHARED);
@@ -731,7 +896,6 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
                                              vlan->vif.type);
        }
 
-       sdata->vif.bss_conf.beacon_int = params->beacon_interval;
        sdata->vif.bss_conf.dtim_period = params->dtim_period;
        sdata->vif.bss_conf.enable_beacon = true;
        sdata->vif.bss_conf.allow_p2p_go_ps = sdata->vif.p2p;
@@ -1357,9 +1521,6 @@ static int ieee80211_change_station(struct wiphy *wiphy,
                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 (params->vlan->ieee80211_ptr->use_4addr) {
@@ -1369,26 +1530,21 @@ static int ieee80211_change_station(struct wiphy *wiphy,
                        }
 
                        rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
-                       new_4addr = true;
                        __ieee80211_check_fast_rx_iface(vlansdata);
                }
 
                if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
-                   sta->sdata->u.vlan.sta) {
+                   sta->sdata->u.vlan.sta)
                        RCU_INIT_POINTER(sta->sdata->u.vlan.sta, NULL);
-                       prev_4addr = true;
-               }
+
+               if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+                       ieee80211_vif_dec_num_mcast(sta->sdata);
 
                sta->sdata = vlansdata;
                ieee80211_check_fast_xmit(sta);
 
-               if (sta->sta_state == IEEE80211_STA_AUTHORIZED &&
-                   prev_4addr != new_4addr) {
-                       if (new_4addr)
-                               atomic_dec(&sta->sdata->bss->num_mcast_sta);
-                       else
-                               atomic_inc(&sta->sdata->bss->num_mcast_sta);
-               }
+               if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+                       ieee80211_vif_inc_num_mcast(sta->sdata);
 
                ieee80211_send_layer2_update(sta);
        }
@@ -2015,6 +2171,7 @@ static int ieee80211_scan(struct wiphy *wiphy,
                     !(req->flags & NL80211_SCAN_FLAG_AP)))
                        return -EOPNOTSUPP;
                break;
+       case NL80211_IFTYPE_NAN:
        default:
                return -EOPNOTSUPP;
        }
@@ -2313,13 +2470,6 @@ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
            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,
               "SMPS %d requested in AP mode, sending Action frame to %d stations\n",
               smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta));
@@ -2940,10 +3090,6 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        }
 
        chanctx = container_of(conf, struct ieee80211_chanctx, conf);
-       if (!chanctx) {
-               err = -EBUSY;
-               goto out;
-       }
 
        ch_switch.timestamp = 0;
        ch_switch.device_timestamp = 0;
@@ -3360,6 +3506,63 @@ static int ieee80211_del_tx_ts(struct wiphy *wiphy, struct net_device *dev,
        return -ENOENT;
 }
 
+void ieee80211_nan_func_terminated(struct ieee80211_vif *vif,
+                                  u8 inst_id,
+                                  enum nl80211_nan_func_term_reason reason,
+                                  gfp_t gfp)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct cfg80211_nan_func *func;
+       u64 cookie;
+
+       if (WARN_ON(vif->type != NL80211_IFTYPE_NAN))
+               return;
+
+       spin_lock_bh(&sdata->u.nan.func_lock);
+
+       func = idr_find(&sdata->u.nan.function_inst_ids, inst_id);
+       if (WARN_ON(!func)) {
+               spin_unlock_bh(&sdata->u.nan.func_lock);
+               return;
+       }
+
+       cookie = func->cookie;
+       idr_remove(&sdata->u.nan.function_inst_ids, inst_id);
+
+       spin_unlock_bh(&sdata->u.nan.func_lock);
+
+       cfg80211_free_nan_func(func);
+
+       cfg80211_nan_func_terminated(ieee80211_vif_to_wdev(vif), inst_id,
+                                    reason, cookie, gfp);
+}
+EXPORT_SYMBOL(ieee80211_nan_func_terminated);
+
+void ieee80211_nan_func_match(struct ieee80211_vif *vif,
+                             struct cfg80211_nan_match_params *match,
+                             gfp_t gfp)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct cfg80211_nan_func *func;
+
+       if (WARN_ON(vif->type != NL80211_IFTYPE_NAN))
+               return;
+
+       spin_lock_bh(&sdata->u.nan.func_lock);
+
+       func = idr_find(&sdata->u.nan.function_inst_ids,  match->inst_id);
+       if (WARN_ON(!func)) {
+               spin_unlock_bh(&sdata->u.nan.func_lock);
+               return;
+       }
+       match->cookie = func->cookie;
+
+       spin_unlock_bh(&sdata->u.nan.func_lock);
+
+       cfg80211_nan_match(ieee80211_vif_to_wdev(vif), match, gfp);
+}
+EXPORT_SYMBOL(ieee80211_nan_func_match);
+
 const struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
@@ -3445,4 +3648,9 @@ const struct cfg80211_ops mac80211_config_ops = {
        .set_ap_chanwidth = ieee80211_set_ap_chanwidth,
        .add_tx_ts = ieee80211_add_tx_ts,
        .del_tx_ts = ieee80211_del_tx_ts,
+       .start_nan = ieee80211_start_nan,
+       .stop_nan = ieee80211_stop_nan,
+       .nan_change_conf = ieee80211_nan_change_conf,
+       .add_nan_func = ieee80211_add_nan_func,
+       .del_nan_func = ieee80211_del_nan_func,
 };