2014-11-26 20:56:29

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v4 1/4] cfg80211: leave invalid channels on regdomain change

When the regulatory settings change, some channels might become invalid.
Disconnect interfaces acting on these channels, after giving userspace
code a grace period to leave them.

This mode is currently opt-in, and not all interface operating modes are
supported for regulatory-enforcement checks. A wiphy that wishes to use
the new enforcement code must specify an appropriate regulatory flag,
and all its supported interface modes must be supported by the chekcing
code.

Signed-off-by: Arik Nemtsov <[email protected]>
---
include/net/regulatory.h | 12 ++++++
net/wireless/core.c | 11 +++++
net/wireless/reg.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 129 insertions(+), 1 deletion(-)

diff --git a/include/net/regulatory.h b/include/net/regulatory.h
index dad7ab2..b776d72 100644
--- a/include/net/regulatory.h
+++ b/include/net/regulatory.h
@@ -136,6 +136,17 @@ struct regulatory_request {
* otherwise initiating radiation is not allowed. This will enable the
* relaxations enabled under the CFG80211_REG_RELAX_NO_IR configuration
* option
+ * @REGULATORY_IGNORE_STALE_KICKOFF: the regulatory core will _not_ make sure
+ * all interfaces on this wiphy reside on allowed channels. If this flag
+ * is not set, upon a regdomain change, the interfaces are given a grace
+ * period (currently 60 seconds) to disconnect or move to an allowed
+ * channel. Interfaces on forbidden channels are forcibly disconnected.
+ * Currently these types of interfaces are supported for enforcement:
+ * NL80211_IFTYPE_ADHOC, NL80211_IFTYPE_STATION, NL80211_IFTYPE_AP,
+ * NL80211_IFTYPE_AP_VLAN, NL80211_IFTYPE_MONITOR,
+ * NL80211_IFTYPE_P2P_CLIENT, NL80211_IFTYPE_P2P_GO,
+ * NL80211_IFTYPE_P2P_DEVICE. The flag will be set by default if a device
+ * includes any modes unsupported for enforcement checking.
*/
enum ieee80211_regulatory_flags {
REGULATORY_CUSTOM_REG = BIT(0),
@@ -144,6 +155,7 @@ enum ieee80211_regulatory_flags {
REGULATORY_COUNTRY_IE_FOLLOW_POWER = BIT(3),
REGULATORY_COUNTRY_IE_IGNORE = BIT(4),
REGULATORY_ENABLE_RELAX_NO_IR = BIT(5),
+ REGULATORY_IGNORE_STALE_KICKOFF = BIT(6),
};

struct ieee80211_freq_range {
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 4c2e501..ef0d3a0 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -546,6 +546,17 @@ int wiphy_register(struct wiphy *wiphy)
!rdev->ops->tdls_cancel_channel_switch)))
return -EINVAL;

+ /*
+ * if a wiphy has unsupported modes for regulatory channel enforcement,
+ * opt-out of enforcement checking
+ */
+ if (wiphy->interface_modes & ~(BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_P2P_DEVICE) | BIT(NL80211_IFTYPE_AP_VLAN) |
+ BIT(NL80211_IFTYPE_MONITOR)))
+ wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
+
if (WARN_ON(wiphy->coalesce &&
(!wiphy->coalesce->n_rules ||
!wiphy->coalesce->n_patterns) &&
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 32d8310..47be616 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -56,6 +56,7 @@
#include <net/cfg80211.h>
#include "core.h"
#include "reg.h"
+#include "rdev-ops.h"
#include "regdb.h"
#include "nl80211.h"

@@ -66,6 +67,12 @@
#define REG_DBG_PRINT(args...)
#endif

+/*
+ * Grace period we give before making sure all current interfaces reside on
+ * channels allowed by the current regulatory domain.
+ */
+#define REG_ENFORCE_GRACE_MS 60000
+
/**
* enum reg_request_treatment - regulatory request treatment
*
@@ -210,6 +217,9 @@ struct reg_beacon {
struct ieee80211_channel chan;
};

+static void reg_check_chans_work(struct work_struct *work);
+static DECLARE_DELAYED_WORK(reg_check_chans, reg_check_chans_work);
+
static void reg_todo(struct work_struct *work);
static DECLARE_WORK(reg_work, reg_todo);

@@ -1518,6 +1528,96 @@ static void reg_call_notifier(struct wiphy *wiphy,
wiphy->reg_notifier(wiphy, request);
}

+static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ struct ieee80211_channel *ch;
+ struct cfg80211_chan_def chandef;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ bool ret = true;
+
+ wdev_lock(wdev);
+
+ if (!wdev->netdev || !netif_running(wdev->netdev))
+ goto out;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ if (!wdev->beacon_interval)
+ goto out;
+
+ ret = cfg80211_reg_can_beacon(wiphy,
+ &wdev->chandef, wdev->iftype);
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_ADHOC:
+ if (!wdev->current_bss ||
+ !wdev->current_bss->pub.channel)
+ goto out;
+
+ ch = wdev->current_bss->pub.channel;
+ if (rdev->ops->get_channel &&
+ !rdev_get_channel(rdev, wdev, &chandef))
+ ret = cfg80211_chandef_usable(wiphy, &chandef,
+ IEEE80211_CHAN_DISABLED);
+ else
+ ret = !(ch->flags & IEEE80211_CHAN_DISABLED);
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ /* no enforcement required */
+ break;
+ default:
+ /* others not implemented for now */
+ WARN_ON(1);
+ break;
+ }
+
+out:
+ wdev_unlock(wdev);
+ return ret;
+}
+
+static void reg_leave_invalid_chans(struct wiphy *wiphy)
+{
+ struct wireless_dev *wdev;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ ASSERT_RTNL();
+
+ list_for_each_entry(wdev, &rdev->wdev_list, list)
+ if (!reg_wdev_chan_valid(wiphy, wdev))
+ cfg80211_leave(rdev, wdev);
+}
+
+static void reg_check_chans_work(struct work_struct *work)
+{
+ struct cfg80211_registered_device *rdev;
+
+ REG_DBG_PRINT("Verifying active interfaces after reg change\n");
+ rtnl_lock();
+
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list)
+ if (!(rdev->wiphy.regulatory_flags &
+ REGULATORY_IGNORE_STALE_KICKOFF))
+ reg_leave_invalid_chans(&rdev->wiphy);
+
+ rtnl_unlock();
+}
+
+static void reg_check_channels(void)
+{
+ /*
+ * Give usermode a chance to do something nicer (move to another
+ * channel, orderly disconnection), before forcing a disconnection.
+ */
+ mod_delayed_work(system_power_efficient_wq,
+ &reg_check_chans,
+ msecs_to_jiffies(REG_ENFORCE_GRACE_MS));
+}
+
static void wiphy_update_regulatory(struct wiphy *wiphy,
enum nl80211_reg_initiator initiator)
{
@@ -1557,6 +1657,8 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
wiphy = &rdev->wiphy;
wiphy_update_regulatory(wiphy, initiator);
}
+
+ reg_check_channels();
}

