Skip to content

Commit

Permalink
cfg80211: support reloading regulatory database
Browse files Browse the repository at this point in the history
If the regulatory database is loaded, and then updated, it may
be necessary to reload it. Add an nl80211 command to do this.

Note that this just reloads the database, it doesn't re-apply
the rules from it immediately.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  • Loading branch information
Johannes Berg committed Oct 11, 2017
1 parent 007f6c5 commit 1ea4ff3
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 20 deletions.
4 changes: 4 additions & 0 deletions include/uapi/linux/nl80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,8 @@
* &NL80211_CMD_CONNECT or &NL80211_CMD_ROAM. If the 4 way handshake failed
* &NL80211_CMD_DISCONNECT should be indicated instead.
*
* @NL80211_CMD_RELOAD_REGDB: Request that the regdb firmware file is reloaded.
*
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
Expand Down Expand Up @@ -1194,6 +1196,8 @@ enum nl80211_commands {

NL80211_CMD_PORT_AUTHORIZED,

NL80211_CMD_RELOAD_REGDB,

/* add new commands above here */

/* used to define NL80211_CMD_MAX below */
Expand Down
11 changes: 11 additions & 0 deletions net/wireless/nl80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -5678,6 +5678,11 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
}
}

static int nl80211_reload_regdb(struct sk_buff *skb, struct genl_info *info)
{
return reg_reload_regdb();
}

static int nl80211_get_mesh_config(struct sk_buff *skb,
struct genl_info *info)
{
Expand Down Expand Up @@ -12708,6 +12713,12 @@ static const struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NL80211_CMD_RELOAD_REGDB,
.doit = nl80211_reload_regdb,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NL80211_CMD_GET_MESH_CONFIG,
.doit = nl80211_get_mesh_config,
Expand Down
80 changes: 60 additions & 20 deletions net/wireless/reg.c
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,8 @@ static int query_regdb(const char *alpha2)
const struct fwdb_header *hdr = regdb;
const struct fwdb_country *country;

ASSERT_RTNL();

if (IS_ERR(regdb))
return PTR_ERR(regdb);

Expand All @@ -796,41 +798,47 @@ static int query_regdb(const char *alpha2)

static void regdb_fw_cb(const struct firmware *fw, void *context)
{
int set_error = 0;
bool restore = true;
void *db;

if (!fw) {
pr_info("failed to load regulatory.db\n");
regdb = ERR_PTR(-ENODATA);
goto restore;
}

if (!valid_regdb(fw->data, fw->size)) {
set_error = -ENODATA;
} else if (!valid_regdb(fw->data, fw->size)) {
pr_info("loaded regulatory.db is malformed\n");
release_firmware(fw);
regdb = ERR_PTR(-EINVAL);
goto restore;
set_error = -EINVAL;
}

db = kmemdup(fw->data, fw->size, GFP_KERNEL);
release_firmware(fw);
rtnl_lock();
if (WARN_ON(regdb && !IS_ERR(regdb))) {
/* just restore and free new db */
} else if (set_error) {
regdb = ERR_PTR(set_error);
} else if (fw) {
db = kmemdup(fw->data, fw->size, GFP_KERNEL);
if (db) {
regdb = db;
restore = context && query_regdb(context);
} else {
restore = true;
}
}

if (!db)
goto restore;
regdb = db;
if (restore)
restore_regulatory_settings(true);

if (query_regdb(context))
goto restore;
goto free;
restore:
rtnl_lock();
restore_regulatory_settings(true);
rtnl_unlock();
free:

kfree(context);

release_firmware(fw);
}

static int query_regdb_file(const char *alpha2)
{
ASSERT_RTNL();

if (regdb)
return query_regdb(alpha2);

Expand All @@ -843,6 +851,38 @@ static int query_regdb_file(const char *alpha2)
(void *)alpha2, regdb_fw_cb);
}

int reg_reload_regdb(void)
{
const struct firmware *fw;
void *db;
int err;

err = request_firmware(&fw, "regulatory.db", &reg_pdev->dev);
if (err)
return err;

if (!valid_regdb(fw->data, fw->size)) {
err = -ENODATA;
goto out;
}

db = kmemdup(fw->data, fw->size, GFP_KERNEL);
if (!db) {
err = -ENOMEM;
goto out;
}

rtnl_lock();
if (!IS_ERR_OR_NULL(regdb))
kfree(regdb);
regdb = db;
rtnl_unlock();

out:
release_firmware(fw);
return err;
}

static bool reg_query_database(struct regulatory_request *request)
{
/* query internal regulatory database (if it exists) */
Expand Down
6 changes: 6 additions & 0 deletions net/wireless/reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,10 @@ void regulatory_propagate_dfs_state(struct wiphy *wiphy,
* @wiphy2 - wiphy it's dfs_region to be checked against that of wiphy1
*/
bool reg_dfs_domain_same(struct wiphy *wiphy1, struct wiphy *wiphy2);

/**
* reg_reload_regdb - reload the regulatory.db firmware file
*/
int reg_reload_regdb(void);

#endif /* __NET_WIRELESS_REG_H */

0 comments on commit 1ea4ff3

Please sign in to comment.