cfg80211: allow wiphy specific regdomain management
Add a new regulatory flag that allows a driver to manage regdomain changes/updates for its own wiphy. A self-managed wiphys only employs regulatory information obtained from the FW and driver and does not use other cfg80211 sources like beacon-hints, country-code IEs and hints from other devices on the same system. Conversely, a self-managed wiphy does not share its regulatory hints with other devices in the system. If a system contains several devices, one or more of which are self-managed, there might be contradictory regulatory settings between them. Usage of flag is generally discouraged. Only use it if the FW/driver is incompatible with non-locally originated hints. A new API lets the driver send a complete regdomain, to be applied on its wiphy only. After a wiphy-specific regdomain change takes place, usermode will get a new type of change notification. The regulatory core also takes care enforce regulatory restrictions, in case some interfaces are on forbidden channels. Signed-off-by: Jonathan Doron <jonathanx.doron@intel.com> Signed-off-by: Arik Nemtsov <arikx.nemtsov@intel.com> Reviewed-by: Luis R. Rodriguez <mcgrof@suse.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Родитель
ad30ca2c03
Коммит
b0d7aa5959
|
@ -3807,6 +3807,20 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type,
|
||||||
*/
|
*/
|
||||||
int regulatory_hint(struct wiphy *wiphy, const char *alpha2);
|
int regulatory_hint(struct wiphy *wiphy, const char *alpha2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* regulatory_set_wiphy_regd - set regdom info for self managed drivers
|
||||||
|
* @wiphy: the wireless device we want to process the regulatory domain on
|
||||||
|
* @rd: the regulatory domain informatoin to use for this wiphy
|
||||||
|
*
|
||||||
|
* Set the regulatory domain information for self-managed wiphys, only they
|
||||||
|
* may use this function. See %REGULATORY_WIPHY_SELF_MANAGED for more
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* Return: 0 on success. -EINVAL, -EPERM
|
||||||
|
*/
|
||||||
|
int regulatory_set_wiphy_regd(struct wiphy *wiphy,
|
||||||
|
struct ieee80211_regdomain *rd);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* wiphy_apply_custom_regulatory - apply a custom driver regulatory domain
|
* wiphy_apply_custom_regulatory - apply a custom driver regulatory domain
|
||||||
* @wiphy: the wireless device we want to process the regulatory domain on
|
* @wiphy: the wireless device we want to process the regulatory domain on
|
||||||
|
|
|
@ -147,6 +147,24 @@ struct regulatory_request {
|
||||||
* NL80211_IFTYPE_P2P_CLIENT, NL80211_IFTYPE_P2P_GO,
|
* NL80211_IFTYPE_P2P_CLIENT, NL80211_IFTYPE_P2P_GO,
|
||||||
* NL80211_IFTYPE_P2P_DEVICE. The flag will be set by default if a device
|
* NL80211_IFTYPE_P2P_DEVICE. The flag will be set by default if a device
|
||||||
* includes any modes unsupported for enforcement checking.
|
* includes any modes unsupported for enforcement checking.
|
||||||
|
* @REGULATORY_WIPHY_SELF_MANAGED: for devices that employ wiphy-specific
|
||||||
|
* regdom management. These devices will ignore all regdom changes not
|
||||||
|
* originating from their own wiphy.
|
||||||
|
* A self-managed wiphys only employs regulatory information obtained from
|
||||||
|
* the FW and driver and does not use other cfg80211 sources like
|
||||||
|
* beacon-hints, country-code IEs and hints from other devices on the same
|
||||||
|
* system. Conversely, a self-managed wiphy does not share its regulatory
|
||||||
|
* hints with other devices in the system. If a system contains several
|
||||||
|
* devices, one or more of which are self-managed, there might be
|
||||||
|
* contradictory regulatory settings between them. Usage of flag is
|
||||||
|
* generally discouraged. Only use it if the FW/driver is incompatible
|
||||||
|
* with non-locally originated hints.
|
||||||
|
* This flag is incompatible with the flags: %REGULATORY_CUSTOM_REG,
|
||||||
|
* %REGULATORY_STRICT_REG, %REGULATORY_COUNTRY_IE_FOLLOW_POWER,
|
||||||
|
* %REGULATORY_COUNTRY_IE_IGNORE and %REGULATORY_DISABLE_BEACON_HINTS.
|
||||||
|
* Mixing any of the above flags with this flag will result in a failure
|
||||||
|
* to register the wiphy. This flag implies
|
||||||
|
* %REGULATORY_DISABLE_BEACON_HINTS and %REGULATORY_COUNTRY_IE_IGNORE.
|
||||||
*/
|
*/
|
||||||
enum ieee80211_regulatory_flags {
|
enum ieee80211_regulatory_flags {
|
||||||
REGULATORY_CUSTOM_REG = BIT(0),
|
REGULATORY_CUSTOM_REG = BIT(0),
|
||||||
|
@ -156,6 +174,7 @@ enum ieee80211_regulatory_flags {
|
||||||
REGULATORY_COUNTRY_IE_IGNORE = BIT(4),
|
REGULATORY_COUNTRY_IE_IGNORE = BIT(4),
|
||||||
REGULATORY_ENABLE_RELAX_NO_IR = BIT(5),
|
REGULATORY_ENABLE_RELAX_NO_IR = BIT(5),
|
||||||
REGULATORY_IGNORE_STALE_KICKOFF = BIT(6),
|
REGULATORY_IGNORE_STALE_KICKOFF = BIT(6),
|
||||||
|
REGULATORY_WIPHY_SELF_MANAGED = BIT(7),
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ieee80211_freq_range {
|
struct ieee80211_freq_range {
|
||||||
|
|
|
@ -782,6 +782,10 @@
|
||||||
* peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel
|
* peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel
|
||||||
* when this command completes.
|
* when this command completes.
|
||||||
*
|
*
|
||||||
|
* @NL80211_CMD_WIPHY_REG_CHANGE: Similar to %NL80211_CMD_REG_CHANGE, but used
|
||||||
|
* as an event to indicate changes for devices with wiphy-specific regdom
|
||||||
|
* management.
|
||||||
|
*
|
||||||
* @NL80211_CMD_MAX: highest used command number
|
* @NL80211_CMD_MAX: highest used command number
|
||||||
* @__NL80211_CMD_AFTER_LAST: internal use
|
* @__NL80211_CMD_AFTER_LAST: internal use
|
||||||
*/
|
*/
|
||||||
|
@ -966,6 +970,8 @@ enum nl80211_commands {
|
||||||
NL80211_CMD_TDLS_CHANNEL_SWITCH,
|
NL80211_CMD_TDLS_CHANNEL_SWITCH,
|
||||||
NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
|
NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
|
||||||
|
|
||||||
|
NL80211_CMD_WIPHY_REG_CHANGE,
|
||||||
|
|
||||||
/* add new commands above here */
|
/* add new commands above here */
|
||||||
|
|
||||||
/* used to define NL80211_CMD_MAX below */
|
/* used to define NL80211_CMD_MAX below */
|
||||||
|
|
|
@ -561,6 +561,14 @@ int wiphy_register(struct wiphy *wiphy)
|
||||||
BIT(NL80211_IFTYPE_MONITOR)))
|
BIT(NL80211_IFTYPE_MONITOR)))
|
||||||
wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
|
wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
|
||||||
|
|
||||||
|
if (WARN_ON((wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) &&
|
||||||
|
(wiphy->regulatory_flags &
|
||||||
|
(REGULATORY_CUSTOM_REG |
|
||||||
|
REGULATORY_STRICT_REG |
|
||||||
|
REGULATORY_COUNTRY_IE_FOLLOW_POWER |
|
||||||
|
REGULATORY_COUNTRY_IE_IGNORE))))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
if (WARN_ON(wiphy->coalesce &&
|
if (WARN_ON(wiphy->coalesce &&
|
||||||
(!wiphy->coalesce->n_rules ||
|
(!wiphy->coalesce->n_rules ||
|
||||||
!wiphy->coalesce->n_patterns) &&
|
!wiphy->coalesce->n_patterns) &&
|
||||||
|
|
|
@ -36,6 +36,13 @@ struct cfg80211_registered_device {
|
||||||
* the country on the country IE changed. */
|
* the country on the country IE changed. */
|
||||||
char country_ie_alpha2[2];
|
char country_ie_alpha2[2];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* the driver requests the regulatory core to set this regulatory
|
||||||
|
* domain as the wiphy's. Only used for %REGULATORY_WIPHY_SELF_MANAGED
|
||||||
|
* devices using the regulatory_set_wiphy_regd() API
|
||||||
|
*/
|
||||||
|
const struct ieee80211_regdomain *requested_regd;
|
||||||
|
|
||||||
/* If a Country IE has been received this tells us the environment
|
/* If a Country IE has been received this tells us the environment
|
||||||
* which its telling us its in. This defaults to ENVIRON_ANY */
|
* which its telling us its in. This defaults to ENVIRON_ANY */
|
||||||
enum environment_cap env;
|
enum environment_cap env;
|
||||||
|
|
|
@ -11042,25 +11042,9 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
|
||||||
NL80211_MCGRP_SCAN, GFP_KERNEL);
|
NL80211_MCGRP_SCAN, GFP_KERNEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static bool nl80211_reg_change_event_fill(struct sk_buff *msg,
|
||||||
* This can happen on global regulatory changes or device specific settings
|
struct regulatory_request *request)
|
||||||
* based on custom world regulatory domains.
|
|
||||||
*/
|
|
||||||
void nl80211_send_reg_change_event(struct regulatory_request *request)
|
|
||||||
{
|
{
|
||||||
struct sk_buff *msg;
|
|
||||||
void *hdr;
|
|
||||||
|
|
||||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
||||||
if (!msg)
|
|
||||||
return;
|
|
||||||
|
|
||||||
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
|
|
||||||
if (!hdr) {
|
|
||||||
nlmsg_free(msg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Userspace can always count this one always being set */
|
/* Userspace can always count this one always being set */
|
||||||
if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator))
|
if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator))
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
|
@ -11094,6 +11078,35 @@ void nl80211_send_reg_change_event(struct regulatory_request *request)
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
nla_put_failure:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This can happen on global regulatory changes or device specific settings
|
||||||
|
* based on custom regulatory domains.
|
||||||
|
*/
|
||||||
|
void nl80211_common_reg_change_event(enum nl80211_commands cmd_id,
|
||||||
|
struct regulatory_request *request)
|
||||||
|
{
|
||||||
|
struct sk_buff *msg;
|
||||||
|
void *hdr;
|
||||||
|
|
||||||
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||||
|
if (!msg)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hdr = nl80211hdr_put(msg, 0, 0, 0, cmd_id);
|
||||||
|
if (!hdr) {
|
||||||
|
nlmsg_free(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nl80211_reg_change_event_fill(msg, request) == false)
|
||||||
|
goto nla_put_failure;
|
||||||
|
|
||||||
genlmsg_end(msg, hdr);
|
genlmsg_end(msg, hdr);
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
|
|
|
@ -17,7 +17,21 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
|
||||||
struct net_device *netdev, u32 cmd);
|
struct net_device *netdev, u32 cmd);
|
||||||
void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
|
void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
|
||||||
struct net_device *netdev);
|
struct net_device *netdev);
|
||||||
void nl80211_send_reg_change_event(struct regulatory_request *request);
|
void nl80211_common_reg_change_event(enum nl80211_commands cmd_id,
|
||||||
|
struct regulatory_request *request);
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
nl80211_send_reg_change_event(struct regulatory_request *request)
|
||||||
|
{
|
||||||
|
nl80211_common_reg_change_event(NL80211_CMD_REG_CHANGE, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
nl80211_send_wiphy_reg_change_event(struct regulatory_request *request)
|
||||||
|
{
|
||||||
|
nl80211_common_reg_change_event(NL80211_CMD_WIPHY_REG_CHANGE, request);
|
||||||
|
}
|
||||||
|
|
||||||
void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
|
void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
|
||||||
struct net_device *netdev,
|
struct net_device *netdev,
|
||||||
const u8 *buf, size_t len, gfp_t gfp);
|
const u8 *buf, size_t len, gfp_t gfp);
|
||||||
|
|
|
@ -1307,6 +1307,9 @@ static bool ignore_reg_update(struct wiphy *wiphy,
|
||||||
{
|
{
|
||||||
struct regulatory_request *lr = get_last_request();
|
struct regulatory_request *lr = get_last_request();
|
||||||
|
|
||||||
|
if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
|
||||||
|
return true;
|
||||||
|
|
||||||
if (!lr) {
|
if (!lr) {
|
||||||
REG_DBG_PRINT("Ignoring regulatory request set by %s "
|
REG_DBG_PRINT("Ignoring regulatory request set by %s "
|
||||||
"since last_request is not set\n",
|
"since last_request is not set\n",
|
||||||
|
@ -2147,11 +2150,52 @@ static void reg_process_pending_beacon_hints(void)
|
||||||
spin_unlock_bh(®_pending_beacons_lock);
|
spin_unlock_bh(®_pending_beacons_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void reg_process_self_managed_hints(void)
|
||||||
|
{
|
||||||
|
struct cfg80211_registered_device *rdev;
|
||||||
|
struct wiphy *wiphy;
|
||||||
|
const struct ieee80211_regdomain *tmp;
|
||||||
|
const struct ieee80211_regdomain *regd;
|
||||||
|
enum ieee80211_band band;
|
||||||
|
struct regulatory_request request = {};
|
||||||
|
|
||||||
|
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
|
||||||
|
wiphy = &rdev->wiphy;
|
||||||
|
|
||||||
|
spin_lock(®_requests_lock);
|
||||||
|
regd = rdev->requested_regd;
|
||||||
|
rdev->requested_regd = NULL;
|
||||||
|
spin_unlock(®_requests_lock);
|
||||||
|
|
||||||
|
if (regd == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
tmp = get_wiphy_regdom(wiphy);
|
||||||
|
rcu_assign_pointer(wiphy->regd, regd);
|
||||||
|
rcu_free_regdom(tmp);
|
||||||
|
|
||||||
|
for (band = 0; band < IEEE80211_NUM_BANDS; band++)
|
||||||
|
handle_band_custom(wiphy, wiphy->bands[band], regd);
|
||||||
|
|
||||||
|
reg_process_ht_flags(wiphy);
|
||||||
|
|
||||||
|
request.wiphy_idx = get_wiphy_idx(wiphy);
|
||||||
|
request.alpha2[0] = regd->alpha2[0];
|
||||||
|
request.alpha2[1] = regd->alpha2[1];
|
||||||
|
request.initiator = NL80211_REGDOM_SET_BY_DRIVER;
|
||||||
|
|
||||||
|
nl80211_send_wiphy_reg_change_event(&request);
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_check_channels();
|
||||||
|
}
|
||||||
|
|
||||||
static void reg_todo(struct work_struct *work)
|
static void reg_todo(struct work_struct *work)
|
||||||
{
|
{
|
||||||
rtnl_lock();
|
rtnl_lock();
|
||||||
reg_process_pending_hints();
|
reg_process_pending_hints();
|
||||||
reg_process_pending_beacon_hints();
|
reg_process_pending_beacon_hints();
|
||||||
|
reg_process_self_managed_hints();
|
||||||
rtnl_unlock();
|
rtnl_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2432,6 +2476,8 @@ static void restore_regulatory_settings(bool reset_user)
|
||||||
world_alpha2[1] = cfg80211_world_regdom->alpha2[1];
|
world_alpha2[1] = cfg80211_world_regdom->alpha2[1];
|
||||||
|
|
||||||
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
|
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
|
||||||
|
if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
|
||||||
|
continue;
|
||||||
if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG)
|
if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG)
|
||||||
restore_custom_reg_settings(&rdev->wiphy);
|
restore_custom_reg_settings(&rdev->wiphy);
|
||||||
}
|
}
|
||||||
|
@ -2835,10 +2881,52 @@ int set_regdom(const struct ieee80211_regdomain *rd)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int regulatory_set_wiphy_regd(struct wiphy *wiphy,
|
||||||
|
struct ieee80211_regdomain *rd)
|
||||||
|
{
|
||||||
|
const struct ieee80211_regdomain *regd;
|
||||||
|
const struct ieee80211_regdomain *prev_regd;
|
||||||
|
struct cfg80211_registered_device *rdev;
|
||||||
|
|
||||||
|
if (WARN_ON(!wiphy || !rd))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (WARN(!(wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED),
|
||||||
|
"wiphy should have REGULATORY_WIPHY_SELF_MANAGED\n"))
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
|
if (WARN(!is_valid_rd(rd), "Invalid regulatory domain detected\n")) {
|
||||||
|
print_regdomain_info(rd);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
regd = reg_copy_regd(rd);
|
||||||
|
if (IS_ERR(regd))
|
||||||
|
return PTR_ERR(regd);
|
||||||
|
|
||||||
|
rdev = wiphy_to_rdev(wiphy);
|
||||||
|
|
||||||
|
spin_lock(®_requests_lock);
|
||||||
|
prev_regd = rdev->requested_regd;
|
||||||
|
rdev->requested_regd = regd;
|
||||||
|
spin_unlock(®_requests_lock);
|
||||||
|
|
||||||
|
kfree(prev_regd);
|
||||||
|
|
||||||
|
schedule_work(®_work);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(regulatory_set_wiphy_regd);
|
||||||
|
|
||||||
void wiphy_regulatory_register(struct wiphy *wiphy)
|
void wiphy_regulatory_register(struct wiphy *wiphy)
|
||||||
{
|
{
|
||||||
struct regulatory_request *lr;
|
struct regulatory_request *lr;
|
||||||
|
|
||||||
|
/* self-managed devices ignore external hints */
|
||||||
|
if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
|
||||||
|
wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS |
|
||||||
|
REGULATORY_COUNTRY_IE_IGNORE;
|
||||||
|
|
||||||
if (!reg_dev_ignore_cell_hint(wiphy))
|
if (!reg_dev_ignore_cell_hint(wiphy))
|
||||||
reg_num_devs_support_basehint++;
|
reg_num_devs_support_basehint++;
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче