Add AP channel switch mechanism

Build CSA settings and call the driver to perform the switch. Construct
Beacon, Probe Response, and (Re)Association Response frames both for CSA
period and for the new channel. These frames are built based on the
current configuration. Add CSA IE in Beacon and Probe Response frames.

Signed-hostap: Andrei Otcheretianski <andrei.otcheretianski@intel.com>
This commit is contained in:
Andrei Otcheretianski 2013-11-14 12:28:31 +02:00 committed by Jouni Malinen
parent e44a384b68
commit bf281c129f
9 changed files with 317 additions and 2 deletions

View file

@ -257,4 +257,13 @@ static inline const char * hostapd_drv_get_radio_name(struct hostapd_data *hapd)
return hapd->driver->get_radio_name(hapd->drv_priv); return hapd->driver->get_radio_name(hapd->drv_priv);
} }
static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd,
struct csa_settings *settings)
{
if (hapd->driver == NULL || hapd->driver->switch_channel == NULL)
return -ENOTSUP;
return hapd->driver->switch_channel(hapd->drv_priv, settings);
}
#endif /* AP_DRV_OPS */ #endif /* AP_DRV_OPS */

View file

@ -203,13 +203,34 @@ static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len)
} }
static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid)
{
u8 chan;
if (!hapd->iface->cs_freq)
return eid;
if (ieee80211_freq_to_chan(hapd->iface->cs_freq, &chan) ==
NUM_HOSTAPD_MODES)
return eid;
*eid++ = WLAN_EID_CHANNEL_SWITCH;
*eid++ = 3;
*eid++ = hapd->iface->cs_block_tx;
*eid++ = chan;
*eid++ = hapd->iface->cs_count;
return eid;
}
static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
struct sta_info *sta, struct sta_info *sta,
const struct ieee80211_mgmt *req, const struct ieee80211_mgmt *req,
int is_p2p, size_t *resp_len) int is_p2p, size_t *resp_len)
{ {
struct ieee80211_mgmt *resp; struct ieee80211_mgmt *resp;
u8 *pos, *epos; u8 *pos, *epos, *old_pos;
size_t buflen; size_t buflen;
#define MAX_PROBERESP_LEN 768 #define MAX_PROBERESP_LEN 768
@ -283,6 +304,13 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
pos = hostapd_eid_adv_proto(hapd, pos); pos = hostapd_eid_adv_proto(hapd, pos);
pos = hostapd_eid_roaming_consortium(hapd, pos); pos = hostapd_eid_roaming_consortium(hapd, pos);
old_pos = pos;
pos = hostapd_eid_csa(hapd, pos);
/* save an offset to the counter - should be last byte */
hapd->iface->cs_c_off_proberesp = (pos != old_pos) ?
pos - (u8 *) resp - 1 : 0;
#ifdef CONFIG_IEEE80211AC #ifdef CONFIG_IEEE80211AC
pos = hostapd_eid_vht_capabilities(hapd, pos); pos = hostapd_eid_vht_capabilities(hapd, pos);
pos = hostapd_eid_vht_operation(hapd, pos); pos = hostapd_eid_vht_operation(hapd, pos);
@ -598,7 +626,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
size_t resp_len = 0; size_t resp_len = 0;
#ifdef NEED_AP_MLME #ifdef NEED_AP_MLME
u16 capab_info; u16 capab_info;
u8 *pos, *tailpos; u8 *pos, *tailpos, *old_pos;
#define BEACON_HEAD_BUF_SIZE 256 #define BEACON_HEAD_BUF_SIZE 256
#define BEACON_TAIL_BUF_SIZE 512 #define BEACON_TAIL_BUF_SIZE 512
@ -693,6 +721,10 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
tailpos = hostapd_eid_interworking(hapd, tailpos); tailpos = hostapd_eid_interworking(hapd, tailpos);
tailpos = hostapd_eid_adv_proto(hapd, tailpos); tailpos = hostapd_eid_adv_proto(hapd, tailpos);
tailpos = hostapd_eid_roaming_consortium(hapd, tailpos); tailpos = hostapd_eid_roaming_consortium(hapd, tailpos);
old_pos = tailpos;
tailpos = hostapd_eid_csa(hapd, tailpos);
hapd->iface->cs_c_off_beacon = (old_pos != tailpos) ?
tailpos - tail - 1 : 0;
#ifdef CONFIG_IEEE80211AC #ifdef CONFIG_IEEE80211AC
tailpos = hostapd_eid_vht_capabilities(hapd, tailpos); tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
@ -817,6 +849,11 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
struct wpa_driver_ap_params params; struct wpa_driver_ap_params params;
struct wpabuf *beacon, *proberesp, *assocresp; struct wpabuf *beacon, *proberesp, *assocresp;
if (hapd->iface->csa_in_progress) {
wpa_printf(MSG_ERROR, "Cannot set beacons during CSA period");
return;
}
hapd->beacon_set_done = 1; hapd->beacon_set_done = 1;
if (ieee802_11_build_ap_params(hapd, &params) < 0) if (ieee802_11_build_ap_params(hapd, &params) < 0)

View file

@ -403,6 +403,13 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
hapd->iconf->channel = channel; hapd->iconf->channel = channel;
hapd->iconf->ieee80211n = ht; hapd->iconf->ieee80211n = ht;
hapd->iconf->secondary_channel = offset; hapd->iconf->secondary_channel = offset;
if (hapd->iface->csa_in_progress && freq == hapd->iface->cs_freq) {
hostapd_cleanup_cs_params(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED "freq=%d",
freq);
}
#endif /* NEED_AP_MLME */ #endif /* NEED_AP_MLME */
} }

