aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/ap/ap_drv_ops.h9
-rw-r--r--src/ap/beacon.c41
-rw-r--r--src/ap/drv_callbacks.c7
-rw-r--r--src/ap/hostapd.c227
-rw-r--r--src/ap/hostapd.h13
-rw-r--r--src/common/ieee802_11_defs.h4
-rw-r--r--src/common/wpa_ctrl.h2
-rw-r--r--wpa_supplicant/ap.c14
-rw-r--r--wpa_supplicant/ap.h2
9 files changed, 317 insertions, 2 deletions
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index ce2bb91..1eab939 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -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);
}
+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 */
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 4b75a75..298c0fa 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -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,
struct sta_info *sta,
const struct ieee80211_mgmt *req,
int is_p2p, size_t *resp_len)
{
struct ieee80211_mgmt *resp;
- u8 *pos, *epos;
+ u8 *pos, *epos, *old_pos;
size_t buflen;
#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_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
pos = hostapd_eid_vht_capabilities(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;
#ifdef NEED_AP_MLME
u16 capab_info;
- u8 *pos, *tailpos;
+ u8 *pos, *tailpos, *old_pos;
#define BEACON_HEAD_BUF_SIZE 256
#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_adv_proto(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
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 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;
if (ieee802_11_build_ap_params(hapd, &params) < 0)
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index aee2946..1b69ba8 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -403,6 +403,13 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
hapd->iconf->channel = channel;
hapd->iconf->ieee80211n = ht;
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 */
}
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index a06ec9f..51b1035 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -2014,3 +2014,230 @@ void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s)
hostapd_state_text(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 */
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 9a10626..3dac6ea 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -27,6 +27,8 @@ union wps_event_data;
struct hostapd_iface;
struct hostapd_dynamic_iface;
+struct csa_settings;
+
struct hapd_interfaces {
int (*reload_config)(struct hostapd_iface *iface);
struct hostapd_config * (*config_read_cb)(const char *config_fname);
@@ -332,6 +334,14 @@ struct hostapd_iface {
/* lowest observed noise floor in dBm */
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
unsigned int acs_num_completed_scans;
#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_set_state(struct hostapd_iface *iface, 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 */
int hostapd_register_probereq_cb(struct hostapd_data *hapd,
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index ca122d9..9b2d54f 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -1157,4 +1157,8 @@ enum wnm_sleep_mode_subelement_id {
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 */
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 0b6e395..b435310 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -176,6 +176,8 @@ extern "C" {
#define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED "
#define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED "
+#define AP_CSA_FINISHED "AP-CSA-FINISHED "
+
/* BSS command information masks */
#define WPA_BSS_MASK_ALL 0xFFFDFFFF
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 6887469..ef18dbd 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -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,
int offset)
{
diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
index 74a0b18..f62b8ba 100644
--- a/wpa_supplicant/ap.h
+++ b/wpa_supplicant/ap.h
@@ -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,
const u8 *addr);
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,
int offset);
struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s,