Cumulative patch from commit b2b688d18d40cd667d0faa149b4a7172166b3bd4
b2b688d P2P: Fix crash when failed to create GO interface
6197169 WPS NFC: Fix build without CONFIG_AP=y
e1ae5d7 SAE: Fix build without CONFIG_AP=y
813e7b3 P2P: Remove group from timeout on PSK failure
5bf9a6c P2P: Add event messages for possible PSK failures on P2P groups
eac8dab P2P: Document per-client keys and p2p_remove_client
43c693c P2P: Do not store duplicate PSK entries for the same device
f2c5660 P2P: Add a command for removing a client from all groups
01a57fe P2P: Maintain list of per-client PSKs for persistent groups
759fd76 P2P: Select PSK based on Device Address instead of Interface Address
94ddef3 P2P: Make peer's P2P Device Address available to authenticator
52177fb P2P: Store P2P Device Address in per-device PSK records
05766ed P2P: Allow per-device PSK to be assigned
698e921 wpa_cli: Add tab completion for p2p_set field values
0b5fb86 P2P: Stop listen state when listen-only duration is over
02a3e5c wpa_cli: Allow first DISCONNECTED event to be reported
cdf8bfa Disallow WEP configuration in WPA network
731ef43 D-Bus: Fix per-iface object unregistration on not existing objects
447969e D-Bus: Do not send network notification for all P2P groups
eb32460 Fix switching from EAP-SIM to EAP-AKA/AKA'
f2b3f4d P2P: Allow P2P functionality to be disabled per interface
50f4f2a hostapd: Add Automatic Channel Selection (ACS) support
43ee470 P2P: Immediate group removal in GC in case of deauthentication
fcf2052 Fix MNC length for Swisscom SIM cards
Bug: 10606228, 10513949
Change-Id: I63ba0e2ab4fa76e6afa7a34be42e8e847e1511b0
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 1054dc0..695e99c 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -884,6 +884,12 @@
OBJS_c += src/utils/edit_simple.c
endif
+ifdef CONFIG_ACS
+L_CFLAGS += -DCONFIG_ACS
+OBJS += src/ap/acs.c
+LIBS += -lm
+endif
+
########################
include $(CLEAR_VARS)
diff --git a/hostapd/Makefile b/hostapd/Makefile
index a30a244..fda4e4e 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -817,6 +817,12 @@
OBJS_c += ../src/utils/edit_simple.o
endif
+ifdef CONFIG_ACS
+CFLAGS += -DCONFIG_ACS
+OBJS += ../src/ap/acs.o
+LIBS += -lm
+endif
+
ifdef CONFIG_NO_STDOUT_DEBUG
CFLAGS += -DCONFIG_NO_STDOUT_DEBUG
endif
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index e9d324f..0b4fd77 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -1104,6 +1104,24 @@
return -1;
}
+ if (bss->wpa) {
+ int wep, i;
+
+ wep = bss->default_wep_key_len > 0 ||
+ bss->individual_wep_key_len > 0;
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (bss->ssid.wep.keys_set) {
+ wep = 1;
+ break;
+ }
+ }
+
+ if (wep) {
+ wpa_printf(MSG_ERROR, "WEP configuration in a WPA network is not supported");
+ return -1;
+ }
+ }
+
if (bss->wpa && bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
wpa_printf(MSG_ERROR, "WPA-PSK using RADIUS enabled, but no "
@@ -2311,7 +2329,15 @@
errors++;
}
} else if (os_strcmp(buf, "channel") == 0) {
- conf->channel = atoi(pos);
+ if (os_strcmp(pos, "acs_survey") == 0) {
+#ifndef CONFIG_ACS
+ wpa_printf(MSG_ERROR, "Line %d: tries to enable ACS but CONFIG_ACS disabled",
+ line);
+ errors++;
+#endif /* CONFIG_ACS */
+ conf->channel = 0;
+ } else
+ conf->channel = atoi(pos);
} else if (os_strcmp(buf, "beacon_int") == 0) {
int val = atoi(pos);
/* MIB defines range as 1..65535, but very small values
@@ -2326,6 +2352,16 @@
errors++;
} else
conf->beacon_int = val;
+#ifdef CONFIG_ACS
+ } else if (os_strcmp(buf, "acs_num_scans") == 0) {
+ int val = atoi(pos);
+ if (val <= 0 || val > 100) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid acs_num_scans %d (expected 1..100)",
+ line, val);
+ errors++;
+ } else
+ conf->acs_num_scans = val;
+#endif /* CONFIG_ACS */
} else if (os_strcmp(buf, "dtim_period") == 0) {
bss->dtim_period = atoi(pos);
if (bss->dtim_period < 1 || bss->dtim_period > 255) {
diff --git a/hostapd/defconfig b/hostapd/defconfig
index c288f46..2dd6fc8 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -278,3 +278,27 @@
# certain percentage of probe requests or auth/(re)assoc frames.
#
#CONFIG_TESTING_OPTIONS=y
+
+# Automatic Channel Selection
+# This will allow hostapd to pick the channel automatically when channel is set
+# to "acs_survey" or "0". Eventually, other ACS algorithms can be added in
+# similar way.
+#
+# Automatic selection is currently only done through initialization, later on
+# we hope to do background checks to keep us moving to more ideal channels as
+# time goes by. ACS is currently only supported through the nl80211 driver and
+# your driver must have survey dump capability that is filled by the driver
+# during scanning.
+#
+# You can customize the ACS survey algorithm with the hostapd.conf variable
+# acs_num_scans.
+#
+# Supported ACS drivers:
+# * ath9k
+# * ath5k
+# * ath10k
+#
+# For more details refer to:
+# http://wireless.kernel.org/en/users/Documentation/acs
+#
+#CONFIG_ACS=y
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index c46dff5..45897ed 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -121,8 +121,28 @@
# (default: 0, i.e., not set)
# Please note that some drivers do not use this value from hostapd and the
# channel will need to be configured separately with iwconfig.
+#
+# If CONFIG_ACS build option is enabled, the channel can be selected
+# automatically at run time by setting channel=acs_survey or channel=0, both of
+# which will enable the ACS survey based algorithm.
channel=1
+# ACS tuning - Automatic Channel Selection
+# See: http://wireless.kernel.org/en/users/Documentation/acs
+#
+# You can customize the ACS survey algorithm with following variables:
+#
+# acs_num_scans requirement is 1..100 - number of scans to be performed that
+# are used to trigger survey data gathering of an underlying device driver.
+# Scans are passive and typically take a little over 100ms (depending on the
+# driver) on each available channel for given hw_mode. Increasing this value
+# means sacrificing startup time and gathering more data wrt channel
+# interference that may help choosing a better channel. This can also help fine
+# tune the ACS scan time in case a driver has different scan dwell times.
+#
+# Defaults:
+#acs_num_scans=5
+
# Beacon interval in kus (1.024 ms) (default: 100; range 15..65535)
beacon_int=100
diff --git a/src/ap/acs.c b/src/ap/acs.c
new file mode 100644
index 0000000..d5e3f59
--- /dev/null
+++ b/src/ap/acs.c
@@ -0,0 +1,770 @@
+/*
+ * ACS - Automatic Channel Selection module
+ * Copyright (c) 2011, Atheros Communications
+ * 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 <math.h>
+
+#include "utils/common.h"
+#include "utils/list.h"
+#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+#include "hw_features.h"
+#include "acs.h"
+
+/*
+ * Automatic Channel Selection
+ * ===========================
+ *
+ * More info at
+ * ------------
+ * http://wireless.kernel.org/en/users/Documentation/acs
+ *
+ * How to use
+ * ----------
+ * - make sure you have CONFIG_ACS=y in hostapd's .config
+ * - use channel=0 or channel=acs to enable ACS
+ *
+ * How does it work
+ * ----------------
+ * 1. passive scans are used to collect survey data
+ * (it is assumed that scan trigger collection of survey data in driver)
+ * 2. interference factor is calculated for each channel
+ * 3. ideal channel is picked depending on channel width by using adjacent
+ * channel interference factors
+ *
+ * Known limitations
+ * -----------------
+ * - Current implementation depends heavily on the amount of time willing to
+ * spend gathering survey data during hostapd startup. Short traffic bursts
+ * may be missed and a suboptimal channel may be picked.
+ * - Ideal channel may end up overlapping a channel with 40 MHz intolerant BSS
+ *
+ * Todo / Ideas
+ * ------------
+ * - implement other interference computation methods
+ * - BSS/RSSI based
+ * - spectral scan based
+ * (should be possibly to hook this up with current ACS scans)
+ * - add wpa_supplicant support (for P2P)
+ * - collect a histogram of interference over time allowing more educated
+ * guess about an ideal channel (perhaps CSA could be used to migrate AP to a
+ * new "better" channel while running)
+ * - include neighboring BSS scan to avoid conflicts with 40 MHz intolerant BSSs
+ * when choosing the ideal channel
+ *
+ * Survey interference factor implementation details
+ * -------------------------------------------------
+ * Generic interference_factor in struct hostapd_channel_data is used.
+ *
+ * The survey interference factor is defined as the ratio of the
+ * observed busy time over the time we spent on the channel,
+ * this value is then amplified by the observed noise floor on
+ * the channel in comparison to the lowest noise floor observed
+ * on the entire band.
+ *
+ * This corresponds to:
+ * ---
+ * (busy time - tx time) / (active time - tx time) * 2^(chan_nf + band_min_nf)
+ * ---
+ *
+ * The coefficient of 2 reflects the way power in "far-field"
+ * radiation decreases as the square of distance from the antenna [1].
+ * What this does is it decreases the observed busy time ratio if the
+ * noise observed was low but increases it if the noise was high,
+ * proportionally to the way "far field" radiation changes over
+ * distance.
+ *
+ * If channel busy time is not available the fallback is to use channel RX time.
+ *
+ * Since noise floor is in dBm it is necessary to convert it into Watts so that
+ * combined channel interference (e.g., HT40, which uses two channels) can be
+ * calculated easily.
+ * ---
+ * (busy time - tx time) / (active time - tx time) *
+ * 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
+ * ---
+ *
+ * However to account for cases where busy/rx time is 0 (channel load is then
+ * 0%) channel noise floor signal power is combined into the equation so a
+ * channel with lower noise floor is preferred. The equation becomes:
+ * ---
+ * 10^(chan_nf/5) + (busy time - tx time) / (active time - tx time) *
+ * 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
+ * ---
+ *
+ * All this "interference factor" is purely subjective and only time
+ * will tell how usable this is. By using the minimum noise floor we
+ * remove any possible issues due to card calibration. The computation
+ * of the interference factor then is dependent on what the card itself
+ * picks up as the minimum noise, not an actual real possible card
+ * noise value.
+ *
+ * Total interference computation details
+ * --------------------------------------
+ * The above channel interference factor is calculated with no respect to
+ * target operational bandwidth.
+ *
+ * To find an ideal channel the above data is combined by taking into account
+ * the target operational bandwidth and selected band. E.g., on 2.4 GHz channels
+ * overlap with 20 MHz bandwidth, but there is no overlap for 20 MHz bandwidth
+ * on 5 GHz.
+ *
+ * Each valid and possible channel spec (i.e., channel + width) is taken and its
+ * interference factor is computed by summing up interferences of each channel
+ * it overlaps. The one with least total interference is picked up.
+ *
+ * Note: This implies base channel interference factor must be non-negative
+ * allowing easy summing up.
+ *
+ * Example ACS analysis printout
+ * -----------------------------
+ *
+ * ACS: Trying survey-based ACS
+ * ACS: Survey analysis for channel 1 (2412 MHz)
+ * ACS: 1: min_nf=-113 interference_factor=0.0802469 nf=-113 time=162 busy=0 rx=13
+ * ACS: 2: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
+ * ACS: 3: min_nf=-113 interference_factor=0.0679012 nf=-113 time=162 busy=0 rx=11
+ * ACS: 4: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
+ * ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
+ * ACS: * interference factor average: 0.0557166
+ * ACS: Survey analysis for channel 2 (2417 MHz)
+ * ACS: 1: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
+ * ACS: 2: min_nf=-113 interference_factor=0.0246914 nf=-113 time=162 busy=0 rx=4
+ * ACS: 3: min_nf=-113 interference_factor=0.037037 nf=-113 time=162 busy=0 rx=6
+ * ACS: 4: min_nf=-113 interference_factor=0.149068 nf=-113 time=161 busy=0 rx=24
+ * ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
+ * ACS: * interference factor average: 0.050832
+ * ACS: Survey analysis for channel 3 (2422 MHz)
+ * ACS: 1: min_nf=-113 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
+ * ACS: 2: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
+ * ACS: 3: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
+ * ACS: 4: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
+ * ACS: 5: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
+ * ACS: * interference factor average: 0.0148838
+ * ACS: Survey analysis for channel 4 (2427 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
+ * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
+ * ACS: 4: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
+ * ACS: 5: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS: * interference factor average: 0.0160801
+ * ACS: Survey analysis for channel 5 (2432 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=0.409938 nf=-113 time=161 busy=0 rx=66
+ * ACS: 2: min_nf=-114 interference_factor=0.0432099 nf=-113 time=162 busy=0 rx=7
+ * ACS: 3: min_nf=-114 interference_factor=0.0124224 nf=-113 time=161 busy=0 rx=2
+ * ACS: 4: min_nf=-114 interference_factor=0.677019 nf=-113 time=161 busy=0 rx=109
+ * ACS: 5: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
+ * ACS: * interference factor average: 0.232244
+ * ACS: Survey analysis for channel 6 (2437 MHz)
+ * ACS: 1: min_nf=-113 interference_factor=0.552795 nf=-113 time=161 busy=0 rx=89
+ * ACS: 2: min_nf=-113 interference_factor=0.0807453 nf=-112 time=161 busy=0 rx=13
+ * ACS: 3: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
+ * ACS: 4: min_nf=-113 interference_factor=0.434783 nf=-112 time=161 busy=0 rx=70
+ * ACS: 5: min_nf=-113 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
+ * ACS: * interference factor average: 0.232298
+ * ACS: Survey analysis for channel 7 (2442 MHz)
+ * ACS: 1: min_nf=-113 interference_factor=0.440994 nf=-112 time=161 busy=0 rx=71
+ * ACS: 2: min_nf=-113 interference_factor=0.385093 nf=-113 time=161 busy=0 rx=62
+ * ACS: 3: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
+ * ACS: 4: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
+ * ACS: 5: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
+ * ACS: * interference factor average: 0.195031
+ * ACS: Survey analysis for channel 8 (2447 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=0.0496894 nf=-112 time=161 busy=0 rx=8
+ * ACS: 2: min_nf=-114 interference_factor=0.0496894 nf=-114 time=161 busy=0 rx=8
+ * ACS: 3: min_nf=-114 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
+ * ACS: 4: min_nf=-114 interference_factor=0.12963 nf=-113 time=162 busy=0 rx=21
+ * ACS: 5: min_nf=-114 interference_factor=0.166667 nf=-114 time=162 busy=0 rx=27
+ * ACS: * interference factor average: 0.0865885
+ * ACS: Survey analysis for channel 9 (2452 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=0.0124224 nf=-114 time=161 busy=0 rx=2
+ * ACS: 2: min_nf=-114 interference_factor=0.0310559 nf=-114 time=161 busy=0 rx=5
+ * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
+ * ACS: 4: min_nf=-114 interference_factor=0.00617284 nf=-114 time=162 busy=0 rx=1
+ * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS: * interference factor average: 0.00993022
+ * ACS: Survey analysis for channel 10 (2457 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS: 3: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS: 4: min_nf=-114 interference_factor=0.0493827 nf=-114 time=162 busy=0 rx=8
+ * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS: * interference factor average: 0.0136033
+ * ACS: Survey analysis for channel 11 (2462 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
+ * ACS: 2: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
+ * ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
+ * ACS: 4: min_nf=-114 interference_factor=0.0432099 nf=-114 time=162 busy=0 rx=7
+ * ACS: 5: min_nf=-114 interference_factor=0.0925926 nf=-114 time=162 busy=0 rx=15
+ * ACS: * interference factor average: 0.0271605
+ * ACS: Survey analysis for channel 12 (2467 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
+ * ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
+ * ACS: 4: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
+ * ACS: 5: min_nf=-114 interference_factor=0.00617284 nf=-113 time=162 busy=0 rx=1
+ * ACS: * interference factor average: 0.0148992
+ * ACS: Survey analysis for channel 13 (2472 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=0.0745342 nf=-114 time=161 busy=0 rx=12
+ * ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
+ * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS: 4: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS: * interference factor average: 0.0260179
+ * ACS: Survey analysis for selected bandwidth 20MHz
+ * ACS: * channel 1: total interference = 0.121432
+ * ACS: * channel 2: total interference = 0.137512
+ * ACS: * channel 3: total interference = 0.369757
+ * ACS: * channel 4: total interference = 0.546338
+ * ACS: * channel 5: total interference = 0.690538
+ * ACS: * channel 6: total interference = 0.762242
+ * ACS: * channel 7: total interference = 0.756092
+ * ACS: * channel 8: total interference = 0.537451
+ * ACS: * channel 9: total interference = 0.332313
+ * ACS: * channel 10: total interference = 0.152182
+ * ACS: * channel 11: total interference = 0.0916111
+ * ACS: * channel 12: total interference = 0.0816809
+ * ACS: * channel 13: total interference = 0.0680776
+ * ACS: Ideal channel is 13 (2472 MHz) with total interference factor of 0.0680776
+ *
+ * [1] http://en.wikipedia.org/wiki/Near_and_far_field
+ */
+
+
+static int acs_request_scan(struct hostapd_iface *iface);
+
+
+static void acs_clean_chan_surveys(struct hostapd_channel_data *chan)
+{
+ struct freq_survey *survey, *tmp;
+
+ if (dl_list_empty(&chan->survey_list))
+ return;
+
+ dl_list_for_each_safe(survey, tmp, &chan->survey_list,
+ struct freq_survey, list) {
+ dl_list_del(&survey->list);
+ os_free(survey);
+ }
+}
+
+
+static void acs_cleanup(struct hostapd_iface *iface)
+{
+ int i;
+ struct hostapd_channel_data *chan;
+
+ for (i = 0; i < iface->current_mode->num_channels; i++) {
+ chan = &iface->current_mode->channels[i];
+
+ if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED)
+ acs_clean_chan_surveys(chan);
+
+ dl_list_init(&chan->survey_list);
+ chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED;
+ chan->min_nf = 0;
+ }
+
+ iface->chans_surveyed = 0;
+ iface->acs_num_completed_scans = 0;
+}
+
+
+void acs_fail(struct hostapd_iface *iface)
+{
+ wpa_printf(MSG_ERROR, "ACS: Failed to start");
+ acs_cleanup(iface);
+}
+
+
+static long double
+acs_survey_interference_factor(struct freq_survey *survey, s8 min_nf)
+{
+ long double factor, busy, total;
+
+ if (survey->filled & SURVEY_HAS_CHAN_TIME_BUSY)
+ busy = survey->channel_time_busy;
+ else if (survey->filled & SURVEY_HAS_CHAN_TIME_RX)
+ busy = survey->channel_time_rx;
+ else {
+ /* This shouldn't really happen as survey data is checked in
+ * acs_sanity_check() */
+ wpa_printf(MSG_ERROR, "ACS: Survey data missing");
+ return 0;
+ }
+
+ total = survey->channel_time;
+
+ if (survey->filled & SURVEY_HAS_CHAN_TIME_TX) {
+ busy -= survey->channel_time_tx;
+ total -= survey->channel_time_tx;
+ }
+
+ /* TODO: figure out the best multiplier for noise floor base */
+ factor = pow(10, survey->nf / 5.0L) +
+ (busy / total) *
+ pow(2, pow(10, (long double) survey->nf / 10.0L) -
+ pow(10, (long double) min_nf / 10.0L));
+
+ return factor;
+}
+
+
+static void
+acs_survey_chan_interference_factor(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan)
+{
+ struct freq_survey *survey;
+ unsigned int i = 0;
+ long double int_factor = 0;
+
+ if (dl_list_empty(&chan->survey_list))
+ return;
+
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ return;
+
+ chan->interference_factor = 0;
+
+ dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
+ {
+ int_factor = acs_survey_interference_factor(survey,
+ iface->lowest_nf);
+ chan->interference_factor += int_factor;
+ wpa_printf(MSG_DEBUG, "ACS: %d: min_nf=%d interference_factor=%Lg nf=%d time=%lu busy=%lu rx=%lu",
+ ++i, chan->min_nf, int_factor,
+ survey->nf, (unsigned long) survey->channel_time,
+ (unsigned long) survey->channel_time_busy,
+ (unsigned long) survey->channel_time_rx);
+ }
+
+ chan->interference_factor = chan->interference_factor /
+ dl_list_len(&chan->survey_list);
+}
+
+
+static int acs_usable_chan(struct hostapd_channel_data *chan)
+{
+ if (dl_list_empty(&chan->survey_list))
+ return 0;
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ return 0;
+ return 1;
+}
+
+
+static int acs_usable_ht40_chan(struct hostapd_channel_data *chan)
+{
+ const 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 acs_survey_is_sufficient(struct freq_survey *survey)
+{
+ if (!(survey->filled & SURVEY_HAS_NF)) {
+ wpa_printf(MSG_ERROR, "ACS: Survey is missing noise floor");
+ return 0;
+ }
+
+ if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
+ wpa_printf(MSG_ERROR, "ACS: Survey is missing channel time");
+ return 0;
+ }
+
+ if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
+ !(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) {
+ wpa_printf(MSG_ERROR, "ACS: Survey is missing RX and busy time (at least one is required)");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int acs_surveys_are_sufficient(struct hostapd_iface *iface)
+{
+ int i;
+ struct hostapd_channel_data *chan;
+ struct freq_survey *survey;
+
+ for (i = 0; i < iface->current_mode->num_channels; i++) {
+ chan = &iface->current_mode->channels[i];
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+
+ dl_list_for_each(survey, &chan->survey_list,
+ struct freq_survey, list)
+ {
+ if (!acs_survey_is_sufficient(survey)) {
+ wpa_printf(MSG_ERROR, "ACS: Channel %d has insufficient survey data",
+ chan->chan);
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+
+static void acs_survey_all_chans_intereference_factor(
+ struct hostapd_iface *iface)
+{
+ int i;
+ struct hostapd_channel_data *chan;
+
+ for (i = 0; i < iface->current_mode->num_channels; i++) {
+ chan = &iface->current_mode->channels[i];
+
+ if (!acs_usable_chan(chan))
+ continue;
+
+ wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)",
+ chan->chan, chan->freq);
+
+ acs_survey_chan_interference_factor(iface, chan);
+
+ wpa_printf(MSG_DEBUG, "ACS: * interference factor average: %Lg",
+ chan->interference_factor);
+ }
+}
+
+
+static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface,
+ int freq)
+{
+ struct hostapd_channel_data *chan;
+ int i;
+
+ for (i = 0; i < iface->current_mode->num_channels; i++) {
+ chan = &iface->current_mode->channels[i];
+
+ if (!acs_usable_chan(chan))
+ continue;
+
+ if (chan->freq == freq)
+ return chan;
+ }
+
+ return NULL;
+}
+
+
+/*
+ * At this point it's assumed chan->interface_factor has been computed.
+ * This function should be reusable regardless of interference computation
+ * option (survey, BSS, spectral, ...). chan->interference factor must be
+ * summable (i.e., must be always greater than zero).
+ */
+static struct hostapd_channel_data *
+acs_find_ideal_chan(struct hostapd_iface *iface)
+{
+ struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL;
+ long double factor, ideal_factor = 0;
+ int i, j;
+ int n_chans = 1;
+
+ /* TODO: HT40- support */
+
+ if (iface->conf->ieee80211n &&
+ iface->conf->secondary_channel == -1) {
+ wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
+ return NULL;
+ }
+
+ if (iface->conf->ieee80211n &&
+ iface->conf->secondary_channel)
+ n_chans = 2;
+
+ if (iface->conf->ieee80211ac &&
+ iface->conf->vht_oper_chwidth == 1)
+ n_chans = 4;
+
+ /* TODO: VHT80+80, VHT160. Update acs_adjust_vht_center_freq() too. */
+
+ wpa_printf(MSG_DEBUG, "ACS: Survey analysis for selected bandwidth %d MHz",
+ n_chans == 1 ? 20 :
+ n_chans == 2 ? 40 :
+ n_chans == 4 ? 80 :
+ -1);
+
+ for (i = 0; i < iface->current_mode->num_channels; i++) {
+ chan = &iface->current_mode->channels[i];
+
+ if (!acs_usable_chan(chan))
+ continue;
+
+ /* HT40 on 5 GHz has a limited set of primary channels as per
+ * 11n Annex J */
+ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ iface->conf->ieee80211n &&
+ iface->conf->secondary_channel &&
+ !acs_usable_ht40_chan(chan)) {
+ wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for HT40",
+ chan->chan);
+ continue;
+ }
+
+ factor = chan->interference_factor;
+
+ for (j = 1; j < n_chans; j++) {
+ adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
+ if (!adj_chan)
+ break;
+
+ factor += adj_chan->interference_factor;
+ }
+
+ if (j != n_chans) {
+ wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth",
+ chan->chan);
+ continue;
+ }
+
+ /* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
+ * channel interference factor. */
+ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B ||
+ iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) {
+ for (j = 0; j < n_chans; j++) {
+ /* TODO: perhaps a multiplier should be used
+ * here? */
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+ (j * 20) - 5);
+ if (adj_chan)
+ factor += adj_chan->interference_factor;
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+ (j * 20) - 10);
+ if (adj_chan)
+ factor += adj_chan->interference_factor;
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+ (j * 20) + 5);
+ if (adj_chan)
+ factor += adj_chan->interference_factor;
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+ (j * 20) + 10);
+ if (adj_chan)
+ factor += adj_chan->interference_factor;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "ACS: * channel %d: total interference = %Lg",
+ chan->chan, factor);
+
+ if (!ideal_chan || factor < ideal_factor) {
+ ideal_factor = factor;
+ ideal_chan = chan;
+ }
+ }
+
+ if (ideal_chan)
+ wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg",
+ ideal_chan->chan, ideal_chan->freq, ideal_factor);
+
+ return ideal_chan;
+}
+
+
+static void acs_adjust_vht_center_freq(struct hostapd_iface *iface)
+{
+ wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
+
+ switch (iface->conf->vht_oper_chwidth) {
+ case VHT_CHANWIDTH_USE_HT:
+ iface->conf->vht_oper_centr_freq_seg0_idx =
+ iface->conf->channel + 2;
+ break;
+ case VHT_CHANWIDTH_80MHZ:
+ iface->conf->vht_oper_centr_freq_seg0_idx =
+ iface->conf->channel + 6;
+ break;
+ default:
+ /* TODO: How can this be calculated? Adjust
+ * acs_find_ideal_chan() */
+ wpa_printf(MSG_INFO, "ACS: Only VHT20/40/80 is supported now");
+ break;
+ }
+}
+
+
+static int acs_study_survey_based(struct hostapd_iface *iface)
+{
+ wpa_printf(MSG_DEBUG, "ACS: Trying survey-based ACS");
+
+ if (!iface->chans_surveyed) {
+ wpa_printf(MSG_ERROR, "ACS: Unable to collect survey data");
+ return -1;
+ }
+
+ if (!acs_surveys_are_sufficient(iface)) {
+ wpa_printf(MSG_ERROR, "ACS: Surveys have insufficient data");
+ return -1;
+ }
+
+ acs_survey_all_chans_intereference_factor(iface);
+ return 0;
+}
+
+
+static int acs_study_options(struct hostapd_iface *iface)
+{
+ int err;
+
+ err = acs_study_survey_based(iface);
+ if (err == 0)
+ return 0;
+
+ /* TODO: If no surveys are available/sufficient this is a good
+ * place to fallback to BSS-based ACS */
+
+ return -1;
+}
+
+
+static void acs_study(struct hostapd_iface *iface)
+{
+ struct hostapd_channel_data *ideal_chan;
+ int err;
+
+ err = acs_study_options(iface);
+ if (err < 0) {
+ wpa_printf(MSG_ERROR, "ACS: All study options have failed");
+ goto fail;
+ }
+
+ ideal_chan = acs_find_ideal_chan(iface);
+ if (!ideal_chan) {
+ wpa_printf(MSG_ERROR, "ACS: Failed to compute ideal channel");
+ goto fail;
+ }
+
+ iface->conf->channel = ideal_chan->chan;
+
+ if (iface->conf->ieee80211ac)
+ acs_adjust_vht_center_freq(iface);
+
+ /*
+ * hostapd_setup_interface_complete() will return -1 on failure,
+ * 0 on success and 0 is HOSTAPD_CHAN_VALID :)
+ */
+ switch (hostapd_acs_completed(iface)) {
+ case HOSTAPD_CHAN_VALID:
+ acs_cleanup(iface);
+ return;
+ case HOSTAPD_CHAN_INVALID:
+ case HOSTAPD_CHAN_ACS:
+ default:
+ /* This can possibly happen if channel parameters (secondary
+ * channel, center frequencies) are misconfigured */
+ wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid, please report this along with your config file.");
+ goto fail;
+ }
+
+fail:
+ acs_fail(iface);
+}
+
+
+static void acs_scan_complete(struct hostapd_iface *iface)
+{
+ int err;
+
+ iface->scan_cb = NULL;
+
+ wpa_printf(MSG_DEBUG, "ACS: Using survey based algorithm (acs_num_scans=%d)",
+ iface->conf->acs_num_scans);
+
+ err = hostapd_drv_get_survey(iface->bss[0], 0);
+ if (err) {
+ wpa_printf(MSG_ERROR, "ACS: Failed to get survey data");
+ acs_fail(iface);
+ }
+
+ if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) {
+ err = acs_request_scan(iface);
+ if (err) {
+ wpa_printf(MSG_ERROR, "ACS: Failed to request scan");
+ acs_fail(iface);
+ return;
+ }
+
+ return;
+ }
+
+ acs_study(iface);
+}
+
+
+static int acs_request_scan(struct hostapd_iface *iface)
+{
+ struct wpa_driver_scan_params params;
+ struct hostapd_channel_data *chan;
+ int i, *freq;
+
+ os_memset(¶ms, 0, sizeof(params));
+ params.freqs = os_calloc(iface->current_mode->num_channels + 1,
+ sizeof(params.freqs[0]));
+ if (params.freqs == NULL)
+ return -1;
+
+ freq = params.freqs;
+ for (i = 0; i < iface->current_mode->num_channels; i++) {
+ chan = &iface->current_mode->channels[i];
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+
+ *freq++ = chan->freq;
+ }
+ *freq = 0;
+
+ iface->scan_cb = acs_scan_complete;
+
+ wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d",
+ iface->acs_num_completed_scans + 1,
+ iface->conf->acs_num_scans);
+
+ if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) {
+ wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan");
+ acs_cleanup(iface);
+ return -1;
+ }
+
+ os_free(params.freqs);
+ return 0;
+}
+
+
+enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
+{
+ int err;
+
+ wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
+
+ acs_cleanup(iface);
+
+ err = acs_request_scan(iface);
+ if (err < 0)
+ return HOSTAPD_CHAN_INVALID;
+
+ return HOSTAPD_CHAN_ACS;
+}
diff --git a/src/ap/acs.h b/src/ap/acs.h
new file mode 100644
index 0000000..0d1d0f1
--- /dev/null
+++ b/src/ap/acs.h
@@ -0,0 +1,28 @@
+/*
+ * ACS - Automatic Channel Selection module
+ * Copyright (c) 2011, Atheros Communications
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef ACS_H
+#define ACS_H
+
+#ifdef CONFIG_ACS
+
+enum hostapd_chan_status acs_init(struct hostapd_iface *iface);
+int hostapd_acs_completed(struct hostapd_iface *iface);
+
+#else /* CONFIG_ACS */
+
+static inline enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
+{
+ wpa_printf(MSG_ERROR, "ACS was disabled on your build, rebuild hostapd with CONFIG_ACS=y or set channel");
+ return HOSTAPD_CHAN_INVALID;
+}
+
+#endif /* CONFIG_ACS */
+
+#endif /* ACS_H */
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 54a2e75..caf75c4 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -1,6 +1,6 @@
/*
* hostapd / Configuration helper functions
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -171,6 +171,10 @@
conf->corrupt_gtk_rekey_mic_probability = 0.0d;
#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_ACS
+ conf->acs_num_scans = 5;
+#endif /* CONFIG_ACS */
+
return conf;
}
@@ -621,14 +625,31 @@
const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
- const u8 *addr, const u8 *prev_psk)
+ const u8 *addr, const u8 *p2p_dev_addr,
+ const u8 *prev_psk)
{
struct hostapd_wpa_psk *psk;
int next_ok = prev_psk == NULL;
+ if (p2p_dev_addr) {
+ wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
+ " p2p_dev_addr=" MACSTR " prev_psk=%p",
+ MAC2STR(addr), MAC2STR(p2p_dev_addr), prev_psk);
+ if (!is_zero_ether_addr(p2p_dev_addr))
+ addr = NULL; /* Use P2P Device Address for matching */
+ } else {
+ wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
+ " prev_psk=%p",
+ MAC2STR(addr), prev_psk);
+ }
+
for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) {
if (next_ok &&
- (psk->group || os_memcmp(psk->addr, addr, ETH_ALEN) == 0))
+ (psk->group ||
+ (addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) ||
+ (!addr && p2p_dev_addr &&
+ os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
+ 0)))
return psk->psk;
if (psk->psk == prev_psk)
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 9b87686..c5531fa 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -105,6 +105,7 @@
int group;
u8 psk[PMK_LEN];
u8 addr[ETH_ALEN];
+ u8 p2p_dev_addr[ETH_ALEN];
};
struct hostapd_eap_user {
@@ -358,6 +359,7 @@
u8 *extra_cred;
size_t extra_cred_len;
int wps_cred_processing;
+ int force_per_enrollee_psk;
u8 *ap_settings;
size_t ap_settings_len;
char *upnp_iface;
@@ -532,6 +534,10 @@
double ignore_reassoc_probability;
double corrupt_gtk_rekey_mic_probability;
#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_ACS
+ unsigned int acs_num_scans;
+#endif /* CONFIG_ACS */
};
@@ -546,7 +552,8 @@
int hostapd_wep_key_cmp(struct hostapd_wep_keys *a,
struct hostapd_wep_keys *b);
const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
- const u8 *addr, const u8 *prev_psk);
+ const u8 *addr, const u8 *p2p_dev_addr,
+ const u8 *prev_psk);
int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf);
int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id);
const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan,
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index e3ba224..d6bc98d 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -1,6 +1,6 @@
/*
* hostapd / Callback functions for driver wrappers
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -45,6 +45,7 @@
#endif /* CONFIG_IEEE80211R */
u16 reason = WLAN_REASON_UNSPECIFIED;
u16 status = WLAN_STATUS_SUCCESS;
+ const u8 *p2p_dev_addr = NULL;
if (addr == NULL) {
/*
@@ -108,6 +109,8 @@
wpabuf_free(sta->p2p_ie);
sta->p2p_ie = ieee802_11_vendor_ie_concat(req_ies, req_ies_len,
P2P_IE_VENDOR_TYPE);
+ if (sta->p2p_ie)
+ p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
}
#endif /* CONFIG_P2P */
@@ -156,7 +159,8 @@
if (sta->wpa_sm == NULL)
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
- sta->addr);
+ sta->addr,
+ p2p_dev_addr);
if (sta->wpa_sm == NULL) {
wpa_printf(MSG_ERROR, "Failed to initialize WPA state "
"machine");
@@ -481,7 +485,7 @@
sta->auth_alg = WLAN_AUTH_FT;
if (sta->wpa_sm == NULL)
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
- sta->addr);
+ sta->addr, NULL);
if (sta->wpa_sm == NULL) {
wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
"state machine");
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 7925a3e..fd1ca2b 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -919,6 +919,10 @@
"channel. (%d)", ret);
return -1;
}
+ if (ret == 1) {
+ wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback (ACS)");
+ return 0;
+ }
ret = hostapd_check_ht_capab(iface);
if (ret < 0)
return -1;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 673dd34..dbf1b52 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -204,6 +204,11 @@
void (*setup_complete_cb)(void *ctx);
void *setup_complete_cb_ctx;
+ void (*new_psk_cb)(void *ctx, const u8 *mac_addr,
+ const u8 *p2p_dev_addr, const u8 *psk,
+ size_t psk_len);
+ void *new_psk_cb_ctx;
+
#ifdef CONFIG_P2P
struct p2p_data *p2p;
struct p2p_group *p2p_group;
@@ -310,6 +315,10 @@
/* lowest observed noise floor in dBm */
s8 lowest_nf;
+#ifdef CONFIG_ACS
+ unsigned int acs_num_completed_scans;
+#endif /* CONFIG_ACS */
+
void (*scan_cb)(struct hostapd_iface *iface);
};
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index bf0dd95..8a239f4 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -24,6 +24,7 @@
#include "hostapd.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
+#include "acs.h"
#include "hw_features.h"
@@ -686,10 +687,18 @@
}
/*
- * The user set channel=0 which is used to trigger ACS,
- * which we do not yet support.
+ * The user set channel=0 or channel=acs_survey
+ * which is used to trigger ACS.
*/
- return HOSTAPD_CHAN_INVALID;
+
+ switch (acs_init(iface)) {
+ case HOSTAPD_CHAN_ACS:
+ return HOSTAPD_CHAN_ACS;
+ case HOSTAPD_CHAN_VALID:
+ case HOSTAPD_CHAN_INVALID:
+ default:
+ return HOSTAPD_CHAN_INVALID;
+ }
}
@@ -709,6 +718,36 @@
}
+int hostapd_acs_completed(struct hostapd_iface *iface)
+{
+ int ret;
+
+ switch (hostapd_check_chans(iface)) {
+ case HOSTAPD_CHAN_VALID:
+ break;
+ case HOSTAPD_CHAN_ACS:
+ wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available");
+ hostapd_notify_bad_chans(iface);
+ return -1;
+ case HOSTAPD_CHAN_INVALID:
+ default:
+ wpa_printf(MSG_ERROR, "ACS picked unusable channels");
+ hostapd_notify_bad_chans(iface);
+ return -1;
+ }
+
+ ret = hostapd_check_ht_capab(iface);
+ if (ret < 0)
+ return -1;
+ if (ret == 1) {
+ wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback");
+ return 0;
+ }
+
+ return hostapd_setup_interface_complete(iface, 0);
+}
+
+
/**
* hostapd_select_hw_mode - Select the hardware mode
* @iface: Pointer to interface data.
@@ -747,7 +786,8 @@
switch (hostapd_check_chans(iface)) {
case HOSTAPD_CHAN_VALID:
return 0;
- case HOSTAPD_CHAN_ACS: /* ACS not supported yet */
+ case HOSTAPD_CHAN_ACS: /* ACS will run and later complete */
+ return 1;
case HOSTAPD_CHAN_INVALID:
default:
hostapd_notify_bad_chans(iface);
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 35282af..781f826 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1,6 +1,6 @@
/*
* hostapd / IEEE 802.11 Management
- * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -719,7 +719,7 @@
sta->auth_alg = WLAN_AUTH_FT;
if (sta->wpa_sm == NULL)
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
- sta->addr);
+ sta->addr, NULL);
if (sta->wpa_sm == NULL) {
wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
"state machine");
@@ -866,6 +866,7 @@
u16 resp;
const u8 *wpa_ie;
size_t wpa_ie_len;
+ const u8 *p2p_dev_addr = NULL;
if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -911,6 +912,19 @@
}
#endif /* CONFIG_IEEE80211AC */
+#ifdef CONFIG_P2P
+ if (elems.p2p) {
+ wpabuf_free(sta->p2p_ie);
+ sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+ P2P_IE_VENDOR_TYPE);
+ if (sta->p2p_ie)
+ p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
+ } else {
+ wpabuf_free(sta->p2p_ie);
+ sta->p2p_ie = NULL;
+ }
+#endif /* CONFIG_P2P */
+
if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) {
wpa_ie = elems.rsn_ie;
wpa_ie_len = elems.rsn_ie_len;
@@ -962,7 +976,8 @@
wpa_ie_len += 2;
if (sta->wpa_sm == NULL)
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
- sta->addr);
+ sta->addr,
+ p2p_dev_addr);
if (sta->wpa_sm == NULL) {
wpa_printf(MSG_WARNING, "Failed to initialize WPA "
"state machine");
@@ -1058,16 +1073,6 @@
wpa_auth_sta_no_wpa(sta->wpa_sm);
#ifdef CONFIG_P2P
- if (elems.p2p) {
- wpabuf_free(sta->p2p_ie);
- sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
- P2P_IE_VENDOR_TYPE);
-
- } else {
- wpabuf_free(sta->p2p_ie);
- sta->p2p_ie = NULL;
- }
-
p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
#endif /* CONFIG_P2P */
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index d38330f..6704c09 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -1,6 +1,6 @@
/*
* hostapd / Station table
- * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -70,6 +70,30 @@
}
+#ifdef CONFIG_P2P
+struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct sta_info *sta;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ const u8 *p2p_dev_addr;
+
+ if (sta->p2p_ie == NULL)
+ continue;
+
+ p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
+ if (p2p_dev_addr == NULL)
+ continue;
+
+ if (os_memcmp(p2p_dev_addr, addr, ETH_ALEN) == 0)
+ return sta;
+ }
+
+ return NULL;
+}
+#endif /* CONFIG_P2P */
+
+
static void ap_sta_list_del(struct hostapd_data *hapd, struct sta_info *sta)
{
struct sta_info *tmp;
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index f8f5a83..e5b5069 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -156,6 +156,7 @@
void *ctx),
void *ctx);
struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta);
+struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr);
void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta);
void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
void hostapd_free_stas(struct hostapd_data *hapd);
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 83cc857..0286c5b 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -1,6 +1,6 @@
/*
* IEEE 802.11 RSN / WPA Authenticator
- * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -82,11 +82,14 @@
static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
- const u8 *addr, const u8 *prev_psk)
+ const u8 *addr,
+ const u8 *p2p_dev_addr,
+ const u8 *prev_psk)
{
if (wpa_auth->cb.get_psk == NULL)
return NULL;
- return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, prev_psk);
+ return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, p2p_dev_addr,
+ prev_psk);
}
@@ -508,7 +511,8 @@
struct wpa_state_machine *
-wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr)
+wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *p2p_dev_addr)
{
struct wpa_state_machine *sm;
@@ -516,6 +520,8 @@
if (sm == NULL)
return NULL;
os_memcpy(sm->addr, addr, ETH_ALEN);
+ if (p2p_dev_addr)
+ os_memcpy(sm->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
sm->wpa_auth = wpa_auth;
sm->group = wpa_auth->group;
@@ -1678,7 +1684,7 @@
{
const u8 *psk;
SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk);
- psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL);
+ psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL);
if (psk) {
os_memcpy(sm->PMK, psk, PMK_LEN);
#ifdef CONFIG_IEEE80211R
@@ -1771,7 +1777,8 @@
* the packet */
for (;;) {
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
- pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, pmk);
+ pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
+ sm->p2p_dev_addr, pmk);
if (pmk == NULL)
break;
} else
@@ -2158,7 +2165,8 @@
}
break;
case WPA_PTK_INITPSK:
- if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL))
+ if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr,
+ NULL))
SM_ENTER(WPA_PTK, PTKSTART);
else {
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index ebfe86f..47503d0 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -184,7 +184,8 @@
void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var,
int value);
int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var);
- const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *prev_psk);
+ const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr,
+ const u8 *prev_psk);
int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len);
int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg,
const u8 *addr, int idx, u8 *key, size_t key_len);
@@ -227,7 +228,8 @@
const u8 *mdie, size_t mdie_len);
int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
struct wpa_state_machine *
-wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr);
+wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *p2p_dev_addr);
int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm);
void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm);
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index e2be1ea..d977b42 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -186,6 +186,7 @@
static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
+ const u8 *p2p_dev_addr,
const u8 *prev_psk)
{
struct hostapd_data *hapd = ctx;
@@ -200,7 +201,7 @@
}
#endif /* CONFIG_SAE */
- psk = hostapd_get_psk(hapd->conf, addr, prev_psk);
+ psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk);
/*
* This is about to iterate over all psks, prev_psk gives the last
* returned psk which should not be returned again.
@@ -471,7 +472,7 @@
return sta->wpa_sm;
}
- sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr);
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL);
if (sta->wpa_sm == NULL) {
ap_free_sta(hapd, sta);
return NULL;
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 97489d3..12e59bc 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -26,6 +26,7 @@
struct wpa_group *group;
u8 addr[ETH_ALEN];
+ u8 p2p_dev_addr[ETH_ALEN];
enum {
WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED,
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 3304c06..eb300f1 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -91,15 +91,24 @@
}
-static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
+static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr,
+ const u8 *p2p_dev_addr, const u8 *psk,
size_t psk_len)
{
struct hostapd_data *hapd = ctx;
struct hostapd_wpa_psk *p;
struct hostapd_ssid *ssid = &hapd->conf->ssid;
- wpa_printf(MSG_DEBUG, "Received new WPA/WPA2-PSK from WPS for STA "
- MACSTR, MAC2STR(mac_addr));
+ if (is_zero_ether_addr(p2p_dev_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "Received new WPA/WPA2-PSK from WPS for STA " MACSTR,
+ MAC2STR(mac_addr));
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "Received new WPA/WPA2-PSK from WPS for STA " MACSTR
+ " P2P Device Addr " MACSTR,
+ MAC2STR(mac_addr), MAC2STR(p2p_dev_addr));
+ }
wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
if (psk_len != PMK_LEN) {
@@ -113,8 +122,14 @@
if (p == NULL)
return -1;
os_memcpy(p->addr, mac_addr, ETH_ALEN);
+ os_memcpy(p->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
os_memcpy(p->psk, psk, PMK_LEN);
+ if (hapd->new_psk_cb) {
+ hapd->new_psk_cb(hapd->new_psk_cb_ctx, mac_addr, p2p_dev_addr,
+ psk, psk_len);
+ }
+
p->next = ssid->wpa_psk;
ssid->wpa_psk = p;
@@ -1145,6 +1160,7 @@
cfg.dualband = 1;
if (cfg.dualband)
wpa_printf(MSG_DEBUG, "WPS: Dualband AP");
+ cfg.force_per_enrollee_psk = conf->force_per_enrollee_psk;
wps->registrar = wps_registrar_init(wps, &cfg);
if (wps->registrar == NULL) {
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 63829fe..fd7f686 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -142,6 +142,7 @@
#define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED "
#define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT "
#define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED "
+#define P2P_EVENT_PERSISTENT_PSK_FAIL "P2P-PERSISTENT-PSK-FAIL id="
/* parameters: <PMF enabled> <timeout in ms> <Session Information URL> */
#define ESS_DISASSOC_IMMINENT "ESS-DISASSOC-IMMINENT "
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index e875d22..b8c9829 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -29,6 +29,7 @@
#define HOSTAPD_CHAN_HT40PLUS 0x00000010
#define HOSTAPD_CHAN_HT40MINUS 0x00000020
#define HOSTAPD_CHAN_HT40 0x00000040
+#define HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED 0x00000080
#define HOSTAPD_CHAN_DFS_UNKNOWN 0x00000000
#define HOSTAPD_CHAN_DFS_USABLE 0x00000100
@@ -70,6 +71,15 @@
* surveyed channel data
*/
s8 min_nf;
+
+#ifdef CONFIG_ACS
+ /**
+ * interference_factor - Computed interference factor on this
+ * channel (used internally in src/ap/acs.c; driver wrappers do not
+ * need to set this)
+ */
+ long double interference_factor;
+#endif /* CONFIG_ACS */
};
#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index c0d7078..554e7e9 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -938,6 +938,8 @@
mcc_str[3] = '\0';
mcc = atoi(mcc_str);
+ if (mcc == 228)
+ return 2; /* Networks in Switzerland use 2-digit MNC */
if (mcc == 244)
return 2; /* Networks in Finland use 2-digit MNC */
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index b9a754c..738436c 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -1188,6 +1188,18 @@
}
+void p2p_stop_listen(struct p2p_data *p2p)
+{
+ if (p2p->state != P2P_LISTEN_ONLY) {
+ p2p_dbg(p2p, "Skip stop_listen since not in listen_only state.");
+ return;
+ }
+
+ p2p_stop_listen_for_freq(p2p, 0);
+ p2p_set_state(p2p, P2P_IDLE);
+}
+
+
void p2p_stop_find(struct p2p_data *p2p)
{
p2p_stop_find_for_freq(p2p, 0);
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index b206d23..7f845b2 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -904,6 +904,12 @@
int p2p_listen(struct p2p_data *p2p, unsigned int timeout);
/**
+ * p2p_stop_listen - Stop P2P Listen
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_stop_listen(struct p2p_data *p2p);
+
+/**
* p2p_connect - Start P2P group formation (GO negotiation)
* @p2p: P2P module context from p2p_init()
* @peer_addr: MAC address of the peer P2P client
diff --git a/src/wps/wps.h b/src/wps/wps.h
index dc82c44..15137a8 100644
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -246,14 +246,15 @@
* new_psk_cb - Callback for new PSK
* @ctx: Higher layer context data (cb_ctx)
* @mac_addr: MAC address of the Enrollee
+ * @p2p_dev_addr: P2P Device Address of the Enrollee or all zeros if not
* @psk: The new PSK
* @psk_len: The length of psk in octets
* Returns: 0 on success, -1 on failure
*
* This callback is called when a new per-device PSK is provisioned.
*/
- int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk,
- size_t psk_len);
+ int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr,
+ const u8 *psk, size_t psk_len);
/**
* set_ie_cb - Callback for WPS IE changes
@@ -382,6 +383,14 @@
* dualband - Whether this is a concurrent dualband AP
*/
int dualband;
+
+ /**
+ * force_per_enrollee_psk - Force per-Enrollee random PSK
+ *
+ * This forces per-Enrollee random PSK to be generated even if a default
+ * PSK is set for a network.
+ */
+ int force_per_enrollee_psk;
};
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index 0befca2..b7fcd9c 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -1,6 +1,6 @@
/*
* Wi-Fi Protected Setup - Registrar
- * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -142,8 +142,8 @@
int pbc;
int selected_registrar;
- int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk,
- size_t psk_len);
+ int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr,
+ const u8 *psk, size_t psk_len);
int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie,
struct wpabuf *probe_resp_ie);
void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
@@ -171,6 +171,7 @@
int sel_reg_config_methods_override;
int static_wep_only;
int dualband;
+ int force_per_enrollee_psk;
struct wps_registrar_device *devices;
@@ -672,6 +673,7 @@
reg->sel_reg_config_methods_override = -1;
reg->static_wep_only = cfg->static_wep_only;
reg->dualband = cfg->dualband;
+ reg->force_per_enrollee_psk = cfg->force_per_enrollee_psk;
if (wps_set_ie(reg)) {
wps_registrar_deinit(reg);
@@ -1167,12 +1169,13 @@
static int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr,
- const u8 *psk, size_t psk_len)
+ const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len)
{
if (reg->new_psk_cb == NULL)
return 0;
- return reg->new_psk_cb(reg->cb_ctx, mac_addr, psk, psk_len);
+ return reg->new_psk_cb(reg->cb_ctx, mac_addr, p2p_dev_addr, psk,
+ psk_len);
}
@@ -1645,13 +1648,15 @@
wps->new_psk, wps->new_psk_len);
os_memcpy(wps->cred.key, wps->new_psk, wps->new_psk_len);
wps->cred.key_len = wps->new_psk_len;
- } else if (wps->use_psk_key && wps->wps->psk_set) {
+ } else if (!wps->wps->registrar->force_per_enrollee_psk &&
+ wps->use_psk_key && wps->wps->psk_set) {
char hex[65];
wpa_printf(MSG_DEBUG, "WPS: Use PSK format for Network Key");
wpa_snprintf_hex(hex, sizeof(hex), wps->wps->psk, 32);
os_memcpy(wps->cred.key, hex, 32 * 2);
wps->cred.key_len = 32 * 2;
- } else if (wps->wps->network_key) {
+ } else if (!wps->wps->registrar->force_per_enrollee_psk &&
+ wps->wps->network_key) {
os_memcpy(wps->cred.key, wps->wps->network_key,
wps->wps->network_key_len);
wps->cred.key_len = wps->wps->network_key_len;
@@ -3169,7 +3174,8 @@
if (wps->new_psk) {
if (wps_cb_new_psk(wps->wps->registrar, wps->mac_addr_e,
- wps->new_psk, wps->new_psk_len)) {
+ wps->p2p_dev_addr, wps->new_psk,
+ wps->new_psk_len)) {
wpa_printf(MSG_DEBUG, "WPS: Failed to configure the "
"new PSK");
}
diff --git a/wpa_supplicant/README-P2P b/wpa_supplicant/README-P2P
index 8447a90..76f8219 100644
--- a/wpa_supplicant/README-P2P
+++ b/wpa_supplicant/README-P2P
@@ -199,6 +199,14 @@
step. If the WPS provisioning step has been completed, the group is not
terminated.
+p2p_remove_client <peer's P2P Device Address|iface=<interface address>>
+
+This command can be used to remove the specified client from all groups
+(operating and persistent) from the local GO. Note that the peer device
+can rejoin the group if it is in possession of a valid key. See p2p_set
+per_sta_psk command below for more details on how the peer can be
+removed securely.
+
Service Discovery
p2p_serv_disc_req
@@ -456,6 +464,20 @@
(DIRECT-<two random characters>). For example, postfix of "-testing"
could result in the SSID becoming DIRECT-ab-testing.
+p2p_set per_sta_psk <0/1>
+
+Disabled(default)/enables use of per-client PSK in the P2P groups. This
+can be used to request GO to assign a unique PSK for each client during
+WPS provisioning. When enabled, this allow clients to be removed from
+the group securily with p2p_remove_client command since that client's
+PSK is removed at the same time to prevent it from connecting back using
+the old PSK. When per-client PSK is not used, the client can still be
+disconnected, but it will be able to re-join the group since the PSK it
+learned previously is still valid. It should be noted that the default
+passphrase on the GO that is normally used to allow legacy stations to
+connect through manual configuration does not change here, so if that is
+shared, devices with knowledge of that passphrase can still connect.
+
set <field> <value>
Set global configuration parameters which may also affect P2P
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 2950d2d..fdbe248 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -148,6 +148,7 @@
}
bss->isolate = !wpa_s->conf->p2p_intra_bss;
+ bss->force_per_enrollee_psk = wpa_s->global->p2p_per_sta_psk;
#endif /* CONFIG_P2P */
if (ssid->ssid_len == 0) {
@@ -366,6 +367,19 @@
}
+#ifdef CONFIG_P2P
+static void ap_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr,
+ const u8 *psk, size_t psk_len)
+{
+
+ struct wpa_supplicant *wpa_s = ctx;
+ if (wpa_s->ap_iface == NULL || wpa_s->current_ssid == NULL)
+ return;
+ wpas_p2p_new_psk_cb(wpa_s, mac_addr, p2p_dev_addr, psk, psk_len);
+}
+#endif /* CONFIG_P2P */
+
+
static int ap_vendor_action_rx(void *ctx, const u8 *buf, size_t len, int freq)
{
#ifdef CONFIG_P2P
@@ -491,11 +505,6 @@
if (wpa_drv_associate(wpa_s, ¶ms) < 0) {
wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality");
-#ifdef CONFIG_P2P
- if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION &&
- wpa_s->global->p2p_group_formation == wpa_s)
- wpas_p2p_group_formation_failed(wpa_s->parent);
-#endif /* CONFIG_P2P */
return -1;
}
@@ -570,6 +579,8 @@
hapd_iface->bss[i]->sta_authorized_cb = ap_sta_authorized_cb;
hapd_iface->bss[i]->sta_authorized_cb_ctx = wpa_s;
#ifdef CONFIG_P2P
+ hapd_iface->bss[i]->new_psk_cb = ap_new_psk_cb;
+ hapd_iface->bss[i]->new_psk_cb_ctx = wpa_s;
hapd_iface->bss[i]->p2p = wpa_s->global->p2p;
hapd_iface->bss[i]->p2p_group = wpas_p2p_group_init(wpa_s,
ssid);
diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
index fd4c25a..74a0b18 100644
--- a/wpa_supplicant/ap.h
+++ b/wpa_supplicant/ap.h
@@ -54,7 +54,16 @@
int offset);
struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
int ndef);
+#ifdef CONFIG_AP
struct wpabuf * wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
int ndef);
+#else /* CONFIG_AP */
+static inline struct wpabuf *
+wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+ int ndef)
+{
+ return NULL;
+}
+#endif /* CONFIG_AP */
#endif /* AP_H */
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index a35be51..d666c91 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -1372,6 +1372,60 @@
}
#endif /* NO_CONFIG_WRITE */
+
+static int wpa_config_parse_psk_list(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ struct psk_list_entry *p;
+ const char *pos;
+
+ p = os_zalloc(sizeof(*p));
+ if (p == NULL)
+ return -1;
+
+ pos = value;
+ if (os_strncmp(pos, "P2P-", 4) == 0) {
+ p->p2p = 1;
+ pos += 4;
+ }
+
+ if (hwaddr_aton(pos, p->addr)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list address '%s'",
+ line, pos);
+ os_free(p);
+ return -1;
+ }
+ pos += 17;
+ if (*pos != '-') {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list '%s'",
+ line, pos);
+ os_free(p);
+ return -1;
+ }
+ pos++;
+
+ if (hexstr2bin(pos, p->psk, PMK_LEN) || pos[PMK_LEN * 2] != '\0') {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list PSK '%s'",
+ line, pos);
+ os_free(p);
+ return -1;
+ }
+
+ dl_list_add(&ssid->psk_list, &p->list);
+
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_psk_list(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ return NULL;
+}
+#endif /* NO_CONFIG_WRITE */
+
#endif /* CONFIG_P2P */
/* Helper macros for network block parser */
@@ -1539,6 +1593,7 @@
{ INT_RANGE(ignore_broadcast_ssid, 0, 2) },
#ifdef CONFIG_P2P
{ FUNC(p2p_client_list) },
+ { FUNC(psk_list) },
#endif /* CONFIG_P2P */
#ifdef CONFIG_HT_OVERRIDES
{ INT_RANGE(disable_ht, 0, 1) },
@@ -1731,6 +1786,8 @@
*/
void wpa_config_free_ssid(struct wpa_ssid *ssid)
{
+ struct psk_list_entry *psk;
+
os_free(ssid->ssid);
os_free(ssid->passphrase);
os_free(ssid->ext_psk);
@@ -1745,6 +1802,11 @@
#ifdef CONFIG_HT_OVERRIDES
os_free(ssid->ht_mcs);
#endif /* CONFIG_HT_OVERRIDES */
+ while ((psk = dl_list_first(&ssid->psk_list, struct psk_list_entry,
+ list))) {
+ dl_list_del(&psk->list);
+ os_free(psk);
+ }
os_free(ssid);
}
@@ -1908,6 +1970,7 @@
if (ssid == NULL)
return NULL;
ssid->id = id;
+ dl_list_init(&ssid->psk_list);
if (last)
last->next = ssid;
else
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index d03de0b..a2791eb 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -158,6 +158,7 @@
ssid = os_zalloc(sizeof(*ssid));
if (ssid == NULL)
return NULL;
+ dl_list_init(&ssid->psk_list);
ssid->id = id;
wpa_config_set_network_defaults(ssid);
@@ -604,6 +605,7 @@
#ifdef CONFIG_P2P
+
static void write_p2p_client_list(FILE *f, struct wpa_ssid *ssid)
{
char *value = wpa_config_get(ssid, "p2p_client_list");
@@ -612,6 +614,20 @@
fprintf(f, "\tp2p_client_list=%s\n", value);
os_free(value);
}
+
+
+static void write_psk_list(FILE *f, struct wpa_ssid *ssid)
+{
+ struct psk_list_entry *psk;
+ char hex[32 * 2 + 1];
+
+ dl_list_for_each(psk, &ssid->psk_list, struct psk_list_entry, list) {
+ wpa_snprintf_hex(hex, sizeof(hex), psk->psk, sizeof(psk->psk));
+ fprintf(f, "\tpsk_list=%s" MACSTR "-%s\n",
+ psk->p2p ? "P2P-" : "", MAC2STR(psk->addr), hex);
+ }
+}
+
#endif /* CONFIG_P2P */
@@ -696,6 +712,7 @@
STR(id_str);
#ifdef CONFIG_P2P
write_p2p_client_list(f, ssid);
+ write_psk_list(f, ssid);
#endif /* CONFIG_P2P */
INT(dtim_period);
INT(beacon_int);
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index baa28b3..c6ea963 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -1,6 +1,6 @@
/*
* WPA Supplicant / Network configuration structures
- * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -10,6 +10,7 @@
#define CONFIG_SSID_H
#include "common/defs.h"
+#include "utils/list.h"
#include "eap_peer/eap_config.h"
#define MAX_SSID_LEN 32
@@ -33,6 +34,13 @@
#define DEFAULT_AMPDU_FACTOR -1 /* no change */
#define DEFAULT_AMPDU_DENSITY -1 /* no change */
+struct psk_list_entry {
+ struct dl_list list;
+ u8 addr[ETH_ALEN];
+ u8 psk[32];
+ u8 p2p;
+};
+
/**
* struct wpa_ssid - Network configuration data
*
@@ -456,6 +464,11 @@
#endif /* P2P_MAX_STORED_CLIENTS */
/**
+ * psk_list - Per-client PSKs (struct psk_list_entry)
+ */
+ struct dl_list psk_list;
+
+ /**
* p2p_group - Network generated as a P2P group (used internally)
*/
int p2p_group;
diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c
index 3cf3a91..1b5e237 100644
--- a/wpa_supplicant/config_winreg.c
+++ b/wpa_supplicant/config_winreg.c
@@ -302,6 +302,7 @@
RegCloseKey(nhk);
return NULL;
}
+ dl_list_init(&ssid->psk_list);
ssid->id = id;
wpa_config_set_network_defaults(ssid);
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 55f6a06..8736b07 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant / Control interface (shared code for all backends)
- * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -1503,7 +1503,10 @@
}
#ifdef CONFIG_SAE
if (wpa_s->wpa_state >= WPA_ASSOCIATED &&
- wpa_s->sme.sae.state == SAE_ACCEPTED && !wpa_s->ap_iface) {
+#ifdef CONFIG_AP
+ !wpa_s->ap_iface &&
+#endif /* CONFIG_AP */
+ wpa_s->sme.sae.state == SAE_ACCEPTED) {
ret = os_snprintf(pos, end - pos, "sae_group=%d\n",
wpa_s->sme.sae.group);
if (ret < 0 || ret >= end - pos)
@@ -4578,6 +4581,11 @@
max_disc_int, max_disc_tu);
}
+ if (os_strcmp(cmd, "per_sta_psk") == 0) {
+ wpa_s->global->p2p_per_sta_psk = !!atoi(param);
+ return 0;
+ }
+
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'",
cmd);
@@ -4643,6 +4651,25 @@
return wpas_p2p_ext_listen(wpa_s, period, interval);
}
+
+static int p2p_ctrl_remove_client(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+ const char *pos;
+ u8 peer[ETH_ALEN];
+ int iface_addr = 0;
+
+ pos = cmd;
+ if (os_strncmp(pos, "iface=", 6) == 0) {
+ iface_addr = 1;
+ pos += 6;
+ }
+ if (hwaddr_aton(pos, peer))
+ return -1;
+
+ wpas_p2p_remove_client(wpa_s, peer, iface_addr);
+ return 0;
+}
+
#endif /* CONFIG_P2P */
@@ -5478,6 +5505,9 @@
} else if (os_strcmp(buf, "P2P_EXT_LISTEN") == 0) {
if (p2p_ctrl_ext_listen(wpa_s, "") < 0)
reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_REMOVE_CLIENT ", 18) == 0) {
+ if (p2p_ctrl_remove_client(wpa_s, buf + 18) < 0)
+ reply_len = -1;
#endif /* CONFIG_P2P */
#ifdef CONFIG_WIFI_DISPLAY
} else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) {
@@ -5979,6 +6009,7 @@
"P2P_UNAUTHORIZE ",
"P2P_PRESENCE_REQ ",
"P2P_EXT_LISTEN ",
+ "P2P_REMOVE_CLIENT ",
NULL
};
int found = 0;
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c
index cfa6a15..e26086d 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.c
+++ b/wpa_supplicant/dbus/dbus_new_helpers.c
@@ -590,11 +590,11 @@
if (!obj_desc) {
wpa_printf(MSG_ERROR, "dbus: %s: Could not obtain object's "
"private data: %s", __func__, path);
- } else {
- eloop_cancel_timeout(flush_object_timeout_handler, con,
- obj_desc);
+ return 0;
}
+ eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc);
+
if (!dbus_connection_unregister_object_path(con, path))
return -1;
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index d77e96b..8c29b20 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -276,7 +276,7 @@
{
#ifdef IEEE8021X_EAPOL
#ifdef PCSC_FUNCS
- int aka = 0, sim = 0, type;
+ int aka = 0, sim = 0;
if (ssid->eap.pcsc == NULL || wpa_s->scard != NULL)
return 0;
@@ -315,14 +315,9 @@
wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to use SIM "
"(sim=%d aka=%d) - initialize PCSC", sim, aka);
- if (sim && aka)
- type = SCARD_TRY_BOTH;
- else if (aka)
- type = SCARD_USIM_ONLY;
- else
- type = SCARD_GSM_SIM_ONLY;
- wpa_s->scard = scard_init(type, NULL);
+ wpa_s->scard = scard_init((!sim && aka) ? SCARD_USIM_ONLY :
+ SCARD_TRY_BOTH, NULL);
if (wpa_s->scard == NULL) {
wpa_msg(wpa_s, MSG_WARNING, "Failed to initialize SIM "
"(pcsc-lite)");
@@ -2029,6 +2024,8 @@
if (could_be_psk_mismatch(wpa_s, reason_code, locally_generated)) {
wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - "
"pre-shared key may be incorrect");
+ if (wpas_p2p_4way_hs_failed(wpa_s) > 0)
+ return; /* P2P group removed */
wpas_auth_failed(wpa_s);
}
if (!wpa_s->disconnected &&
@@ -2484,7 +2481,7 @@
wpas_auth_failed(wpa_s);
#ifdef CONFIG_P2P
- if (deauth && ie && ie_len > 0) {
+ if (deauth && reason_code > 0) {
if (wpas_p2p_deauth_notif(wpa_s, addr, reason_code, ie, ie_len,
locally_generated) > 0) {
/*
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 3e5d412..47ef35e 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -257,7 +257,8 @@
}
-static const u8 * auth_get_psk(void *ctx, const u8 *addr, const u8 *prev_psk)
+static const u8 * auth_get_psk(void *ctx, const u8 *addr,
+ const u8 *p2p_dev_addr, const u8 *prev_psk)
{
struct ibss_rsn *ibss_rsn = ctx;
wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
@@ -444,7 +445,7 @@
static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn,
struct ibss_rsn_peer *peer)
{
- peer->auth = wpa_auth_sta_init(ibss_rsn->auth_group, peer->addr);
+ peer->auth = wpa_auth_sta_init(ibss_rsn->auth_group, peer->addr, NULL);
if (peer->auth == NULL) {
wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed");
return -1;
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index f2cbdd7..ab62bea 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -226,7 +226,7 @@
* applications since these network objects won't behave like
* regular ones.
*/
- if (wpa_s->global->p2p_group_formation != wpa_s)
+ if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s)
wpas_dbus_register_network(wpa_s, ssid);
}
@@ -254,7 +254,7 @@
{
if (wpa_s->wpa)
wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
- if (wpa_s->global->p2p_group_formation != wpa_s)
+ if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s)
wpas_dbus_unregister_network(wpa_s, ssid->id);
#ifdef CONFIG_P2P
wpas_p2p_network_removed(wpa_s, ssid);
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 239e608..7179080 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -17,6 +17,8 @@
#include "p2p/p2p.h"
#include "ap/hostapd.h"
#include "ap/ap_config.h"
+#include "ap/sta_info.h"
+#include "ap/ap_drv_ops.h"
#include "ap/p2p_hostapd.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "rsn_supp/wpa.h"
@@ -85,6 +87,7 @@
P2P_GROUP_REMOVAL_IDLE_TIMEOUT,
P2P_GROUP_REMOVAL_UNAVAILABLE,
P2P_GROUP_REMOVAL_GO_ENDING_SESSION,
+ P2P_GROUP_REMOVAL_PSK_FAILURE,
#ifdef ANDROID_P2P
P2P_GROUP_REMOVAL_FREQ_CONFLICT
#endif
@@ -399,6 +402,9 @@
case P2P_GROUP_REMOVAL_GO_ENDING_SESSION:
reason = " reason=GO_ENDING_SESSION";
break;
+ case P2P_GROUP_REMOVAL_PSK_FAILURE:
+ reason = " reason=PSK_FAILURE";
+ break;
#ifdef ANDROID_P2P
case P2P_GROUP_REMOVAL_FREQ_CONFLICT:
reason = " reason=FREQ_CONFLICT";
@@ -618,6 +624,11 @@
s->ssid_len = ssid->ssid_len;
os_memcpy(s->ssid, ssid->ssid, s->ssid_len);
}
+ if (ssid->mode == WPAS_MODE_P2P_GO && wpa_s->global->add_psk) {
+ dl_list_add(&s->psk_list, &wpa_s->global->add_psk->list);
+ wpa_s->global->add_psk = NULL;
+ changed = 1;
+ }
#ifndef CONFIG_NO_CONFIG_WRITE
if (changed && wpa_s->conf->update_config &&
@@ -800,6 +811,10 @@
if (persistent)
network_id = wpas_p2p_store_persistent_group(wpa_s->parent,
ssid, go_dev_addr);
+ else {
+ os_free(wpa_s->global->add_psk);
+ wpa_s->global->add_psk = NULL;
+ }
if (network_id < 0 && ssid)
network_id = ssid->id;
if (!client) {
@@ -904,6 +919,44 @@
}
+static void wpas_p2p_add_psk_list(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ struct wpa_ssid *persistent;
+ struct psk_list_entry *psk;
+ struct hostapd_data *hapd;
+
+ if (!wpa_s->ap_iface)
+ return;
+
+ persistent = wpas_p2p_get_persistent(wpa_s->parent, NULL, ssid->ssid,
+ ssid->ssid_len);
+ if (persistent == NULL)
+ return;
+
+ hapd = wpa_s->ap_iface->bss[0];
+
+ dl_list_for_each(psk, &persistent->psk_list, struct psk_list_entry,
+ list) {
+ struct hostapd_wpa_psk *hpsk;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add persistent group PSK entry for "
+ MACSTR " psk=%d",
+ MAC2STR(psk->addr), psk->p2p);
+ hpsk = os_zalloc(sizeof(*hpsk));
+ if (hpsk == NULL)
+ break;
+ os_memcpy(hpsk->psk, psk->psk, PMK_LEN);
+ if (psk->p2p)
+ os_memcpy(hpsk->p2p_dev_addr, psk->addr, ETH_ALEN);
+ else
+ os_memcpy(hpsk->addr, psk->addr, ETH_ALEN);
+ hpsk->next = hapd->conf->ssid.wpa_psk;
+ hapd->conf->ssid.wpa_psk = hpsk;
+ }
+}
+
+
static void p2p_go_configured(void *ctx, void *data)
{
struct wpa_supplicant *wpa_s = ctx;
@@ -943,10 +996,12 @@
" [PERSISTENT]" : "");
}
- if (params->persistent_group)
+ if (params->persistent_group) {
network_id = wpas_p2p_store_persistent_group(
wpa_s->parent, ssid,
wpa_s->global->p2p_dev_addr);
+ wpas_p2p_add_psk_list(wpa_s, ssid);
+ }
if (network_id < 0)
network_id = ssid->id;
wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0);
@@ -1228,6 +1283,18 @@
}
+void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->global->p2p_group_formation != wpa_s)
+ return;
+ /* Speed up group formation timeout since this cannot succeed */
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL);
+ eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL);
+}
+
+
void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
{
struct wpa_supplicant *wpa_s = ctx;
@@ -2686,7 +2753,7 @@
static void wpas_remove_persistent_peer(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
- const u8 *peer)
+ const u8 *peer, int inv)
{
size_t i;
@@ -2711,8 +2778,9 @@
}
wpa_printf(MSG_DEBUG, "P2P: Remove peer " MACSTR " from persistent "
- "group %d client list due to invitation result",
- MAC2STR(peer), ssid->id);
+ "group %d client list%s",
+ MAC2STR(peer), ssid->id,
+ inv ? " due to invitation result" : "");
os_memmove(ssid->p2p_client_list + i * ETH_ALEN,
ssid->p2p_client_list + (i + 1) * ETH_ALEN,
(ssid->num_p2p_clients - i - 1) * ETH_ALEN);
@@ -2739,7 +2807,7 @@
return; /* Not operating as a GO in persistent group */
ssid = wpas_p2p_get_persistent(wpa_s->parent, peer,
ssid->ssid, ssid->ssid_len);
- wpas_remove_persistent_peer(wpa_s, ssid, peer);
+ wpas_remove_persistent_peer(wpa_s, ssid, peer, 1);
}
@@ -2778,7 +2846,7 @@
if (status == P2P_SC_FAIL_UNKNOWN_GROUP) {
ssid = wpa_config_get_network(
wpa_s->conf, wpa_s->pending_invite_ssid_id);
- wpas_remove_persistent_peer(wpa_s, ssid, peer);
+ wpas_remove_persistent_peer(wpa_s, ssid, peer, 1);
}
wpas_p2p_remove_pending_group_interface(wpa_s);
return;
@@ -3155,6 +3223,9 @@
unsigned int r;
int i;
+ if (wpa_s->conf->p2p_disabled)
+ return 0;
+
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE))
return 0;
@@ -4021,6 +4092,9 @@
return -1;
}
+ os_free(wpa_s->global->add_psk);
+ wpa_s->global->add_psk = NULL;
+
if (go_intent < 0)
go_intent = wpa_s->conf->p2p_go_intent;
@@ -4181,6 +4255,12 @@
if (wpa_s->p2p_long_listen > 0) {
wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state");
wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen);
+ } else {
+ /*
+ * When listen duration is over, stop listen & update p2p_state
+ * to IDLE.
+ */
+ p2p_stop_listen(wpa_s->global->p2p);
}
}
@@ -4469,6 +4549,9 @@
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
+ os_free(wpa_s->global->add_psk);
+ wpa_s->global->add_psk = NULL;
+
/* Make sure we are not running find during connection establishment */
wpa_printf(MSG_DEBUG, "P2P: Stop any on-going P2P FIND");
wpas_p2p_stop_find_oper(wpa_s);
@@ -4506,6 +4589,7 @@
wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0);
if (wpa_s == NULL)
return -1;
+ wpa_s->p2p_last_4way_hs_fail = NULL;
wpa_supplicant_ap_deinit(wpa_s);
@@ -4560,6 +4644,9 @@
return 0;
}
+ os_free(wpa_s->global->add_psk);
+ wpa_s->global->add_psk = NULL;
+
/* Make sure we are not running find during connection establishment */
wpas_p2p_stop_find_oper(wpa_s);
@@ -5888,6 +5975,11 @@
(ssid_len != s->ssid_len ||
os_memcmp(ssid, s->ssid, ssid_len) != 0))
continue;
+ if (addr == NULL) {
+ if (s->mode == WPAS_MODE_P2P_GO)
+ return s;
+ continue;
+ }
if (os_memcmp(s->bssid, addr, ETH_ALEN) == 0)
return s; /* peer is GO in the persistent group */
if (s->mode != WPAS_MODE_P2P_GO || s->p2p_client_list == NULL)
@@ -6012,6 +6104,277 @@
}
}
+
+static int wpas_p2p_remove_psk_entry(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *s, const u8 *addr,
+ int iface_addr)
+{
+ struct psk_list_entry *psk, *tmp;
+ int changed = 0;
+
+ dl_list_for_each_safe(psk, tmp, &s->psk_list, struct psk_list_entry,
+ list) {
+ if ((iface_addr && !psk->p2p &&
+ os_memcmp(addr, psk->addr, ETH_ALEN) == 0) ||
+ (!iface_addr && psk->p2p &&
+ os_memcmp(addr, psk->addr, ETH_ALEN) == 0)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Remove persistent group PSK list entry for "
+ MACSTR " p2p=%u",
+ MAC2STR(psk->addr), psk->p2p);
+ dl_list_del(&psk->list);
+ os_free(psk);
+ changed++;
+ }
+ }
+
+ return changed;
+}
+
+
+void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr,
+ const u8 *p2p_dev_addr,
+ const u8 *psk, size_t psk_len)
+{
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ struct wpa_ssid *persistent;
+ struct psk_list_entry *p;
+
+ if (psk_len != sizeof(p->psk))
+ return;
+
+ if (p2p_dev_addr) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New PSK for addr=" MACSTR
+ " p2p_dev_addr=" MACSTR,
+ MAC2STR(mac_addr), MAC2STR(p2p_dev_addr));
+ if (is_zero_ether_addr(p2p_dev_addr))
+ p2p_dev_addr = NULL;
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New PSK for addr=" MACSTR,
+ MAC2STR(mac_addr));
+ }
+
+ if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: new_psk_cb during group formation");
+ /* To be added to persistent group once created */
+ if (wpa_s->global->add_psk == NULL) {
+ wpa_s->global->add_psk = os_zalloc(sizeof(*p));
+ if (wpa_s->global->add_psk == NULL)
+ return;
+ }
+ p = wpa_s->global->add_psk;
+ if (p2p_dev_addr) {
+ p->p2p = 1;
+ os_memcpy(p->addr, p2p_dev_addr, ETH_ALEN);
+ } else {
+ p->p2p = 0;
+ os_memcpy(p->addr, mac_addr, ETH_ALEN);
+ }
+ os_memcpy(p->psk, psk, psk_len);
+ return;
+ }
+
+ if (ssid->mode != WPAS_MODE_P2P_GO || !ssid->p2p_persistent_group) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Ignore new_psk_cb on not-persistent GO");
+ return;
+ }
+
+ persistent = wpas_p2p_get_persistent(wpa_s->parent, NULL, ssid->ssid,
+ ssid->ssid_len);
+ if (!persistent) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not find persistent group information to store the new PSK");
+ return;
+ }
+
+ p = os_zalloc(sizeof(*p));
+ if (p == NULL)
+ return;
+ if (p2p_dev_addr) {
+ p->p2p = 1;
+ os_memcpy(p->addr, p2p_dev_addr, ETH_ALEN);
+ } else {
+ p->p2p = 0;
+ os_memcpy(p->addr, mac_addr, ETH_ALEN);
+ }
+ os_memcpy(p->psk, psk, psk_len);
+
+ if (dl_list_len(&persistent->psk_list) > P2P_MAX_STORED_CLIENTS) {
+ struct psk_list_entry *last;
+ last = dl_list_last(&persistent->psk_list,
+ struct psk_list_entry, list);
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove oldest PSK entry for "
+ MACSTR " (p2p=%u) to make room for a new one",
+ MAC2STR(last->addr), last->p2p);
+ dl_list_del(&last->list);
+ os_free(last);
+ }
+
+ wpas_p2p_remove_psk_entry(wpa_s->parent, persistent,
+ p2p_dev_addr ? p2p_dev_addr : mac_addr,
+ p2p_dev_addr == NULL);
+ if (p2p_dev_addr) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add new PSK for p2p_dev_addr="
+ MACSTR, MAC2STR(p2p_dev_addr));
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add new PSK for addr=" MACSTR,
+ MAC2STR(mac_addr));
+ }
+ dl_list_add(&persistent->psk_list, &p->list);
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+ if (wpa_s->parent->conf->update_config &&
+ wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
+ wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+#endif /* CONFIG_NO_CONFIG_WRITE */
+}
+
+
+static void wpas_p2p_remove_psk(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *s, const u8 *addr,
+ int iface_addr)
+{
+ int res;
+
+ res = wpas_p2p_remove_psk_entry(wpa_s, s, addr, iface_addr);
+ if (res > 0) {
+#ifndef CONFIG_NO_CONFIG_WRITE
+ if (wpa_s->conf->update_config &&
+ wpa_config_write(wpa_s->confname, wpa_s->conf))
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Failed to update configuration");
+#endif /* CONFIG_NO_CONFIG_WRITE */
+ }
+}
+
+
+static void wpas_p2p_remove_client_go(struct wpa_supplicant *wpa_s,
+ const u8 *peer, int iface_addr)
+{
+ struct hostapd_data *hapd;
+ struct hostapd_wpa_psk *psk, *prev, *rem;
+ struct sta_info *sta;
+
+ if (wpa_s->ap_iface == NULL || wpa_s->current_ssid == NULL ||
+ wpa_s->current_ssid->mode != WPAS_MODE_P2P_GO)
+ return;
+
+ /* Remove per-station PSK entry */
+ hapd = wpa_s->ap_iface->bss[0];
+ prev = NULL;
+ psk = hapd->conf->ssid.wpa_psk;
+ while (psk) {
+ if ((iface_addr && os_memcmp(peer, psk->addr, ETH_ALEN) == 0) ||
+ (!iface_addr &&
+ os_memcmp(peer, psk->p2p_dev_addr, ETH_ALEN) == 0)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove operating group PSK entry for "
+ MACSTR " iface_addr=%d",
+ MAC2STR(peer), iface_addr);
+ if (prev)
+ prev->next = psk->next;
+ else
+ hapd->conf->ssid.wpa_psk = psk->next;
+ rem = psk;
+ psk = psk->next;
+ os_free(rem);
+ } else {
+ prev = psk;
+ psk = psk->next;
+ }
+ }
+
+ /* Disconnect from group */
+ if (iface_addr)
+ sta = ap_get_sta(hapd, peer);
+ else
+ sta = ap_get_sta_p2p(hapd, peer);
+ if (sta) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disconnect peer " MACSTR
+ " (iface_addr=%d) from group",
+ MAC2STR(peer), iface_addr);
+ hostapd_drv_sta_deauth(hapd, sta->addr,
+ WLAN_REASON_DEAUTH_LEAVING);
+ ap_sta_deauthenticate(hapd, sta, WLAN_REASON_DEAUTH_LEAVING);
+ }
+}
+
+
+void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer,
+ int iface_addr)
+{
+ struct wpa_ssid *s;
+ struct wpa_supplicant *w;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove client " MACSTR, MAC2STR(peer));
+
+ /* Remove from any persistent group */
+ for (s = wpa_s->parent->conf->ssid; s; s = s->next) {
+ if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO)
+ continue;
+ if (!iface_addr)
+ wpas_remove_persistent_peer(wpa_s, s, peer, 0);
+ wpas_p2p_remove_psk(wpa_s->parent, s, peer, iface_addr);
+ }
+
+ /* Remove from any operating group */
+ for (w = wpa_s->global->ifaces; w; w = w->next)
+ wpas_p2p_remove_client_go(w, peer, iface_addr);
+}
+
+
+static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_PSK_FAILURE);
+}
+
+
+int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ if (ssid == NULL || !ssid->p2p_group)
+ return 0;
+
+ if (wpa_s->p2p_last_4way_hs_fail &&
+ wpa_s->p2p_last_4way_hs_fail == ssid) {
+ u8 go_dev_addr[ETH_ALEN];
+ struct wpa_ssid *persistent;
+
+ if (wpas_p2p_persistent_group(wpa_s, go_dev_addr,
+ ssid->ssid,
+ ssid->ssid_len) <= 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not determine whether 4-way handshake failures were for a persistent group");
+ goto disconnect;
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Two 4-way handshake failures for a P2P group - go_dev_addr="
+ MACSTR, MAC2STR(go_dev_addr));
+ persistent = wpas_p2p_get_persistent(wpa_s->parent, go_dev_addr,
+ ssid->ssid,
+ ssid->ssid_len);
+ if (persistent == NULL || persistent->mode != WPAS_MODE_INFRA) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No matching persistent group stored");
+ goto disconnect;
+ }
+ wpa_msg_global(wpa_s->parent, MSG_INFO,
+ P2P_EVENT_PERSISTENT_PSK_FAIL "%d",
+ persistent->id);
+ disconnect:
+ wpa_s->p2p_last_4way_hs_fail = NULL;
+ /*
+ * Remove the group from a timeout to avoid issues with caller
+ * continuing to use the interface if this is on a P2P group
+ * interface.
+ */
+ eloop_register_timeout(0, 0, wpas_p2p_psk_failure_removal,
+ wpa_s, NULL);
+ return 1;
+ }
+
+ wpa_s->p2p_last_4way_hs_fail = ssid;
+ return 0;
+}
+
#ifdef ANDROID_P2P
static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx)
{
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index 7febac7..65ccbf9 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -156,13 +156,29 @@
int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode, u8 channel);
unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s);
+void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr,
+ const u8 *p2p_dev_addr,
+ const u8 *psk, size_t psk_len);
+void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer,
+ int iface_addr);
#ifdef CONFIG_P2P
void wpas_p2p_continue_after_scan(struct wpa_supplicant *wpa_s);
+int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s);
+void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s);
#else /* CONFIG_P2P */
static inline void wpas_p2p_continue_after_scan(struct wpa_supplicant *wpa_s)
{
}
+
+static inline int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
+static inline void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s)
+{
+}
#endif /* CONFIG_P2P */
#endif /* P2P_SUPPLICANT_H */
diff --git a/wpa_supplicant/tests/test_wpa.c b/wpa_supplicant/tests/test_wpa.c
index ba2be6f..484a406 100644
--- a/wpa_supplicant/tests/test_wpa.c
+++ b/wpa_supplicant/tests/test_wpa.c
@@ -298,7 +298,7 @@
static int auth_init(struct wpa *wpa)
{
- wpa->auth = wpa_auth_sta_init(wpa->auth_group, wpa->supp_addr);
+ wpa->auth = wpa_auth_sta_init(wpa->auth_group, wpa->supp_addr, NULL);
if (wpa->auth == NULL) {
wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed");
return -1;
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index ba1f207..a379d65 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -70,7 +70,7 @@
static struct wpa_ctrl *mon_conn;
static int wpa_cli_quit = 0;
static int wpa_cli_attached = 0;
-static int wpa_cli_connected = 0;
+static int wpa_cli_connected = -1;
static int wpa_cli_last_id = 0;
#ifndef CONFIG_CTRL_IFACE_DIR
#define CONFIG_CTRL_IFACE_DIR "/var/run/wpa_supplicant"
@@ -2055,6 +2055,50 @@
}
+static char ** wpa_cli_complete_p2p_set(const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ const char *fields[] = {
+ "discoverability",
+ "managed",
+ "listen_channel",
+ "ssid_postfix",
+ "noa",
+ "ps",
+ "oppps",
+ "ctwindow",
+ "disabled",
+ "conc_pref",
+ "force_long_sd",
+ "peer_filter",
+ "cross_connect",
+ "go_apsd",
+ "client_apsd",
+ "disallow_freq",
+ "disc_int",
+ "per_sta_psk",
+ };
+ int i, num_fields = sizeof(fields) / sizeof(fields[0]);
+
+ if (arg == 1) {
+ char **res = os_calloc(num_fields + 1, sizeof(char *));
+ if (res == NULL)
+ return NULL;
+ for (i = 0; i < num_fields; i++) {
+ res[i] = os_strdup(fields[i]);
+ if (res[i] == NULL)
+ return res;
+ }
+ return res;
+ }
+
+ if (arg == 2 && os_strncasecmp(str, "p2p_set peer_filter ", 20) == 0)
+ return cli_txt_list_array(&p2p_peers);
+
+ return NULL;
+}
+
+
static int wpa_cli_cmd_p2p_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "P2P_FLUSH");
@@ -2105,6 +2149,13 @@
return wpa_cli_cmd(ctrl, "P2P_EXT_LISTEN", 0, argc, argv);
}
+
+static int wpa_cli_cmd_p2p_remove_client(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "P2P_REMOVE_CLIENT", 1, argc, argv);
+}
+
#endif /* CONFIG_P2P */
#ifdef CONFIG_WIFI_DISPLAY
@@ -2706,7 +2757,8 @@
{ "p2p_peer", wpa_cli_cmd_p2p_peer, wpa_cli_complete_p2p_peer,
cli_cmd_flag_none,
"<address> = show information about known P2P peer" },
- { "p2p_set", wpa_cli_cmd_p2p_set, NULL, cli_cmd_flag_none,
+ { "p2p_set", wpa_cli_cmd_p2p_set, wpa_cli_complete_p2p_set,
+ cli_cmd_flag_none,
"<field> <value> = set a P2P parameter" },
{ "p2p_flush", wpa_cli_cmd_p2p_flush, NULL, cli_cmd_flag_none,
"= flush P2P state" },
@@ -2722,6 +2774,9 @@
{ "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, NULL,
cli_cmd_flag_none,
"[<period> <interval>] = set extended listen timing" },
+ { "p2p_remove_client", wpa_cli_cmd_p2p_remove_client,
+ wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
+ "<address|iface=address> = remove a peer from all groups" },
#endif /* CONFIG_P2P */
#ifdef CONFIG_WIFI_DISPLAY
{ "wfd_subelem_set", wpa_cli_cmd_wfd_subelem_set, NULL,
@@ -3080,7 +3135,7 @@
os_setenv("WPA_CTRL_DIR", ctrl_iface_dir, 1);
- if (!wpa_cli_connected || new_id != wpa_cli_last_id) {
+ if (wpa_cli_connected <= 0 || new_id != wpa_cli_last_id) {
wpa_cli_connected = 1;
wpa_cli_last_id = new_id;
wpa_cli_exec(action_file, ctrl_ifname, "CONNECTED");
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index caab28b..9ae898b 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1304,6 +1304,8 @@
}
if (wpa_supplicant_create_ap(wpa_s, ssid) < 0) {
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+ if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
+ wpas_p2p_ap_setup_failed(wpa_s);
return;
}
wpa_s->current_bss = bss;
@@ -3501,6 +3503,7 @@
os_free(global->params.override_ctrl_interface);
os_free(global->p2p_disallow_freq);
+ os_free(global->add_psk);
os_free(global);
wpa_debug_close_syslog();
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 604997e..d69cd61 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -265,12 +265,15 @@
WPA_CONC_PREF_P2P
} conc_pref;
unsigned int p2p_cb_on_scan_complete:1;
+ unsigned int p2p_per_sta_psk:1;
#ifdef CONFIG_WIFI_DISPLAY
int wifi_display;
#define MAX_WFD_SUBELEMS 10
struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS];
#endif /* CONFIG_WIFI_DISPLAY */
+
+ struct psk_list_entry *add_psk; /* From group formation */
};
@@ -649,6 +652,7 @@
int p2p_go_intent;
int p2p_connect_freq;
struct os_time p2p_auto_started;
+ struct wpa_ssid *p2p_last_4way_hs_fail;
#endif /* CONFIG_P2P */
struct wpa_ssid *bgscan_ssid;
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index b855dbd..f6c2fcb 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -1205,11 +1205,20 @@
}
-static int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
+static int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr,
+ const u8 *p2p_dev_addr, const u8 *psk,
size_t psk_len)
{
- wpa_printf(MSG_DEBUG, "WPS: Received new WPA/WPA2-PSK from WPS for "
- "STA " MACSTR, MAC2STR(mac_addr));
+ if (is_zero_ether_addr(p2p_dev_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "Received new WPA/WPA2-PSK from WPS for STA " MACSTR,
+ MAC2STR(mac_addr));
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "Received new WPA/WPA2-PSK from WPS for STA " MACSTR
+ " P2P Device Addr " MACSTR,
+ MAC2STR(mac_addr), MAC2STR(p2p_dev_addr));
+ }
wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
/* TODO */