Commit c1d8bd8d authored by Johannes Berg's avatar Johannes Berg

wifi: cfg80211: add regulatory flag to allow VLP AP operation

Add a regulatory flag to allow VLP AP operation even on
channels otherwise marked NO_IR, which may be possible
in some regulatory domains/countries.

Note that this requires checking also when the beacon is
changed, since that may change the regulatory power type.
Reviewed-by: default avatarMiriam Rachel Korenblit <miriam.rachel.korenblit@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Link: https://msgid.link/20240523120945.63792ce19790.Ie2a02750d283b78fbf3c686b10565fb0388889e2@changeidSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 9fd171a7
...@@ -125,6 +125,8 @@ struct wiphy; ...@@ -125,6 +125,8 @@ struct wiphy;
* @IEEE80211_CHAN_CAN_MONITOR: This channel can be used for monitor * @IEEE80211_CHAN_CAN_MONITOR: This channel can be used for monitor
* mode even in the presence of other (regulatory) restrictions, * mode even in the presence of other (regulatory) restrictions,
* even if it is otherwise disabled. * even if it is otherwise disabled.
* @IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP: Allow using this channel for AP operation
* with very low power (VLP), even if otherwise set to NO_IR.
*/ */
enum ieee80211_channel_flags { enum ieee80211_channel_flags {
IEEE80211_CHAN_DISABLED = BIT(0), IEEE80211_CHAN_DISABLED = BIT(0),
...@@ -152,6 +154,7 @@ enum ieee80211_channel_flags { ...@@ -152,6 +154,7 @@ enum ieee80211_channel_flags {
IEEE80211_CHAN_NO_6GHZ_VLP_CLIENT = BIT(22), IEEE80211_CHAN_NO_6GHZ_VLP_CLIENT = BIT(22),
IEEE80211_CHAN_NO_6GHZ_AFC_CLIENT = BIT(23), IEEE80211_CHAN_NO_6GHZ_AFC_CLIENT = BIT(23),
IEEE80211_CHAN_CAN_MONITOR = BIT(24), IEEE80211_CHAN_CAN_MONITOR = BIT(24),
IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP = BIT(25),
}; };
#define IEEE80211_CHAN_NO_HT40 \ #define IEEE80211_CHAN_NO_HT40 \
...@@ -8806,9 +8809,12 @@ static inline void cfg80211_report_obss_beacon(struct wiphy *wiphy, ...@@ -8806,9 +8809,12 @@ static inline void cfg80211_report_obss_beacon(struct wiphy *wiphy,
* @relax: allow IR-relaxation conditions to apply (e.g. another * @relax: allow IR-relaxation conditions to apply (e.g. another
* interface connected already on the same channel) * interface connected already on the same channel)
* NOTE: If this is set, wiphy mutex must be held. * NOTE: If this is set, wiphy mutex must be held.
* @reg_power: &enum ieee80211_ap_reg_power value indicating the
* advertised/used 6 GHz regulatory power setting
*/ */
struct cfg80211_beaconing_check_config { struct cfg80211_beaconing_check_config {
enum nl80211_iftype iftype; enum nl80211_iftype iftype;
enum ieee80211_ap_reg_power reg_power;
bool relax; bool relax;
}; };
......
...@@ -4277,6 +4277,8 @@ enum nl80211_wmm_rule { ...@@ -4277,6 +4277,8 @@ enum nl80211_wmm_rule {
* @NL80211_FREQUENCY_ATTR_CAN_MONITOR: This channel can be used in monitor * @NL80211_FREQUENCY_ATTR_CAN_MONITOR: This channel can be used in monitor
* mode despite other (regulatory) restrictions, even if the channel is * mode despite other (regulatory) restrictions, even if the channel is
* otherwise completely disabled. * otherwise completely disabled.
* @NL80211_FREQUENCY_ATTR_ALLOW_6GHZ_VLP_AP: This channel can be used for a
* very low power (VLP) AP, despite being NO_IR.
* @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
* currently defined * currently defined
* @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
...@@ -4320,6 +4322,7 @@ enum nl80211_frequency_attr { ...@@ -4320,6 +4322,7 @@ enum nl80211_frequency_attr {
NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT, NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT,
NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT, NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT,
NL80211_FREQUENCY_ATTR_CAN_MONITOR, NL80211_FREQUENCY_ATTR_CAN_MONITOR,
NL80211_FREQUENCY_ATTR_ALLOW_6GHZ_VLP_AP,
/* keep last */ /* keep last */
__NL80211_FREQUENCY_ATTR_AFTER_LAST, __NL80211_FREQUENCY_ATTR_AFTER_LAST,
...@@ -4529,6 +4532,8 @@ enum nl80211_sched_scan_match_attr { ...@@ -4529,6 +4532,8 @@ enum nl80211_sched_scan_match_attr {
* Should be used together with %NL80211_RRF_DFS only. * Should be used together with %NL80211_RRF_DFS only.
* @NL80211_RRF_NO_6GHZ_VLP_CLIENT: Client connection to VLP AP not allowed * @NL80211_RRF_NO_6GHZ_VLP_CLIENT: Client connection to VLP AP not allowed
* @NL80211_RRF_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP not allowed * @NL80211_RRF_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP not allowed
* @NL80211_RRF_ALLOW_6GHZ_VLP_AP: Very low power (VLP) AP can be permitted
* despite NO_IR configuration.
*/ */
enum nl80211_reg_rule_flags { enum nl80211_reg_rule_flags {
NL80211_RRF_NO_OFDM = 1<<0, NL80211_RRF_NO_OFDM = 1<<0,
...@@ -4553,6 +4558,7 @@ enum nl80211_reg_rule_flags { ...@@ -4553,6 +4558,7 @@ enum nl80211_reg_rule_flags {
NL80211_RRF_DFS_CONCURRENT = 1<<21, NL80211_RRF_DFS_CONCURRENT = 1<<21,
NL80211_RRF_NO_6GHZ_VLP_CLIENT = 1<<22, NL80211_RRF_NO_6GHZ_VLP_CLIENT = 1<<22,
NL80211_RRF_NO_6GHZ_AFC_CLIENT = 1<<23, NL80211_RRF_NO_6GHZ_AFC_CLIENT = 1<<23,
NL80211_RRF_ALLOW_6GHZ_VLP_AP = 1<<24,
}; };
#define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR #define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR
......
...@@ -1523,28 +1523,38 @@ static bool cfg80211_ir_permissive_chan(struct wiphy *wiphy, ...@@ -1523,28 +1523,38 @@ static bool cfg80211_ir_permissive_chan(struct wiphy *wiphy,
static bool _cfg80211_reg_can_beacon(struct wiphy *wiphy, static bool _cfg80211_reg_can_beacon(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef, struct cfg80211_chan_def *chandef,
enum nl80211_iftype iftype, enum nl80211_iftype iftype,
bool check_no_ir) u32 prohibited_flags,
u32 permitting_flags)
{ {
bool res; bool res, check_radar;
u32 prohibited_flags = IEEE80211_CHAN_DISABLED;
int dfs_required; int dfs_required;
trace_cfg80211_reg_can_beacon(wiphy, chandef, iftype, check_no_ir); trace_cfg80211_reg_can_beacon(wiphy, chandef, iftype,
prohibited_flags,
permitting_flags);
if (check_no_ir) if (!_cfg80211_chandef_usable(wiphy, chandef,
prohibited_flags |= IEEE80211_CHAN_NO_IR; IEEE80211_CHAN_DISABLED, 0))
return false;
dfs_required = cfg80211_chandef_dfs_required(wiphy, chandef, iftype); dfs_required = cfg80211_chandef_dfs_required(wiphy, chandef, iftype);
if (dfs_required != 0) check_radar = dfs_required != 0;
prohibited_flags |= IEEE80211_CHAN_RADAR;
if (dfs_required > 0 && if (dfs_required > 0 &&
cfg80211_chandef_dfs_available(wiphy, chandef)) { cfg80211_chandef_dfs_available(wiphy, chandef)) {
/* We can skip IEEE80211_CHAN_NO_IR if chandef dfs available */ /* We can skip IEEE80211_CHAN_NO_IR if chandef dfs available */
prohibited_flags = IEEE80211_CHAN_DISABLED; prohibited_flags &= ~IEEE80211_CHAN_NO_IR;
check_radar = false;
} }
res = _cfg80211_chandef_usable(wiphy, chandef, prohibited_flags, 0); if (check_radar &&
!_cfg80211_chandef_usable(wiphy, chandef,
IEEE80211_CHAN_RADAR, 0))
return false;
res = _cfg80211_chandef_usable(wiphy, chandef,
prohibited_flags,
permitting_flags);
trace_cfg80211_return_bool(res); trace_cfg80211_return_bool(res);
return res; return res;
...@@ -1555,6 +1565,7 @@ bool cfg80211_reg_check_beaconing(struct wiphy *wiphy, ...@@ -1555,6 +1565,7 @@ bool cfg80211_reg_check_beaconing(struct wiphy *wiphy,
struct cfg80211_beaconing_check_config *cfg) struct cfg80211_beaconing_check_config *cfg)
{ {
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
u32 permitting_flags = 0;
bool check_no_ir = true; bool check_no_ir = true;
/* /*
...@@ -1569,8 +1580,12 @@ bool cfg80211_reg_check_beaconing(struct wiphy *wiphy, ...@@ -1569,8 +1580,12 @@ bool cfg80211_reg_check_beaconing(struct wiphy *wiphy,
chandef->chan); chandef->chan);
} }
if (cfg->reg_power == IEEE80211_REG_VLP_AP)
permitting_flags |= IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP;
return _cfg80211_reg_can_beacon(wiphy, chandef, cfg->iftype, return _cfg80211_reg_can_beacon(wiphy, chandef, cfg->iftype,
check_no_ir); check_no_ir ? IEEE80211_CHAN_NO_IR : 0,
permitting_flags);
} }
EXPORT_SYMBOL(cfg80211_reg_check_beaconing); EXPORT_SYMBOL(cfg80211_reg_check_beaconing);
......
...@@ -1207,6 +1207,9 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy, ...@@ -1207,6 +1207,9 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy,
if ((chan->flags & IEEE80211_CHAN_CAN_MONITOR) && if ((chan->flags & IEEE80211_CHAN_CAN_MONITOR) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_CAN_MONITOR)) nla_put_flag(msg, NL80211_FREQUENCY_ATTR_CAN_MONITOR))
goto nla_put_failure; 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, if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
...@@ -5954,6 +5957,7 @@ static int nl80211_validate_ap_phy_operation(struct cfg80211_ap_settings *params ...@@ -5954,6 +5957,7 @@ static int nl80211_validate_ap_phy_operation(struct cfg80211_ap_settings *params
static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
{ {
struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct cfg80211_beaconing_check_config beacon_check = {};
unsigned int link_id = nl80211_link_id(info->attrs); unsigned int link_id = nl80211_link_id(info->attrs);
struct net_device *dev = info->user_ptr[1]; struct net_device *dev = info->user_ptr[1];
struct wireless_dev *wdev = dev->ieee80211_ptr; struct wireless_dev *wdev = dev->ieee80211_ptr;
...@@ -6103,8 +6107,13 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) ...@@ -6103,8 +6107,13 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
goto out; goto out;
} }
if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, &params->chandef, beacon_check.iftype = wdev->iftype;
wdev->iftype)) { beacon_check.relax = true;
beacon_check.reg_power =
cfg80211_get_6ghz_power_type(params->beacon.tail,
params->beacon.tail_len);
if (!cfg80211_reg_check_beaconing(&rdev->wiphy, &params->chandef,
&beacon_check)) {
err = -EINVAL; err = -EINVAL;
goto out; goto out;
} }
...@@ -6261,6 +6270,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) ...@@ -6261,6 +6270,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info) static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
{ {
struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct cfg80211_beaconing_check_config beacon_check = {};
unsigned int link_id = nl80211_link_id(info->attrs); unsigned int link_id = nl80211_link_id(info->attrs);
struct net_device *dev = info->user_ptr[1]; struct net_device *dev = info->user_ptr[1];
struct wireless_dev *wdev = dev->ieee80211_ptr; struct wireless_dev *wdev = dev->ieee80211_ptr;
...@@ -6287,6 +6297,19 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info) ...@@ -6287,6 +6297,19 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
if (err) if (err)
goto out; goto out;
/* recheck beaconing is permitted with possibly changed power type */
beacon_check.iftype = wdev->iftype;
beacon_check.relax = true;
beacon_check.reg_power =
cfg80211_get_6ghz_power_type(params->beacon.tail,
params->beacon.tail_len);
if (!cfg80211_reg_check_beaconing(&rdev->wiphy,
&wdev->links[link_id].ap.chandef,
&beacon_check)) {
err = -EINVAL;
goto out;
}
attr = info->attrs[NL80211_ATTR_FILS_DISCOVERY]; attr = info->attrs[NL80211_ATTR_FILS_DISCOVERY];
if (attr) { if (attr) {
err = nl80211_parse_fils_discovery(rdev, attr, err = nl80211_parse_fils_discovery(rdev, attr,
......
...@@ -1600,6 +1600,8 @@ static u32 map_regdom_flags(u32 rd_flags) ...@@ -1600,6 +1600,8 @@ static u32 map_regdom_flags(u32 rd_flags)
channel_flags |= IEEE80211_CHAN_NO_6GHZ_AFC_CLIENT; channel_flags |= IEEE80211_CHAN_NO_6GHZ_AFC_CLIENT;
if (rd_flags & NL80211_RRF_PSD) if (rd_flags & NL80211_RRF_PSD)
channel_flags |= IEEE80211_CHAN_PSD; channel_flags |= IEEE80211_CHAN_PSD;
if (rd_flags & NL80211_RRF_ALLOW_6GHZ_VLP_AP)
channel_flags |= IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP;
return channel_flags; return channel_flags;
} }
......
...@@ -3389,23 +3389,26 @@ TRACE_EVENT(cfg80211_cqm_rssi_notify, ...@@ -3389,23 +3389,26 @@ TRACE_EVENT(cfg80211_cqm_rssi_notify,
TRACE_EVENT(cfg80211_reg_can_beacon, TRACE_EVENT(cfg80211_reg_can_beacon,
TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef, TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef,
enum nl80211_iftype iftype, bool check_no_ir), enum nl80211_iftype iftype, u32 prohibited_flags,
TP_ARGS(wiphy, chandef, iftype, check_no_ir), u32 permitting_flags),
TP_ARGS(wiphy, chandef, iftype, prohibited_flags, permitting_flags),
TP_STRUCT__entry( TP_STRUCT__entry(
WIPHY_ENTRY WIPHY_ENTRY
CHAN_DEF_ENTRY CHAN_DEF_ENTRY
__field(enum nl80211_iftype, iftype) __field(enum nl80211_iftype, iftype)
__field(bool, check_no_ir) __field(u32, prohibited_flags)
__field(u32, permitting_flags)
), ),
TP_fast_assign( TP_fast_assign(
WIPHY_ASSIGN; WIPHY_ASSIGN;
CHAN_DEF_ASSIGN(chandef); CHAN_DEF_ASSIGN(chandef);
__entry->iftype = iftype; __entry->iftype = iftype;
__entry->check_no_ir = check_no_ir; __entry->prohibited_flags = prohibited_flags;
__entry->permitting_flags = permitting_flags;
), ),
TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", iftype=%d check_no_ir=%s", TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", iftype=%d prohibited_flags=0x%x permitting_flags=0x%x",
WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->iftype, WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->iftype,
BOOL_TO_STR(__entry->check_no_ir)) __entry->prohibited_flags, __entry->permitting_flags)
); );
TRACE_EVENT(cfg80211_chandef_dfs_required, TRACE_EVENT(cfg80211_chandef_dfs_required,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment