Merge tag 'gcc-plugins-v4.9-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git...
[cascardo/linux.git] / net / wireless / nl80211.c
index 4809f4d..c510810 100644 (file)
@@ -56,6 +56,7 @@ enum nl80211_multicast_groups {
        NL80211_MCGRP_REGULATORY,
        NL80211_MCGRP_MLME,
        NL80211_MCGRP_VENDOR,
+       NL80211_MCGRP_NAN,
        NL80211_MCGRP_TESTMODE /* keep last - ifdef! */
 };
 
@@ -65,6 +66,7 @@ static const struct genl_multicast_group nl80211_mcgrps[] = {
        [NL80211_MCGRP_REGULATORY] = { .name = NL80211_MULTICAST_GROUP_REG },
        [NL80211_MCGRP_MLME] = { .name = NL80211_MULTICAST_GROUP_MLME },
        [NL80211_MCGRP_VENDOR] = { .name = NL80211_MULTICAST_GROUP_VENDOR },
+       [NL80211_MCGRP_NAN] = { .name = NL80211_MULTICAST_GROUP_NAN },
 #ifdef CONFIG_NL80211_TESTMODE
        [NL80211_MCGRP_TESTMODE] = { .name = NL80211_MULTICAST_GROUP_TESTMODE }
 #endif
@@ -409,6 +411,9 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
                .len = VHT_MUMIMO_GROUPS_DATA_LEN
        },
        [NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR] = { .len = ETH_ALEN },
+       [NL80211_ATTR_NAN_MASTER_PREF] = { .type = NLA_U8 },
+       [NL80211_ATTR_NAN_DUAL] = { .type = NLA_U8 },
+       [NL80211_ATTR_NAN_FUNC] = { .type = NLA_NESTED },
 };
 
 /* policy for the key attributes */
@@ -502,6 +507,39 @@ nl80211_bss_select_policy[NL80211_BSS_SELECT_ATTR_MAX + 1] = {
        },
 };
 
+/* policy for NAN function attributes */
+static const struct nla_policy
+nl80211_nan_func_policy[NL80211_NAN_FUNC_ATTR_MAX + 1] = {
+       [NL80211_NAN_FUNC_TYPE] = { .type = NLA_U8 },
+       [NL80211_NAN_FUNC_SERVICE_ID] = { .type = NLA_BINARY,
+                                   .len = NL80211_NAN_FUNC_SERVICE_ID_LEN },
+       [NL80211_NAN_FUNC_PUBLISH_TYPE] = { .type = NLA_U8 },
+       [NL80211_NAN_FUNC_PUBLISH_BCAST] = { .type = NLA_FLAG },
+       [NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE] = { .type = NLA_FLAG },
+       [NL80211_NAN_FUNC_FOLLOW_UP_ID] = { .type = NLA_U8 },
+       [NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID] = { .type = NLA_U8 },
+       [NL80211_NAN_FUNC_FOLLOW_UP_DEST] = { .len = ETH_ALEN },
+       [NL80211_NAN_FUNC_CLOSE_RANGE] = { .type = NLA_FLAG },
+       [NL80211_NAN_FUNC_TTL] = { .type = NLA_U32 },
+       [NL80211_NAN_FUNC_SERVICE_INFO] = { .type = NLA_BINARY,
+                       .len = NL80211_NAN_FUNC_SERVICE_SPEC_INFO_MAX_LEN },
+       [NL80211_NAN_FUNC_SRF] = { .type = NLA_NESTED },
+       [NL80211_NAN_FUNC_RX_MATCH_FILTER] = { .type = NLA_NESTED },
+       [NL80211_NAN_FUNC_TX_MATCH_FILTER] = { .type = NLA_NESTED },
+       [NL80211_NAN_FUNC_INSTANCE_ID] = { .type = NLA_U8 },
+       [NL80211_NAN_FUNC_TERM_REASON] = { .type = NLA_U8 },
+};
+
+/* policy for Service Response Filter attributes */
+static const struct nla_policy
+nl80211_nan_srf_policy[NL80211_NAN_SRF_ATTR_MAX + 1] = {
+       [NL80211_NAN_SRF_INCLUDE] = { .type = NLA_FLAG },
+       [NL80211_NAN_SRF_BF] = { .type = NLA_BINARY,
+                                .len =  NL80211_NAN_FUNC_SRF_MAX_LEN },
+       [NL80211_NAN_SRF_BF_IDX] = { .type = NLA_U8 },
+       [NL80211_NAN_SRF_MAC_ADDRS] = { .type = NLA_NESTED },
+};
+
 static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
                                     struct netlink_callback *cb,
                                     struct cfg80211_registered_device **rdev,
@@ -848,13 +886,21 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
        struct nlattr *key;
        struct cfg80211_cached_keys *result;
        int rem, err, def = 0;
+       bool have_key = false;
+
+       nla_for_each_nested(key, keys, rem) {
+               have_key = true;
+               break;
+       }
+
+       if (!have_key)
+               return NULL;
 
        result = kzalloc(sizeof(*result), GFP_KERNEL);
        if (!result)
                return ERR_PTR(-ENOMEM);
 
        result->def = -1;
-       result->defmgmt = -1;
 
        nla_for_each_nested(key, keys, rem) {
                memset(&parse, 0, sizeof(parse));
@@ -866,7 +912,7 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
                err = -EINVAL;
                if (!parse.p.key)
                        goto error;
-               if (parse.idx < 0 || parse.idx > 4)
+               if (parse.idx < 0 || parse.idx > 3)
                        goto error;
                if (parse.def) {
                        if (def)
@@ -881,16 +927,24 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
                                                     parse.idx, false, NULL);
                if (err)
                        goto error;
+               if (parse.p.cipher != WLAN_CIPHER_SUITE_WEP40 &&
+                   parse.p.cipher != WLAN_CIPHER_SUITE_WEP104) {
+                       err = -EINVAL;
+                       goto error;
+               }
                result->params[parse.idx].cipher = parse.p.cipher;
                result->params[parse.idx].key_len = parse.p.key_len;
                result->params[parse.idx].key = result->data[parse.idx];
                memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
 
-               if (parse.p.cipher == WLAN_CIPHER_SUITE_WEP40 ||
-                   parse.p.cipher == WLAN_CIPHER_SUITE_WEP104) {
-                       if (no_ht)
-                               *no_ht = true;
-               }
+               /* must be WEP key if we got here */
+               if (no_ht)
+                       *no_ht = true;
+       }
+
+       if (result->def < 0) {
+               err = -EINVAL;
+               goto error;
        }
 
        return result;
@@ -918,6 +972,7 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
        case NL80211_IFTYPE_UNSPECIFIED:
        case NL80211_IFTYPE_OCB:
        case NL80211_IFTYPE_MONITOR:
+       case NL80211_IFTYPE_NAN:
        case NL80211_IFTYPE_P2P_DEVICE:
        case NL80211_IFTYPE_WDS:
        case NUM_NL80211_IFTYPES:
@@ -2525,10 +2580,35 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
        int if_idx = 0;
        int wp_start = cb->args[0];
        int if_start = cb->args[1];
+       int filter_wiphy = -1;
        struct cfg80211_registered_device *rdev;
        struct wireless_dev *wdev;
 
        rtnl_lock();
+       if (!cb->args[2]) {
+               struct nl80211_dump_wiphy_state state = {
+                       .filter_wiphy = -1,
+               };
+               int ret;
+
+               ret = nl80211_dump_wiphy_parse(skb, cb, &state);
+               if (ret)
+                       return ret;
+
+               filter_wiphy = state.filter_wiphy;
+
+               /*
+                * if filtering, set cb->args[2] to +1 since 0 is the default
+                * value needed to determine that parsing is necessary.
+                */
+               if (filter_wiphy >= 0)
+                       cb->args[2] = filter_wiphy + 1;
+               else
+                       cb->args[2] = -1;
+       } else if (cb->args[2] > 0) {
+               filter_wiphy = cb->args[2] - 1;
+       }
+
        list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
                if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
                        continue;
