diff options
author | Arik Nemtsov <arik@wizery.com> | 2014-12-15 12:25:59 -0500 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2014-12-17 05:49:55 -0500 |
commit | ad30ca2c03cecfb1b0749874bdceead269542de6 (patch) | |
tree | 72f6782278fd458c5d57b38123c2c8e74ec51eab | |
parent | 2ae70efcea7a695a62bb47170d3fb16674b8dbea (diff) |
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 <arikx.nemtsov@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r-- | include/uapi/linux/nl80211.h | 10 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 173 | ||||
-rw-r--r-- | net/wireless/reg.c | 2 | ||||
-rw-r--r-- | net/wireless/reg.h | 1 |
4 files changed, 151 insertions, 35 deletions
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index b37bd5a1cb82..2d384d041224 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h | |||
@@ -252,7 +252,15 @@ | |||
252 | * %NL80211_ATTR_IFINDEX. | 252 | * %NL80211_ATTR_IFINDEX. |
253 | * | 253 | * |
254 | * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set | 254 | * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set |
255 | * regulatory domain. | 255 | * regulatory domain. If %NL80211_ATTR_WIPHY is specified and the device |
256 | * has a private regulatory domain, it will be returned. Otherwise, the | ||
257 | * global regdomain will be returned. | ||
258 | * A device will have a private regulatory domain if it uses the | ||
259 | * regulatory_hint() API. Even when a private regdomain is used the channel | ||
260 | * information will still be mended according to further hints from | ||
261 | * the regulatory core to help with compliance. A dump version of this API | ||
262 | * is now available which will returns the global regdomain as well as | ||
263 | * all private regdomains of present wiphys (for those that have it). | ||
256 | * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command | 264 | * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command |
257 | * after being queried by the kernel. CRDA replies by sending a regulatory | 265 | * after being queried by the kernel. CRDA replies by sending a regulatory |
258 | * domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our | 266 | * domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a17d6bc6b22c..2d5dc428c5ab 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -5327,42 +5327,20 @@ static int nl80211_update_mesh_config(struct sk_buff *skb, | |||
5327 | return err; | 5327 | return err; |
5328 | } | 5328 | } |
5329 | 5329 | ||
5330 | static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) | 5330 | static int nl80211_put_regdom(const struct ieee80211_regdomain *regdom, |
5331 | struct sk_buff *msg) | ||
5331 | { | 5332 | { |
5332 | const struct ieee80211_regdomain *regdom; | ||
5333 | struct sk_buff *msg; | ||
5334 | void *hdr = NULL; | ||
5335 | struct nlattr *nl_reg_rules; | 5333 | struct nlattr *nl_reg_rules; |
5336 | unsigned int i; | 5334 | unsigned int i; |
5337 | 5335 | ||
5338 | if (!cfg80211_regdomain) | ||
5339 | return -EINVAL; | ||
5340 | |||
5341 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
5342 | if (!msg) | ||
5343 | return -ENOBUFS; | ||
5344 | |||
5345 | hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, | ||
5346 | NL80211_CMD_GET_REG); | ||
5347 | if (!hdr) | ||
5348 | goto put_failure; | ||
5349 | |||
5350 | if (reg_last_request_cell_base() && | ||
5351 | nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE, | ||
5352 | NL80211_USER_REG_HINT_CELL_BASE)) | ||
5353 | goto nla_put_failure; | ||
5354 | |||
5355 | rcu_read_lock(); | ||
5356 | regdom = rcu_dereference(cfg80211_regdomain); | ||
5357 | |||
5358 | if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) || | 5336 | if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) || |
5359 | (regdom->dfs_region && | 5337 | (regdom->dfs_region && |
5360 | nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region))) | 5338 | nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region))) |
5361 | goto nla_put_failure_rcu; | 5339 | goto nla_put_failure; |
5362 | 5340 | ||
5363 | nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES); | 5341 | nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES); |
5364 | if (!nl_reg_rules) | 5342 | if (!nl_reg_rules) |
5365 | goto nla_put_failure_rcu; | 5343 | goto nla_put_failure; |
5366 | 5344 | ||
5367 | for (i = 0; i < regdom->n_reg_rules; i++) { | 5345 | for (i = 0; i < regdom->n_reg_rules; i++) { |
5368 | struct nlattr *nl_reg_rule; | 5346 | struct nlattr *nl_reg_rule; |
@@ -5377,7 +5355,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) | |||
5377 | 5355 | ||
5378 | nl_reg_rule = nla_nest_start(msg, i); | 5356 | nl_reg_rule = nla_nest_start(msg, i); |
5379 | if (!nl_reg_rule) | 5357 | if (!nl_reg_rule) |
5380 | goto nla_put_failure_rcu; | 5358 | goto nla_put_failure; |
5381 | 5359 | ||
5382 | max_bandwidth_khz = freq_range->max_bandwidth_khz; | 5360 | max_bandwidth_khz = freq_range->max_bandwidth_khz; |
5383 | if (!max_bandwidth_khz) | 5361 | if (!max_bandwidth_khz) |
@@ -5398,13 +5376,64 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) | |||
5398 | power_rule->max_eirp) || | 5376 | power_rule->max_eirp) || |
5399 | nla_put_u32(msg, NL80211_ATTR_DFS_CAC_TIME, | 5377 | nla_put_u32(msg, NL80211_ATTR_DFS_CAC_TIME, |
5400 | reg_rule->dfs_cac_ms)) | 5378 | reg_rule->dfs_cac_ms)) |
5401 | goto nla_put_failure_rcu; | 5379 | goto nla_put_failure; |
5402 | 5380 | ||
5403 | nla_nest_end(msg, nl_reg_rule); | 5381 | nla_nest_end(msg, nl_reg_rule); |
5404 | } | 5382 | } |
5405 | rcu_read_unlock(); | ||
5406 | 5383 | ||
5407 | nla_nest_end(msg, nl_reg_rules); | 5384 | nla_nest_end(msg, nl_reg_rules); |
5385 | return 0; | ||
5386 | |||
5387 | nla_put_failure: | ||
5388 | return -EMSGSIZE; | ||
5389 | } | ||
5390 | |||
5391 | static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info) | ||
5392 | { | ||
5393 | const struct ieee80211_regdomain *regdom = NULL; | ||
5394 | struct cfg80211_registered_device *rdev; | ||
5395 | struct wiphy *wiphy = NULL; | ||
5396 | struct sk_buff *msg; | ||
5397 | void *hdr; | ||
5398 | |||
5399 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
5400 | if (!msg) | ||
5401 | return -ENOBUFS; | ||
5402 | |||
5403 | hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, | ||
5404 | NL80211_CMD_GET_REG); | ||
5405 | if (!hdr) | ||
5406 | goto put_failure; | ||
5407 | |||
5408 | if (info->attrs[NL80211_ATTR_WIPHY]) { | ||
5409 | rdev = cfg80211_get_dev_from_info(genl_info_net(info), info); | ||
5410 | if (IS_ERR(rdev)) { | ||
5411 | nlmsg_free(msg); | ||
5412 | return PTR_ERR(rdev); | ||
5413 | } | ||
5414 | |||
5415 | wiphy = &rdev->wiphy; | ||
5416 | regdom = get_wiphy_regdom(wiphy); | ||
5417 | |||
5418 | if (regdom && | ||
5419 | nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy))) | ||
5420 | goto nla_put_failure; | ||
5421 | } | ||
5422 | |||
5423 | if (!wiphy && reg_last_request_cell_base() && | ||
5424 | nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE, | ||
5425 | NL80211_USER_REG_HINT_CELL_BASE)) | ||
5426 | goto nla_put_failure; | ||
5427 | |||
5428 | rcu_read_lock(); | ||
5429 | |||
5430 | if (!regdom) | ||
5431 | regdom = rcu_dereference(cfg80211_regdomain); | ||
5432 | |||
5433 | if (nl80211_put_regdom(regdom, msg)) | ||
5434 | goto nla_put_failure_rcu; | ||
5435 | |||
5436 | rcu_read_unlock(); | ||
5408 | 5437 | ||
5409 | genlmsg_end(msg, hdr); | 5438 | genlmsg_end(msg, hdr); |
5410 | return genlmsg_reply(msg, info); | 5439 | return genlmsg_reply(msg, info); |
@@ -5418,6 +5447,79 @@ put_failure: | |||
5418 | return -EMSGSIZE; | 5447 | return -EMSGSIZE; |
5419 | } | 5448 | } |
5420 | 5449 | ||
5450 | static int nl80211_send_regdom(struct sk_buff *msg, struct netlink_callback *cb, | ||
5451 | u32 seq, int flags, struct wiphy *wiphy, | ||
5452 | const struct ieee80211_regdomain *regdom) | ||
5453 | { | ||
5454 | void *hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).portid, seq, flags, | ||
5455 | NL80211_CMD_GET_REG); | ||
5456 | |||
5457 | if (!hdr) | ||
5458 | return -1; | ||
5459 | |||
5460 | genl_dump_check_consistent(cb, hdr, &nl80211_fam); | ||
5461 | |||
5462 | if (nl80211_put_regdom(regdom, msg)) | ||
5463 | goto nla_put_failure; | ||
5464 | |||
5465 | if (!wiphy && reg_last_request_cell_base() && | ||
5466 | nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE, | ||
5467 | NL80211_USER_REG_HINT_CELL_BASE)) | ||
5468 | goto nla_put_failure; | ||
5469 | |||
5470 | if (wiphy && | ||
5471 | nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy))) | ||
5472 | goto nla_put_failure; | ||
5473 | |||
5474 | return genlmsg_end(msg, hdr); | ||
5475 | |||
5476 | nla_put_failure: | ||
5477 | genlmsg_cancel(msg, hdr); | ||
5478 | return -EMSGSIZE; | ||
5479 | } | ||
5480 | |||
5481 | static int nl80211_get_reg_dump(struct sk_buff *skb, | ||
5482 | struct netlink_callback *cb) | ||
5483 | { | ||
5484 | const struct ieee80211_regdomain *regdom = NULL; | ||
5485 | struct cfg80211_registered_device *rdev; | ||
5486 | int err, reg_idx, start = cb->args[2]; | ||
5487 | |||
5488 | rtnl_lock(); | ||
5489 | |||
5490 | if (cfg80211_regdomain && start == 0) { | ||
5491 | err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq, | ||
5492 | NLM_F_MULTI, NULL, | ||
5493 | rtnl_dereference(cfg80211_regdomain)); | ||
5494 | if (err < 0) | ||
5495 | goto out_err; | ||
5496 | } | ||
5497 | |||
5498 | /* the global regdom is idx 0 */ | ||
5499 | reg_idx = 1; | ||
5500 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { | ||
5501 | regdom = get_wiphy_regdom(&rdev->wiphy); | ||
5502 | if (!regdom) | ||
5503 | continue; | ||
5504 | |||
5505 | if (++reg_idx <= start) | ||
5506 | continue; | ||
5507 | |||
5508 | err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq, | ||
5509 | NLM_F_MULTI, &rdev->wiphy, regdom); | ||
5510 | if (err < 0) { | ||
5511 | reg_idx--; | ||
5512 | break; | ||
5513 | } | ||
5514 | } | ||
5515 | |||
5516 | cb->args[2] = reg_idx; | ||
5517 | err = skb->len; | ||
5518 | out_err: | ||
5519 | rtnl_unlock(); | ||
5520 | return err; | ||
5521 | } | ||
5522 | |||
5421 | static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) | 5523 | static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) |
5422 | { | 5524 | { |
5423 | struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; | 5525 | struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; |
@@ -10225,7 +10327,8 @@ static const struct genl_ops nl80211_ops[] = { | |||
10225 | }, | 10327 | }, |
10226 | { | 10328 | { |
10227 | .cmd = NL80211_CMD_GET_REG, | 10329 | .cmd = NL80211_CMD_GET_REG, |
10228 | .doit = nl80211_get_reg, | 10330 | .doit = nl80211_get_reg_do, |
10331 | .dumpit = nl80211_get_reg_dump, | ||
10229 | .policy = nl80211_policy, | 10332 | .policy = nl80211_policy, |
10230 | .internal_flags = NL80211_FLAG_NEED_RTNL, | 10333 | .internal_flags = NL80211_FLAG_NEED_RTNL, |
10231 | /* can be retrieved by unprivileged users */ | 10334 | /* can be retrieved by unprivileged users */ |
@@ -10983,9 +11086,13 @@ void nl80211_send_reg_change_event(struct regulatory_request *request) | |||
10983 | goto nla_put_failure; | 11086 | goto nla_put_failure; |
10984 | } | 11087 | } |
10985 | 11088 | ||
10986 | if (request->wiphy_idx != WIPHY_IDX_INVALID && | 11089 | if (request->wiphy_idx != WIPHY_IDX_INVALID) { |
10987 | nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx)) | 11090 | struct wiphy *wiphy = wiphy_idx_to_wiphy(request->wiphy_idx); |
10988 | goto nla_put_failure; | 11091 | |
11092 | if (wiphy && | ||
11093 | nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx)) | ||
11094 | goto nla_put_failure; | ||
11095 | } | ||
10989 | 11096 | ||
10990 | genlmsg_end(msg, hdr); | 11097 | genlmsg_end(msg, hdr); |
10991 | 11098 | ||
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index d83480b6efde..2d9760084b74 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -142,7 +142,7 @@ static const struct ieee80211_regdomain *get_cfg80211_regdom(void) | |||
142 | return rtnl_dereference(cfg80211_regdomain); | 142 | return rtnl_dereference(cfg80211_regdomain); |
143 | } | 143 | } |
144 | 144 | ||
145 | static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy) | 145 | const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy) |
146 | { | 146 | { |
147 | return rtnl_dereference(wiphy->regd); | 147 | return rtnl_dereference(wiphy->regd); |
148 | } | 148 | } |
diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 5e48031ccb9a..4b45d6e61d24 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, | |||
38 | const struct ieee80211_reg_rule *rule); | 38 | const struct ieee80211_reg_rule *rule); |
39 | 39 | ||
40 | bool reg_last_request_cell_base(void); | 40 | bool reg_last_request_cell_base(void); |
41 | const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy); | ||
41 | 42 | ||
42 | /** | 43 | /** |
43 | * regulatory_hint_found_beacon - hints a beacon was found on a channel | 44 | * regulatory_hint_found_beacon - hints a beacon was found on a channel |