blob: 7397a372c78eb7b79a1348311a2d26a119c35093 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* This is the new netlink-based wireless configuration interface.
*
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2015-2017 Intel Deutschland GmbH
* Copyright (C) 2018-2024 Intel Corporation
*/
#include <linux/if.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/if_ether.h>
#include <linux/ieee80211.h>
#include <linux/nl80211.h>
#include <linux/rtnetlink.h>
#include <linux/netlink.h>
#include <linux/nospec.h>
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
#include <net/net_namespace.h>
#include <net/genetlink.h>
#include <net/cfg80211.h>
#include <net/sock.h>
#include <net/inet_connection_sock.h>
#include "core.h"
#include "nl80211.h"
#include "reg.h"
#include "rdev-ops.h"
static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
struct genl_info *info,
struct cfg80211_crypto_settings *settings,
int cipher_limit);
/* the netlink family */
static struct genl_family nl80211_fam;
/* multicast groups */
enum nl80211_multicast_groups {
NL80211_MCGRP_CONFIG,
NL80211_MCGRP_SCAN,
NL80211_MCGRP_REGULATORY,
NL80211_MCGRP_MLME,
NL80211_MCGRP_VENDOR,
NL80211_MCGRP_NAN,
NL80211_MCGRP_TESTMODE /* keep last - ifdef! */
};
static const struct genl_multicast_group nl80211_mcgrps[] = {
[NL80211_MCGRP_CONFIG] = { .name = NL80211_MULTICAST_GROUP_CONFIG },
[NL80211_MCGRP_SCAN] = { .name = NL80211_MULTICAST_GROUP_SCAN },
[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
};
/* returns ERR_PTR values */
static struct wireless_dev *
__cfg80211_wdev_from_attrs(struct cfg80211_registered_device *rdev,
struct net *netns, struct nlattr **attrs)
{
struct wireless_dev *result = NULL;
bool have_ifidx = attrs[NL80211_ATTR_IFINDEX];
bool have_wdev_id = attrs[NL80211_ATTR_WDEV];
u64 wdev_id = 0;
int wiphy_idx = -1;
int ifidx = -1;
if (!have_ifidx && !have_wdev_id)
return ERR_PTR(-EINVAL);
if (have_ifidx)
ifidx = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
if (have_wdev_id) {
wdev_id = nla_get_u64(attrs[NL80211_ATTR_WDEV]);
wiphy_idx = wdev_id >> 32;
}
if (rdev) {
struct wireless_dev *wdev;
lockdep_assert_held(&rdev->wiphy.mtx);
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
if (have_ifidx && wdev->netdev &&
wdev->netdev->ifindex == ifidx) {
result = wdev;
break;
}
if (have_wdev_id && wdev->identifier == (u32)wdev_id) {
result = wdev;
break;
}
}
return result ?: ERR_PTR(-ENODEV);
}
ASSERT_RTNL();
for_each_rdev(rdev) {
struct wireless_dev *wdev;
if (wiphy_net(&rdev->wiphy) != netns)
continue;
if (have_wdev_id && rdev->wiphy_idx != wiphy_idx)
continue;
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
if (have_ifidx && wdev->netdev &&
wdev->netdev->ifindex == ifidx) {
result = wdev;
break;
}
if (have_wdev_id && wdev->identifier == (u32)wdev_id) {
result = wdev;
break;
}
}
if (result)
break;
}
if (result)
return result;
return ERR_PTR(-ENODEV);
}
static struct cfg80211_registered_device *
__cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
{
struct cfg80211_registered_device *rdev = NULL, *tmp;
struct net_device *netdev;
ASSERT_RTNL();
if (!attrs[NL80211_ATTR_WIPHY] &&
!attrs[NL80211_ATTR_IFINDEX] &&
!attrs[NL80211_ATTR_WDEV])
return ERR_PTR(-EINVAL);
if (attrs[NL80211_ATTR_WIPHY])
rdev = cfg80211_rdev_by_wiphy_idx(
nla_get_u32(attrs[NL80211_ATTR_WIPHY]));
if (attrs[NL80211_ATTR_WDEV]) {
u64 wdev_id = nla_get_u64(attrs[NL80211_ATTR_WDEV]);
struct wireless_dev *wdev;
bool found = false;
tmp = cfg80211_rdev_by_wiphy_idx(wdev_id >> 32);
if (tmp) {
/* make sure wdev exists */
list_for_each_entry(wdev, &tmp->wiphy.wdev_list, list) {
if (wdev->identifier != (u32)wdev_id)
continue;
found = true;
break;
}
if (!found)
tmp = NULL;
if (rdev && tmp != rdev)
return ERR_PTR(-EINVAL);
rdev = tmp;
}
}
if (attrs[NL80211_ATTR_IFINDEX]) {
int ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
netdev = __dev_get_by_index(netns, ifindex);
if (netdev) {
if (netdev->ieee80211_ptr)
tmp = wiphy_to_rdev(
netdev->ieee80211_ptr->wiphy);
else
tmp = NULL;
/* not wireless device -- return error */
if (!tmp)
return ERR_PTR(-EINVAL);
/* mismatch -- return error */
if (rdev && tmp != rdev)
return ERR_PTR(-EINVAL);
rdev = tmp;
}
}
if (!rdev)
return ERR_PTR(-ENODEV);
if (netns != wiphy_net(&rdev->wiphy))
return ERR_PTR(-ENODEV);
return rdev;
}
/*
* This function returns a pointer to the driver
* that the genl_info item that is passed refers to.
*
* The result of this can be a PTR_ERR and hence must
* be checked with IS_ERR() for errors.
*/
static struct cfg80211_registered_device *
cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info)
{
return __cfg80211_rdev_from_attrs(netns, info->attrs);
}
static int validate_beacon_head(const struct nlattr *attr,
struct netlink_ext_ack *extack)
{
const u8 *data = nla_data(attr);
unsigned int len = nla_len(attr);
const struct element *elem;
const struct ieee80211_mgmt *mgmt = (void *)data;
unsigned int fixedlen, hdrlen;
bool s1g_bcn;
if (len < offsetofend(typeof(*mgmt), frame_control))
goto err;
s1g_bcn = ieee80211_is_s1g_beacon(mgmt->frame_control);
if (s1g_bcn) {
fixedlen = offsetof(struct ieee80211_ext,
u.s1g_beacon.variable);
hdrlen = offsetof(struct ieee80211_ext, u.s1g_beacon);
} else {
fixedlen = offsetof(struct ieee80211_mgmt,
u.beacon.variable);
hdrlen = offsetof(struct ieee80211_mgmt, u.beacon);
}
if (len < fixedlen)
goto err;
if (ieee80211_hdrlen(mgmt->frame_control) != hdrlen)
goto err;
data += fixedlen;
len -= fixedlen;
for_each_element(elem, data, len) {
/* nothing */
}
if (for_each_element_completed(elem, data, len))
return 0;
err:
NL_SET_ERR_MSG_ATTR(extack, attr, "malformed beacon head");
return -EINVAL;
}
static int validate_ie_attr(const struct nlattr *attr,
struct netlink_ext_ack *extack)
{
const u8 *data = nla_data(attr);
unsigned int len = nla_len(attr);
const struct element *elem;
for_each_element(elem, data, len) {
/* nothing */
}
if (for_each_element_completed(elem, data, len))
return 0;
NL_SET_ERR_MSG_ATTR(extack, attr, "malformed information elements");
return -EINVAL;
}
static int validate_he_capa(const struct nlattr *attr,
struct netlink_ext_ack *extack)
{
if (!ieee80211_he_capa_size_ok(nla_data(attr), nla_len(attr)))
return -EINVAL;
return 0;
}
/* policy for the attributes */
static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR];
static const struct nla_policy
nl80211_ftm_responder_policy[NL80211_FTM_RESP_ATTR_MAX + 1] = {
[NL80211_FTM_RESP_ATTR_ENABLED] = { .type = NLA_FLAG, },
[NL80211_FTM_RESP_ATTR_LCI] = { .type = NLA_BINARY,
.len = U8_MAX },
[NL80211_FTM_RESP_ATTR_CIVICLOC] = { .type = NLA_BINARY,
.len = U8_MAX },
};
static const struct nla_policy
nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = {
[NL80211_PMSR_FTM_REQ_ATTR_ASAP] = { .type = NLA_FLAG },
[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE] = { .type = NLA_U32 },
[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP] =
NLA_POLICY_MAX(NLA_U8, 15),
[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD] = { .type = NLA_U16 },
[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION] =
NLA_POLICY_MAX(NLA_U8, 15),
[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST] = { .type = NLA_U8 },
[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES] = { .type = NLA_U8 },
[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI] = { .type = NLA_FLAG },
[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC] = { .type = NLA_FLAG },
[NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED] = { .type = NLA_FLAG },
[NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED] = { .type = NLA_FLAG },
[NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK] = { .type = NLA_FLAG },
[NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR] = { .type = NLA_U8 },
};
static const struct nla_policy
nl80211_pmsr_req_data_policy[NL80211_PMSR_TYPE_MAX + 1] = {
[NL80211_PMSR_TYPE_FTM] =
NLA_POLICY_NESTED(nl80211_pmsr_ftm_req_attr_policy),
};
static const struct nla_policy
nl80211_pmsr_req_attr_policy[NL80211_PMSR_REQ_ATTR_MAX + 1] = {
[NL80211_PMSR_REQ_ATTR_DATA] =
NLA_POLICY_NESTED(nl80211_pmsr_req_data_policy),
[NL80211_PMSR_REQ_ATTR_GET_AP_TSF] = { .type = NLA_FLAG },
};
static const struct nla_policy
nl80211_pmsr_peer_attr_policy[NL80211_PMSR_PEER_ATTR_MAX + 1] = {
[NL80211_PMSR_PEER_ATTR_ADDR] = NLA_POLICY_ETH_ADDR,
[NL80211_PMSR_PEER_ATTR_CHAN] = NLA_POLICY_NESTED(nl80211_policy),
[NL80211_PMSR_PEER_ATTR_REQ] =
NLA_POLICY_NESTED(nl80211_pmsr_req_attr_policy),
[NL80211_PMSR_PEER_ATTR_RESP] = { .type = NLA_REJECT },
};
static const struct nla_policy
nl80211_pmsr_attr_policy[NL80211_PMSR_ATTR_MAX + 1] = {
[NL80211_PMSR_ATTR_MAX_PEERS] = { .type = NLA_REJECT },
[NL80211_PMSR_ATTR_REPORT_AP_TSF] = { .type = NLA_REJECT },
[NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR] = { .type = NLA_REJECT },
[NL80211_PMSR_ATTR_TYPE_CAPA] = { .type = NLA_REJECT },
[NL80211_PMSR_ATTR_PEERS] =
NLA_POLICY_NESTED_ARRAY(nl80211_pmsr_peer_attr_policy),
};
static const struct nla_policy
he_obss_pd_policy[NL80211_HE_OBSS_PD_ATTR_MAX + 1] = {
[NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET] =
NLA_POLICY_RANGE(NLA_U8, 1, 20),
[NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET] =
NLA_POLICY_RANGE(NLA_U8, 1, 20),
[NL80211_HE_OBSS_PD_ATTR_NON_SRG_MAX_OFFSET] =
NLA_POLICY_RANGE(NLA_U8, 1, 20),
[NL80211_HE_OBSS_PD_ATTR_BSS_COLOR_BITMAP] =
NLA_POLICY_EXACT_LEN(8),
[NL80211_HE_OBSS_PD_ATTR_PARTIAL_BSSID_BITMAP] =
NLA_POLICY_EXACT_LEN(8),
[NL80211_HE_OBSS_PD_ATTR_SR_CTRL] = { .type = NLA_U8 },
};
static const struct nla_policy
he_bss_color_policy[NL80211_HE_BSS_COLOR_ATTR_MAX + 1] = {
[NL80211_HE_BSS_COLOR_ATTR_COLOR] = NLA_POLICY_RANGE(NLA_U8, 1, 63),
[NL80211_HE_BSS_COLOR_ATTR_DISABLED] = { .type = NLA_FLAG },
[NL80211_HE_BSS_COLOR_ATTR_PARTIAL] = { .type = NLA_FLAG },
};
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] = NLA_POLICY_EXACT_LEN_WARN(sizeof(struct nl80211_txrate_vht)),
[NL80211_TXRATE_GI] = { .type = NLA_U8 },
[NL80211_TXRATE_HE] = NLA_POLICY_EXACT_LEN(sizeof(struct nl80211_txrate_he)),
[NL80211_TXRATE_HE_GI] = NLA_POLICY_RANGE(NLA_U8,
NL80211_RATE_INFO_HE_GI_0_8,
NL80211_RATE_INFO_HE_GI_3_2),
[NL80211_TXRATE_HE_LTF] = NLA_POLICY_RANGE(NLA_U8,
NL80211_RATE_INFO_HE_1XLTF,
NL80211_RATE_INFO_HE_4XLTF),
};
static const struct nla_policy
nl80211_tid_config_attr_policy[NL80211_TID_CONFIG_ATTR_MAX + 1] = {
[NL80211_TID_CONFIG_ATTR_VIF_SUPP] = { .type = NLA_U64 },
[NL80211_TID_CONFIG_ATTR_PEER_SUPP] = { .type = NLA_U64 },
[NL80211_TID_CONFIG_ATTR_OVERRIDE] = { .type = NLA_FLAG },
[NL80211_TID_CONFIG_ATTR_TIDS] = NLA_POLICY_RANGE(NLA_U16, 1, 0xff),
[NL80211_TID_CONFIG_ATTR_NOACK] =
NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE),
[NL80211_TID_CONFIG_ATTR_RETRY_SHORT] = NLA_POLICY_MIN(NLA_U8, 1),
[NL80211_TID_CONFIG_ATTR_RETRY_LONG] = NLA_POLICY_MIN(NLA_U8, 1),
[NL80211_TID_CONFIG_ATTR_AMPDU_CTRL] =
NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE),
[NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL] =
NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE),
[NL80211_TID_CONFIG_ATTR_AMSDU_CTRL] =
NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE),
[NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE] =
NLA_POLICY_MAX(NLA_U8, NL80211_TX_RATE_FIXED),
[NL80211_TID_CONFIG_ATTR_TX_RATE] =
NLA_POLICY_NESTED(nl80211_txattr_policy),
};
static const struct nla_policy
nl80211_fils_discovery_policy[NL80211_FILS_DISCOVERY_ATTR_MAX + 1] = {
[NL80211_FILS_DISCOVERY_ATTR_INT_MIN] = NLA_POLICY_MAX(NLA_U32, 10000),
[NL80211_FILS_DISCOVERY_ATTR_INT_MAX] = NLA_POLICY_MAX(NLA_U32, 10000),
[NL80211_FILS_DISCOVERY_ATTR_TMPL] =
NLA_POLICY_RANGE(NLA_BINARY,
NL80211_FILS_DISCOVERY_TMPL_MIN_LEN,
IEEE80211_MAX_DATA_LEN),
};
static const struct nla_policy
nl80211_unsol_bcast_probe_resp_policy[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_MAX + 1] = {
[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT] = NLA_POLICY_MAX(NLA_U32, 20),
[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_DATA_LEN }
};
static const struct nla_policy
sar_specs_policy[NL80211_SAR_ATTR_SPECS_MAX + 1] = {
[NL80211_SAR_ATTR_SPECS_POWER] = { .type = NLA_S32 },
[NL80211_SAR_ATTR_SPECS_RANGE_INDEX] = {.type = NLA_U32 },
};
static const struct nla_policy
sar_policy[NL80211_SAR_ATTR_MAX + 1] = {
[NL80211_SAR_ATTR_TYPE] = NLA_POLICY_MAX(NLA_U32, NUM_NL80211_SAR_TYPE),
[NL80211_SAR_ATTR_SPECS] = NLA_POLICY_NESTED_ARRAY(sar_specs_policy),
};
static const struct nla_policy
nl80211_mbssid_config_policy[NL80211_MBSSID_CONFIG_ATTR_MAX + 1] = {
[NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES] = NLA_POLICY_MIN(NLA_U8, 2),
[NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY] =
NLA_POLICY_MIN(NLA_U8, 1),
[NL80211_MBSSID_CONFIG_ATTR_INDEX] = { .type = NLA_U8 },
[NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX] = { .type = NLA_U32 },
[NL80211_MBSSID_CONFIG_ATTR_EMA] = { .type = NLA_FLAG },
};
static const struct nla_policy
nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] = {
[NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
[NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
};
static const struct netlink_range_validation nl80211_punct_bitmap_range = {
.min = 0,
.max = 0xffff,
};
static const struct netlink_range_validation q_range = {
.max = INT_MAX,
};
static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[0] = { .strict_start_type = NL80211_ATTR_HE_OBSS_PD },
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
.len = 20-1 },
[NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
[NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_EDMG_CHANNELS] = NLA_POLICY_RANGE(NLA_U8,
NL80211_EDMG_CHANNELS_MIN,
NL80211_EDMG_CHANNELS_MAX),
[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG] = NLA_POLICY_RANGE(NLA_U8,
NL80211_EDMG_BW_CONFIG_MIN,
NL80211_EDMG_BW_CONFIG_MAX),
[NL80211_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 },
[NL80211_ATTR_CENTER_FREQ1] = { .type = NLA_U32 },
[NL80211_ATTR_CENTER_FREQ1_OFFSET] = NLA_POLICY_RANGE(NLA_U32, 0, 999),
[NL80211_ATTR_CENTER_FREQ2] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_RETRY_SHORT] = NLA_POLICY_MIN(NLA_U8, 1),
[NL80211_ATTR_WIPHY_RETRY_LONG] = NLA_POLICY_MIN(NLA_U8, 1),
[NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 },
[NL80211_ATTR_WIPHY_DYN_ACK] = { .type = NLA_FLAG },
[NL80211_ATTR_IFTYPE] = NLA_POLICY_MAX(NLA_U32, NL80211_IFTYPE_MAX),
[NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
[NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
[NL80211_ATTR_MAC] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
[NL80211_ATTR_PREV_BSSID] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
[NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
[NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
.len = WLAN_MAX_KEY_LEN },
[NL80211_ATTR_KEY_IDX] = NLA_POLICY_MAX(NLA_U8, 7),
[NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
[NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
[NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
[NL80211_ATTR_KEY_TYPE] =
NLA_POLICY_MAX(NLA_U32, NUM_NL80211_KEYTYPES),
[NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
[NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
[NL80211_ATTR_BEACON_HEAD] =
NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_beacon_head,
IEEE80211_MAX_DATA_LEN),
[NL80211_ATTR_BEACON_TAIL] =
NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_ie_attr,
IEEE80211_MAX_DATA_LEN),
[NL80211_ATTR_STA_AID] =
NLA_POLICY_RANGE(NLA_U16, 1, IEEE80211_MAX_AID),
[NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
[NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
[NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
.len = NL80211_MAX_SUPP_RATES },
[NL80211_ATTR_STA_PLINK_ACTION] =
NLA_POLICY_MAX(NLA_U8, NUM_NL80211_PLINK_ACTIONS - 1),
[NL80211_ATTR_STA_TX_POWER_SETTING] =
NLA_POLICY_RANGE(NLA_U8,
NL80211_TX_POWER_AUTOMATIC,
NL80211_TX_POWER_FIXED),
[NL80211_ATTR_STA_TX_POWER] = { .type = NLA_S16 },
[NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
[NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
[NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_MESH_ID_LEN },
[NL80211_ATTR_MPATH_NEXT_HOP] = NLA_POLICY_ETH_ADDR_COMPAT,
/* allow 3 for NUL-termination, we used to declare this NLA_STRING */
[NL80211_ATTR_REG_ALPHA2] = NLA_POLICY_RANGE(NLA_BINARY, 2, 3),
[NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
[NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
[NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
[NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
[NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
.len = NL80211_MAX_SUPP_RATES },
[NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 },
[NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED },
[NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG },
[NL80211_ATTR_HT_CAPABILITY] = NLA_POLICY_EXACT_LEN_WARN(NL80211_HT_CAPABILITY_LEN),
[NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
[NL80211_ATTR_IE] = NLA_POLICY_VALIDATE_FN(NLA_BINARY,
validate_ie_attr,
IEEE80211_MAX_DATA_LEN),
[NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
[NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
[NL80211_ATTR_SSID] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_SSID_LEN },
[NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
[NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
[NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
[NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
[NL80211_ATTR_USE_MFP] = NLA_POLICY_RANGE(NLA_U32,
NL80211_MFP_NO,
NL80211_MFP_OPTIONAL),
[NL80211_ATTR_STA_FLAGS2] =
NLA_POLICY_EXACT_LEN_WARN(sizeof(struct nl80211_sta_flag_update)),
[NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
[NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 },
[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG },
[NL80211_ATTR_CONTROL_PORT_OVER_NL80211] = { .type = NLA_FLAG },
[NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
[NL80211_ATTR_STATUS_CODE] = { .type = NLA_U16 },
[NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
[NL80211_ATTR_WPA_VERSIONS] =
NLA_POLICY_RANGE(NLA_U32, 0,
NL80211_WPA_VERSION_1 |
NL80211_WPA_VERSION_2 |
NL80211_WPA_VERSION_3),
[NL80211_ATTR_PID] = { .type = NLA_U32 },
[NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
[NL80211_ATTR_PMKID] = NLA_POLICY_EXACT_LEN_WARN(WLAN_PMKID_LEN),
[NL80211_ATTR_DURATION] = { .type = NLA_U32 },
[NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
[NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
[NL80211_ATTR_FRAME] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_DATA_LEN },
[NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
[NL80211_ATTR_PS_STATE] = NLA_POLICY_RANGE(NLA_U32,
NL80211_PS_DISABLED,
NL80211_PS_ENABLED),
[NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
[NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
[NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 },
[NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 },
[NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 },
[NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 },
[NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 },
[NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG },
[NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
[NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED },
[NL80211_ATTR_STA_PLINK_STATE] =
NLA_POLICY_MAX(NLA_U8, NUM_NL80211_PLINK_STATES - 1),
[NL80211_ATTR_MEASUREMENT_DURATION] = { .type = NLA_U16 },
[NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY] = { .type = NLA_FLAG },
[NL80211_ATTR_MESH_PEER_AID] =
NLA_POLICY_RANGE(NLA_U16, 1, IEEE80211_MAX_AID),
[NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 },
[NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED },
[NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED },
[NL80211_ATTR_HIDDEN_SSID] =
NLA_POLICY_RANGE(NLA_U32,
NL80211_HIDDEN_SSID_NOT_IN_USE,
NL80211_HIDDEN_SSID_ZERO_CONTENTS),
[NL80211_ATTR_IE_PROBE_RESP] =
NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_ie_attr,
IEEE80211_MAX_DATA_LEN),
[NL80211_ATTR_IE_ASSOC_RESP] =
NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_ie_attr,
IEEE80211_MAX_DATA_LEN),
[NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG },
[NL80211_ATTR_STA_WME] = NLA_POLICY_NESTED(nl80211_sta_wme_policy),
[NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED },
[NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG },
[NL80211_ATTR_TDLS_ACTION] = { .type = NLA_U8 },
[NL80211_ATTR_TDLS_DIALOG_TOKEN] = { .type = NLA_U8 },
[NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 },
[NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG },
[NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG },
[NL80211_ATTR_TDLS_INITIATOR] = { .type = NLA_FLAG },
[NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG },
[NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_DATA_LEN },
[NL80211_ATTR_DFS_REGION] = { .type = NLA_U8 },
[NL80211_ATTR_DISABLE_HT] = { .type = NLA_FLAG },
[NL80211_ATTR_HT_CAPABILITY_MASK] = {
.len = NL80211_HT_CAPABILITY_LEN
},
[NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 },
[NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 },
[NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },
[NL80211_ATTR_WDEV] = { .type = NLA_U64 },
[NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 },
/* need to include at least Auth Transaction and Status Code */
[NL80211_ATTR_AUTH_DATA] = NLA_POLICY_MIN_LEN(4),
[NL80211_ATTR_VHT_CAPABILITY] = NLA_POLICY_EXACT_LEN_WARN(NL80211_VHT_CAPABILITY_LEN),
[NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 },
[NL80211_ATTR_P2P_CTWINDOW] = NLA_POLICY_MAX(NLA_U8, 127),
[NL80211_ATTR_P2P_OPPPS] = NLA_POLICY_MAX(NLA_U8, 1),
[NL80211_ATTR_LOCAL_MESH_POWER_MODE] =
NLA_POLICY_RANGE(NLA_U32,
NL80211_MESH_POWER_UNKNOWN + 1,
NL80211_MESH_POWER_MAX),
[NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 },
[NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED },
[NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 },
[NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, },
[NL80211_ATTR_SPLIT_WIPHY_DUMP] = { .type = NLA_FLAG, },
[NL80211_ATTR_DISABLE_VHT] = { .type = NLA_FLAG },
[NL80211_ATTR_VHT_CAPABILITY_MASK] = {
.len = NL80211_VHT_CAPABILITY_LEN,
},
[NL80211_ATTR_MDID] = { .type = NLA_U16 },
[NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_DATA_LEN },
[NL80211_ATTR_CRIT_PROT_ID] = { .type = NLA_U16 },
[NL80211_ATTR_MAX_CRIT_PROT_DURATION] =
NLA_POLICY_MAX(NLA_U16, NL80211_CRIT_PROTO_MAX_DURATION),
[NL80211_ATTR_PEER_AID] =
NLA_POLICY_RANGE(NLA_U16, 1, IEEE80211_MAX_AID),
[NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 },
[NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG },
[NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
[NL80211_ATTR_CNTDWN_OFFS_BEACON] = { .type = NLA_BINARY },
[NL80211_ATTR_CNTDWN_OFFS_PRESP] = { .type = NLA_BINARY },
[NL80211_ATTR_STA_SUPPORTED_CHANNELS] = NLA_POLICY_MIN_LEN(2),
/*
* The value of the Length field of the Supported Operating
* Classes element is between 2 and 253.
*/
[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] =
NLA_POLICY_RANGE(NLA_BINARY, 2, 253),
[NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG },
[NL80211_ATTR_OPMODE_NOTIF] = { .type = NLA_U8 },
[NL80211_ATTR_VENDOR_ID] = { .type = NLA_U32 },
[NL80211_ATTR_VENDOR_SUBCMD] = { .type = NLA_U32 },
[NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY },
[NL80211_ATTR_QOS_MAP] = NLA_POLICY_RANGE(NLA_BINARY,
IEEE80211_QOS_MAP_LEN_MIN,
IEEE80211_QOS_MAP_LEN_MAX),
[NL80211_ATTR_MAC_HINT] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
[NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 },
[NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 },
[NL80211_ATTR_SOCKET_OWNER] = { .type = NLA_FLAG },
[NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY },
[NL80211_ATTR_USE_RRM] = { .type = NLA_FLAG },
[NL80211_ATTR_TSID] = NLA_POLICY_MAX(NLA_U8, IEEE80211_NUM_TIDS - 1),
[NL80211_ATTR_USER_PRIO] =
NLA_POLICY_MAX(NLA_U8, IEEE80211_NUM_UPS - 1),
[NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 },
[NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
[NL80211_ATTR_OPER_CLASS] = { .type = NLA_U8 },
[NL80211_ATTR_MAC_MASK] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
[NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG },
[NL80211_ATTR_NETNS_FD] = { .type = NLA_U32 },
[NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 },
[NL80211_ATTR_REG_INDOOR] = { .type = NLA_FLAG },
[NL80211_ATTR_PBSS] = { .type = NLA_FLAG },
[NL80211_ATTR_BSS_SELECT] = { .type = NLA_NESTED },
[NL80211_ATTR_STA_SUPPORT_P2P_PS] =
NLA_POLICY_MAX(NLA_U8, NUM_NL80211_P2P_PS_STATUS - 1),
[NL80211_ATTR_MU_MIMO_GROUP_DATA] = {
.len = VHT_MUMIMO_GROUPS_DATA_LEN
},
[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
[NL80211_ATTR_NAN_MASTER_PREF] = NLA_POLICY_MIN(NLA_U8, 1),
[NL80211_ATTR_BANDS] = { .type = NLA_U32 },
[NL80211_ATTR_NAN_FUNC] = { .type = NLA_NESTED },
[NL80211_ATTR_FILS_KEK] = { .type = NLA_BINARY,
.len = FILS_MAX_KEK_LEN },
[NL80211_ATTR_FILS_NONCES] = NLA_POLICY_EXACT_LEN_WARN(2 * FILS_NONCE_LEN),
[NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED] = { .type = NLA_FLAG, },
[NL80211_ATTR_BSSID] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI] = { .type = NLA_S8 },
[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST] = {
.len = sizeof(struct nl80211_bss_select_rssi_adjust)
},
[NL80211_ATTR_TIMEOUT_REASON] = { .type = NLA_U32 },
[NL80211_ATTR_FILS_ERP_USERNAME] = { .type = NLA_BINARY,
.len = FILS_ERP_MAX_USERNAME_LEN },
[NL80211_ATTR_FILS_ERP_REALM] = { .type = NLA_BINARY,
.len = FILS_ERP_MAX_REALM_LEN },
[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] = { .type = NLA_U16 },
[NL80211_ATTR_FILS_ERP_RRK] = { .type = NLA_BINARY,
.len = FILS_ERP_MAX_RRK_LEN },
[NL80211_ATTR_FILS_CACHE_ID] = NLA_POLICY_EXACT_LEN_WARN(2),
[NL80211_ATTR_PMK] = { .type = NLA_BINARY, .len = PMK_MAX_LEN },
[NL80211_ATTR_PMKR0_NAME] = NLA_POLICY_EXACT_LEN(WLAN_PMK_NAME_LEN),
[NL80211_ATTR_SCHED_SCAN_MULTI] = { .type = NLA_FLAG },
[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT] = { .type = NLA_FLAG },
[NL80211_ATTR_TXQ_LIMIT] = { .type = NLA_U32 },
[NL80211_ATTR_TXQ_MEMORY_LIMIT] = { .type = NLA_U32 },
[NL80211_ATTR_TXQ_QUANTUM] = NLA_POLICY_FULL_RANGE(NLA_U32, &q_range),
[NL80211_ATTR_HE_CAPABILITY] =
NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_he_capa,
NL80211_HE_MAX_CAPABILITY_LEN),
[NL80211_ATTR_FTM_RESPONDER] =
NLA_POLICY_NESTED(nl80211_ftm_responder_policy),
[NL80211_ATTR_TIMEOUT] = NLA_POLICY_MIN(NLA_U32, 1),
[NL80211_ATTR_PEER_MEASUREMENTS] =
NLA_POLICY_NESTED(nl80211_pmsr_attr_policy),
[NL80211_ATTR_AIRTIME_WEIGHT] = NLA_POLICY_MIN(NLA_U16, 1),
[NL80211_ATTR_SAE_PASSWORD] = { .type = NLA_BINARY,
.len = SAE_PASSWORD_MAX_LEN },
[NL80211_ATTR_TWT_RESPONDER] = { .type = NLA_FLAG },
[NL80211_ATTR_HE_OBSS_PD] = NLA_POLICY_NESTED(he_obss_pd_policy),
[NL80211_ATTR_VLAN_ID] = NLA_POLICY_RANGE(NLA_U16, 1, VLAN_N_VID - 2),
[NL80211_ATTR_HE_BSS_COLOR] = NLA_POLICY_NESTED(he_bss_color_policy),
[NL80211_ATTR_TID_CONFIG] =
NLA_POLICY_NESTED_ARRAY(nl80211_tid_config_attr_policy),
[NL80211_ATTR_CONTROL_PORT_NO_PREAUTH] = { .type = NLA_FLAG },
[NL80211_ATTR_PMK_LIFETIME] = NLA_POLICY_MIN(NLA_U32, 1),
[NL80211_ATTR_PMK_REAUTH_THRESHOLD] = NLA_POLICY_RANGE(NLA_U8, 1, 100),
[NL80211_ATTR_RECEIVE_MULTICAST] = { .type = NLA_FLAG },
[NL80211_ATTR_WIPHY_FREQ_OFFSET] = NLA_POLICY_RANGE(NLA_U32, 0, 999),
[NL80211_ATTR_SCAN_FREQ_KHZ] = { .type = NLA_NESTED },
[NL80211_ATTR_HE_6GHZ_CAPABILITY] =
NLA_POLICY_EXACT_LEN(sizeof(struct ieee80211_he_6ghz_capa)),
[NL80211_ATTR_FILS_DISCOVERY] =
NLA_POLICY_NESTED(nl80211_fils_discovery_policy),
[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP] =
NLA_POLICY_NESTED(nl80211_unsol_bcast_probe_resp_policy),
[NL80211_ATTR_S1G_CAPABILITY] =
NLA_POLICY_EXACT_LEN(IEEE80211_S1G_CAPABILITY_LEN),
[NL80211_ATTR_S1G_CAPABILITY_MASK] =
NLA_POLICY_EXACT_LEN(IEEE80211_S1G_CAPABILITY_LEN),
[NL80211_ATTR_SAE_PWE] =
NLA_POLICY_RANGE(NLA_U8, NL80211_SAE_PWE_HUNT_AND_PECK,
NL80211_SAE_PWE_BOTH),
[NL80211_ATTR_RECONNECT_REQUESTED] = { .type = NLA_REJECT },
[NL80211_ATTR_SAR_SPEC] = NLA_POLICY_NESTED(sar_policy),
[NL80211_ATTR_DISABLE_HE] = { .type = NLA_FLAG },
[NL80211_ATTR_OBSS_COLOR_BITMAP] = { .type = NLA_U64 },
[NL80211_ATTR_COLOR_CHANGE_COUNT] = { .type = NLA_U8 },
[NL80211_ATTR_COLOR_CHANGE_COLOR] = { .type = NLA_U8 },
[NL80211_ATTR_COLOR_CHANGE_ELEMS] = NLA_POLICY_NESTED(nl80211_policy),
[NL80211_ATTR_MBSSID_CONFIG] =
NLA_POLICY_NESTED(nl80211_mbssid_config_policy),
[NL80211_ATTR_MBSSID_ELEMS] = { .type = NLA_NESTED },
[NL80211_ATTR_RADAR_BACKGROUND] = { .type = NLA_FLAG },
[NL80211_ATTR_AP_SETTINGS_FLAGS] = { .type = NLA_U32 },
[NL80211_ATTR_EHT_CAPABILITY] =
NLA_POLICY_RANGE(NLA_BINARY,
NL80211_EHT_MIN_CAPABILITY_LEN,
NL80211_EHT_MAX_CAPABILITY_LEN),
[NL80211_ATTR_DISABLE_EHT] = { .type = NLA_FLAG },
[NL80211_ATTR_MLO_LINKS] =
NLA_POLICY_NESTED_ARRAY(nl80211_policy),
[NL80211_ATTR_MLO_LINK_ID] =
NLA_POLICY_RANGE(NLA_U8, 0, IEEE80211_MLD_MAX_NUM_LINKS),
[NL80211_ATTR_MLD_ADDR] = NLA_POLICY_EXACT_LEN(ETH_ALEN),
[NL80211_ATTR_MLO_SUPPORT] = { .type = NLA_FLAG },
[NL80211_ATTR_MAX_NUM_AKM_SUITES] = { .type = NLA_REJECT },
[NL80211_ATTR_PUNCT_BITMAP] =
NLA_POLICY_FULL_RANGE(NLA_U32, &nl80211_punct_bitmap_range),
[NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS] = { .type = NLA_U16 },
[NL80211_ATTR_HW_TIMESTAMP_ENABLED] = { .type = NLA_FLAG },
[NL80211_ATTR_EMA_RNR_ELEMS] = { .type = NLA_NESTED },
[NL80211_ATTR_MLO_LINK_DISABLED] = { .type = NLA_FLAG },
[NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA] = { .type = NLA_FLAG },
[NL80211_ATTR_MLO_TTLM_DLINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8),
[NL80211_ATTR_MLO_TTLM_ULINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8),
[NL80211_ATTR_ASSOC_SPP_AMSDU] = { .type = NLA_FLAG },
};
/* policy for the key attributes */
static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
[NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
[NL80211_KEY_IDX] = { .type = NLA_U8 },
[NL80211_KEY_CIPHER] = { .type = NLA_U32 },
[NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
[NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
[NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
[NL80211_KEY_TYPE] = NLA_POLICY_MAX(NLA_U32, NUM_NL80211_KEYTYPES - 1),
[NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
[NL80211_KEY_MODE] = NLA_POLICY_RANGE(NLA_U8, 0, NL80211_KEY_SET_TX),
};
/* policy for the key default flags */
static const struct nla_policy
nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = {
[NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG },
[NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG },
};
#ifdef CONFIG_PM
/* policy for WoWLAN attributes */
static const struct nla_policy
nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
[NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED },
[NL80211_WOWLAN_TRIG_NET_DETECT] = { .type = NLA_NESTED },
};
static const struct nla_policy
nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = {
[NL80211_WOWLAN_TCP_SRC_IPV4] = { .type = NLA_U32 },
[NL80211_WOWLAN_TCP_DST_IPV4] = { .type = NLA_U32 },
[NL80211_WOWLAN_TCP_DST_MAC] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
[NL80211_WOWLAN_TCP_SRC_PORT] = { .type = NLA_U16 },
[NL80211_WOWLAN_TCP_DST_PORT] = { .type = NLA_U16 },
[NL80211_WOWLAN_TCP_DATA_PAYLOAD] = NLA_POLICY_MIN_LEN(1),
[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ] = {
.len = sizeof(struct nl80211_wowlan_tcp_data_seq)
},
[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN] = {
.len = sizeof(struct nl80211_wowlan_tcp_data_token)
},
[NL80211_WOWLAN_TCP_DATA_INTERVAL] = { .type = NLA_U32 },
[NL80211_WOWLAN_TCP_WAKE_PAYLOAD] = NLA_POLICY_MIN_LEN(1),
[NL80211_WOWLAN_TCP_WAKE_MASK] = NLA_POLICY_MIN_LEN(1),
};
#endif /* CONFIG_PM */
/* policy for coalesce rule attributes */
static const struct nla_policy
nl80211_coalesce_policy[NUM_NL80211_ATTR_COALESCE_RULE] = {
[NL80211_ATTR_COALESCE_RULE_DELAY] = { .type = NLA_U32 },
[NL80211_ATTR_COALESCE_RULE_CONDITION] =
NLA_POLICY_RANGE(NLA_U32,
NL80211_COALESCE_CONDITION_MATCH,
NL80211_COALESCE_CONDITION_NO_MATCH),
[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN] = { .type = NLA_NESTED },
};
/* policy for GTK rekey offload attributes */
static const struct nla_policy
nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
[NL80211_REKEY_DATA_KEK] = {
.type = NLA_BINARY,
.len = NL80211_KEK_EXT_LEN
},
[NL80211_REKEY_DATA_KCK] = {
.type = NLA_BINARY,
.len = NL80211_KCK_EXT_LEN_32
},
[NL80211_REKEY_DATA_REPLAY_CTR] = NLA_POLICY_EXACT_LEN(NL80211_REPLAY_CTR_LEN),
[NL80211_REKEY_DATA_AKM] = { .type = NLA_U32 },
};
static const struct nla_policy
nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = {
[NL80211_SCHED_SCAN_MATCH_ATTR_SSID] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_SSID_LEN },
[NL80211_SCHED_SCAN_MATCH_ATTR_BSSID] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 },
};
static const struct nla_policy
nl80211_plan_policy[NL80211_SCHED_SCAN_PLAN_MAX + 1] = {
[NL80211_SCHED_SCAN_PLAN_INTERVAL] = { .type = NLA_U32 },
[NL80211_SCHED_SCAN_PLAN_ITERATIONS] = { .type = NLA_U32 },
};
static const struct nla_policy
nl80211_bss_select_policy[NL80211_BSS_SELECT_ATTR_MAX + 1] = {
[NL80211_BSS_SELECT_ATTR_RSSI] = { .type = NLA_FLAG },
[NL80211_BSS_SELECT_ATTR_BAND_PREF] = { .type = NLA_U32 },
[NL80211_BSS_SELECT_ATTR_RSSI_ADJUST] = {
.len = sizeof(struct nl80211_bss_select_rssi_adjust)
},
};
/* policy for NAN function attributes */
static const struct nla_policy
nl80211_nan_func_policy[NL80211_NAN_FUNC_ATTR_MAX + 1] = {
[NL80211_NAN_FUNC_TYPE] =
NLA_POLICY_MAX(NLA_U8, NL80211_NAN_FUNC_MAX_TYPE),
[NL80211_NAN_FUNC_SERVICE_ID] = {
.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] = NLA_POLICY_EXACT_LEN_WARN(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 },
};
/* policy for packet pattern attributes */
static const struct nla_policy
nl80211_packet_pattern_policy[MAX_NL80211_PKTPAT + 1] = {
[NL80211_PKTPAT_MASK] = { .type = NLA_BINARY, },
[NL80211_PKTPAT_PATTERN] = { .type = NLA_BINARY, },
[NL80211_PKTPAT_OFFSET] = { .type = NLA_U32 },
};
static int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
struct cfg80211_registered_device **rdev,
struct wireless_dev **wdev,
struct nlattr **attrbuf)
{
int err;
if (!cb->args[0]) {
struct nlattr **attrbuf_free = NULL;
if (!attrbuf) {
attrbuf = kcalloc(NUM_NL80211_ATTR, sizeof(*attrbuf),
GFP_KERNEL);
if (!attrbuf)
return -ENOMEM;
attrbuf_free = attrbuf;
}
err = nlmsg_parse_deprecated(cb->nlh,
GENL_HDRLEN + nl80211_fam.hdrsize,
attrbuf, nl80211_fam.maxattr,
nl80211_policy, NULL);
if (err) {
kfree(attrbuf_free);
return err;
}
rtnl_lock();
*wdev = __cfg80211_wdev_from_attrs(NULL, sock_net(cb->skb->sk),
attrbuf);
kfree(attrbuf_free);
if (IS_ERR(*wdev)) {
rtnl_unlock();
return PTR_ERR(*wdev);
}
*rdev = wiphy_to_rdev((*wdev)->wiphy);
mutex_lock(&(*rdev)->wiphy.mtx);
rtnl_unlock();
/* 0 is the first index - add 1 to parse only once */
cb->args[0] = (*rdev)->wiphy_idx + 1;
cb->args[1] = (*wdev)->identifier;
} else {
/* subtract the 1 again here */
struct wiphy *wiphy;
struct wireless_dev *tmp;
rtnl_lock();
wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1);
if (!wiphy) {
rtnl_unlock();
return -ENODEV;
}
*rdev = wiphy_to_rdev(wiphy);
*wdev = NULL;
list_for_each_entry(tmp, &(*rdev)->wiphy.wdev_list, list) {
if (tmp->identifier == cb->args[1]) {
*wdev = tmp;
break;
}
}
if (!*wdev) {
rtnl_unlock();
return -ENODEV;
}
mutex_lock(&(*rdev)->wiphy.mtx);
rtnl_unlock();
}
return 0;
}
/* message building helper */
void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
int flags, u8 cmd)
{
/* since there is no private header just add the generic one */
return genlmsg_put(skb, portid, seq, &nl80211_fam, flags, cmd);
}
static int nl80211_msg_put_wmm_rules(struct sk_buff *msg,
const struct ieee80211_reg_rule *rule)
{
int j;
struct nlattr *nl_wmm_rules =
nla_nest_start_noflag(msg, NL80211_FREQUENCY_ATTR_WMM);
if (!nl_wmm_rules)
goto nla_put_failure;
for (j = 0; j < IEEE80211_NUM_ACS; j++) {
struct nlattr *nl_wmm_rule = nla_nest_start_noflag(msg, j);
if (!nl_wmm_rule)
goto nla_put_failure;
if (nla_put_u16(msg, NL80211_WMMR_CW_MIN,
rule->wmm_rule.client[j].cw_min) ||
nla_put_u16(msg, NL80211_WMMR_CW_MAX,
rule->wmm_rule.client[j].cw_max) ||
nla_put_u8(msg, NL80211_WMMR_AIFSN,
rule->wmm_rule.client[j].aifsn) ||
nla_put_u16(msg, NL80211_WMMR_TXOP,
rule->wmm_rule.client[j].cot))
goto nla_put_failure;
nla_nest_end(msg, nl_wmm_rule);
}
nla_nest_end(msg, nl_wmm_rules);
return 0;
nla_put_failure:
return -ENOBUFS;
}
static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy,
struct ieee80211_channel *chan,
bool large)
{
/* Some channels must be completely excluded from the
* list to protect old user-space tools from breaking
*/
if (!large && chan->flags &
(IEEE80211_CHAN_NO_10MHZ | IEEE80211_CHAN_NO_20MHZ))
return 0;
if (!large && chan->freq_offset)
return 0;
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_FREQ,
chan->center_freq))
goto nla_put_failure;
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_OFFSET, chan->freq_offset))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_PSD) &&
nla_put_s8(msg, NL80211_FREQUENCY_ATTR_PSD, chan->psd))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_DISABLED) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DISABLED))
goto nla_put_failure;
if (chan->flags & IEEE80211_CHAN_NO_IR) {
if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IR))
goto nla_put_failure;
if (nla_put_flag(msg, __NL80211_FREQUENCY_ATTR_NO_IBSS))
goto nla_put_failure;
}
if (chan->flags & IEEE80211_CHAN_RADAR) {
if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
goto nla_put_failure;
if (large) {
u32 time;
time = elapsed_jiffies_msecs(chan->dfs_state_entered);
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_STATE,
chan->dfs_state))
goto nla_put_failure;
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME,
time))
goto nla_put_failure;
if (nla_put_u32(msg,
NL80211_FREQUENCY_ATTR_DFS_CAC_TIME,
chan->dfs_cac_ms))
goto nla_put_failure;
}
}
if (large) {
if ((chan->flags & IEEE80211_CHAN_NO_HT40MINUS) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_MINUS))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_NO_HT40PLUS) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_PLUS))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_NO_80MHZ) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_80MHZ))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_160MHZ))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_INDOOR_ONLY) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_INDOOR_ONLY))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_IR_CONCURRENT) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_IR_CONCURRENT))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_NO_20MHZ) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_20MHZ))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_NO_10MHZ) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_10MHZ))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_NO_HE) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HE))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_1MHZ) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_1MHZ))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_2MHZ) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_2MHZ))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_4MHZ) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_4MHZ))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_8MHZ) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_8MHZ))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_16MHZ) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_16MHZ))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_NO_320MHZ) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_320MHZ))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_NO_EHT) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_EHT))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_DFS_CONCURRENT) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DFS_CONCURRENT))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_NO_6GHZ_VLP_CLIENT) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_NO_6GHZ_AFC_CLIENT) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_CAN_MONITOR) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_CAN_MONITOR))
goto nla_put_failure;
if ((chan->flags & IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_ALLOW_6GHZ_VLP_AP))
goto nla_put_failure;
}
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
DBM_TO_MBM(chan->max_power)))
goto nla_put_failure;
if (large) {
const struct ieee80211_reg_rule *rule =
freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq));
if (!IS_ERR_OR_NULL(rule) && rule->has_wmm) {
if (nl80211_msg_put_wmm_rules(msg, rule))
goto nla_put_failure;
}
}
return 0;
nla_put_failure:
return -ENOBUFS;
}
static bool nl80211_put_txq_stats(struct sk_buff *msg,
struct cfg80211_txq_stats *txqstats,
int attrtype)
{
struct nlattr *txqattr;
#define PUT_TXQVAL_U32(attr, memb) do { \
if (txqstats->filled & BIT(NL80211_TXQ_STATS_ ## attr) && \
nla_put_u32(msg, NL80211_TXQ_STATS_ ## attr, txqstats->memb)) \
return false; \
} while (0)
txqattr = nla_nest_start_noflag(msg, attrtype);
if (!txqattr)
return false;
PUT_TXQVAL_U32(BACKLOG_BYTES, backlog_bytes);
PUT_TXQVAL_U32(BACKLOG_PACKETS, backlog_packets);
PUT_TXQVAL_U32(FLOWS, flows);
PUT_TXQVAL_U32(DROPS, drops);
PUT_TXQVAL_U32(ECN_MARKS, ecn_marks);
PUT_TXQVAL_U32(OVERLIMIT, overlimit);
PUT_TXQVAL_U32(OVERMEMORY, overmemory);
PUT_TXQVAL_U32(COLLISIONS, collisions);
PUT_TXQVAL_U32(TX_BYTES, tx_bytes);
PUT_TXQVAL_U32(TX_PACKETS, tx_packets);
PUT_TXQVAL_U32(MAX_FLOWS, max_flows);
nla_nest_end(msg, txqattr);
#undef PUT_TXQVAL_U32
return true;
}
/* netlink command implementations */
/**
* nl80211_link_id - return link ID
* @attrs: attributes to look at
*
* Returns: the link ID or 0 if not given
*
* Note this function doesn't do any validation of the link
* ID validity wrt. links that were actually added, so it must
* be called only from ops with %NL80211_FLAG_MLO_VALID_LINK_ID
* or if additional validation is done.
*/
static unsigned int nl80211_link_id(struct nlattr **attrs)
{
struct nlattr *linkid = attrs[NL80211_ATTR_MLO_LINK_ID];
if (!linkid)
return 0;
return nla_get_u8(linkid);
}
static int nl80211_link_id_or_invalid(struct nlattr **attrs)
{
struct nlattr *linkid = attrs[NL80211_ATTR_MLO_LINK_ID];
if (!linkid)
return -1;
return nla_get_u8(linkid);
}
struct key_parse {
struct key_params p;
int idx;
int type;
bool def, defmgmt, defbeacon;
bool def_uni, def_multi;
};
static int nl80211_parse_key_new(struct genl_info *info, struct nlattr *key,
struct key_parse *k)
{
struct nlattr *tb[NL80211_KEY_MAX + 1];
int err = nla_parse_nested_deprecated(tb, NL80211_KEY_MAX, key,
nl80211_key_policy,
info->extack);
if (err)
return err;
k->def = !!tb[NL80211_KEY_DEFAULT];
k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
k->defbeacon = !!tb[NL80211_KEY_DEFAULT_BEACON];
if (k->def) {
k->def_uni = true;
k->def_multi = true;
}
if (k->defmgmt || k->defbeacon)
k->def_multi = true;
if (tb[NL80211_KEY_IDX])
k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
if (tb[NL80211_KEY_DATA]) {
k->p.key = nla_data(tb[NL80211_KEY_DATA]);
k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
}
if (tb[NL80211_KEY_SEQ]) {
k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
}
if (tb[NL80211_KEY_CIPHER])
k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
if (tb[NL80211_KEY_TYPE])
k->type = nla_get_u32(tb[NL80211_KEY_TYPE]);
if (tb[NL80211_KEY_DEFAULT_TYPES]) {
struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
err = nla_parse_nested_deprecated(kdt,
NUM_NL80211_KEY_DEFAULT_TYPES - 1,
tb[NL80211_KEY_DEFAULT_TYPES],
nl80211_key_default_policy,
info->extack);
if (err)
return err;
k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
}
if (tb[NL80211_KEY_MODE])
k->p.mode = nla_get_u8(tb[NL80211_KEY_MODE]);
return 0;
}
static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
{
if (info->attrs[NL80211_ATTR_KEY_DATA]) {
k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
}
if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
}
if (info->attrs[NL80211_ATTR_KEY_IDX])
k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
if (info->attrs[NL80211_ATTR_KEY_CIPHER])
k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
if (k->def) {
k->def_uni = true;
k->def_multi = true;
}
if (k->defmgmt)
k->def_multi = true;
if (info->attrs[NL80211_ATTR_KEY_TYPE])
k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) {
struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
int err = nla_parse_nested_deprecated(kdt,
NUM_NL80211_KEY_DEFAULT_TYPES - 1,
info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES],
nl80211_key_default_policy,
info->extack);
if (err)
return err;
k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
}
return 0;
}
static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
{
int err;
memset(k, 0, sizeof(*k));
k->idx = -1;
k->type = -1;
if (info->attrs[NL80211_ATTR_KEY])
err = nl80211_parse_key_new(info, info->attrs[NL80211_ATTR_KEY], k);
else
err = nl80211_parse_key_old(info, k);
if (err)
return err;
if ((k->def ? 1 : 0) + (k->defmgmt ? 1 : 0) +
(k->defbeacon ? 1 : 0) > 1) {
GENL_SET_ERR_MSG(info,
"key with multiple default flags is invalid");
return -EINVAL;
}
if (k->defmgmt || k->defbeacon) {
if (k->def_uni || !k->def_multi) {
GENL_SET_ERR_MSG(info,
"defmgmt/defbeacon key must be mcast");
return -EINVAL;
}
}
if (k->idx != -1) {
if (k->defmgmt) {
if (k->idx < 4 || k->idx > 5) {
GENL_SET_ERR_MSG(info,
"defmgmt key idx not 4 or 5");
return -EINVAL;
}
} else if (k->defbeacon) {
if (k->idx < 6 || k->idx > 7) {
GENL_SET_ERR_MSG(info,
"defbeacon key idx not 6 or 7");
return -EINVAL;
}
} else if (k->def) {
if (k->idx < 0 || k->idx > 3) {
GENL_SET_ERR_MSG(info, "def key idx not 0-3");
return -EINVAL;
}
} else {
if (k->idx < 0 || k->idx > 7) {
GENL_SET_ERR_MSG(info, "key idx not 0-7");
return -EINVAL;
}
}
}
return 0;
}
static struct cfg80211_cached_keys *
nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
struct genl_info *info, bool *no_ht)
{
struct nlattr *keys = info->attrs[NL80211_ATTR_KEYS];
struct key_parse parse;
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;
nla_for_each_nested(key, keys, rem) {
memset(&parse, 0, sizeof(parse));
parse.idx = -1;
err = nl80211_parse_key_new(info, key, &parse);
if (err)
goto error;
err = -EINVAL;
if (!parse.p.key)
goto error;
if (parse.idx < 0 || parse.idx > 3) {
GENL_SET_ERR_MSG(info, "key index out of range [0-3]");
goto error;
}
if (parse.def) {
if (def) {
GENL_SET_ERR_MSG(info,
"only one key can be default");
goto error;
}
def = 1;
result->def = parse.idx;
if (!parse.def_uni || !parse.def_multi)
goto error;
} else if (parse.defmgmt)
goto error;
err = cfg80211_validate_key_settings(rdev, &parse.p,
parse.idx, false, NULL);
if (err)
goto error;
if (parse.p.cipher != WLAN_CIPHER_SUITE_WEP40 &&
parse.p.cipher != WLAN_CIPHER_SUITE_WEP104) {
GENL_SET_ERR_MSG(info, "connect key must be WEP");
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);
/* must be WEP key if we got here */
if (no_ht)
*no_ht = true;
}
if (result->def < 0) {
err = -EINVAL;
GENL_SET_ERR_MSG(info, "need a default/TX key");
goto error;
}
return result;
error:
kfree(result);
return ERR_PTR(err);
}
static int nl80211_key_allowed(struct wireless_dev *wdev)
{
lockdep_assert_wiphy(wdev->wiphy);
switch (wdev->iftype) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_P2P_GO:
case NL80211_IFTYPE_MESH_POINT:
break;
case NL80211_IFTYPE_ADHOC:
if (wdev->u.ibss.current_bss)
return 0;
return -ENOLINK;
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
if (wdev->connected)
return 0;
return -ENOLINK;
case NL80211_IFTYPE_NAN:
if (wiphy_ext_feature_isset(wdev->wiphy,
NL80211_EXT_FEATURE_SECURE_NAN))
return 0;
return -EINVAL;
case NL80211_IFTYPE_UNSPECIFIED:
case NL80211_IFTYPE_OCB:
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_P2P_DEVICE:
case NL80211_IFTYPE_WDS:
case NUM_NL80211_IFTYPES:
return -EINVAL;
}
return 0;
}
static struct ieee80211_channel *nl80211_get_valid_chan(struct wiphy *wiphy,
u32 freq)
{
struct ieee80211_channel *chan;
chan = ieee80211_get_channel_khz(wiphy, freq);
if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
return NULL;
return chan;
}
static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes)
{
struct nlattr *nl_modes = nla_nest_start_noflag(msg, attr);
int i;
if (!nl_modes)
goto nla_put_failure;
i = 0;
while (ifmodes) {
if ((ifmodes & 1) && nla_put_flag(msg, i))
goto nla_put_failure;
ifmodes >>= 1;
i++;
}
nla_nest_end(msg, nl_modes);
return 0;
nla_put_failure:
return -ENOBUFS;
}
static int nl80211_put_ifcomb_data(struct sk_buff *msg, bool large, int idx,
const struct ieee80211_iface_combination *c,
u16 nested)
{
struct nlattr *nl_combi, *nl_limits;
int i;
nl_combi = nla_nest_start_noflag(msg, idx | nested);
if (!nl_combi)
goto nla_put_failure;
nl_limits = nla_nest_start_noflag(msg, NL80211_IFACE_COMB_LIMITS |
nested);
if (!nl_limits)
goto nla_put_failure;
for (i = 0; i < c->n_limits; i++) {
struct nlattr *nl_limit;
nl_limit = nla_nest_start_noflag(msg, i + 1);
if (!nl_limit)
goto nla_put_failure;
if (nla_put_u32(msg, NL80211_IFACE_LIMIT_MAX, c->limits[i].max))
goto nla_put_failure;
if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES,
c->limits[i].types))
goto nla_put_failure;
nla_nest_end(msg, nl_limit);
}
nla_nest_end(msg, nl_limits);
if (c->beacon_int_infra_match &&
nla_put_flag(msg, NL80211_IFACE_COMB_STA_AP_BI_MATCH))
goto nla_put_failure;
if (nla_put_u32(msg, NL80211_IFACE_COMB_NUM_CHANNELS,
c->num_different_channels) ||
nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM,
c->max_interfaces))
goto nla_put_failure;
if (large &&
(nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
c->radar_detect_widths) ||
nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_REGIONS,
c->radar_detect_regions)))
goto nla_put_failure;
if (c->beacon_int_min_gcd &&
nla_put_u32(msg, NL80211_IFACE_COMB_BI_MIN_GCD,
c->beacon_int_min_gcd))
goto nla_put_failure;
nla_nest_end(msg, nl_combi);
return 0;
nla_put_failure:
return -ENOBUFS;
}
static int nl80211_put_iface_combinations(struct wiphy *wiphy,
struct sk_buff *msg,
int attr, int radio,
bool large, u16 nested)
{
const struct ieee80211_iface_combination *c;
struct nlattr *nl_combis;
int i, n;
nl_combis = nla_nest_start_noflag(msg, attr | nested);
if (!nl_combis)
goto nla_put_failure;
if (radio >= 0) {
c = wiphy->radio[0].iface_combinations;
n = wiphy->radio[0].n_iface_combinations;
} else {
c = wiphy->iface_combinations;
n = wiphy->n_iface_combinations;
}
for (i = 0; i < n; i++)
if (nl80211_put_ifcomb_data(msg, large, i + 1, &c[i], nested))
goto nla_put_failure;
nla_nest_end(msg, nl_combis);
return 0;
nla_put_failure:
return -ENOBUFS;
}
#ifdef CONFIG_PM
static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev,
struct sk_buff *msg)
{
const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan->tcp;
struct nlattr *nl_tcp;
if (!tcp)
return 0;
nl_tcp = nla_nest_start_noflag(msg,
NL80211_WOWLAN_TRIG_TCP_CONNECTION);
if (!nl_tcp)
return -ENOBUFS;
if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
tcp->data_payload_max))
return -ENOBUFS;
if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
tcp->data_payload_max))
return -ENOBUFS;
if (tcp->seq && nla_put_flag(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ))
return -ENOBUFS;
if (tcp->tok && nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
sizeof(*tcp->tok), tcp->tok))
return -ENOBUFS;
if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL,
tcp->data_interval_max))
return -ENOBUFS;
if (nla_put_u32(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
tcp->wake_payload_max))
return -ENOBUFS;
nla_nest_end(msg, nl_tcp);
return 0;
}
static int nl80211_send_wowlan(struct sk_buff *msg,
struct cfg80211_registered_device *rdev,
bool large)
{
struct nlattr *nl_wowlan;
if (!rdev->wiphy.wowlan)
return 0;
nl_wowlan = nla_nest_start_noflag(msg,
NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
if (!nl_wowlan)
return -ENOBUFS;
if (((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_ANY) &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_DISCONNECT) &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT) &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
return -ENOBUFS;
if (rdev->wiphy.wowlan->n_patterns) {
struct nl80211_pattern_support pat = {
.max_patterns = rdev->wiphy.wowlan->n_patterns,
.min_pattern_len = rdev->wiphy.wowlan->pattern_min_len,
.max_pattern_len = rdev->wiphy.wowlan->pattern_max_len,
.max_pkt_offset = rdev->wiphy.wowlan->max_pkt_offset,
};
if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
sizeof(pat), &pat))
return -ENOBUFS;
}
if ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_NET_DETECT) &&
nla_put_u32(msg, NL80211_WOWLAN_TRIG_NET_DETECT,
rdev->wiphy.wowlan->max_nd_match_sets))
return -ENOBUFS;
if (large && nl80211_send_wowlan_tcp_caps(rdev, msg))
return -ENOBUFS;
nla_nest_end(msg, nl_wowlan);
return 0;
}
#endif
static int nl80211_send_coalesce(struct sk_buff *msg,
struct cfg80211_registered_device *rdev)
{
struct nl80211_coalesce_rule_support rule;
if (!rdev->wiphy.coalesce)
return 0;
rule.max_rules = rdev->wiphy.coalesce->n_rules;
rule.max_delay = rdev->wiphy.coalesce->max_delay;
rule.pat.max_patterns = rdev->wiphy.coalesce->n_patterns;
rule.pat.min_pattern_len = rdev->wiphy.coalesce->pattern_min_len;
rule.pat.max_pattern_len = rdev->wiphy.coalesce->pattern_max_len;
rule.pat.max_pkt_offset = rdev->wiphy.coalesce->max_pkt_offset;
if (nla_put(msg, NL80211_ATTR_COALESCE_RULE, sizeof(rule), &rule))
return -ENOBUFS;
return 0;
}
static int
nl80211_send_iftype_data(struct sk_buff *msg,
const struct ieee80211_supported_band *sband,
const struct ieee80211_sband_iftype_data *iftdata)
{
const struct ieee80211_sta_he_cap *he_cap = &iftdata->he_cap;
const struct ieee80211_sta_eht_cap *eht_cap = &iftdata->eht_cap;
if (nl80211_put_iftypes(msg, NL80211_BAND_IFTYPE_ATTR_IFTYPES,
iftdata->types_mask))
return -ENOBUFS;
if (he_cap->has_he) {
if (nla_put(msg, NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC,
sizeof(he_cap->he_cap_elem.mac_cap_info),
he_cap->he_cap_elem.mac_cap_info) ||
nla_put(msg, NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY,
sizeof(he_cap->he_cap_elem.phy_cap_info),
he_cap->he_cap_elem.phy_cap_info) ||
nla_put(msg, NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET,
sizeof(he_cap->he_mcs_nss_supp),
&he_cap->he_mcs_nss_supp) ||
nla_put(msg, NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE,
sizeof(he_cap->ppe_thres), he_cap->ppe_thres))
return -ENOBUFS;
}
if (eht_cap->has_eht && he_cap->has_he) {
u8 mcs_nss_size, ppe_thresh_size;
u16 ppe_thres_hdr;
bool is_ap;
is_ap = iftdata->types_mask & BIT(NL80211_IFTYPE_AP) ||
iftdata->types_mask & BIT(NL80211_IFTYPE_P2P_GO);
mcs_nss_size =
ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem,
&eht_cap->eht_cap_elem,
is_ap);
ppe_thres_hdr = get_unaligned_le16(&eht_cap->eht_ppe_thres[0]);
ppe_thresh_size =
ieee80211_eht_ppe_size(ppe_thres_hdr,
eht_cap->eht_cap_elem.phy_cap_info);
if (nla_put(msg, NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC,
sizeof(eht_cap->eht_cap_elem.mac_cap_info),
eht_cap->eht_cap_elem.mac_cap_info) ||
nla_put(msg, NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY,
sizeof(eht_cap->eht_cap_elem.phy_cap_info),
eht_cap->eht_cap_elem.phy_cap_info) ||
nla_put(msg, NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET,
mcs_nss_size, &eht_cap->eht_mcs_nss_supp) ||
nla_put(msg, NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE,
ppe_thresh_size, eht_cap->eht_ppe_thres))
return -ENOBUFS;
}
if (sband->band == NL80211_BAND_6GHZ &&
nla_put(msg, NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA,
sizeof(iftdata->he_6ghz_capa),
&iftdata->he_6ghz_capa))
return -ENOBUFS;
if (iftdata->vendor_elems.data && iftdata->vendor_elems.len &&
nla_put(msg, NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS,
iftdata->vendor_elems.len, iftdata->vendor_elems.data))
return -ENOBUFS;
return 0;
}
static int nl80211_send_band_rateinfo(struct sk_buff *msg,
struct ieee80211_supported_band *sband,
bool large)
{
struct nlattr *nl_rates, *nl_rate;
struct ieee80211_rate *rate;
int i;
/* add HT info */
if (sband->ht_cap.ht_supported &&
(nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET,
sizeof(sband->ht_cap.mcs),
&sband->ht_cap.mcs) ||
nla_put_u16(msg, NL80211_BAND_ATTR_HT_CAPA,
sband->ht_cap.cap) ||
nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
sband->ht_cap.ampdu_factor) ||
nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
sband->ht_cap.ampdu_density)))
return -ENOBUFS;
/* add VHT info */
if (sband->vht_cap.vht_supported &&
(nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET,
sizeof(sband->vht_cap.vht_mcs),
&sband->vht_cap.vht_mcs) ||
nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA,
sband->vht_cap.cap)))
return -ENOBUFS;
if (large && sband->n_iftype_data) {
struct nlattr *nl_iftype_data =
nla_nest_start_noflag(msg,
NL80211_BAND_ATTR_IFTYPE_DATA);
const struct ieee80211_sband_iftype_data *iftd;
int err;
if (!nl_iftype_data)
return -ENOBUFS;
for_each_sband_iftype_data(sband, i, iftd) {
struct nlattr *iftdata;
iftdata = nla_nest_start_noflag(msg, i + 1);
if (!iftdata)
return -ENOBUFS;
err = nl80211_send_iftype_data(msg, sband, iftd);
if (err)
return err;
nla_nest_end(msg, iftdata);
}
nla_nest_end(msg, nl_iftype_data);
}
/* add EDMG info */
if (large && sband->edmg_cap.channels &&
(nla_put_u8(msg, NL80211_BAND_ATTR_EDMG_CHANNELS,
sband->edmg_cap.channels) ||
nla_put_u8(msg, NL80211_BAND_ATTR_EDMG_BW_CONFIG,
sband->edmg_cap.bw_config)))
return -ENOBUFS;
/* add bitrates */
nl_rates = nla_nest_start_noflag(msg, NL80211_BAND_ATTR_RATES);
if (!nl_rates)
return -ENOBUFS;
for (i = 0; i < sband->n_bitrates; i++) {
nl_rate = nla_nest_start_noflag(msg, i);
if (!nl_rate)
return -ENOBUFS;
rate = &sband->bitrates[i];
if (nla_put_u32(msg, NL80211_BITRATE_ATTR_RATE,
rate->bitrate))
return -ENOBUFS;
if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
nla_put_flag(msg,
NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE))
return -ENOBUFS;
nla_nest_end(msg, nl_rate);
}
nla_nest_end(msg, nl_rates);
/* S1G capabilities */
if (sband->band == NL80211_BAND_S1GHZ && sband->s1g_cap.s1g &&
(nla_put(msg, NL80211_BAND_ATTR_S1G_CAPA,
sizeof(sband->s1g_cap.cap),
sband->s1g_cap.cap) ||
nla_put(msg, NL80211_BAND_ATTR_S1G_MCS_NSS_SET,
sizeof(sband->s1g_cap.nss_mcs),
sband->s1g_cap.nss_mcs)))
return -ENOBUFS;
return 0;
}
static int
nl80211_send_mgmt_stypes(struct sk_buff *msg,
const struct ieee80211_txrx_stypes *mgmt_stypes)
{
u16 stypes;
struct nlattr *nl_ftypes, *nl_ifs;
enum nl80211_iftype ift;
int i;
if (!mgmt_stypes)
return 0;
nl_ifs = nla_nest_start_noflag(msg, NL80211_ATTR_TX_FRAME_TYPES);
if (!nl_ifs)
return -ENOBUFS;
for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
nl_ftypes = nla_nest_start_noflag(msg, ift);
if (!nl_ftypes)
return -ENOBUFS;
i = 0;
stypes = mgmt_stypes[ift].tx;
while (stypes) {
if ((stypes & 1) &&
nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
(i << 4) | IEEE80211_FTYPE_MGMT))
return -ENOBUFS;
stypes >>= 1;
i++;
}
nla_nest_end(msg, nl_ftypes);
}
nla_nest_end(msg, nl_ifs);
nl_ifs = nla_nest_start_noflag(msg, NL80211_ATTR_RX_FRAME_TYPES);
if (!nl_ifs)
return -ENOBUFS;
for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
nl_ftypes = nla_nest_start_noflag(msg, ift);
if (!nl_ftypes)
return -ENOBUFS;
i = 0;
stypes = mgmt_stypes[ift].rx;
while (stypes) {
if ((stypes & 1) &&
nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
(i << 4) | IEEE80211_FTYPE_MGMT))
return -ENOBUFS;
stypes >>= 1;
i++;
}
nla_nest_end(msg, nl_ftypes);
}
nla_nest_end(msg, nl_ifs);
return 0;
}
#define CMD(op, n) \
do { \
if (rdev->ops->op) { \
i++; \
if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \
goto nla_put_failure; \
} \
} while (0)
static int nl80211_add_commands_unsplit(struct cfg80211_registered_device *rdev,
struct sk_buff *msg)
{
int i = 0;
/*
* do *NOT* add anything into this function, new things need to be
* advertised only to new versions of userspace that can deal with
* the split (and they can't possibly care about new features...
*/
CMD(add_virtual_intf, NEW_INTERFACE);
CMD(change_virtual_intf, SET_INTERFACE);
CMD(add_key, NEW_KEY);
CMD(start_ap, START_AP);
CMD(add_station, NEW_STATION);
CMD(add_mpath, NEW_MPATH);
CMD(update_mesh_config, SET_MESH_CONFIG);
CMD(change_bss, SET_BSS);
CMD(auth, AUTHENTICATE);
CMD(assoc, ASSOCIATE);
CMD(deauth, DEAUTHENTICATE);
CMD(disassoc, DISASSOCIATE);
CMD(join_ibss, JOIN_IBSS);
CMD(join_mesh, JOIN_MESH);
CMD(set_pmksa, SET_PMKSA);
CMD(del_pmksa, DEL_PMKSA);
CMD(flush_pmksa, FLUSH_PMKSA);
if (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
CMD(remain_on_channel, REMAIN_ON_CHANNEL);
CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
CMD(mgmt_tx, FRAME);
CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
if (rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
i++;
if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))
goto nla_put_failure;
}
if (rdev->ops->set_monitor_channel || rdev->ops->start_ap ||
rdev->ops->join_mesh) {
i++;
if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
goto nla_put_failure;
}
if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
CMD(tdls_mgmt, TDLS_MGMT);
CMD(tdls_oper, TDLS_OPER);
}
if (rdev->wiphy.max_sched_scan_reqs)
CMD(sched_scan_start, START_SCHED_SCAN);
CMD(probe_client, PROBE_CLIENT);
CMD(set_noack_map, SET_NOACK_MAP);
if (rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
i++;
if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS))
goto nla_put_failure;
}
CMD(start_p2p_device, START_P2P_DEVICE);
CMD(set_mcast_rate, SET_MCAST_RATE);
#ifdef CONFIG_NL80211_TESTMODE
CMD(testmode_cmd, TESTMODE);
#endif
if (rdev->ops->connect || rdev->ops->auth) {
i++;
if (nla_put_u32(msg, i, NL80211_CMD_CONNECT))
goto nla_put_failure;
}
if (rdev->ops->disconnect || rdev->ops->deauth) {
i++;
if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT))
goto nla_put_failure;
}
return i;
nla_put_failure:
return -ENOBUFS;
}
static int
nl80211_send_pmsr_ftm_capa(const struct cfg80211_pmsr_capabilities *cap,
struct sk_buff *msg)
{
struct nlattr *ftm;
if (!cap->ftm.supported)
return 0;
ftm = nla_nest_start_noflag(msg, NL80211_PMSR_TYPE_FTM);
if (!ftm)
return -ENOBUFS;
if (cap->ftm.asap && nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_ASAP))
return -ENOBUFS;
if (cap->ftm.non_asap &&
nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP))
return -ENOBUFS;
if (cap->ftm.request_lci &&
nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI))
return -ENOBUFS;
if (cap->ftm.request_civicloc &&
nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC))
return -ENOBUFS;
if (nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES,
cap->ftm.preambles))
return -ENOBUFS;
if (nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS,
cap->ftm.bandwidths))
return -ENOBUFS;
if (cap->ftm.max_bursts_exponent >= 0 &&
nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT,
cap->ftm.max_bursts_exponent))
return -ENOBUFS;
if (cap->ftm.max_ftms_per_burst &&
nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST,
cap->ftm.max_ftms_per_burst))
return -ENOBUFS;
if (cap->ftm.trigger_based &&
nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED))
return -ENOBUFS;
if (cap->ftm.non_trigger_based &&
nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED))
return -ENOBUFS;
nla_nest_end(msg, ftm);
return 0;
}
static int nl80211_send_pmsr_capa(struct cfg80211_registered_device *rdev,
struct sk_buff *msg)
{
const struct cfg80211_pmsr_capabilities *cap = rdev->wiphy.pmsr_capa;
struct nlattr *pmsr, *caps;
if (!cap)
return 0;
/*
* we don't need to clean up anything here since the caller
* will genlmsg_cancel() if we fail
*/
pmsr = nla_nest_start_noflag(msg, NL80211_ATTR_PEER_MEASUREMENTS);
if (!pmsr)
return -ENOBUFS;
if (nla_put_u32(msg, NL80211_PMSR_ATTR_MAX_PEERS, cap->max_peers))
return -ENOBUFS;
if (cap->report_ap_tsf &&
nla_put_flag(msg, NL80211_PMSR_ATTR_REPORT_AP_TSF))
return -ENOBUFS;
if (cap->randomize_mac_addr &&
nla_put_flag(msg, NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR))
return -ENOBUFS;
caps = nla_nest_start_noflag(msg, NL80211_PMSR_ATTR_TYPE_CAPA);
if (!caps)
return -ENOBUFS;
if (nl80211_send_pmsr_ftm_capa(cap, msg))
return -ENOBUFS;
nla_nest_end(msg, caps);
nla_nest_end(msg, pmsr);
return 0;
}
static int
nl80211_put_iftype_akm_suites(struct cfg80211_registered_device *rdev,
struct sk_buff *msg)
{
int i;
struct nlattr *nested, *nested_akms;
const struct wiphy_iftype_akm_suites *iftype_akms;
if (!rdev->wiphy.num_iftype_akm_suites ||
!rdev->wiphy.iftype_akm_suites)
return 0;
nested = nla_nest_start(msg, NL80211_ATTR_IFTYPE_AKM_SUITES);
if (!nested)
return -ENOBUFS;
for (i = 0; i < rdev->wiphy.num_iftype_akm_suites; i++) {
nested_akms = nla_nest_start(msg, i + 1);
if (!nested_akms)
return -ENOBUFS;
iftype_akms = &rdev->wiphy.iftype_akm_suites[i];
if (nl80211_put_iftypes(msg, NL80211_IFTYPE_AKM_ATTR_IFTYPES,
iftype_akms->iftypes_mask))
return -ENOBUFS;
if (nla_put(msg, NL80211_IFTYPE_AKM_ATTR_SUITES,
sizeof(u32) * iftype_akms->n_akm_suites,
iftype_akms->akm_suites)) {
return -ENOBUFS;
}
nla_nest_end(msg, nested_akms);
}
nla_nest_end(msg, nested);
return 0;
}
static int
nl80211_put_tid_config_support(struct cfg80211_registered_device *rdev,
struct sk_buff *msg)
{
struct nlattr *supp;
if (!rdev->wiphy.tid_config_support.vif &&
!rdev->wiphy.tid_config_support.peer)
return 0;
supp = nla_nest_start(msg, NL80211_ATTR_TID_CONFIG);
if (!supp)
return -ENOSPC;
if (rdev->wiphy.tid_config_support.vif &&
nla_put_u64_64bit(msg, NL80211_TID_CONFIG_ATTR_VIF_SUPP,
rdev->wiphy.tid_config_support.vif,
NL80211_TID_CONFIG_ATTR_PAD))
goto fail;
if (rdev->wiphy.tid_config_support.peer &&
nla_put_u64_64bit(msg, NL80211_TID_CONFIG_ATTR_PEER_SUPP,
rdev->wiphy.tid_config_support.peer,
NL80211_TID_CONFIG_ATTR_PAD))
goto fail;
/* for now we just use the same value ... makes more sense */
if (nla_put_u8(msg, NL80211_TID_CONFIG_ATTR_RETRY_SHORT,
rdev->wiphy.tid_config_support.max_retry))
goto fail;
if (nla_put_u8(msg, NL80211_TID_CONFIG_ATTR_RETRY_LONG,
rdev->wiphy.tid_config_support.max_retry))
goto fail;
nla_nest_end(msg, supp);
return 0;
fail:
nla_nest_cancel(msg, supp);
return -ENOBUFS;
}
static int
nl80211_put_sar_specs(struct cfg80211_registered_device *rdev,
struct sk_buff *msg)
{
struct nlattr *sar_capa, *specs, *sub_freq_range;
u8 num_freq_ranges;
int i;
if (!rdev->wiphy.sar_capa)
return 0;
num_freq_ranges = rdev->wiphy.sar_capa->num_freq_ranges;
sar_capa = nla_nest_start(msg, NL80211_ATTR_SAR_SPEC);
if (!sar_capa)
return -ENOSPC;
if (nla_put_u32(msg, NL80211_SAR_ATTR_TYPE, rdev->wiphy.sar_capa->type))
goto fail;
specs = nla_nest_start(msg, NL80211_SAR_ATTR_SPECS);
if (!specs)
goto fail;
/* report supported freq_ranges */
for (i = 0; i < num_freq_ranges; i++) {
sub_freq_range = nla_nest_start(msg, i + 1);
if (!sub_freq_range)
goto fail;
if (nla_put_u32(msg, NL80211_SAR_ATTR_SPECS_START_FREQ,
rdev->wiphy.sar_capa->freq_ranges[i].start_freq))
goto fail;
if (nla_put_u32(msg, NL80211_SAR_ATTR_SPECS_END_FREQ,
rdev->wiphy.sar_capa->freq_ranges[i].end_freq))
goto fail;
nla_nest_end(msg, sub_freq_range);
}
nla_nest_end(msg, specs);
nla_nest_end(msg, sar_capa);
return 0;
fail:
nla_nest_cancel(msg, sar_capa);
return -ENOBUFS;
}
static int nl80211_put_mbssid_support(struct wiphy *wiphy, struct sk_buff *msg)
{
struct nlattr *config;
if (!wiphy->mbssid_max_interfaces)
return 0;
config = nla_nest_start(msg, NL80211_ATTR_MBSSID_CONFIG);
if (!config)
return -ENOBUFS;
if (nla_put_u8(msg, NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES,
wiphy->mbssid_max_interfaces))
goto fail;
if (wiphy->ema_max_profile_periodicity &&
nla_put_u8(msg,
NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY,
wiphy->ema_max_profile_periodicity))
goto fail;
nla_nest_end(msg, config);
return 0;
fail:
nla_nest_cancel(msg, config);
return -ENOBUFS;
}
static int nl80211_put_radio(struct wiphy *wiphy, struct sk_buff *msg, int idx)
{
const struct wiphy_radio *r = &wiphy->radio[idx];
struct nlattr *radio, *freq;
int i;
radio = nla_nest_start(msg, idx);
if (!radio)
return -ENOBUFS;
if (nla_put_u32(msg, NL80211_WIPHY_RADIO_ATTR_INDEX, idx))
goto nla_put_failure;
for (i = 0; i < r->n_freq_range; i++) {
const struct wiphy_radio_freq_range *range = &r->freq_range[i];
freq = nla_nest_start(msg, NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE);
if (!freq)
goto nla_put_failure;
if (nla_put_u32(msg, NL80211_WIPHY_RADIO_FREQ_ATTR_START,
range->start_freq) ||
nla_put_u32(msg, NL80211_WIPHY_RADIO_FREQ_ATTR_END,
range->end_freq))
goto nla_put_failure;
nla_nest_end(msg, freq);
}
for (i = 0; i < r->n_iface_combinations; i++)
if (nl80211_put_ifcomb_data(msg, true,
NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION,
&r->iface_combinations[i],
NLA_F_NESTED))
goto nla_put_failure;
nla_nest_end(msg, radio);
return 0;
nla_put_failure:
return -ENOBUFS;
}
static int nl80211_put_radios(struct wiphy *wiphy, struct sk_buff *msg)
{
struct nlattr *radios;
int i;
if (!wiphy->n_radio)
return 0;
radios = nla_nest_start(msg, NL80211_ATTR_WIPHY_RADIOS);
if (!radios)
return -ENOBUFS;
for (i = 0; i < wiphy->n_radio; i++)
if (nl80211_put_radio(wiphy, msg, i))
goto fail;
nla_nest_end(msg, radios);
if (nl80211_put_iface_combinations(wiphy, msg,
NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS,
-1, true, NLA_F_NESTED))
return -ENOBUFS;
return 0;
fail:
nla_nest_cancel(msg, radios);
return -ENOBUFS;
}
struct nl80211_dump_wiphy_state {
s64 filter_wiphy;
long start;
long split_start, band_start, chan_start, capa_start;
bool split;
};
static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
enum nl80211_commands cmd,
struct sk_buff *msg, u32 portid, u32 seq,
int flags, struct nl80211_dump_wiphy_state *state)
{
void *hdr;
struct nlattr *nl_bands, *nl_band;
struct nlattr *nl_freqs, *nl_freq;
struct nlattr *nl_cmds;
enum nl80211_band band;
struct ieee80211_channel *chan;
int i;
const struct ieee80211_txrx_stypes *mgmt_stypes =
rdev->wiphy.mgmt_stypes;
u32 features;
hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
if (!hdr)
return -ENOBUFS;
if (WARN_ON(!state))
return -EINVAL;
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
nla_put_string(msg, NL80211_ATTR_WIPHY_NAME,
wiphy_name(&rdev->wiphy)) ||
nla_put_u32(msg, NL80211_ATTR_GENERATION,
cfg80211_rdev_list_generation))
goto nla_put_failure;
if (cmd != NL80211_CMD_NEW_WIPHY)
goto finish;
switch (state->split_start) {
case 0:
if (nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
rdev->wiphy.retry_short) ||
nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
rdev->wiphy.retry_long) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
rdev->wiphy.frag_threshold) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
rdev->wiphy.rts_threshold) ||
nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
rdev->wiphy.coverage_class) ||
nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
rdev->wiphy.max_scan_ssids) ||
nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
rdev->wiphy.max_sched_scan_ssids) ||
nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
rdev->wiphy.max_scan_ie_len) ||
nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
rdev->wiphy.max_sched_scan_ie_len) ||
nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
rdev->wiphy.max_match_sets))
goto nla_put_failure;
if ((rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN))
goto nla_put_failure;
if ((rdev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH))
goto nla_put_failure;
if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD))
goto nla_put_failure;
if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT))
goto nla_put_failure;
if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT))
goto nla_put_failure;
if ((rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
goto nla_put_failure;
state->split_start++;
if (state->split)
break;
fallthrough;
case 1:
if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
sizeof(u32) * rdev->wiphy.n_cipher_suites,
rdev->wiphy.cipher_suites))
goto nla_put_failure;
if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
rdev->wiphy.max_num_pmkids))
goto nla_put_failure;
if ((rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))
goto nla_put_failure;
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
rdev->wiphy.available_antennas_tx) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
rdev->wiphy.available_antennas_rx))
goto nla_put_failure;
if ((rdev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
rdev->wiphy.probe_resp_offload))
goto nla_put_failure;
if ((rdev->wiphy.available_antennas_tx ||
rdev->wiphy.available_antennas_rx) &&
rdev->ops->get_antenna) {
u32 tx_ant = 0, rx_ant = 0;
int res;
res = rdev_get_antenna(rdev, &tx_ant, &rx_ant);
if (!res) {
if (nla_put_u32(msg,
NL80211_ATTR_WIPHY_ANTENNA_TX,
tx_ant) ||
nla_put_u32(msg,
NL80211_ATTR_WIPHY_ANTENNA_RX,
rx_ant))
goto nla_put_failure;
}
}
state->split_start++;
if (state->split)
break;
fallthrough;
case 2:
if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
rdev->wiphy.interface_modes))
goto nla_put_failure;
state->split_start++;
if (state->split)
break;
fallthrough;
case 3:
nl_bands = nla_nest_start_noflag(msg,
NL80211_ATTR_WIPHY_BANDS);
if (!nl_bands)
goto nla_put_failure;
for (band = state->band_start;
band < (state->split ?
NUM_NL80211_BANDS :
NL80211_BAND_60GHZ + 1);
band++) {
struct ieee80211_supported_band *sband;
/* omit higher bands for ancient software */
if (band > NL80211_BAND_5GHZ && !state->split)
break;
sband = rdev->wiphy.bands[band];
if (!sband)
continue;
nl_band = nla_nest_start_noflag(msg, band);
if (!nl_band)
goto nla_put_failure;
switch (state->chan_start) {
case 0:
if (nl80211_send_band_rateinfo(msg, sband,
state->split))
goto nla_put_failure;
state->chan_start++;
if (state->split)
break;
fallthrough;
default:
/* add frequencies */
nl_freqs = nla_nest_start_noflag(msg,
NL80211_BAND_ATTR_FREQS);
if (!nl_freqs)
goto nla_put_failure;
for (i = state->chan_start - 1;
i < sband->n_channels;
i++) {
nl_freq = nla_nest_start_noflag(msg,
i);
if (!nl_freq)
goto nla_put_failure;
chan = &sband->channels[i];
if (nl80211_msg_put_channel(
msg, &rdev->wiphy, chan,
state->split))
goto nla_put_failure;
nla_nest_end(msg, nl_freq);
if (state->split)
break;
}
if (i < sband->n_channels)
state->chan_start = i + 2;
else
state->chan_start = 0;
nla_nest_end(msg, nl_freqs);
}
nla_nest_end(msg, nl_band);
if (state->split) {
/* start again here */
if (state->chan_start)
band--;
break;
}
}
nla_nest_end(msg, nl_bands);
if (band < NUM_NL80211_BANDS)
state->band_start = band + 1;
else
state->band_start = 0;
/* if bands & channels are done, continue outside */
if (state->band_start == 0 && state->chan_start == 0)
state->split_start++;
if (state->split)
break;
fallthrough;
case 4:
nl_cmds = nla_nest_start_noflag(msg,
NL80211_ATTR_SUPPORTED_COMMANDS);
if (!nl_cmds)
goto nla_put_failure;
i = nl80211_add_commands_unsplit(rdev, msg);
if (i < 0)
goto nla_put_failure;
if (state->split) {
CMD(crit_proto_start, CRIT_PROTOCOL_START);
CMD(crit_proto_stop, CRIT_PROTOCOL_STOP);
if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
CMD(channel_switch, CHANNEL_SWITCH);
CMD(set_qos_map, SET_QOS_MAP);
if (rdev->wiphy.features &
NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)
CMD(add_tx_ts, ADD_TX_TS);
CMD(set_multicast_to_unicast, SET_MULTICAST_TO_UNICAST);
CMD(update_connect_params, UPDATE_CONNECT_PARAMS);
CMD(update_ft_ies, UPDATE_FT_IES);
if (rdev->wiphy.sar_capa)
CMD(set_sar_specs, SET_SAR_SPECS);
}
#undef CMD
nla_nest_end(msg, nl_cmds);
state->split_start++;
if (state->split)
break;
fallthrough;
case 5:
if (rdev->ops->remain_on_channel &&
(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
nla_put_u32(msg,
NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
rdev->wiphy.max_remain_on_channel_duration))
goto nla_put_failure;
if ((rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) &&
nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK))
goto nla_put_failure;
state->split_start++;
if (state->split)
break;
fallthrough;
case 6:
#ifdef CONFIG_PM
if (nl80211_send_wowlan(msg, rdev, state->split))
goto nla_put_failure;
state->split_start++;
if (state->split)
break;
#else
state->split_start++;