@@ -2536,6 +2616,10 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
                        wp_idx++;
                        continue;
                }
+
+               if (filter_wiphy >= 0 && filter_wiphy != rdev->wiphy_idx)
+                       continue;
+
                if_idx = 0;
 
                list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
@@ -2751,7 +2835,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct vif_params params;
        struct wireless_dev *wdev;
-       struct sk_buff *msg, *event;
+       struct sk_buff *msg;
        int err;
        enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
        u32 flags;
@@ -2774,7 +2858,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
            !(rdev->wiphy.interface_modes & (1 << type)))
                return -EOPNOTSUPP;
 
-       if ((type == NL80211_IFTYPE_P2P_DEVICE ||
+       if ((type == NL80211_IFTYPE_P2P_DEVICE || type == NL80211_IFTYPE_NAN ||
             rdev->wiphy.features & NL80211_FEATURE_MAC_ON_CREATE) &&
            info->attrs[NL80211_ATTR_MAC]) {
                nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC],
@@ -2830,9 +2914,10 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
                       wdev->mesh_id_up_len);
                wdev_unlock(wdev);
                break;
+       case NL80211_IFTYPE_NAN:
        case NL80211_IFTYPE_P2P_DEVICE:
                /*
-                * P2P Device doesn't have a netdev, so doesn't go
+                * P2P Device and NAN do not have a netdev, so don't go
                 * through the netdev notifier and must be added here
                 */
                mutex_init(&wdev->mtx);
@@ -2855,20 +2940,15 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
                return -ENOBUFS;
        }
 
-       event = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-       if (event) {
-               if (nl80211_send_iface(event, 0, 0, 0,
-                                      rdev, wdev, false) < 0) {
-                       nlmsg_free(event);
-                       goto out;
-               }
-
-               genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
-                                       event, 0, NL80211_MCGRP_CONFIG,
-                                       GFP_KERNEL);
-       }
+       /*
+        * For wdevs which have no associated netdev object (e.g. of type
+        * NL80211_IFTYPE_P2P_DEVICE), emit the NEW_INTERFACE event here.
+        * For all other types, the event will be generated from the
+        * netdev notifier
+        */
+       if (!wdev->netdev)
+               nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);
 
-out:
        return genlmsg_reply(msg, info);
 }
 
@@ -2876,18 +2956,10 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct wireless_dev *wdev = info->user_ptr[1];
-       struct sk_buff *msg;
-       int status;
 
        if (!rdev->ops->del_virtual_intf)
                return -EOPNOTSUPP;
 
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-       if (msg && nl80211_send_iface(msg, 0, 0, 0, rdev, wdev, true) < 0) {
-               nlmsg_free(msg);
-               msg = NULL;
-       }
-
        /*
         * If we remove a wireless device without a netdev then clear
         * user_ptr[1] so that nl80211_post_doit won't dereference it
@@ -2898,15 +2970,7 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
        if (!wdev->netdev)
                info->user_ptr[1] = NULL;
 
-       status = rdev_del_virtual_intf(rdev, wdev);
-       if (status >= 0 && msg)
-               genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
-                                       msg, 0, NL80211_MCGRP_CONFIG,
-                                       GFP_KERNEL);
-       else
-               nlmsg_free(msg);
-
-       return status;
+       return rdev_del_virtual_intf(rdev, wdev);
 }
 
 static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
@@ -3316,147 +3380,432 @@ static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info)
        return err;
 }
 
-static int nl80211_parse_beacon(struct nlattr *attrs[],
-                               struct cfg80211_beacon_data *bcn)
+static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
+                          u8 *rates, u8 rates_len)
 {
-       bool haveinfo = false;
-
-       if (!is_valid_ie_attr(attrs[NL80211_ATTR_BEACON_TAIL]) ||
-           !is_valid_ie_attr(attrs[NL80211_ATTR_IE]) ||
-           !is_valid_ie_attr(attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
-           !is_valid_ie_attr(attrs[NL80211_ATTR_IE_ASSOC_RESP]))
-               return -EINVAL;
+       u8 i;
+       u32 mask = 0;
 
-       memset(bcn, 0, sizeof(*bcn));
+       for (i = 0; i < rates_len; i++) {
+               int rate = (rates[i] & 0x7f) * 5;
+               int ridx;
 
-       if (attrs[NL80211_ATTR_BEACON_HEAD]) {
-               bcn->head = nla_data(attrs[NL80211_ATTR_BEACON_HEAD]);
-               bcn->head_len = nla_len(attrs[NL80211_ATTR_BEACON_HEAD]);
-               if (!bcn->head_len)
-                       return -EINVAL;
-               haveinfo = true;
+               for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
+                       struct ieee80211_rate *srate =
+                               &sband->bitrates[ridx];
+                       if (rate == srate->bitrate) {
+                               mask |= 1 << ridx;
+                               break;
+                       }
+               }
+               if (ridx == sband->n_bitrates)
+                       return 0; /* rate not found */
        }
 
-       if (attrs[NL80211_ATTR_BEACON_TAIL]) {
-               bcn->tail = nla_data(attrs[NL80211_ATTR_BEACON_TAIL]);
-               bcn->tail_len = nla_len(attrs[NL80211_ATTR_BEACON_TAIL]);
-               haveinfo = true;
-       }
+       return mask;
+}
 
-       if (!haveinfo)
-               return -EINVAL;
+static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
+                              u8 *rates, u8 rates_len,
+                              u8 mcs[IEEE80211_HT_MCS_MASK_LEN])
+{
+       u8 i;
 
-       if (attrs[NL80211_ATTR_IE]) {
-               bcn->beacon_ies = nla_data(attrs[NL80211_ATTR_IE]);
-               bcn->beacon_ies_len = nla_len(attrs[NL80211_ATTR_IE]);
-       }
+       memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN);
 
-       if (attrs[NL80211_ATTR_IE_PROBE_RESP]) {
-               bcn->proberesp_ies =
-                       nla_data(attrs[NL80211_ATTR_IE_PROBE_RESP]);
-               bcn->proberesp_ies_len =
-                       nla_len(attrs[NL80211_ATTR_IE_PROBE_RESP]);
-       }
+       for (i = 0; i < rates_len; i++) {
+               int ridx, rbit;
 
-       if (attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
-               bcn->assocresp_ies =
-                       nla_data(attrs[NL80211_ATTR_IE_ASSOC_RESP]);
-               bcn->assocresp_ies_len =
-                       nla_len(attrs[NL80211_ATTR_IE_ASSOC_RESP]);
-       }
+               ridx = rates[i] / 8;
+               rbit = BIT(rates[i] % 8);
 
-       if (attrs[NL80211_ATTR_PROBE_RESP]) {
-               bcn->probe_resp = nla_data(attrs[NL80211_ATTR_PROBE_RESP]);
-               bcn->probe_resp_len = nla_len(attrs[NL80211_ATTR_PROBE_RESP]);
+               /* check validity */
+               if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN))
+                       return false;
+
+               /* check availability */
+               if (sband->ht_cap.mcs.rx_mask[ridx] & rbit)
+                       mcs[ridx] |= rbit;
+               else
+                       return false;
        }
 
-       return 0;
+       return true;
 }
 
-static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,
-                                  struct cfg80211_ap_settings *params)
+static u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map)
 {
-       struct wireless_dev *wdev;
-       bool ret = false;
-
-       list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
-               if (wdev->iftype != NL80211_IFTYPE_AP &&
-                   wdev->iftype != NL80211_IFTYPE_P2P_GO)
-                       continue;
-
-               if (!wdev->preset_chandef.chan)
-                       continue;
+       u16 mcs_mask = 0;
 
-               params->chandef = wdev->preset_chandef;
-               ret = true;
+       switch (vht_mcs_map) {
+       case IEEE80211_VHT_MCS_NOT_SUPPORTED:
+               break;
+       case IEEE80211_VHT_MCS_SUPPORT_0_7:
+               mcs_mask = 0x00FF;
+               break;
+       case IEEE80211_VHT_MCS_SUPPORT_0_8:
+               mcs_mask = 0x01FF;
+               break;
+       case IEEE80211_VHT_MCS_SUPPORT_0_9:
+               mcs_mask = 0x03FF;
+               break;
+       default:
                break;
        }
 
-       return ret;
+       return mcs_mask;
 }
 
