Add a wpa_supplicant ctrl_iface event for regdom changes

CTRL-EVENT-REGDOM-CHANGE event provides an external notification of
regulatory domain (and any driver channel list) changes.

Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2014-04-13 16:32:38 +03:00
parent 150fd0b2b5
commit 142817b2f9
4 changed files with 123 additions and 30 deletions

View file

@ -66,6 +66,8 @@ extern "C" {
#define WPA_EVENT_BSS_REMOVED "CTRL-EVENT-BSS-REMOVED "
/** Change in the signal level was reported by the driver */
#define WPA_EVENT_SIGNAL_CHANGE "CTRL-EVENT-SIGNAL-CHANGE "
/** Regulatory domain channel */
#define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE "
/** RSN IBSS 4-way handshakes completed with specified peer */
#define IBSS_RSN_COMPLETED "IBSS-RSN-COMPLETED "

View file

@ -50,6 +50,14 @@ enum reg_change_initiator {
REGDOM_BEACON_HINT,
};
enum reg_type {
REGDOM_TYPE_UNKNOWN,
REGDOM_TYPE_COUNTRY,
REGDOM_TYPE_WORLD,
REGDOM_TYPE_CUSTOM_WORLD,
REGDOM_TYPE_INTERSECTION,
};
/**
* struct hostapd_channel_data - Channel information
*/
@ -3851,9 +3859,13 @@ union wpa_event_data {
/**
* channel_list_changed - Data for EVENT_CHANNEL_LIST_CHANGED
* @initiator: Initiator of the regulatory change
* @type: Regulatory change type
* @alpha2: Country code (or "" if not available)
*/
struct channel_list_changed {
enum reg_change_initiator initiator;
enum reg_type type;
char alpha2[3];
} channel_list_changed;
/**

View file

@ -2889,6 +2889,69 @@ static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv,
}
static void nl80211_reg_change_event(struct wpa_driver_nl80211_data *drv,
struct nlattr *tb[])
{
union wpa_event_data data;
enum nl80211_reg_initiator init;
wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
if (tb[NL80211_ATTR_REG_INITIATOR] == NULL)
return;
os_memset(&data, 0, sizeof(data));
init = nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR]);
wpa_printf(MSG_DEBUG, " * initiator=%d", init);
switch (init) {
case NL80211_REGDOM_SET_BY_CORE:
data.channel_list_changed.initiator = REGDOM_SET_BY_CORE;
break;
case NL80211_REGDOM_SET_BY_USER:
data.channel_list_changed.initiator = REGDOM_SET_BY_USER;
break;
case NL80211_REGDOM_SET_BY_DRIVER:
data.channel_list_changed.initiator = REGDOM_SET_BY_DRIVER;
break;
case NL80211_REGDOM_SET_BY_COUNTRY_IE:
data.channel_list_changed.initiator = REGDOM_SET_BY_COUNTRY_IE;
break;
}
if (tb[NL80211_ATTR_REG_TYPE]) {
enum nl80211_reg_type type;
type = nla_get_u8(tb[NL80211_ATTR_REG_TYPE]);
wpa_printf(MSG_DEBUG, " * type=%d", type);
switch (type) {
case NL80211_REGDOM_TYPE_COUNTRY:
data.channel_list_changed.type = REGDOM_TYPE_COUNTRY;
break;
case NL80211_REGDOM_TYPE_WORLD:
data.channel_list_changed.type = REGDOM_TYPE_WORLD;
break;
case NL80211_REGDOM_TYPE_CUSTOM_WORLD:
data.channel_list_changed.type =
REGDOM_TYPE_CUSTOM_WORLD;
break;
case NL80211_REGDOM_TYPE_INTERSECTION:
data.channel_list_changed.type =
REGDOM_TYPE_INTERSECTION;
break;
}
}
if (tb[NL80211_ATTR_REG_ALPHA2]) {
os_strlcpy(data.channel_list_changed.alpha2,
nla_get_string(tb[NL80211_ATTR_REG_ALPHA2]),
sizeof(data.channel_list_changed.alpha2));
wpa_printf(MSG_DEBUG, " * alpha2=%s",
data.channel_list_changed.alpha2);
}
wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data);
}
static void do_process_drv_event(struct i802_bss *bss, int cmd,
struct nlattr **tb)
{
@ -3008,34 +3071,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
nl80211_cqm_event(drv, tb);
break;
case NL80211_CMD_REG_CHANGE:
wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
if (tb[NL80211_ATTR_REG_INITIATOR] == NULL)
break;
os_memset(&data, 0, sizeof(data));
switch (nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR])) {
case NL80211_REGDOM_SET_BY_CORE:
data.channel_list_changed.initiator =
REGDOM_SET_BY_CORE;
break;
case NL80211_REGDOM_SET_BY_USER:
data.channel_list_changed.initiator =
REGDOM_SET_BY_USER;
break;
case NL80211_REGDOM_SET_BY_DRIVER:
data.channel_list_changed.initiator =
REGDOM_SET_BY_DRIVER;
break;
case NL80211_REGDOM_SET_BY_COUNTRY_IE:
data.channel_list_changed.initiator =
REGDOM_SET_BY_COUNTRY_IE;
break;
default:
wpa_printf(MSG_DEBUG, "nl80211: Unknown reg change initiator %d received",
nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR]));
break;
}
wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
&data);
nl80211_reg_change_event(drv, tb);
break;
case NL80211_CMD_REG_BEACON_HINT:
wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint");

View file

@ -2720,10 +2720,52 @@ static void wpas_event_deauth(struct wpa_supplicant *wpa_s,
}
static void wpa_supplicant_update_channel_list(struct wpa_supplicant *wpa_s)
static const char * reg_init_str(enum reg_change_initiator init)
{
switch (init) {
case REGDOM_SET_BY_CORE:
return "CORE";
case REGDOM_SET_BY_USER:
return "USER";
case REGDOM_SET_BY_DRIVER:
return "DRIVER";
case REGDOM_SET_BY_COUNTRY_IE:
return "COUNTRY_IE";
case REGDOM_BEACON_HINT:
return "BEACON_HINT";
}
return "?";
}
static const char * reg_type_str(enum reg_type type)
{
switch (type) {
case REGDOM_TYPE_UNKNOWN:
return "UNKNOWN";
case REGDOM_TYPE_COUNTRY:
return "COUNTRY";
case REGDOM_TYPE_WORLD:
return "WORLD";
case REGDOM_TYPE_CUSTOM_WORLD:
return "CUSTOM_WORLD";
case REGDOM_TYPE_INTERSECTION:
return "INTERSECTION";
}
return "?";
}
static void wpa_supplicant_update_channel_list(
struct wpa_supplicant *wpa_s, struct channel_list_changed *info)
{
struct wpa_supplicant *ifs;
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REGDOM_CHANGE "init=%s type=%s%s%s",
reg_init_str(info->type), reg_type_str(info->type),
info->alpha2[0] ? " alpha2=" : "",
info->alpha2[0] ? info->alpha2 : "");
if (wpa_s->drv_priv == NULL)
return; /* Ignore event during drv initialization */
@ -3326,7 +3368,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
break;
case EVENT_CHANNEL_LIST_CHANGED:
wpa_supplicant_update_channel_list(wpa_s);
wpa_supplicant_update_channel_list(
wpa_s, &data->channel_list_changed);
break;
case EVENT_INTERFACE_UNAVAILABLE:
#ifdef CONFIG_P2P