diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 5defd8b38..024339ab8 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -242,4 +242,11 @@ static inline int hostapd_drv_get_survey(struct hostapd_data *hapd, return hapd->driver->get_survey(hapd->drv_priv, freq); } +static inline int hostapd_get_country(struct hostapd_data *hapd, char *alpha2) +{ + if (hapd->driver == NULL || hapd->driver->get_country == NULL) + return -1; + return hapd->driver->get_country(hapd->drv_priv, alpha2); +} + #endif /* AP_DRV_OPS */ diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index b30da145e..d0a429d78 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -1007,6 +1007,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, /* TODO: check this. hostapd_get_hw_features() initializes * too much stuff. */ /* hostapd_get_hw_features(hapd->iface); */ + hostapd_channel_list_updated(hapd->iface); break; #endif /* NEED_AP_MLME */ default: diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index d16c36d0d..f41cb145b 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -38,6 +38,8 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd); static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd); +static int setup_interface2(struct hostapd_iface *iface); +static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx); extern int wpa_debug_level; extern struct wpa_driver_ops *wpa_drivers[]; @@ -337,6 +339,8 @@ static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) */ static void hostapd_cleanup_iface(struct hostapd_iface *iface) { + eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); + hostapd_cleanup_iface_partial(iface); hostapd_config_free(iface->conf); iface->conf = NULL; @@ -926,11 +930,39 @@ static int start_ctrl_iface(struct hostapd_iface *iface) } +static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_iface *iface = eloop_ctx; + + if (!iface->wait_channel_update) { + wpa_printf(MSG_INFO, "Channel list update timeout, but interface was not waiting for it"); + return; + } + + /* + * It is possible that the existing channel list is acceptable, so try + * to proceed. + */ + wpa_printf(MSG_DEBUG, "Channel list update timeout - try to continue anyway"); + setup_interface2(iface); +} + + +void hostapd_channel_list_updated(struct hostapd_iface *iface) +{ + if (!iface->wait_channel_update) + return; + + wpa_printf(MSG_DEBUG, "Channel list updated - continue setup"); + eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); + setup_interface2(iface); +} + + static int setup_interface(struct hostapd_iface *iface) { struct hostapd_data *hapd = iface->bss[0]; size_t i; - char country[4]; /* * Make sure that all BSSes get configured with a pointer to the same @@ -953,14 +985,39 @@ static int setup_interface(struct hostapd_iface *iface) return -1; if (hapd->iconf->country[0] && hapd->iconf->country[1]) { + char country[4], previous_country[4]; + + if (hostapd_get_country(hapd, previous_country) < 0) + previous_country[0] = '\0'; + os_memcpy(country, hapd->iconf->country, 3); country[3] = '\0'; if (hostapd_set_country(hapd, country) < 0) { wpa_printf(MSG_ERROR, "Failed to set country code"); return -1; } + + wpa_printf(MSG_DEBUG, "Previous country code %s, new country code %s", + previous_country, country); + + if (os_strncmp(previous_country, country, 2) != 0) { + wpa_printf(MSG_DEBUG, "Continue interface setup after channel list update"); + iface->wait_channel_update = 1; + eloop_register_timeout(1, 0, + channel_list_update_timeout, + iface, NULL); + return 0; + } } + return setup_interface2(iface); +} + + +static int setup_interface2(struct hostapd_iface *iface) +{ + iface->wait_channel_update = 0; + if (hostapd_get_hw_features(iface)) { /* Not all drivers support this yet, so continue without hw * feature data. */ @@ -1171,6 +1228,9 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) if (iface == NULL) return; + eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); + iface->wait_channel_update = 0; + hostapd_cleanup_iface_pre(iface); for (j = iface->num_bss - 1; j >= 0; j--) { struct hostapd_data *hapd = iface->bss[j]; diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index ce2516ebb..dacdfa7da 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -254,6 +254,8 @@ struct hostapd_iface { size_t num_bss; struct hostapd_data **bss; + unsigned int wait_channel_update:1; + int num_ap; /* number of entries in ap_list */ struct ap_info *ap_list; /* AP info list head */ struct ap_info *ap_hash[STA_HASH_SIZE]; @@ -361,6 +363,7 @@ int hostapd_reload_iface(struct hostapd_iface *hapd_iface); int hostapd_disable_iface(struct hostapd_iface *hapd_iface); int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf); int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf); +void hostapd_channel_list_updated(struct hostapd_iface *iface); /* utils.c */ int hostapd_register_probereq_cb(struct hostapd_data *hapd, diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 85f66ee41..f233032eb 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1569,6 +1569,14 @@ struct wpa_driver_ops { */ int (*set_country)(void *priv, const char *alpha2); + /** + * get_country - Get country + * @priv: Private driver interface data + * @alpha2: Buffer for returning country code (at least 3 octets) + * Returns: 0 on success, -1 on failure + */ + int (*get_country)(void *priv, char *alpha2); + /** * global_init - Global driver initialization * Returns: Pointer to private data (global), %NULL on failure diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 8bd075963..4dffb3eb0 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -2934,6 +2934,44 @@ nla_put_failure: } +static int nl80211_get_country(struct nl_msg *msg, void *arg) +{ + char *alpha2 = arg; + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb_msg[NL80211_ATTR_REG_ALPHA2]) { + wpa_printf(MSG_DEBUG, "nl80211: No country information available"); + return NL_SKIP; + } + os_strlcpy(alpha2, nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), 3); + return NL_SKIP; +} + + +static int wpa_driver_nl80211_get_country(void *priv, char *alpha2) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG); + alpha2[0] = '\0'; + ret = send_and_recv_msgs(drv, msg, nl80211_get_country, alpha2); + if (!alpha2[0]) + ret = -1; + + return ret; +} + + static int protocol_feature_handler(struct nl_msg *msg, void *arg) { u32 *feat = arg; @@ -11204,6 +11242,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .set_operstate = wpa_driver_nl80211_set_operstate, .set_supp_port = wpa_driver_nl80211_set_supp_port, .set_country = wpa_driver_nl80211_set_country, + .get_country = wpa_driver_nl80211_get_country, .set_ap = wpa_driver_nl80211_set_ap, .set_acl = wpa_driver_nl80211_set_acl, .if_add = wpa_driver_nl80211_if_add,