static void handle_channel_custom(struct wiphy *wiphy,
@@ -1976,8 +2078,10 @@ static void reg_process_hint(struct regulatory_request *reg_request)

/* This is required so that the orig_* parameters are saved */
if (treatment == REG_REQ_ALREADY_SET && wiphy &&
- wiphy->regulatory_flags & REGULATORY_STRICT_REG)
+ wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
wiphy_update_regulatory(wiphy, reg_request->initiator);
+ reg_check_channels();
+ }

return;

@@ -2858,6 +2962,7 @@ void regulatory_exit(void)

cancel_work_sync(&reg_work);
cancel_delayed_work_sync(&reg_timeout);
+ cancel_delayed_work_sync(&reg_check_chans);

/* Lock to suppress warnings */
rtnl_lock();
--
1.9.1



2014-11-27 07:08:19

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH v4 3/4] cfg80211: allow usermode to query wiphy specific regdom

On Wed, Nov 26, 2014 at 11:35 PM, Luis R. Rodriguez <[email protected]> wrote:
> On Wed, Nov 26, 2014 at 10:56:27PM +0200, Arik Nemtsov wrote:
>> If a wiphy-idx is specified, the kernel will return the wiphy specific
>> regdomain, if such exists. Otherwise return the global regdom.
>>
>> When no wiphy-idx is specified, return the global regdomain as well as
>> all wiphy-specific regulatory domains in the system, via a new nested
>> list of attributes.
>
> Very sexy.
>
>> Add a new attribute for each wiphy-specific regdomain, for usermode to
>> identify it as such.
>
> I think I found a couple of minor issues.
>
>> Signed-off-by: Arik Nemtsov <[email protected]>
>> ---
>> Rest assured, I have an iw version that prints all the regdomains. I just
>> need a bit of time to clean it up.
>>
>> include/uapi/linux/nl80211.h | 16 +++++-
>> net/wireless/nl80211.c | 123 +++++++++++++++++++++++++++++++++----------
>> net/wireless/reg.c | 2 +-
>> net/wireless/reg.h | 1 +
>> 4 files changed, 111 insertions(+), 31 deletions(-)
>>
>> diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
>> index 3771e7d..6517210 100644
>> --- a/include/uapi/linux/nl80211.h
>> +++ b/include/uapi/linux/nl80211.h
>> @@ -252,7 +252,9 @@
>> * %NL80211_ATTR_IFINDEX.
>> *
>> * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set
>> - * regulatory domain.
>> + * regulatory domain. If %NL80211_ATTR_WIPHY is specified and the device
>> + * its private regulatory domain will be returned.
>> + * returned, even if it's regulatory is not self-managed.
>
> This is all jumbled up.

I'll fix it.

>
>> +static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
>> +{
>> + const struct ieee80211_regdomain *regdom = NULL;
>> + struct cfg80211_registered_device *rdev;
>> + struct wiphy *wiphy;
>> + struct sk_buff *msg;
>> + struct nlattr *nl_priv_regdoms, *nl_priv_regdom;
>> + void *hdr = NULL;
>> + int i;
>> +
>> + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
>> + if (!msg)
>> + return -ENOBUFS;
>> +
>> + hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
>> + NL80211_CMD_GET_REG);
>> + if (!hdr)
>> + goto put_failure;
>> +
>> + if (info->attrs[NL80211_ATTR_WIPHY]) {
>> + rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
>> + if (IS_ERR(rdev)) {
>> + nlmsg_free(msg);
>> + return PTR_ERR(rdev);
>> + }
>> +
>> + wiphy = &rdev->wiphy;
>> + regdom = get_wiphy_regdom(wiphy);
>> + if (regdom &&
>> + nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
>> + goto nla_put_failure;
>> + }
>
> If the wiphy was used as part of the query aren't we still sending
> all the data?

right. i'll fix it.

>
>> +
>> + if (!regdom) {
>> + if (!cfg80211_regdomain) {
>> + nlmsg_free(msg);
>> + return -EINVAL;
>> + }
>> +
>> + if (reg_last_request_cell_base() &&
>> + nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
>> + NL80211_USER_REG_HINT_CELL_BASE))
>> + goto nla_put_failure;
>> + }
>> +
>> + rcu_read_lock();
>> +
>> + if (!regdom)
>> + regdom = rcu_dereference(cfg80211_regdomain);
>> +
>> + if (nl80211_put_regdom(regdom, msg))
>> + goto nla_put_failure_rcu;
>> +
>> + nl_priv_regdoms = nla_nest_start(msg, NL80211_ATTR_WIPHY_REGDOM_LIST);
>> + if (!nl_priv_regdoms)
>> + goto nla_put_failure_rcu;
>> +
>
> As I read this even if the query had a wiphy specified we'd go
> on providing all the wiphys with their regd.

