wiphy: Track regulatory domain changes

When a new wiphy is added query its regulatory domain and listen for
nl80211 regulatory notifications to be able to provide current
regulatory country code through the new wiphy_get_reg_domain_country().
This commit is contained in:
Andrew Zaborowski 2020-04-23 18:24:35 +02:00 committed by Denis Kenzior
parent 0f3f0086ae
commit b43e915b98
2 changed files with 119 additions and 0 deletions

View File

@ -52,6 +52,7 @@
#include "src/common.h"
#include "src/watchlist.h"
#include "src/nl80211util.h"
#include "src/nl80211cmd.h"
#define EXT_CAP_LEN 10
@ -60,6 +61,7 @@ static struct l_hwdb *hwdb;
static char **whitelist_filter;
static char **blacklist_filter;
static int mac_randomize_bytes = 6;
static char regdom_country[2];
struct wiphy {
uint32_t id;
@ -81,6 +83,8 @@ struct wiphy {
uint8_t *iftype_extended_capabilities[NUM_NL80211_IFTYPES];
uint8_t *supported_rates[NUM_NL80211_BANDS];
uint8_t rm_enabled_capabilities[7]; /* 5 size max + header */
struct l_genl_family *nl80211;
char regdom_country[2];
bool support_scheduled_scan:1;
bool support_rekey_offload:1;
@ -227,6 +231,7 @@ static void wiphy_free(void *data)
l_free(wiphy->model_str);
l_free(wiphy->vendor_str);
l_free(wiphy->driver_str);
l_genl_family_free(wiphy->nl80211);
l_free(wiphy);
}
@ -562,6 +567,18 @@ const uint8_t *wiphy_get_supported_rates(struct wiphy *wiphy, unsigned int band,
return wiphy->supported_rates[band];
}
void wiphy_get_reg_domain_country(struct wiphy *wiphy, char *out)
{
char *country = wiphy->regdom_country;
if (!country[0])
/* Wiphy uses the global regulatory domain */
country = regdom_country;
out[0] = country[0];
out[1] = country[1];
}
uint32_t wiphy_state_watch_add(struct wiphy *wiphy,
wiphy_state_watch_func_t func,
void *user_data, wiphy_destroy_func_t destroy)
@ -1046,9 +1063,11 @@ static void wiphy_register(struct wiphy *wiphy)
struct wiphy *wiphy_create(uint32_t wiphy_id, const char *name)
{
struct wiphy *wiphy;
struct l_genl *genl = iwd_get_genl();
wiphy = wiphy_new(wiphy_id);
l_strlcpy(wiphy->name, name, sizeof(wiphy->name));
wiphy->nl80211 = l_genl_family_new(genl, NL80211_GENL_NAME);
l_queue_push_head(wiphy_list, wiphy);
if (!wiphy_is_managed(name))
@ -1138,6 +1157,70 @@ static void wiphy_setup_rm_enabled_capabilities(struct wiphy *wiphy)
*/
}
static void wiphy_update_reg_domain(struct wiphy *wiphy, bool global,
struct l_genl_msg *msg)
{
char *out_country;
if (global)
/*
* Leave @wiphy->regdom_country as all zeros to mean that it
* uses the global @regdom_country, i.e. is not self-managed.
*
* Even if we're called because we queried a new wiphy's
* reg domain, use the value we received here to update our
* global @regdom_country in case this is the first opportunity
* we have to update it -- possibly because this is the first
* wiphy created (that is not self-managed anyway) and we
* haven't received any REG_CHANGE events yet.
*/
out_country = regdom_country;
else
out_country = wiphy->regdom_country;
/*
* Write the new country code or XX if the reg domain is not a
* country domain.
*/
if (nl80211_parse_attrs(msg, NL80211_ATTR_REG_ALPHA2, out_country,
NL80211_ATTR_UNSPEC) < 0)
out_country[0] = out_country[1] = 'X';
l_debug("New reg domain country code for %s is %c%c",
global ? "(global)" : wiphy->name,
out_country[0], out_country[1]);
}
static void wiphy_get_reg_cb(struct l_genl_msg *msg, void *user_data)
{
struct wiphy *wiphy = user_data;
uint32_t tmp;
bool global;
/*
* NL80211_CMD_GET_REG contains an NL80211_ATTR_WIPHY iff the wiphy
* uses a self-managed regulatory domain.
*/
global = nl80211_parse_attrs(msg, NL80211_ATTR_WIPHY, &tmp,
NL80211_ATTR_UNSPEC) < 0;
wiphy_update_reg_domain(wiphy, global, msg);
}
static void wiphy_get_reg_domain(struct wiphy *wiphy)
{
struct l_genl_msg *msg;
msg = l_genl_msg_new(NL80211_CMD_GET_REG);
l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY, 4, &wiphy->id);
if (!l_genl_family_send(wiphy->nl80211, msg, wiphy_get_reg_cb, wiphy,
NULL)) {
l_error("Error sending NL80211_CMD_GET_REG for %s", wiphy->name);
l_genl_msg_unref(msg);
}
}
void wiphy_create_complete(struct wiphy *wiphy)
{
wiphy_register(wiphy);
@ -1152,6 +1235,7 @@ void wiphy_create_complete(struct wiphy *wiphy)
wiphy_set_station_capability_bits(wiphy);
wiphy_setup_rm_enabled_capabilities(wiphy);
wiphy_get_reg_domain(wiphy);
wiphy_print_basic_info(wiphy);
}
@ -1338,6 +1422,36 @@ static void setup_wiphy_interface(struct l_dbus_interface *interface)
NULL);
}
static void wiphy_reg_notify(struct l_genl_msg *msg, void *user_data)
{
uint8_t cmd = l_genl_msg_get_command(msg);
l_debug("Notification of command %s(%u)",
nl80211cmd_to_string(cmd), cmd);
switch (cmd) {
case NL80211_CMD_REG_CHANGE:
wiphy_update_reg_domain(NULL, true, msg);
break;
case NL80211_CMD_WIPHY_REG_CHANGE:
{
uint32_t wiphy_id;
struct wiphy *wiphy;
if (nl80211_parse_attrs(msg, NL80211_ATTR_WIPHY, &wiphy_id,
NL80211_ATTR_UNSPEC) < 0)
break;
wiphy = wiphy_find(wiphy_id);
if (!wiphy)
break;
wiphy_update_reg_domain(wiphy, false, msg);
break;
}
}
}
static int wiphy_init(void)
{
struct l_genl *genl = iwd_get_genl();
@ -1388,6 +1502,10 @@ static int wiphy_init(void)
" value: %s", s);
}
if (!l_genl_family_register(nl80211, NL80211_MULTICAST_GROUP_REG,
wiphy_reg_notify, NULL, NULL))
l_error("Registering for regulatory notifications failed");
return 0;
}

View File

@ -80,6 +80,7 @@ const uint8_t *wiphy_get_permanent_address(struct wiphy *wiphy);
const uint8_t *wiphy_get_extended_capabilities(struct wiphy *wiphy,
uint32_t iftype);
const uint8_t *wiphy_get_rm_enabled_capabilities(struct wiphy *wiphy);
void wiphy_get_reg_domain_country(struct wiphy *wiphy, char *out);
void wiphy_generate_random_address(struct wiphy *wiphy, uint8_t addr[static 6]);
void wiphy_generate_address_from_ssid(struct wiphy *wiphy, const char *ssid,