2014-11-27 07:44:58

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v5 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]>
Reviewed-by: Luis R. Rodriguez <[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:45:00

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v5 2/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]>
---
v5: don't return all regdomains if a specific wiphy is requested

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

diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index d775245..1f2f7d6 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
+ * has a private regulatory domain, it will be returned. Otherwise, the
+ * global regdomain will be returned.
* @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
@@ -1688,6 +1690,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
@@ -2045,6 +2055,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 b5e3c48..ddee3ba 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,102 @@ 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;
+
+ /* bail here if a specific wiphy was requested */
+ if (info->attrs[NL80211_ATTR_WIPHY])
+ goto end;
+
+ 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);
+end:
+ 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 47be616..48d90da 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-30 12:27:06

by Arik Nemtsov

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

On Sat, Nov 29, 2014 at 12:00 AM, Luis R. Rodriguez <[email protected]> wrote:
> On Fri, Nov 28, 2014 at 02:46:27PM +0100, Johannes Berg wrote:
>> On Thu, 2014-11-27 at 09:44 +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.
>>
>> Is that really a good idea? Seems rather easy to overrun the message
>> size with that, in which case your current code will not return anything
>> at all... that'll cause strange errors if somebody plugs in a few
>> devices or has hwsim open as well or so ...
>
> Good point, perhaps require 'iw reg get --all' for all listing then?
> This would mean requiring an new optional flag passed on reg get too
> then.

I think Johannes' point was that it's easy to overrun the message size
if there are a lot of wiphys.
So I'll simply add an iterator over all wiphys in "iw reg get" and
kernel-mode will only return a single regdomain in each GET_REG
invocation.

About the "--all" suggestion - I think it's fine to not have backward
compatibility in the output of "iw reg get"? So we can just output the
global first, and then output private regdoms for all wiphys that have
them.

Does that sound ok?

>
>> > Add a new attribute for each wiphy-specific regdomain, for usermode to
>> > identify it as such.
>>
>> Shouldn't userspace also *request* this for backward compatibility?
>> Otherwise older userspace might assume that a returned regd applies to
>> everything, when it doesn't really?
>
> If the flag --all is used and passed then I see no issue.

Well you have to give a wiphy-idx in order to get a private regdom in
the first place. And only new userspace will add a wiphy-idx in the
first place..

Also, when a global regdom is returned for a given wiphy-idx instead
of a private one, it is valid, since the global one is being used for
this wiphy.
If there is a private regdom (from regulatory_hint()) then we'll
return it to usermode. This is less restrictive than the global one,
and that's the one being used for channel verification for this wiphy.

Arik

2014-11-28 13:44:43

by Johannes Berg

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

On Thu, 2014-11-27 at 09:44 +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.

Applied, with some trivial changes.

johannes


2014-11-30 12:32:20

by Johannes Berg

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

On Sun, 2014-11-30 at 14:26 +0200, Arik Nemtsov wrote:

> I think Johannes' point was that it's easy to overrun the message size
> if there are a lot of wiphys.

Yes.

> So I'll simply add an iterator over all wiphys in "iw reg get" and
> kernel-mode will only return a single regdomain in each GET_REG
> invocation.

Err, there's such a thing built into netlink already - just support
dumpit instead of doit :)
iw will have to fall back to doit for older kernels though I guess.

> About the "--all" suggestion - I think it's fine to not have backward
> compatibility in the output of "iw reg get"? So we can just output the
> global first, and then output private regdoms for all wiphys that have
> them.
>
> Does that sound ok?

Yeah I wasn't taking about the iw display, and adding --all there
doesn't help for what I was concerned about.

> Well you have to give a wiphy-idx in order to get a private regdom in
> the first place. And only new userspace will add a wiphy-idx in the
> first place..

Are you sure about the last part though? wpa_supplicant often passed a
netdev index instead of a wiphy index for example, so I could imagine it
passing a wiphy index here even though it was previously ignored?

If it didn't though then I think there's no problem, there shouldn't
really be any userspace other than wpa_s and iw for this I guess/hope.

johannes


2014-11-28 22:00:22

by Luis Chamberlain

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

On Fri, Nov 28, 2014 at 02:46:27PM +0100, Johannes Berg wrote:
> On Thu, 2014-11-27 at 09:44 +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.
>
> Is that really a good idea? Seems rather easy to overrun the message
> size with that, in which case your current code will not return anything
> at all... that'll cause strange errors if somebody plugs in a few
> devices or has hwsim open as well or so ...

Good point, perhaps require 'iw reg get --all' for all listing then?
This would mean requiring an new optional flag passed on reg get too
then.

> > Add a new attribute for each wiphy-specific regdomain, for usermode to
> > identify it as such.
>
> Shouldn't userspace also *request* this for backward compatibility?
> Otherwise older userspace might assume that a returned regd applies to
> everything, when it doesn't really?

If the flag --all is used and passed then I see no issue.

Luis

2014-11-28 13:46:31

by Johannes Berg

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

On Thu, 2014-11-27 at 09:44 +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.

Is that really a good idea? Seems rather easy to overrun the message
size with that, in which case your current code will not return anything
at all... that'll cause strange errors if somebody plugs in a few
devices or has hwsim open as well or so ...

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

