aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/nl80211.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r--net/wireless/nl80211.c202
1 files changed, 155 insertions, 47 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 2705e3ee8fc4..597c52236514 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1545,7 +1545,6 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
return -ENOLINK;
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
- /* for MLO, require driver validation of the link ID */
if (wdev->connected)
return 0;
return -ENOLINK;
@@ -1821,10 +1820,15 @@ nl80211_send_iftype_data(struct sk_buff *msg,
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);
+ &eht_cap->eht_cap_elem,
+ is_ap);
ppe_thres_hdr = get_unaligned_le16(&eht_cap->eht_ppe_thres[0]);
ppe_thresh_size =
@@ -3476,8 +3480,21 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
if (result)
goto out;
- result = rdev_set_txq_params(rdev, netdev,
- &txq_params);
+ txq_params.link_id =
+ nl80211_link_id_or_invalid(info->attrs);
+
+ wdev_lock(netdev->ieee80211_ptr);
+ if (txq_params.link_id >= 0 &&
+ !(netdev->ieee80211_ptr->valid_links &
+ BIT(txq_params.link_id)))
+ result = -ENOLINK;
+ else if (txq_params.link_id >= 0 &&
+ !netdev->ieee80211_ptr->valid_links)
+ result = -EINVAL;
+ else
+ result = rdev_set_txq_params(rdev, netdev,
+ &txq_params);
+ wdev_unlock(netdev->ieee80211_ptr);
if (result)
goto out;
}
@@ -3848,12 +3865,19 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
for_each_valid_link(wdev, link_id) {
struct nlattr *link = nla_nest_start(msg, link_id + 1);
+ struct cfg80211_chan_def chandef = {};
+ int ret;
if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
goto nla_put_failure;
if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
wdev->links[link_id].addr))
goto nla_put_failure;
+
+ ret = rdev_get_channel(rdev, wdev, link_id, &chandef);
+ if (ret == 0 && nl80211_send_chandef(msg, &chandef))
+ goto nla_put_failure;
+
nla_nest_end(msg, link);
}
@@ -4320,6 +4344,38 @@ static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
return rdev_set_noack_map(rdev, dev, noack_map);
}
+static int nl80211_validate_key_link_id(struct genl_info *info,
+ struct wireless_dev *wdev,
+ int link_id, bool pairwise)
+{
+ if (pairwise) {
+ if (link_id != -1) {
+ GENL_SET_ERR_MSG(info,
+ "link ID not allowed for pairwise key");
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ if (wdev->valid_links) {
+ if (link_id == -1) {
+ GENL_SET_ERR_MSG(info,
+ "link ID must for MLO group key");
+ return -EINVAL;
+ }
+ if (!(wdev->valid_links & BIT(link_id))) {
+ GENL_SET_ERR_MSG(info, "invalid link ID for MLO group key");
+ return -EINVAL;
+ }
+ } else if (link_id != -1) {
+ GENL_SET_ERR_MSG(info, "link ID not allowed for non-MLO group key");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
struct get_key_cookie {
struct sk_buff *msg;
int error;
@@ -4381,13 +4437,15 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
void *hdr;
struct sk_buff *msg;
bool bigtk_support = false;
+ int link_id = nl80211_link_id_or_invalid(info->attrs);
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
if (wiphy_ext_feature_isset(&rdev->wiphy,
NL80211_EXT_FEATURE_BEACON_PROTECTION))
bigtk_support = true;
- if ((dev->ieee80211_ptr->iftype == NL80211_IFTYPE_STATION ||
- dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_CLIENT) &&
+ if ((wdev->iftype == NL80211_IFTYPE_STATION ||
+ wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) &&
wiphy_ext_feature_isset(&rdev->wiphy,
NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT))
bigtk_support = true;
@@ -4439,8 +4497,12 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr))
goto nla_put_failure;
- err = rdev_get_key(rdev, dev, key_idx, pairwise, mac_addr, &cookie,
- get_key_callback);
+ err = nl80211_validate_key_link_id(info, wdev, link_id, pairwise);
+ if (err)
+ goto free_msg;
+
+ err = rdev_get_key(rdev, dev, link_id, key_idx, pairwise, mac_addr,
+ &cookie, get_key_callback);
if (err)
goto free_msg;
@@ -4464,6 +4526,8 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
struct key_parse key;
int err;
struct net_device *dev = info->user_ptr[1];
+ int link_id = nl80211_link_id_or_invalid(info->attrs);
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
err = nl80211_parse_key(info, &key);
if (err)
@@ -4479,7 +4543,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
!(key.p.mode == NL80211_KEY_SET_TX))
return -EINVAL;
- wdev_lock(dev->ieee80211_ptr);
+ wdev_lock(wdev);
if (key.def) {
if (!rdev->ops->set_default_key) {
@@ -4487,18 +4551,22 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- err = nl80211_key_allowed(dev->ieee80211_ptr);
+ err = nl80211_key_allowed(wdev);
if (err)
goto out;
- err = rdev_set_default_key(rdev, dev, key.idx,
- key.def_uni, key.def_multi);
+ err = nl80211_validate_key_link_id(info, wdev, link_id, false);
+ if (err)
+ goto out;
+
+ err = rdev_set_default_key(rdev, dev, link_id, key.idx,
+ key.def_uni, key.def_multi);
if (err)
goto out;
#ifdef CONFIG_CFG80211_WEXT
- dev->ieee80211_ptr->wext.default_key = key.idx;
+ wdev->wext.default_key = key.idx;
#endif
} else if (key.defmgmt) {
if (key.def_uni || !key.def_multi) {
@@ -4511,16 +4579,20 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- err = nl80211_key_allowed(dev->ieee80211_ptr);
+ err = nl80211_key_allowed(wdev);
if (err)
goto out;
- err = rdev_set_default_mgmt_key(rdev, dev, key.idx);
+ err = nl80211_validate_key_link_id(info, wdev, link_id, false);
+ if (err)
+ goto out;
+
+ err = rdev_set_default_mgmt_key(rdev, dev, link_id, key.idx);
if (err)
goto out;
#ifdef CONFIG_CFG80211_WEXT
- dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
+ wdev->wext.default_mgmt_key = key.idx;
#endif
} else if (key.defbeacon) {
if (key.def_uni || !key.def_multi) {
@@ -4533,11 +4605,15 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- err = nl80211_key_allowed(dev->ieee80211_ptr);
+ err = nl80211_key_allowed(wdev);
+ if (err)
+ goto out;
+
+ err = nl80211_validate_key_link_id(info, wdev, link_id, false);
if (err)
goto out;
- err = rdev_set_default_beacon_key(rdev, dev, key.idx);
+ err = rdev_set_default_beacon_key(rdev, dev, link_id, key.idx);
if (err)
goto out;
} else if (key.p.mode == NL80211_KEY_SET_TX &&
@@ -4553,14 +4629,18 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- err = rdev_add_key(rdev, dev, key.idx,
+ err = nl80211_validate_key_link_id(info, wdev, link_id, true);
+ if (err)
+ goto out;
+
+ err = rdev_add_key(rdev, dev, link_id, key.idx,
NL80211_KEYTYPE_PAIRWISE,
mac_addr, &key.p);
} else {
err = -EINVAL;
}
out:
- wdev_unlock(dev->ieee80211_ptr);
+ wdev_unlock(wdev);
return err;
}
@@ -4572,6 +4652,8 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
struct net_device *dev = info->user_ptr[1];
struct key_parse key;
const u8 *mac_addr = NULL;
+ int link_id = nl80211_link_id_or_invalid(info->attrs);
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
err = nl80211_parse_key(info, &key);
if (err)
@@ -4613,18 +4695,23 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
}
- wdev_lock(dev->ieee80211_ptr);
- err = nl80211_key_allowed(dev->ieee80211_ptr);
+ wdev_lock(wdev);
+ err = nl80211_key_allowed(wdev);
if (err)
GENL_SET_ERR_MSG(info, "key not allowed");
+
+ if (!err)
+ err = nl80211_validate_key_link_id(info, wdev, link_id,
+ key.type == NL80211_KEYTYPE_PAIRWISE);
+
if (!err) {
- err = rdev_add_key(rdev, dev, key.idx,
+ err = rdev_add_key(rdev, dev, link_id, key.idx,
key.type == NL80211_KEYTYPE_PAIRWISE,
mac_addr, &key.p);
if (err)
GENL_SET_ERR_MSG(info, "key addition failed");
}
- wdev_unlock(dev->ieee80211_ptr);
+ wdev_unlock(wdev);
return err;
}
@@ -4636,6 +4723,8 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
struct net_device *dev = info->user_ptr[1];
u8 *mac_addr = NULL;
struct key_parse key;
+ int link_id = nl80211_link_id_or_invalid(info->attrs);
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
err = nl80211_parse_key(info, &key);
if (err)
@@ -4663,27 +4752,31 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
if (!rdev->ops->del_key)
return -EOPNOTSUPP;
- wdev_lock(dev->ieee80211_ptr);
- err = nl80211_key_allowed(dev->ieee80211_ptr);
+ wdev_lock(wdev);
+ err = nl80211_key_allowed(wdev);
if (key.type == NL80211_KEYTYPE_GROUP && mac_addr &&
!(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
err = -ENOENT;
if (!err)
- err = rdev_del_key(rdev, dev, key.idx,
+ err = nl80211_validate_key_link_id(info, wdev, link_id,
+ key.type == NL80211_KEYTYPE_PAIRWISE);
+
+ if (!err)
+ err = rdev_del_key(rdev, dev, link_id, key.idx,
key.type == NL80211_KEYTYPE_PAIRWISE,
mac_addr);
#ifdef CONFIG_CFG80211_WEXT
if (!err) {
- if (key.idx == dev->ieee80211_ptr->wext.default_key)
- dev->ieee80211_ptr->wext.default_key = -1;
- else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
- dev->ieee80211_ptr->wext.default_mgmt_key = -1;
+ if (key.idx == wdev->wext.default_key)
+ wdev->wext.default_key = -1;
+ else if (key.idx == wdev->wext.default_mgmt_key)
+ wdev->wext.default_mgmt_key = -1;
}
#endif
- wdev_unlock(dev->ieee80211_ptr);
+ wdev_unlock(wdev);
return err;
}
@@ -5587,7 +5680,7 @@ static int nl80211_calculate_ap_params(struct cfg80211_ap_settings *params)
params->eht_cap = (void *)(cap->data + 1);
if (!ieee80211_eht_capa_size_ok((const u8 *)params->he_cap,
(const u8 *)params->eht_cap,
- cap->datalen - 1))
+ cap->datalen - 1, true))
return -EINVAL;
}
cap = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_OPERATION, ies, ies_len);
@@ -6816,7 +6909,8 @@ static int nl80211_set_station_tdls(struct genl_info *info,
if (!ieee80211_eht_capa_size_ok((const u8 *)params->link_sta_params.he_capa,
(const u8 *)params->link_sta_params.eht_capa,
- params->link_sta_params.eht_capa_len))
+ params->link_sta_params.eht_capa_len,
+ false))
return -EINVAL;
}
}
@@ -7127,7 +7221,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
if (!ieee80211_eht_capa_size_ok((const u8 *)params.link_sta_params.he_capa,
(const u8 *)params.link_sta_params.eht_capa,
- params.link_sta_params.eht_capa_len))
+ params.link_sta_params.eht_capa_len,
+ false))
return -EINVAL;
}
}
@@ -10087,8 +10182,10 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
(nla_put_u32(msg, NL80211_BSS_STATUS,
NL80211_BSS_STATUS_ASSOCIATED) ||
(wdev->valid_links &&
- nla_put_u8(msg, NL80211_BSS_MLO_LINK_ID,
- link_id))))
+ (nla_put_u8(msg, NL80211_BSS_MLO_LINK_ID,
+ link_id) ||
+ nla_put(msg, NL80211_BSS_MLD_ADDR, ETH_ALEN,
+ wdev->u.client.connected_addr)))))
goto nla_put_failure;
}
break;
@@ -11184,7 +11281,6 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
struct net_device *dev = info->user_ptr[1];
int mcast_rate[NUM_NL80211_BANDS];
u32 nla_rate;
- int err;
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
@@ -11203,9 +11299,7 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
if (!nl80211_parse_mcast_rate(rdev, mcast_rate, nla_rate))
return -EINVAL;
- err = rdev_set_mcast_rate(rdev, dev, mcast_rate);
-
- return err;
+ return rdev_set_mcast_rate(rdev, dev, mcast_rate);
}
static struct sk_buff *
@@ -13171,7 +13265,9 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
wake_mask_size);
if (tok) {
cfg->tokens_size = tokens_size;
- memcpy(&cfg->payload_tok, tok, sizeof(*tok) + tokens_size);
+ cfg->payload_tok = *tok;
+ memcpy(cfg->payload_tok.token_stream, tok->token_stream,
+ tokens_size);
}
trig->tcp = cfg;
@@ -15887,7 +15983,8 @@ nl80211_add_mod_link_station(struct sk_buff *skb, struct genl_info *info,
if (!ieee80211_eht_capa_size_ok((const u8 *)params.he_capa,
(const u8 *)params.eht_capa,
- params.eht_capa_len))
+ params.eht_capa_len,
+ false))
return -EINVAL;
}
}
@@ -17141,6 +17238,7 @@ static struct genl_family nl80211_fam __ro_after_init = {
.n_ops = ARRAY_SIZE(nl80211_ops),
.small_ops = nl80211_small_ops,
.n_small_ops = ARRAY_SIZE(nl80211_small_ops),
+ .resv_start_op = NL80211_CMD_REMOVE_LINK_STA + 1,
.mcgrps = nl80211_mcgrps,
.n_mcgrps = ARRAY_SIZE(nl80211_mcgrps),
.parallel_ops = true,
@@ -18836,11 +18934,13 @@ EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
+ unsigned int link_id,
struct cfg80211_chan_def *chandef,
gfp_t gfp,
enum nl80211_commands notif,
u8 count, bool quiet)
{
+ struct wireless_dev *wdev = netdev->ieee80211_ptr;
struct sk_buff *msg;
void *hdr;
@@ -18857,6 +18957,10 @@ static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
goto nla_put_failure;
+ if (wdev->valid_links &&
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+ goto nla_put_failure;
+
if (nl80211_send_chandef(msg, chandef))
goto nla_put_failure;
@@ -18916,22 +19020,26 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
cfg80211_sched_dfs_chan_update(rdev);
- nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
+ nl80211_ch_switch_notify(rdev, dev, link_id, chandef, GFP_KERNEL,
NL80211_CMD_CH_SWITCH_NOTIFY, 0, false);
}
EXPORT_SYMBOL(cfg80211_ch_switch_notify);
void cfg80211_ch_switch_started_notify(struct net_device *dev,
struct cfg80211_chan_def *chandef,
- u8 count, bool quiet)
+ unsigned int link_id, u8 count,
+ bool quiet)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
- trace_cfg80211_ch_switch_started_notify(dev, chandef);
+ ASSERT_WDEV_LOCK(wdev);
+ WARN_INVALID_LINK_ID(wdev, link_id);
+
+ trace_cfg80211_ch_switch_started_notify(dev, chandef, link_id);
- nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
+ nl80211_ch_switch_notify(rdev, dev, link_id, chandef, GFP_KERNEL,
NL80211_CMD_CH_SWITCH_STARTED_NOTIFY,
count, quiet);
}