yea this should be skipped if wiphy is specified.

>
>> + i = 0;
>> + list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
>> + wiphy = &rdev->wiphy;
>> + regdom = get_wiphy_regdom(wiphy);
>> + if (!regdom)
>> + continue;
>> +
>> + nl_priv_regdom = nla_nest_start(msg, i);
>> + if (!nl_priv_regdom)
>> + goto nla_put_failure_rcu;
>> +
>> + if (nl80211_put_regdom(regdom, msg))
>> + goto nla_put_failure_rcu;
>> +
>> + if (nla_put_flag(msg, NL80211_ATTR_WIPHY_PRIV_REG))
>> + goto nla_put_failure_rcu;
>
> We're not checking here if this was a managed wiphy or not, we
> want userspace to be able to tell if a wiphy was managed or not.

that's in the next patch, but i'll reorder the patches as you've suggested.

i'll probably just merge patches 2 and 4, and put 3 before them.

Arik

2014-11-26 20:56:32

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v4 3/4] cfg80211: allow usermode to query wiphy specific regdom

If a wiphy-idx is specified, the kernel will return the wiphy specific
regdomain, if such exists. Otherwise return the global regdom.

When no wiphy-idx is specified, return the global regdomain as well as
all wiphy-specific regulatory domains in the system, via a new nested
list of attributes.

Add a new attribute for each wiphy-specific regdomain, for usermode to
identify it as such.

Signed-off-by: Arik Nemtsov <[email protected]>
---
Rest assured, I have an iw version that prints all the regdomains. I just
need a bit of time to clean it up.

include/uapi/linux/nl80211.h | 16 +++++-
net/wireless/nl80211.c | 123 +++++++++++++++++++++++++++++++++----------
net/wireless/reg.c | 2 +-
net/wireless/reg.h | 1 +
4 files changed, 111 insertions(+), 31 deletions(-)

diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 3771e7d..6517210 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -252,7 +252,9 @@
* %NL80211_ATTR_IFINDEX.
*
* @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set
- * regulatory domain.
+ * regulatory domain. If %NL80211_ATTR_WIPHY is specified and the device
+ * its private regulatory domain will be returned.
+ * returned, even if it's regulatory is not self-managed.
* @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command
* after being queried by the kernel. CRDA replies by sending a regulatory
* domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our
@@ -1693,6 +1695,14 @@ enum nl80211_commands {
*
* @NL80211_ATTR_MAC_MASK: MAC address mask
*
+ * @NL80211_ATTR_WIPHY_PRIV_REG: flag attribute indicating the regulatory
+ * information was obtained from the device's wiphy. This can happen
+ * when the driver uses the regulatory_hint() API for setting the device's
+ * regulatory domain.
+ *
+ * @NL80211_ATTR_WIPHY_REGDOM_LIST: Nested set of attributes containing
+ * a list of wiphy specific regdomains.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2050,6 +2060,10 @@ enum nl80211_attrs {

NL80211_ATTR_MAC_MASK,

+ NL80211_ATTR_WIPHY_PRIV_REG,
+
+ NL80211_ATTR_WIPHY_REGDOM_LIST,
+
/* add attributes here, update the policy in nl80211.c */

__NL80211_ATTR_AFTER_LAST,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index f1e2f59..65bab11 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -396,6 +396,8 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 },
[NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
[NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN },
+ [NL80211_ATTR_WIPHY_PRIV_REG] = { .type = NLA_FLAG },
+ [NL80211_ATTR_WIPHY_REGDOM_LIST] = { .type = NLA_NESTED },
};

/* policy for the key attributes */
@@ -5326,42 +5328,20 @@ static int nl80211_update_mesh_config(struct sk_buff *skb,
return err;
}

-static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_put_regdom(const struct ieee80211_regdomain *regdom,
+ struct sk_buff *msg)
{
- const struct ieee80211_regdomain *regdom;
- struct sk_buff *msg;
- void *hdr = NULL;
struct nlattr *nl_reg_rules;
unsigned int i;

- if (!cfg80211_regdomain)
- return -EINVAL;
-
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (!msg)
- return -ENOBUFS;
-
- hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
- NL80211_CMD_GET_REG);
- if (!hdr)
- goto put_failure;
-
- if (reg_last_request_cell_base() &&
- nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
- NL80211_USER_REG_HINT_CELL_BASE))
- goto nla_put_failure;
-
- rcu_read_lock();
- regdom = rcu_dereference(cfg80211_regdomain);
-
if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) ||
(regdom->dfs_region &&
nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region)))
- goto nla_put_failure_rcu;
+ goto nla_put_failure;

nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
if (!nl_reg_rules)
- goto nla_put_failure_rcu;
+ goto nla_put_failure;

for (i = 0; i < regdom->n_reg_rules; i++) {
struct nlattr *nl_reg_rule;
@@ -5376,7 +5356,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)

nl_reg_rule = nla_nest_start(msg, i);
if (!nl_reg_rule)
- goto nla_put_failure_rcu;
+ goto nla_put_failure;

max_bandwidth_khz = freq_range->max_bandwidth_khz;
if (!max_bandwidth_khz)
@@ -5397,13 +5377,98 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
power_rule->max_eirp) ||
nla_put_u32(msg, NL80211_ATTR_DFS_CAC_TIME,
reg_rule->dfs_cac_ms))
- goto nla_put_failure_rcu;
+ goto nla_put_failure;

nla_nest_end(msg, nl_reg_rule);
}
- rcu_read_unlock();

nla_nest_end(msg, nl_reg_rules);
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
+{
+ const struct ieee80211_regdomain *regdom = NULL;
+ struct cfg80211_registered_device *rdev;
+ struct wiphy *wiphy;
+ struct sk_buff *msg;
+ struct nlattr *nl_priv_regdoms, *nl_priv_regdom;
+ void *hdr = NULL;
+ int i;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOBUFS;
+
+ hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
+ NL80211_CMD_GET_REG);
+ if (!hdr)
+ goto put_failure;
+
+ if (info->attrs[NL80211_ATTR_WIPHY]) {
+ rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
+ if (IS_ERR(rdev)) {
+ nlmsg_free(msg);
+ return PTR_ERR(rdev);
+ }
+
+ wiphy = &rdev->wiphy;
+ regdom = get_wiphy_regdom(wiphy);
+ if (regdom &&
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
+ goto nla_put_failure;
+ }
+
+ if (!regdom) {
+ if (!cfg80211_regdomain) {
+ nlmsg_free(msg);
+ return -EINVAL;
+ }
+
+ if (reg_last_request_cell_base() &&
+ nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
+ NL80211_USER_REG_HINT_CELL_BASE))
+ goto nla_put_failure;
+ }
+
+ rcu_read_lock();
+
+ if (!regdom)
+ regdom = rcu_dereference(cfg80211_regdomain);
+
+ if (nl80211_put_regdom(regdom, msg))
+ goto nla_put_failure_rcu;
+
+ nl_priv_regdoms = nla_nest_start(msg, NL80211_ATTR_WIPHY_REGDOM_LIST);
+ if (!nl_priv_regdoms)
+ goto nla_put_failure_rcu;
+
+ i = 0;
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+ wiphy = &rdev->wiphy;
+ regdom = get_wiphy_regdom(wiphy);
+ if (!regdom)
+ continue;
+
+ nl_priv_regdom = nla_nest_start(msg, i);
+ if (!nl_priv_regdom)
+ goto nla_put_failure_rcu;
+
+ if (nl80211_put_regdom(regdom, msg))
+ goto nla_put_failure_rcu;
+
+ if (nla_put_flag(msg, NL80211_ATTR_WIPHY_PRIV_REG))
+ goto nla_put_failure_rcu;
+
+ nla_nest_end(msg, nl_priv_regdom);
+ i++;
+ }
+ nla_nest_end(msg, nl_priv_regdoms);
+
+ rcu_read_unlock();

genlmsg_end(msg, hdr);
return genlmsg_reply(msg, info);
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index b23d513..41badc4 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -142,7 +142,7 @@ static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
return rtnl_dereference(cfg80211_regdomain);
}

-static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
+const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
{
return rtnl_dereference(wiphy->regd);
}
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index 5e48031..4b45d6e 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -38,6 +38,7 @@ unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
const struct ieee80211_reg_rule *rule);

bool reg_last_request_cell_base(void);
+const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy);

/**
* regulatory_hint_found_beacon - hints a beacon was found on a channel
--
1.9.1


2014-11-26 20:56:34

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v4 4/4] cfg80211: return private regdom for self-managed devices

If a device has self-managed regulatory, insist on returning the wiphy
specific regdomain if a wiphy-idx is specified. The global regdomain is
meaningless for such devices.

Also add an attribute for self-managed devices, so usermode can distinguish
them as such.

Signed-off-by: Arik Nemtsov <[email protected]>
---
include/uapi/linux/nl80211.h | 7 +++++++
net/wireless/nl80211.c | 31 ++++++++++++++++++++++++++++---
2 files changed, 35 insertions(+), 3 deletions(-)

diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 6517210..00a00ca 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1703,6 +1703,11 @@ enum nl80211_commands {
* @NL80211_ATTR_WIPHY_REGDOM_LIST: Nested set of attributes containing
* a list of wiphy specific regdomains.
*
+ * @NL80211_ATTR_WIPHY_SELF_MANAGED_REG: flag attribute indicating this device
+ * is self-managing its regulatory information and any regulatory domain
+ * obtained from it is coming from the device's wiphy and not the global
+ * cfg80211 regdomain.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2064,6 +2069,8 @@ enum nl80211_attrs {

NL80211_ATTR_WIPHY_REGDOM_LIST,

+ NL80211_ATTR_WIPHY_SELF_MANAGED_REG,
+
/* add attributes here, update the policy in nl80211.c */

__NL80211_ATTR_AFTER_LAST,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 65bab11..2a7f051 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -398,6 +398,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN },
[NL80211_ATTR_WIPHY_PRIV_REG] = { .type = NLA_FLAG },
[NL80211_ATTR_WIPHY_REGDOM_LIST] = { .type = NLA_NESTED },
+ [NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG },
};