View file

@ -2014,3 +2014,230 @@ void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s)
hostapd_state_text(s)); hostapd_state_text(s));
iface->state = s; iface->state = s;
} }
#ifdef NEED_AP_MLME
static void free_beacon_data(struct beacon_data *beacon)
{
os_free(beacon->head);
beacon->head = NULL;
os_free(beacon->tail);
beacon->tail = NULL;
os_free(beacon->probe_resp);
beacon->probe_resp = NULL;
os_free(beacon->beacon_ies);
beacon->beacon_ies = NULL;
os_free(beacon->proberesp_ies);
beacon->proberesp_ies = NULL;
os_free(beacon->assocresp_ies);
beacon->assocresp_ies = NULL;
}
static int hostapd_build_beacon_data(struct hostapd_iface *iface,
struct beacon_data *beacon)
{
struct wpabuf *beacon_extra, *proberesp_extra, *assocresp_extra;
struct wpa_driver_ap_params params;
int ret;
struct hostapd_data *hapd = iface->bss[0];
ret = ieee802_11_build_ap_params(hapd, &params);
if (ret < 0)
return ret;
ret = hostapd_build_ap_extra_ies(hapd, &beacon_extra,
&proberesp_extra,
&assocresp_extra);
if (ret)
goto free_ap_params;
ret = -1;
beacon->head = os_malloc(params.head_len);
if (!beacon->head)
goto free_ap_extra_ies;
os_memcpy(beacon->head, params.head, params.head_len);
beacon->head_len = params.head_len;
beacon->tail = os_malloc(params.tail_len);
if (!beacon->tail)
goto free_beacon;
os_memcpy(beacon->tail, params.tail, params.tail_len);
beacon->tail_len = params.tail_len;
if (params.proberesp != NULL) {
beacon->probe_resp = os_malloc(params.proberesp_len);
if (!beacon->probe_resp)
goto free_beacon;
os_memcpy(beacon->probe_resp, params.proberesp,
params.proberesp_len);
beacon->probe_resp_len = params.proberesp_len;
}
/* copy the extra ies */
if (beacon_extra) {
beacon->beacon_ies = os_malloc(wpabuf_len(beacon_extra));
if (!beacon->beacon_ies)
goto free_beacon;
os_memcpy(beacon->beacon_ies,
beacon_extra->buf, wpabuf_len(beacon_extra));
beacon->beacon_ies_len = wpabuf_len(beacon_extra);
}
if (proberesp_extra) {
beacon->proberesp_ies =
os_malloc(wpabuf_len(proberesp_extra));
if (!beacon->proberesp_ies)
goto free_beacon;
os_memcpy(beacon->proberesp_ies, proberesp_extra->buf,
wpabuf_len(proberesp_extra));
beacon->proberesp_ies_len = wpabuf_len(proberesp_extra);
}
if (assocresp_extra) {
beacon->assocresp_ies =
os_malloc(wpabuf_len(assocresp_extra));
if (!beacon->assocresp_ies)
goto free_beacon;
os_memcpy(beacon->assocresp_ies, assocresp_extra->buf,
wpabuf_len(assocresp_extra));
beacon->assocresp_ies_len = wpabuf_len(assocresp_extra);
}
ret = 0;
free_beacon:
/* if the function fails, the caller should not free beacon data */
if (ret)
free_beacon_data(beacon);
free_ap_extra_ies:
hostapd_free_ap_extra_ies(hapd, beacon_extra, proberesp_extra,
assocresp_extra);
free_ap_params:
ieee802_11_free_ap_params(&params);
return ret;
}
/*
* TODO: This flow currently supports only changing frequency within the
* same hw_mode. Any other changes to MAC parameters or provided settings (even
* width) are not supported.
*/
static int hostapd_change_config_freq(struct hostapd_data *hapd,
struct hostapd_config *conf,
struct hostapd_freq_params *params,
struct hostapd_freq_params *old_params)
{
int channel;
if (!params->channel) {
/* check if the new channel is supported by hw */
channel = hostapd_hw_get_channel(hapd, params->freq);
if (!channel)
return -1;
} else {
channel = params->channel;
}
/* if a pointer to old_params is provided we save previous state */
if (old_params) {
old_params->channel = conf->channel;
old_params->ht_enabled = conf->ieee80211n;
old_params->sec_channel_offset = conf->secondary_channel;
}
conf->channel = channel;
conf->ieee80211n = params->ht_enabled;
conf->secondary_channel = params->sec_channel_offset;
/* TODO: maybe call here hostapd_config_check here? */
return 0;
}
static int hostapd_fill_csa_settings(struct hostapd_iface *iface,
struct csa_settings *settings)
{
struct hostapd_freq_params old_freq;
int ret;
os_memset(&old_freq, 0, sizeof(old_freq));
if (!iface || !iface->freq || iface->csa_in_progress)
return -1;
ret = hostapd_change_config_freq(iface->bss[0], iface->conf,
&settings->freq_params,
&old_freq);
if (ret)
return ret;
ret = hostapd_build_beacon_data(iface, &settings->beacon_after);
/* change back the configuration */
hostapd_change_config_freq(iface->bss[0], iface->conf,
&old_freq, NULL);
if (ret)
return ret;
/* set channel switch parameters for csa ie */
iface->cs_freq = settings->freq_params.freq;
iface->cs_count = settings->cs_count;
iface->cs_block_tx = settings->block_tx;
ret = hostapd_build_beacon_data(iface, &settings->beacon_csa);
if (ret) {
free_beacon_data(&settings->beacon_after);
return ret;
}
settings->counter_offset_beacon = iface->cs_c_off_beacon;
settings->counter_offset_presp = iface->cs_c_off_proberesp;
return 0;
}
void hostapd_cleanup_cs_params(struct hostapd_data *hapd)
{
hapd->iface->cs_freq = 0;
hapd->iface->cs_count = 0;
hapd->iface->cs_block_tx = 0;
hapd->iface->cs_c_off_beacon = 0;
hapd->iface->cs_c_off_proberesp = 0;
hapd->iface->csa_in_progress = 0;
}
int hostapd_switch_channel(struct hostapd_data *hapd,
struct csa_settings *settings)
{
int ret;
ret = hostapd_fill_csa_settings(hapd->iface, settings);
if (ret)
return ret;
ret = hostapd_drv_switch_channel(hapd, settings);
free_beacon_data(&settings->beacon_csa);
free_beacon_data(&settings->beacon_after);
if (ret) {
/* if we failed, clean cs parameters */
hostapd_cleanup_cs_params(hapd);
return ret;
}
hapd->iface->csa_in_progress = 1;
return 0;
}
#endif /* NEED_AP_MLME */