-static bool nl80211_valid_auth_type(struct cfg80211_registered_device *rdev,
-                                   enum nl80211_auth_type auth_type,
-                                   enum nl80211_commands cmd)
+static void vht_build_mcs_mask(u16 vht_mcs_map,
+                              u16 vht_mcs_mask[NL80211_VHT_NSS_MAX])
 {
-       if (auth_type > NL80211_AUTHTYPE_MAX)
-               return false;
+       u8 nss;
 
-       switch (cmd) {
-       case NL80211_CMD_AUTHENTICATE:
-               if (!(rdev->wiphy.features & NL80211_FEATURE_SAE) &&
-                   auth_type == NL80211_AUTHTYPE_SAE)
-                       return false;
-               return true;
-       case NL80211_CMD_CONNECT:
-       case NL80211_CMD_START_AP:
-               /* SAE not supported yet */
-               if (auth_type == NL80211_AUTHTYPE_SAE)
-                       return false;
-               return true;
-       default:
-               return false;
+       for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) {
+               vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map & 0x03);
+               vht_mcs_map >>= 2;
        }
 }
 
-static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
+static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband,
+                            struct nl80211_txrate_vht *txrate,
+                            u16 mcs[NL80211_VHT_NSS_MAX])
 {
-       struct cfg80211_registered_device *rdev = info->user_ptr[0];
-       struct net_device *dev = info->user_ptr[1];
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_ap_settings params;
-       int err;
+       u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
+       u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {};
+       u8 i;
 
-       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
-           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
-               return -EOPNOTSUPP;
+       if (!sband->vht_cap.vht_supported)
+               return false;
 
-       if (!rdev->ops->start_ap)
-               return -EOPNOTSUPP;
+       memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX);
 
-       if (wdev->beacon_interval)
-               return -EALREADY;
+       /* Build vht_mcs_mask from VHT capabilities */
+       vht_build_mcs_mask(tx_mcs_map, tx_mcs_mask);
 
-       memset(&params, 0, sizeof(params));
+       for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
+               if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i])
+                       mcs[i] = txrate->mcs[i];
+               else
+                       return false;
+       }
 
-       /* these are required for START_AP */
-       if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
-           !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
-           !info->attrs[NL80211_ATTR_BEACON_HEAD])
-               return -EINVAL;
+       return true;
+}
 
-       err = nl80211_parse_beacon(info->attrs, &params.beacon);
-       if (err)
-               return err;
+static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
+       [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
+                                   .len = NL80211_MAX_SUPP_RATES },
+       [NL80211_TXRATE_HT] = { .type = NLA_BINARY,
+                               .len = NL80211_MAX_SUPP_HT_RATES },
+       [NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)},
+       [NL80211_TXRATE_GI] = { .type = NLA_U8 },
+};
 