/* policy for the key attributes */
@@ -5409,6 +5410,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
goto put_failure;

if (info->attrs[NL80211_ATTR_WIPHY]) {
+ bool self_managed;
rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
if (IS_ERR(rdev)) {
nlmsg_free(msg);
@@ -5416,10 +5418,22 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
}

wiphy = &rdev->wiphy;
+ self_managed = wiphy->regulatory_flags &
+ REGULATORY_WIPHY_SELF_MANAGED;
regdom = get_wiphy_regdom(wiphy);
if (regdom &&
nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
goto nla_put_failure;
+
+ /* a self-managed-reg device must have a private regdom */
+ if (WARN_ON(!regdom && self_managed)) {
+ nlmsg_free(msg);
+ return -EINVAL;
+ }
+
+ if (self_managed &&
+ nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
+ goto nla_put_failure;
}

if (!regdom) {
@@ -5463,6 +5477,10 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
if (nla_put_flag(msg, NL80211_ATTR_WIPHY_PRIV_REG))
goto nla_put_failure_rcu;

+ if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+ nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
+ goto nla_put_failure_rcu;
+
nla_nest_end(msg, nl_priv_regdom);
i++;
}
@@ -11031,9 +11049,16 @@ static bool nl80211_reg_change_event_fill(struct sk_buff *msg,
goto nla_put_failure;
}

- if (request->wiphy_idx != WIPHY_IDX_INVALID &&
- nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
- goto nla_put_failure;
+ if (request->wiphy_idx != WIPHY_IDX_INVALID) {
+ struct wiphy *wiphy;
+
+ wiphy = wiphy_idx_to_wiphy(request->wiphy_idx);
+ if (wiphy &&
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx) &&
+ wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+ nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
+ goto nla_put_failure;
+ }

return true;

--
1.9.1


2014-11-28 07:31:31

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH v4 3/4] cfg80211: allow usermode to query wiphy specific regdom

On Thu, Nov 27, 2014 at 5:35 PM, Luis R. Rodriguez <[email protected]> wrote:
> On Thu, Nov 27, 2014 at 09:12:11AM +0200, Arik Nemtsov wrote:
>> On Wed, Nov 26, 2014 at 11:43 PM, Luis R. Rodriguez <[email protected]> wrote:
>> > On Wed, Nov 26, 2014 at 4:35 PM, Luis R. Rodriguez <[email protected]> wrote:
>> >> We're not checking here if this was a managed wiphy or not, we
>> >> want userspace to be able to tell if a wiphy was managed or not.
>> >
>> > Ah I see what you did, yes that is what I wanted but since you allowed
>> > for self managed regdomains *first* the placement of this patch in
>> > terms of order would mean we are not giving userspace information it
>> > should have needed right after this commit. After your 4th patch we do
>> > though. We want commits to work well linearly in history so can you
>> > move this patch to number 2 in the series and bump number 2 to be
>> > patch number 3? That way right after commit 2 we are not lying to
>> > userspace.
>> >
>> > The question about having userspace specify the wiphy still stands as
>> > I'd expect userspace asking only for a regd for a wiphy would want
>> > only that one, not all, or the central one.
>>
>> In general, we want to return only the regdom of the wiphy the user
>> asked for. And yea, I'll fix it.
>>
>> But there's a tricky part here. In the not-self-managed case, if a
>> user specifies a wiphy-idx, and there's no wiphy->regd, we'll return
>> the global one.
>
> Hm, that's fine if its well understood by userspace that even if
> a wiphy has a specific regd (priv one) that the central one is the one that is
> used for it anyway, the wiphy regd would just be informational. It may be
> a bit odd to give the central one if the wiphy-idx has no regd in this
> case then, so just think about this a bit, wihch is why I am a bit
> inclined to just have cfg80211 not return any regd if the wiphy has
> no priv or self-manged regd.
>
>> This is fine, since in effect, the global regd is the one used by this
>> wiphy anyway.
>> Essentially we want this part to just be able to add the wiphy-idx to
>> wpa_s code for GET_REG, and remain backward compatible. But like I
>> said above, it's the right thing to do
>
> Sure, you have a handle of what is needed, so just make the API very
> clear to users so they don't think that if cfg80211 returns a regd
> for a wiphy if its a priv regd that its not the one that applies
> but rather informational for what it started with.

Do you see a problem with v5 of the patches?

Arik

2014-11-26 20:56:30

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v4 2/4] cfg80211: allow wiphy specific regdomain management

From: Jonathan Doron <[email protected]>

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 <[email protected]>
Signed-off-by: Arik Nemtsov <[email protected]>
---
include/net/cfg80211.h | 20 +++++++++++
include/net/regulatory.h | 19 ++++++++++
include/uapi/linux/nl80211.h | 5 +++
net/wireless/core.c | 8 +++++
net/wireless/nl80211.c | 80 +++++++++++++++++++++++++++++++----------
net/wireless/nl80211.h | 1 +
net/wireless/reg.c | 84 ++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 199 insertions(+), 18 deletions(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index bb748c4..60f46f6 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -2971,6 +2971,10 @@ struct wiphy_vendor_command {
* the regulatory_hint() API. This can be used by the driver
* on the reg_notifier() if it chooses to ignore future
* regulatory domain changes caused by other drivers.
+ * @requested_regd: 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
* @signal_type: signal type reported in &struct cfg80211_bss.
* @cipher_suites: supported cipher suites
* @n_cipher_suites: number of supported cipher suites
@@ -3183,6 +3187,8 @@ struct wiphy {

const struct ieee80211_regdomain __rcu *regd;

+ const struct ieee80211_regdomain *requested_regd;
+
/* the item in /sys/class/ieee80211/ points to this,
* you need use set_wiphy_dev() (see below) */
struct device dev;
@@ -3808,6 +3814,20 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type,
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: the wireless device we want to process the regulatory domain on
* @regd: the custom regulatory domain to use for this wiphy
diff --git a/include/net/regulatory.h b/include/net/regulatory.h
index b776d72..d72b9a3 100644
--- a/include/net/regulatory.h
+++ b/include/net/regulatory.h
@@ -147,6 +147,24 @@ struct regulatory_request {
* NL80211_IFTYPE_P2P_CLIENT, NL80211_IFTYPE_P2P_GO,
* NL80211_IFTYPE_P2P_DEVICE. The flag will be set by default if a device
* 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.
*/
enum ieee80211_regulatory_flags {
REGULATORY_CUSTOM_REG = BIT(0),
@@ -156,6 +174,7 @@ enum ieee80211_regulatory_flags {
REGULATORY_COUNTRY_IE_IGNORE = BIT(4),
REGULATORY_ENABLE_RELAX_NO_IR = BIT(5),
REGULATORY_IGNORE_STALE_KICKOFF = BIT(6),
+ REGULATORY_WIPHY_SELF_MANAGED = BIT(7),
};

struct ieee80211_freq_range {
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index d775245..3771e7d 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -774,6 +774,9 @@
* peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel
* when this command completes.
*
+ * @NL80211_CMD_WIPHY_REG_CHANGE: Similar to %NL80211_CMD_REG_CHANGE, but used
+ * for indicating changes for devices with wiphy-specific regdom management
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -958,6 +961,8 @@ enum nl80211_commands {
NL80211_CMD_TDLS_CHANNEL_SWITCH,
NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,

+ NL80211_CMD_WIPHY_REG_CHANGE,
+
/* add new commands above here */

/* used to define NL80211_CMD_MAX below */
diff --git a/net/wireless/core.c b/net/wireless/core.c
index ef0d3a0..1f48d2d 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -557,6 +557,14 @@ int wiphy_register(struct wiphy *wiphy)
BIT(NL80211_IFTYPE_MONITOR)))
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 |
+ REGULATORY_DISABLE_BEACON_HINTS))))
+ return -EINVAL;
+
if (WARN_ON(wiphy->coalesce &&
(!wiphy->coalesce->n_rules ||
!wiphy->coalesce->n_patterns) &&
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index b5e3c48..f1e2f59 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -10938,25 +10938,9 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
NL80211_MCGRP_SCAN, GFP_KERNEL);
}

-/*
- * This can happen on global regulatory changes or device specific settings
- * based on custom world regulatory domains.
- */
-void nl80211_send_reg_change_event(struct regulatory_request *request)
+static bool nl80211_reg_change_event_fill(struct sk_buff *msg,
+ 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 */
if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator))
goto nla_put_failure;
@@ -10986,6 +10970,66 @@ void nl80211_send_reg_change_event(struct regulatory_request *request)
nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
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 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;
+ }
+
+ if (nl80211_reg_change_event_fill(msg, request) == false)
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ rcu_read_lock();
+ genlmsg_multicast_allns(&nl80211_fam, msg, 0,
+ NL80211_MCGRP_REGULATORY, GFP_ATOMIC);
+ rcu_read_unlock();
+
+ return;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void nl80211_send_wiphy_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_WIPHY_REG_CHANGE);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nl80211_reg_change_event_fill(msg, request) == false)
+ goto nla_put_failure;
+
genlmsg_end(msg, hdr);