View file

@ -27,6 +27,8 @@ union wps_event_data;
struct hostapd_iface; struct hostapd_iface;
struct hostapd_dynamic_iface; struct hostapd_dynamic_iface;
struct csa_settings;
struct hapd_interfaces { struct hapd_interfaces {
int (*reload_config)(struct hostapd_iface *iface); int (*reload_config)(struct hostapd_iface *iface);
struct hostapd_config * (*config_read_cb)(const char *config_fname); struct hostapd_config * (*config_read_cb)(const char *config_fname);
@ -332,6 +334,14 @@ struct hostapd_iface {
/* lowest observed noise floor in dBm */ /* lowest observed noise floor in dBm */
s8 lowest_nf; s8 lowest_nf;
/* channel switch parameters */
int cs_freq;
u8 cs_count;
int cs_block_tx;
unsigned int cs_c_off_beacon;
unsigned int cs_c_off_proberesp;
int csa_in_progress;
#ifdef CONFIG_ACS #ifdef CONFIG_ACS
unsigned int acs_num_completed_scans; unsigned int acs_num_completed_scans;
#endif /* CONFIG_ACS */ #endif /* CONFIG_ACS */
@ -378,6 +388,9 @@ int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf);
void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator); void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator);
void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s); void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s);
const char * hostapd_state_text(enum hostapd_iface_state s); const char * hostapd_state_text(enum hostapd_iface_state s);
int hostapd_switch_channel(struct hostapd_data *hapd,
struct csa_settings *settings);
void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
/* utils.c */ /* utils.c */
int hostapd_register_probereq_cb(struct hostapd_data *hapd, int hostapd_register_probereq_cb(struct hostapd_data *hapd,

View file

@ -1157,4 +1157,8 @@ enum wnm_sleep_mode_subelement_id {
WNM_SLEEP_SUBELEM_IGTK = 1 WNM_SLEEP_SUBELEM_IGTK = 1
}; };
/* Channel Switch modes (802.11h) */
#define CHAN_SWITCH_MODE_ALLOW_TX 0
#define CHAN_SWITCH_MODE_BLOCK_TX 1
#endif /* IEEE802_11_DEFS_H */ #endif /* IEEE802_11_DEFS_H */

View file

@ -176,6 +176,8 @@ extern "C" {
#define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED " #define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED "
#define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED " #define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED "
#define AP_CSA_FINISHED "AP-CSA-FINISHED "
/* BSS command information masks */ /* BSS command information masks */
#define WPA_BSS_MASK_ALL 0xFFFDFFFF #define WPA_BSS_MASK_ALL 0xFFFDFFFF

View file

@ -1058,6 +1058,20 @@ int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s)
} }
int ap_switch_channel(struct wpa_supplicant *wpa_s,
struct csa_settings *settings)
{
#ifdef NEED_AP_MLME
if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
return -1;
return hostapd_switch_channel(wpa_s->ap_iface->bss[0], settings);
#else /* NEED_AP_MLME */
return -1;
#endif /* NEED_AP_MLME */
}
void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
int offset) int offset)
{ {

View file

@ -50,6 +50,8 @@ int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s);
int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s, int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
const u8 *addr); const u8 *addr);
void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s); void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s);
int ap_switch_channel(struct wpa_supplicant *wpa_s,
struct csa_settings *settings);
void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
int offset); int offset);
struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s, struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s,