-       params.beacon_interval =
-               nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
-       params.dtim_period =
-               nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
+static int nl80211_parse_tx_bitrate_mask(struct genl_info *info,
+                                        struct cfg80211_bitrate_mask *mask)
+{
+       struct nlattr *tb[NL80211_TXRATE_MAX + 1];
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       int rem, i;
+       struct nlattr *tx_rates;
+       struct ieee80211_supported_band *sband;
+       u16 vht_tx_mcs_map;
 
-       err = cfg80211_validate_beacon_int(rdev, params.beacon_interval);
-       if (err)
-               return err;
+       memset(mask, 0, sizeof(*mask));
+       /* Default to all rates enabled */
+       for (i = 0; i < NUM_NL80211_BANDS; i++) {
+               sband = rdev->wiphy.bands[i];
+
+               if (!sband)
+                       continue;
+
+               mask->control[i].legacy = (1 << sband->n_bitrates) - 1;
+               memcpy(mask->control[i].ht_mcs,
+                      sband->ht_cap.mcs.rx_mask,
+                      sizeof(mask->control[i].ht_mcs));
+
+               if (!sband->vht_cap.vht_supported)
+                       continue;
+
+               vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
+               vht_build_mcs_mask(vht_tx_mcs_map, mask->control[i].vht_mcs);
+       }
+
+       /* if no rates are given set it back to the defaults */
+       if (!info->attrs[NL80211_ATTR_TX_RATES])
+               goto out;
+
+       /* The nested attribute uses enum nl80211_band as the index. This maps
+        * directly to the enum nl80211_band values used in cfg80211.
+        */
+       BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
+       nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) {
+               enum nl80211_band band = nla_type(tx_rates);
+               int err;
+
+               if (band < 0 || band >= NUM_NL80211_BANDS)
+                       return -EINVAL;
+               sband = rdev->wiphy.bands[band];
+               if (sband == NULL)
+                       return -EINVAL;
+               err = nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
+                               nla_len(tx_rates), nl80211_txattr_policy);
+               if (err)
+                       return err;
+               if (tb[NL80211_TXRATE_LEGACY]) {
+                       mask->control[band].legacy = rateset_to_mask(
+                               sband,
+                               nla_data(tb[NL80211_TXRATE_LEGACY]),
+                               nla_len(tb[NL80211_TXRATE_LEGACY]));
+                       if ((mask->control[band].legacy == 0) &&
+                           nla_len(tb[NL80211_TXRATE_LEGACY]))
+                               return -EINVAL;
+               }
+               if (tb[NL80211_TXRATE_HT]) {
+                       if (!ht_rateset_to_mask(
+                                       sband,
+                                       nla_data(tb[NL80211_TXRATE_HT]),
+                                       nla_len(tb[NL80211_TXRATE_HT]),
+                                       mask->control[band].ht_mcs))
+                               return -EINVAL;
+               }
+               if (tb[NL80211_TXRATE_VHT]) {
+                       if (!vht_set_mcs_mask(
+                                       sband,
+                                       nla_data(tb[NL80211_TXRATE_VHT]),
+                                       mask->control[band].vht_mcs))
+                               return -EINVAL;
+               }
+               if (tb[NL80211_TXRATE_GI]) {
+                       mask->control[band].gi =
+                               nla_get_u8(tb[NL80211_TXRATE_GI]);
+                       if (mask->control[band].gi > NL80211_TXRATE_FORCE_LGI)
+                               return -EINVAL;
+               }
+
+               if (mask->control[band].legacy == 0) {
+                       /* don't allow empty legacy rates if HT or VHT
+                        * are not even supported.
+                        */
+                       if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported ||
+                             rdev->wiphy.bands[band]->vht_cap.vht_supported))
+                               return -EINVAL;
+
+                       for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
+                               if (mask->control[band].ht_mcs[i])
+                                       goto out;
+
+                       for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
+                               if (mask->control[band].vht_mcs[i])
+                                       goto out;
+
+                       /* legacy and mcs rates may not be both empty */
+                       return -EINVAL;
+               }
+       }
+
+out:
+       return 0;
+}
+
+static int validate_beacon_tx_rate(struct cfg80211_registered_device *rdev,
+                                  enum nl80211_band band,
+                                  struct cfg80211_bitrate_mask *beacon_rate)
+{
+       u32 count_ht, count_vht, i;
+       u32 rate = beacon_rate->control[band].legacy;
+
+       /* Allow only one rate */
+       if (hweight32(rate) > 1)
+               return -EINVAL;
+
+       count_ht = 0;
+       for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+               if (hweight8(beacon_rate->control[band].ht_mcs[i]) > 1) {
+                       return -EINVAL;
+               } else if (beacon_rate->control[band].ht_mcs[i]) {
+                       count_ht++;
+                       if (count_ht > 1)
+                               return -EINVAL;
+               }
+               if (count_ht && rate)
+                       return -EINVAL;
+       }
+
+       count_vht = 0;
+       for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
+               if (hweight16(beacon_rate->control[band].vht_mcs[i]) > 1) {
+                       return -EINVAL;
+               } else if (beacon_rate->control[band].vht_mcs[i]) {
+                       count_vht++;
+                       if (count_vht > 1)
+                               return -EINVAL;
+               }
+               if (count_vht && rate)
+                       return -EINVAL;
+       }
+
+       if ((count_ht && count_vht) || (!rate && !count_ht && !count_vht))
+               return -EINVAL;
+
+       if (rate &&
+           !wiphy_ext_feature_isset(&rdev->wiphy,
+                                    NL80211_EXT_FEATURE_BEACON_RATE_LEGACY))
+               return -EINVAL;
+       if (count_ht &&
+           !wiphy_ext_feature_isset(&rdev->wiphy,
+                                    NL80211_EXT_FEATURE_BEACON_RATE_HT))
+               return -EINVAL;
+       if (count_vht &&
+           !wiphy_ext_feature_isset(&rdev->wiphy,
+                                    NL80211_EXT_FEATURE_BEACON_RATE_VHT))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int nl80211_parse_beacon(struct nlattr *attrs[],
+                               struct cfg80211_beacon_data *bcn)
+{
+       bool haveinfo = false;
+
+       if (!is_valid_ie_attr(attrs[NL80211_ATTR_BEACON_TAIL]) ||
+           !is_valid_ie_attr(attrs[NL80211_ATTR_IE]) ||
+           !is_valid_ie_attr(attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
+           !is_valid_ie_attr(attrs[NL80211_ATTR_IE_ASSOC_RESP]))
+               return -EINVAL;
+
+       memset(bcn, 0, sizeof(*bcn));
+
+       if (attrs[NL80211_ATTR_BEACON_HEAD]) {
+               bcn->head = nla_data(attrs[NL80211_ATTR_BEACON_HEAD]);
+               bcn->head_len = nla_len(attrs[NL80211_ATTR_BEACON_HEAD]);
+               if (!bcn->head_len)
+                       return -EINVAL;
+               haveinfo = true;
+       }
+
+       if (attrs[NL80211_ATTR_BEACON_TAIL]) {
+               bcn->tail = nla_data(attrs[NL80211_ATTR_BEACON_TAIL]);
+               bcn->tail_len = nla_len(attrs[NL80211_ATTR_BEACON_TAIL]);
+               haveinfo = true;
+       }
+
+       if (!haveinfo)
+               return -EINVAL;
+
+       if (attrs[NL80211_ATTR_IE]) {
+               bcn->beacon_ies = nla_data(attrs[NL80211_ATTR_IE]);
+               bcn->beacon_ies_len = nla_len(attrs[NL80211_ATTR_IE]);
+       }
+
+       if (attrs[NL80211_ATTR_IE_PROBE_RESP]) {
+               bcn->proberesp_ies =
+                       nla_data(attrs[NL80211_ATTR_IE_PROBE_RESP]);
+               bcn->proberesp_ies_len =
+                       nla_len(attrs[NL80211_ATTR_IE_PROBE_RESP]);
+       }
+
+       if (attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
+               bcn->assocresp_ies =
+                       nla_data(attrs[NL80211_ATTR_IE_ASSOC_RESP]);
+               bcn->assocresp_ies_len =
+                       nla_len(attrs[NL80211_ATTR_IE_ASSOC_RESP]);
+       }
+
+       if (attrs[NL80211_ATTR_PROBE_RESP]) {
+               bcn->probe_resp = nla_data(attrs[NL80211_ATTR_PROBE_RESP]);
+               bcn->probe_resp_len = nla_len(attrs[NL80211_ATTR_PROBE_RESP]);
+       }
+
+       return 0;
+}
+
+static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,
+                                  struct cfg80211_ap_settings *params)
+{
+       struct wireless_dev *wdev;
+       bool ret = false;
+
+       list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+               if (wdev->iftype != NL80211_IFTYPE_AP &&
+                   wdev->iftype != NL80211_IFTYPE_P2P_GO)
+                       continue;
+
+               if (!wdev->preset_chandef.chan)
+                       continue;
+
+               params->chandef = wdev->preset_chandef;
+               ret = true;
+               break;
+       }
+
+       return ret;
+}
+
+static bool nl80211_valid_auth_type(struct cfg80211_registered_device *rdev,
+                                   enum nl80211_auth_type auth_type,
+                                   enum nl80211_commands cmd)
+{
+       if (auth_type > NL80211_AUTHTYPE_MAX)
+               return false;
+
+       switch (cmd) {
+       case NL80211_CMD_AUTHENTICATE:
+               if (!(rdev->wiphy.features & NL80211_FEATURE_SAE) &&
+                   auth_type == NL80211_AUTHTYPE_SAE)
+                       return false;
+               return true;
+       case NL80211_CMD_CONNECT:
+       case NL80211_CMD_START_AP:
+               /* SAE not supported yet */
+               if (auth_type == NL80211_AUTHTYPE_SAE)
+                       return false;
+               return true;
+       default:
+               return false;
+       }
+}
+
+static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_ap_settings params;
+       int err;
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+               return -EOPNOTSUPP;
+
+       if (!rdev->ops->start_ap)
+               return -EOPNOTSUPP;
+
+       if (wdev->beacon_interval)
+               return -EALREADY;
+
+       memset(&params, 0, sizeof(params));
+
+       /* these are required for START_AP */
+       if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
+           !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
+           !info->attrs[NL80211_ATTR_BEACON_HEAD])
+               return -EINVAL;
+
+       err = nl80211_parse_beacon(info->attrs, &params.beacon);
+       if (err)
+               return err;
+
+       params.beacon_interval =
+               nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
+       params.dtim_period =
+               nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
+
+       err = cfg80211_validate_beacon_int(rdev, params.beacon_interval);
+       if (err)
+               return err;
 
        /*
         * In theory, some of these attributes should be required here
@@ -3545,6 +3894,17 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
                                           wdev->iftype))
                return -EINVAL;
 
+       if (info->attrs[NL80211_ATTR_TX_RATES]) {
+               err = nl80211_parse_tx_bitrate_mask(info, &params.beacon_rate);
+               if (err)
+                       return err;
+
+               err = validate_beacon_tx_rate(rdev, params.chandef.chan->band,
+                                             &params.beacon_rate);
+               if (err)
+                       return err;
+       }
+
        if (info->attrs[NL80211_ATTR_SMPS_MODE]) {
                params.smps_mode =
                        nla_get_u8(info->attrs[NL80211_ATTR_SMPS_MODE]);
@@ -5374,6 +5734,18 @@ static int nl80211_check_s32(const struct nlattr *nla, s32 min, s32 max, s32 *ou
        return 0;
 }
 
+static int nl80211_check_power_mode(const struct nlattr *nla,
+                                   enum nl80211_mesh_power_mode min,
+                                   enum nl80211_mesh_power_mode max,
+                                   enum nl80211_mesh_power_mode *out)
+{
+       u32 val = nla_get_u32(nla);
+       if (val < min || val > max)
+               return -EINVAL;
+       *out = val;
+       return 0;
+}
+
 static int nl80211_parse_mesh_config(struct genl_info *info,
                                     struct mesh_config *cfg,
                                     u32 *mask_out)
@@ -5518,7 +5890,7 @@ do {                                                                          \
                                  NL80211_MESH_POWER_ACTIVE,
                                  NL80211_MESH_POWER_MAX,
                                  mask, NL80211_MESHCONF_POWER_MODE,
-                                 nl80211_check_u32);
+                                 nl80211_check_power_mode);
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration,
                                  0, 65535, mask,
                                  NL80211_MESHCONF_AWAKE_WINDOW, nl80211_check_u16);
@@ -6102,6 +6474,9 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
 
        wiphy = &rdev->wiphy;
 
+       if (wdev->iftype == NL80211_IFTYPE_NAN)
+               return -EOPNOTSUPP;
+
        if (!rdev->ops->scan)
                return -EOPNOTSUPP;
 
@@ -7368,7 +7743,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
                    (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
                     key.p.key_len != WLAN_KEY_LEN_WEP104))
                        return -EINVAL;
-               if (key.idx > 4)
+               if (key.idx > 3)
                        return -EINVAL;
        } else {
                key.p.key_len = 0;
@@ -7773,12 +8148,13 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
 
        ibss.beacon_interval = 100;
 
-       if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
+       if (info->attrs[NL80211_ATTR_BEACON_INTERVAL])
                ibss.beacon_interval =
                        nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
-               if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
-                       return -EINVAL;
-       }
+
+       err = cfg80211_validate_beacon_int(rdev, ibss.beacon_interval);
+       if (err)
+               return err;
 
        if (!rdev->ops->join_ibss)
                return -EOPNOTSUPP;
@@ -7985,6 +8361,8 @@ __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev,
        }
 
        data = nla_nest_start(skb, attr);
+       if (!data)
+               goto nla_put_failure;
 
        ((void **)skb->cb)[0] = rdev;
        ((void **)skb->cb)[1] = hdr;
@@ -8602,238 +8980,21 @@ static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
        return rdev_cancel_remain_on_channel(rdev, wdev, cookie);
 }
 
-static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
-                          u8 *rates, u8 rates_len)
-{
-       u8 i;
-       u32 mask = 0;
-
-       for (i = 0; i < rates_len; i++) {
-               int rate = (rates[i] & 0x7f) * 5;
-               int ridx;
-
-               for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
-                       struct ieee80211_rate *srate =
-                               &sband->bitrates[ridx];
-                       if (rate == srate->bitrate) {
-                               mask |= 1 << ridx;
-                               break;
-                       }
-               }
-               if (ridx == sband->n_bitrates)
-                       return 0; /* rate not found */
-       }
-
-       return mask;
-}
-
-static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
-                              u8 *rates, u8 rates_len,
-                              u8 mcs[IEEE80211_HT_MCS_MASK_LEN])
-{
-       u8 i;
-
-       memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN);
-
-       for (i = 0; i < rates_len; i++) {
-               int ridx, rbit;
-
-               ridx = rates[i] / 8;
-               rbit = BIT(rates[i] % 8);
-
-               /* check validity */
-               if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN))
-                       return false;
-
-               /* check availability */
-               if (sband->ht_cap.mcs.rx_mask[ridx] & rbit)
-                       mcs[ridx] |= rbit;
-               else
-                       return false;
-       }
-
-       return true;
-}
-
-static u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map)
-{
-       u16 mcs_mask = 0;
-
-       switch (vht_mcs_map) {
-       case IEEE80211_VHT_MCS_NOT_SUPPORTED:
-               break;
-       case IEEE80211_VHT_MCS_SUPPORT_0_7:
-               mcs_mask = 0x00FF;
-               break;
-       case IEEE80211_VHT_MCS_SUPPORT_0_8:
-               mcs_mask = 0x01FF;
-               break;
-       case IEEE80211_VHT_MCS_SUPPORT_0_9:
-               mcs_mask = 0x03FF;
-               break;
-       default:
-               break;
-       }
-
-       return mcs_mask;
-}
-
-static void vht_build_mcs_mask(u16 vht_mcs_map,
-                              u16 vht_mcs_mask[NL80211_VHT_NSS_MAX])
-{
-       u8 nss;
-
-       for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) {
-               vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map & 0x03);
-               vht_mcs_map >>= 2;
-       }
-}
-
-static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband,
-                            struct nl80211_txrate_vht *txrate,
-                            u16 mcs[NL80211_VHT_NSS_MAX])
-{
-       u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
-       u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {};
-       u8 i;
-
-       if (!sband->vht_cap.vht_supported)
-               return false;
-
-       memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX);
-
-       /* Build vht_mcs_mask from VHT capabilities */
-       vht_build_mcs_mask(tx_mcs_map, tx_mcs_mask);
-
-       for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
-               if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i])
-                       mcs[i] = txrate->mcs[i];
-               else
-                       return false;
-       }
-
-       return true;
-}
-
-static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
-       [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
-                                   .len = NL80211_MAX_SUPP_RATES },
-       [NL80211_TXRATE_HT] = { .type = NLA_BINARY,
-                               .len = NL80211_MAX_SUPP_HT_RATES },
-       [NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)},
-       [NL80211_TXRATE_GI] = { .type = NLA_U8 },
-};
-
 static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
                                       struct genl_info *info)
 {
-       struct nlattr *tb[NL80211_TXRATE_MAX + 1];
-       struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct cfg80211_bitrate_mask mask;
-       int rem, i;
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
-       struct nlattr *tx_rates;
-       struct ieee80211_supported_band *sband;
-       u16 vht_tx_mcs_map;
+       int err;
 
        if (!rdev->ops->set_bitrate_mask)
                return -EOPNOTSUPP;
 
-       memset(&mask, 0, sizeof(mask));
-       /* Default to all rates enabled */
-       for (i = 0; i < NUM_NL80211_BANDS; i++) {
-               sband = rdev->wiphy.bands[i];
-
-               if (!sband)
-                       continue;
-
-               mask.control[i].legacy = (1 << sband->n_bitrates) - 1;
-               memcpy(mask.control[i].ht_mcs,
-                      sband->ht_cap.mcs.rx_mask,
-                      sizeof(mask.control[i].ht_mcs));
-
-               if (!sband->vht_cap.vht_supported)
-                       continue;
-
-               vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
-               vht_build_mcs_mask(vht_tx_mcs_map, mask.control[i].vht_mcs);
-       }
-
-       /* if no rates are given set it back to the defaults */
-       if (!info->attrs[NL80211_ATTR_TX_RATES])
-               goto out;
-
-       /*
-        * The nested attribute uses enum nl80211_band as the index. This maps
-        * directly to the enum nl80211_band values used in cfg80211.
-        */
-       BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
-       nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) {
-               enum nl80211_band band = nla_type(tx_rates);
-               int err;
-
-               if (band < 0 || band >= NUM_NL80211_BANDS)
-                       return -EINVAL;
-               sband = rdev->wiphy.bands[band];
-               if (sband == NULL)
-                       return -EINVAL;
-               err = nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
-                               nla_len(tx_rates), nl80211_txattr_policy);
-               if (err)
-                       return err;
-               if (tb[NL80211_TXRATE_LEGACY]) {
-                       mask.control[band].legacy = rateset_to_mask(
-                               sband,
-                               nla_data(tb[NL80211_TXRATE_LEGACY]),
-                               nla_len(tb[NL80211_TXRATE_LEGACY]));
-                       if ((mask.control[band].legacy == 0) &&
-                           nla_len(tb[NL80211_TXRATE_LEGACY]))
-                               return -EINVAL;
-               }
-               if (tb[NL80211_TXRATE_HT]) {
-                       if (!ht_rateset_to_mask(
-                                       sband,
-                                       nla_data(tb[NL80211_TXRATE_HT]),
-                                       nla_len(tb[NL80211_TXRATE_HT]),
-                                       mask.control[band].ht_mcs))
-                               return -EINVAL;
-               }
-               if (tb[NL80211_TXRATE_VHT]) {
-                       if (!vht_set_mcs_mask(
-                                       sband,
-                                       nla_data(tb[NL80211_TXRATE_VHT]),
-                                       mask.control[band].vht_mcs))
-                               return -EINVAL;
-               }
-               if (tb[NL80211_TXRATE_GI]) {
-                       mask.control[band].gi =
-                               nla_get_u8(tb[NL80211_TXRATE_GI]);
-                       if (mask.control[band].gi > NL80211_TXRATE_FORCE_LGI)
-                               return -EINVAL;
-               }
-
-               if (mask.control[band].legacy == 0) {
-                       /* don't allow empty legacy rates if HT or VHT
-                        * are not even supported.
-                        */
-                       if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported ||
-                             rdev->wiphy.bands[band]->vht_cap.vht_supported))
-                               return -EINVAL;
-
-                       for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
-                               if (mask.control[band].ht_mcs[i])
-                                       goto out;
-
-                       for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
-                               if (mask.control[band].vht_mcs[i])
-                                       goto out;
-
-                       /* legacy and mcs rates may not be both empty */
-                       return -EINVAL;
-               }
-       }
+       err = nl80211_parse_tx_bitrate_mask(info, &mask);
+       if (err)
+               return err;
 
