Cumulative patch from commit 538922a628d4f5403b9a96b171a59235bcb3d921
538922a dbus: Add boolean AllowRoam option to Scan() method options dictionary
c6f5dec Don't start second scan when changing scan interval
cd3b070 nl80211: Fix DFS radar event parsing
2b72df6 nl80211: Free BSS structure even if netdev does not exists
41cc50d nl80211: Update send_action_cookie on AP-offchannel-TX path
313424d GAS: Add support for multiple pending queries for the same destination
cbc5484 GAS: Do not start new scan operation during an ongoing GAS query
c377514 GAS: Delay GAS query Tx while scanning/connecting
24c694b GAS: Delay GAS query Tx while another query is in progress
7255983 WPS: Clear after_wps from number of new locations
73b54d6 P2P: Fix Operating Channel in Invitation Request for operating group
dc46fd6 P2P: Cancel offchannel TX wait on Invitation Response RX
0c92963 D-Bus: Clean up debug print for P2P invitation result
8d82c21 P2P: Fix PD retry channel on join-a-group case
d285888 P2P: Add GO BSS entry details to debug log on join-a-group
512629a P2P: Accept Invitation Response non-success without Channel List
e241b1b eap_proxy: Fix IMSI fetch for home vs. visited network determination
db13605 EAP-AKA/AKA' peer: Allow external USIM processing to be used
569ccf7 EAP-SIM peer: Allow external SIM processing to be used
84dc137 hlr_auc_gw: Add GSM-AUTH-REQ command
a5d44ac EAP peer: Add framework for external SIM/USIM processing
7e8bc7d eapol_test: Initialize BSS lists
bceb843 Send CTRL-RSP command response before processing EAPOL update
b607796 eapol_test: Fix external EAP request mechanism
94de082 eapol_test: Initialize wpa_s->global to fix ctrl_iface
f07bba3 Android: Add dfs.c into build
0cf0af2 WNM: Set Disassoc Imminent flag in ESS Disassoc Imminent frame
f47c145 Interworking: Add required_roaming_consortium parameter for credentials
a83e574 GAS: Update timeout from TX status handler
e88060e HTTP server: Allow TCP socket to be reused
9bc3386 Add test option for specifying hardcoded BSS Load element
9c7e43a Define BSS Load element id
56f5af4 Interworking: Add support for QoS Mapping functionality for the STA
850e1c2 atheros: Add support for QoS Mapping configuration
c551700 Interworking: Add support for QoS Mapping functionality for the AP
ac1bc54 Interworking: Add domain_suffix_match for credentials
463c8ff Interworking: Add support for multiple home FQDNs
01f809c Add AAA server domain name suffix matching constraint
be7963b OpenSSL: Fix code indentation in OCSP processing
899cc14 hostapd: Add support for DFS with 160 MHz channel width
6de0e0c Mark DFS functions static and rename them
58b73e3 hostapd: DFS with 40/80 MHz channel width support
846de15 DFS: Add more parameters to radar events
04e8003 nl80211: Use struct hostapd_freq_params with start_dfs_cac
72c753d hostapd: Split hostapd_set_freq to helper function
e76da50 hostapd: Add AP DFS support
Change-Id: Ie9ed4662ba6d81e6d8b14bccb29ffa192becf0f2
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index c5531fa..4f6a739 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -442,6 +442,9 @@
u16 gas_comeback_delay;
int gas_frag_limit;
+ u8 qos_map_set[16 + 2 * 21];
+ unsigned int qos_map_set_len;
+
#ifdef CONFIG_HS20
int hs20;
int disable_dgaf;
@@ -464,6 +467,11 @@
unsigned int sae_anti_clogging_threshold;
int *sae_groups;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 bss_load_test[5];
+ u8 bss_load_test_set;
+#endif /* CONFIG_TESTING_OPTIONS */
};
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 3072562..9023eab 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -463,44 +463,44 @@
}
-int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
- int channel, int ht_enabled, int vht_enabled,
- int sec_channel_offset, int vht_oper_chwidth,
- int center_segment0, int center_segment1)
+static int hostapd_set_freq_params(struct hostapd_freq_params *data, int mode,
+ int freq, int channel, int ht_enabled,
+ int vht_enabled, int sec_channel_offset,
+ int vht_oper_chwidth, int center_segment0,
+ int center_segment1)
{
- struct hostapd_freq_params data;
int tmp;
- os_memset(&data, 0, sizeof(data));
- data.mode = mode;
- data.freq = freq;
- data.channel = channel;
- data.ht_enabled = ht_enabled;
- data.vht_enabled = vht_enabled;
- data.sec_channel_offset = sec_channel_offset;
- data.center_freq1 = freq + sec_channel_offset * 10;
- data.center_freq2 = 0;
- data.bandwidth = sec_channel_offset ? 40 : 20;
+ os_memset(data, 0, sizeof(*data));
+ data->mode = mode;
+ data->freq = freq;
+ data->channel = channel;
+ data->ht_enabled = ht_enabled;
+ data->vht_enabled = vht_enabled;
+ data->sec_channel_offset = sec_channel_offset;
+ data->center_freq1 = freq + sec_channel_offset * 10;
+ data->center_freq2 = 0;
+ data->bandwidth = sec_channel_offset ? 40 : 20;
/*
* This validation code is probably misplaced, maybe it should be
* in src/ap/hw_features.c and check the hardware support as well.
*/
- if (data.vht_enabled) switch (vht_oper_chwidth) {
+ if (data->vht_enabled) switch (vht_oper_chwidth) {
case VHT_CHANWIDTH_USE_HT:
if (center_segment1)
return -1;
- if (5000 + center_segment0 * 5 != data.center_freq1)
+ if (5000 + center_segment0 * 5 != data->center_freq1)
return -1;
break;
case VHT_CHANWIDTH_80P80MHZ:
if (center_segment1 == center_segment0 + 4 ||
center_segment1 == center_segment0 - 4)
return -1;
- data.center_freq2 = 5000 + center_segment1 * 5;
+ data->center_freq2 = 5000 + center_segment1 * 5;
/* fall through */
case VHT_CHANWIDTH_80MHZ:
- data.bandwidth = 80;
+ data->bandwidth = 80;
if (vht_oper_chwidth == 1 && center_segment1)
return -1;
if (vht_oper_chwidth == 3 && !center_segment1)
@@ -510,13 +510,13 @@
/* primary 40 part must match the HT configuration */
tmp = (30 + freq - 5000 - center_segment0 * 5)/20;
tmp /= 2;
- if (data.center_freq1 != 5000 +
+ if (data->center_freq1 != 5000 +
center_segment0 * 5 - 20 + 40 * tmp)
return -1;
- data.center_freq1 = 5000 + center_segment0 * 5;
+ data->center_freq1 = 5000 + center_segment0 * 5;
break;
case VHT_CHANWIDTH_160MHZ:
- data.bandwidth = 160;
+ data->bandwidth = 160;
if (center_segment1)
return -1;
if (!sec_channel_offset)
@@ -524,12 +524,30 @@
/* primary 40 part must match the HT configuration */
tmp = (70 + freq - 5000 - center_segment0 * 5)/20;
tmp /= 2;
- if (data.center_freq1 != 5000 +
+ if (data->center_freq1 != 5000 +
center_segment0 * 5 - 60 + 40 * tmp)
return -1;
- data.center_freq1 = 5000 + center_segment0 * 5;
+ data->center_freq1 = 5000 + center_segment0 * 5;
break;
}
+
+ return 0;
+}
+
+
+int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
+ int channel, int ht_enabled, int vht_enabled,
+ int sec_channel_offset, int vht_oper_chwidth,
+ int center_segment0, int center_segment1)
+{
+ struct hostapd_freq_params data;
+
+ if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
+ vht_enabled, sec_channel_offset,
+ vht_oper_chwidth,
+ center_segment0, center_segment1))
+ return -1;
+
if (hapd->driver == NULL)
return 0;
if (hapd->driver->set_freq == NULL)
@@ -699,3 +717,38 @@
hapd->own_addr, hapd->own_addr, data,
len, 0);
}
+
+int hostapd_start_dfs_cac(struct hostapd_data *hapd, int mode, int freq,
+ int channel, int ht_enabled, int vht_enabled,
+ int sec_channel_offset, int vht_oper_chwidth,
+ int center_segment0, int center_segment1)
+{
+ struct hostapd_freq_params data;
+
+ if (!hapd->driver || !hapd->driver->start_dfs_cac)
+ return 0;
+
+ if (!hapd->iface->conf->ieee80211h) {
+ wpa_printf(MSG_ERROR, "Can't start DFS CAC, DFS functionality "
+ "is not enabled");
+ return -1;
+ }
+
+ if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
+ vht_enabled, sec_channel_offset,
+ vht_oper_chwidth, center_segment0,
+ center_segment1))
+ return -1;
+
+ return hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
+}
+
+
+int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
+ const u8 *qos_map_set, u8 qos_map_set_len)
+{
+ if (hapd->driver == NULL || hapd->driver->set_qos_map == NULL)
+ return 0;
+ return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
+ qos_map_set_len);
+}
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 23277b6..8a2b4dc 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -101,6 +101,10 @@
int reassoc, u16 status, const u8 *ie, size_t len);
int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
u8 *tspec_ie, size_t tspec_ielen);
+int hostapd_start_dfs_cac(struct hostapd_data *hapd, int mode, int freq,
+ int channel, int ht_enabled, int vht_enabled,
+ int sec_channel_offset, int vht_oper_chwidth,
+ int center_segment0, int center_segment1);
#include "drivers/driver.h"
@@ -109,6 +113,9 @@
enum wnm_oper oper, const u8 *peer,
u8 *buf, u16 *buf_len);
+int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set,
+ u8 qos_map_set_len);
+
static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd,
int enabled)
{
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 2f4ba23..360001f 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -38,6 +38,22 @@
#ifdef NEED_AP_MLME
+static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->bss_load_test_set) {
+ if (2 + 5 > len)
+ return eid;
+ *eid++ = WLAN_EID_BSS_LOAD;
+ *eid++ = 5;
+ os_memcpy(eid, hapd->conf->bss_load_test, 5);
+ eid += 5;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ return eid;
+}
+
+
static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
{
u8 erp = 0;
@@ -251,6 +267,8 @@
/* RSN, MDIE, WPA */
pos = hostapd_eid_wpa(hapd, pos, epos - pos);
+ pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
+
#ifdef CONFIG_IEEE80211N
pos = hostapd_eid_ht_capabilities(hapd, pos);
pos = hostapd_eid_ht_operation(hapd, pos);
@@ -662,6 +680,9 @@
tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE -
tailpos);
+ tailpos = hostapd_eid_bss_load(hapd, tailpos,
+ tail + BEACON_TAIL_BUF_SIZE - tailpos);
+
#ifdef CONFIG_IEEE80211N
tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
tailpos = hostapd_eid_ht_operation(hapd, tailpos);
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
new file mode 100644
index 0000000..37bbd20
--- /dev/null
+++ b/src/ap/dfs.c
@@ -0,0 +1,601 @@
+/*
+ * DFS - Dynamic Frequency Selection
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "drivers/driver.h"
+#include "dfs.h"
+
+
+static int dfs_get_used_n_chans(struct hostapd_data *hapd)
+{
+ int n_chans = 1;
+
+ if (hapd->iconf->ieee80211n && hapd->iconf->secondary_channel)
+ n_chans = 2;
+
+ if (hapd->iconf->ieee80211ac) {
+ switch (hapd->iconf->vht_oper_chwidth) {
+ case VHT_CHANWIDTH_USE_HT:
+ break;
+ case VHT_CHANWIDTH_80MHZ:
+ n_chans = 4;
+ break;
+ case VHT_CHANWIDTH_160MHZ:
+ n_chans = 8;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return n_chans;
+}
+
+
+static int dfs_channel_available(struct hostapd_channel_data *chan)
+{
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ return 0;
+ if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+ ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+ HOSTAPD_CHAN_DFS_UNAVAILABLE))
+ return 0;
+ return 1;
+}
+
+
+static int dfs_is_ht40_allowed(struct hostapd_channel_data *chan)
+{
+ int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
+ 184, 192 };
+ unsigned int i;
+
+ for (i = 0; i < sizeof(allowed) / sizeof(allowed[0]); i++) {
+ if (chan->chan == allowed[i])
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int dfs_find_channel(struct hostapd_data *hapd,
+ struct hostapd_channel_data **ret_chan,
+ int idx)
+{
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan, *next_chan;
+ int i, j, channel_idx = 0, n_chans;
+
+ mode = hapd->iface->current_mode;
+ n_chans = dfs_get_used_n_chans(hapd);
+
+ wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+
+ /* Skip not available channels */
+ if (!dfs_channel_available(chan))
+ continue;
+
+ /* Skip HT40/VHT uncompatible channels */
+ if (hapd->iconf->ieee80211n &&
+ hapd->iconf->secondary_channel) {
+ if (!dfs_is_ht40_allowed(chan))
+ continue;
+
+ for (j = 1; j < n_chans; j++) {
+ next_chan = &mode->channels[i + j];
+ if (!dfs_channel_available(next_chan))
+ break;
+ }
+ if (j != n_chans)
+ continue;
+
+ /* Set HT40+ */
+ hapd->iconf->secondary_channel = 1;
+ }
+
+ if (ret_chan && idx == channel_idx) {
+ wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan);
+ *ret_chan = chan;
+ return idx;
+ }
+ wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan);
+ channel_idx++;
+ }
+ return channel_idx;
+}
+
+
+static void dfs_adjust_vht_center_freq(struct hostapd_data *hapd,
+ struct hostapd_channel_data *chan)
+{
+ if (!hapd->iconf->ieee80211ac)
+ return;
+
+ if (!chan)
+ return;
+
+ switch (hapd->iconf->vht_oper_chwidth) {
+ case VHT_CHANWIDTH_USE_HT:
+ hapd->iconf->vht_oper_centr_freq_seg0_idx = chan->chan + 2;
+ break;
+ case VHT_CHANWIDTH_80MHZ:
+ hapd->iconf->vht_oper_centr_freq_seg0_idx = chan->chan + 6;
+ break;
+ case VHT_CHANWIDTH_160MHZ:
+ hapd->iconf->vht_oper_centr_freq_seg0_idx =
+ chan->chan + 14;
+ default:
+ wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now");
+ break;
+ }
+
+ wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d",
+ hapd->iconf->vht_oper_centr_freq_seg0_idx);
+}
+
+
+/* Return start channel idx we will use for mode->channels[idx] */
+static int dfs_get_start_chan_idx(struct hostapd_data *hapd)
+{
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan;
+ int channel_no = hapd->iconf->channel;
+ int res = -1, i;
+
+ /* HT40- */
+ if (hapd->iconf->ieee80211n && hapd->iconf->secondary_channel == -1)
+ channel_no -= 4;
+
+ /* VHT */
+ if (hapd->iconf->ieee80211ac) {
+ switch (hapd->iconf->vht_oper_chwidth) {
+ case VHT_CHANWIDTH_USE_HT:
+ break;
+ case VHT_CHANWIDTH_80MHZ:
+ channel_no =
+ hapd->iconf->vht_oper_centr_freq_seg0_idx - 6;
+ break;
+ case VHT_CHANWIDTH_160MHZ:
+ channel_no =
+ hapd->iconf->vht_oper_centr_freq_seg0_idx - 14;
+ break;
+ default:
+ wpa_printf(MSG_INFO,
+ "DFS only VHT20/40/80/160 is supported now");
+ channel_no = -1;
+ break;
+ }
+ }
+
+ /* Get idx */
+ mode = hapd->iface->current_mode;
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+ if (chan->chan == channel_no) {
+ res = i;
+ break;
+ }
+ }
+
+ if (res == -1)
+ wpa_printf(MSG_DEBUG, "DFS chan_idx seems wrong: -1");
+
+ return res;
+}
+
+
+/* At least one channel have radar flag */
+static int dfs_check_chans_radar(struct hostapd_data *hapd, int start_chan_idx,
+ int n_chans)
+{
+ struct hostapd_channel_data *channel;
+ struct hostapd_hw_modes *mode;
+ int i, res = 0;
+
+ mode = hapd->iface->current_mode;
+
+ for (i = 0; i < n_chans; i++) {
+ channel = &mode->channels[start_chan_idx + i];
+ if (channel->flag & HOSTAPD_CHAN_RADAR)
+ res++;
+ }
+
+ return res;
+}
+
+
+/* All channels available */
+static int dfs_check_chans_available(struct hostapd_data *hapd,
+ int start_chan_idx, int n_chans)
+{
+ struct hostapd_channel_data *channel;
+ struct hostapd_hw_modes *mode;
+ int i;
+
+ mode = hapd->iface->current_mode;
+
+ for(i = 0; i < n_chans; i++) {
+ channel = &mode->channels[start_chan_idx + i];
+ if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
+ HOSTAPD_CHAN_DFS_AVAILABLE)
+ break;
+ }
+
+ return i == n_chans;
+}
+
+
+/* At least one channel unavailable */
+static int dfs_check_chans_unavailable(struct hostapd_data *hapd,
+ int start_chan_idx,
+ int n_chans)
+{
+ struct hostapd_channel_data *channel;
+ struct hostapd_hw_modes *mode;
+ int i, res = 0;
+
+ mode = hapd->iface->current_mode;
+
+ for(i = 0; i < n_chans; i++) {
+ channel = &mode->channels[start_chan_idx + i];
+ if (channel->flag & HOSTAPD_CHAN_DISABLED)
+ res++;
+ if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
+ HOSTAPD_CHAN_DFS_UNAVAILABLE)
+ res++;
+ }
+
+ return res;
+}
+
+
+static struct hostapd_channel_data * dfs_get_valid_channel(
+ struct hostapd_data *hapd)
+{
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan = NULL;
+ int channel_idx, new_channel_idx;
+ u32 _rand;
+
+ wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
+
+ if (hapd->iface->current_mode == NULL)
+ return NULL;
+
+ mode = hapd->iface->current_mode;
+ if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return NULL;
+
+ /* get random available channel */
+ channel_idx = dfs_find_channel(hapd, NULL, 0);
+ if (channel_idx > 0) {
+ os_get_random((u8 *) &_rand, sizeof(_rand));
+ new_channel_idx = _rand % channel_idx;
+ dfs_find_channel(hapd, &chan, new_channel_idx);
+ }
+
+ /* VHT */
+ dfs_adjust_vht_center_freq(hapd, chan);
+
+ return chan;
+}
+
+
+static int set_dfs_state_freq(struct hostapd_data *hapd, int freq, u32 state)
+{
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan = NULL;
+ int i;
+
+ mode = hapd->iface->current_mode;
+ if (mode == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
+ for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
+ chan = &hapd->iface->current_mode->channels[i];
+ if (chan->freq == freq) {
+ if (chan->flag & HOSTAPD_CHAN_RADAR) {
+ chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
+ chan->flag |= state;
+ return 1; /* Channel found */
+ }
+ }
+ }
+ wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
+ return 0;
+}
+
+
+static int set_dfs_state(struct hostapd_data *hapd, int freq, int ht_enabled,
+ int chan_offset, int chan_width, int cf1,
+ int cf2, u32 state)
+{
+ int n_chans = 1, i;
+ struct hostapd_hw_modes *mode;
+ int frequency = freq;
+ int ret = 0;
+
+ mode = hapd->iface->current_mode;
+ if (mode == NULL)
+ return 0;
+
+ if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
+ wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
+ return 0;
+ }
+
+ /* Seems cf1 and chan_width is enough here */
+ switch (chan_width) {
+ case CHAN_WIDTH_20_NOHT:
+ case CHAN_WIDTH_20:
+ n_chans = 1;
+ frequency = cf1;
+ break;
+ case CHAN_WIDTH_40:
+ n_chans = 2;
+ frequency = cf1 - 10;
+ break;
+ case CHAN_WIDTH_80:
+ n_chans = 4;
+ frequency = cf1 - 30;
+ break;
+ case CHAN_WIDTH_160:
+ n_chans = 8;
+ frequency = cf1 - 70;
+ break;
+ default:
+ wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
+ chan_width);
+ break;
+ }
+
+ wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
+ n_chans);
+ for (i = 0; i < n_chans; i++) {
+ ret += set_dfs_state_freq(hapd, frequency, state);
+ frequency = frequency + 20;
+ }
+
+ return ret;
+}
+
+
+static int dfs_are_channels_overlapped(struct hostapd_data *hapd, int freq,
+ int chan_width, int cf1, int cf2)
+{
+ int start_chan_idx;
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan;
+ int n_chans, i, j, frequency = freq, radar_n_chans = 1;
+ u8 radar_chan;
+ int res = 0;
+
+ if (hapd->iface->freq == freq)
+ res++;
+
+ /* Our configuration */
+ mode = hapd->iface->current_mode;
+ start_chan_idx = dfs_get_start_chan_idx(hapd);
+ n_chans = dfs_get_used_n_chans(hapd);
+
+ /* Reported via radar event */
+ switch (chan_width) {
+ case CHAN_WIDTH_20_NOHT:
+ case CHAN_WIDTH_20:
+ radar_n_chans = 1;
+ frequency = cf1;
+ break;
+ case CHAN_WIDTH_40:
+ radar_n_chans = 2;
+ frequency = cf1 - 10;
+ break;
+ case CHAN_WIDTH_80:
+ radar_n_chans = 4;
+ frequency = cf1 - 30;
+ break;
+ case CHAN_WIDTH_160:
+ radar_n_chans = 8;
+ frequency = cf1 - 70;
+ break;
+ default:
+ wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
+ chan_width);
+ break;
+ }
+
+ ieee80211_freq_to_chan(frequency, &radar_chan);
+
+ for (i = 0; i < n_chans; i++) {
+ chan = &mode->channels[start_chan_idx + i];
+ for (j = 0; j < radar_n_chans; j++) {
+ wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
+ chan->chan, radar_chan + j * 4);
+ if (chan->chan == radar_chan + j * 4)
+ res++;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "overlapped: %d", res);
+
+ return res;
+}
+
+
+/*
+ * Main DFS handler
+ * 1 - continue channel/ap setup
+ * 0 - channel/ap setup will be continued after CAC
+ * -1 - hit critical error
+ */
+int hostapd_handle_dfs(struct hostapd_data *hapd)
+{
+ struct hostapd_channel_data *channel;
+ int res, n_chans, start_chan_idx;
+
+ do {
+ /* Get start (first) channel for current configuration */
+ start_chan_idx = dfs_get_start_chan_idx(hapd);
+ if (start_chan_idx == -1)
+ return -1;
+
+ /* Get number of used channels, depend on width */
+ n_chans = dfs_get_used_n_chans(hapd);
+
+ /* Check if any of configured channels require DFS */
+ res = dfs_check_chans_radar(hapd, start_chan_idx, n_chans);
+ wpa_printf(MSG_DEBUG,
+ "DFS %d channels required radar detection",
+ res);
+ if (!res)
+ return 1;
+
+ /* Check if all channels are DFS available */
+ res = dfs_check_chans_available(hapd, start_chan_idx, n_chans);
+ wpa_printf(MSG_DEBUG,
+ "DFS all channels available, (SKIP CAC): %s",
+ res ? "yes" : "no");
+ if (res)
+ return 1;
+
+ /* Check if any of configured channels is unavailable */
+ res = dfs_check_chans_unavailable(hapd, start_chan_idx,
+ n_chans);
+ wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
+ res, res ? "yes": "no");
+ if (res) {
+ channel = dfs_get_valid_channel(hapd);
+ if (!channel) {
+ wpa_printf(MSG_ERROR, "could not get valid channel");
+ return -1;
+ }
+ hapd->iconf->channel = channel->chan;
+ hapd->iface->freq = channel->freq;
+ }
+ } while (res);
+
+ /* Finally start CAC */
+ wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", hapd->iface->freq);
+ if (hostapd_start_dfs_cac(hapd, hapd->iconf->hw_mode,
+ hapd->iface->freq,
+ hapd->iconf->channel,
+ hapd->iconf->ieee80211n,
+ hapd->iconf->ieee80211ac,
+ hapd->iconf->secondary_channel,
+ hapd->iconf->vht_oper_chwidth,
+ hapd->iconf->vht_oper_centr_freq_seg0_idx,
+ hapd->iconf->vht_oper_centr_freq_seg1_idx)) {
+ wpa_printf(MSG_DEBUG, "DFS start_dfs_cac() failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int hostapd_dfs_complete_cac(struct hostapd_data *hapd, int success, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2)
+{
+ struct hostapd_channel_data *channel;
+ int err = 1;
+
+ if (success) {
+ /* Complete iface/ap configuration */
+ set_dfs_state(hapd, freq, ht_enabled, chan_offset,
+ chan_width, cf1, cf2,
+ HOSTAPD_CHAN_DFS_AVAILABLE);
+ hostapd_setup_interface_complete(hapd->iface, 0);
+ } else {
+ /* Switch to new channel */
+ set_dfs_state(hapd, freq, ht_enabled, chan_offset,
+ chan_width, cf1, cf2,
+ HOSTAPD_CHAN_DFS_UNAVAILABLE);
+ channel = dfs_get_valid_channel(hapd);
+ if (channel) {
+ hapd->iconf->channel = channel->chan;
+ hapd->iface->freq = channel->freq;
+ err = 0;
+ } else
+ wpa_printf(MSG_ERROR, "No valid channel available");
+
+ hostapd_setup_interface_complete(hapd->iface, err);
+ }
+
+ return 0;
+}
+
+
+static int hostapd_dfs_start_channel_switch(struct hostapd_data *hapd)
+{
+ struct hostapd_channel_data *channel;
+ int err = 1;
+
+ wpa_printf(MSG_DEBUG, "%s called", __func__);
+ channel = dfs_get_valid_channel(hapd);
+ if (channel) {
+ hapd->iconf->channel = channel->chan;
+ hapd->iface->freq = channel->freq;
+ err = 0;
+ }
+
+ hapd->driver->stop_ap(hapd->drv_priv);
+
+ hostapd_setup_interface_complete(hapd->iface, err);
+ return 0;
+}
+
+
+int hostapd_dfs_radar_detected(struct hostapd_data *hapd, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2)
+{
+ int res;
+
+ if (!hapd->iconf->ieee80211h)
+ return 0;
+
+ /* mark radar frequency as invalid */
+ res = set_dfs_state(hapd, freq, ht_enabled, chan_offset,
+ chan_width, cf1, cf2,
+ HOSTAPD_CHAN_DFS_UNAVAILABLE);
+
+ /* Skip if reported radar event not overlapped our channels */
+ res = dfs_are_channels_overlapped(hapd, freq, chan_width, cf1, cf2);
+ if (!res)
+ return 0;
+
+ /* we are working on non-DFS channel - skip event */
+ if (res == 0)
+ return 0;
+
+ /* radar detected while operating, switch the channel. */
+ res = hostapd_dfs_start_channel_switch(hapd);
+
+ return res;
+}
+
+
+int hostapd_dfs_nop_finished(struct hostapd_data *hapd, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2)
+{
+ /* TODO add correct implementation here */
+ set_dfs_state(hapd, freq, ht_enabled, chan_offset, chan_width, cf1, cf2,
+ HOSTAPD_CHAN_DFS_USABLE);
+ return 0;
+}
diff --git a/src/ap/dfs.h b/src/ap/dfs.h
new file mode 100644
index 0000000..c9f0578
--- /dev/null
+++ b/src/ap/dfs.h
@@ -0,0 +1,25 @@
+/*
+ * DFS - Dynamic Frequency Selection
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#ifndef DFS_H
+#define DFS_H
+
+int hostapd_handle_dfs(struct hostapd_data *hapd);
+
+int hostapd_dfs_complete_cac(struct hostapd_data *hapd, int success, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2);
+int hostapd_dfs_radar_detected(struct hostapd_data *hapd, int freq,
+ int ht_enabled,
+ int chan_offset, int chan_width,
+ int cf1, int cf2);
+int hostapd_dfs_nop_finished(struct hostapd_data *hapd, int freq,
+ int ht_enabled,
+ int chan_offset, int chan_width, int cf1, int cf2);
+
+#endif /* DFS_H */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index d6bc98d..b30da14 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -29,6 +29,7 @@
#include "ap_drv_ops.h"
#include "ap_config.h"
#include "hw_features.h"
+#include "dfs.h"
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
@@ -114,6 +115,13 @@
}
#endif /* CONFIG_P2P */
+#ifdef CONFIG_INTERWORKING
+ if (elems.ext_capab && elems.ext_capab_len > 4) {
+ if (elems.ext_capab[4] & 0x01)
+ sta->qos_map_enabled = 1;
+ }
+#endif /* CONFIG_INTERWORKING */
+
#ifdef CONFIG_HS20
wpabuf_free(sta->hs20_ie);
if (elems.hs20 && elems.hs20_len > 4) {
@@ -785,6 +793,50 @@
}
+#ifdef NEED_AP_MLME
+
+static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd,
+ struct dfs_event *radar)
+{
+ wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq);
+ hostapd_dfs_radar_detected(hapd, radar->freq, radar->ht_enabled,
+ radar->chan_offset, radar->chan_width,
+ radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd,
+ struct dfs_event *radar)
+{
+ wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq);
+ hostapd_dfs_complete_cac(hapd, 1, radar->freq, radar->ht_enabled,
+ radar->chan_offset, radar->chan_width,
+ radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_aborted(struct hostapd_data *hapd,
+ struct dfs_event *radar)
+{
+ wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq);
+ hostapd_dfs_complete_cac(hapd, 0, radar->freq, radar->ht_enabled,
+ radar->chan_offset, radar->chan_width,
+ radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_nop_finished(struct hostapd_data *hapd,
+ struct dfs_event *radar)
+{
+ wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq);
+ hostapd_dfs_nop_finished(hapd, radar->freq, radar->ht_enabled,
+ radar->chan_offset, radar->chan_width,
+ radar->cf1, radar->cf2);
+}
+
+#endif /* NEED_AP_MLME */
+
+
void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
@@ -929,6 +981,34 @@
case EVENT_SURVEY:
hostapd_event_get_survey(hapd, &data->survey_results);
break;
+#ifdef NEED_AP_MLME
+ case EVENT_DFS_RADAR_DETECTED:
+ if (!data)
+ break;
+ hostapd_event_dfs_radar_detected(hapd, &data->dfs_event);
+ break;
+ case EVENT_DFS_CAC_FINISHED:
+ if (!data)
+ break;
+ hostapd_event_dfs_cac_finished(hapd, &data->dfs_event);
+ break;
+ case EVENT_DFS_CAC_ABORTED:
+ if (!data)
+ break;
+ hostapd_event_dfs_cac_aborted(hapd, &data->dfs_event);
+ break;
+ case EVENT_DFS_NOP_FINISHED:
+ if (!data)
+ break;
+ hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
+ break;
+ case EVENT_CHANNEL_LIST_CHANGED:
+ /* channel list changed (regulatory?), update channel list */
+ /* TODO: check this. hostapd_get_hw_features() initializes
+ * too much stuff. */
+ /* hostapd_get_hw_features(hapd->iface); */
+ break;
+#endif /* NEED_AP_MLME */
default:
wpa_printf(MSG_DEBUG, "Unknown event %d", event);
break;
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 575ef2a..3bca385 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -32,6 +32,7 @@
#include "ap_config.h"
#include "p2p_hostapd.h"
#include "gas_serv.h"
+#include "dfs.h"
static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
@@ -931,6 +932,9 @@
"be completed in a callback");
return 0;
}
+
+ if (iface->conf->ieee80211h)
+ wpa_printf(MSG_DEBUG, "DFS support is enabled");
}
return hostapd_setup_interface_complete(iface, 0);
}
@@ -950,12 +954,23 @@
wpa_printf(MSG_DEBUG, "Completing interface initialization");
if (hapd->iconf->channel) {
+#ifdef NEED_AP_MLME
+ int res;
+#endif /* NEED_AP_MLME */
+
iface->freq = hostapd_hw_get_freq(hapd, hapd->iconf->channel);
wpa_printf(MSG_DEBUG, "Mode: %s Channel: %d "
"Frequency: %d MHz",
hostapd_hw_mode_txt(hapd->iconf->hw_mode),
hapd->iconf->channel, iface->freq);
+#ifdef NEED_AP_MLME
+ /* Check DFS */
+ res = hostapd_handle_dfs(hapd);
+ if (res <= 0)
+ return res;
+#endif /* NEED_AP_MLME */
+
if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
hapd->iconf->channel,
hapd->iconf->ieee80211n,
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 8a239f4..9e5becc 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -45,6 +45,36 @@
}
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static char * dfs_info(struct hostapd_channel_data *chan)
+{
+ static char info[256];
+ char *state;
+
+ switch (chan->flag & HOSTAPD_CHAN_DFS_MASK) {
+ case HOSTAPD_CHAN_DFS_UNKNOWN:
+ state = "unknown";
+ break;
+ case HOSTAPD_CHAN_DFS_USABLE:
+ state = "usable";
+ break;
+ case HOSTAPD_CHAN_DFS_UNAVAILABLE:
+ state = "unavailable";
+ break;
+ case HOSTAPD_CHAN_DFS_AVAILABLE:
+ state = "available";
+ break;
+ default:
+ return "";
+ }
+ os_snprintf(info, sizeof(info), " (DFS state = %s)", state);
+ info[sizeof(info) - 1] = '\0';
+
+ return info;
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
int hostapd_get_hw_features(struct hostapd_iface *iface)
{
struct hostapd_data *hapd = iface->bss[0];
@@ -71,30 +101,40 @@
for (i = 0; i < num_modes; i++) {
struct hostapd_hw_modes *feature = &modes[i];
+ int dfs_enabled = hapd->iconf->ieee80211h &&
+ (iface->drv_flags & WPA_DRIVER_FLAGS_RADAR);
+
/* set flag for channels we can use in current regulatory
* domain */
for (j = 0; j < feature->num_channels; j++) {
+ int dfs = 0;
+
/*
* Disable all channels that are marked not to allow
- * IBSS operation or active scanning. In addition,
- * disable all channels that require radar detection,
- * since that (in addition to full DFS) is not yet
- * supported.
+ * IBSS operation or active scanning.
+ * Use radar channels only if the driver supports DFS.
*/
- if (feature->channels[j].flag &
- (HOSTAPD_CHAN_NO_IBSS |
- HOSTAPD_CHAN_PASSIVE_SCAN |
- HOSTAPD_CHAN_RADAR))
+ if ((feature->channels[j].flag &
+ HOSTAPD_CHAN_RADAR) && dfs_enabled) {
+ dfs = 1;
+ } else if (feature->channels[j].flag &
+ (HOSTAPD_CHAN_NO_IBSS |
+ HOSTAPD_CHAN_PASSIVE_SCAN |
+ HOSTAPD_CHAN_RADAR)) {
feature->channels[j].flag |=
HOSTAPD_CHAN_DISABLED;
+ }
+
if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED)
continue;
+
wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d "
- "chan=%d freq=%d MHz max_tx_power=%d dBm",
+ "chan=%d freq=%d MHz max_tx_power=%d dBm%s",
feature->mode,
feature->channels[j].chan,
feature->channels[j].freq,
- feature->channels[j].max_tx_power);
+ feature->channels[j].max_tx_power,
+ dfs ? dfs_info(&feature->channels[j]) : "");
}
}
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 781f826..c7db7f4 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -859,6 +859,21 @@
}
+static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ext_capab_ie, size_t ext_capab_ie_len)
+{
+#ifdef CONFIG_INTERWORKING
+ /* check for QoS Map support */
+ if (ext_capab_ie_len >= 5) {
+ if (ext_capab_ie[4] & 0x01)
+ sta->qos_map_enabled = 1;
+ }
+#endif /* CONFIG_INTERWORKING */
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ies, size_t ies_len, int reassoc)
{
@@ -881,6 +896,9 @@
resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
+ resp = check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
resp = copy_supp_rates(hapd, sta, &elems);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
@@ -1169,6 +1187,8 @@
p = hostapd_eid_ext_capab(hapd, p);
p = hostapd_eid_bss_max_idle_period(hapd, p);
+ if (sta->qos_map_enabled)
+ p = hostapd_eid_qos_map_set(hapd, p);
if (sta->flags & WLAN_STA_WMM)
p = hostapd_eid_wmm(hapd, p);
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 2aab56d..61f1316 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -41,6 +41,7 @@
u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
int probe);
u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index c36bbe3..4172218 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -189,6 +189,8 @@
*pos |= 0x80; /* Bit 31 - Interworking */
break;
case 4: /* Bits 32-39 */
+ if (hapd->conf->qos_map_set_len)
+ *pos |= 0x01; /* Bit 32 - QoS Map */
if (hapd->conf->tdls & TDLS_PROHIBIT)
*pos |= 0x40; /* Bit 38 - TDLS Prohibited */
if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) {
@@ -250,6 +252,23 @@
}
+u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+ u8 len = hapd->conf->qos_map_set_len;
+
+ if (!len)
+ return eid;
+
+ *pos++ = WLAN_EID_QOS_MAP_SET;
+ *pos++ = len;
+ os_memcpy(pos, hapd->conf->qos_map_set, len);
+ pos += len;
+
+ return pos;
+}
+
+
u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid)
{
u8 *pos = eid;
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index e5b5069..197e46b 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -57,6 +57,7 @@
unsigned int no_ht_set:1;
unsigned int ht_20mhz_set:1;
unsigned int no_p2p_set:1;
+ unsigned int qos_map_enabled:1;
u16 auth_alg;
u8 previous_ap[6];