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];
diff --git a/src/common/defs.h b/src/common/defs.h
index 281dd8a..0c90c24 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -312,6 +312,7 @@
WPA_CTRL_REQ_EAP_PIN,
WPA_CTRL_REQ_EAP_OTP,
WPA_CTRL_REQ_EAP_PASSPHRASE,
+ WPA_CTRL_REQ_SIM,
NUM_WPA_CTRL_REQS
};
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index aab8ac6..304dfc6 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -276,6 +276,12 @@
elems->interworking = pos;
elems->interworking_len = elen;
break;
+ case WLAN_EID_QOS_MAP_SET:
+ if (elen < 16)
+ break;
+ elems->qos_map_set = pos;
+ elems->qos_map_set_len = elen;
+ break;
case WLAN_EID_EXT_CAPAB:
elems->ext_capab = pos;
elems->ext_capab_len = elen;
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 68c6b96..c4618b2 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -40,6 +40,7 @@
const u8 *wfd;
const u8 *link_id;
const u8 *interworking;
+ const u8 *qos_map_set;
const u8 *hs20;
const u8 *ext_capab;
const u8 *bss_max_idle_period;
@@ -73,6 +74,7 @@
u8 p2p_len;
u8 wfd_len;
u8 interworking_len;
+ u8 qos_map_set_len;
u8 hs20_len;
u8 ext_capab_len;
u8 ssid_list_len;
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 137c309..ca38701 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -203,6 +203,7 @@
#define WLAN_EID_TIM 5
#define WLAN_EID_IBSS_PARAMS 6
#define WLAN_EID_COUNTRY 7
+#define WLAN_EID_BSS_LOAD 11
#define WLAN_EID_CHALLENGE 16
/* EIDs defined by IEEE 802.11h - START */
#define WLAN_EID_PWR_CONSTRAINT 32
@@ -242,6 +243,7 @@
#define WLAN_EID_LINK_ID 101
#define WLAN_EID_INTERWORKING 107
#define WLAN_EID_ADV_PROTO 108
+#define WLAN_EID_QOS_MAP_SET 110
#define WLAN_EID_ROAMING_CONSORTIUM 111
#define WLAN_EID_EXT_CAPAB 127
#define WLAN_EID_CCKM 156
@@ -1079,6 +1081,15 @@
#define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES 70
#define WNM_NEIGHBOR_MULTIPLE_BSSID 71
+/* QoS action */
+enum qos_action {
+ QOS_ADDTS_REQ = 0,
+ QOS_ADDTS_RESP = 1,
+ QOS_DELTS = 2,
+ QOS_SCHEDULE = 3,
+ QOS_QOS_MAP_CONFIG = 4,
+};
+
/* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */
#define WLAN_20_40_BSS_COEX_INFO_REQ BIT(0)
#define WLAN_20_40_BSS_COEX_40MHZ_INTOL BIT(1)
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 2fdaa02..feba13f 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -40,7 +40,8 @@
TLS_FAIL_SUBJECT_MISMATCH = 5,
TLS_FAIL_ALTSUBJECT_MISMATCH = 6,
TLS_FAIL_BAD_CERTIFICATE = 7,
- TLS_FAIL_SERVER_CHAIN_PROBE = 8
+ TLS_FAIL_SERVER_CHAIN_PROBE = 8,
+ TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9
};
union tls_event_data {
@@ -96,6 +97,8 @@
* %NULL to allow all subjects
* @altsubject_match: String to match in the alternative subject of the peer
* certificate or %NULL to allow all alternative subjects
+ * @suffix_match: String to suffix match in the dNSName or CN of the peer
+ * certificate or %NULL to allow all domain names
* @client_cert: File or reference name for client X.509 certificate in PEM or
* DER format
* @client_cert_blob: client_cert as inlined data or %NULL if not used
@@ -137,6 +140,7 @@
const char *ca_path;
const char *subject_match;
const char *altsubject_match;
+ const char *suffix_match;
const char *client_cert;
const u8 *client_cert_blob;
size_t client_cert_blob_len;
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 18e0e78..48c4876 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -93,7 +93,7 @@
ENGINE *engine; /* functional reference to the engine */
EVP_PKEY *private_key; /* the private key if using engine */
#endif /* OPENSSL_NO_ENGINE */
- char *subject_match, *altsubject_match;
+ char *subject_match, *altsubject_match, *suffix_match;
int read_alerts, write_alerts, failed;
tls_session_ticket_cb session_ticket_cb;
@@ -1049,6 +1049,7 @@
tls_engine_deinit(conn);
os_free(conn->subject_match);
os_free(conn->altsubject_match);
+ os_free(conn->suffix_match);
os_free(conn->session_ticket);
os_free(conn);
}
@@ -1139,6 +1140,97 @@
}
+static int domain_suffix_match(const u8 *val, size_t len, const char *match)
+{
+ size_t i, match_len;
+
+ /* Check for embedded nuls that could mess up suffix matching */
+ for (i = 0; i < len; i++) {
+ if (val[i] == '\0') {
+ wpa_printf(MSG_DEBUG, "TLS: Embedded null in a string - reject");
+ return 0;
+ }
+ }
+
+ match_len = os_strlen(match);
+ if (match_len > len)
+ return 0;
+
+ if (os_strncasecmp((const char *) val + len - match_len, match,
+ match_len) != 0)
+ return 0; /* no match */
+
+ if (match_len == len)
+ return 1; /* exact match */
+
+ if (val[len - match_len - 1] == '.')
+ return 1; /* full label match completes suffix match */
+
+ wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match");
+ return 0;
+}
+
+
+static int tls_match_suffix(X509 *cert, const char *match)
+{
+ GENERAL_NAME *gen;
+ void *ext;
+ int i;
+ int dns_name = 0;
+ X509_NAME *name;
+
+ wpa_printf(MSG_DEBUG, "TLS: Match domain against suffix %s", match);
+
+ ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+
+ for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
+ gen = sk_GENERAL_NAME_value(ext, i);
+ if (gen->type != GEN_DNS)
+ continue;
+ dns_name++;
+ wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName",
+ gen->d.dNSName->data,
+ gen->d.dNSName->length);
+ if (domain_suffix_match(gen->d.dNSName->data,
+ gen->d.dNSName->length, match) == 1) {
+ wpa_printf(MSG_DEBUG, "TLS: Suffix match in dNSName found");
+ return 1;
+ }
+ }
+
+ if (dns_name) {
+ wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched");
+ return 0;
+ }
+
+ name = X509_get_subject_name(cert);
+ i = -1;
+ for (;;) {
+ X509_NAME_ENTRY *e;
+ ASN1_STRING *cn;
+
+ i = X509_NAME_get_index_by_NID(name, NID_commonName, i);
+ if (i == -1)
+ break;
+ e = X509_NAME_get_entry(name, i);
+ if (e == NULL)
+ continue;
+ cn = X509_NAME_ENTRY_get_data(e);
+ if (cn == NULL)
+ continue;
+ wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
+ cn->data, cn->length);
+ if (domain_suffix_match(cn->data, cn->length, match) == 1) {
+ wpa_printf(MSG_DEBUG, "TLS: Suffix match in commonName found");
+ return 1;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "TLS: No CommonName suffix match found");
+ return 0;
+}
+
+
static enum tls_fail_reason openssl_tls_fail_reason(int err)
{
switch (err) {
@@ -1267,7 +1359,7 @@
SSL *ssl;
struct tls_connection *conn;
struct tls_context *context;
- char *match, *altmatch;
+ char *match, *altmatch, *suffix_match;
const char *err_str;
err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
@@ -1289,6 +1381,7 @@
context = conn->context;
match = conn->subject_match;
altmatch = conn->altsubject_match;
+ suffix_match = conn->suffix_match;
if (!preverify_ok && !conn->ca_cert_verify)
preverify_ok = 1;
@@ -1357,6 +1450,14 @@
openssl_tls_fail_event(conn, err_cert, err, depth, buf,
"AltSubject mismatch",
TLS_FAIL_ALTSUBJECT_MISMATCH);
+ } else if (depth == 0 && suffix_match &&
+ !tls_match_suffix(err_cert, suffix_match)) {
+ wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found",
+ suffix_match);
+ preverify_ok = 0;
+ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "Domain suffix mismatch",
+ TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
} else
openssl_tls_cert_event(conn, err_cert, depth, buf);
@@ -1619,7 +1720,8 @@
static int tls_connection_set_subject_match(struct tls_connection *conn,
const char *subject_match,
- const char *altsubject_match)
+ const char *altsubject_match,
+ const char *suffix_match)
{
os_free(conn->subject_match);
conn->subject_match = NULL;
@@ -1637,6 +1739,14 @@
return -1;
}
+ os_free(conn->suffix_match);
+ conn->suffix_match = NULL;
+ if (suffix_match) {
+ conn->suffix_match = os_strdup(suffix_match);
+ if (conn->suffix_match == NULL)
+ return -1;
+ }
+
return 0;
}
@@ -2909,7 +3019,7 @@
wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required");
return 0;
}
- wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue");
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue");
return 1;
}
@@ -2974,7 +3084,8 @@
}
if (tls_connection_set_subject_match(conn,
params->subject_match,
- params->altsubject_match))
+ params->altsubject_match,
+ params->suffix_match))
return -1;
if (params->engine && params->ca_cert_id) {
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index edd05c4..fd75cd2 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -2572,6 +2572,15 @@
u8 *buf, u16 *buf_len);
/**
+ * set_qos_map - Set QoS Map
+ * @priv: Private driver interface data
+ * @qos_map_set: QoS Map
+ * @qos_map_set_len: Length of QoS Map
+ */
+ int (*set_qos_map)(void *priv, const u8 *qos_map_set,
+ u8 qos_map_set_len);
+
+ /**
* signal_poll - Get current connection information
* @priv: Private driver interface data
* @signal_info: Connection info structure
@@ -2746,10 +2755,10 @@
/**
* start_dfs_cac - Listen for radar interference on the channel
* @priv: Private driver interface data
- * @freq: Frequency (in MHz) of the channel
+ * @freq: Channel parameters
* Returns: 0 on success, -1 on failure
*/
- int (*start_dfs_cac)(void *priv, int freq);
+ int (*start_dfs_cac)(void *priv, struct hostapd_freq_params *freq);
/**
* stop_ap - Removes beacon from AP
@@ -3970,6 +3979,11 @@
*/
struct dfs_event {
int freq;
+ int ht_enabled;
+ int chan_offset;
+ enum chan_width chan_width;
+ int cf1;
+ int cf2;
} dfs_event;
/**
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index b7e92dd..2921afb 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -872,8 +872,59 @@
event.rx_mgmt.frame_len = len;
wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
}
+
#endif /* CONFIG_HS20 */
+
+static int atheros_set_qos_map(void *ctx, const u8 *qos_map_set,
+ u8 qos_map_set_len)
+{
+#ifdef CONFIG_ATHEROS_QOS_MAP
+ struct atheros_driver_data *drv = ctx;
+ struct ieee80211req_athdbg req;
+ struct ieee80211_qos_map *qos_map = &req.data.qos_map;
+ struct iwreq iwr;
+ int i, up_start;
+
+ if (qos_map_set_len < 16 || qos_map_set_len > 58 ||
+ qos_map_set_len & 1) {
+ wpa_printf(MSG_ERROR, "Invalid QoS Map");
+ return -1;
+ } else {
+ memset(&req, 0, sizeof(struct ieee80211req_athdbg));
+ req.cmd = IEEE80211_DBGREQ_SETQOSMAPCONF;
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strncpy(iwr.ifr_name, drv->iface, sizeof(iwr.ifr_name));
+ iwr.u.data.pointer = (void *) &req;
+ iwr.u.data.length = sizeof(struct ieee80211req_athdbg);
+ }
+
+ qos_map->valid = 1;
+ qos_map->num_dscp_except = (qos_map_set_len - 16) / 2;
+ if (qos_map->num_dscp_except) {
+ for (i = 0; i < qos_map->num_dscp_except; i++) {
+ qos_map->dscp_exception[i].dscp = qos_map_set[i * 2];
+ qos_map->dscp_exception[i].up = qos_map_set[i * 2 + 1];
+ }
+ }
+
+ up_start = qos_map_set_len - 16;
+ for (i = 0; i < IEEE80211_MAX_QOS_UP_RANGE; i++) {
+ qos_map->up[i].low = qos_map_set[up_start + (i * 2)];
+ qos_map->up[i].high = qos_map_set[up_start + (i * 2) + 1];
+ }
+
+ if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_DBGREQ, &iwr) < 0) {
+ perror("ioctl[IEEE80211_IOCTL_DBGREQ]");
+ wpa_printf(MSG_DEBUG, "%s: %s: Failed to set QoS Map",
+ __func__, drv->iface);
+ return -1;
+ }
+#endif /* CONFIG_ATHEROS_QOS_MAP */
+
+ return 0;
+}
+
#if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R)
static void atheros_raw_recv_11v(void *ctx, const u8 *src_addr, const u8 *buf,
size_t len)
@@ -2153,4 +2204,5 @@
#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM)
.wnm_oper = atheros_wnm_oper,
#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */
+ .set_qos_map = atheros_set_qos_map,
};
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index c098150..508022f 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -2513,6 +2513,8 @@
}
+static enum chan_width convert2width(int width);
+
static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
struct nlattr **tb)
{
@@ -2523,11 +2525,40 @@
return;
os_memset(&data, 0, sizeof(data));
- data.dfs_event.freq = nla_get_u16(tb[NL80211_ATTR_WIPHY_FREQ]);
- event_type = nla_get_u8(tb[NL80211_ATTR_RADAR_EVENT]);
+ data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+ event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]);
- wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz",
- data.dfs_event.freq);
+ /* Check HT params */
+ if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+ data.dfs_event.ht_enabled = 1;
+ data.dfs_event.chan_offset = 0;
+
+ switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) {
+ case NL80211_CHAN_NO_HT:
+ data.dfs_event.ht_enabled = 0;
+ break;
+ case NL80211_CHAN_HT20:
+ break;
+ case NL80211_CHAN_HT40PLUS:
+ data.dfs_event.chan_offset = 1;
+ break;
+ case NL80211_CHAN_HT40MINUS:
+ data.dfs_event.chan_offset = -1;
+ break;
+ }
+ }
+
+ /* Get VHT params */
+ data.dfs_event.chan_width =
+ convert2width(nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]));
+ data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+ if (tb[NL80211_ATTR_CENTER_FREQ2])
+ data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+
+ wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz",
+ data.dfs_event.freq, data.dfs_event.ht_enabled,
+ data.dfs_event.chan_offset, data.dfs_event.chan_width,
+ data.dfs_event.cf1, data.dfs_event.cf2);
switch (event_type) {
case NL80211_RADAR_DETECTED:
@@ -3798,6 +3829,11 @@
wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP "
"handle %p", bss->nl_mgmt);
+#ifdef CONFIG_INTERWORKING
+ /* QoS Map Configure */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x01\x04", 2) < 0)
+ return -1;
+#endif /* CONFIG_INTERWORKING */
#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING)
/* GAS Initial Request */
if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0)
@@ -6137,6 +6173,7 @@
{
struct wpa_driver_nl80211_data *drv = bss->drv;
u64 cookie;
+ int res;
if (freq == 0) {
wpa_printf(MSG_DEBUG, "nl80211: send_frame - Use bss->freq=%u",
@@ -6152,8 +6189,26 @@
}
wpa_printf(MSG_DEBUG, "nl80211: send_frame -> send_frame_cmd");
- return nl80211_send_frame_cmd(bss, freq, wait_time, data, len,
- &cookie, no_cck, noack, offchanok);
+ res = nl80211_send_frame_cmd(bss, freq, wait_time, data, len,
+ &cookie, no_cck, noack, offchanok);
+ if (res == 0 && !noack) {
+ const struct ieee80211_mgmt *mgmt;
+ u16 fc;
+
+ mgmt = (const struct ieee80211_mgmt *) data;
+ fc = le_to_host16(mgmt->frame_control);
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) {
+ wpa_printf(MSG_MSGDUMP,
+ "nl80211: Update send_action_cookie from 0x%llx to 0x%llx",
+ (long long unsigned int)
+ drv->send_action_cookie,
+ (long long unsigned int) cookie);
+ drv->send_action_cookie = cookie;
+ }
+ }
+
+ return res;
}
@@ -9233,10 +9288,8 @@
wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d",
__func__, type, ifname, ifindex);
- if (ifindex <= 0)
- return -1;
-
- nl80211_remove_iface(drv, ifindex);
+ if (ifindex > 0)
+ nl80211_remove_iface(drv, ifindex);
#ifdef HOSTAPD
if (type != WPA_IF_AP_BSS)
@@ -10385,14 +10438,18 @@
}
-static int nl80211_start_radar_detection(void *priv, int freq)
+static int nl80211_start_radar_detection(void *priv,
+ struct hostapd_freq_params *freq)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
- wpa_printf(MSG_DEBUG, "nl80211: Start radar detection (CAC)");
+ wpa_printf(MSG_DEBUG, "nl80211: Start radar detection (CAC) %d MHz (ht_enabled=%d, vht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
+ freq->freq, freq->ht_enabled, freq->vht_enabled,
+ freq->bandwidth, freq->center_freq1, freq->center_freq2);
+
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_RADAR)) {
wpa_printf(MSG_DEBUG, "nl80211: Driver does not support radar "
"detection");
@@ -10405,10 +10462,53 @@
nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_RADAR_DETECT);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
- NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq);
- /* only HT20 is supported at this point */
- NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT20);
+ if (freq->vht_enabled) {
+ switch (freq->bandwidth) {
+ case 20:
+ NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+ NL80211_CHAN_WIDTH_20);
+ break;
+ case 40:
+ NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+ NL80211_CHAN_WIDTH_40);
+ break;
+ case 80:
+ if (freq->center_freq2)
+ NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+ NL80211_CHAN_WIDTH_80P80);
+ else
+ NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+ NL80211_CHAN_WIDTH_80);
+ break;
+ case 160:
+ NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+ NL80211_CHAN_WIDTH_160);
+ break;
+ default:
+ return -1;
+ }
+ NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq->center_freq1);
+ if (freq->center_freq2)
+ NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2,
+ freq->center_freq2);
+ } else if (freq->ht_enabled) {
+ switch (freq->sec_channel_offset) {
+ case -1:
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+ NL80211_CHAN_HT40MINUS);
+ break;
+ case 1:
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+ NL80211_CHAN_HT40PLUS);
+ break;
+ default:
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+ NL80211_CHAN_HT20);
+ break;
+ }
+ }
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
if (ret == 0)
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 554e7e9..3439c2d 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -1638,7 +1638,8 @@
const char *msg, size_t msglen)
{
struct eap_peer_config *config;
- char *txt = NULL, *tmp;
+ const char *txt = NULL;
+ char *tmp;
if (sm == NULL)
return;
@@ -1681,6 +1682,9 @@
case WPA_CTRL_REQ_EAP_PASSPHRASE:
config->pending_req_passphrase++;
break;
+ case WPA_CTRL_REQ_SIM:
+ txt = msg;
+ break;
default:
return;
}
@@ -1792,6 +1796,17 @@
/**
+ * eap_sm_request_sim - Request external SIM processing
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @req: EAP method specific request
+ */
+void eap_sm_request_sim(struct eap_sm *sm, const char *req)
+{
+ eap_sm_request(sm, WPA_CTRL_REQ_SIM, req, os_strlen(req));
+}
+
+
+/**
* eap_sm_notify_ctrl_attached - Notification of attached monitor
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
*
@@ -2304,6 +2319,17 @@
}
+/**
+ * eap_set_external_sim - Set external_sim flag
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @external_sim: Whether external SIM/USIM processing is used
+ */
+void eap_set_external_sim(struct eap_sm *sm, int external_sim)
+{
+ sm->external_sim = external_sim;
+}
+
+
/**
* eap_notify_pending - Notify that EAP method is ready to re-process a request
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
index f87f9b3..711f41f 100644
--- a/src/eap_peer/eap.h
+++ b/src/eap_peer/eap.h
@@ -296,6 +296,7 @@
void eap_sm_request_pin(struct eap_sm *sm);
void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len);
void eap_sm_request_passphrase(struct eap_sm *sm);
+void eap_sm_request_sim(struct eap_sm *sm, const char *req);
void eap_sm_notify_ctrl_attached(struct eap_sm *sm);
u32 eap_get_phase2_type(const char *name, int *vendor);
struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
@@ -303,6 +304,7 @@
void eap_set_fast_reauth(struct eap_sm *sm, int enabled);
void eap_set_workaround(struct eap_sm *sm, unsigned int workaround);
void eap_set_force_disabled(struct eap_sm *sm, int disabled);
+void eap_set_external_sim(struct eap_sm *sm, int external_sim);
int eap_key_available(struct eap_sm *sm);
void eap_notify_success(struct eap_sm *sm);
void eap_notify_lower_layer_success(struct eap_sm *sm);
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
index dc424d7..d3cbaca 100644
--- a/src/eap_peer/eap_aka.c
+++ b/src/eap_peer/eap_aka.c
@@ -142,6 +142,89 @@
}
+static int eap_aka_ext_sim_req(struct eap_sm *sm, struct eap_aka_data *data)
+{
+ char req[200], *pos, *end;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Use external USIM processing");
+ pos = req;
+ end = pos + sizeof(req);
+ pos += os_snprintf(pos, end - pos, "UMTS-AUTH");
+ pos += os_snprintf(pos, end - pos, ":");
+ pos += wpa_snprintf_hex(pos, end - pos, data->rand, EAP_AKA_RAND_LEN);
+ pos += os_snprintf(pos, end - pos, ":");
+ pos += wpa_snprintf_hex(pos, end - pos, data->autn, EAP_AKA_AUTN_LEN);
+
+ eap_sm_request_sim(sm, req);
+ return 1;
+}
+
+
+static int eap_aka_ext_sim_result(struct eap_sm *sm, struct eap_aka_data *data,
+ struct eap_peer_config *conf)
+{
+ char *resp, *pos;
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-AKA: Use result from external USIM processing");
+
+ resp = conf->external_sim_resp;
+ conf->external_sim_resp = NULL;
+
+ if (os_strncmp(resp, "UMTS-AUTS:", 10) == 0) {
+ pos = resp + 10;
+ if (hexstr2bin(pos, data->auts, EAP_AKA_AUTS_LEN) < 0)
+ goto invalid;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: AUTS", data->auts,
+ EAP_AKA_AUTS_LEN);
+ os_free(resp);
+ return -2;
+ }
+
+ if (os_strncmp(resp, "UMTS-AUTH:", 10) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized external USIM processing response");
+ os_free(resp);
+ return -1;
+ }
+
+ pos = resp + 10;
+ wpa_hexdump(MSG_DEBUG, "EAP-AKA: RAND", data->rand, EAP_AKA_RAND_LEN);
+
+ if (hexstr2bin(pos, data->ik, EAP_AKA_IK_LEN) < 0)
+ goto invalid;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", data->ik, EAP_AKA_IK_LEN);
+ pos += EAP_AKA_IK_LEN * 2;
+ if (*pos != ':')
+ goto invalid;
+ pos++;
+
+ if (hexstr2bin(pos, data->ck, EAP_AKA_CK_LEN) < 0)
+ goto invalid;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", data->ck, EAP_AKA_CK_LEN);
+ pos += EAP_AKA_CK_LEN * 2;
+ if (*pos != ':')
+ goto invalid;
+ pos++;
+
+ data->res_len = os_strlen(pos) / 2;
+ if (data->res_len > EAP_AKA_RES_MAX_LEN) {
+ data->res_len = 0;
+ goto invalid;
+ }
+ if (hexstr2bin(pos, data->res, data->res_len) < 0)
+ goto invalid;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: RES", data->res, data->res_len);
+
+ os_free(resp);
+ return 0;
+
+invalid:
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Invalid external USIM processing UMTS-AUTH response");
+ os_free(resp);
+ return -1;
+}
+
+
static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data)
{
struct eap_peer_config *conf;
@@ -151,6 +234,14 @@
conf = eap_get_config(sm);
if (conf == NULL)
return -1;
+
+ if (sm->external_sim) {
+ if (conf->external_sim_resp)
+ return eap_aka_ext_sim_result(sm, data, conf);
+ else
+ return eap_aka_ext_sim_req(sm, data);
+ }
+
if (conf->pcsc) {
return scard_umts_auth(sm->scard_ctx, data->rand,
data->autn, data->res, &data->res_len,
@@ -861,6 +952,9 @@
wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
"failed (AUTN seq# -> AUTS)");
return eap_aka_synchronization_failure(data, id);
+ } else if (res > 0) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Wait for external USIM processing");
+ return NULL;
} else if (res) {
wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed");
return eap_aka_client_error(data, id,
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index 42f525b..98ec1f7 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -208,6 +208,24 @@
u8 *altsubject_match;
/**
+ * domain_suffix_match - Constraint for server domain name
+ *
+ * If set, this FQDN is used as a suffix match requirement for the
+ * server certificate in SubjectAltName dNSName element(s). If a
+ * matching dNSName is found, this constraint is met. If no dNSName
+ * values are present, this constraint is matched against SubjetName CN
+ * using same suffix match comparison. Suffix match here means that the
+ * host/domain name is compared one label at a time starting from the
+ * top-level domain and all the labels in domain_suffix_match shall be
+ * included in the certificate. The certificate may include additional
+ * sub-level labels in addition to the required labels.
+ *
+ * For example, domain_suffix_match=example.com would match
+ * test.example.com but would not match test-example.com.
+ */
+ char *domain_suffix_match;
+
+ /**
* ca_cert2 - File path to CA certificate file (PEM/DER) (Phase 2)
*
* This file can have one or more trusted CA certificates. If ca_cert2
@@ -303,6 +321,14 @@
u8 *altsubject_match2;
/**
+ * domain_suffix_match2 - Constraint for server domain name
+ *
+ * This field is like domain_suffix_match, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ char *domain_suffix_match2;
+
+ /**
* eap_methods - Allowed EAP methods
*
* (vendor=EAP_VENDOR_IETF,method=EAP_TYPE_NONE) terminated list of
@@ -643,6 +669,15 @@
* 2 = require valid OCSP stapling response
*/
int ocsp;
+
+ /**
+ * external_sim_resp - Response from external SIM processing
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request external
+ * SIM/USIM processing.
+ */
+ char *external_sim_resp;
};
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
index 62c867c..9307f3f 100644
--- a/src/eap_peer/eap_i.h
+++ b/src/eap_peer/eap_i.h
@@ -348,6 +348,8 @@
struct ext_password_data *ext_pw;
struct wpabuf *ext_pw_buf;
+
+ int external_sim;
};
const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len);
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index 82ea18d..d856054 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -145,6 +145,80 @@
}
+static int eap_sim_ext_sim_req(struct eap_sm *sm, struct eap_sim_data *data)
+{
+ char req[200], *pos, *end;
+ size_t i;
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Use external SIM processing");
+ pos = req;
+ end = pos + sizeof(req);
+ pos += os_snprintf(pos, end - pos, "GSM-AUTH");
+ for (i = 0; i < data->num_chal; i++) {
+ pos += os_snprintf(pos, end - pos, ":");
+ pos += wpa_snprintf_hex(pos, end - pos, data->rand[i],
+ GSM_RAND_LEN);
+ }
+
+ eap_sm_request_sim(sm, req);
+ return 1;
+}
+
+
+static int eap_sim_ext_sim_result(struct eap_sm *sm, struct eap_sim_data *data,
+ struct eap_peer_config *conf)
+{
+ char *resp, *pos;
+ size_t i;
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-SIM: Use result from external SIM processing");
+
+ resp = conf->external_sim_resp;
+ conf->external_sim_resp = NULL;
+
+ if (os_strncmp(resp, "GSM-AUTH:", 9) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized external SIM processing response");
+ os_free(resp);
+ return -1;
+ }
+
+ pos = resp + 9;
+ for (i = 0; i < data->num_chal; i++) {
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: RAND",
+ data->rand[i], GSM_RAND_LEN);
+
+ if (hexstr2bin(pos, data->kc[i], EAP_SIM_KC_LEN) < 0)
+ goto invalid;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Kc",
+ data->kc[i], EAP_SIM_KC_LEN);
+ pos += EAP_SIM_KC_LEN * 2;
+ if (*pos != ':')
+ goto invalid;
+ pos++;
+
+ if (hexstr2bin(pos, data->sres[i], EAP_SIM_SRES_LEN) < 0)
+ goto invalid;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: SRES",
+ data->sres[i], EAP_SIM_SRES_LEN);
+ pos += EAP_SIM_SRES_LEN * 2;
+ if (i + 1 < data->num_chal) {
+ if (*pos != ':')
+ goto invalid;
+ pos++;
+ }
+ }
+
+ os_free(resp);
+ return 0;
+
+invalid:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Invalid external SIM processing GSM-AUTH response");
+ os_free(resp);
+ return -1;
+}
+
+
static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data)
{
struct eap_peer_config *conf;
@@ -154,6 +228,14 @@
conf = eap_get_config(sm);
if (conf == NULL)
return -1;
+
+ if (sm->external_sim) {
+ if (conf->external_sim_resp)
+ return eap_sim_ext_sim_result(sm, data, conf);
+ else
+ return eap_sim_ext_sim_req(sm, data);
+ }
+
if (conf->pcsc) {
if (scard_gsm_auth(sm->scard_ctx, data->rand[0],
data->sres[0], data->kc[0]) ||
@@ -605,6 +687,7 @@
const u8 *identity;
size_t identity_len;
struct eap_sim_attrs eattr;
+ int res;
wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge");
data->reauth = 0;
@@ -648,8 +731,13 @@
os_memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN);
data->num_chal = attr->num_chal;
-
- if (eap_sim_gsm_auth(sm, data)) {
+
+ res = eap_sim_gsm_auth(sm, data);
+ if (res > 0) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Wait for external SIM processing");
+ return NULL;
+ }
+ if (res) {
wpa_printf(MSG_WARNING, "EAP-SIM: GSM authentication failed");
return eap_sim_client_error(data, id,
EAP_SIM_UNABLE_TO_PROCESS_PACKET);
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index be8c301..008af37 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -78,6 +78,7 @@
params->dh_file = (char *) config->dh_file;
params->subject_match = (char *) config->subject_match;
params->altsubject_match = (char *) config->altsubject_match;
+ params->suffix_match = config->domain_suffix_match;
params->engine = config->engine;
params->engine_id = config->engine_id;
params->pin = config->pin;
@@ -99,6 +100,7 @@
params->dh_file = (char *) config->dh_file2;
params->subject_match = (char *) config->subject_match2;
params->altsubject_match = (char *) config->altsubject_match2;
+ params->suffix_match = config->domain_suffix_match2;
params->engine = config->engine2;
params->engine_id = config->engine2_id;
params->pin = config->pin2;
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index c4475e5..03ec2cb 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -1479,6 +1479,7 @@
eap_set_fast_reauth(sm->eap, conf->fast_reauth);
eap_set_workaround(sm->eap, conf->workaround);
eap_set_force_disabled(sm->eap, conf->eap_disabled);
+ eap_set_external_sim(sm->eap, conf->external_sim);
}
}
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index c4b87da..6faf816 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -53,6 +53,11 @@
* eap_disabled - Whether EAP is disabled
*/
int eap_disabled;
+
+ /**
+ * external_sim - Use external processing for SIM/USIM operations
+ */
+ int external_sim;
};
struct eapol_sm;
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 738436c..64ca006 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -1672,6 +1672,7 @@
rx_freq);
break;
case P2P_INVITATION_RESP:
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
p2p_process_invitation_resp(p2p, sa, data + 1, len - 1);
break;
case P2P_PROV_DISC_REQ:
@@ -2774,7 +2775,8 @@
MAC2STR(dev->info.p2p_device_addr),
dev->req_config_methods);
p2p_send_prov_disc_req(p2p, dev,
- dev->flags & P2P_DEV_PD_FOR_JOIN, 0);
+ dev->flags & P2P_DEV_PD_FOR_JOIN,
+ p2p->pd_force_freq);
return;
}
}
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 81e521e..f323ef7 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -450,6 +450,14 @@
*/
int pd_retries;
+ /**
+ * pd_force_freq - Forced frequency for PD retries or 0 to auto-select
+ *
+ * This is is used during PD retries for join-a-group case to use the
+ * correct operating frequency determined from a BSS entry for the GO.
+ */
+ int pd_force_freq;
+
u8 go_timeout;
u8 client_timeout;
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index 687b943..4e4593e 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -407,7 +407,7 @@
return;
}
- if (!msg.channel_list) {
+ if (!msg.channel_list && *msg.status == P2P_SC_SUCCESS) {
p2p_dbg(p2p, "Mandatory Channel List attribute missing in Invitation Response from "
MACSTR, MAC2STR(sa));
#ifdef CONFIG_P2P_STRICT
@@ -416,6 +416,9 @@
#endif /* CONFIG_P2P_STRICT */
/* Try to survive without peer channel list */
channels = &p2p->channels;
+ } else if (!msg.channel_list) {
+ /* Non-success cases are not required to include Channel List */
+ channels = &p2p->channels;
} else if (p2p_peer_channels_check(p2p, &p2p->channels, dev,
msg.channel_list,
msg.channel_list_len) < 0) {
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index 54aa428..409405f 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -437,6 +437,7 @@
}
p2p->user_initiated_pd = user_initiated_pd;
+ p2p->pd_force_freq = force_freq;
if (p2p->user_initiated_pd)
p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES;
@@ -472,4 +473,5 @@
p2p->user_initiated_pd = 0;
os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
p2p->pd_retries = 0;
+ p2p->pd_force_freq = 0;
}
diff --git a/src/wps/http_server.c b/src/wps/http_server.c
index 6ca3214..06c8bee 100644
--- a/src/wps/http_server.c
+++ b/src/wps/http_server.c
@@ -232,6 +232,7 @@
{
struct sockaddr_in sin;
struct http_server *srv;
+ int on = 1;
srv = os_zalloc(sizeof(*srv));
if (srv == NULL)
@@ -242,6 +243,9 @@
srv->fd = socket(AF_INET, SOCK_STREAM, 0);
if (srv->fd < 0)
goto fail;
+
+ setsockopt(srv->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0)
goto fail;
if (port < 0)