rcu_read_lock();
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 7ad70d6..b91b9c5 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -18,6 +18,7 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
struct net_device *netdev);
void nl80211_send_reg_change_event(struct regulatory_request *request);
+void nl80211_send_wiphy_reg_change_event(struct regulatory_request *request);
void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
const u8 *buf, size_t len, gfp_t gfp);
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 47be616..b23d513 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -1307,6 +1307,9 @@ static bool ignore_reg_update(struct wiphy *wiphy,
{
struct regulatory_request *lr = get_last_request();

+ if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
+ return true;
+
if (!lr) {
REG_DBG_PRINT("Ignoring regulatory request set by %s "
"since last_request is not set\n",
@@ -1370,6 +1373,9 @@ static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx,
sband = wiphy->bands[reg_beacon->chan.band];
chan = &sband->channels[chan_idx];

+ if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
+ return;
+
if (likely(chan->center_freq != reg_beacon->chan.center_freq))
return;

@@ -2147,11 +2153,52 @@ static void reg_process_pending_beacon_hints(void)
spin_unlock_bh(&reg_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(&reg_requests_lock);
+ regd = wiphy->requested_regd;
+ wiphy->requested_regd = NULL;
+ spin_unlock(&reg_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)
{
rtnl_lock();
reg_process_pending_hints();
reg_process_pending_beacon_hints();
+ reg_process_self_managed_hints();
rtnl_unlock();
}

@@ -2432,6 +2479,9 @@ static void restore_regulatory_settings(bool reset_user)
world_alpha2[1] = cfg80211_world_regdom->alpha2[1];

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)
restore_custom_reg_settings(&rdev->wiphy);
}
@@ -2835,6 +2885,40 @@ int set_regdom(const struct ieee80211_regdomain *rd)
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;
+
+ 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);
+
+ spin_lock(&reg_requests_lock);
+ prev_regd = wiphy->requested_regd;
+ wiphy->requested_regd = regd;
+ spin_unlock(&reg_requests_lock);
+
+ kfree(prev_regd);
+
+ schedule_work(&reg_work);
+ return 0;
+}
+EXPORT_SYMBOL(regulatory_set_wiphy_regd);
+
void wiphy_regulatory_register(struct wiphy *wiphy)
{
struct regulatory_request *lr;
--
1.9.1


2014-11-27 07:12:28

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH v4 3/4] cfg80211: allow usermode to query wiphy specific regdom

On Wed, Nov 26, 2014 at 11:43 PM, Luis R. Rodriguez <[email protected]> wrote:
> On Wed, Nov 26, 2014 at 4:35 PM, Luis R. Rodriguez <[email protected]> wrote:
>> We're not checking here if this was a managed wiphy or not, we
>> want userspace to be able to tell if a wiphy was managed or not.
>
> Ah I see what you did, yes that is what I wanted but since you allowed
> for self managed regdomains *first* the placement of this patch in
> terms of order would mean we are not giving userspace information it
> should have needed right after this commit. After your 4th patch we do
> though. We want commits to work well linearly in history so can you
> move this patch to number 2 in the series and bump number 2 to be
> patch number 3? That way right after commit 2 we are not lying to
> userspace.
>
> The question about having userspace specify the wiphy still stands as
> I'd expect userspace asking only for a regd for a wiphy would want
> only that one, not all, or the central one.

In general, we want to return only the regdom of the wiphy the user
asked for. And yea, I'll fix it.

But there's a tricky part here. In the not-self-managed case, if a
user specifies a wiphy-idx, and there's no wiphy->regd, we'll return
the global one.
This is fine, since in effect, the global regd is the one used by this
wiphy anyway.
Essentially we want this part to just be able to add the wiphy-idx to
wpa_s code for GET_REG, and remain backward compatible. But like I
said above, it's the right thing to do.

Arik

2014-11-26 21:35:34

by Luis Chamberlain

[permalink] [raw]
Subject: Re: [PATCH v4 3/4] cfg80211: allow usermode to query wiphy specific regdom

On Wed, Nov 26, 2014 at 10:56:27PM +0200, Arik Nemtsov wrote:
> If a wiphy-idx is specified, the kernel will return the wiphy specific
> regdomain, if such exists. Otherwise return the global regdom.
>
> When no wiphy-idx is specified, return the global regdomain as well as
> all wiphy-specific regulatory domains in the system, via a new nested
> list of attributes.

Very sexy.

> Add a new attribute for each wiphy-specific regdomain, for usermode to
> identify it as such.

I think I found a couple of minor issues.

> Signed-off-by: Arik Nemtsov <[email protected]>
> ---
> Rest assured, I have an iw version that prints all the regdomains. I just
> need a bit of time to clean it up.
>
> include/uapi/linux/nl80211.h | 16 +++++-
> net/wireless/nl80211.c | 123 +++++++++++++++++++++++++++++++++----------
> net/wireless/reg.c | 2 +-
> net/wireless/reg.h | 1 +
> 4 files changed, 111 insertions(+), 31 deletions(-)
>
> diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
> index 3771e7d..6517210 100644
> --- a/include/uapi/linux/nl80211.h
> +++ b/include/uapi/linux/nl80211.h
> @@ -252,7 +252,9 @@
> * %NL80211_ATTR_IFINDEX.
> *
> * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set
> - * regulatory domain.
> + * regulatory domain. If %NL80211_ATTR_WIPHY is specified and the device
> + * its private regulatory domain will be returned.
> + * returned, even if it's regulatory is not self-managed.

This is all jumbled up.

> +static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
> +{
> + const struct ieee80211_regdomain *regdom = NULL;
> + struct cfg80211_registered_device *rdev;
> + struct wiphy *wiphy;
> + struct sk_buff *msg;
> + struct nlattr *nl_priv_regdoms, *nl_priv_regdom;
> + void *hdr = NULL;
> + int i;
> +
> + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> + if (!msg)
> + return -ENOBUFS;
> +
> + hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
> + NL80211_CMD_GET_REG);
> + if (!hdr)
> + goto put_failure;
> +
> + if (info->attrs[NL80211_ATTR_WIPHY]) {
> + rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
> + if (IS_ERR(rdev)) {
> + nlmsg_free(msg);
> + return PTR_ERR(rdev);
> + }
> +
> + wiphy = &rdev->wiphy;
> + regdom = get_wiphy_regdom(wiphy);
> + if (regdom &&
> + nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
> + goto nla_put_failure;
> + }

If the wiphy was used as part of the query aren't we still sending
all the data?

> +
> + if (!regdom) {
> + if (!cfg80211_regdomain) {
> + nlmsg_free(msg);
> + return -EINVAL;
> + }
> +
> + if (reg_last_request_cell_base() &&
> + nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
> + NL80211_USER_REG_HINT_CELL_BASE))
> + goto nla_put_failure;
> + }
> +
> + rcu_read_lock();
> +
> + if (!regdom)
> + regdom = rcu_dereference(cfg80211_regdomain);
> +
> + if (nl80211_put_regdom(regdom, msg))
> + goto nla_put_failure_rcu;
> +
> + nl_priv_regdoms = nla_nest_start(msg, NL80211_ATTR_WIPHY_REGDOM_LIST);
> + if (!nl_priv_regdoms)
> + goto nla_put_failure_rcu;
> +

As I read this even if the query had a wiphy specified we'd go
on providing all the wiphys with their regd.

> + i = 0;
> + list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
> + wiphy = &rdev->wiphy;
> + regdom = get_wiphy_regdom(wiphy);
> + if (!regdom)
> + continue;
> +
> + nl_priv_regdom = nla_nest_start(msg, i);
> + if (!nl_priv_regdom)
> + goto nla_put_failure_rcu;
> +
> + if (nl80211_put_regdom(regdom, msg))
> + goto nla_put_failure_rcu;
> +
> + if (nla_put_flag(msg, NL80211_ATTR_WIPHY_PRIV_REG))
> + goto nla_put_failure_rcu;

We're not checking here if this was a managed wiphy or not, we
want userspace to be able to tell if a wiphy was managed or not.

Luis

2014-11-26 21:21:16

by Luis Chamberlain

[permalink] [raw]
Subject: Re: [PATCH v4 2/4] cfg80211: allow wiphy specific regdomain management

On Wed, Nov 26, 2014 at 10:56:26PM +0200, Arik Nemtsov wrote:
> From: Jonathan Doron <[email protected]>
>
> 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 <[email protected]>
> Signed-off-by: Arik Nemtsov <[email protected]>

Reviewed-by: Luis R. Rodriguez <[email protected]>

Luis

2014-11-26 21:43:55

by Luis Chamberlain

[permalink] [raw]
Subject: Re: [PATCH v4 3/4] cfg80211: allow usermode to query wiphy specific regdom

On Wed, Nov 26, 2014 at 4:35 PM, Luis R. Rodriguez <[email protected]> wrote:
> We're not checking here if this was a managed wiphy or not, we
> want userspace to be able to tell if a wiphy was managed or not.

Ah I see what you did, yes that is what I wanted but since you allowed
for self managed regdomains *first* the placement of this patch in
terms of order would mean we are not giving userspace information it
should have needed right after this commit. After your 4th patch we do
though. We want commits to work well linearly in history so can you
move this patch to number 2 in the series and bump number 2 to be
patch number 3? That way right after commit 2 we are not lying to
userspace.

The question about having userspace specify the wiphy still stands as
I'd expect userspace asking only for a regd for a wiphy would want
only that one, not all, or the central one.

Luis

2014-11-26 21:13:33

by Luis Chamberlain

[permalink] [raw]
Subject: Re: [PATCH v4 1/4] cfg80211: leave invalid channels on regdomain change

On Wed, Nov 26, 2014 at 10:56:25PM +0200, Arik Nemtsov wrote:
> When the regulatory settings change, some channels might become invalid.
> Disconnect interfaces acting on these channels, after giving userspace
> code a grace period to leave them.
>
> This mode is currently opt-in, and not all interface operating modes are
> supported for regulatory-enforcement checks. A wiphy that wishes to use
> the new enforcement code must specify an appropriate regulatory flag,
> and all its supported interface modes must be supported by the chekcing
> code.

Reviewed-by: Luis R. Rodriguez <[email protected]>

Luis

2014-11-27 15:35:30

by Luis Chamberlain

[permalink] [raw]
Subject: Re: [PATCH v4 3/4] cfg80211: allow usermode to query wiphy specific regdom

On Thu, Nov 27, 2014 at 09:12:11AM +0200, Arik Nemtsov wrote:
> On Wed, Nov 26, 2014 at 11:43 PM, Luis R. Rodriguez <[email protected]> wrote:
> > On Wed, Nov 26, 2014 at 4:35 PM, Luis R. Rodriguez <[email protected]> wrote:
> >> We're not checking here if this was a managed wiphy or not, we
> >> want userspace to be able to tell if a wiphy was managed or not.
> >
> > Ah I see what you did, yes that is what I wanted but since you allowed
> > for self managed regdomains *first* the placement of this patch in
> > terms of order would mean we are not giving userspace information it
> > should have needed right after this commit. After your 4th patch we do
> > though. We want commits to work well linearly in history so can you
> > move this patch to number 2 in the series and bump number 2 to be
> > patch number 3? That way right after commit 2 we are not lying to
> > userspace.
> >
> > The question about having userspace specify the wiphy still stands as
> > I'd expect userspace asking only for a regd for a wiphy would want
> > only that one, not all, or the central one.
>
> In general, we want to return only the regdom of the wiphy the user
> asked for. And yea, I'll fix it.
>
> But there's a tricky part here. In the not-self-managed case, if a
> user specifies a wiphy-idx, and there's no wiphy->regd, we'll return
> the global one.

Hm, that's fine if its well understood by userspace that even if
a wiphy has a specific regd (priv one) that the central one is the one that is
used for it anyway, the wiphy regd would just be informational. It may be
a bit odd to give the central one if the wiphy-idx has no regd in this
case then, so just think about this a bit, wihch is why I am a bit
inclined to just have cfg80211 not return any regd if the wiphy has
no priv or self-manged regd.

> This is fine, since in effect, the global regd is the one used by this
> wiphy anyway.
> Essentially we want this part to just be able to add the wiphy-idx to
> wpa_s code for GET_REG, and remain backward compatible. But like I
> said above, it's the right thing to do

Sure, you have a handle of what is needed, so just make the API very
clear to users so they don't think that if cfg80211 returns a regd
for a wiphy if its a priv regd that its not the one that applies
but rather informational for what it started with.

Luis