-out:
        return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);
 }
 
@@ -8859,6 +9020,7 @@ static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
        case NL80211_IFTYPE_P2P_GO:
        case NL80211_IFTYPE_P2P_DEVICE:
                break;
+       case NL80211_IFTYPE_NAN:
        default:
                return -EOPNOTSUPP;
        }
@@ -8904,6 +9066,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_P2P_GO:
                break;
+       case NL80211_IFTYPE_NAN:
        default:
                return -EOPNOTSUPP;
        }
@@ -9020,6 +9183,7 @@ static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *in
        case NL80211_IFTYPE_P2P_GO:
        case NL80211_IFTYPE_P2P_DEVICE:
                break;
+       case NL80211_IFTYPE_NAN:
        default:
                return -EOPNOTSUPP;
        }
@@ -9252,9 +9416,10 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
        if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
                setup.beacon_interval =
                        nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
-               if (setup.beacon_interval < 10 ||
-                   setup.beacon_interval > 10000)
-                       return -EINVAL;
+
+               err = cfg80211_validate_beacon_int(rdev, setup.beacon_interval);
+               if (err)
+                       return err;
        }
 
        if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
@@ -9300,6 +9465,17 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
                        return err;
        }
 
+       if (info->attrs[NL80211_ATTR_TX_RATES]) {
+               err = nl80211_parse_tx_bitrate_mask(info, &setup.beacon_rate);
+               if (err)
+                       return err;
+
+               err = validate_beacon_tx_rate(rdev, setup.chandef.chan->band,
+                                             &setup.beacon_rate);
+               if (err)
+                       return err;
+       }
+
        return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
 }
 
