diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 056c779fdb56..f9727303ad4b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6394,9 +6394,104 @@ static int nl80211_parse_he_bss_color(struct nlattr *attrs, return 0; } +static void nl80211_check_ap_rate_selectors(struct cfg80211_beacon_data *bcn, + const struct element *rates) +{ + int i; + + if (!rates) + return; + + for (i = 0; i < rates->datalen; i++) { + if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_HT_PHY) + bcn->ht_required = true; + if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_VHT_PHY) + bcn->vht_required = true; + } +} + +/* + * Since the nl80211 API didn't include, from the beginning, attributes about + * HT/VHT/... operation, we parse them out of the elements and check for + * validity for use by drivers/mac80211. + */ +static int nl80211_calculate_ap_operation(struct nlattr *attrs[], + struct cfg80211_beacon_data *bcn, + struct netlink_ext_ack *extack) +{ + size_t ies_len = bcn->tail_len; + const u8 *ies = bcn->tail; + const struct element *rates; + const struct element *op; + + rates = cfg80211_find_elem(WLAN_EID_SUPP_RATES, ies, ies_len); + nl80211_check_ap_rate_selectors(bcn, rates); + + rates = cfg80211_find_elem(WLAN_EID_EXT_SUPP_RATES, ies, ies_len); + nl80211_check_ap_rate_selectors(bcn, rates); + + op = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION, ies, ies_len); + if (op) { + if (op->datalen < sizeof(*bcn->he_oper) + 1) { + NL_SET_ERR_MSG(extack, "bad HE operation in beacon"); + return -EINVAL; + } + bcn->he_oper = (void *)(op->data + 1); + /* takes extension ID into account */ + if (op->datalen < ieee80211_he_oper_size((void *)bcn->he_oper)) { + NL_SET_ERR_MSG(extack, "bad HE operation in beacon"); + return -EINVAL; + } + } + + op = cfg80211_find_elem(WLAN_EID_HT_OPERATION, ies, ies_len); + if (op) { + if (op->datalen < sizeof(*bcn->ht_oper)) { + NL_SET_ERR_MSG(extack, "bad HT operation in beacon"); + return -EINVAL; + } + bcn->ht_oper = (void *)op->data; + } + + op = cfg80211_find_elem(WLAN_EID_VHT_OPERATION, ies, ies_len); + if (op) { + if (op->datalen < sizeof(*bcn->vht_oper)) { + NL_SET_ERR_MSG(extack, "bad VHT operation in beacon"); + return -EINVAL; + } + bcn->vht_oper = (void *)op->data; + } + + op = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_OPERATION, ies, ies_len); + if (op) { + if (!ieee80211_eht_oper_size_ok(op->data + 1, + op->datalen - 1)) { + NL_SET_ERR_MSG(extack, "bad EHT operation in beacon"); + return -EINVAL; + } + bcn->eht_oper = (void *)(op->data + 1); + } + + op = cfg80211_find_ext_elem(WLAN_EID_EXT_UHR_OPER, ies, ies_len); + if (op) { + /* need full UHR operation separately */ + if (!attrs[NL80211_ATTR_UHR_OPERATION]) { + NL_SET_ERR_MSG(extack, "missing UHR operation"); + return -EINVAL; + } + bcn->uhr_oper = nla_data(attrs[NL80211_ATTR_UHR_OPERATION]); + } else if (attrs[NL80211_ATTR_UHR_OPERATION]) { + NL_SET_ERR_MSG(extack, "unexpected UHR operation"); + return -EINVAL; + } + + return 0; +} + static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev, struct nlattr *attrs[], struct cfg80211_beacon_data *bcn, + struct ieee80211_channel *chan, struct netlink_ext_ack *extack) { bool haveinfo = false; @@ -6511,6 +6606,19 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev, } } + err = nl80211_calculate_ap_operation(attrs, bcn, extack); + if (err) + return err; + + if (bcn->he_oper && (chan->flags & IEEE80211_CHAN_NO_HE)) + return -EOPNOTSUPP; + + if (bcn->eht_oper && (chan->flags & IEEE80211_CHAN_NO_EHT)) + return -EOPNOTSUPP; + + if (bcn->uhr_oper && (chan->flags & IEEE80211_CHAN_NO_UHR)) + return -EOPNOTSUPP; + return 0; } @@ -6628,22 +6736,6 @@ nl80211_parse_unsol_bcast_probe_resp(struct cfg80211_registered_device *rdev, return 0; } -static void nl80211_check_ap_rate_selectors(struct cfg80211_beacon_data *bcn, - const struct element *rates) -{ - int i; - - if (!rates) - return; - - for (i = 0; i < rates->datalen; i++) { - if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_HT_PHY) - bcn->ht_required = true; - if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_VHT_PHY) - bcn->vht_required = true; - } -} - /* * Since the nl80211 API didn't include, from the beginning, attributes about * HT/VHT/... capabilities, we parse them out of the elements and check for @@ -6697,83 +6789,6 @@ static int nl80211_calculate_ap_capabilities(struct genl_info *info, return 0; } -/* - * Since the nl80211 API didn't include, from the beginning, attributes about - * HT/VHT/... operation, we parse them out of the elements and check for - * validity for use by drivers/mac80211. - */ -static int nl80211_calculate_ap_operation(struct genl_info *info, - struct cfg80211_beacon_data *bcn) -{ - size_t ies_len = bcn->tail_len; - const u8 *ies = bcn->tail; - const struct element *rates; - const struct element *op; - - rates = cfg80211_find_elem(WLAN_EID_SUPP_RATES, ies, ies_len); - nl80211_check_ap_rate_selectors(bcn, rates); - - rates = cfg80211_find_elem(WLAN_EID_EXT_SUPP_RATES, ies, ies_len); - nl80211_check_ap_rate_selectors(bcn, rates); - - op = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION, ies, ies_len); - if (op) { - if (op->datalen < sizeof(*bcn->he_oper) + 1) { - GENL_SET_ERR_MSG(info, "bad HE operation in beacon"); - return -EINVAL; - } - bcn->he_oper = (void *)(op->data + 1); - /* takes extension ID into account */ - if (op->datalen < ieee80211_he_oper_size((void *)bcn->he_oper)) { - GENL_SET_ERR_MSG(info, "bad HE operation in beacon"); - return -EINVAL; - } - } - - op = cfg80211_find_elem(WLAN_EID_HT_OPERATION, ies, ies_len); - if (op) { - if (op->datalen < sizeof(*bcn->ht_oper)) { - GENL_SET_ERR_MSG(info, "bad HT operation in beacon"); - return -EINVAL; - } - bcn->ht_oper = (void *)op->data; - } - - op = cfg80211_find_elem(WLAN_EID_VHT_OPERATION, ies, ies_len); - if (op) { - if (op->datalen < sizeof(*bcn->vht_oper)) { - GENL_SET_ERR_MSG(info, "bad VHT operation in beacon"); - return -EINVAL; - } - bcn->vht_oper = (void *)op->data; - } - - op = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_OPERATION, ies, ies_len); - if (op) { - if (!ieee80211_eht_oper_size_ok(op->data + 1, - op->datalen - 1)) { - GENL_SET_ERR_MSG(info, "bad EHT operation in beacon"); - return -EINVAL; - } - bcn->eht_oper = (void *)(op->data + 1); - } - - op = cfg80211_find_ext_elem(WLAN_EID_EXT_UHR_OPER, ies, ies_len); - if (op) { - /* need full UHR operation separately */ - if (!info->attrs[NL80211_ATTR_UHR_OPERATION]) { - GENL_SET_ERR_MSG(info, "missing UHR operation"); - return -EINVAL; - } - bcn->uhr_oper = nla_data(info->attrs[NL80211_ATTR_UHR_OPERATION]); - } else if (info->attrs[NL80211_ATTR_UHR_OPERATION]) { - GENL_SET_ERR_MSG(info, "unexpected UHR operation"); - return -EINVAL; - } - - return 0; -} - static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev, struct cfg80211_ap_settings *params) { @@ -6898,21 +6913,6 @@ static void nl80211_send_ap_started(struct wireless_dev *wdev, nlmsg_free(msg); } -static int nl80211_validate_ap_phy_operation(struct ieee80211_channel *channel, - struct cfg80211_beacon_data *bcn) -{ - if (bcn->he_oper && (channel->flags & IEEE80211_CHAN_NO_HE)) - return -EOPNOTSUPP; - - if (bcn->eht_oper && (channel->flags & IEEE80211_CHAN_NO_EHT)) - return -EOPNOTSUPP; - - if (bcn->uhr_oper && (channel->flags & IEEE80211_CHAN_NO_UHR)) - return -EOPNOTSUPP; - - return 0; -} - static int nl80211_parse_s1g_short_beacon(struct cfg80211_registered_device *rdev, struct nlattr *attrs, @@ -6985,11 +6985,6 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) if (!params) return -ENOMEM; - err = nl80211_parse_beacon(rdev, info->attrs, ¶ms->beacon, - info->extack); - if (err) - goto out; - params->beacon_interval = nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); params->dtim_period = @@ -7106,6 +7101,11 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) goto out; } + err = nl80211_parse_beacon(rdev, info->attrs, ¶ms->beacon, + params->chandef.chan, info->extack); + if (err) + goto out; + beacon_check.iftype = wdev->iftype; beacon_check.relax = true; beacon_check.reg_power = @@ -7209,15 +7209,6 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) if (err) goto out; - err = nl80211_calculate_ap_operation(info, ¶ms->beacon); - if (err) - goto out; - - err = nl80211_validate_ap_phy_operation(params->chandef.chan, - ¶ms->beacon); - if (err) - goto out; - if (info->attrs[NL80211_ATTR_AP_SETTINGS_FLAGS]) params->flags = nla_get_u32( info->attrs[NL80211_ATTR_AP_SETTINGS_FLAGS]); @@ -7285,19 +7276,11 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info) return -ENOMEM; err = nl80211_parse_beacon(rdev, info->attrs, ¶ms->beacon, + wdev->links[link_id].ap.chandef.chan, info->extack); if (err) goto out; - err = nl80211_calculate_ap_operation(info, ¶ms->beacon); - if (err) - goto out; - - err = nl80211_validate_ap_phy_operation(wdev->links[link_id].ap.chandef.chan, - ¶ms->beacon); - if (err) - goto out; - /* recheck beaconing is permitted with possibly changed power type */ beacon_check.iftype = wdev->iftype; beacon_check.relax = true; @@ -11898,11 +11881,16 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) params.count = cs_count; + err = nl80211_parse_chandef(rdev, info->extack, info->attrs, + ¶ms.chandef); + if (err) + goto free; + if (!need_new_beacon) goto skip_beacons; err = nl80211_parse_beacon(rdev, info->attrs, ¶ms.beacon_after, - info->extack); + params.chandef.chan, info->extack); if (err) goto free; @@ -11919,6 +11907,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) goto free; err = nl80211_parse_beacon(rdev, csa_attrs, ¶ms.beacon_csa, + wdev->links[link_id].ap.chandef.chan, info->extack); if (err) goto free; @@ -11947,11 +11936,6 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) goto free; skip_beacons: - err = nl80211_parse_chandef(rdev, info->extack, info->attrs, - ¶ms.chandef); - if (err) - goto free; - if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, ¶ms.chandef, wdev->iftype)) { err = -EINVAL; @@ -18405,6 +18389,7 @@ static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info) params.color = nla_get_u8(info->attrs[NL80211_ATTR_COLOR_CHANGE_COLOR]); err = nl80211_parse_beacon(rdev, info->attrs, ¶ms.beacon_next, + wdev->links[params.link_id].ap.chandef.chan, info->extack); if (err) return err; @@ -18420,6 +18405,7 @@ static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info) goto out; err = nl80211_parse_beacon(rdev, tb, ¶ms.beacon_color_change, + wdev->links[params.link_id].ap.chandef.chan, info->extack); if (err) goto out;