From 142817b2f901f1be878f520e63a6d9023ddfd795 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 13 Apr 2014 16:32:38 +0300 Subject: [PATCH] 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 --- src/common/wpa_ctrl.h | 2 + src/drivers/driver.h | 12 +++++ src/drivers/driver_nl80211.c | 92 +++++++++++++++++++++++++----------- wpa_supplicant/events.c | 47 +++++++++++++++++- 4 files changed, 123 insertions(+), 30 deletions(-) diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 5ef75e835..534bc997c 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -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 " diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 02ade1259..13bf7183d 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -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; /** diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 5b5ceb051..9a75f960b 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -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"); diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index adcdf01df..cad0938c5 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -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