Shouldn't userspace also *request* this for backward compatibility?
Otherwise older userspace might assume that a returned regd applies to
everything, when it doesn't really?

johannes


2014-11-28 22:03:27

by Luis Chamberlain

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

On Thu, Nov 27, 2014 at 09:44:56AM +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.
>
> Add a new attribute for each wiphy-specific regdomain, for usermode to
> identify it as such.
>
> Signed-off-by: Arik Nemtsov <[email protected]>
> ---
> v5: don't return all regdomains if a specific wiphy is requested
>
> include/uapi/linux/nl80211.h | 16 +++++-
> net/wireless/nl80211.c | 127 +++++++++++++++++++++++++++++++++----------
> net/wireless/reg.c | 2 +-
> net/wireless/reg.h | 1 +
> 4 files changed, 115 insertions(+), 31 deletions(-)
>
> diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
> index d775245..1f2f7d6 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
> + * has a private regulatory domain, it will be returned. Otherwise, the
> + * global regdomain will be returned.
> * @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
> @@ -1688,6 +1690,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.

Can you clarify here that even if a driver used regulatory_hint() its device
will still have some settings further restricted by consenus with other
regulatory data gathered by cfg80211, the main cfg80211 regulatory domain
is reflective of what the wiphy is really allowed, the wiphy->regd in this
case would be reflective of the regulatory domain that the device originally
wanted.

Other than that I think we need a flag to let nl80211 pass all regdomains,
to address Johannes' concerns.

Luis

2014-11-28 13:57:45

by Johannes Berg

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

On Thu, 2014-11-27 at 09:44 +0200, Arik Nemtsov wrote:
> if (info->attrs[NL80211_ATTR_WIPHY]) {
> + bool self_managed;
> rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);

please add a blank line after variable declarations

johannes


2014-11-27 07:45:03

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v5 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 0143c59..67eb8ff 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 361a897..42bc9ed 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) {
@@ -5467,6 +5481,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++;
}
@@ -11035,9 +11053,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 13:53:35

by Johannes Berg

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

On Thu, 2014-11-27 at 09:44 +0200, Arik Nemtsov wrote:
> @@ -3183,6 +3187,8 @@ struct wiphy {
>
> const struct ieee80211_regdomain __rcu *regd;
>
> + const struct ieee80211_regdomain *requested_regd;

It's passed to the function:

> /**
> + * 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);

so why should it be stored in the publically visible wiphy, rather than
the rdev struct?

> + * @NL80211_CMD_WIPHY_REG_CHANGE: Similar to %NL80211_CMD_REG_CHANGE, but used
> + * for indicating changes for devices with wiphy-specific regdom management

... but used as an event to indicate ... ?


> +++ 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;

I think you could align all of those under each other

wiphy->regulatory_flags & (... |
... |
...

etc to show clearly the nesting of the condition

> +void nl80211_send_reg_change_event(struct regulatory_request *request)
> +void nl80211_send_wiphy_reg_change_event(struct regulatory_request *request)

Seems like you could share more between these functions by just passing
the nl80211 cmd ID as well as the regdomain?

> @@ -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;

Isn't there already a flag - you said in the docs that this flag implied
the "ignore beacons" flag so why not just set that flag?


> @@ -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;

I don't think that's any better than going >80 cols here :)

johannes


2014-11-27 07:45:02

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v5 3/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]>
Reviewed-by: Luis R. Rodriguez <[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 1f2f7d6..0143c59 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -776,6 +776,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
*/
@@ -960,6 +963,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 ddee3ba..361a897 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -11007,25 +11007,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;
@@ -11055,6 +11039,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 48d90da..41badc4 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-30 12:37:15

by Arik Nemtsov

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

On Sun, Nov 30, 2014 at 2:32 PM, Johannes Berg
<[email protected]> wrote:
> On Sun, 2014-11-30 at 14:26 +0200, Arik Nemtsov wrote:
>
>> I think Johannes' point was that it's easy to overrun the message size
>> if there are a lot of wiphys.
>
> Yes.
>
>> So I'll simply add an iterator over all wiphys in "iw reg get" and
>> kernel-mode will only return a single regdomain in each GET_REG
>> invocation.
>
> Err, there's such a thing built into netlink already - just support
> dumpit instead of doit :)

That's cool. I'll use it then :)

> iw will have to fall back to doit for older kernels though I guess.

Well older kernels don't have this feature anyway, so it's fine to
leave the doit as is.
For backports, we are bringing this code with us anyway right?

>
>> About the "--all" suggestion - I think it's fine to not have backward
>> compatibility in the output of "iw reg get"? So we can just output the
>> global first, and then output private regdoms for all wiphys that have
>> them.
>>
>> Does that sound ok?
>
> Yeah I wasn't taking about the iw display, and adding --all there
> doesn't help for what I was concerned about.
>
>> Well you have to give a wiphy-idx in order to get a private regdom in
>> the first place. And only new userspace will add a wiphy-idx in the
>> first place..
>
> Are you sure about the last part though? wpa_supplicant often passed a
> netdev index instead of a wiphy index for example, so I could imagine it
> passing a wiphy index here even though it was previously ignored?
>
> If it didn't though then I think there's no problem, there shouldn't
> really be any userspace other than wpa_s and iw for this I guess/hope.

It definitely doesn't pass it now. Otherwise we wouldn't have to change it :)

Arik