aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArik Nemtsov <arik@wizery.com>2014-12-15 12:25:59 -0500
committerJohannes Berg <johannes.berg@intel.com>2014-12-17 05:49:55 -0500
commitad30ca2c03cecfb1b0749874bdceead269542de6 (patch)
tree72f6782278fd458c5d57b38123c2c8e74ec51eab
parent2ae70efcea7a695a62bb47170d3fb16674b8dbea (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.h10
-rw-r--r--net/wireless/nl80211.c173
-rw-r--r--net/wireless/reg.c2
-rw-r--r--net/wireless/reg.h1
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
5330static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) 5330static 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
5387nla_put_failure:
5388 return -EMSGSIZE;
5389}
5390
5391static 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
5450static 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
5476nla_put_failure:
5477 genlmsg_cancel(msg, hdr);
5478 return -EMSGSIZE;
5479}
5480
5481static 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;
5518out_err:
5519 rtnl_unlock();
5520 return err;
5521}
5522
5421static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) 5523static 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
145static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy) 145const 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
40bool reg_last_request_cell_base(void); 40bool reg_last_request_cell_base(void);
41const 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