@@ -9413,18 +9589,27 @@ static int nl80211_send_wowlan_nd(struct sk_buff *msg,
        if (!freqs)
                return -ENOBUFS;
 
-       for (i = 0; i < req->n_channels; i++)
-               nla_put_u32(msg, i, req->channels[i]->center_freq);
+       for (i = 0; i < req->n_channels; i++) {
+               if (nla_put_u32(msg, i, req->channels[i]->center_freq))
+                       return -ENOBUFS;
+       }
 
        nla_nest_end(msg, freqs);
 
        if (req->n_match_sets) {
                matches = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH);
+               if (!matches)
+                       return -ENOBUFS;
+
                for (i = 0; i < req->n_match_sets; i++) {
                        match = nla_nest_start(msg, i);
-                       nla_put(msg, NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
-                               req->match_sets[i].ssid.ssid_len,
-                               req->match_sets[i].ssid.ssid);
+                       if (!match)
+                               return -ENOBUFS;
+
+                       if (nla_put(msg, NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
+                                   req->match_sets[i].ssid.ssid_len,
+                                   req->match_sets[i].ssid.ssid))
+                               return -ENOBUFS;
                        nla_nest_end(msg, match);
                }
                nla_nest_end(msg, matches);
@@ -9436,6 +9621,9 @@ static int nl80211_send_wowlan_nd(struct sk_buff *msg,
 
        for (i = 0; i < req->n_scan_plans; i++) {
                scan_plan = nla_nest_start(msg, i + 1);
+               if (!scan_plan)
+                       return -ENOBUFS;
+
                if (!scan_plan ||
                    nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_INTERVAL,
                                req->scan_plans[i].interval) ||
@@ -10362,12 +10550,555 @@ static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info)
        return 0;
 }
 
-static int nl80211_get_protocol_features(struct sk_buff *skb,
-                                        struct genl_info *info)
+static int nl80211_start_nan(struct sk_buff *skb, struct genl_info *info)
 {
-       void *hdr;
-       struct sk_buff *msg;
-
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct wireless_dev *wdev = info->user_ptr[1];
+       struct cfg80211_nan_conf conf = {};
+       int err;
+
+       if (wdev->iftype != NL80211_IFTYPE_NAN)
+               return -EOPNOTSUPP;
+
+       if (wdev->nan_started)
+               return -EEXIST;
+
+       if (rfkill_blocked(rdev->rfkill))
+               return -ERFKILL;
+
+       if (!info->attrs[NL80211_ATTR_NAN_MASTER_PREF])
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_NAN_DUAL])
+               return -EINVAL;
+
+       conf.master_pref =
+               nla_get_u8(info->attrs[NL80211_ATTR_NAN_MASTER_PREF]);
+       if (!conf.master_pref)
+               return -EINVAL;
+
+       conf.dual = nla_get_u8(info->attrs[NL80211_ATTR_NAN_DUAL]);
+
+       err = rdev_start_nan(rdev, wdev, &conf);
+       if (err)
+               return err;
+
+       wdev->nan_started = true;
+       rdev->opencount++;
+
+       return 0;
+}
+
+static int nl80211_stop_nan(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct wireless_dev *wdev = info->user_ptr[1];
+
+       if (wdev->iftype != NL80211_IFTYPE_NAN)
+               return -EOPNOTSUPP;
+
+       cfg80211_stop_nan(rdev, wdev);
+
+       return 0;
+}
+
+static int validate_nan_filter(struct nlattr *filter_attr)
+{
+       struct nlattr *attr;
+       int len = 0, n_entries = 0, rem;
+
+       nla_for_each_nested(attr, filter_attr, rem) {
+               len += nla_len(attr);
+               n_entries++;
+       }
+
+       if (len >= U8_MAX)
+               return -EINVAL;
+
+       return n_entries;
+}
+
+static int handle_nan_filter(struct nlattr *attr_filter,
+                            struct cfg80211_nan_func *func,
+                            bool tx)
+{
+       struct nlattr *attr;
+       int n_entries, rem, i;
+       struct cfg80211_nan_func_filter *filter;
+
+       n_entries = validate_nan_filter(attr_filter);
+       if (n_entries < 0)
+               return n_entries;
+
+       BUILD_BUG_ON(sizeof(*func->rx_filters) != sizeof(*func->tx_filters));
+
+       filter = kcalloc(n_entries, sizeof(*func->rx_filters), GFP_KERNEL);
+       if (!filter)
+               return -ENOMEM;
+
+       i = 0;
+       nla_for_each_nested(attr, attr_filter, rem) {
+               filter[i].filter = kmemdup(nla_data(attr), nla_len(attr),
+                                          GFP_KERNEL);
+               filter[i].len = nla_len(attr);
+               i++;
+       }
+       if (tx) {
+               func->num_tx_filters = n_entries;
+               func->tx_filters = filter;
+       } else {
+               func->num_rx_filters = n_entries;
+               func->rx_filters = filter;
+       }
+
+       return 0;
+}
+
+static int nl80211_nan_add_func(struct sk_buff *skb,
+                               struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct wireless_dev *wdev = info->user_ptr[1];
+       struct nlattr *tb[NUM_NL80211_NAN_FUNC_ATTR], *func_attr;
+       struct cfg80211_nan_func *func;
+       struct sk_buff *msg = NULL;
+       void *hdr = NULL;
+       int err = 0;
+
+       if (wdev->iftype != NL80211_IFTYPE_NAN)
+               return -EOPNOTSUPP;
+
+       if (!wdev->nan_started)
+               return -ENOTCONN;
+
+       if (!info->attrs[NL80211_ATTR_NAN_FUNC])
+               return -EINVAL;
+
+       if (wdev->owner_nlportid &&
+           wdev->owner_nlportid != info->snd_portid)
+               return -ENOTCONN;
+
+       err = nla_parse(tb, NL80211_NAN_FUNC_ATTR_MAX,
+                       nla_data(info->attrs[NL80211_ATTR_NAN_FUNC]),
+                       nla_len(info->attrs[NL80211_ATTR_NAN_FUNC]),
+                       nl80211_nan_func_policy);
+       if (err)
+               return err;
+
+       func = kzalloc(sizeof(*func), GFP_KERNEL);
+       if (!func)
+               return -ENOMEM;
+
+       func->cookie = wdev->wiphy->cookie_counter++;
+
+       if (!tb[NL80211_NAN_FUNC_TYPE] ||
+           nla_get_u8(tb[NL80211_NAN_FUNC_TYPE]) > NL80211_NAN_FUNC_MAX_TYPE) {
+               err = -EINVAL;
+               goto out;
+       }
+
+
+       func->type = nla_get_u8(tb[NL80211_NAN_FUNC_TYPE]);
+
+       if (!tb[NL80211_NAN_FUNC_SERVICE_ID]) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       memcpy(func->service_id, nla_data(tb[NL80211_NAN_FUNC_SERVICE_ID]),
+              sizeof(func->service_id));
+
+       func->close_range =
+               nla_get_flag(tb[NL80211_NAN_FUNC_CLOSE_RANGE]);
+
+       if (tb[NL80211_NAN_FUNC_SERVICE_INFO]) {
+               func->serv_spec_info_len =
+                       nla_len(tb[NL80211_NAN_FUNC_SERVICE_INFO]);
+               func->serv_spec_info =
+                       kmemdup(nla_data(tb[NL80211_NAN_FUNC_SERVICE_INFO]),
+                               func->serv_spec_info_len,
+                               GFP_KERNEL);
+               if (!func->serv_spec_info) {
+                       err = -ENOMEM;
+                       goto out;
+               }
+       }
+
+       if (tb[NL80211_NAN_FUNC_TTL])
+               func->ttl = nla_get_u32(tb[NL80211_NAN_FUNC_TTL]);
+
+       switch (func->type) {
+       case NL80211_NAN_FUNC_PUBLISH:
+               if (!tb[NL80211_NAN_FUNC_PUBLISH_TYPE]) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               func->publish_type =
+                       nla_get_u8(tb[NL80211_NAN_FUNC_PUBLISH_TYPE]);
+               func->publish_bcast =
+                       nla_get_flag(tb[NL80211_NAN_FUNC_PUBLISH_BCAST]);
+
+               if ((!(func->publish_type & NL80211_NAN_SOLICITED_PUBLISH)) &&
+                       func->publish_bcast) {
+                       err = -EINVAL;
+                       goto out;
+               }
+               break;
+       case NL80211_NAN_FUNC_SUBSCRIBE:
+               func->subscribe_active =
+                       nla_get_flag(tb[NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE]);
+               break;
+       case NL80211_NAN_FUNC_FOLLOW_UP:
+               if (!tb[NL80211_NAN_FUNC_FOLLOW_UP_ID] ||
+                   !tb[NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID]) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               func->followup_id =
+                       nla_get_u8(tb[NL80211_NAN_FUNC_FOLLOW_UP_ID]);
+               func->followup_reqid =
+                       nla_get_u8(tb[NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID]);
+               memcpy(func->followup_dest.addr,
+                      nla_data(tb[NL80211_NAN_FUNC_FOLLOW_UP_DEST]),
+                      sizeof(func->followup_dest.addr));
+               if (func->ttl) {
+                       err = -EINVAL;
+                       goto out;
+               }
+               break;
+       default:
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (tb[NL80211_NAN_FUNC_SRF]) {
+               struct nlattr *srf_tb[NUM_NL80211_NAN_SRF_ATTR];
+
+               err = nla_parse(srf_tb, NL80211_NAN_SRF_ATTR_MAX,
+                               nla_data(tb[NL80211_NAN_FUNC_SRF]),
+                               nla_len(tb[NL80211_NAN_FUNC_SRF]), NULL);
+               if (err)
+                       goto out;
+
+               func->srf_include =
+                       nla_get_flag(srf_tb[NL80211_NAN_SRF_INCLUDE]);
+
+               if (srf_tb[NL80211_NAN_SRF_BF]) {
+                       if (srf_tb[NL80211_NAN_SRF_MAC_ADDRS] ||
+                           !srf_tb[NL80211_NAN_SRF_BF_IDX]) {
+                               err = -EINVAL;
+                               goto out;
+                       }
+
+                       func->srf_bf_len =
+                               nla_len(srf_tb[NL80211_NAN_SRF_BF]);
+                       func->srf_bf =
+                               kmemdup(nla_data(srf_tb[NL80211_NAN_SRF_BF]),
+                                       func->srf_bf_len, GFP_KERNEL);
+                       if (!func->srf_bf) {
+                               err = -ENOMEM;
+                               goto out;
+                       }
+
+                       func->srf_bf_idx =
+                               nla_get_u8(srf_tb[NL80211_NAN_SRF_BF_IDX]);
+               } else {
+                       struct nlattr *attr, *mac_attr =
+                               srf_tb[NL80211_NAN_SRF_MAC_ADDRS];
+                       int n_entries, rem, i = 0;
+
+                       if (!mac_attr) {
+                               err = -EINVAL;
+                               goto out;
+                       }
+
+                       n_entries = validate_acl_mac_addrs(mac_attr);
+                       if (n_entries <= 0) {
+                               err = -EINVAL;
+                               goto out;
+                       }
+
+                       func->srf_num_macs = n_entries;
+                       func->srf_macs =
+                               kzalloc(sizeof(*func->srf_macs) * n_entries,
+                                       GFP_KERNEL);
+                       if (!func->srf_macs) {
+                               err = -ENOMEM;
+                               goto out;
+                       }
+
+                       nla_for_each_nested(attr, mac_attr, rem)
+                               memcpy(func->srf_macs[i++].addr, nla_data(attr),
+                                      sizeof(*func->srf_macs));
+               }
+       }
+
+       if (tb[NL80211_NAN_FUNC_TX_MATCH_FILTER]) {
+               err = handle_nan_filter(tb[NL80211_NAN_FUNC_TX_MATCH_FILTER],
+                                       func, true);
+               if (err)
+                       goto out;
+       }
+
+       if (tb[NL80211_NAN_FUNC_RX_MATCH_FILTER]) {
+               err = handle_nan_filter(tb[NL80211_NAN_FUNC_RX_MATCH_FILTER],
+                                       func, false);
+               if (err)
+                       goto out;
+       }
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
+                            NL80211_CMD_ADD_NAN_FUNCTION);
+       /* This can't really happen - we just allocated 4KB */
+       if (WARN_ON(!hdr)) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       err = rdev_add_nan_func(rdev, wdev, func);
+out:
+       if (err < 0) {
+               cfg80211_free_nan_func(func);
+               nlmsg_free(msg);
+               return err;
+       }
+
+       /* propagate the instance id and cookie to userspace  */
+       if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, func->cookie,
+                             NL80211_ATTR_PAD))
+               goto nla_put_failure;
+
+       func_attr = nla_nest_start(msg, NL80211_ATTR_NAN_FUNC);
+       if (!func_attr)
+               goto nla_put_failure;
+
+       if (nla_put_u8(msg, NL80211_NAN_FUNC_INSTANCE_ID,
+                      func->instance_id))
+               goto nla_put_failure;
+
+       nla_nest_end(msg, func_attr);
+
+       genlmsg_end(msg, hdr);
+       return genlmsg_reply(msg, info);
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return -ENOBUFS;
+}
+
+static int nl80211_nan_del_func(struct sk_buff *skb,
+                              struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct wireless_dev *wdev = info->user_ptr[1];
+       u64 cookie;
+
+       if (wdev->iftype != NL80211_IFTYPE_NAN)
+               return -EOPNOTSUPP;
+
+       if (!wdev->nan_started)
+               return -ENOTCONN;
+
+       if (!info->attrs[NL80211_ATTR_COOKIE])
+               return -EINVAL;
+
+       if (wdev->owner_nlportid &&
+           wdev->owner_nlportid != info->snd_portid)
+               return -ENOTCONN;
+
+       cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
+
+       rdev_del_nan_func(rdev, wdev, cookie);
+
+       return 0;
+}
+
+static int nl80211_nan_change_config(struct sk_buff *skb,
+                                    struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct wireless_dev *wdev = info->user_ptr[1];
+       struct cfg80211_nan_conf conf = {};
+       u32 changed = 0;
+
+       if (wdev->iftype != NL80211_IFTYPE_NAN)
+               return -EOPNOTSUPP;
+
+       if (!wdev->nan_started)
+               return -ENOTCONN;
+
+       if (info->attrs[NL80211_ATTR_NAN_MASTER_PREF]) {
+               conf.master_pref =
+                       nla_get_u8(info->attrs[NL80211_ATTR_NAN_MASTER_PREF]);
+               if (conf.master_pref <= 1 || conf.master_pref == 255)
+                       return -EINVAL;
+
+               changed |= CFG80211_NAN_CONF_CHANGED_PREF;
+       }
+
+       if (info->attrs[NL80211_ATTR_NAN_DUAL]) {
+               conf.dual = nla_get_u8(info->attrs[NL80211_ATTR_NAN_DUAL]);
+               changed |= CFG80211_NAN_CONF_CHANGED_DUAL;
+       }
+
+       if (!changed)
+               return -EINVAL;
+
+       return rdev_nan_change_conf(rdev, wdev, &conf, changed);
+}
+
+void cfg80211_nan_match(struct wireless_dev *wdev,
+                       struct cfg80211_nan_match_params *match, gfp_t gfp)
+{
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+       struct nlattr *match_attr, *local_func_attr, *peer_func_attr;
+       struct sk_buff *msg;
+       void *hdr;
+
+       if (WARN_ON(!match->inst_id || !match->peer_inst_id || !match->addr))
+               return;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NAN_MATCH);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+           (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
+                                        wdev->netdev->ifindex)) ||
+           nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+                             NL80211_ATTR_PAD))
+               goto nla_put_failure;
+
+       if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, match->cookie,
+                             NL80211_ATTR_PAD) ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, match->addr))
+               goto nla_put_failure;
+
+       match_attr = nla_nest_start(msg, NL80211_ATTR_NAN_MATCH);
+       if (!match_attr)
+               goto nla_put_failure;
+
+       local_func_attr = nla_nest_start(msg, NL80211_NAN_MATCH_FUNC_LOCAL);
+       if (!local_func_attr)
+               goto nla_put_failure;
+
+       if (nla_put_u8(msg, NL80211_NAN_FUNC_INSTANCE_ID, match->inst_id))
+               goto nla_put_failure;
+
+       nla_nest_end(msg, local_func_attr);
+
+       peer_func_attr = nla_nest_start(msg, NL80211_NAN_MATCH_FUNC_PEER);
+       if (!peer_func_attr)
+               goto nla_put_failure;
+
+       if (nla_put_u8(msg, NL80211_NAN_FUNC_TYPE, match->type) ||
+           nla_put_u8(msg, NL80211_NAN_FUNC_INSTANCE_ID, match->peer_inst_id))
+               goto nla_put_failure;
+
+       if (match->info && match->info_len &&
+           nla_put(msg, NL80211_NAN_FUNC_SERVICE_INFO, match->info_len,
+                   match->info))
+               goto nla_put_failure;
+
+       nla_nest_end(msg, peer_func_attr);
+       nla_nest_end(msg, match_attr);
+       genlmsg_end(msg, hdr);
+
+       if (!wdev->owner_nlportid)
+               genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
+                                       msg, 0, NL80211_MCGRP_NAN, gfp);
+       else
+               genlmsg_unicast(wiphy_net(&rdev->wiphy), msg,
+                               wdev->owner_nlportid);
+
+       return;
+
+nla_put_failure:
+       nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_nan_match);
+
+void cfg80211_nan_func_terminated(struct wireless_dev *wdev,
+                                 u8 inst_id,
+                                 enum nl80211_nan_func_term_reason reason,
+                                 u64 cookie, gfp_t gfp)
+{
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+       struct sk_buff *msg;
+       struct nlattr *func_attr;
+       void *hdr;
+
+       if (WARN_ON(!inst_id))
+               return;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_NAN_FUNCTION);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+           (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
+                                        wdev->netdev->ifindex)) ||
+           nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+                             NL80211_ATTR_PAD))
+               goto nla_put_failure;
+
+       if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, cookie,
+                             NL80211_ATTR_PAD))
+               goto nla_put_failure;
+
+       func_attr = nla_nest_start(msg, NL80211_ATTR_NAN_FUNC);
+       if (!func_attr)
+               goto nla_put_failure;
+
+       if (nla_put_u8(msg, NL80211_NAN_FUNC_INSTANCE_ID, inst_id) ||
+           nla_put_u8(msg, NL80211_NAN_FUNC_TERM_REASON, reason))
+               goto nla_put_failure;
+
+       nla_nest_end(msg, func_attr);
+       genlmsg_end(msg, hdr);
+
+       if (!wdev->owner_nlportid)
+               genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
+                                       msg, 0, NL80211_MCGRP_NAN, gfp);
+       else
+               genlmsg_unicast(wiphy_net(&rdev->wiphy), msg,
+                               wdev->owner_nlportid);
+
+       return;
+
+nla_put_failure:
+       nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_nan_func_terminated);
+
+static int nl80211_get_protocol_features(struct sk_buff *skb,
+                                        struct genl_info *info)
+{
+       void *hdr;
+       struct sk_buff *msg;
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return -ENOMEM;
@@ -11063,7 +11794,14 @@ static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
 
                        dev_hold(dev);
                } else if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP) {
-                       if (!wdev->p2p_started) {
+                       if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE &&
+                           !wdev->p2p_started) {
+                               if (rtnl)
+                                       rtnl_unlock();
+                               return -ENETDOWN;
+                       }
+                       if (wdev->iftype == NL80211_IFTYPE_NAN &&
+                           !wdev->nan_started) {
                                if (rtnl)
                                        rtnl_unlock();
                                return -ENETDOWN;
@@ -11696,6 +12434,46 @@ static const struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_START_NAN,
+               .doit = nl80211_start_nan,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_WDEV |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_STOP_NAN,
+               .doit = nl80211_stop_nan,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_ADD_NAN_FUNCTION,
+               .doit = nl80211_nan_add_func,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_DEL_NAN_FUNCTION,
+               .doit = nl80211_nan_del_func,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_CHANGE_NAN_CONFIG,
+               .doit = nl80211_nan_change_config,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
        {
                .cmd = NL80211_CMD_SET_MCAST_RATE,
                .doit = nl80211_set_mcast_rate,
@@ -11847,6 +12625,29 @@ void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev,
                                NL80211_MCGRP_CONFIG, GFP_KERNEL);
 }
 
+void nl80211_notify_iface(struct cfg80211_registered_device *rdev,
+                               struct wireless_dev *wdev,
+                               enum nl80211_commands cmd)
+{
+       struct sk_buff *msg;
+
+       WARN_ON(cmd != NL80211_CMD_NEW_INTERFACE &&
+               cmd != NL80211_CMD_DEL_INTERFACE);
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return;
+
+       if (nl80211_send_iface(msg, 0, 0, 0, rdev, wdev,
+                              cmd == NL80211_CMD_DEL_INTERFACE) < 0) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_CONFIG, GFP_KERNEL);
+}
+
 static int nl80211_add_scan_req(struct sk_buff *msg,
                                struct cfg80211_registered_device *rdev)
 {