368de263bc
Scans are allowed on the current interface for group formation only when in the P2P_SEARCH, P2P_IDLE, or P2P_PROVISIONING state. But Extended Listen timeout being enabled moves to the P2P_LISTEN_ONLY state. Due to this P2P connection fails if the GO is not found with scans triggerred before Extended Listen timeout. To avoid this skip Extended Listen timeout to allow scans during group formation on the current interface. Signed-off-by: Shivani Baranwal <quic_shivbara@quicinc.com>
10159 lines
288 KiB
C
10159 lines
288 KiB
C
/*
|
|
* wpa_supplicant - P2P
|
|
* Copyright (c) 2009-2010, Atheros Communications
|
|
* Copyright (c) 2010-2014, Jouni Malinen <j@w1.fi>
|
|
*
|
|
* This software may be distributed under the terms of the BSD license.
|
|
* See README for more details.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
|
|
#include "common.h"
|
|
#include "eloop.h"
|
|
#include "common/ieee802_11_common.h"
|
|
#include "common/ieee802_11_defs.h"
|
|
#include "common/wpa_ctrl.h"
|
|
#include "wps/wps_i.h"
|
|
#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/wps_hostapd.h"
|
|
#include "ap/p2p_hostapd.h"
|
|
#include "ap/dfs.h"
|
|
#include "eapol_supp/eapol_supp_sm.h"
|
|
#include "rsn_supp/wpa.h"
|
|
#include "wpa_supplicant_i.h"
|
|
#include "driver_i.h"
|
|
#include "ap.h"
|
|
#include "config_ssid.h"
|
|
#include "config.h"
|
|
#include "notify.h"
|
|
#include "scan.h"
|
|
#include "bss.h"
|
|
#include "offchannel.h"
|
|
#include "wps_supplicant.h"
|
|
#include "p2p_supplicant.h"
|
|
#include "wifi_display.h"
|
|
|
|
|
|
/*
|
|
* How many times to try to scan to find the GO before giving up on join
|
|
* request.
|
|
*/
|
|
#define P2P_MAX_JOIN_SCAN_ATTEMPTS 10
|
|
|
|
#define P2P_AUTO_PD_SCAN_ATTEMPTS 5
|
|
|
|
/**
|
|
* Defines time interval in seconds when a GO needs to evacuate a frequency that
|
|
* it is currently using, but is no longer valid for P2P use cases.
|
|
*/
|
|
#define P2P_GO_FREQ_CHANGE_TIME 5
|
|
|
|
/**
|
|
* Defines CSA parameters which are used when GO evacuates the no longer valid
|
|
* channel (and if the driver supports channel switch).
|
|
*/
|
|
#define P2P_GO_CSA_COUNT 7
|
|
#define P2P_GO_CSA_BLOCK_TX 0
|
|
|
|
#ifndef P2P_MAX_CLIENT_IDLE
|
|
/*
|
|
* How many seconds to try to reconnect to the GO when connection in P2P client
|
|
* role has been lost.
|
|
*/
|
|
#define P2P_MAX_CLIENT_IDLE 10
|
|
#endif /* P2P_MAX_CLIENT_IDLE */
|
|
|
|
#ifndef P2P_MAX_INITIAL_CONN_WAIT
|
|
/*
|
|
* How many seconds to wait for initial 4-way handshake to get completed after
|
|
* WPS provisioning step or after the re-invocation of a persistent group on a
|
|
* P2P Client.
|
|
*/
|
|
#define P2P_MAX_INITIAL_CONN_WAIT 10
|
|
#endif /* P2P_MAX_INITIAL_CONN_WAIT */
|
|
|
|
#ifndef P2P_MAX_INITIAL_CONN_WAIT_GO
|
|
/*
|
|
* How many seconds to wait for initial 4-way handshake to get completed after
|
|
* WPS provisioning step on the GO. This controls the extra time the P2P
|
|
* operation is considered to be in progress (e.g., to delay other scans) after
|
|
* WPS provisioning has been completed on the GO during group formation.
|
|
*/
|
|
#define P2P_MAX_INITIAL_CONN_WAIT_GO 10
|
|
#endif /* P2P_MAX_INITIAL_CONN_WAIT_GO */
|
|
|
|
#ifndef P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE
|
|
/*
|
|
* How many seconds to wait for initial 4-way handshake to get completed after
|
|
* re-invocation of a persistent group on the GO when the client is expected
|
|
* to connect automatically (no user interaction).
|
|
*/
|
|
#define P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE 15
|
|
#endif /* P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE */
|
|
|
|
#define P2P_MGMT_DEVICE_PREFIX "p2p-dev-"
|
|
|
|
/*
|
|
* How many seconds to wait to re-attempt to move GOs, in case previous attempt
|
|
* was not possible.
|
|
*/
|
|
#define P2P_RECONSIDER_GO_MOVE_DELAY 30
|
|
|
|
enum p2p_group_removal_reason {
|
|
P2P_GROUP_REMOVAL_UNKNOWN,
|
|
P2P_GROUP_REMOVAL_SILENT,
|
|
P2P_GROUP_REMOVAL_FORMATION_FAILED,
|
|
P2P_GROUP_REMOVAL_REQUESTED,
|
|
P2P_GROUP_REMOVAL_IDLE_TIMEOUT,
|
|
P2P_GROUP_REMOVAL_UNAVAILABLE,
|
|
P2P_GROUP_REMOVAL_GO_ENDING_SESSION,
|
|
P2P_GROUP_REMOVAL_PSK_FAILURE,
|
|
P2P_GROUP_REMOVAL_FREQ_CONFLICT,
|
|
P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL
|
|
};
|
|
|
|
|
|
static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx);
|
|
static struct wpa_supplicant *
|
|
wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
|
|
int go);
|
|
static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq,
|
|
const u8 *ssid, size_t ssid_len);
|
|
static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
|
|
int *force_freq, int *pref_freq, int go,
|
|
struct weighted_pcl *pref_freq_list,
|
|
unsigned int *num_pref_freq);
|
|
static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
|
|
const u8 *ssid, size_t ssid_len);
|
|
static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx);
|
|
static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
|
|
const u8 *dev_addr, enum p2p_wps_method wps_method,
|
|
int auto_join, int freq,
|
|
const u8 *ssid, size_t ssid_len);
|
|
static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s);
|
|
static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s);
|
|
static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx);
|
|
static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s);
|
|
static void wpas_p2p_group_formation_timeout(void *eloop_ctx,
|
|
void *timeout_ctx);
|
|
static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx);
|
|
static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
|
|
int group_added);
|
|
static void wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s);
|
|
static void wpas_stop_listen(void *ctx);
|
|
static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx);
|
|
static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s);
|
|
static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s,
|
|
enum wpa_driver_if_type type);
|
|
static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s,
|
|
int already_deleted);
|
|
static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s,
|
|
struct wpa_used_freq_data *freqs,
|
|
unsigned int num);
|
|
static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx);
|
|
static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq);
|
|
static void
|
|
wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s,
|
|
struct wpa_used_freq_data *freqs, unsigned int num,
|
|
enum wpas_p2p_channel_update_trig trig);
|
|
static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx);
|
|
|
|
|
|
static int wpas_get_6ghz_he_chwidth_capab(struct hostapd_hw_modes *mode)
|
|
{
|
|
int he_capab = 0;
|
|
|
|
if (mode)
|
|
he_capab = mode->he_capab[WPAS_MODE_INFRA].phy_cap[
|
|
HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
|
|
return he_capab;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the number of concurrent channels that the HW can operate, but that are
|
|
* currently not in use by any of the wpa_supplicant interfaces.
|
|
*/
|
|
static int wpas_p2p_num_unused_channels(struct wpa_supplicant *wpa_s)
|
|
{
|
|
int *freqs;
|
|
int num, unused;
|
|
|
|
freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int));
|
|
if (!freqs)
|
|
return -1;
|
|
|
|
num = get_shared_radio_freqs(wpa_s, freqs,
|
|
wpa_s->num_multichan_concurrent);
|
|
os_free(freqs);
|
|
|
|
unused = wpa_s->num_multichan_concurrent - num;
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: num_unused_channels: %d", unused);
|
|
return unused;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the frequencies that are currently in use by one or more of the virtual
|
|
* interfaces, and that are also valid for P2P operation.
|
|
*/
|
|
static unsigned int
|
|
wpas_p2p_valid_oper_freqs(struct wpa_supplicant *wpa_s,
|
|
struct wpa_used_freq_data *p2p_freqs,
|
|
unsigned int len)
|
|
{
|
|
struct wpa_used_freq_data *freqs;
|
|
unsigned int num, i, j;
|
|
|
|
freqs = os_calloc(wpa_s->num_multichan_concurrent,
|
|
sizeof(struct wpa_used_freq_data));
|
|
if (!freqs)
|
|
return 0;
|
|
|
|
num = get_shared_radio_freqs_data(wpa_s, freqs,
|
|
wpa_s->num_multichan_concurrent);
|
|
|
|
os_memset(p2p_freqs, 0, sizeof(struct wpa_used_freq_data) * len);
|
|
|
|
for (i = 0, j = 0; i < num && j < len; i++) {
|
|
if (p2p_supported_freq(wpa_s->global->p2p, freqs[i].freq))
|
|
p2p_freqs[j++] = freqs[i];
|
|
}
|
|
|
|
os_free(freqs);
|
|
|
|
dump_freq_data(wpa_s, "valid for P2P", p2p_freqs, j);
|
|
|
|
return j;
|
|
}
|
|
|
|
|
|
static void wpas_p2p_set_own_freq_preference(struct wpa_supplicant *wpa_s,
|
|
int freq)
|
|
{
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return;
|
|
|
|
/* Use the wpa_s used to control the P2P Device operation */
|
|
wpa_s = wpa_s->global->p2p_init_wpa_s;
|
|
|
|
if (wpa_s->conf->p2p_ignore_shared_freq &&
|
|
freq > 0 && wpa_s->num_multichan_concurrent > 1 &&
|
|
wpas_p2p_num_unused_channels(wpa_s) > 0) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Ignore own channel preference %d MHz due to p2p_ignore_shared_freq=1 configuration",
|
|
freq);
|
|
freq = 0;
|
|
}
|
|
p2p_set_own_freq_preference(wpa_s->global->p2p, freq);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_scan_res_handled(struct wpa_supplicant *wpa_s)
|
|
{
|
|
unsigned int delay = wpas_p2p_search_delay(wpa_s);
|
|
|
|
/* In case of concurrent P2P and external scans, delay P2P search. */
|
|
if (external_scan_running(wpa_s->radio)) {
|
|
delay = wpa_s->conf->p2p_search_delay;
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Delay next P2P search by %d ms to let externally triggered scan complete",
|
|
delay);
|
|
}
|
|
|
|
p2p_scan_res_handled(wpa_s->global->p2p, delay);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
|
|
struct wpa_scan_results *scan_res)
|
|
{
|
|
size_t i;
|
|
|
|
if (wpa_s->p2p_scan_work) {
|
|
struct wpa_radio_work *work = wpa_s->p2p_scan_work;
|
|
wpa_s->p2p_scan_work = NULL;
|
|
radio_work_done(work);
|
|
}
|
|
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return;
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS)",
|
|
(int) scan_res->num);
|
|
|
|
for (i = 0; i < scan_res->num; i++) {
|
|
struct wpa_scan_res *bss = scan_res->res[i];
|
|
struct os_reltime time_tmp_age, entry_ts;
|
|
const u8 *ies;
|
|
size_t ies_len;
|
|
|
|
time_tmp_age.sec = bss->age / 1000;
|
|
time_tmp_age.usec = (bss->age % 1000) * 1000;
|
|
os_reltime_sub(&scan_res->fetch_time, &time_tmp_age, &entry_ts);
|
|
|
|
ies = (const u8 *) (bss + 1);
|
|
ies_len = bss->ie_len;
|
|
if (bss->beacon_ie_len > 0 &&
|
|
!wpa_scan_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
|
|
wpa_scan_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Use P2P IE(s) from Beacon frame since no P2P IE(s) in Probe Response frames received for "
|
|
MACSTR, MAC2STR(bss->bssid));
|
|
ies = ies + ies_len;
|
|
ies_len = bss->beacon_ie_len;
|
|
}
|
|
|
|
|
|
if (p2p_scan_res_handler(wpa_s->global->p2p, bss->bssid,
|
|
bss->freq, &entry_ts, bss->level,
|
|
ies, ies_len) > 0)
|
|
break;
|
|
}
|
|
|
|
wpas_p2p_scan_res_handled(wpa_s);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_scan_res_fail_handler(struct wpa_supplicant *wpa_s)
|
|
{
|
|
if (wpa_s->p2p_scan_work) {
|
|
struct wpa_radio_work *work = wpa_s->p2p_scan_work;
|
|
|
|
wpa_s->p2p_scan_work = NULL;
|
|
radio_work_done(work);
|
|
}
|
|
|
|
if (wpa_s->global->p2p_disabled || !wpa_s->global->p2p)
|
|
return;
|
|
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Failed to get scan results - try to continue");
|
|
wpas_p2p_scan_res_handled(wpa_s);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
|
|
{
|
|
struct wpa_supplicant *wpa_s = work->wpa_s;
|
|
struct wpa_driver_scan_params *params = work->ctx;
|
|
int ret;
|
|
|
|
if (deinit) {
|
|
if (!work->started) {
|
|
wpa_scan_free_params(params);
|
|
return;
|
|
}
|
|
|
|
wpa_s->p2p_scan_work = NULL;
|
|
return;
|
|
}
|
|
|
|
if (wpa_s->clear_driver_scan_cache) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"Request driver to clear scan cache due to local BSS flush");
|
|
params->only_new_results = 1;
|
|
}
|
|
|
|
if (!params->p2p_include_6ghz && !params->freqs) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Exclude 6 GHz channels - update the scan frequency list");
|
|
wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, params,
|
|
0);
|
|
wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params,
|
|
0);
|
|
}
|
|
ret = wpa_drv_scan(wpa_s, params);
|
|
if (ret == 0)
|
|
wpa_s->curr_scan_cookie = params->scan_cookie;
|
|
wpa_scan_free_params(params);
|
|
work->ctx = NULL;
|
|
if (ret) {
|
|
radio_work_done(work);
|
|
p2p_notify_scan_trigger_status(wpa_s->global->p2p, ret);
|
|
return;
|
|
}
|
|
|
|
p2p_notify_scan_trigger_status(wpa_s->global->p2p, ret);
|
|
os_get_reltime(&wpa_s->scan_trigger_time);
|
|
wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
|
|
wpa_s->scan_res_fail_handler = wpas_p2p_scan_res_fail_handler;
|
|
wpa_s->own_scan_requested = 1;
|
|
wpa_s->clear_driver_scan_cache = 0;
|
|
wpa_s->p2p_scan_work = work;
|
|
}
|
|
|
|
|
|
static int wpas_p2p_search_social_channel(struct wpa_supplicant *wpa_s,
|
|
int freq)
|
|
{
|
|
if (wpa_s->global->p2p_24ghz_social_channels &&
|
|
(freq == 2412 || freq == 2437 || freq == 2462)) {
|
|
/*
|
|
* Search all social channels regardless of whether these have
|
|
* been disabled for P2P operating channel use to avoid missing
|
|
* peers.
|
|
*/
|
|
return 1;
|
|
}
|
|
return p2p_supported_freq(wpa_s->global->p2p, freq);
|
|
}
|
|
|
|
|
|
static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq,
|
|
unsigned int num_req_dev_types,
|
|
const u8 *req_dev_types, const u8 *dev_id, u16 pw_id,
|
|
bool include_6ghz)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
struct wpa_driver_scan_params *params = NULL;
|
|
struct wpabuf *wps_ie, *ies;
|
|
unsigned int num_channels = 0;
|
|
int social_channels_freq[] = { 2412, 2437, 2462, 60480 };
|
|
size_t ielen;
|
|
u8 *n, i;
|
|
unsigned int bands;
|
|
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return -1;
|
|
|
|
if (wpa_s->p2p_scan_work) {
|
|
wpa_dbg(wpa_s, MSG_INFO, "P2P: Reject scan trigger since one is already pending");
|
|
return -1;
|
|
}
|
|
|
|
params = os_zalloc(sizeof(*params));
|
|
if (params == NULL)
|
|
return -1;
|
|
|
|
/* P2P Wildcard SSID */
|
|
params->num_ssids = 1;
|
|
n = os_malloc(P2P_WILDCARD_SSID_LEN);
|
|
if (n == NULL)
|
|
goto fail;
|
|
os_memcpy(n, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
|
|
params->ssids[0].ssid = n;
|
|
params->ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
|
|
|
|
wpa_s->wps->dev.p2p = 1;
|
|
wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev,
|
|
wpa_s->wps->uuid, WPS_REQ_ENROLLEE,
|
|
num_req_dev_types, req_dev_types);
|
|
if (wps_ie == NULL)
|
|
goto fail;
|
|
if (!wpa_s->conf->p2p_6ghz_disable)
|
|
params->p2p_include_6ghz = include_6ghz;
|
|
switch (type) {
|
|
case P2P_SCAN_SOCIAL:
|
|
params->freqs = os_calloc(ARRAY_SIZE(social_channels_freq) + 1,
|
|
sizeof(int));
|
|
if (params->freqs == NULL)
|
|
goto fail;
|
|
for (i = 0; i < ARRAY_SIZE(social_channels_freq); i++) {
|
|
if (wpas_p2p_search_social_channel(
|
|
wpa_s, social_channels_freq[i]))
|
|
params->freqs[num_channels++] =
|
|
social_channels_freq[i];
|
|
}
|
|
params->freqs[num_channels++] = 0;
|
|
break;
|
|
case P2P_SCAN_FULL:
|
|
break;
|
|
case P2P_SCAN_SPECIFIC:
|
|
params->freqs = os_calloc(2, sizeof(int));
|
|
if (params->freqs == NULL)
|
|
goto fail;
|
|
params->freqs[0] = freq;
|
|
params->freqs[1] = 0;
|
|
break;
|
|
case P2P_SCAN_SOCIAL_PLUS_ONE:
|
|
params->freqs = os_calloc(ARRAY_SIZE(social_channels_freq) + 2,
|
|
sizeof(int));
|
|
if (params->freqs == NULL)
|
|
goto fail;
|
|
for (i = 0; i < ARRAY_SIZE(social_channels_freq); i++) {
|
|
if (wpas_p2p_search_social_channel(
|
|
wpa_s, social_channels_freq[i]))
|
|
params->freqs[num_channels++] =
|
|
social_channels_freq[i];
|
|
}
|
|
if (p2p_supported_freq(wpa_s->global->p2p, freq))
|
|
params->freqs[num_channels++] = freq;
|
|
params->freqs[num_channels++] = 0;
|
|
break;
|
|
}
|
|
|
|
ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
|
|
ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen);
|
|
if (ies == NULL) {
|
|
wpabuf_free(wps_ie);
|
|
goto fail;
|
|
}
|
|
wpabuf_put_buf(ies, wps_ie);
|
|
wpabuf_free(wps_ie);
|
|
|
|
bands = wpas_get_bands(wpa_s, params->freqs);
|
|
p2p_scan_ie(wpa_s->global->p2p, ies, dev_id, bands);
|
|
|
|
params->p2p_probe = 1;
|
|
n = os_malloc(wpabuf_len(ies));
|
|
if (n == NULL) {
|
|
wpabuf_free(ies);
|
|
goto fail;
|
|
}
|
|
os_memcpy(n, wpabuf_head(ies), wpabuf_len(ies));
|
|
params->extra_ies = n;
|
|
params->extra_ies_len = wpabuf_len(ies);
|
|
wpabuf_free(ies);
|
|
|
|
radio_remove_works(wpa_s, "p2p-scan", 0);
|
|
if (radio_add_work(wpa_s, 0, "p2p-scan", 0, wpas_p2p_trigger_scan_cb,
|
|
params) < 0)
|
|
goto fail;
|
|
return 0;
|
|
|
|
fail:
|
|
wpa_scan_free_params(params);
|
|
return -1;
|
|
}
|
|
|
|
|
|
static enum wpa_driver_if_type wpas_p2p_if_type(int p2p_group_interface)
|
|
{
|
|
switch (p2p_group_interface) {
|
|
case P2P_GROUP_INTERFACE_PENDING:
|
|
return WPA_IF_P2P_GROUP;
|
|
case P2P_GROUP_INTERFACE_GO:
|
|
return WPA_IF_P2P_GO;
|
|
case P2P_GROUP_INTERFACE_CLIENT:
|
|
return WPA_IF_P2P_CLIENT;
|
|
default:
|
|
return WPA_IF_P2P_GROUP;
|
|
}
|
|
}
|
|
|
|
|
|
static struct wpa_supplicant * wpas_get_p2p_group(struct wpa_supplicant *wpa_s,
|
|
const u8 *ssid,
|
|
size_t ssid_len, int *go)
|
|
{
|
|
struct wpa_ssid *s;
|
|
|
|
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
|
|
for (s = wpa_s->conf->ssid; s; s = s->next) {
|
|
if (s->disabled != 0 || !s->p2p_group ||
|
|
s->ssid_len != ssid_len ||
|
|
os_memcmp(ssid, s->ssid, ssid_len) != 0)
|
|
continue;
|
|
if (s->mode == WPAS_MODE_P2P_GO &&
|
|
s != wpa_s->current_ssid)
|
|
continue;
|
|
if (go)
|
|
*go = s->mode == WPAS_MODE_P2P_GO;
|
|
return wpa_s;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void run_wpas_p2p_disconnect(void *eloop_ctx, void *timeout_ctx)
|
|
{
|
|
struct wpa_supplicant *wpa_s = eloop_ctx;
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Complete previously requested removal of %s",
|
|
wpa_s->ifname);
|
|
wpas_p2p_disconnect(wpa_s);
|
|
}
|
|
|
|
|
|
static int wpas_p2p_disconnect_safely(struct wpa_supplicant *wpa_s,
|
|
struct wpa_supplicant *calling_wpa_s)
|
|
{
|
|
if (calling_wpa_s == wpa_s && wpa_s &&
|
|
wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
|
|
/*
|
|
* The calling wpa_s instance is going to be removed. Do that
|
|
* from an eloop callback to keep the instance available until
|
|
* the caller has returned. This may be needed, e.g., to provide
|
|
* control interface responses on the per-interface socket.
|
|
*/
|
|
if (eloop_register_timeout(0, 0, run_wpas_p2p_disconnect,
|
|
wpa_s, NULL) < 0)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
return wpas_p2p_disconnect(wpa_s);
|
|
}
|
|
|
|
|
|
/* Determine total number of clients in active groups where we are the GO */
|
|
static unsigned int p2p_group_go_member_count(struct wpa_supplicant *wpa_s)
|
|
{
|
|
unsigned int count = 0;
|
|
struct wpa_ssid *s;
|
|
|
|
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
|
|
for (s = wpa_s->conf->ssid; s; s = s->next) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d",
|
|
wpa_s, s, s->disabled, s->p2p_group,
|
|
s->mode);
|
|
if (!s->disabled && s->p2p_group &&
|
|
s->mode == WPAS_MODE_P2P_GO) {
|
|
count += p2p_get_group_num_members(
|
|
wpa_s->p2p_group);
|
|
}
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
static unsigned int p2p_is_active_persistent_group(struct wpa_supplicant *wpa_s)
|
|
{
|
|
return !wpa_s->p2p_mgmt && wpa_s->current_ssid &&
|
|
!wpa_s->current_ssid->disabled &&
|
|
wpa_s->current_ssid->p2p_group &&
|
|
wpa_s->current_ssid->p2p_persistent_group;
|
|
}
|
|
|
|
|
|
static unsigned int p2p_is_active_persistent_go(struct wpa_supplicant *wpa_s)
|
|
{
|
|
return p2p_is_active_persistent_group(wpa_s) &&
|
|
wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO;
|
|
}
|
|
|
|
|
|
/* Find an interface for a P2P group where we are the GO */
|
|
static struct wpa_supplicant *
|
|
wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s)
|
|
{
|
|
struct wpa_supplicant *save = NULL;
|
|
|
|
if (!wpa_s)
|
|
return NULL;
|
|
|
|
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
|
|
if (!p2p_is_active_persistent_go(wpa_s))
|
|
continue;
|
|
|
|
/* Prefer a group with connected clients */
|
|
if (p2p_get_group_num_members(wpa_s->p2p_group))
|
|
return wpa_s;
|
|
save = wpa_s;
|
|
}
|
|
|
|
/* No group with connected clients, so pick the one without (if any) */
|
|
return save;
|
|
}
|
|
|
|
|
|
static unsigned int p2p_is_active_persistent_cli(struct wpa_supplicant *wpa_s)
|
|
{
|
|
return p2p_is_active_persistent_group(wpa_s) &&
|
|
wpa_s->current_ssid->mode == WPAS_MODE_INFRA;
|
|
}
|
|
|
|
|
|
/* Find an interface for a P2P group where we are the P2P Client */
|
|
static struct wpa_supplicant *
|
|
wpas_p2p_get_cli_group(struct wpa_supplicant *wpa_s)
|
|
{
|
|
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
|
|
if (p2p_is_active_persistent_cli(wpa_s))
|
|
return wpa_s;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* Find a persistent group where we are the GO */
|
|
static struct wpa_ssid *
|
|
wpas_p2p_get_persistent_go(struct wpa_supplicant *wpa_s)
|
|
{
|
|
struct wpa_ssid *s;
|
|
|
|
for (s = wpa_s->conf->ssid; s; s = s->next) {
|
|
if (s->disabled == 2 && s->mode == WPAS_MODE_P2P_GO)
|
|
return s;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role,
|
|
unsigned int *force_freq,
|
|
unsigned int *pref_freq)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
struct wpa_ssid *s;
|
|
u8 conncap = P2PS_SETUP_NONE;
|
|
unsigned int owned_members = 0;
|
|
struct wpa_supplicant *go_wpa_s, *cli_wpa_s;
|
|
struct wpa_ssid *persistent_go;
|
|
int p2p_no_group_iface;
|
|
struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS];
|
|
unsigned int size;
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role);
|
|
|
|
if (force_freq)
|
|
*force_freq = 0;
|
|
if (pref_freq)
|
|
*pref_freq = 0;
|
|
|
|
size = P2P_MAX_PREF_CHANNELS;
|
|
if (force_freq && pref_freq &&
|
|
!wpas_p2p_setup_freqs(wpa_s, 0, (int *) force_freq,
|
|
(int *) pref_freq, 0, pref_freq_list, &size))
|
|
wpas_p2p_set_own_freq_preference(wpa_s,
|
|
*force_freq ? *force_freq :
|
|
*pref_freq);
|
|
|
|
/*
|
|
* For non-concurrent capable devices:
|
|
* If persistent_go, then no new.
|
|
* If GO, then no client.
|
|
* If client, then no GO.
|
|
*/
|
|
go_wpa_s = wpas_p2p_get_go_group(wpa_s);
|
|
if (go_wpa_s)
|
|
owned_members = p2p_get_group_num_members(go_wpa_s->p2p_group);
|
|
persistent_go = wpas_p2p_get_persistent_go(wpa_s);
|
|
p2p_no_group_iface = !wpas_p2p_create_iface(wpa_s);
|
|
cli_wpa_s = wpas_p2p_get_cli_group(wpa_s);
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: GO(iface)=%p members=%u CLI(iface)=%p persistent(ssid)=%p",
|
|
go_wpa_s, owned_members, cli_wpa_s, persistent_go);
|
|
|
|
/* If not concurrent, restrict our choices */
|
|
if (p2p_no_group_iface) {
|
|
wpa_printf(MSG_DEBUG, "P2P: p2p_no_group_iface");
|
|
|
|
if (cli_wpa_s)
|
|
return P2PS_SETUP_NONE;
|
|
|
|
if (go_wpa_s) {
|
|
if (role == P2PS_SETUP_CLIENT ||
|
|
incoming == P2PS_SETUP_GROUP_OWNER ||
|
|
p2p_client_limit_reached(go_wpa_s->p2p_group))
|
|
return P2PS_SETUP_NONE;
|
|
|
|
return P2PS_SETUP_GROUP_OWNER;
|
|
}
|
|
|
|
if (persistent_go) {
|
|
if (role == P2PS_SETUP_NONE || role == P2PS_SETUP_NEW) {
|
|
if (!incoming)
|
|
return P2PS_SETUP_GROUP_OWNER |
|
|
P2PS_SETUP_CLIENT;
|
|
if (incoming == P2PS_SETUP_NEW) {
|
|
u8 r;
|
|
|
|
if (os_get_random(&r, sizeof(r)) < 0 ||
|
|
(r & 1))
|
|
return P2PS_SETUP_CLIENT;
|
|
return P2PS_SETUP_GROUP_OWNER;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If a required role has been specified, handle it here */
|
|
if (role && role != P2PS_SETUP_NEW) {
|
|
switch (incoming) {
|
|
case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
|
|
case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
|
|
/*
|
|
* Peer has an active GO, so if the role allows it and
|
|
* we do not have any active roles, become client.
|
|
*/
|
|
if ((role & P2PS_SETUP_CLIENT) && !go_wpa_s &&
|
|
!cli_wpa_s)
|
|
return P2PS_SETUP_CLIENT;
|
|
|
|
/* fall through */
|
|
|
|
case P2PS_SETUP_NONE:
|
|
case P2PS_SETUP_NEW:
|
|
conncap = role;
|
|
goto grp_owner;
|
|
|
|
case P2PS_SETUP_GROUP_OWNER:
|
|
/*
|
|
* Must be a complimentary role - cannot be a client to
|
|
* more than one peer.
|
|
*/
|
|
if (incoming == role || cli_wpa_s)
|
|
return P2PS_SETUP_NONE;
|
|
|
|
return P2PS_SETUP_CLIENT;
|
|
|
|
case P2PS_SETUP_CLIENT:
|
|
/* Must be a complimentary role */
|
|
if (incoming != role) {
|
|
conncap = P2PS_SETUP_GROUP_OWNER;
|
|
goto grp_owner;
|
|
}
|
|
/* fall through */
|
|
|
|
default:
|
|
return P2PS_SETUP_NONE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* For now, we only will support ownership of one group, and being a
|
|
* client of one group. Therefore, if we have either an existing GO
|
|
* group, or an existing client group, we will not do a new GO
|
|
* negotiation, but rather try to re-use the existing groups.
|
|
*/
|
|
switch (incoming) {
|
|
case P2PS_SETUP_NONE:
|
|
case P2PS_SETUP_NEW:
|
|
if (cli_wpa_s)
|
|
conncap = P2PS_SETUP_GROUP_OWNER;
|
|
else if (!owned_members)
|
|
conncap = P2PS_SETUP_NEW;
|
|
else if (incoming == P2PS_SETUP_NONE)
|
|
conncap = P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT;
|
|
else
|
|
conncap = P2PS_SETUP_CLIENT;
|
|
break;
|
|
|
|
case P2PS_SETUP_CLIENT:
|
|
conncap = P2PS_SETUP_GROUP_OWNER;
|
|
break;
|
|
|
|
case P2PS_SETUP_GROUP_OWNER:
|
|
if (!cli_wpa_s)
|
|
conncap = P2PS_SETUP_CLIENT;
|
|
break;
|
|
|
|
case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
|
|
case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
|
|
if (cli_wpa_s)
|
|
conncap = P2PS_SETUP_GROUP_OWNER;
|
|
else {
|
|
u8 r;
|
|
|
|
if (os_get_random(&r, sizeof(r)) < 0 ||
|
|
(r & 1))
|
|
conncap = P2PS_SETUP_CLIENT;
|
|
else
|
|
conncap = P2PS_SETUP_GROUP_OWNER;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return P2PS_SETUP_NONE;
|
|
}
|
|
|
|
grp_owner:
|
|
if ((conncap & P2PS_SETUP_GROUP_OWNER) ||
|
|
(!incoming && (conncap & P2PS_SETUP_NEW))) {
|
|
if (go_wpa_s && p2p_client_limit_reached(go_wpa_s->p2p_group))
|
|
conncap &= ~P2PS_SETUP_GROUP_OWNER;
|
|
|
|
s = wpas_p2p_get_persistent_go(wpa_s);
|
|
if (!s && !go_wpa_s && p2p_no_group_iface) {
|
|
p2p_set_intended_addr(wpa_s->global->p2p,
|
|
wpa_s->p2p_mgmt ?
|
|
wpa_s->parent->own_addr :
|
|
wpa_s->own_addr);
|
|
} else if (!s && !go_wpa_s) {
|
|
if (wpas_p2p_add_group_interface(wpa_s,
|
|
WPA_IF_P2P_GROUP) < 0) {
|
|
wpa_printf(MSG_ERROR,
|
|
"P2P: Failed to allocate a new interface for the group");
|
|
return P2PS_SETUP_NONE;
|
|
}
|
|
wpa_s->global->pending_group_iface_for_p2ps = 1;
|
|
p2p_set_intended_addr(wpa_s->global->p2p,
|
|
wpa_s->pending_interface_addr);
|
|
}
|
|
}
|
|
|
|
return conncap;
|
|
}
|
|
|
|
|
|
static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
|
|
enum p2p_group_removal_reason removal_reason)
|
|
{
|
|
struct wpa_ssid *ssid;
|
|
char *gtype;
|
|
const char *reason;
|
|
|
|
ssid = wpa_s->current_ssid;
|
|
if (ssid == NULL) {
|
|
/*
|
|
* The current SSID was not known, but there may still be a
|
|
* pending P2P group interface waiting for provisioning or a
|
|
* P2P group that is trying to reconnect.
|
|
*/
|
|
ssid = wpa_s->conf->ssid;
|
|
while (ssid) {
|
|
if (ssid->p2p_group && ssid->disabled != 2)
|
|
break;
|
|
ssid = ssid->next;
|
|
}
|
|
if (ssid == NULL &&
|
|
wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)
|
|
{
|
|
wpa_printf(MSG_ERROR, "P2P: P2P group interface "
|
|
"not found");
|
|
return -1;
|
|
}
|
|
}
|
|
if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO)
|
|
gtype = "GO";
|
|
else if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT ||
|
|
(ssid && ssid->mode == WPAS_MODE_INFRA)) {
|
|
wpa_s->reassociate = 0;
|
|
wpa_s->disconnected = 1;
|
|
gtype = "client";
|
|
} else
|
|
gtype = "GO";
|
|
|
|
if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid)
|
|
wpas_notify_p2p_group_removed(wpa_s, ssid, gtype);
|
|
|
|
if (os_strcmp(gtype, "client") == 0) {
|
|
wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
|
|
if (eloop_is_timeout_registered(wpas_p2p_psk_failure_removal,
|
|
wpa_s, NULL)) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: PSK failure removal was scheduled, so use PSK failure as reason for group removal");
|
|
removal_reason = P2P_GROUP_REMOVAL_PSK_FAILURE;
|
|
eloop_cancel_timeout(wpas_p2p_psk_failure_removal,
|
|
wpa_s, NULL);
|
|
}
|
|
}
|
|
|
|
if (wpa_s->cross_connect_in_use) {
|
|
wpa_s->cross_connect_in_use = 0;
|
|
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
|
|
P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
|
|
wpa_s->ifname, wpa_s->cross_connect_uplink);
|
|
}
|
|
switch (removal_reason) {
|
|
case P2P_GROUP_REMOVAL_REQUESTED:
|
|
reason = " reason=REQUESTED";
|
|
break;
|
|
case P2P_GROUP_REMOVAL_FORMATION_FAILED:
|
|
reason = " reason=FORMATION_FAILED";
|
|
break;
|
|
case P2P_GROUP_REMOVAL_IDLE_TIMEOUT:
|
|
reason = " reason=IDLE";
|
|
break;
|
|
case P2P_GROUP_REMOVAL_UNAVAILABLE:
|
|
reason = " reason=UNAVAILABLE";
|
|
break;
|
|
case P2P_GROUP_REMOVAL_GO_ENDING_SESSION:
|
|
reason = " reason=GO_ENDING_SESSION";
|
|
break;
|
|
case P2P_GROUP_REMOVAL_PSK_FAILURE:
|
|
reason = " reason=PSK_FAILURE";
|
|
break;
|
|
case P2P_GROUP_REMOVAL_FREQ_CONFLICT:
|
|
reason = " reason=FREQ_CONFLICT";
|
|
break;
|
|
default:
|
|
reason = "";
|
|
break;
|
|
}
|
|
if (removal_reason != P2P_GROUP_REMOVAL_SILENT) {
|
|
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
|
|
P2P_EVENT_GROUP_REMOVED "%s %s%s",
|
|
wpa_s->ifname, gtype, reason);
|
|
}
|
|
|
|
if (eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL) > 0)
|
|
wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group freq_conflict timeout");
|
|
if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
|
|
wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
|
|
if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
|
|
wpa_s->p2pdev, NULL) > 0) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation "
|
|
"timeout");
|
|
wpa_s->p2p_in_provisioning = 0;
|
|
wpas_p2p_group_formation_failed(wpa_s, 1);
|
|
}
|
|
|
|
wpa_s->p2p_in_invitation = 0;
|
|
eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
|
|
eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, wpa_s, NULL);
|
|
|
|
/*
|
|
* Make sure wait for the first client does not remain active after the
|
|
* group has been removed.
|
|
*/
|
|
wpa_s->global->p2p_go_wait_client.sec = 0;
|
|
|
|
if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
|
|
struct wpa_global *global;
|
|
char *ifname;
|
|
enum wpa_driver_if_type type;
|
|
wpa_printf(MSG_DEBUG, "P2P: Remove group interface %s",
|
|
wpa_s->ifname);
|
|
global = wpa_s->global;
|
|
ifname = os_strdup(wpa_s->ifname);
|
|
type = wpas_p2p_if_type(wpa_s->p2p_group_interface);
|
|
eloop_cancel_timeout(run_wpas_p2p_disconnect, wpa_s, NULL);
|
|
wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0);
|
|
wpa_s = global->ifaces;
|
|
if (wpa_s && ifname)
|
|
wpa_drv_if_remove(wpa_s, type, ifname);
|
|
os_free(ifname);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* The primary interface was used for P2P group operations, so
|
|
* need to reset its p2pdev.
|
|
*/
|
|
wpa_s->p2pdev = wpa_s->parent;
|
|
|
|
if (!wpa_s->p2p_go_group_formation_completed) {
|
|
wpa_s->global->p2p_group_formation = NULL;
|
|
wpa_s->p2p_in_provisioning = 0;
|
|
}
|
|
|
|
wpa_s->show_group_started = 0;
|
|
os_free(wpa_s->go_params);
|
|
wpa_s->go_params = NULL;
|
|
|
|
os_free(wpa_s->p2p_group_common_freqs);
|
|
wpa_s->p2p_group_common_freqs = NULL;
|
|
wpa_s->p2p_group_common_freqs_num = 0;
|
|
wpa_s->p2p_go_do_acs = 0;
|
|
wpa_s->p2p_go_allow_dfs = 0;
|
|
|
|
wpa_s->waiting_presence_resp = 0;
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Remove temporary group network");
|
|
if (ssid && (ssid->p2p_group ||
|
|
ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION ||
|
|
(ssid->key_mgmt & WPA_KEY_MGMT_WPS))) {
|
|
int id = ssid->id;
|
|
if (ssid == wpa_s->current_ssid) {
|
|
wpa_sm_set_config(wpa_s->wpa, NULL);
|
|
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
|
|
wpa_s->current_ssid = NULL;
|
|
}
|
|
/*
|
|
* Networks objects created during any P2P activities are not
|
|
* exposed out as they might/will confuse certain non-P2P aware
|
|
* applications since these network objects won't behave like
|
|
* regular ones.
|
|
*
|
|
* Likewise, we don't send out network removed signals for such
|
|
* network objects.
|
|
*/
|
|
wpa_config_remove_network(wpa_s->conf, id);
|
|
wpa_supplicant_clear_status(wpa_s);
|
|
wpa_supplicant_cancel_sched_scan(wpa_s);
|
|
} else {
|
|
wpa_printf(MSG_DEBUG, "P2P: Temporary group network not "
|
|
"found");
|
|
}
|
|
if (wpa_s->ap_iface)
|
|
wpa_supplicant_ap_deinit(wpa_s);
|
|
else
|
|
wpa_drv_deinit_p2p_cli(wpa_s);
|
|
|
|
os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s,
|
|
u8 *go_dev_addr,
|
|
const u8 *ssid, size_t ssid_len)
|
|
{
|
|
struct wpa_bss *bss;
|
|
const u8 *bssid;
|
|
struct wpabuf *p2p;
|
|
u8 group_capab;
|
|
const u8 *addr;
|
|
|
|
if (wpa_s->go_params)
|
|
bssid = wpa_s->go_params->peer_interface_addr;
|
|
else
|
|
bssid = wpa_s->bssid;
|
|
|
|
bss = wpa_bss_get(wpa_s, bssid, ssid, ssid_len);
|
|
if (bss == NULL && wpa_s->go_params &&
|
|
!is_zero_ether_addr(wpa_s->go_params->peer_device_addr))
|
|
bss = wpa_bss_get_p2p_dev_addr(
|
|
wpa_s, wpa_s->go_params->peer_device_addr);
|
|
if (bss == NULL) {
|
|
u8 iface_addr[ETH_ALEN];
|
|
if (p2p_get_interface_addr(wpa_s->global->p2p, bssid,
|
|
iface_addr) == 0)
|
|
bss = wpa_bss_get(wpa_s, iface_addr, ssid, ssid_len);
|
|
}
|
|
if (bss == NULL) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether "
|
|
"group is persistent - BSS " MACSTR " not found",
|
|
MAC2STR(bssid));
|
|
return 0;
|
|
}
|
|
|
|
p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
|
|
if (p2p == NULL)
|
|
p2p = wpa_bss_get_vendor_ie_multi_beacon(bss,
|
|
P2P_IE_VENDOR_TYPE);
|
|
if (p2p == NULL) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether "
|
|
"group is persistent - BSS " MACSTR
|
|
" did not include P2P IE", MAC2STR(bssid));
|
|
wpa_hexdump(MSG_DEBUG, "P2P: Probe Response IEs",
|
|
wpa_bss_ie_ptr(bss), bss->ie_len);
|
|
wpa_hexdump(MSG_DEBUG, "P2P: Beacon IEs",
|
|
wpa_bss_ie_ptr(bss) + bss->ie_len,
|
|
bss->beacon_ie_len);
|
|
return 0;
|
|
}
|
|
|
|
group_capab = p2p_get_group_capab(p2p);
|
|
addr = p2p_get_go_dev_addr(p2p);
|
|
wpa_printf(MSG_DEBUG, "P2P: Checking whether group is persistent: "
|
|
"group_capab=0x%x", group_capab);
|
|
if (addr) {
|
|
os_memcpy(go_dev_addr, addr, ETH_ALEN);
|
|
wpa_printf(MSG_DEBUG, "P2P: GO Device Address " MACSTR,
|
|
MAC2STR(addr));
|
|
} else
|
|
os_memset(go_dev_addr, 0, ETH_ALEN);
|
|
wpabuf_free(p2p);
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: BSS " MACSTR " group_capab=0x%x "
|
|
"go_dev_addr=" MACSTR,
|
|
MAC2STR(bssid), group_capab, MAC2STR(go_dev_addr));
|
|
|
|
return !!(group_capab & P2P_GROUP_CAPAB_PERSISTENT_GROUP);
|
|
}
|
|
|
|
|
|
static int wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s,
|
|
struct wpa_ssid *ssid,
|
|
const u8 *go_dev_addr)
|
|
{
|
|
struct wpa_ssid *s;
|
|
int changed = 0;
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Storing credentials for a persistent "
|
|
"group (GO Dev Addr " MACSTR ")", MAC2STR(go_dev_addr));
|
|
for (s = wpa_s->conf->ssid; s; s = s->next) {
|
|
if (s->disabled == 2 &&
|
|
os_memcmp(go_dev_addr, s->bssid, ETH_ALEN) == 0 &&
|
|
s->ssid_len == ssid->ssid_len &&
|
|
os_memcmp(ssid->ssid, s->ssid, ssid->ssid_len) == 0)
|
|
break;
|
|
}
|
|
|
|
if (s) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Update existing persistent group "
|
|
"entry");
|
|
if (ssid->passphrase && !s->passphrase)
|
|
changed = 1;
|
|
else if (ssid->passphrase && s->passphrase &&
|
|
os_strcmp(ssid->passphrase, s->passphrase) != 0)
|
|
changed = 1;
|
|
} else {
|
|
wpa_printf(MSG_DEBUG, "P2P: Create a new persistent group "
|
|
"entry");
|
|
changed = 1;
|
|
s = wpa_config_add_network(wpa_s->conf);
|
|
if (s == NULL)
|
|
return -1;
|
|
|
|
/*
|
|
* Instead of network_added we emit persistent_group_added
|
|
* notification. Also to keep the defense checks in
|
|
* persistent_group obj registration method, we set the
|
|
* relevant flags in s to designate it as a persistent group.
|
|
*/
|
|
s->p2p_group = 1;
|
|
s->p2p_persistent_group = 1;
|
|
wpas_notify_persistent_group_added(wpa_s, s);
|
|
wpa_config_set_network_defaults(s);
|
|
}
|
|
|
|
s->p2p_group = 1;
|
|
s->p2p_persistent_group = 1;
|
|
s->disabled = 2;
|
|
s->bssid_set = 1;
|
|
os_memcpy(s->bssid, go_dev_addr, ETH_ALEN);
|
|
s->mode = ssid->mode;
|
|
s->auth_alg = WPA_AUTH_ALG_OPEN;
|
|
s->key_mgmt = WPA_KEY_MGMT_PSK;
|
|
s->proto = WPA_PROTO_RSN;
|
|
s->pbss = ssid->pbss;
|
|
s->pairwise_cipher = ssid->pbss ? WPA_CIPHER_GCMP : WPA_CIPHER_CCMP;
|
|
s->export_keys = 1;
|
|
if (ssid->passphrase) {
|
|
os_free(s->passphrase);
|
|
s->passphrase = os_strdup(ssid->passphrase);
|
|
}
|
|
if (ssid->psk_set) {
|
|
s->psk_set = 1;
|
|
os_memcpy(s->psk, ssid->psk, 32);
|
|
}
|
|
if (s->passphrase && !s->psk_set)
|
|
wpa_config_update_psk(s);
|
|
if (s->ssid == NULL || s->ssid_len < ssid->ssid_len) {
|
|
os_free(s->ssid);
|
|
s->ssid = os_malloc(ssid->ssid_len);
|
|
}
|
|
if (s->ssid) {
|
|
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;
|
|
}
|
|
|
|
if (changed && wpa_s->conf->update_config &&
|
|
wpa_config_write(wpa_s->confname, wpa_s->conf)) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
|
|
}
|
|
|
|
return s->id;
|
|
}
|
|
|
|
|
|
static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s,
|
|
const u8 *addr)
|
|
{
|
|
struct wpa_ssid *ssid, *s;
|
|
u8 *n;
|
|
size_t i;
|
|
int found = 0;
|
|
struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s;
|
|
|
|
ssid = wpa_s->current_ssid;
|
|
if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
|
|
!ssid->p2p_persistent_group)
|
|
return;
|
|
|
|
for (s = p2p_wpa_s->conf->ssid; s; s = s->next) {
|
|
if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO)
|
|
continue;
|
|
|
|
if (s->ssid_len == ssid->ssid_len &&
|
|
os_memcmp(s->ssid, ssid->ssid, s->ssid_len) == 0)
|
|
break;
|
|
}
|
|
|
|
if (s == NULL)
|
|
return;
|
|
|
|
for (i = 0; s->p2p_client_list && i < s->num_p2p_clients; i++) {
|
|
if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN, addr,
|
|
ETH_ALEN) != 0)
|
|
continue;
|
|
|
|
if (i == s->num_p2p_clients - 1)
|
|
return; /* already the most recent entry */
|
|
|
|
/* move the entry to mark it most recent */
|
|
os_memmove(s->p2p_client_list + i * 2 * ETH_ALEN,
|
|
s->p2p_client_list + (i + 1) * 2 * ETH_ALEN,
|
|
(s->num_p2p_clients - i - 1) * 2 * ETH_ALEN);
|
|
os_memcpy(s->p2p_client_list +
|
|
(s->num_p2p_clients - 1) * 2 * ETH_ALEN, addr,
|
|
ETH_ALEN);
|
|
os_memset(s->p2p_client_list +
|
|
(s->num_p2p_clients - 1) * 2 * ETH_ALEN + ETH_ALEN,
|
|
0xff, ETH_ALEN);
|
|
found = 1;
|
|
break;
|
|
}
|
|
|
|
if (!found && s->num_p2p_clients < P2P_MAX_STORED_CLIENTS) {
|
|
n = os_realloc_array(s->p2p_client_list,
|
|
s->num_p2p_clients + 1, 2 * ETH_ALEN);
|
|
if (n == NULL)
|
|
return;
|
|
os_memcpy(n + s->num_p2p_clients * 2 * ETH_ALEN, addr,
|
|
ETH_ALEN);
|
|
os_memset(n + s->num_p2p_clients * 2 * ETH_ALEN + ETH_ALEN,
|
|
0xff, ETH_ALEN);
|
|
s->p2p_client_list = n;
|
|
s->num_p2p_clients++;
|
|
} else if (!found && s->p2p_client_list) {
|
|
/* Not enough room for an additional entry - drop the oldest
|
|
* entry */
|
|
os_memmove(s->p2p_client_list,
|
|
s->p2p_client_list + 2 * ETH_ALEN,
|
|
(s->num_p2p_clients - 1) * 2 * ETH_ALEN);
|
|
os_memcpy(s->p2p_client_list +
|
|
(s->num_p2p_clients - 1) * 2 * ETH_ALEN,
|
|
addr, ETH_ALEN);
|
|
os_memset(s->p2p_client_list +
|
|
(s->num_p2p_clients - 1) * 2 * ETH_ALEN + ETH_ALEN,
|
|
0xff, ETH_ALEN);
|
|
}
|
|
|
|
if (p2p_wpa_s->conf->update_config &&
|
|
wpa_config_write(p2p_wpa_s->confname, p2p_wpa_s->conf))
|
|
wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
|
|
}
|
|
|
|
|
|
static void wpas_p2p_group_started(struct wpa_supplicant *wpa_s,
|
|
int go, struct wpa_ssid *ssid, int freq,
|
|
const u8 *psk, const char *passphrase,
|
|
const u8 *go_dev_addr, int persistent,
|
|
const char *extra)
|
|
{
|
|
const char *ssid_txt;
|
|
char psk_txt[65];
|
|
|
|
if (psk)
|
|
wpa_snprintf_hex(psk_txt, sizeof(psk_txt), psk, 32);
|
|
else
|
|
psk_txt[0] = '\0';
|
|
|
|
if (ssid)
|
|
ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
|
|
else
|
|
ssid_txt = "";
|
|
|
|
if (passphrase && passphrase[0] == '\0')
|
|
passphrase = NULL;
|
|
|
|
/*
|
|
* Include PSK/passphrase only in the control interface message and
|
|
* leave it out from the debug log entry.
|
|
*/
|
|
wpa_msg_global_ctrl(wpa_s->p2pdev, MSG_INFO,
|
|
P2P_EVENT_GROUP_STARTED
|
|
"%s %s ssid=\"%s\" freq=%d%s%s%s%s%s go_dev_addr="
|
|
MACSTR "%s%s",
|
|
wpa_s->ifname, go ? "GO" : "client", ssid_txt, freq,
|
|
psk ? " psk=" : "", psk_txt,
|
|
passphrase ? " passphrase=\"" : "",
|
|
passphrase ? passphrase : "",
|
|
passphrase ? "\"" : "",
|
|
MAC2STR(go_dev_addr),
|
|
persistent ? " [PERSISTENT]" : "", extra);
|
|
wpa_printf(MSG_INFO, P2P_EVENT_GROUP_STARTED
|
|
"%s %s ssid=\"%s\" freq=%d go_dev_addr=" MACSTR "%s%s",
|
|
wpa_s->ifname, go ? "GO" : "client", ssid_txt, freq,
|
|
MAC2STR(go_dev_addr), persistent ? " [PERSISTENT]" : "",
|
|
extra);
|
|
}
|
|
|
|
|
|
static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
|
|
int success, int already_deleted)
|
|
{
|
|
struct wpa_ssid *ssid;
|
|
int client;
|
|
int persistent;
|
|
u8 go_dev_addr[ETH_ALEN];
|
|
|
|
/*
|
|
* This callback is likely called for the main interface. Update wpa_s
|
|
* to use the group interface if a new interface was created for the
|
|
* group.
|
|
*/
|
|
if (wpa_s->global->p2p_group_formation)
|
|
wpa_s = wpa_s->global->p2p_group_formation;
|
|
if (wpa_s->p2p_go_group_formation_completed) {
|
|
wpa_s->global->p2p_group_formation = NULL;
|
|
wpa_s->p2p_in_provisioning = 0;
|
|
} else if (wpa_s->p2p_in_provisioning && !success) {
|
|
wpa_msg(wpa_s, MSG_DEBUG,
|
|
"P2P: Stop provisioning state due to failure");
|
|
wpa_s->p2p_in_provisioning = 0;
|
|
}
|
|
wpa_s->p2p_in_invitation = 0;
|
|
wpa_s->group_formation_reported = 1;
|
|
|
|
if (!success) {
|
|
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
|
|
P2P_EVENT_GROUP_FORMATION_FAILURE);
|
|
wpas_notify_p2p_group_formation_failure(wpa_s, "");
|
|
if (already_deleted)
|
|
return;
|
|
wpas_p2p_group_delete(wpa_s,
|
|
P2P_GROUP_REMOVAL_FORMATION_FAILED);
|
|
return;
|
|
}
|
|
|
|
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
|
|
P2P_EVENT_GROUP_FORMATION_SUCCESS);
|
|
|
|
ssid = wpa_s->current_ssid;
|
|
if (ssid && ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
|
|
ssid->mode = WPAS_MODE_P2P_GO;
|
|
p2p_group_notif_formation_done(wpa_s->p2p_group);
|
|
wpa_supplicant_ap_mac_addr_filter(wpa_s, NULL);
|
|
}
|
|
|
|
persistent = 0;
|
|
if (ssid) {
|
|
client = ssid->mode == WPAS_MODE_INFRA;
|
|
if (ssid->mode == WPAS_MODE_P2P_GO) {
|
|
persistent = ssid->p2p_persistent_group;
|
|
os_memcpy(go_dev_addr, wpa_s->global->p2p_dev_addr,
|
|
ETH_ALEN);
|
|
} else
|
|
persistent = wpas_p2p_persistent_group(wpa_s,
|
|
go_dev_addr,
|
|
ssid->ssid,
|
|
ssid->ssid_len);
|
|
} else {
|
|
client = wpa_s->p2p_group_interface ==
|
|
P2P_GROUP_INTERFACE_CLIENT;
|
|
os_memset(go_dev_addr, 0, ETH_ALEN);
|
|
}
|
|
|
|
wpa_s->show_group_started = 0;
|
|
if (client) {
|
|
/*
|
|
* Indicate event only after successfully completed 4-way
|
|
* handshake, i.e., when the interface is ready for data
|
|
* packets.
|
|
*/
|
|
wpa_s->show_group_started = 1;
|
|
} else {
|
|
wpas_p2p_group_started(wpa_s, 1, ssid,
|
|
ssid ? ssid->frequency : 0,
|
|
ssid && ssid->passphrase == NULL &&
|
|
ssid->psk_set ? ssid->psk : NULL,
|
|
ssid ? ssid->passphrase : NULL,
|
|
go_dev_addr, persistent, "");
|
|
wpas_p2p_cross_connect_setup(wpa_s);
|
|
wpas_p2p_set_group_idle_timeout(wpa_s);
|
|
}
|
|
|
|
if (persistent)
|
|
wpas_p2p_store_persistent_group(wpa_s->p2pdev,
|
|
ssid, go_dev_addr);
|
|
else {
|
|
os_free(wpa_s->global->add_psk);
|
|
wpa_s->global->add_psk = NULL;
|
|
}
|
|
|
|
if (!client) {
|
|
wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 0, NULL);
|
|
os_get_reltime(&wpa_s->global->p2p_go_wait_client);
|
|
}
|
|
}
|
|
|
|
|
|
struct send_action_work {
|
|
unsigned int freq;
|
|
u8 dst[ETH_ALEN];
|
|
u8 src[ETH_ALEN];
|
|
u8 bssid[ETH_ALEN];
|
|
size_t len;
|
|
unsigned int wait_time;
|
|
u8 buf[0];
|
|
};
|
|
|
|
|
|
static void wpas_p2p_free_send_action_work(struct wpa_supplicant *wpa_s)
|
|
{
|
|
struct send_action_work *awork = wpa_s->p2p_send_action_work->ctx;
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Free Action frame radio work @%p (freq=%u dst="
|
|
MACSTR " src=" MACSTR " bssid=" MACSTR " wait_time=%u)",
|
|
wpa_s->p2p_send_action_work, awork->freq,
|
|
MAC2STR(awork->dst), MAC2STR(awork->src),
|
|
MAC2STR(awork->bssid), awork->wait_time);
|
|
wpa_hexdump(MSG_DEBUG, "P2P: Freeing pending Action frame",
|
|
awork->buf, awork->len);
|
|
os_free(awork);
|
|
wpa_s->p2p_send_action_work->ctx = NULL;
|
|
radio_work_done(wpa_s->p2p_send_action_work);
|
|
wpa_s->p2p_send_action_work = NULL;
|
|
}
|
|
|
|
|
|
static void wpas_p2p_send_action_work_timeout(void *eloop_ctx,
|
|
void *timeout_ctx)
|
|
{
|
|
struct wpa_supplicant *wpa_s = eloop_ctx;
|
|
|
|
if (!wpa_s->p2p_send_action_work)
|
|
return;
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Send Action frame radio work timed out");
|
|
wpas_p2p_free_send_action_work(wpa_s);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_action_tx_clear(struct wpa_supplicant *wpa_s)
|
|
{
|
|
if (wpa_s->p2p_send_action_work) {
|
|
struct send_action_work *awork;
|
|
|
|
awork = wpa_s->p2p_send_action_work->ctx;
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Clear Action TX work @%p (wait_time=%u)",
|
|
wpa_s->p2p_send_action_work, awork->wait_time);
|
|
if (awork->wait_time == 0) {
|
|
wpas_p2p_free_send_action_work(wpa_s);
|
|
} else {
|
|
/*
|
|
* In theory, this should not be needed, but number of
|
|
* places in the P2P code is still using non-zero wait
|
|
* time for the last Action frame in the sequence and
|
|
* some of these do not call send_action_done().
|
|
*/
|
|
eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
|
|
wpa_s, NULL);
|
|
eloop_register_timeout(
|
|
0, awork->wait_time * 1000,
|
|
wpas_p2p_send_action_work_timeout,
|
|
wpa_s, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void wpas_p2p_send_action_tx_status(struct wpa_supplicant *wpa_s,
|
|
unsigned int freq,
|
|
const u8 *dst, const u8 *src,
|
|
const u8 *bssid,
|
|
const u8 *data, size_t data_len,
|
|
enum offchannel_send_action_result
|
|
result)
|
|
{
|
|
enum p2p_send_action_result res = P2P_SEND_ACTION_SUCCESS;
|
|
|
|
wpas_p2p_action_tx_clear(wpa_s);
|
|
|
|
if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
|
|
return;
|
|
|
|
switch (result) {
|
|
case OFFCHANNEL_SEND_ACTION_SUCCESS:
|
|
res = P2P_SEND_ACTION_SUCCESS;
|
|
break;
|
|
case OFFCHANNEL_SEND_ACTION_NO_ACK:
|
|
res = P2P_SEND_ACTION_NO_ACK;
|
|
break;
|
|
case OFFCHANNEL_SEND_ACTION_FAILED:
|
|
res = P2P_SEND_ACTION_FAILED;
|
|
break;
|
|
}
|
|
|
|
p2p_send_action_cb(wpa_s->global->p2p, freq, dst, src, bssid, res);
|
|
|
|
if (result != OFFCHANNEL_SEND_ACTION_SUCCESS &&
|
|
wpa_s->pending_pd_before_join &&
|
|
(os_memcmp(dst, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
|
|
os_memcmp(dst, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0) &&
|
|
wpa_s->p2p_fallback_to_go_neg) {
|
|
wpa_s->pending_pd_before_join = 0;
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No ACK for PD Req "
|
|
"during p2p_connect-auto");
|
|
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
|
|
P2P_EVENT_FALLBACK_TO_GO_NEG
|
|
"reason=no-ACK-to-PD-Req");
|
|
wpas_p2p_fallback_to_go_neg(wpa_s, 0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
static void wpas_send_action_cb(struct wpa_radio_work *work, int deinit)
|
|
{
|
|
struct wpa_supplicant *wpa_s = work->wpa_s;
|
|
struct send_action_work *awork = work->ctx;
|
|
|
|
if (deinit) {
|
|
if (work->started) {
|
|
eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
|
|
wpa_s, NULL);
|
|
wpa_s->p2p_send_action_work = NULL;
|
|
offchannel_send_action_done(wpa_s);
|
|
}
|
|
os_free(awork);
|
|
return;
|
|
}
|
|
|
|
if (offchannel_send_action(wpa_s, awork->freq, awork->dst, awork->src,
|
|
awork->bssid, awork->buf, awork->len,
|
|
awork->wait_time,
|
|
wpas_p2p_send_action_tx_status, 1) < 0) {
|
|
os_free(awork);
|
|
radio_work_done(work);
|
|
return;
|
|
}
|
|
wpa_s->p2p_send_action_work = work;
|
|
}
|
|
|
|
|
|
static int wpas_send_action_work(struct wpa_supplicant *wpa_s,
|
|
unsigned int freq, const u8 *dst,
|
|
const u8 *src, const u8 *bssid, const u8 *buf,
|
|
size_t len, unsigned int wait_time)
|
|
{
|
|
struct send_action_work *awork;
|
|
|
|
if (radio_work_pending(wpa_s, "p2p-send-action")) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Cannot schedule new p2p-send-action work since one is already pending");
|
|
return -1;
|
|
}
|
|
|
|
awork = os_zalloc(sizeof(*awork) + len);
|
|
if (awork == NULL)
|
|
return -1;
|
|
|
|
awork->freq = freq;
|
|
os_memcpy(awork->dst, dst, ETH_ALEN);
|
|
os_memcpy(awork->src, src, ETH_ALEN);
|
|
os_memcpy(awork->bssid, bssid, ETH_ALEN);
|
|
awork->len = len;
|
|
awork->wait_time = wait_time;
|
|
os_memcpy(awork->buf, buf, len);
|
|
|
|
if (radio_add_work(wpa_s, freq, "p2p-send-action", 1,
|
|
wpas_send_action_cb, awork) < 0) {
|
|
os_free(awork);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst,
|
|
const u8 *src, const u8 *bssid, const u8 *buf,
|
|
size_t len, unsigned int wait_time, int *scheduled)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
int listen_freq = -1, send_freq = -1;
|
|
|
|
if (scheduled)
|
|
*scheduled = 0;
|
|
if (wpa_s->p2p_listen_work)
|
|
listen_freq = wpa_s->p2p_listen_work->freq;
|
|
if (wpa_s->p2p_send_action_work)
|
|
send_freq = wpa_s->p2p_send_action_work->freq;
|
|
if (listen_freq != (int) freq && send_freq != (int) freq) {
|
|
int res;
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Schedule new radio work for Action frame TX (listen_freq=%d send_freq=%d freq=%u)",
|
|
listen_freq, send_freq, freq);
|
|
res = wpas_send_action_work(wpa_s, freq, dst, src, bssid, buf,
|
|
len, wait_time);
|
|
if (res == 0 && scheduled)
|
|
*scheduled = 1;
|
|
return res;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Use ongoing radio work for Action frame TX");
|
|
return offchannel_send_action(wpa_s, freq, dst, src, bssid, buf, len,
|
|
wait_time,
|
|
wpas_p2p_send_action_tx_status, 1);
|
|
}
|
|
|
|
|
|
static void wpas_send_action_done(void *ctx)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
|
|
if (wpa_s->p2p_send_action_work) {
|
|
eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
|
|
wpa_s, NULL);
|
|
os_free(wpa_s->p2p_send_action_work->ctx);
|
|
radio_work_done(wpa_s->p2p_send_action_work);
|
|
wpa_s->p2p_send_action_work = NULL;
|
|
}
|
|
|
|
offchannel_send_action_done(wpa_s);
|
|
}
|
|
|
|
|
|
static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s,
|
|
struct p2p_go_neg_results *params)
|
|
{
|
|
if (wpa_s->go_params == NULL) {
|
|
wpa_s->go_params = os_malloc(sizeof(*params));
|
|
if (wpa_s->go_params == NULL)
|
|
return -1;
|
|
}
|
|
os_memcpy(wpa_s->go_params, params, sizeof(*params));
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
|
|
struct p2p_go_neg_results *res)
|
|
{
|
|
wpa_s->group_formation_reported = 0;
|
|
wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR
|
|
" dev_addr " MACSTR " wps_method %d",
|
|
MAC2STR(res->peer_interface_addr),
|
|
MAC2STR(res->peer_device_addr), res->wps_method);
|
|
wpa_hexdump_ascii(MSG_DEBUG, "P2P: Start WPS Enrollee for SSID",
|
|
res->ssid, res->ssid_len);
|
|
wpa_supplicant_ap_deinit(wpa_s);
|
|
wpas_copy_go_neg_results(wpa_s, res);
|
|
if (res->wps_method == WPS_PBC) {
|
|
wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1, 0);
|
|
#ifdef CONFIG_WPS_NFC
|
|
} else if (res->wps_method == WPS_NFC) {
|
|
wpas_wps_start_nfc(wpa_s, res->peer_device_addr,
|
|
res->peer_interface_addr,
|
|
wpa_s->p2pdev->p2p_oob_dev_pw,
|
|
wpa_s->p2pdev->p2p_oob_dev_pw_id, 1,
|
|
wpa_s->p2pdev->p2p_oob_dev_pw_id ==
|
|
DEV_PW_NFC_CONNECTION_HANDOVER ?
|
|
wpa_s->p2pdev->p2p_peer_oob_pubkey_hash :
|
|
NULL,
|
|
NULL, 0, 0);
|
|
#endif /* CONFIG_WPS_NFC */
|
|
} else {
|
|
u16 dev_pw_id = DEV_PW_DEFAULT;
|
|
if (wpa_s->p2p_wps_method == WPS_P2PS)
|
|
dev_pw_id = DEV_PW_P2PS_DEFAULT;
|
|
if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD)
|
|
dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED;
|
|
wpas_wps_start_pin(wpa_s, res->peer_interface_addr,
|
|
wpa_s->p2p_pin, 1, dev_pw_id);
|
|
}
|
|
}
|
|
|
|
|
|
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->p2pdev, 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_dump_common_freqs(struct wpa_supplicant *wpa_s)
|
|
{
|
|
char buf[20 + P2P_MAX_CHANNELS * 6];
|
|
char *pos, *end;
|
|
unsigned int i;
|
|
int res;
|
|
|
|
pos = buf;
|
|
end = pos + sizeof(buf);
|
|
for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
|
|
res = os_snprintf(pos, end - pos, " %d",
|
|
wpa_s->p2p_group_common_freqs[i]);
|
|
if (os_snprintf_error(end - pos, res))
|
|
break;
|
|
pos += res;
|
|
}
|
|
*pos = '\0';
|
|
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Common group frequencies:%s", buf);
|
|
}
|
|
|
|
|
|
static void p2p_go_save_group_common_freqs(struct wpa_supplicant *wpa_s,
|
|
struct p2p_go_neg_results *params)
|
|
{
|
|
unsigned int i, len = int_array_len(wpa_s->go_params->freq_list);
|
|
|
|
wpa_s->p2p_group_common_freqs_num = 0;
|
|
os_free(wpa_s->p2p_group_common_freqs);
|
|
wpa_s->p2p_group_common_freqs = os_calloc(len, sizeof(int));
|
|
if (!wpa_s->p2p_group_common_freqs)
|
|
return;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (!wpa_s->go_params->freq_list[i])
|
|
break;
|
|
wpa_s->p2p_group_common_freqs[i] =
|
|
wpa_s->go_params->freq_list[i];
|
|
}
|
|
wpa_s->p2p_group_common_freqs_num = i;
|
|
}
|
|
|
|
|
|
static void p2p_config_write(struct wpa_supplicant *wpa_s)
|
|
{
|
|
#ifndef CONFIG_NO_CONFIG_WRITE
|
|
if (wpa_s->p2pdev->conf->update_config &&
|
|
wpa_config_write(wpa_s->p2pdev->confname, wpa_s->p2pdev->conf))
|
|
wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
|
|
#endif /* CONFIG_NO_CONFIG_WRITE */
|
|
}
|
|
|
|
|
|
static void p2p_go_configured(void *ctx, void *data)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
struct p2p_go_neg_results *params = data;
|
|
struct wpa_ssid *ssid;
|
|
|
|
wpa_s->ap_configured_cb = NULL;
|
|
wpa_s->ap_configured_cb_ctx = NULL;
|
|
wpa_s->ap_configured_cb_data = NULL;
|
|
if (!wpa_s->go_params) {
|
|
wpa_printf(MSG_ERROR,
|
|
"P2P: p2p_go_configured() called with wpa_s->go_params == NULL");
|
|
return;
|
|
}
|
|
|
|
p2p_go_save_group_common_freqs(wpa_s, params);
|
|
p2p_go_dump_common_freqs(wpa_s);
|
|
|
|
ssid = wpa_s->current_ssid;
|
|
if (ssid && ssid->mode == WPAS_MODE_P2P_GO) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Group setup without provisioning");
|
|
if (wpa_s->global->p2p_group_formation == wpa_s)
|
|
wpa_s->global->p2p_group_formation = NULL;
|
|
wpas_p2p_group_started(wpa_s, 1, ssid, ssid->frequency,
|
|
params->passphrase[0] == '\0' ?
|
|
params->psk : NULL,
|
|
params->passphrase,
|
|
wpa_s->global->p2p_dev_addr,
|
|
params->persistent_group, "");
|
|
wpa_s->group_formation_reported = 1;
|
|
|
|
if (wpa_s->p2pdev->p2ps_method_config_any) {
|
|
if (is_zero_ether_addr(wpa_s->p2pdev->p2ps_join_addr)) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2PS: Setting default PIN for ANY");
|
|
wpa_supplicant_ap_wps_pin(wpa_s, NULL,
|
|
"12345670", NULL, 0,
|
|
0);
|
|
} else {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2PS: Setting default PIN for " MACSTR,
|
|
MAC2STR(wpa_s->p2pdev->p2ps_join_addr));
|
|
wpa_supplicant_ap_wps_pin(
|
|
wpa_s, wpa_s->p2pdev->p2ps_join_addr,
|
|
"12345670", NULL, 0, 0);
|
|
}
|
|
wpa_s->p2pdev->p2ps_method_config_any = 0;
|
|
}
|
|
|
|
os_get_reltime(&wpa_s->global->p2p_go_wait_client);
|
|
if (params->persistent_group) {
|
|
wpas_p2p_store_persistent_group(
|
|
wpa_s->p2pdev, ssid,
|
|
wpa_s->global->p2p_dev_addr);
|
|
wpas_p2p_add_psk_list(wpa_s, ssid);
|
|
}
|
|
|
|
wpas_notify_p2p_group_started(wpa_s, ssid,
|
|
params->persistent_group, 0,
|
|
NULL);
|
|
wpas_p2p_cross_connect_setup(wpa_s);
|
|
wpas_p2p_set_group_idle_timeout(wpa_s);
|
|
|
|
if (wpa_s->p2p_first_connection_timeout) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Start group formation timeout of %d seconds until first data connection on GO",
|
|
wpa_s->p2p_first_connection_timeout);
|
|
wpa_s->p2p_go_group_formation_completed = 0;
|
|
wpa_s->global->p2p_group_formation = wpa_s;
|
|
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
|
|
wpa_s->p2pdev, NULL);
|
|
eloop_register_timeout(
|
|
wpa_s->p2p_first_connection_timeout, 0,
|
|
wpas_p2p_group_formation_timeout,
|
|
wpa_s->p2pdev, NULL);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Setting up WPS for GO provisioning");
|
|
if (wpa_supplicant_ap_mac_addr_filter(wpa_s,
|
|
params->peer_interface_addr)) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Failed to setup MAC address "
|
|
"filtering");
|
|
return;
|
|
}
|
|
if (params->wps_method == WPS_PBC) {
|
|
wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr,
|
|
params->peer_device_addr);
|
|
#ifdef CONFIG_WPS_NFC
|
|
} else if (params->wps_method == WPS_NFC) {
|
|
if (wpa_s->p2pdev->p2p_oob_dev_pw_id !=
|
|
DEV_PW_NFC_CONNECTION_HANDOVER &&
|
|
!wpa_s->p2pdev->p2p_oob_dev_pw) {
|
|
wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known");
|
|
return;
|
|
}
|
|
wpas_ap_wps_add_nfc_pw(
|
|
wpa_s, wpa_s->p2pdev->p2p_oob_dev_pw_id,
|
|
wpa_s->p2pdev->p2p_oob_dev_pw,
|
|
wpa_s->p2pdev->p2p_peer_oob_pk_hash_known ?
|
|
wpa_s->p2pdev->p2p_peer_oob_pubkey_hash : NULL);
|
|
#endif /* CONFIG_WPS_NFC */
|
|
} else if (wpa_s->p2p_pin[0])
|
|
wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr,
|
|
wpa_s->p2p_pin, NULL, 0, 0);
|
|
os_free(wpa_s->go_params);
|
|
wpa_s->go_params = NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
* wpas_p2p_freq_to_edmg_channel - Convert frequency into EDMG channel
|
|
* @freq: Frequency (MHz) to convert
|
|
* @op_class: Buffer for returning operating class
|
|
* @op_edmg_channel: Buffer for returning channel number
|
|
* Returns: 0 on success, -1 on failure
|
|
*
|
|
* This can be used to find the highest channel bonding which includes the
|
|
* specified frequency.
|
|
*/
|
|
static int wpas_p2p_freq_to_edmg_channel(struct wpa_supplicant *wpa_s,
|
|
unsigned int freq,
|
|
u8 *op_class, u8 *op_edmg_channel)
|
|
{
|
|
struct hostapd_hw_modes *hwmode;
|
|
struct ieee80211_edmg_config edmg;
|
|
unsigned int i;
|
|
enum chan_width chanwidth[] = {
|
|
CHAN_WIDTH_8640,
|
|
CHAN_WIDTH_6480,
|
|
CHAN_WIDTH_4320,
|
|
};
|
|
|
|
if (!wpa_s->hw.modes)
|
|
return -1;
|
|
|
|
hwmode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
|
|
HOSTAPD_MODE_IEEE80211AD, false);
|
|
if (!hwmode) {
|
|
wpa_printf(MSG_ERROR,
|
|
"Unsupported AP mode: HOSTAPD_MODE_IEEE80211AD");
|
|
return -1;
|
|
}
|
|
|
|
/* Find the highest EDMG channel bandwidth to start the P2P GO */
|
|
for (i = 0; i < ARRAY_SIZE(chanwidth); i++) {
|
|
if (ieee80211_chaninfo_to_channel(freq, chanwidth[i], 0,
|
|
op_class,
|
|
op_edmg_channel) < 0)
|
|
continue;
|
|
|
|
hostapd_encode_edmg_chan(1, *op_edmg_channel, 0, &edmg);
|
|
if (edmg.channels &&
|
|
ieee802_edmg_is_allowed(hwmode->edmg, edmg)) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"Freq %u to EDMG channel %u at opclass %u",
|
|
freq, *op_edmg_channel, *op_class);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
int wpas_p2p_try_edmg_channel(struct wpa_supplicant *wpa_s,
|
|
struct p2p_go_neg_results *params)
|
|
{
|
|
u8 op_channel, op_class;
|
|
int freq;
|
|
|
|
/* Try social channel as primary channel frequency */
|
|
freq = (!params->freq) ? 58320 + 1 * 2160 : params->freq;
|
|
|
|
if (wpas_p2p_freq_to_edmg_channel(wpa_s, freq, &op_class,
|
|
&op_channel) == 0) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"Freq %d will be used to set an EDMG connection (channel=%u opclass=%u)",
|
|
freq, op_channel, op_class);
|
|
params->freq = freq;
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
static void wpas_start_wps_go(struct wpa_supplicant *wpa_s,
|
|
struct p2p_go_neg_results *params,
|
|
int group_formation)
|
|
{
|
|
struct wpa_ssid *ssid;
|
|
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Starting GO");
|
|
if (wpas_copy_go_neg_results(wpa_s, params) < 0) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not copy GO Negotiation "
|
|
"results");
|
|
return;
|
|
}
|
|
|
|
ssid = wpa_config_add_network(wpa_s->conf);
|
|
if (ssid == NULL) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not add network for GO");
|
|
return;
|
|
}
|
|
|
|
wpa_s->show_group_started = 0;
|
|
wpa_s->p2p_go_group_formation_completed = 0;
|
|
wpa_s->group_formation_reported = 0;
|
|
os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
|
|
|
|
wpa_config_set_network_defaults(ssid);
|
|
ssid->temporary = 1;
|
|
ssid->p2p_group = 1;
|
|
ssid->p2p_persistent_group = !!params->persistent_group;
|
|
ssid->mode = group_formation ? WPAS_MODE_P2P_GROUP_FORMATION :
|
|
WPAS_MODE_P2P_GO;
|
|
ssid->frequency = params->freq;
|
|
ssid->ht40 = params->ht40;
|
|
ssid->vht = params->vht;
|
|
ssid->max_oper_chwidth = params->max_oper_chwidth;
|
|
ssid->vht_center_freq2 = params->vht_center_freq2;
|
|
ssid->he = params->he;
|
|
if (params->edmg) {
|
|
u8 op_channel, op_class;
|
|
|
|
if (!wpas_p2p_freq_to_edmg_channel(wpa_s, params->freq,
|
|
&op_class, &op_channel)) {
|
|
ssid->edmg_channel = op_channel;
|
|
ssid->enable_edmg = params->edmg;
|
|
} else {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Could not match EDMG channel, freq %d, for GO",
|
|
params->freq);
|
|
}
|
|
}
|
|
|
|
ssid->ssid = os_zalloc(params->ssid_len + 1);
|
|
if (ssid->ssid) {
|
|
os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
|
|
ssid->ssid_len = params->ssid_len;
|
|
}
|
|
ssid->auth_alg = WPA_AUTH_ALG_OPEN;
|
|
ssid->key_mgmt = WPA_KEY_MGMT_PSK;
|
|
if (is_6ghz_freq(ssid->frequency) &&
|
|
is_p2p_6ghz_capable(wpa_s->global->p2p)) {
|
|
ssid->auth_alg |= WPA_AUTH_ALG_SAE;
|
|
ssid->key_mgmt = WPA_KEY_MGMT_SAE;
|
|
ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
|
|
ssid->sae_pwe = 1;
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use SAE auth_alg and key_mgmt");
|
|
} else {
|
|
p2p_set_6ghz_dev_capab(wpa_s->global->p2p, false);
|
|
}
|
|
ssid->proto = WPA_PROTO_RSN;
|
|
ssid->pairwise_cipher = WPA_CIPHER_CCMP;
|
|
ssid->group_cipher = WPA_CIPHER_CCMP;
|
|
if (params->freq > 56160) {
|
|
/*
|
|
* Enable GCMP instead of CCMP as pairwise_cipher and
|
|
* group_cipher in 60 GHz.
|
|
*/
|
|
ssid->pairwise_cipher = WPA_CIPHER_GCMP;
|
|
ssid->group_cipher = WPA_CIPHER_GCMP;
|
|
/* P2P GO in 60 GHz is always a PCP (PBSS) */
|
|
ssid->pbss = 1;
|
|
}
|
|
if (os_strlen(params->passphrase) > 0) {
|
|
ssid->passphrase = os_strdup(params->passphrase);
|
|
if (ssid->passphrase == NULL) {
|
|
wpa_msg_global(wpa_s, MSG_ERROR,
|
|
"P2P: Failed to copy passphrase for GO");
|
|
wpa_config_remove_network(wpa_s->conf, ssid->id);
|
|
return;
|
|
}
|
|
} else
|
|
ssid->passphrase = NULL;
|
|
ssid->psk_set = params->psk_set;
|
|
if (ssid->psk_set)
|
|
os_memcpy(ssid->psk, params->psk, sizeof(ssid->psk));
|
|
else if (ssid->passphrase)
|
|
wpa_config_update_psk(ssid);
|
|
ssid->ap_max_inactivity = wpa_s->p2pdev->conf->p2p_go_max_inactivity;
|
|
|
|
wpa_s->ap_configured_cb = p2p_go_configured;
|
|
wpa_s->ap_configured_cb_ctx = wpa_s;
|
|
wpa_s->ap_configured_cb_data = wpa_s->go_params;
|
|
wpa_s->scan_req = NORMAL_SCAN_REQ;
|
|
wpa_s->connect_without_scan = ssid;
|
|
wpa_s->reassociate = 1;
|
|
wpa_s->disconnected = 0;
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Request scan (that will be skipped) to "
|
|
"start GO)");
|
|
wpa_supplicant_req_scan(wpa_s, 0, 0);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_clone_config(struct wpa_supplicant *dst,
|
|
const struct wpa_supplicant *src)
|
|
{
|
|
struct wpa_config *d;
|
|
const struct wpa_config *s;
|
|
|
|
d = dst->conf;
|
|
s = src->conf;
|
|
|
|
#define C(n) \
|
|
do { \
|
|
if (s->n && !d->n) \
|
|
d->n = os_strdup(s->n); \
|
|
} while (0)
|
|
|
|
C(device_name);
|
|
C(manufacturer);
|
|
C(model_name);
|
|
C(model_number);
|
|
C(serial_number);
|
|
C(config_methods);
|
|
#undef C
|
|
|
|
os_memcpy(d->device_type, s->device_type, WPS_DEV_TYPE_LEN);
|
|
os_memcpy(d->sec_device_type, s->sec_device_type,
|
|
sizeof(d->sec_device_type));
|
|
d->num_sec_device_types = s->num_sec_device_types;
|
|
|
|
d->p2p_group_idle = s->p2p_group_idle;
|
|
d->p2p_go_freq_change_policy = s->p2p_go_freq_change_policy;
|
|
d->p2p_intra_bss = s->p2p_intra_bss;
|
|
d->persistent_reconnect = s->persistent_reconnect;
|
|
d->max_num_sta = s->max_num_sta;
|
|
d->pbc_in_m1 = s->pbc_in_m1;
|
|
d->ignore_old_scan_res = s->ignore_old_scan_res;
|
|
d->beacon_int = s->beacon_int;
|
|
d->dtim_period = s->dtim_period;
|
|
d->p2p_go_ctwindow = s->p2p_go_ctwindow;
|
|
d->disassoc_low_ack = s->disassoc_low_ack;
|
|
d->disable_scan_offload = s->disable_scan_offload;
|
|
d->passive_scan = s->passive_scan;
|
|
d->pmf = s->pmf;
|
|
d->p2p_6ghz_disable = s->p2p_6ghz_disable;
|
|
d->sae_pwe = s->sae_pwe;
|
|
|
|
if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey &&
|
|
!d->wps_nfc_pw_from_config) {
|
|
wpabuf_free(d->wps_nfc_dh_privkey);
|
|
wpabuf_free(d->wps_nfc_dh_pubkey);
|
|
d->wps_nfc_dh_privkey = wpabuf_dup(s->wps_nfc_dh_privkey);
|
|
d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey);
|
|
}
|
|
d->p2p_cli_probe = s->p2p_cli_probe;
|
|
d->go_interworking = s->go_interworking;
|
|
d->go_access_network_type = s->go_access_network_type;
|
|
d->go_internet = s->go_internet;
|
|
d->go_venue_group = s->go_venue_group;
|
|
d->go_venue_type = s->go_venue_type;
|
|
d->p2p_add_cli_chan = s->p2p_add_cli_chan;
|
|
}
|
|
|
|
|
|
static void wpas_p2p_get_group_ifname(struct wpa_supplicant *wpa_s,
|
|
char *ifname, size_t len)
|
|
{
|
|
char *ifname_ptr = wpa_s->ifname;
|
|
|
|
if (os_strncmp(wpa_s->ifname, P2P_MGMT_DEVICE_PREFIX,
|
|
os_strlen(P2P_MGMT_DEVICE_PREFIX)) == 0) {
|
|
ifname_ptr = os_strrchr(wpa_s->ifname, '-') + 1;
|
|
}
|
|
|
|
os_snprintf(ifname, len, "p2p-%s-%d", ifname_ptr, wpa_s->p2p_group_idx);
|
|
if (os_strlen(ifname) >= IFNAMSIZ &&
|
|
os_strlen(wpa_s->ifname) < IFNAMSIZ) {
|
|
int res;
|
|
|
|
/* Try to avoid going over the IFNAMSIZ length limit */
|
|
res = os_snprintf(ifname, len, "p2p-%d", wpa_s->p2p_group_idx);
|
|
if (os_snprintf_error(len, res) && len)
|
|
ifname[len - 1] = '\0';
|
|
}
|
|
}
|
|
|
|
|
|
static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s,
|
|
enum wpa_driver_if_type type)
|
|
{
|
|
char ifname[120], force_ifname[120];
|
|
|
|
if (wpa_s->pending_interface_name[0]) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Pending virtual interface exists "
|
|
"- skip creation of a new one");
|
|
if (is_zero_ether_addr(wpa_s->pending_interface_addr)) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Pending virtual address "
|
|
"unknown?! ifname='%s'",
|
|
wpa_s->pending_interface_name);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
wpas_p2p_get_group_ifname(wpa_s, ifname, sizeof(ifname));
|
|
force_ifname[0] = '\0';
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Create a new interface %s for the group",
|
|
ifname);
|
|
wpa_s->p2p_group_idx++;
|
|
|
|
wpa_s->pending_interface_type = type;
|
|
if (wpa_drv_if_add(wpa_s, type, ifname, NULL, NULL, force_ifname,
|
|
wpa_s->pending_interface_addr, NULL) < 0) {
|
|
wpa_printf(MSG_ERROR, "P2P: Failed to create new group "
|
|
"interface");
|
|
return -1;
|
|
}
|
|
|
|
if (wpa_s->conf->p2p_interface_random_mac_addr) {
|
|
random_mac_addr(wpa_s->pending_interface_addr);
|
|
wpa_printf(MSG_DEBUG, "P2P: Generate random MAC address " MACSTR
|
|
" for the group",
|
|
MAC2STR(wpa_s->pending_interface_addr));
|
|
}
|
|
|
|
if (force_ifname[0]) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Driver forced interface name %s",
|
|
force_ifname);
|
|
os_strlcpy(wpa_s->pending_interface_name, force_ifname,
|
|
sizeof(wpa_s->pending_interface_name));
|
|
} else
|
|
os_strlcpy(wpa_s->pending_interface_name, ifname,
|
|
sizeof(wpa_s->pending_interface_name));
|
|
wpa_printf(MSG_DEBUG, "P2P: Created pending virtual interface %s addr "
|
|
MACSTR, wpa_s->pending_interface_name,
|
|
MAC2STR(wpa_s->pending_interface_addr));
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void wpas_p2p_remove_pending_group_interface(
|
|
struct wpa_supplicant *wpa_s)
|
|
{
|
|
if (!wpa_s->pending_interface_name[0] ||
|
|
is_zero_ether_addr(wpa_s->pending_interface_addr))
|
|
return; /* No pending virtual interface */
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Removing pending group interface %s",
|
|
wpa_s->pending_interface_name);
|
|
wpa_drv_if_remove(wpa_s, wpa_s->pending_interface_type,
|
|
wpa_s->pending_interface_name);
|
|
os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
|
|
wpa_s->pending_interface_name[0] = '\0';
|
|
wpa_s->global->pending_group_iface_for_p2ps = 0;
|
|
}
|
|
|
|
|
|
static struct wpa_supplicant *
|
|
wpas_p2p_init_group_interface(struct wpa_supplicant *wpa_s, int go)
|
|
{
|
|
struct wpa_interface iface;
|
|
struct wpa_supplicant *group_wpa_s;
|
|
|
|
if (!wpa_s->pending_interface_name[0]) {
|
|
wpa_printf(MSG_ERROR, "P2P: No pending group interface");
|
|
if (!wpas_p2p_create_iface(wpa_s))
|
|
return NULL;
|
|
/*
|
|
* Something has forced us to remove the pending interface; try
|
|
* to create a new one and hope for the best that we will get
|
|
* the same local address.
|
|
*/
|
|
if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO :
|
|
WPA_IF_P2P_CLIENT) < 0)
|
|
return NULL;
|
|
}
|
|
|
|
os_memset(&iface, 0, sizeof(iface));
|
|
iface.ifname = wpa_s->pending_interface_name;
|
|
iface.driver = wpa_s->driver->name;
|
|
if (wpa_s->conf->ctrl_interface == NULL &&
|
|
wpa_s->parent != wpa_s &&
|
|
wpa_s->p2p_mgmt &&
|
|
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE))
|
|
iface.ctrl_interface = wpa_s->parent->conf->ctrl_interface;
|
|
else
|
|
iface.ctrl_interface = wpa_s->conf->ctrl_interface;
|
|
iface.driver_param = wpa_s->conf->driver_param;
|
|
group_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s);
|
|
if (group_wpa_s == NULL) {
|
|
wpa_printf(MSG_ERROR, "P2P: Failed to create new "
|
|
"wpa_supplicant interface");
|
|
return NULL;
|
|
}
|
|
wpa_s->pending_interface_name[0] = '\0';
|
|
group_wpa_s->p2p_group_interface = go ? P2P_GROUP_INTERFACE_GO :
|
|
P2P_GROUP_INTERFACE_CLIENT;
|
|
wpa_s->global->p2p_group_formation = group_wpa_s;
|
|
wpa_s->global->pending_group_iface_for_p2ps = 0;
|
|
|
|
wpas_p2p_clone_config(group_wpa_s, wpa_s);
|
|
|
|
if (wpa_s->conf->p2p_interface_random_mac_addr) {
|
|
if (wpa_drv_set_mac_addr(group_wpa_s,
|
|
wpa_s->pending_interface_addr) < 0) {
|
|
wpa_msg(group_wpa_s, MSG_INFO,
|
|
"Failed to set random MAC address");
|
|
wpa_supplicant_remove_iface(wpa_s->global, group_wpa_s,
|
|
0);
|
|
return NULL;
|
|
}
|
|
|
|
if (wpa_supplicant_update_mac_addr(group_wpa_s) < 0) {
|
|
wpa_msg(group_wpa_s, MSG_INFO,
|
|
"Could not update MAC address information");
|
|
wpa_supplicant_remove_iface(wpa_s->global, group_wpa_s,
|
|
0);
|
|
return NULL;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Using random MAC address " MACSTR
|
|
" for the group",
|
|
MAC2STR(wpa_s->pending_interface_addr));
|
|
}
|
|
|
|
return group_wpa_s;
|
|
}
|
|
|
|
|
|
static void wpas_p2p_group_formation_timeout(void *eloop_ctx,
|
|
void *timeout_ctx)
|
|
{
|
|
struct wpa_supplicant *wpa_s = eloop_ctx;
|
|
wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out");
|
|
wpas_p2p_group_formation_failed(wpa_s, 0);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s,
|
|
int already_deleted)
|
|
{
|
|
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
|
|
wpa_s->p2pdev, NULL);
|
|
if (wpa_s->global->p2p)
|
|
p2p_group_formation_failed(wpa_s->global->p2p);
|
|
wpas_group_formation_completed(wpa_s, 0, already_deleted);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_grpform_fail_after_wps(struct wpa_supplicant *wpa_s)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "P2P: Reject group formation due to WPS provisioning failure");
|
|
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
|
|
wpa_s->p2pdev, NULL);
|
|
eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
|
|
wpa_s->p2pdev, NULL);
|
|
wpa_s->global->p2p_fail_on_wps_complete = 0;
|
|
}
|
|
|
|
|
|
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->p2pdev, NULL);
|
|
eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
|
|
wpa_s->p2pdev, NULL);
|
|
}
|
|
|
|
|
|
static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
struct wpa_supplicant *group_wpa_s;
|
|
|
|
if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
|
|
wpa_drv_cancel_remain_on_channel(wpa_s);
|
|
wpa_s->off_channel_freq = 0;
|
|
wpa_s->roc_waiting_drv_freq = 0;
|
|
}
|
|
|
|
if (res->status) {
|
|
wpa_msg_global(wpa_s, MSG_INFO,
|
|
P2P_EVENT_GO_NEG_FAILURE "status=%d",
|
|
res->status);
|
|
wpas_notify_p2p_go_neg_completed(wpa_s, res);
|
|
wpas_p2p_remove_pending_group_interface(wpa_s);
|
|
return;
|
|
}
|
|
|
|
if (!res->role_go) {
|
|
/* Inform driver of the operating channel of GO. */
|
|
wpa_drv_set_prob_oper_freq(wpa_s, res->freq);
|
|
}
|
|
|
|
if (wpa_s->p2p_go_ht40)
|
|
res->ht40 = 1;
|
|
if (wpa_s->p2p_go_vht)
|
|
res->vht = 1;
|
|
if (wpa_s->p2p_go_he)
|
|
res->he = 1;
|
|
if (wpa_s->p2p_go_edmg)
|
|
res->edmg = 1;
|
|
res->max_oper_chwidth = wpa_s->p2p_go_max_oper_chwidth;
|
|
res->vht_center_freq2 = wpa_s->p2p_go_vht_center_freq2;
|
|
|
|
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS "role=%s "
|
|
"freq=%d ht40=%d peer_dev=" MACSTR " peer_iface=" MACSTR
|
|
" wps_method=%s",
|
|
res->role_go ? "GO" : "client", res->freq, res->ht40,
|
|
MAC2STR(res->peer_device_addr),
|
|
MAC2STR(res->peer_interface_addr),
|
|
p2p_wps_method_text(res->wps_method));
|
|
wpas_notify_p2p_go_neg_completed(wpa_s, res);
|
|
|
|
if (res->role_go && wpa_s->p2p_persistent_id >= 0) {
|
|
struct wpa_ssid *ssid;
|
|
ssid = wpa_config_get_network(wpa_s->conf,
|
|
wpa_s->p2p_persistent_id);
|
|
if (ssid && ssid->disabled == 2 &&
|
|
ssid->mode == WPAS_MODE_P2P_GO && ssid->passphrase) {
|
|
size_t len = os_strlen(ssid->passphrase);
|
|
wpa_printf(MSG_DEBUG, "P2P: Override passphrase based "
|
|
"on requested persistent group");
|
|
os_memcpy(res->passphrase, ssid->passphrase, len);
|
|
res->passphrase[len] = '\0';
|
|
}
|
|
}
|
|
|
|
if (wpa_s->create_p2p_iface) {
|
|
group_wpa_s =
|
|
wpas_p2p_init_group_interface(wpa_s, res->role_go);
|
|
if (group_wpa_s == NULL) {
|
|
wpas_p2p_remove_pending_group_interface(wpa_s);
|
|
eloop_cancel_timeout(wpas_p2p_long_listen_timeout,
|
|
wpa_s, NULL);
|
|
wpas_p2p_group_formation_failed(wpa_s, 1);
|
|
return;
|
|
}
|
|
os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
|
|
wpa_s->pending_interface_name[0] = '\0';
|
|
} else {
|
|
group_wpa_s = wpa_s->parent;
|
|
wpa_s->global->p2p_group_formation = group_wpa_s;
|
|
if (group_wpa_s != wpa_s)
|
|
wpas_p2p_clone_config(group_wpa_s, wpa_s);
|
|
}
|
|
|
|
group_wpa_s->p2p_in_provisioning = 1;
|
|
group_wpa_s->p2pdev = wpa_s;
|
|
if (group_wpa_s != wpa_s) {
|
|
os_memcpy(group_wpa_s->p2p_pin, wpa_s->p2p_pin,
|
|
sizeof(group_wpa_s->p2p_pin));
|
|
group_wpa_s->p2p_wps_method = wpa_s->p2p_wps_method;
|
|
}
|
|
if (res->role_go) {
|
|
wpas_start_wps_go(group_wpa_s, res, 1);
|
|
} else {
|
|
os_get_reltime(&group_wpa_s->scan_min_time);
|
|
wpas_start_wps_enrollee(group_wpa_s, res);
|
|
}
|
|
|
|
wpa_s->global->p2p_long_listen = 0;
|
|
eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
|
|
|
|
eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
|
|
eloop_register_timeout(15 + res->peer_config_timeout / 100,
|
|
(res->peer_config_timeout % 100) * 10000,
|
|
wpas_p2p_group_formation_timeout, wpa_s, NULL);
|
|
}
|
|
|
|
|
|
static void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id,
|
|
u8 go_intent)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR
|
|
" dev_passwd_id=%u go_intent=%u", MAC2STR(src),
|
|
dev_passwd_id, go_intent);
|
|
|
|
wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id, go_intent);
|
|
}
|
|
|
|
|
|
static void wpas_dev_found(void *ctx, const u8 *addr,
|
|
const struct p2p_peer_info *info,
|
|
int new_device)
|
|
{
|
|
#ifndef CONFIG_NO_STDOUT_DEBUG
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
char devtype[WPS_DEV_TYPE_BUFSIZE];
|
|
char *wfd_dev_info_hex = NULL;
|
|
|
|
#ifdef CONFIG_WIFI_DISPLAY
|
|
wfd_dev_info_hex = wifi_display_subelem_hex(info->wfd_subelems,
|
|
WFD_SUBELEM_DEVICE_INFO);
|
|
#endif /* CONFIG_WIFI_DISPLAY */
|
|
|
|
if (info->p2ps_instance) {
|
|
char str[256];
|
|
const u8 *buf = wpabuf_head(info->p2ps_instance);
|
|
size_t len = wpabuf_len(info->p2ps_instance);
|
|
|
|
while (len) {
|
|
u32 id;
|
|
u16 methods;
|
|
u8 str_len;
|
|
|
|
if (len < 4 + 2 + 1)
|
|
break;
|
|
id = WPA_GET_LE32(buf);
|
|
buf += sizeof(u32);
|
|
methods = WPA_GET_BE16(buf);
|
|
buf += sizeof(u16);
|
|
str_len = *buf++;
|
|
if (str_len > len - 4 - 2 - 1)
|
|
break;
|
|
os_memcpy(str, buf, str_len);
|
|
str[str_len] = '\0';
|
|
buf += str_len;
|
|
len -= str_len + sizeof(u32) + sizeof(u16) + sizeof(u8);
|
|
|
|
wpa_msg_global(wpa_s, MSG_INFO,
|
|
P2P_EVENT_DEVICE_FOUND MACSTR
|
|
" p2p_dev_addr=" MACSTR
|
|
" pri_dev_type=%s name='%s'"
|
|
" config_methods=0x%x"
|
|
" dev_capab=0x%x"
|
|
" group_capab=0x%x"
|
|
" adv_id=%x asp_svc=%s%s",
|
|
MAC2STR(addr),
|
|
MAC2STR(info->p2p_device_addr),
|
|
wps_dev_type_bin2str(
|
|
info->pri_dev_type,
|
|
devtype, sizeof(devtype)),
|
|
info->device_name, methods,
|
|
info->dev_capab, info->group_capab,
|
|
id, str,
|
|
info->vendor_elems ?
|
|
" vendor_elems=1" : "");
|
|
}
|
|
goto done;
|
|
}
|
|
|
|
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR
|
|
" p2p_dev_addr=" MACSTR
|
|
" pri_dev_type=%s name='%s' config_methods=0x%x "
|
|
"dev_capab=0x%x group_capab=0x%x%s%s%s new=%d",
|
|
MAC2STR(addr), MAC2STR(info->p2p_device_addr),
|
|
wps_dev_type_bin2str(info->pri_dev_type, devtype,
|
|
sizeof(devtype)),
|
|
info->device_name, info->config_methods,
|
|
info->dev_capab, info->group_capab,
|
|
wfd_dev_info_hex ? " wfd_dev_info=0x" : "",
|
|
wfd_dev_info_hex ? wfd_dev_info_hex : "",
|
|
info->vendor_elems ? " vendor_elems=1" : "",
|
|
new_device);
|
|
|
|
done:
|
|
os_free(wfd_dev_info_hex);
|
|
#endif /* CONFIG_NO_STDOUT_DEBUG */
|
|
|
|
wpas_notify_p2p_device_found(ctx, info->p2p_device_addr, new_device);
|
|
}
|
|
|
|
|
|
static void wpas_dev_lost(void *ctx, const u8 *dev_addr)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
|
|
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_LOST
|
|
"p2p_dev_addr=" MACSTR, MAC2STR(dev_addr));
|
|
|
|
wpas_notify_p2p_device_lost(wpa_s, dev_addr);
|
|
}
|
|
|
|
|
|
static void wpas_find_stopped(void *ctx)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
|
|
if (wpa_s->p2p_scan_work && wpas_abort_ongoing_scan(wpa_s) < 0)
|
|
wpa_printf(MSG_DEBUG, "P2P: Abort ongoing scan failed");
|
|
|
|
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_FIND_STOPPED);
|
|
wpas_notify_p2p_find_stopped(wpa_s);
|
|
}
|
|
|
|
|
|
struct wpas_p2p_listen_work {
|
|
unsigned int freq;
|
|
unsigned int duration;
|
|
struct wpabuf *probe_resp_ie;
|
|
};
|
|
|
|
|
|
static void wpas_p2p_listen_work_free(struct wpas_p2p_listen_work *lwork)
|
|
{
|
|
if (lwork == NULL)
|
|
return;
|
|
wpabuf_free(lwork->probe_resp_ie);
|
|
os_free(lwork);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_listen_work_done(struct wpa_supplicant *wpa_s)
|
|
{
|
|
struct wpas_p2p_listen_work *lwork;
|
|
|
|
if (!wpa_s->p2p_listen_work)
|
|
return;
|
|
|
|
lwork = wpa_s->p2p_listen_work->ctx;
|
|
wpas_p2p_listen_work_free(lwork);
|
|
radio_work_done(wpa_s->p2p_listen_work);
|
|
wpa_s->p2p_listen_work = NULL;
|
|
}
|
|
|
|
|
|
static void wpas_start_listen_cb(struct wpa_radio_work *work, int deinit)
|
|
{
|
|
struct wpa_supplicant *wpa_s = work->wpa_s;
|
|
struct wpas_p2p_listen_work *lwork = work->ctx;
|
|
unsigned int duration;
|
|
|
|
if (deinit) {
|
|
if (work->started) {
|
|
wpa_s->p2p_listen_work = NULL;
|
|
wpas_stop_listen(wpa_s);
|
|
}
|
|
wpas_p2p_listen_work_free(lwork);
|
|
return;
|
|
}
|
|
|
|
wpa_s->p2p_listen_work = work;
|
|
|
|
wpa_drv_set_ap_wps_ie(wpa_s, NULL, lwork->probe_resp_ie, NULL);
|
|
|
|
if (wpa_drv_probe_req_report(wpa_s, 1) < 0) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver to "
|
|
"report received Probe Request frames");
|
|
wpas_p2p_listen_work_done(wpa_s);
|
|
return;
|
|
}
|
|
|
|
wpa_s->pending_listen_freq = lwork->freq;
|
|
wpa_s->pending_listen_duration = lwork->duration;
|
|
|
|
duration = lwork->duration;
|
|
#ifdef CONFIG_TESTING_OPTIONS
|
|
if (wpa_s->extra_roc_dur) {
|
|
wpa_printf(MSG_DEBUG, "TESTING: Increase ROC duration %u -> %u",
|
|
duration, duration + wpa_s->extra_roc_dur);
|
|
duration += wpa_s->extra_roc_dur;
|
|
}
|
|
#endif /* CONFIG_TESTING_OPTIONS */
|
|
|
|
if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, duration) < 0) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver "
|
|
"to remain on channel (%u MHz) for Listen "
|
|
"state", lwork->freq);
|
|
wpas_p2p_listen_work_done(wpa_s);
|
|
wpa_s->pending_listen_freq = 0;
|
|
return;
|
|
}
|
|
wpa_s->off_channel_freq = 0;
|
|
wpa_s->roc_waiting_drv_freq = lwork->freq;
|
|
}
|
|
|
|
|
|
static int wpas_start_listen(void *ctx, unsigned int freq,
|
|
unsigned int duration,
|
|
const struct wpabuf *probe_resp_ie)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
struct wpas_p2p_listen_work *lwork;
|
|
|
|
if (wpa_s->p2p_listen_work) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Reject start_listen since p2p_listen_work already exists");
|
|
return -1;
|
|
}
|
|
|
|
lwork = os_zalloc(sizeof(*lwork));
|
|
if (lwork == NULL)
|
|
return -1;
|
|
lwork->freq = freq;
|
|
lwork->duration = duration;
|
|
if (probe_resp_ie) {
|
|
lwork->probe_resp_ie = wpabuf_dup(probe_resp_ie);
|
|
if (lwork->probe_resp_ie == NULL) {
|
|
wpas_p2p_listen_work_free(lwork);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (radio_add_work(wpa_s, freq, "p2p-listen", 0, wpas_start_listen_cb,
|
|
lwork) < 0) {
|
|
wpas_p2p_listen_work_free(lwork);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void wpas_stop_listen(void *ctx)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
|
|
wpa_drv_cancel_remain_on_channel(wpa_s);
|
|
wpa_s->off_channel_freq = 0;
|
|
wpa_s->roc_waiting_drv_freq = 0;
|
|
}
|
|
wpa_drv_set_ap_wps_ie(wpa_s, NULL, NULL, NULL);
|
|
|
|
/*
|
|
* Don't cancel Probe Request RX reporting for a connected P2P Client
|
|
* handling Probe Request frames.
|
|
*/
|
|
if (!wpa_s->p2p_cli_probe)
|
|
wpa_drv_probe_req_report(wpa_s, 0);
|
|
|
|
wpas_p2p_listen_work_done(wpa_s);
|
|
}
|
|
|
|
|
|
static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf,
|
|
unsigned int freq)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1,
|
|
freq, 0);
|
|
}
|
|
|
|
|
|
static void wpas_prov_disc_local_display(struct wpa_supplicant *wpa_s,
|
|
const u8 *peer, const char *params,
|
|
unsigned int generated_pin)
|
|
{
|
|
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_SHOW_PIN MACSTR
|
|
" %08d%s", MAC2STR(peer), generated_pin, params);
|
|
}
|
|
|
|
|
|
static void wpas_prov_disc_local_keypad(struct wpa_supplicant *wpa_s,
|
|
const u8 *peer, const char *params)
|
|
{
|
|
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_ENTER_PIN MACSTR
|
|
"%s", MAC2STR(peer), params);
|
|
}
|
|
|
|
|
|
static void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
|
|
const u8 *dev_addr, const u8 *pri_dev_type,
|
|
const char *dev_name, u16 supp_config_methods,
|
|
u8 dev_capab, u8 group_capab, const u8 *group_id,
|
|
size_t group_id_len)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
char devtype[WPS_DEV_TYPE_BUFSIZE];
|
|
char params[300];
|
|
u8 empty_dev_type[8];
|
|
unsigned int generated_pin = 0;
|
|
struct wpa_supplicant *group = NULL;
|
|
int res;
|
|
|
|
if (group_id) {
|
|
for (group = wpa_s->global->ifaces; group; group = group->next)
|
|
{
|
|
struct wpa_ssid *s = group->current_ssid;
|
|
if (s != NULL &&
|
|
s->mode == WPAS_MODE_P2P_GO &&
|
|
group_id_len - ETH_ALEN == s->ssid_len &&
|
|
os_memcmp(group_id + ETH_ALEN, s->ssid,
|
|
s->ssid_len) == 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pri_dev_type == NULL) {
|
|
os_memset(empty_dev_type, 0, sizeof(empty_dev_type));
|
|
pri_dev_type = empty_dev_type;
|
|
}
|
|
res = os_snprintf(params, sizeof(params), " p2p_dev_addr=" MACSTR
|
|
" pri_dev_type=%s name='%s' config_methods=0x%x "
|
|
"dev_capab=0x%x group_capab=0x%x%s%s",
|
|
MAC2STR(dev_addr),
|
|
wps_dev_type_bin2str(pri_dev_type, devtype,
|
|
sizeof(devtype)),
|
|
dev_name, supp_config_methods, dev_capab, group_capab,
|
|
group ? " group=" : "",
|
|
group ? group->ifname : "");
|
|
if (os_snprintf_error(sizeof(params), res))
|
|
wpa_printf(MSG_DEBUG, "P2P: PD Request event truncated");
|
|
params[sizeof(params) - 1] = '\0';
|
|
|
|
if (config_methods & WPS_CONFIG_DISPLAY) {
|
|
if (wps_generate_pin(&generated_pin) < 0) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Could not generate PIN");
|
|
wpas_notify_p2p_provision_discovery(
|
|
wpa_s, peer, 0 /* response */,
|
|
P2P_PROV_DISC_INFO_UNAVAILABLE, 0, 0);
|
|
return;
|
|
}
|
|
wpas_prov_disc_local_display(wpa_s, peer, params,
|
|
generated_pin);
|
|
} else if (config_methods & WPS_CONFIG_KEYPAD)
|
|
wpas_prov_disc_local_keypad(wpa_s, peer, params);
|
|
else if (config_methods & WPS_CONFIG_PUSHBUTTON)
|
|
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_REQ
|
|
MACSTR "%s", MAC2STR(peer), params);
|
|
|
|
wpas_notify_p2p_provision_discovery(wpa_s, peer, 1 /* request */,
|
|
P2P_PROV_DISC_SUCCESS,
|
|
config_methods, generated_pin);
|
|
}
|
|
|
|
|
|
static void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
unsigned int generated_pin = 0;
|
|
char params[20];
|
|
|
|
if (wpa_s->pending_pd_before_join &&
|
|
(os_memcmp(peer, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
|
|
os_memcmp(peer, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) {
|
|
wpa_s->pending_pd_before_join = 0;
|
|
wpa_printf(MSG_DEBUG, "P2P: Starting pending "
|
|
"join-existing-group operation");
|
|
wpas_p2p_join_start(wpa_s, 0, NULL, 0);
|
|
return;
|
|
}
|
|
|
|
if (wpa_s->pending_pd_use == AUTO_PD_JOIN ||
|
|
wpa_s->pending_pd_use == AUTO_PD_GO_NEG) {
|
|
int res;
|
|
|
|
res = os_snprintf(params, sizeof(params), " peer_go=%d",
|
|
wpa_s->pending_pd_use == AUTO_PD_JOIN);
|
|
if (os_snprintf_error(sizeof(params), res))
|
|
params[sizeof(params) - 1] = '\0';
|
|
} else
|
|
params[0] = '\0';
|
|
|
|
if (config_methods & WPS_CONFIG_DISPLAY)
|
|
wpas_prov_disc_local_keypad(wpa_s, peer, params);
|
|
else if (config_methods & WPS_CONFIG_KEYPAD) {
|
|
if (wps_generate_pin(&generated_pin) < 0) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Could not generate PIN");
|
|
wpas_notify_p2p_provision_discovery(
|
|
wpa_s, peer, 0 /* response */,
|
|
P2P_PROV_DISC_INFO_UNAVAILABLE, 0, 0);
|
|
return;
|
|
}
|
|
wpas_prov_disc_local_display(wpa_s, peer, params,
|
|
generated_pin);
|
|
} else if (config_methods & WPS_CONFIG_PUSHBUTTON)
|
|
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP
|
|
MACSTR "%s", MAC2STR(peer), params);
|
|
|
|
wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
|
|
P2P_PROV_DISC_SUCCESS,
|
|
config_methods, generated_pin);
|
|
}
|
|
|
|
|
|
static void wpas_prov_disc_fail(void *ctx, const u8 *peer,
|
|
enum p2p_prov_disc_status status,
|
|
u32 adv_id, const u8 *adv_mac,
|
|
const char *deferred_session_resp)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
|
|
if (wpa_s->p2p_fallback_to_go_neg) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: PD for p2p_connect-auto "
|
|
"failed - fall back to GO Negotiation");
|
|
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
|
|
P2P_EVENT_FALLBACK_TO_GO_NEG
|
|
"reason=PD-failed");
|
|
wpas_p2p_fallback_to_go_neg(wpa_s, 0);
|
|
return;
|
|
}
|
|
|
|
if (status == P2P_PROV_DISC_TIMEOUT_JOIN) {
|
|
wpa_s->pending_pd_before_join = 0;
|
|
wpa_printf(MSG_DEBUG, "P2P: Starting pending "
|
|
"join-existing-group operation (no ACK for PD "
|
|
"Req attempts)");
|
|
wpas_p2p_join_start(wpa_s, 0, NULL, 0);
|
|
return;
|
|
}
|
|
|
|
if (adv_id && adv_mac && deferred_session_resp) {
|
|
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
|
|
" p2p_dev_addr=" MACSTR " status=%d adv_id=%x"
|
|
" deferred_session_resp='%s'",
|
|
MAC2STR(peer), status, adv_id,
|
|
deferred_session_resp);
|
|
} else if (adv_id && adv_mac) {
|
|
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
|
|
" p2p_dev_addr=" MACSTR " status=%d adv_id=%x",
|
|
MAC2STR(peer), status, adv_id);
|
|
} else {
|
|
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
|
|
" p2p_dev_addr=" MACSTR " status=%d",
|
|
MAC2STR(peer), status);
|
|
}
|
|
|
|
wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
|
|
status, 0, 0);
|
|
}
|
|
|
|
|
|
static int freq_included(struct wpa_supplicant *wpa_s,
|
|
const struct p2p_channels *channels,
|
|
unsigned int freq)
|
|
{
|
|
if ((channels == NULL || p2p_channels_includes_freq(channels, freq)) &&
|
|
wpas_p2p_go_is_peer_freq(wpa_s, freq))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void wpas_p2p_go_update_common_freqs(struct wpa_supplicant *wpa_s)
|
|
{
|
|
unsigned int num = P2P_MAX_CHANNELS;
|
|
int *common_freqs;
|
|
int ret;
|
|
|
|
p2p_go_dump_common_freqs(wpa_s);
|
|
common_freqs = os_calloc(num, sizeof(int));
|
|
if (!common_freqs)
|
|
return;
|
|
|
|
ret = p2p_group_get_common_freqs(wpa_s->p2p_group, common_freqs, &num);
|
|
if (ret < 0) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Failed to get group common freqs");
|
|
os_free(common_freqs);
|
|
return;
|
|
}
|
|
|
|
os_free(wpa_s->p2p_group_common_freqs);
|
|
wpa_s->p2p_group_common_freqs = common_freqs;
|
|
wpa_s->p2p_group_common_freqs_num = num;
|
|
p2p_go_dump_common_freqs(wpa_s);
|
|
}
|
|
|
|
|
|
/*
|
|
* Check if the given frequency is one of the possible operating frequencies
|
|
* set after the completion of the GO Negotiation.
|
|
*/
|
|
static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq)
|
|
{
|
|
unsigned int i;
|
|
|
|
p2p_go_dump_common_freqs(wpa_s);
|
|
|
|
/* assume no restrictions */
|
|
if (!wpa_s->p2p_group_common_freqs_num)
|
|
return 1;
|
|
|
|
for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
|
|
if (wpa_s->p2p_group_common_freqs[i] == freq)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpas_sta_check_ecsa(struct hostapd_data *hapd,
|
|
struct sta_info *sta, void *ctx)
|
|
{
|
|
int *ecsa_support = ctx;
|
|
|
|
*ecsa_support &= sta->ecsa_supported;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Check if all the peers support eCSA */
|
|
static int wpas_p2p_go_clients_support_ecsa(struct wpa_supplicant *wpa_s)
|
|
{
|
|
int ecsa_support = 1;
|
|
|
|
ap_for_each_sta(wpa_s->ap_iface->bss[0], wpas_sta_check_ecsa,
|
|
&ecsa_support);
|
|
|
|
return ecsa_support;
|
|
}
|
|
|
|
|
|
/**
|
|
* Pick the best frequency to use from all the currently used frequencies.
|
|
*/
|
|
static int wpas_p2p_pick_best_used_freq(struct wpa_supplicant *wpa_s,
|
|
struct wpa_used_freq_data *freqs,
|
|
unsigned int num)
|
|
{
|
|
unsigned int i, c;
|
|
|
|
/* find a candidate freq that is supported by P2P */
|
|
for (c = 0; c < num; c++)
|
|
if (p2p_supported_freq(wpa_s->global->p2p, freqs[c].freq))
|
|
break;
|
|
|
|
if (c == num)
|
|
return 0;
|
|
|
|
/* once we have a candidate, try to find a 'better' one */
|
|
for (i = c + 1; i < num; i++) {
|
|
if (!p2p_supported_freq(wpa_s->global->p2p, freqs[i].freq))
|
|
continue;
|
|
|
|
/*
|
|
* 1. Infrastructure station interfaces have higher preference.
|
|
* 2. P2P Clients have higher preference.
|
|
* 3. All others.
|
|
*/
|
|
if (freqs[i].flags & WPA_FREQ_USED_BY_INFRA_STATION) {
|
|
c = i;
|
|
break;
|
|
}
|
|
|
|
if ((freqs[i].flags & WPA_FREQ_USED_BY_P2P_CLIENT))
|
|
c = i;
|
|
}
|
|
return freqs[c].freq;
|
|
}
|
|
|
|
|
|
static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid,
|
|
const u8 *go_dev_addr, const u8 *ssid,
|
|
size_t ssid_len, int *go, u8 *group_bssid,
|
|
int *force_freq, int persistent_group,
|
|
const struct p2p_channels *channels,
|
|
int dev_pw_id)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
struct wpa_ssid *s;
|
|
struct wpa_used_freq_data *freqs;
|
|
struct wpa_supplicant *grp;
|
|
int best_freq;
|
|
|
|
if (!persistent_group) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
|
|
" to join an active group (SSID: %s)",
|
|
MAC2STR(sa), wpa_ssid_txt(ssid, ssid_len));
|
|
if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) &&
|
|
(os_memcmp(go_dev_addr, wpa_s->p2p_auth_invite, ETH_ALEN)
|
|
== 0 ||
|
|
os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0)) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Accept previously "
|
|
"authorized invitation");
|
|
goto accept_inv;
|
|
}
|
|
|
|
#ifdef CONFIG_WPS_NFC
|
|
if (dev_pw_id >= 0 && wpa_s->p2p_nfc_tag_enabled &&
|
|
dev_pw_id == wpa_s->p2p_oob_dev_pw_id) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Accept invitation based on local enabled NFC Tag");
|
|
wpa_s->p2p_wps_method = WPS_NFC;
|
|
wpa_s->pending_join_wps_method = WPS_NFC;
|
|
os_memcpy(wpa_s->pending_join_dev_addr,
|
|
go_dev_addr, ETH_ALEN);
|
|
os_memcpy(wpa_s->pending_join_iface_addr,
|
|
bssid, ETH_ALEN);
|
|
goto accept_inv;
|
|
}
|
|
#endif /* CONFIG_WPS_NFC */
|
|
|
|
/*
|
|
* Do not accept the invitation automatically; notify user and
|
|
* request approval.
|
|
*/
|
|
return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
|
|
}
|
|
|
|
grp = wpas_get_p2p_group(wpa_s, ssid, ssid_len, go);
|
|
if (grp) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Accept invitation to already "
|
|
"running persistent group");
|
|
if (*go)
|
|
os_memcpy(group_bssid, grp->own_addr, ETH_ALEN);
|
|
goto accept_inv;
|
|
}
|
|
|
|
if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) &&
|
|
os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Accept previously initiated "
|
|
"invitation to re-invoke a persistent group");
|
|
os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
|
|
} else if (!wpa_s->conf->persistent_reconnect)
|
|
return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
|
|
|
|
for (s = wpa_s->conf->ssid; s; s = s->next) {
|
|
if (s->disabled == 2 &&
|
|
os_memcmp(s->bssid, go_dev_addr, ETH_ALEN) == 0 &&
|
|
s->ssid_len == ssid_len &&
|
|
os_memcmp(ssid, s->ssid, ssid_len) == 0)
|
|
break;
|
|
}
|
|
|
|
if (!s) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
|
|
" requested reinvocation of an unknown group",
|
|
MAC2STR(sa));
|
|
return P2P_SC_FAIL_UNKNOWN_GROUP;
|
|
}
|
|
|
|
if (s->mode == WPAS_MODE_P2P_GO && !wpas_p2p_create_iface(wpa_s)) {
|
|
*go = 1;
|
|
if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
|
|
wpa_printf(MSG_DEBUG, "P2P: The only available "
|
|
"interface is already in use - reject "
|
|
"invitation");
|
|
return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
|
|
}
|
|
if (wpa_s->p2p_mgmt)
|
|
os_memcpy(group_bssid, wpa_s->parent->own_addr,
|
|
ETH_ALEN);
|
|
else
|
|
os_memcpy(group_bssid, wpa_s->own_addr, ETH_ALEN);
|
|
} else if (s->mode == WPAS_MODE_P2P_GO) {
|
|
*go = 1;
|
|
if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO) < 0)
|
|
{
|
|
wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new "
|
|
"interface address for the group");
|
|
return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
|
|
}
|
|
os_memcpy(group_bssid, wpa_s->pending_interface_addr,
|
|
ETH_ALEN);
|
|
}
|
|
|
|
accept_inv:
|
|
wpas_p2p_set_own_freq_preference(wpa_s, 0);
|
|
|
|
best_freq = 0;
|
|
freqs = os_calloc(wpa_s->num_multichan_concurrent,
|
|
sizeof(struct wpa_used_freq_data));
|
|
if (freqs) {
|
|
int num_channels = wpa_s->num_multichan_concurrent;
|
|
int num = wpas_p2p_valid_oper_freqs(wpa_s, freqs, num_channels);
|
|
best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
|
|
os_free(freqs);
|
|
}
|
|
|
|
/* Get one of the frequencies currently in use */
|
|
if (best_freq > 0) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Trying to prefer a channel already used by one of the interfaces");
|
|
wpas_p2p_set_own_freq_preference(wpa_s, best_freq);
|
|
|
|
if (wpa_s->num_multichan_concurrent < 2 ||
|
|
wpas_p2p_num_unused_channels(wpa_s) < 1) {
|
|
wpa_printf(MSG_DEBUG, "P2P: No extra channels available - trying to force channel to match a channel already used by one of the interfaces");
|
|
*force_freq = best_freq;
|
|
}
|
|
}
|
|
|
|
if (*force_freq > 0 && wpa_s->num_multichan_concurrent > 1 &&
|
|
wpas_p2p_num_unused_channels(wpa_s) > 0) {
|
|
if (*go == 0) {
|
|
/* We are the client */
|
|
wpa_printf(MSG_DEBUG, "P2P: Peer was found to be "
|
|
"running a GO but we are capable of MCC, "
|
|
"figure out the best channel to use");
|
|
*force_freq = 0;
|
|
} else if (!freq_included(wpa_s, channels, *force_freq)) {
|
|
/* We are the GO, and *force_freq is not in the
|
|
* intersection */
|
|
wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not "
|
|
"in intersection but we are capable of MCC, "
|
|
"figure out the best channel to use",
|
|
*force_freq);
|
|
*force_freq = 0;
|
|
}
|
|
}
|
|
|
|
return P2P_SC_SUCCESS;
|
|
}
|
|
|
|
|
|
static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid,
|
|
const u8 *ssid, size_t ssid_len,
|
|
const u8 *go_dev_addr, u8 status,
|
|
int op_freq)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
struct wpa_ssid *s;
|
|
|
|
for (s = wpa_s->conf->ssid; s; s = s->next) {
|
|
if (s->disabled == 2 &&
|
|
s->ssid_len == ssid_len &&
|
|
os_memcmp(ssid, s->ssid, ssid_len) == 0)
|
|
break;
|
|
}
|
|
|
|
if (status == P2P_SC_SUCCESS) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR
|
|
" was accepted; op_freq=%d MHz, SSID=%s",
|
|
MAC2STR(sa), op_freq, wpa_ssid_txt(ssid, ssid_len));
|
|
if (s) {
|
|
int go = s->mode == WPAS_MODE_P2P_GO;
|
|
if (go) {
|
|
wpa_msg_global(wpa_s, MSG_INFO,
|
|
P2P_EVENT_INVITATION_ACCEPTED
|
|
"sa=" MACSTR
|
|
" persistent=%d freq=%d",
|
|
MAC2STR(sa), s->id, op_freq);
|
|
} else {
|
|
wpa_msg_global(wpa_s, MSG_INFO,
|
|
P2P_EVENT_INVITATION_ACCEPTED
|
|
"sa=" MACSTR
|
|
" persistent=%d",
|
|
MAC2STR(sa), s->id);
|
|
}
|
|
wpas_p2p_group_add_persistent(
|
|
wpa_s, s, go, 0, op_freq, 0,
|
|
wpa_s->conf->p2p_go_ht40,
|
|
wpa_s->conf->p2p_go_vht,
|
|
0,
|
|
wpa_s->conf->p2p_go_he,
|
|
wpa_s->conf->p2p_go_edmg, NULL,
|
|
go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0,
|
|
1, is_p2p_allow_6ghz(wpa_s->global->p2p));
|
|
} else if (bssid) {
|
|
wpa_s->user_initiated_pd = 0;
|
|
wpa_msg_global(wpa_s, MSG_INFO,
|
|
P2P_EVENT_INVITATION_ACCEPTED
|
|
"sa=" MACSTR " go_dev_addr=" MACSTR
|
|
" bssid=" MACSTR " unknown-network",
|
|
MAC2STR(sa), MAC2STR(go_dev_addr),
|
|
MAC2STR(bssid));
|
|
wpas_p2p_join(wpa_s, bssid, go_dev_addr,
|
|
wpa_s->p2p_wps_method, 0, op_freq,
|
|
ssid, ssid_len);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR
|
|
" was rejected (status %u)", MAC2STR(sa), status);
|
|
return;
|
|
}
|
|
|
|
if (!s) {
|
|
if (bssid) {
|
|
wpa_msg_global(wpa_s, MSG_INFO,
|
|
P2P_EVENT_INVITATION_RECEIVED
|
|
"sa=" MACSTR " go_dev_addr=" MACSTR
|
|
" bssid=" MACSTR " unknown-network",
|
|
MAC2STR(sa), MAC2STR(go_dev_addr),
|
|
MAC2STR(bssid));
|
|
} else {
|
|
wpa_msg_global(wpa_s, MSG_INFO,
|
|
P2P_EVENT_INVITATION_RECEIVED
|
|
"sa=" MACSTR " go_dev_addr=" MACSTR
|
|
" unknown-network",
|
|
MAC2STR(sa), MAC2STR(go_dev_addr));
|
|
}
|
|
wpas_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr,
|
|
bssid, 0, op_freq);
|
|
return;
|
|
}
|
|
|
|
if (s->mode == WPAS_MODE_P2P_GO && op_freq) {
|
|
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED
|
|
"sa=" MACSTR " persistent=%d freq=%d",
|
|
MAC2STR(sa), s->id, op_freq);
|
|
} else {
|
|
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED
|
|
"sa=" MACSTR " persistent=%d",
|
|
MAC2STR(sa), s->id);
|
|
}
|
|
wpas_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid,
|
|
s->id, op_freq);
|
|
}
|
|
|
|
|
|
static void wpas_remove_persistent_peer(struct wpa_supplicant *wpa_s,
|
|
struct wpa_ssid *ssid,
|
|
const u8 *peer, int inv)
|
|
{
|
|
size_t i;
|
|
struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s;
|
|
|
|
if (ssid == NULL)
|
|
return;
|
|
|
|
for (i = 0; ssid->p2p_client_list && i < ssid->num_p2p_clients; i++) {
|
|
if (os_memcmp(ssid->p2p_client_list + i * 2 * ETH_ALEN, peer,
|
|
ETH_ALEN) == 0)
|
|
break;
|
|
}
|
|
if (i >= ssid->num_p2p_clients || !ssid->p2p_client_list) {
|
|
if (ssid->mode != WPAS_MODE_P2P_GO &&
|
|
os_memcmp(ssid->bssid, peer, ETH_ALEN) == 0) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Remove persistent group %d "
|
|
"due to invitation result", ssid->id);
|
|
wpas_notify_network_removed(wpa_s, ssid);
|
|
wpa_config_remove_network(wpa_s->conf, ssid->id);
|
|
return;
|
|
}
|
|
return; /* Peer not found in client list */
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Remove peer " MACSTR " from persistent "
|
|
"group %d client list%s",
|
|
MAC2STR(peer), ssid->id,
|
|
inv ? " due to invitation result" : "");
|
|
os_memmove(ssid->p2p_client_list + i * 2 * ETH_ALEN,
|
|
ssid->p2p_client_list + (i + 1) * 2 * ETH_ALEN,
|
|
(ssid->num_p2p_clients - i - 1) * 2 * ETH_ALEN);
|
|
ssid->num_p2p_clients--;
|
|
if (p2p_wpa_s->conf->update_config &&
|
|
wpa_config_write(p2p_wpa_s->confname, p2p_wpa_s->conf))
|
|
wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
|
|
}
|
|
|
|
|
|
static void wpas_remove_persistent_client(struct wpa_supplicant *wpa_s,
|
|
const u8 *peer)
|
|
{
|
|
struct wpa_ssid *ssid;
|
|
|
|
wpa_s = wpa_s->global->p2p_invite_group;
|
|
if (wpa_s == NULL)
|
|
return; /* No known invitation group */
|
|
ssid = wpa_s->current_ssid;
|
|
if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
|
|
!ssid->p2p_persistent_group)
|
|
return; /* Not operating as a GO in persistent group */
|
|
ssid = wpas_p2p_get_persistent(wpa_s->p2pdev, peer,
|
|
ssid->ssid, ssid->ssid_len);
|
|
wpas_remove_persistent_peer(wpa_s, ssid, peer, 1);
|
|
}
|
|
|
|
|
|
static void wpas_invitation_result(void *ctx, int status, const u8 *bssid,
|
|
const struct p2p_channels *channels,
|
|
const u8 *peer, int neg_freq,
|
|
int peer_oper_freq)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
struct wpa_ssid *ssid;
|
|
int freq;
|
|
|
|
if (bssid) {
|
|
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
|
|
"status=%d " MACSTR,
|
|
status, MAC2STR(bssid));
|
|
} else {
|
|
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
|
|
"status=%d ", status);
|
|
}
|
|
wpas_notify_p2p_invitation_result(wpa_s, status, bssid);
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Invitation result - status=%d peer=" MACSTR,
|
|
status, MAC2STR(peer));
|
|
if (wpa_s->pending_invite_ssid_id == -1) {
|
|
struct wpa_supplicant *group_if =
|
|
wpa_s->global->p2p_invite_group;
|
|
|
|
if (status == P2P_SC_FAIL_UNKNOWN_GROUP)
|
|
wpas_remove_persistent_client(wpa_s, peer);
|
|
|
|
/*
|
|
* Invitation to an active group. If this is successful and we
|
|
* are the GO, set the client wait to postpone some concurrent
|
|
* operations and to allow provisioning and connection to happen
|
|
* more quickly.
|
|
*/
|
|
if (status == P2P_SC_SUCCESS &&
|
|
group_if && group_if->current_ssid &&
|
|
group_if->current_ssid->mode == WPAS_MODE_P2P_GO) {
|
|
os_get_reltime(&wpa_s->global->p2p_go_wait_client);
|
|
#ifdef CONFIG_TESTING_OPTIONS
|
|
if (group_if->p2p_go_csa_on_inv) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"Testing: force P2P GO CSA after invitation");
|
|
eloop_cancel_timeout(
|
|
wpas_p2p_reconsider_moving_go,
|
|
wpa_s, NULL);
|
|
eloop_register_timeout(
|
|
0, 50000,
|
|
wpas_p2p_reconsider_moving_go,
|
|
wpa_s, NULL);
|
|
}
|
|
#endif /* CONFIG_TESTING_OPTIONS */
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Waiting for peer to start another "
|
|
"invitation exchange to indicate readiness for "
|
|
"re-invocation");
|
|
}
|
|
|
|
if (status != P2P_SC_SUCCESS) {
|
|
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, 1);
|
|
}
|
|
wpas_p2p_remove_pending_group_interface(wpa_s);
|
|
return;
|
|
}
|
|
|
|
ssid = wpa_config_get_network(wpa_s->conf,
|
|
wpa_s->pending_invite_ssid_id);
|
|
if (ssid == NULL) {
|
|
wpa_printf(MSG_ERROR, "P2P: Could not find persistent group "
|
|
"data matching with invitation");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* The peer could have missed our ctrl::ack frame for Invitation
|
|
* Response and continue retransmitting the frame. To reduce the
|
|
* likelihood of the peer not getting successful TX status for the
|
|
* Invitation Response frame, wait a short time here before starting
|
|
* the persistent group so that we will remain on the current channel to
|
|
* acknowledge any possible retransmission from the peer.
|
|
*/
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: 50 ms wait on current channel before "
|
|
"starting persistent group");
|
|
os_sleep(0, 50000);
|
|
|
|
if (neg_freq > 0 && ssid->mode == WPAS_MODE_P2P_GO &&
|
|
freq_included(wpa_s, channels, neg_freq))
|
|
freq = neg_freq;
|
|
else if (peer_oper_freq > 0 && ssid->mode != WPAS_MODE_P2P_GO &&
|
|
freq_included(wpa_s, channels, peer_oper_freq))
|
|
freq = peer_oper_freq;
|
|
else
|
|
freq = 0;
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Persistent group invitation success - op_freq=%d MHz SSID=%s",
|
|
freq, wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
|
|
wpas_p2p_group_add_persistent(wpa_s, ssid,
|
|
ssid->mode == WPAS_MODE_P2P_GO,
|
|
wpa_s->p2p_persistent_go_freq,
|
|
freq,
|
|
wpa_s->p2p_go_vht_center_freq2,
|
|
wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht,
|
|
wpa_s->p2p_go_max_oper_chwidth,
|
|
wpa_s->p2p_go_he,
|
|
wpa_s->p2p_go_edmg,
|
|
channels,
|
|
ssid->mode == WPAS_MODE_P2P_GO ?
|
|
P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
|
|
0, 1,
|
|
is_p2p_allow_6ghz(wpa_s->global->p2p));
|
|
}
|
|
|
|
|
|
static int wpas_p2p_disallowed_freq(struct wpa_global *global,
|
|
unsigned int freq)
|
|
{
|
|
if (freq_range_list_includes(&global->p2p_go_avoid_freq, freq))
|
|
return 1;
|
|
return freq_range_list_includes(&global->p2p_disallow_freq, freq);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_add_chan(struct p2p_reg_class *reg, u8 chan)
|
|
{
|
|
reg->channel[reg->channels] = chan;
|
|
reg->channels++;
|
|
}
|
|
|
|
|
|
static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s,
|
|
struct p2p_channels *chan,
|
|
struct p2p_channels *cli_chan)
|
|
{
|
|
int i, cla = 0;
|
|
|
|
wpa_s->global->p2p_24ghz_social_channels = 1;
|
|
|
|
os_memset(cli_chan, 0, sizeof(*cli_chan));
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for 2.4 GHz "
|
|
"band");
|
|
|
|
/* Operating class 81 - 2.4 GHz band channels 1..13 */
|
|
chan->reg_class[cla].reg_class = 81;
|
|
chan->reg_class[cla].channels = 0;
|
|
for (i = 0; i < 11; i++) {
|
|
if (!wpas_p2p_disallowed_freq(wpa_s->global, 2412 + i * 5))
|
|
wpas_p2p_add_chan(&chan->reg_class[cla], i + 1);
|
|
}
|
|
if (chan->reg_class[cla].channels)
|
|
cla++;
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for lower 5 GHz "
|
|
"band");
|
|
|
|
/* Operating class 115 - 5 GHz, channels 36-48 */
|
|
chan->reg_class[cla].reg_class = 115;
|
|
chan->reg_class[cla].channels = 0;
|
|
if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 36 * 5))
|
|
wpas_p2p_add_chan(&chan->reg_class[cla], 36);
|
|
if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 40 * 5))
|
|
wpas_p2p_add_chan(&chan->reg_class[cla], 40);
|
|
if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 44 * 5))
|
|
wpas_p2p_add_chan(&chan->reg_class[cla], 44);
|
|
if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 48 * 5))
|
|
wpas_p2p_add_chan(&chan->reg_class[cla], 48);
|
|
if (chan->reg_class[cla].channels)
|
|
cla++;
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for higher 5 GHz "
|
|
"band");
|
|
|
|
/* Operating class 124 - 5 GHz, channels 149,153,157,161 */
|
|
chan->reg_class[cla].reg_class = 124;
|
|
chan->reg_class[cla].channels = 0;
|
|
if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 149 * 5))
|
|
wpas_p2p_add_chan(&chan->reg_class[cla], 149);
|
|
if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 153 * 5))
|
|
wpas_p2p_add_chan(&chan->reg_class[cla], 153);
|
|
if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 156 * 5))
|
|
wpas_p2p_add_chan(&chan->reg_class[cla], 157);
|
|
if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 161 * 5))
|
|
wpas_p2p_add_chan(&chan->reg_class[cla], 161);
|
|
if (chan->reg_class[cla].channels)
|
|
cla++;
|
|
|
|
chan->reg_classes = cla;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static enum chan_allowed has_channel(struct wpa_global *global,
|
|
struct hostapd_hw_modes *mode, u8 op_class,
|
|
u8 chan, int *flags)
|
|
{
|
|
int i;
|
|
unsigned int freq;
|
|
|
|
freq = ieee80211_chan_to_freq(NULL, op_class, chan);
|
|
if (wpas_p2p_disallowed_freq(global, freq))
|
|
return NOT_ALLOWED;
|
|
|
|
for (i = 0; i < mode->num_channels; i++) {
|
|
if ((unsigned int) mode->channels[i].freq == freq) {
|
|
if (flags)
|
|
*flags = mode->channels[i].flag;
|
|
if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)
|
|
return NOT_ALLOWED;
|
|
if (mode->channels[i].flag & HOSTAPD_CHAN_NO_IR)
|
|
return NO_IR;
|
|
if (mode->channels[i].flag & HOSTAPD_CHAN_RADAR)
|
|
return RADAR;
|
|
return ALLOWED;
|
|
}
|
|
}
|
|
|
|
return NOT_ALLOWED;
|
|
}
|
|
|
|
|
|
static int wpas_p2p_get_center_80mhz(struct wpa_supplicant *wpa_s,
|
|
struct hostapd_hw_modes *mode,
|
|
u8 channel, const u8 *center_channels,
|
|
size_t num_chan)
|
|
{
|
|
size_t i;
|
|
|
|
if (mode->mode != HOSTAPD_MODE_IEEE80211A)
|
|
return 0;
|
|
|
|
for (i = 0; i < num_chan; i++)
|
|
/*
|
|
* In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48),
|
|
* so the center channel is 6 channels away from the start/end.
|
|
*/
|
|
if (channel >= center_channels[i] - 6 &&
|
|
channel <= center_channels[i] + 6)
|
|
return center_channels[i];
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static const u8 center_channels_5ghz_80mhz[] = { 42, 58, 106, 122, 138,
|
|
155, 171 };
|
|
static const u8 center_channels_6ghz_80mhz[] = { 7, 23, 39, 55, 71, 87, 103,
|
|
119, 135, 151, 167, 183, 199,
|
|
215 };
|
|
|
|
static enum chan_allowed wpas_p2p_verify_80mhz(struct wpa_supplicant *wpa_s,
|
|
struct hostapd_hw_modes *mode,
|
|
u8 op_class, u8 channel, u8 bw)
|
|
{
|
|
u8 center_chan;
|
|
int i, flags;
|
|
enum chan_allowed res, ret = ALLOWED;
|
|
const u8 *chans;
|
|
size_t num_chans;
|
|
bool is_6ghz = is_6ghz_op_class(op_class);
|
|
|
|
if (is_6ghz) {
|
|
chans = center_channels_6ghz_80mhz;
|
|
num_chans = ARRAY_SIZE(center_channels_6ghz_80mhz);
|
|
} else {
|
|
chans = center_channels_5ghz_80mhz;
|
|
num_chans = ARRAY_SIZE(center_channels_5ghz_80mhz);
|
|
}
|
|
center_chan = wpas_p2p_get_center_80mhz(wpa_s, mode, channel,
|
|
chans, num_chans);
|
|
if (!center_chan)
|
|
return NOT_ALLOWED;
|
|
if (!wpa_s->p2p_go_allow_dfs &&
|
|
!is_6ghz && center_chan >= 58 && center_chan <= 138)
|
|
return NOT_ALLOWED; /* Do not allow DFS channels for P2P */
|
|
|
|
/* check all the channels are available */
|
|
for (i = 0; i < 4; i++) {
|
|
int adj_chan = center_chan - 6 + i * 4;
|
|
|
|
res = has_channel(wpa_s->global, mode, op_class, adj_chan,
|
|
&flags);
|
|
if (res == NOT_ALLOWED)
|
|
return NOT_ALLOWED;
|
|
if (res == RADAR)
|
|
ret = RADAR;
|
|
if (res == NO_IR)
|
|
ret = NO_IR;
|
|
if (!is_6ghz) {
|
|
if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70))
|
|
return NOT_ALLOWED;
|
|
if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50))
|
|
return NOT_ALLOWED;
|
|
if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30))
|
|
return NOT_ALLOWED;
|
|
if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10))
|
|
return NOT_ALLOWED;
|
|
} else if (is_6ghz &&
|
|
(!(wpas_get_6ghz_he_chwidth_capab(mode) &
|
|
HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G))) {
|
|
return NOT_ALLOWED;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int wpas_p2p_get_center_160mhz(struct wpa_supplicant *wpa_s,
|
|
struct hostapd_hw_modes *mode,
|
|
u8 channel, const u8 *center_channels,
|
|
size_t num_chan)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (mode->mode != HOSTAPD_MODE_IEEE80211A)
|
|
return 0;
|
|
|
|
for (i = 0; i < num_chan; i++)
|
|
/*
|
|
* In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64),
|
|
* so the center channel is 14 channels away from the start/end.
|
|
*/
|
|
if (channel >= center_channels[i] - 14 &&
|
|
channel <= center_channels[i] + 14)
|
|
return center_channels[i];
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static const u8 center_channels_5ghz_160mhz[] = { 50, 114, 163 };
|
|
static const u8 center_channels_6ghz_160mhz[] = { 15, 47, 79, 111, 143, 175,
|
|
207 };
|
|
|
|
static enum chan_allowed wpas_p2p_verify_160mhz(struct wpa_supplicant *wpa_s,
|
|
struct hostapd_hw_modes *mode,
|
|
u8 op_class, u8 channel, u8 bw)
|
|
{
|
|
u8 center_chan;
|
|
int i, flags;
|
|
enum chan_allowed res, ret = ALLOWED;
|
|
const u8 *chans;
|
|
size_t num_chans;
|
|
|
|
if (is_6ghz_op_class(op_class)) {
|
|
chans = center_channels_6ghz_160mhz;
|
|
num_chans = ARRAY_SIZE(center_channels_6ghz_160mhz);
|
|
} else {
|
|
chans = center_channels_5ghz_160mhz;
|
|
num_chans = ARRAY_SIZE(center_channels_5ghz_160mhz);
|
|
}
|
|
center_chan = wpas_p2p_get_center_160mhz(wpa_s, mode, channel,
|
|
chans, num_chans);
|
|
if (!center_chan)
|
|
return NOT_ALLOWED;
|
|
/* VHT 160 MHz uses DFS channels in most countries. */
|
|
|
|
/* Check all the channels are available */
|
|
for (i = 0; i < 8; i++) {
|
|
int adj_chan = center_chan - 14 + i * 4;
|
|
|
|
res = has_channel(wpa_s->global, mode, op_class, adj_chan,
|
|
&flags);
|
|
if (res == NOT_ALLOWED)
|
|
return NOT_ALLOWED;
|
|
|
|
if (res == RADAR)
|
|
ret = RADAR;
|
|
if (res == NO_IR)
|
|
ret = NO_IR;
|
|
|
|
if (!is_6ghz_op_class(op_class)) {
|
|
if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150))
|
|
return NOT_ALLOWED;
|
|
if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130))
|
|
return NOT_ALLOWED;
|
|
if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110))
|
|
return NOT_ALLOWED;
|
|
if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90))
|
|
return NOT_ALLOWED;
|
|
if (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70))
|
|
return NOT_ALLOWED;
|
|
if (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50))
|
|
return NOT_ALLOWED;
|
|
if (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30))
|
|
return NOT_ALLOWED;
|
|
if (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10))
|
|
return NOT_ALLOWED;
|
|
} else if (is_6ghz_op_class(op_class) &&
|
|
(!(wpas_get_6ghz_he_chwidth_capab(mode) &
|
|
HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G))) {
|
|
return NOT_ALLOWED;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static enum chan_allowed wpas_p2p_verify_edmg(struct wpa_supplicant *wpa_s,
|
|
struct hostapd_hw_modes *mode,
|
|
u8 channel)
|
|
{
|
|
struct ieee80211_edmg_config edmg;
|
|
|
|
hostapd_encode_edmg_chan(1, channel, 0, &edmg);
|
|
if (edmg.channels && ieee802_edmg_is_allowed(mode->edmg, edmg))
|
|
return ALLOWED;
|
|
|
|
return NOT_ALLOWED;
|
|
}
|
|
|
|
|
|
static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s,
|
|
struct hostapd_hw_modes *mode,
|
|
u8 op_class, u8 channel, u8 bw)
|
|
{
|
|
int flag = 0;
|
|
enum chan_allowed res, res2;
|
|
|
|
if (is_6ghz_op_class(op_class) && !is_6ghz_psc_frequency(
|
|
p2p_channel_to_freq(op_class, channel)))
|
|
return NOT_ALLOWED;
|
|
|
|
res2 = res = has_channel(wpa_s->global, mode, op_class, channel, &flag);
|
|
if (bw == BW40MINUS) {
|
|
if (!(flag & HOSTAPD_CHAN_HT40MINUS))
|
|
return NOT_ALLOWED;
|
|
res2 = has_channel(wpa_s->global, mode, op_class, channel - 4,
|
|
NULL);
|
|
} else if (bw == BW40PLUS) {
|
|
if (!(flag & HOSTAPD_CHAN_HT40PLUS))
|
|
return NOT_ALLOWED;
|
|
res2 = has_channel(wpa_s->global, mode, op_class, channel + 4,
|
|
NULL);
|
|
} else if (is_6ghz_op_class(op_class) && bw == BW40) {
|
|
if (mode->mode != HOSTAPD_MODE_IEEE80211A)
|
|
return NOT_ALLOWED;
|
|
if (get_6ghz_sec_channel(channel) < 0)
|
|
res2 = has_channel(wpa_s->global, mode, op_class,
|
|
channel - 4, NULL);
|
|
else
|
|
res2 = has_channel(wpa_s->global, mode, op_class,
|
|
channel + 4, NULL);
|
|
} else if (bw == BW80) {
|
|
res2 = wpas_p2p_verify_80mhz(wpa_s, mode, op_class, channel,
|
|
bw);
|
|
} else if (bw == BW160) {
|
|
res2 = wpas_p2p_verify_160mhz(wpa_s, mode, op_class, channel,
|
|
bw);
|
|
} else if (bw == BW4320 || bw == BW6480 || bw == BW8640) {
|
|
return wpas_p2p_verify_edmg(wpa_s, mode, channel);
|
|
}
|
|
|
|
if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
|
|
return NOT_ALLOWED;
|
|
if (res == NO_IR || res2 == NO_IR)
|
|
return NO_IR;
|
|
if (res == RADAR || res2 == RADAR)
|
|
return RADAR;
|
|
return res;
|
|
}
|
|
|
|
|
|
static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s,
|
|
struct p2p_channels *chan,
|
|
struct p2p_channels *cli_chan,
|
|
bool p2p_disable_6ghz)
|
|
{
|
|
struct hostapd_hw_modes *mode;
|
|
int cla, op, cli_cla;
|
|
|
|
if (wpa_s->hw.modes == NULL) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Driver did not support fetching "
|
|
"of all supported channels; assume dualband "
|
|
"support");
|
|
return wpas_p2p_default_channels(wpa_s, chan, cli_chan);
|
|
}
|
|
|
|
cla = cli_cla = 0;
|
|
|
|
for (op = 0; global_op_class[op].op_class; op++) {
|
|
const struct oper_class_map *o = &global_op_class[op];
|
|
unsigned int ch;
|
|
struct p2p_reg_class *reg = NULL, *cli_reg = NULL;
|
|
|
|
if (o->p2p == NO_P2P_SUPP ||
|
|
(is_6ghz_op_class(o->op_class) && p2p_disable_6ghz))
|
|
continue;
|
|
|
|
mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode,
|
|
is_6ghz_op_class(o->op_class));
|
|
if (mode == NULL)
|
|
continue;
|
|
if (mode->mode == HOSTAPD_MODE_IEEE80211G)
|
|
wpa_s->global->p2p_24ghz_social_channels = 1;
|
|
for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
|
|
enum chan_allowed res;
|
|
|
|
/* Check for non-continuous jump in channel index
|
|
* incrementation */
|
|
if ((o->op_class >= 128 && o->op_class <= 130) &&
|
|
ch < 149 && ch + o->inc > 149)
|
|
ch = 149;
|
|
|
|
res = wpas_p2p_verify_channel(wpa_s, mode, o->op_class,
|
|
ch, o->bw);
|
|
if (res == ALLOWED) {
|
|
if (reg == NULL) {
|
|
if (cla == P2P_MAX_REG_CLASSES)
|
|
continue;
|
|
wpa_printf(MSG_DEBUG, "P2P: Add operating class %u",
|
|
o->op_class);
|
|
reg = &chan->reg_class[cla];
|
|
cla++;
|
|
reg->reg_class = o->op_class;
|
|
}
|
|
if (reg->channels == P2P_MAX_REG_CLASS_CHANNELS)
|
|
continue;
|
|
reg->channel[reg->channels] = ch;
|
|
reg->channels++;
|
|
} else if (res == NO_IR &&
|
|
wpa_s->conf->p2p_add_cli_chan) {
|
|
if (cli_reg == NULL) {
|
|
if (cli_cla == P2P_MAX_REG_CLASSES)
|
|
continue;
|
|
wpa_printf(MSG_DEBUG, "P2P: Add operating class %u (client only)",
|
|
o->op_class);
|
|
cli_reg = &cli_chan->reg_class[cli_cla];
|
|
cli_cla++;
|
|
cli_reg->reg_class = o->op_class;
|
|
}
|
|
if (cli_reg->channels ==
|
|
P2P_MAX_REG_CLASS_CHANNELS)
|
|
continue;
|
|
cli_reg->channel[cli_reg->channels] = ch;
|
|
cli_reg->channels++;
|
|
}
|
|
}
|
|
if (reg) {
|
|
wpa_hexdump(MSG_DEBUG, "P2P: Channels",
|
|
reg->channel, reg->channels);
|
|
}
|
|
if (cli_reg) {
|
|
wpa_hexdump(MSG_DEBUG, "P2P: Channels (client only)",
|
|
cli_reg->channel, cli_reg->channels);
|
|
}
|
|
}
|
|
|
|
chan->reg_classes = cla;
|
|
cli_chan->reg_classes = cli_cla;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int wpas_p2p_get_sec_channel_offset_40mhz(struct wpa_supplicant *wpa_s,
|
|
struct hostapd_hw_modes *mode,
|
|
u8 channel)
|
|
{
|
|
int op;
|
|
enum chan_allowed ret;
|
|
|
|
for (op = 0; global_op_class[op].op_class; op++) {
|
|
const struct oper_class_map *o = &global_op_class[op];
|
|
u16 ch = 0;
|
|
|
|
/* Allow DFS channels marked as NO_P2P_SUPP to be used with
|
|
* driver offloaded DFS. */
|
|
if ((o->p2p == NO_P2P_SUPP &&
|
|
(!is_dfs_global_op_class(o->op_class) ||
|
|
!wpa_s->p2p_go_allow_dfs)) ||
|
|
(is_6ghz_op_class(o->op_class) &&
|
|
wpa_s->conf->p2p_6ghz_disable))
|
|
continue;
|
|
|
|
/* IEEE Std 802.11ax-2021 26.17.2.3.2: "A 6 GHz-only AP should
|
|
* set up the BSS with a primary 20 MHz channel that coincides
|
|
* with a preferred scanning channel (PSC)."
|
|
* 6 GHz BW40 operation class 132 in wpa_supplicant uses the
|
|
* lowest 20 MHz channel for simplicity, so increase ch by 4 to
|
|
* match the PSC.
|
|
*/
|
|
if (is_6ghz_op_class(o->op_class) && o->bw == BW40 &&
|
|
get_6ghz_sec_channel(channel) < 0)
|
|
ch = 4;
|
|
|
|
for (ch += o->min_chan; ch <= o->max_chan; ch += o->inc) {
|
|
if (o->mode != HOSTAPD_MODE_IEEE80211A ||
|
|
(o->bw != BW40PLUS && o->bw != BW40MINUS &&
|
|
o->bw != BW40) ||
|
|
ch != channel)
|
|
continue;
|
|
ret = wpas_p2p_verify_channel(wpa_s, mode, o->op_class,
|
|
ch, o->bw);
|
|
if (ret == ALLOWED) {
|
|
if (is_6ghz_op_class(o->op_class) &&
|
|
o->bw == BW40)
|
|
return get_6ghz_sec_channel(channel);
|
|
return (o->bw == BW40MINUS) ? -1 : 1;
|
|
}
|
|
if (ret == RADAR && wpa_s->p2p_go_allow_dfs) {
|
|
/* Allow RADAR channels used for driver
|
|
* offloaded DFS */
|
|
return (o->bw == BW40MINUS) ? -1 : 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s,
|
|
struct hostapd_hw_modes *mode, u8 channel,
|
|
u8 op_class)
|
|
{
|
|
const u8 *chans;
|
|
size_t num_chans;
|
|
enum chan_allowed ret;
|
|
|
|
ret = wpas_p2p_verify_channel(wpa_s, mode, op_class, channel, BW80);
|
|
if (!(ret == ALLOWED || (ret == RADAR && wpa_s->p2p_go_allow_dfs)))
|
|
return 0;
|
|
|
|
if (is_6ghz_op_class(op_class)) {
|
|
chans = center_channels_6ghz_80mhz;
|
|
num_chans = ARRAY_SIZE(center_channels_6ghz_80mhz);
|
|
} else {
|
|
chans = center_channels_5ghz_80mhz;
|
|
num_chans = ARRAY_SIZE(center_channels_5ghz_80mhz);
|
|
}
|
|
return wpas_p2p_get_center_80mhz(wpa_s, mode, channel,
|
|
chans, num_chans);
|
|
}
|
|
|
|
|
|
int wpas_p2p_get_vht160_center(struct wpa_supplicant *wpa_s,
|
|
struct hostapd_hw_modes *mode, u8 channel,
|
|
u8 op_class)
|
|
{
|
|
const u8 *chans;
|
|
size_t num_chans;
|
|
enum chan_allowed ret;
|
|
|
|
ret = wpas_p2p_verify_channel(wpa_s, mode, op_class, channel, BW160);
|
|
if (!(ret == ALLOWED || (ret == RADAR && wpa_s->p2p_go_allow_dfs)))
|
|
return 0;
|
|
if (is_6ghz_op_class(op_class)) {
|
|
chans = center_channels_6ghz_160mhz;
|
|
num_chans = ARRAY_SIZE(center_channels_6ghz_160mhz);
|
|
} else {
|
|
chans = center_channels_5ghz_160mhz;
|
|
num_chans = ARRAY_SIZE(center_channels_5ghz_160mhz);
|
|
}
|
|
return wpas_p2p_get_center_160mhz(wpa_s, mode, channel,
|
|
chans, num_chans);
|
|
}
|
|
|
|
|
|
static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf,
|
|
size_t buf_len)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
|
|
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
|
|
if (os_memcmp(wpa_s->own_addr, interface_addr, ETH_ALEN) == 0)
|
|
break;
|
|
}
|
|
if (wpa_s == NULL)
|
|
return -1;
|
|
|
|
return wpa_drv_get_noa(wpa_s, buf, buf_len);
|
|
}
|
|
|
|
|
|
struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s,
|
|
const u8 *ssid, size_t ssid_len)
|
|
{
|
|
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
|
|
struct wpa_ssid *s = wpa_s->current_ssid;
|
|
if (s == NULL)
|
|
continue;
|
|
if (s->mode != WPAS_MODE_P2P_GO &&
|
|
s->mode != WPAS_MODE_AP &&
|
|
s->mode != WPAS_MODE_P2P_GROUP_FORMATION)
|
|
continue;
|
|
if (s->ssid_len != ssid_len ||
|
|
os_memcmp(ssid, s->ssid, ssid_len) != 0)
|
|
continue;
|
|
return wpa_s;
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s,
|
|
const u8 *peer_dev_addr)
|
|
{
|
|
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
|
|
struct wpa_ssid *ssid = wpa_s->current_ssid;
|
|
if (ssid && (ssid->mode != WPAS_MODE_INFRA || !ssid->p2p_group))
|
|
continue;
|
|
if (os_memcmp(wpa_s->go_dev_addr, peer_dev_addr, ETH_ALEN) == 0)
|
|
return wpa_s;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int wpas_go_connected(void *ctx, const u8 *dev_addr)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
|
|
return wpas_get_p2p_client_iface(wpa_s, dev_addr) != NULL;
|
|
}
|
|
|
|
|
|
static int wpas_is_concurrent_session_active(void *ctx)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
struct wpa_supplicant *ifs;
|
|
|
|
for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
|
|
if (ifs == wpa_s)
|
|
continue;
|
|
if (ifs->wpa_state > WPA_ASSOCIATED)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void wpas_p2p_debug_print(void *ctx, int level, const char *msg)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
wpa_msg_global(wpa_s, level, "P2P: %s", msg);
|
|
}
|
|
|
|
|
|
int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s,
|
|
const char *conf_p2p_dev)
|
|
{
|
|
struct wpa_interface iface;
|
|
struct wpa_supplicant *p2pdev_wpa_s;
|
|
char ifname[100];
|
|
char force_name[100];
|
|
int ret;
|
|
const u8 *if_addr = NULL;
|
|
|
|
ret = os_snprintf(ifname, sizeof(ifname), P2P_MGMT_DEVICE_PREFIX "%s",
|
|
wpa_s->ifname);
|
|
if (os_snprintf_error(sizeof(ifname), ret))
|
|
return -1;
|
|
/* Cut length at the maximum size. Note that we don't need to ensure
|
|
* collision free names here as the created interface is not a netdev.
|
|
*/
|
|
ifname[IFNAMSIZ - 1] = '\0';
|
|
force_name[0] = '\0';
|
|
wpa_s->pending_interface_type = WPA_IF_P2P_DEVICE;
|
|
|
|
if (wpa_s->conf->p2p_device_random_mac_addr == 2 &&
|
|
!is_zero_ether_addr(wpa_s->conf->p2p_device_persistent_mac_addr))
|
|
if_addr = wpa_s->conf->p2p_device_persistent_mac_addr;
|
|
|
|
ret = wpa_drv_if_add(wpa_s, WPA_IF_P2P_DEVICE, ifname, if_addr, NULL,
|
|
force_name, wpa_s->pending_interface_addr, NULL);
|
|
if (ret < 0) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Failed to create P2P Device interface");
|
|
return ret;
|
|
}
|
|
os_strlcpy(wpa_s->pending_interface_name, ifname,
|
|
sizeof(wpa_s->pending_interface_name));
|
|
|
|
os_memset(&iface, 0, sizeof(iface));
|
|
iface.p2p_mgmt = 1;
|
|
iface.ifname = wpa_s->pending_interface_name;
|
|
iface.driver = wpa_s->driver->name;
|
|
iface.driver_param = wpa_s->conf->driver_param;
|
|
|
|
/*
|
|
* If a P2P Device configuration file was given, use it as the interface
|
|
* configuration file (instead of using parent's configuration file.
|
|
*/
|
|
if (conf_p2p_dev) {
|
|
iface.confname = conf_p2p_dev;
|
|
iface.ctrl_interface = NULL;
|
|
} else {
|
|
iface.confname = wpa_s->confname;
|
|
iface.ctrl_interface = wpa_s->conf->ctrl_interface;
|
|
}
|
|
|
|
p2pdev_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s);
|
|
if (!p2pdev_wpa_s) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Failed to add P2P Device interface");
|
|
return -1;
|
|
}
|
|
|
|
p2pdev_wpa_s->p2pdev = p2pdev_wpa_s;
|
|
wpa_s->pending_interface_name[0] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void wpas_presence_resp(void *ctx, const u8 *src, u8 status,
|
|
const u8 *noa, size_t noa_len)
|
|
{
|
|
struct wpa_supplicant *wpa_s, *intf = ctx;
|
|
char hex[100];
|
|
|
|
for (wpa_s = intf->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
|
|
if (wpa_s->waiting_presence_resp)
|
|
break;
|
|
}
|
|
if (!wpa_s) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No group interface was waiting for presence response");
|
|
return;
|
|
}
|
|
wpa_s->waiting_presence_resp = 0;
|
|
|
|
wpa_snprintf_hex(hex, sizeof(hex), noa, noa_len);
|
|
wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PRESENCE_RESPONSE "src=" MACSTR
|
|
" status=%u noa=%s", MAC2STR(src), status, hex);
|
|
}
|
|
|
|
|
|
static int wpas_get_persistent_group(void *ctx, const u8 *addr, const u8 *ssid,
|
|
size_t ssid_len, u8 *go_dev_addr,
|
|
u8 *ret_ssid, size_t *ret_ssid_len,
|
|
u8 *intended_iface_addr)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
struct wpa_ssid *s;
|
|
|
|
s = wpas_p2p_get_persistent(wpa_s, addr, ssid, ssid_len);
|
|
if (s) {
|
|
os_memcpy(ret_ssid, s->ssid, s->ssid_len);
|
|
*ret_ssid_len = s->ssid_len;
|
|
os_memcpy(go_dev_addr, s->bssid, ETH_ALEN);
|
|
|
|
if (s->mode != WPAS_MODE_P2P_GO) {
|
|
os_memset(intended_iface_addr, 0, ETH_ALEN);
|
|
} else if (wpas_p2p_create_iface(wpa_s)) {
|
|
if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO))
|
|
return 0;
|
|
|
|
os_memcpy(intended_iface_addr,
|
|
wpa_s->pending_interface_addr, ETH_ALEN);
|
|
} else {
|
|
os_memcpy(intended_iface_addr, wpa_s->own_addr,
|
|
ETH_ALEN);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpas_get_go_info(void *ctx, u8 *intended_addr,
|
|
u8 *ssid, size_t *ssid_len, int *group_iface,
|
|
unsigned int *freq)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
struct wpa_supplicant *go;
|
|
struct wpa_ssid *s;
|
|
|
|
/*
|
|
* group_iface will be set to 1 only if a dedicated interface for P2P
|
|
* role is required. First, we try to reuse an active GO. However,
|
|
* if it is not present, we will try to reactivate an existing
|
|
* persistent group and set group_iface to 1, so the caller will know
|
|
* that the pending interface should be used.
|
|
*/
|
|
*group_iface = 0;
|
|
|
|
if (freq)
|
|
*freq = 0;
|
|
|
|
go = wpas_p2p_get_go_group(wpa_s);
|
|
if (!go) {
|
|
s = wpas_p2p_get_persistent_go(wpa_s);
|
|
*group_iface = wpas_p2p_create_iface(wpa_s);
|
|
if (s)
|
|
os_memcpy(intended_addr, s->bssid, ETH_ALEN);
|
|
else
|
|
return 0;
|
|
} else {
|
|
s = go->current_ssid;
|
|
os_memcpy(intended_addr, go->own_addr, ETH_ALEN);
|
|
if (freq)
|
|
*freq = go->assoc_freq;
|
|
}
|
|
|
|
os_memcpy(ssid, s->ssid, s->ssid_len);
|
|
*ssid_len = s->ssid_len;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int wpas_remove_stale_groups(void *ctx, const u8 *peer, const u8 *go,
|
|
const u8 *ssid, size_t ssid_len)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
struct wpa_ssid *s;
|
|
int save_config = 0;
|
|
size_t i;
|
|
|
|
/* Start with our first choice of Persistent Groups */
|
|
while ((s = wpas_p2p_get_persistent(wpa_s, peer, NULL, 0))) {
|
|
if (go && ssid && ssid_len &&
|
|
s->ssid_len == ssid_len &&
|
|
os_memcmp(go, s->bssid, ETH_ALEN) == 0 &&
|
|
os_memcmp(ssid, s->ssid, ssid_len) == 0)
|
|
break;
|
|
|
|
/* Remove stale persistent group */
|
|
if (s->mode != WPAS_MODE_P2P_GO || s->num_p2p_clients <= 1) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Remove stale persistent group id=%d",
|
|
s->id);
|
|
wpas_notify_persistent_group_removed(wpa_s, s);
|
|
wpa_config_remove_network(wpa_s->conf, s->id);
|
|
save_config = 1;
|
|
continue;
|
|
}
|
|
|
|
for (i = 0; i < s->num_p2p_clients; i++) {
|
|
if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN,
|
|
peer, ETH_ALEN) != 0)
|
|
continue;
|
|
|
|
os_memmove(s->p2p_client_list + i * 2 * ETH_ALEN,
|
|
s->p2p_client_list + (i + 1) * 2 * ETH_ALEN,
|
|
(s->num_p2p_clients - i - 1) * 2 * ETH_ALEN);
|
|
break;
|
|
}
|
|
s->num_p2p_clients--;
|
|
save_config = 1;
|
|
}
|
|
|
|
if (save_config)
|
|
p2p_config_write(wpa_s);
|
|
|
|
/* Return TRUE if valid SSID remains */
|
|
return s != NULL;
|
|
}
|
|
|
|
|
|
static void wpas_p2ps_get_feat_cap_str(char *buf, size_t buf_len,
|
|
const u8 *feat_cap, size_t feat_cap_len)
|
|
{
|
|
static const char pref[] = " feature_cap=";
|
|
int ret;
|
|
|
|
buf[0] = '\0';
|
|
|
|
/*
|
|
* We expect a feature capability to contain at least one byte to be
|
|
* reported. The string buffer provided by the caller function is
|
|
* expected to be big enough to contain all bytes of the attribute for
|
|
* known specifications. This function truncates the reported bytes if
|
|
* the feature capability data exceeds the string buffer size.
|
|
*/
|
|
if (!feat_cap || !feat_cap_len || buf_len < sizeof(pref) + 2)
|
|
return;
|
|
|
|
os_memcpy(buf, pref, sizeof(pref));
|
|
ret = wpa_snprintf_hex(&buf[sizeof(pref) - 1],
|
|
buf_len - sizeof(pref) + 1,
|
|
feat_cap, feat_cap_len);
|
|
|
|
if (ret != (2 * (int) feat_cap_len))
|
|
wpa_printf(MSG_WARNING, "P2PS feature_cap bytes truncated");
|
|
}
|
|
|
|
|
|
static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev,
|
|
const u8 *adv_mac, const u8 *ses_mac,
|
|
const u8 *grp_mac, u32 adv_id, u32 ses_id,
|
|
u8 conncap, int passwd_id,
|
|
const u8 *persist_ssid,
|
|
size_t persist_ssid_size, int response_done,
|
|
int prov_start, const char *session_info,
|
|
const u8 *feat_cap, size_t feat_cap_len,
|
|
unsigned int freq,
|
|
const u8 *group_ssid, size_t group_ssid_len)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
u8 mac[ETH_ALEN];
|
|
struct wpa_ssid *persistent_go, *stale, *s = NULL;
|
|
int save_config = 0;
|
|
struct wpa_supplicant *go_wpa_s;
|
|
char feat_cap_str[256];
|
|
|
|
if (!dev)
|
|
return;
|
|
|
|
os_memset(mac, 0, ETH_ALEN);
|
|
if (!adv_mac)
|
|
adv_mac = mac;
|
|
if (!ses_mac)
|
|
ses_mac = mac;
|
|
if (!grp_mac)
|
|
grp_mac = mac;
|
|
|
|
wpas_p2ps_get_feat_cap_str(feat_cap_str, sizeof(feat_cap_str),
|
|
feat_cap, feat_cap_len);
|
|
|
|
if (prov_start) {
|
|
if (session_info == NULL) {
|
|
wpa_msg_global(wpa_s, MSG_INFO,
|
|
P2P_EVENT_P2PS_PROVISION_START MACSTR
|
|
" adv_id=%x conncap=%x"
|
|
" adv_mac=" MACSTR
|
|
" session=%x mac=" MACSTR
|
|
" dev_passwd_id=%d%s",
|
|
MAC2STR(dev), adv_id, conncap,
|
|
MAC2STR(adv_mac),
|
|
ses_id, MAC2STR(ses_mac),
|
|
passwd_id, feat_cap_str);
|
|
} else {
|
|
wpa_msg_global(wpa_s, MSG_INFO,
|
|
P2P_EVENT_P2PS_PROVISION_START MACSTR
|
|
" adv_id=%x conncap=%x"
|
|
" adv_mac=" MACSTR
|
|
" session=%x mac=" MACSTR
|
|
" dev_passwd_id=%d info='%s'%s",
|
|
MAC2STR(dev), adv_id, conncap,
|
|
MAC2STR(adv_mac),
|
|
ses_id, MAC2STR(ses_mac),
|
|
passwd_id, session_info, feat_cap_str);
|
|
}
|
|
return;
|
|
}
|
|
|
|
go_wpa_s = wpas_p2p_get_go_group(wpa_s);
|
|
persistent_go = wpas_p2p_get_persistent_go(wpa_s);
|
|
|
|
if (status && status != P2P_SC_SUCCESS_DEFERRED) {
|
|
if (go_wpa_s && !p2p_group_go_member_count(wpa_s))
|
|
wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname);
|
|
|
|
if (persistent_go && !persistent_go->num_p2p_clients) {
|
|
/* remove empty persistent GO */
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Remove empty persistent group id=%d",
|
|
persistent_go->id);
|
|
wpas_notify_persistent_group_removed(wpa_s,
|
|
persistent_go);
|
|
wpa_config_remove_network(wpa_s->conf,
|
|
persistent_go->id);
|
|
}
|
|
|
|
wpa_msg_global(wpa_s, MSG_INFO,
|
|
P2P_EVENT_P2PS_PROVISION_DONE MACSTR
|
|
" status=%d"
|
|
" adv_id=%x adv_mac=" MACSTR
|
|
" session=%x mac=" MACSTR "%s",
|
|
MAC2STR(dev), status,
|
|
adv_id, MAC2STR(adv_mac),
|
|
ses_id, MAC2STR(ses_mac), feat_cap_str);
|
|
return;
|
|
}
|
|
|
|
/* Clean up stale persistent groups with this device */
|
|
if (persist_ssid && persist_ssid_size)
|
|
s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid,
|
|
persist_ssid_size);
|
|
|
|
if (persist_ssid && s && s->mode != WPAS_MODE_P2P_GO &&
|
|
is_zero_ether_addr(grp_mac)) {
|
|
wpa_dbg(wpa_s, MSG_ERROR,
|
|
"P2P: Peer device is a GO in a persistent group, but it did not provide the intended MAC address");
|
|
return;
|
|
}
|
|
|
|
for (;;) {
|
|
stale = wpas_p2p_get_persistent(wpa_s, dev, NULL, 0);
|
|
if (!stale)
|
|
break;
|
|
|
|
if (s && s->ssid_len == stale->ssid_len &&
|
|
os_memcmp(stale->bssid, s->bssid, ETH_ALEN) == 0 &&
|
|
os_memcmp(stale->ssid, s->ssid, s->ssid_len) == 0)
|
|
break;
|
|
|
|
/* Remove stale persistent group */
|
|
if (stale->mode != WPAS_MODE_P2P_GO ||
|
|
stale->num_p2p_clients <= 1) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Remove stale persistent group id=%d",
|
|
stale->id);
|
|
wpas_notify_persistent_group_removed(wpa_s, stale);
|
|
wpa_config_remove_network(wpa_s->conf, stale->id);
|
|
} else {
|
|
size_t i;
|
|
|
|
for (i = 0; i < stale->num_p2p_clients; i++) {
|
|
if (os_memcmp(stale->p2p_client_list +
|
|
i * ETH_ALEN,
|
|
dev, ETH_ALEN) == 0) {
|
|
os_memmove(stale->p2p_client_list +
|
|
i * ETH_ALEN,
|
|
stale->p2p_client_list +
|
|
(i + 1) * ETH_ALEN,
|
|
(stale->num_p2p_clients -
|
|
i - 1) * ETH_ALEN);
|
|
break;
|
|
}
|
|
}
|
|
stale->num_p2p_clients--;
|
|
}
|
|
save_config = 1;
|
|
}
|
|
|
|
if (save_config)
|
|
p2p_config_write(wpa_s);
|
|
|
|
if (s) {
|
|
if (go_wpa_s && !p2p_group_go_member_count(wpa_s))
|
|
wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname);
|
|
|
|
if (persistent_go && s != persistent_go &&
|
|
!persistent_go->num_p2p_clients) {
|
|
/* remove empty persistent GO */
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Remove empty persistent group id=%d",
|
|
persistent_go->id);
|
|
wpas_notify_persistent_group_removed(wpa_s,
|
|
persistent_go);
|
|
wpa_config_remove_network(wpa_s->conf,
|
|
persistent_go->id);
|
|
/* Save config */
|
|
}
|
|
|
|
wpa_msg_global(wpa_s, MSG_INFO,
|
|
P2P_EVENT_P2PS_PROVISION_DONE MACSTR
|
|
" status=%d"
|
|
" adv_id=%x adv_mac=" MACSTR
|
|
" session=%x mac=" MACSTR
|
|
" persist=%d%s",
|
|
MAC2STR(dev), status,
|
|
adv_id, MAC2STR(adv_mac),
|
|
ses_id, MAC2STR(ses_mac), s->id, feat_cap_str);
|
|
return;
|
|
}
|
|
|
|
wpa_s->global->pending_p2ps_group = 0;
|
|
wpa_s->global->pending_p2ps_group_freq = 0;
|
|
|
|
if (conncap == P2PS_SETUP_GROUP_OWNER) {
|
|
/*
|
|
* We need to copy the interface name. Simply saving a
|
|
* pointer isn't enough, since if we use pending_interface_name
|
|
* it will be overwritten when the group is added.
|
|
*/
|
|
char go_ifname[100];
|
|
|
|
go_ifname[0] = '\0';
|
|
if (!go_wpa_s) {
|
|
if (!response_done) {
|
|
wpa_s->global->pending_p2ps_group = 1;
|
|
wpa_s->global->pending_p2ps_group_freq = freq;
|
|
}
|
|
|
|
if (!wpas_p2p_create_iface(wpa_s))
|
|
os_memcpy(go_ifname, wpa_s->ifname,
|
|
sizeof(go_ifname));
|
|
else if (wpa_s->pending_interface_name[0])
|
|
os_memcpy(go_ifname,
|
|
wpa_s->pending_interface_name,
|
|
sizeof(go_ifname));
|
|
|
|
if (!go_ifname[0]) {
|
|
wpas_p2ps_prov_complete(
|
|
wpa_s, P2P_SC_FAIL_UNKNOWN_GROUP,
|
|
dev, adv_mac, ses_mac,
|
|
grp_mac, adv_id, ses_id, 0, 0,
|
|
NULL, 0, 0, 0, NULL, NULL, 0, 0,
|
|
NULL, 0);
|
|
return;
|
|
}
|
|
|
|
/* If PD Resp complete, start up the GO */
|
|
if (response_done && persistent_go) {
|
|
wpas_p2p_group_add_persistent(
|
|
wpa_s, persistent_go,
|
|
0, 0, freq, 0, 0, 0, 0, 0, 0, NULL,
|
|
persistent_go->mode ==
|
|
WPAS_MODE_P2P_GO ?
|
|
P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
|
|
0, 0, false);
|
|
} else if (response_done) {
|
|
wpas_p2p_group_add(wpa_s, 1, freq,
|
|
0, 0, 0, 0, 0, 0, false);
|
|
}
|
|
|
|
if (passwd_id == DEV_PW_P2PS_DEFAULT) {
|
|
os_memcpy(wpa_s->p2ps_join_addr, grp_mac,
|
|
ETH_ALEN);
|
|
wpa_s->p2ps_method_config_any = 1;
|
|
}
|
|
} else if (passwd_id == DEV_PW_P2PS_DEFAULT) {
|
|
os_memcpy(go_ifname, go_wpa_s->ifname,
|
|
sizeof(go_ifname));
|
|
|
|
if (is_zero_ether_addr(grp_mac)) {
|
|
wpa_dbg(go_wpa_s, MSG_DEBUG,
|
|
"P2P: Setting PIN-1 for ANY");
|
|
wpa_supplicant_ap_wps_pin(go_wpa_s, NULL,
|
|
"12345670", NULL, 0,
|
|
0);
|
|
} else {
|
|
wpa_dbg(go_wpa_s, MSG_DEBUG,
|
|
"P2P: Setting PIN-1 for " MACSTR,
|
|
MAC2STR(grp_mac));
|
|
wpa_supplicant_ap_wps_pin(go_wpa_s, grp_mac,
|
|
"12345670", NULL, 0,
|
|
0);
|
|
}
|
|
|
|
os_memcpy(wpa_s->p2ps_join_addr, grp_mac, ETH_ALEN);
|
|
wpa_s->p2ps_method_config_any = 1;
|
|
}
|
|
|
|
wpa_msg_global(wpa_s, MSG_INFO,
|
|
P2P_EVENT_P2PS_PROVISION_DONE MACSTR
|
|
" status=%d conncap=%x"
|
|
" adv_id=%x adv_mac=" MACSTR
|
|
" session=%x mac=" MACSTR
|
|
" dev_passwd_id=%d go=%s%s",
|
|
MAC2STR(dev), status, conncap,
|
|
adv_id, MAC2STR(adv_mac),
|
|
ses_id, MAC2STR(ses_mac),
|
|
passwd_id, go_ifname, feat_cap_str);
|
|
return;
|
|
}
|
|
|
|
if (go_wpa_s && !p2p_group_go_member_count(wpa_s))
|
|
wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname);
|
|
|
|
if (persistent_go && !persistent_go->num_p2p_clients) {
|
|
/* remove empty persistent GO */
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Remove empty persistent group id=%d",
|
|
persistent_go->id);
|
|
wpas_notify_persistent_group_removed(wpa_s, persistent_go);
|
|
wpa_config_remove_network(wpa_s->conf, persistent_go->id);
|
|
}
|
|
|
|
if (conncap == P2PS_SETUP_CLIENT) {
|
|
char ssid_hex[32 * 2 + 1];
|
|
|
|
if (group_ssid)
|
|
wpa_snprintf_hex(ssid_hex, sizeof(ssid_hex),
|
|
group_ssid, group_ssid_len);
|
|
else
|
|
ssid_hex[0] = '\0';
|
|
wpa_msg_global(wpa_s, MSG_INFO,
|
|
P2P_EVENT_P2PS_PROVISION_DONE MACSTR
|
|
" status=%d conncap=%x"
|
|
" adv_id=%x adv_mac=" MACSTR
|
|
" session=%x mac=" MACSTR
|
|
" dev_passwd_id=%d join=" MACSTR "%s%s%s",
|
|
MAC2STR(dev), status, conncap,
|
|
adv_id, MAC2STR(adv_mac),
|
|
ses_id, MAC2STR(ses_mac),
|
|
passwd_id, MAC2STR(grp_mac), feat_cap_str,
|
|
group_ssid ? " group_ssid=" : "", ssid_hex);
|
|
} else {
|
|
wpa_msg_global(wpa_s, MSG_INFO,
|
|
P2P_EVENT_P2PS_PROVISION_DONE MACSTR
|
|
" status=%d conncap=%x"
|
|
" adv_id=%x adv_mac=" MACSTR
|
|
" session=%x mac=" MACSTR
|
|
" dev_passwd_id=%d%s",
|
|
MAC2STR(dev), status, conncap,
|
|
adv_id, MAC2STR(adv_mac),
|
|
ses_id, MAC2STR(ses_mac),
|
|
passwd_id, feat_cap_str);
|
|
}
|
|
}
|
|
|
|
|
|
static int _wpas_p2p_in_progress(void *ctx)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
return wpas_p2p_in_progress(wpa_s);
|
|
}
|
|
|
|
|
|
static int wpas_prov_disc_resp_cb(void *ctx)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
struct wpa_ssid *persistent_go;
|
|
unsigned int freq;
|
|
|
|
if (!wpa_s->global->pending_p2ps_group)
|
|
return 0;
|
|
|
|
freq = wpa_s->global->pending_p2ps_group_freq;
|
|
wpa_s->global->pending_p2ps_group_freq = 0;
|
|
wpa_s->global->pending_p2ps_group = 0;
|
|
|
|
if (wpas_p2p_get_go_group(wpa_s))
|
|
return 0;
|
|
persistent_go = wpas_p2p_get_persistent_go(wpa_s);
|
|
|
|
if (persistent_go) {
|
|
wpas_p2p_group_add_persistent(
|
|
wpa_s, persistent_go, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
NULL,
|
|
persistent_go->mode == WPAS_MODE_P2P_GO ?
|
|
P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0,
|
|
is_p2p_allow_6ghz(wpa_s->global->p2p));
|
|
} else {
|
|
wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0, 0, 0,
|
|
is_p2p_allow_6ghz(wpa_s->global->p2p));
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int wpas_p2p_get_pref_freq_list(void *ctx, int go,
|
|
unsigned int *len,
|
|
struct weighted_pcl *freq_list)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
|
|
return wpa_drv_get_pref_freq_list(wpa_s, go ? WPA_IF_P2P_GO :
|
|
WPA_IF_P2P_CLIENT, len, freq_list);
|
|
}
|
|
|
|
|
|
int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s)
|
|
{
|
|
u8 addr[ETH_ALEN] = {0};
|
|
|
|
if (wpa_s->conf->p2p_device_random_mac_addr == 0)
|
|
return 0;
|
|
|
|
if (wpa_s->conf->p2p_device_random_mac_addr == 2) {
|
|
if (is_zero_ether_addr(
|
|
wpa_s->conf->p2p_device_persistent_mac_addr) &&
|
|
!is_zero_ether_addr(wpa_s->own_addr)) {
|
|
os_memcpy(wpa_s->conf->p2p_device_persistent_mac_addr,
|
|
wpa_s->own_addr, ETH_ALEN);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (!wpa_s->conf->ssid) {
|
|
if (random_mac_addr(addr) < 0) {
|
|
wpa_msg(wpa_s, MSG_INFO,
|
|
"Failed to generate random MAC address");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Store generated MAC address. */
|
|
os_memcpy(wpa_s->conf->p2p_device_persistent_mac_addr, addr,
|
|
ETH_ALEN);
|
|
} else {
|
|
/* If there are existing saved groups, restore last MAC address.
|
|
* if there is no last used MAC address, the last one is
|
|
* factory MAC. */
|
|
if (is_zero_ether_addr(
|
|
wpa_s->conf->p2p_device_persistent_mac_addr))
|
|
return 0;
|
|
os_memcpy(addr, wpa_s->conf->p2p_device_persistent_mac_addr,
|
|
ETH_ALEN);
|
|
wpa_msg(wpa_s, MSG_DEBUG, "Restore last used MAC address.");
|
|
}
|
|
|
|
if (wpa_drv_set_mac_addr(wpa_s, addr) < 0) {
|
|
wpa_msg(wpa_s, MSG_INFO,
|
|
"Failed to set random MAC address");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (wpa_supplicant_update_mac_addr(wpa_s) < 0) {
|
|
wpa_msg(wpa_s, MSG_INFO,
|
|
"Could not update MAC address information");
|
|
return -EINVAL;
|
|
}
|
|
|
|
wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR,
|
|
MAC2STR(addr));
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* wpas_p2p_init - Initialize P2P module for %wpa_supplicant
|
|
* @global: Pointer to global data from wpa_supplicant_init()
|
|
* @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
|
|
* Returns: 0 on success, -1 on failure
|
|
*/
|
|
int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
|
|
{
|
|
struct p2p_config p2p;
|
|
int i;
|
|
|
|
if (wpa_s->conf->p2p_disabled)
|
|
return 0;
|
|
|
|
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE))
|
|
return 0;
|
|
|
|
if (global->p2p)
|
|
return 0;
|
|
|
|
if (wpas_p2p_mac_setup(wpa_s) < 0) {
|
|
wpa_msg(wpa_s, MSG_ERROR,
|
|
"Failed to initialize P2P random MAC address.");
|
|
return -1;
|
|
}
|
|
|
|
os_memset(&p2p, 0, sizeof(p2p));
|
|
p2p.cb_ctx = wpa_s;
|
|
p2p.debug_print = wpas_p2p_debug_print;
|
|
p2p.p2p_scan = wpas_p2p_scan;
|
|
p2p.send_action = wpas_send_action;
|
|
p2p.send_action_done = wpas_send_action_done;
|
|
p2p.go_neg_completed = wpas_go_neg_completed;
|
|
p2p.go_neg_req_rx = wpas_go_neg_req_rx;
|
|
p2p.dev_found = wpas_dev_found;
|
|
p2p.dev_lost = wpas_dev_lost;
|
|
p2p.find_stopped = wpas_find_stopped;
|
|
p2p.start_listen = wpas_start_listen;
|
|
p2p.stop_listen = wpas_stop_listen;
|
|
p2p.send_probe_resp = wpas_send_probe_resp;
|
|
p2p.sd_request = wpas_sd_request;
|
|
p2p.sd_response = wpas_sd_response;
|
|
p2p.prov_disc_req = wpas_prov_disc_req;
|
|
p2p.prov_disc_resp = wpas_prov_disc_resp;
|
|
p2p.prov_disc_fail = wpas_prov_disc_fail;
|
|
p2p.invitation_process = wpas_invitation_process;
|
|
p2p.invitation_received = wpas_invitation_received;
|
|
p2p.invitation_result = wpas_invitation_result;
|
|
p2p.get_noa = wpas_get_noa;
|
|
p2p.go_connected = wpas_go_connected;
|
|
p2p.presence_resp = wpas_presence_resp;
|
|
p2p.is_concurrent_session_active = wpas_is_concurrent_session_active;
|
|
p2p.is_p2p_in_progress = _wpas_p2p_in_progress;
|
|
p2p.get_persistent_group = wpas_get_persistent_group;
|
|
p2p.get_go_info = wpas_get_go_info;
|
|
p2p.remove_stale_groups = wpas_remove_stale_groups;
|
|
p2p.p2ps_prov_complete = wpas_p2ps_prov_complete;
|
|
p2p.prov_disc_resp_cb = wpas_prov_disc_resp_cb;
|
|
p2p.p2ps_group_capability = p2ps_group_capability;
|
|
p2p.get_pref_freq_list = wpas_p2p_get_pref_freq_list;
|
|
p2p.p2p_6ghz_disable = wpa_s->conf->p2p_6ghz_disable;
|
|
|
|
os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
|
|
os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN);
|
|
p2p.dev_name = wpa_s->conf->device_name;
|
|
p2p.manufacturer = wpa_s->conf->manufacturer;
|
|
p2p.model_name = wpa_s->conf->model_name;
|
|
p2p.model_number = wpa_s->conf->model_number;
|
|
p2p.serial_number = wpa_s->conf->serial_number;
|
|
if (wpa_s->wps) {
|
|
os_memcpy(p2p.uuid, wpa_s->wps->uuid, 16);
|
|
p2p.config_methods = wpa_s->wps->config_methods;
|
|
}
|
|
|
|
if (wpas_p2p_setup_channels(wpa_s, &p2p.channels, &p2p.cli_channels,
|
|
p2p.p2p_6ghz_disable)) {
|
|
wpa_printf(MSG_ERROR,
|
|
"P2P: Failed to configure supported channel list");
|
|
return -1;
|
|
}
|
|
|
|
if (wpa_s->conf->p2p_listen_reg_class &&
|
|
wpa_s->conf->p2p_listen_channel) {
|
|
p2p.reg_class = wpa_s->conf->p2p_listen_reg_class;
|
|
p2p.channel = wpa_s->conf->p2p_listen_channel;
|
|
p2p.channel_forced = 1;
|
|
} else {
|
|
/*
|
|
* Pick one of the social channels randomly as the listen
|
|
* channel.
|
|
*/
|
|
if (p2p_config_get_random_social(&p2p, &p2p.reg_class,
|
|
&p2p.channel,
|
|
&global->p2p_go_avoid_freq,
|
|
&global->p2p_disallow_freq) !=
|
|
0) {
|
|
wpa_printf(MSG_INFO,
|
|
"P2P: No social channels supported by the driver - do not enable P2P");
|
|
return 0;
|
|
}
|
|
p2p.channel_forced = 0;
|
|
}
|
|
wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d:%d",
|
|
p2p.reg_class, p2p.channel);
|
|
|
|
if (wpa_s->conf->p2p_oper_reg_class &&
|
|
wpa_s->conf->p2p_oper_channel) {
|
|
p2p.op_reg_class = wpa_s->conf->p2p_oper_reg_class;
|
|
p2p.op_channel = wpa_s->conf->p2p_oper_channel;
|
|
p2p.cfg_op_channel = 1;
|
|
wpa_printf(MSG_DEBUG, "P2P: Configured operating channel: "
|
|
"%d:%d", p2p.op_reg_class, p2p.op_channel);
|
|
|
|
} else {
|
|
/*
|
|
* Use random operation channel from 2.4 GHz band social
|
|
* channels (1, 6, 11) or band 60 GHz social channel (2) if no
|
|
* other preference is indicated.
|
|
*/
|
|
if (p2p_config_get_random_social(&p2p, &p2p.op_reg_class,
|
|
&p2p.op_channel, NULL,
|
|
NULL) != 0) {
|
|
wpa_printf(MSG_INFO,
|
|
"P2P: Failed to select random social channel as operation channel");
|
|
p2p.op_reg_class = 0;
|
|
p2p.op_channel = 0;
|
|
/* This will be overridden during group setup in
|
|
* p2p_prepare_channel(), so allow setup to continue. */
|
|
}
|
|
p2p.cfg_op_channel = 0;
|
|
wpa_printf(MSG_DEBUG, "P2P: Random operating channel: "
|
|
"%d:%d", p2p.op_reg_class, p2p.op_channel);
|
|
}
|
|
|
|
if (wpa_s->conf->p2p_pref_chan && wpa_s->conf->num_p2p_pref_chan) {
|
|
p2p.pref_chan = wpa_s->conf->p2p_pref_chan;
|
|
p2p.num_pref_chan = wpa_s->conf->num_p2p_pref_chan;
|
|
}
|
|
|
|
if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
|
|
os_memcpy(p2p.country, wpa_s->conf->country, 2);
|
|
p2p.country[2] = 0x04;
|
|
} else
|
|
os_memcpy(p2p.country, "XX\x04", 3);
|
|
|
|
os_memcpy(p2p.pri_dev_type, wpa_s->conf->device_type,
|
|
WPS_DEV_TYPE_LEN);
|
|
|
|
p2p.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
|
|
os_memcpy(p2p.sec_dev_type, wpa_s->conf->sec_device_type,
|
|
p2p.num_sec_dev_types * WPS_DEV_TYPE_LEN);
|
|
|
|
p2p.concurrent_operations = !!(wpa_s->drv_flags &
|
|
WPA_DRIVER_FLAGS_P2P_CONCURRENT);
|
|
|
|
p2p.max_peers = 100;
|
|
|
|
if (wpa_s->conf->p2p_ssid_postfix) {
|
|
p2p.ssid_postfix_len =
|
|
os_strlen(wpa_s->conf->p2p_ssid_postfix);
|
|
if (p2p.ssid_postfix_len > sizeof(p2p.ssid_postfix))
|
|
p2p.ssid_postfix_len = sizeof(p2p.ssid_postfix);
|
|
os_memcpy(p2p.ssid_postfix, wpa_s->conf->p2p_ssid_postfix,
|
|
p2p.ssid_postfix_len);
|
|
}
|
|
|
|
p2p.p2p_intra_bss = wpa_s->conf->p2p_intra_bss;
|
|
|
|
p2p.max_listen = wpa_s->max_remain_on_chan;
|
|
|
|
if (wpa_s->conf->p2p_passphrase_len >= 8 &&
|
|
wpa_s->conf->p2p_passphrase_len <= 63)
|
|
p2p.passphrase_len = wpa_s->conf->p2p_passphrase_len;
|
|
else
|
|
p2p.passphrase_len = 8;
|
|
|
|
global->p2p = p2p_init(&p2p);
|
|
if (global->p2p == NULL)
|
|
return -1;
|
|
global->p2p_init_wpa_s = wpa_s;
|
|
|
|
for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) {
|
|
if (wpa_s->conf->wps_vendor_ext[i] == NULL)
|
|
continue;
|
|
p2p_add_wps_vendor_extension(
|
|
global->p2p, wpa_s->conf->wps_vendor_ext[i]);
|
|
}
|
|
|
|
p2p_set_no_go_freq(global->p2p, &wpa_s->conf->p2p_no_go_freq);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* wpas_p2p_deinit - Deinitialize per-interface P2P data
|
|
* @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
|
|
*
|
|
* This function deinitialize per-interface P2P data.
|
|
*/
|
|
void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
|
|
{
|
|
if (wpa_s->driver && wpa_s->drv_priv)
|
|
wpa_drv_probe_req_report(wpa_s, 0);
|
|
|
|
if (wpa_s->go_params) {
|
|
/* Clear any stored provisioning info */
|
|
p2p_clear_provisioning_info(
|
|
wpa_s->global->p2p,
|
|
wpa_s->go_params->peer_device_addr);
|
|
}
|
|
|
|
os_free(wpa_s->go_params);
|
|
wpa_s->go_params = NULL;
|
|
eloop_cancel_timeout(wpas_p2p_psk_failure_removal, wpa_s, NULL);
|
|
eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
|
|
eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
|
|
wpa_s->global->p2p_long_listen = 0;
|
|
eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
|
|
eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
|
|
wpas_p2p_remove_pending_group_interface(wpa_s);
|
|
eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL);
|
|
eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, wpa_s, NULL);
|
|
wpas_p2p_listen_work_done(wpa_s);
|
|
if (wpa_s->p2p_send_action_work) {
|
|
os_free(wpa_s->p2p_send_action_work->ctx);
|
|
radio_work_done(wpa_s->p2p_send_action_work);
|
|
wpa_s->p2p_send_action_work = NULL;
|
|
}
|
|
eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, wpa_s, NULL);
|
|
|
|
wpabuf_free(wpa_s->p2p_oob_dev_pw);
|
|
wpa_s->p2p_oob_dev_pw = NULL;
|
|
|
|
os_free(wpa_s->p2p_group_common_freqs);
|
|
wpa_s->p2p_group_common_freqs = NULL;
|
|
wpa_s->p2p_group_common_freqs_num = 0;
|
|
|
|
/* TODO: remove group interface from the driver if this wpa_s instance
|
|
* is on top of a P2P group interface */
|
|
}
|
|
|
|
|
|
/**
|
|
* wpas_p2p_deinit_global - Deinitialize global P2P module
|
|
* @global: Pointer to global data from wpa_supplicant_init()
|
|
*
|
|
* This function deinitializes the global (per device) P2P module.
|
|
*/
|
|
static void wpas_p2p_deinit_global(struct wpa_global *global)
|
|
{
|
|
struct wpa_supplicant *wpa_s, *tmp;
|
|
|
|
wpa_s = global->ifaces;
|
|
|
|
wpas_p2p_service_flush(global->p2p_init_wpa_s);
|
|
|
|
/* Remove remaining P2P group interfaces */
|
|
while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
|
|
wpa_s = wpa_s->next;
|
|
while (wpa_s) {
|
|
tmp = global->ifaces;
|
|
while (tmp &&
|
|
(tmp == wpa_s ||
|
|
tmp->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)) {
|
|
tmp = tmp->next;
|
|
}
|
|
if (tmp == NULL)
|
|
break;
|
|
/* Disconnect from the P2P group and deinit the interface */
|
|
wpas_p2p_disconnect(tmp);
|
|
}
|
|
|
|
/*
|
|
* Deinit GO data on any possibly remaining interface (if main
|
|
* interface is used as GO).
|
|
*/
|
|
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
|
|
if (wpa_s->ap_iface)
|
|
wpas_p2p_group_deinit(wpa_s);
|
|
}
|
|
|
|
p2p_deinit(global->p2p);
|
|
global->p2p = NULL;
|
|
global->p2p_init_wpa_s = NULL;
|
|
}
|
|
|
|
|
|
static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s)
|
|
{
|
|
if (wpa_s->conf->p2p_no_group_iface)
|
|
return 0; /* separate interface disabled per configuration */
|
|
if (wpa_s->drv_flags &
|
|
(WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE |
|
|
WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P))
|
|
return 1; /* P2P group requires a new interface in every case
|
|
*/
|
|
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CONCURRENT))
|
|
return 0; /* driver does not support concurrent operations */
|
|
if (wpa_s->global->ifaces->next)
|
|
return 1; /* more that one interface already in use */
|
|
if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
|
|
return 1; /* this interface is already in use */
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s,
|
|
const u8 *peer_addr,
|
|
enum p2p_wps_method wps_method,
|
|
int go_intent, const u8 *own_interface_addr,
|
|
unsigned int force_freq, int persistent_group,
|
|
struct wpa_ssid *ssid, unsigned int pref_freq)
|
|
{
|
|
if (persistent_group && wpa_s->conf->persistent_reconnect)
|
|
persistent_group = 2;
|
|
|
|
/*
|
|
* Increase GO config timeout if HT40 is used since it takes some time
|
|
* to scan channels for coex purposes before the BSS can be started.
|
|
*/
|
|
p2p_set_config_timeout(wpa_s->global->p2p,
|
|
wpa_s->p2p_go_ht40 ? 255 : 100, 20);
|
|
|
|
return p2p_connect(wpa_s->global->p2p, peer_addr, wps_method,
|
|
go_intent, own_interface_addr, force_freq,
|
|
persistent_group, ssid ? ssid->ssid : NULL,
|
|
ssid ? ssid->ssid_len : 0,
|
|
wpa_s->p2p_pd_before_go_neg, pref_freq,
|
|
wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id :
|
|
0);
|
|
}
|
|
|
|
|
|
static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s,
|
|
const u8 *peer_addr,
|
|
enum p2p_wps_method wps_method,
|
|
int go_intent, const u8 *own_interface_addr,
|
|
unsigned int force_freq, int persistent_group,
|
|
struct wpa_ssid *ssid, unsigned int pref_freq)
|
|
{
|
|
if (persistent_group && wpa_s->conf->persistent_reconnect)
|
|
persistent_group = 2;
|
|
|
|
return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method,
|
|
go_intent, own_interface_addr, force_freq,
|
|
persistent_group, ssid ? ssid->ssid : NULL,
|
|
ssid ? ssid->ssid_len : 0, pref_freq,
|
|
wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id :
|
|
0);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_check_join_scan_limit(struct wpa_supplicant *wpa_s)
|
|
{
|
|
wpa_s->p2p_join_scan_count++;
|
|
wpa_printf(MSG_DEBUG, "P2P: Join scan attempt %d",
|
|
wpa_s->p2p_join_scan_count);
|
|
if (wpa_s->p2p_join_scan_count > P2P_MAX_JOIN_SCAN_ATTEMPTS) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Failed to find GO " MACSTR
|
|
" for join operationg - stop join attempt",
|
|
MAC2STR(wpa_s->pending_join_iface_addr));
|
|
eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
|
|
if (wpa_s->p2p_auto_pd) {
|
|
wpa_s->p2p_auto_pd = 0;
|
|
wpa_msg_global(wpa_s, MSG_INFO,
|
|
P2P_EVENT_PROV_DISC_FAILURE
|
|
" p2p_dev_addr=" MACSTR " status=N/A",
|
|
MAC2STR(wpa_s->pending_join_dev_addr));
|
|
return;
|
|
}
|
|
if (wpa_s->p2p_fallback_to_go_neg) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Join operation failed - fall back to GO Negotiation");
|
|
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
|
|
P2P_EVENT_FALLBACK_TO_GO_NEG
|
|
"reason=join-failed");
|
|
wpas_p2p_fallback_to_go_neg(wpa_s, 0);
|
|
return;
|
|
}
|
|
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
|
|
P2P_EVENT_GROUP_FORMATION_FAILURE);
|
|
wpas_notify_p2p_group_formation_failure(wpa_s, "");
|
|
}
|
|
}
|
|
|
|
|
|
static int wpas_check_freq_conflict(struct wpa_supplicant *wpa_s, int freq)
|
|
{
|
|
int res;
|
|
unsigned int num, i;
|
|
struct wpa_used_freq_data *freqs;
|
|
|
|
if (wpas_p2p_num_unused_channels(wpa_s) > 0) {
|
|
/* Multiple channels are supported and not all are in use */
|
|
return 0;
|
|
}
|
|
|
|
freqs = os_calloc(wpa_s->num_multichan_concurrent,
|
|
sizeof(struct wpa_used_freq_data));
|
|
if (!freqs)
|
|
return 1;
|
|
|
|
num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
|
|
wpa_s->num_multichan_concurrent);
|
|
|
|
for (i = 0; i < num; i++) {
|
|
if (freqs[i].freq == freq) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Frequency %d MHz in use by another virtual interface and can be used",
|
|
freq);
|
|
res = 0;
|
|
goto exit_free;
|
|
}
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: No valid operating frequencies");
|
|
res = 1;
|
|
|
|
exit_free:
|
|
os_free(freqs);
|
|
return res;
|
|
}
|
|
|
|
|
|
static int wpas_p2p_peer_go(struct wpa_supplicant *wpa_s,
|
|
const u8 *peer_dev_addr)
|
|
{
|
|
struct wpa_bss *bss;
|
|
int updated;
|
|
|
|
bss = wpa_bss_get_p2p_dev_addr(wpa_s, peer_dev_addr);
|
|
if (bss == NULL)
|
|
return -1;
|
|
if (bss->last_update_idx < wpa_s->bss_update_idx) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Peer BSS entry not updated in the "
|
|
"last scan");
|
|
return 0;
|
|
}
|
|
|
|
updated = os_reltime_before(&wpa_s->p2p_auto_started,
|
|
&bss->last_update);
|
|
wpa_printf(MSG_DEBUG, "P2P: Current BSS entry for peer updated at "
|
|
"%ld.%06ld (%supdated in last scan)",
|
|
bss->last_update.sec, bss->last_update.usec,
|
|
updated ? "": "not ");
|
|
|
|
return updated;
|
|
}
|
|
|
|
|
|
static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
|
|
struct wpa_scan_results *scan_res)
|
|
{
|
|
struct wpa_bss *bss = NULL;
|
|
int freq;
|
|
u8 iface_addr[ETH_ALEN];
|
|
|
|
eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
|
|
|
|
if (wpa_s->global->p2p_disabled)
|
|
return;
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS) for %sjoin",
|
|
scan_res ? (int) scan_res->num : -1,
|
|
wpa_s->p2p_auto_join ? "auto_" : "");
|
|
|
|
if (scan_res)
|
|
wpas_p2p_scan_res_handler(wpa_s, scan_res);
|
|
|
|
if (wpa_s->p2p_auto_pd) {
|
|
int join = wpas_p2p_peer_go(wpa_s,
|
|
wpa_s->pending_join_dev_addr);
|
|
if (join == 0 &&
|
|
wpa_s->auto_pd_scan_retry < P2P_AUTO_PD_SCAN_ATTEMPTS) {
|
|
wpa_s->auto_pd_scan_retry++;
|
|
bss = wpa_bss_get_bssid_latest(
|
|
wpa_s, wpa_s->pending_join_dev_addr);
|
|
if (bss) {
|
|
freq = bss->freq;
|
|
wpa_printf(MSG_DEBUG, "P2P: Scan retry %d for "
|
|
"the peer " MACSTR " at %d MHz",
|
|
wpa_s->auto_pd_scan_retry,
|
|
MAC2STR(wpa_s->
|
|
pending_join_dev_addr),
|
|
freq);
|
|
wpas_p2p_join_scan_req(wpa_s, freq, NULL, 0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (join < 0)
|
|
join = 0;
|
|
|
|
wpa_s->p2p_auto_pd = 0;
|
|
wpa_s->pending_pd_use = join ? AUTO_PD_JOIN : AUTO_PD_GO_NEG;
|
|
wpa_printf(MSG_DEBUG, "P2P: Auto PD with " MACSTR " join=%d",
|
|
MAC2STR(wpa_s->pending_join_dev_addr), join);
|
|
if (p2p_prov_disc_req(wpa_s->global->p2p,
|
|
wpa_s->pending_join_dev_addr, NULL,
|
|
wpa_s->pending_pd_config_methods, join,
|
|
0, wpa_s->user_initiated_pd) < 0) {
|
|
wpa_s->p2p_auto_pd = 0;
|
|
wpa_msg_global(wpa_s, MSG_INFO,
|
|
P2P_EVENT_PROV_DISC_FAILURE
|
|
" p2p_dev_addr=" MACSTR " status=N/A",
|
|
MAC2STR(wpa_s->pending_join_dev_addr));
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (wpa_s->p2p_auto_join) {
|
|
int join = wpas_p2p_peer_go(wpa_s,
|
|
wpa_s->pending_join_dev_addr);
|
|
if (join < 0) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Peer was not found to be "
|
|
"running a GO -> use GO Negotiation");
|
|
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
|
|
P2P_EVENT_FALLBACK_TO_GO_NEG
|
|
"reason=peer-not-running-GO");
|
|
wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr,
|
|
wpa_s->p2p_pin, wpa_s->p2p_wps_method,
|
|
wpa_s->p2p_persistent_group, 0, 0, 0,
|
|
wpa_s->p2p_go_intent,
|
|
wpa_s->p2p_connect_freq,
|
|
wpa_s->p2p_go_vht_center_freq2,
|
|
wpa_s->p2p_persistent_id,
|
|
wpa_s->p2p_pd_before_go_neg,
|
|
wpa_s->p2p_go_ht40,
|
|
wpa_s->p2p_go_vht,
|
|
wpa_s->p2p_go_max_oper_chwidth,
|
|
wpa_s->p2p_go_he,
|
|
wpa_s->p2p_go_edmg,
|
|
NULL, 0,
|
|
is_p2p_allow_6ghz(wpa_s->global->p2p));
|
|
return;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Peer was found running GO%s -> "
|
|
"try to join the group", join ? "" :
|
|
" in older scan");
|
|
if (!join) {
|
|
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
|
|
P2P_EVENT_FALLBACK_TO_GO_NEG_ENABLED);
|
|
wpa_s->p2p_fallback_to_go_neg = 1;
|
|
}
|
|
}
|
|
|
|
freq = p2p_get_oper_freq(wpa_s->global->p2p,
|
|
wpa_s->pending_join_iface_addr);
|
|
if (freq < 0 &&
|
|
p2p_get_interface_addr(wpa_s->global->p2p,
|
|
wpa_s->pending_join_dev_addr,
|
|
iface_addr) == 0 &&
|
|
os_memcmp(iface_addr, wpa_s->pending_join_dev_addr, ETH_ALEN) != 0
|
|
&& !wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr)) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Overwrite pending interface "
|
|
"address for join from " MACSTR " to " MACSTR
|
|
" based on newly discovered P2P peer entry",
|
|
MAC2STR(wpa_s->pending_join_iface_addr),
|
|
MAC2STR(iface_addr));
|
|
os_memcpy(wpa_s->pending_join_iface_addr, iface_addr,
|
|
ETH_ALEN);
|
|
|
|
freq = p2p_get_oper_freq(wpa_s->global->p2p,
|
|
wpa_s->pending_join_iface_addr);
|
|
}
|
|
if (freq >= 0) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
|
|
"from P2P peer table: %d MHz", freq);
|
|
}
|
|
if (wpa_s->p2p_join_ssid_len) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Trying to find target GO BSS entry based on BSSID "
|
|
MACSTR " and SSID %s",
|
|
MAC2STR(wpa_s->pending_join_iface_addr),
|
|
wpa_ssid_txt(wpa_s->p2p_join_ssid,
|
|
wpa_s->p2p_join_ssid_len));
|
|
bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr,
|
|
wpa_s->p2p_join_ssid,
|
|
wpa_s->p2p_join_ssid_len);
|
|
} else if (!bss) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Trying to find target GO BSS entry based on BSSID "
|
|
MACSTR, MAC2STR(wpa_s->pending_join_iface_addr));
|
|
bss = wpa_bss_get_bssid_latest(wpa_s,
|
|
wpa_s->pending_join_iface_addr);
|
|
}
|
|
if (bss) {
|
|
u8 dev_addr[ETH_ALEN];
|
|
|
|
freq = bss->freq;
|
|
wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
|
|
"from BSS table: %d MHz (SSID %s)", freq,
|
|
wpa_ssid_txt(bss->ssid, bss->ssid_len));
|
|
if (p2p_parse_dev_addr(wpa_bss_ie_ptr(bss), bss->ie_len,
|
|
dev_addr) == 0 &&
|
|
os_memcmp(wpa_s->pending_join_dev_addr,
|
|
wpa_s->pending_join_iface_addr, ETH_ALEN) == 0 &&
|
|
os_memcmp(dev_addr, wpa_s->pending_join_dev_addr,
|
|
ETH_ALEN) != 0) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Update target GO device address based on BSS entry: " MACSTR " (was " MACSTR ")",
|
|
MAC2STR(dev_addr),
|
|
MAC2STR(wpa_s->pending_join_dev_addr));
|
|
os_memcpy(wpa_s->pending_join_dev_addr, dev_addr,
|
|
ETH_ALEN);
|
|
}
|
|
}
|
|
if (freq > 0) {
|
|
u16 method;
|
|
|
|
if (wpas_check_freq_conflict(wpa_s, freq) > 0) {
|
|
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
|
|
P2P_EVENT_GROUP_FORMATION_FAILURE
|
|
"reason=FREQ_CONFLICT");
|
|
wpas_notify_p2p_group_formation_failure(
|
|
wpa_s, "FREQ_CONFLICT");
|
|
return;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Send Provision Discovery Request "
|
|
"prior to joining an existing group (GO " MACSTR
|
|
" freq=%u MHz)",
|
|
MAC2STR(wpa_s->pending_join_dev_addr), freq);
|
|
wpa_s->pending_pd_before_join = 1;
|
|
|
|
switch (wpa_s->pending_join_wps_method) {
|
|
case WPS_PIN_DISPLAY:
|
|
method = WPS_CONFIG_KEYPAD;
|
|
break;
|
|
case WPS_PIN_KEYPAD:
|
|
method = WPS_CONFIG_DISPLAY;
|
|
break;
|
|
case WPS_PBC:
|
|
method = WPS_CONFIG_PUSHBUTTON;
|
|
break;
|
|
case WPS_P2PS:
|
|
method = WPS_CONFIG_P2PS;
|
|
break;
|
|
default:
|
|
method = 0;
|
|
break;
|
|
}
|
|
|
|
if ((p2p_get_provisioning_info(wpa_s->global->p2p,
|
|
wpa_s->pending_join_dev_addr) ==
|
|
method)) {
|
|
/*
|
|
* We have already performed provision discovery for
|
|
* joining the group. Proceed directly to join
|
|
* operation without duplicated provision discovery. */
|
|
wpa_printf(MSG_DEBUG, "P2P: Provision discovery "
|
|
"with " MACSTR " already done - proceed to "
|
|
"join",
|
|
MAC2STR(wpa_s->pending_join_dev_addr));
|
|
wpa_s->pending_pd_before_join = 0;
|
|
goto start;
|
|
}
|
|
|
|
if (p2p_prov_disc_req(wpa_s->global->p2p,
|
|
wpa_s->pending_join_dev_addr,
|
|
NULL, method, 1,
|
|
freq, wpa_s->user_initiated_pd) < 0) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision "
|
|
"Discovery Request before joining an "
|
|
"existing group");
|
|
wpa_s->pending_pd_before_join = 0;
|
|
goto start;
|
|
}
|
|
return;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Failed to find BSS/GO - try again later");
|
|
eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
|
|
eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL);
|
|
wpas_p2p_check_join_scan_limit(wpa_s);
|
|
return;
|
|
|
|
start:
|
|
/* Start join operation immediately */
|
|
wpas_p2p_join_start(wpa_s, 0, wpa_s->p2p_join_ssid,
|
|
wpa_s->p2p_join_ssid_len);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
|
|
const u8 *ssid, size_t ssid_len)
|
|
{
|
|
int ret;
|
|
struct wpa_driver_scan_params params;
|
|
struct wpabuf *wps_ie, *ies;
|
|
size_t ielen;
|
|
int freqs[2] = { 0, 0 };
|
|
unsigned int bands;
|
|
|
|
os_memset(¶ms, 0, sizeof(params));
|
|
|
|
/* P2P Wildcard SSID */
|
|
params.num_ssids = 1;
|
|
if (ssid && ssid_len) {
|
|
params.ssids[0].ssid = ssid;
|
|
params.ssids[0].ssid_len = ssid_len;
|
|
os_memcpy(wpa_s->p2p_join_ssid, ssid, ssid_len);
|
|
wpa_s->p2p_join_ssid_len = ssid_len;
|
|
} else {
|
|
params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
|
|
params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
|
|
wpa_s->p2p_join_ssid_len = 0;
|
|
}
|
|
|
|
wpa_s->wps->dev.p2p = 1;
|
|
wps_ie = wps_build_probe_req_ie(DEV_PW_DEFAULT, &wpa_s->wps->dev,
|
|
wpa_s->wps->uuid, WPS_REQ_ENROLLEE, 0,
|
|
NULL);
|
|
if (wps_ie == NULL) {
|
|
wpas_p2p_scan_res_join(wpa_s, NULL);
|
|
return;
|
|
}
|
|
|
|
if (!freq) {
|
|
int oper_freq;
|
|
/*
|
|
* If freq is not provided, check the operating freq of the GO
|
|
* and use a single channel scan on if possible.
|
|
*/
|
|
oper_freq = p2p_get_oper_freq(wpa_s->global->p2p,
|
|
wpa_s->pending_join_iface_addr);
|
|
if (oper_freq > 0)
|
|
freq = oper_freq;
|
|
}
|
|
if (freq > 0) {
|
|
freqs[0] = freq;
|
|
params.freqs = freqs;
|
|
} else if (wpa_s->conf->p2p_6ghz_disable ||
|
|
!is_p2p_allow_6ghz(wpa_s->global->p2p)) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: 6 GHz disabled - update the scan frequency list");
|
|
wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, ¶ms,
|
|
0);
|
|
wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, ¶ms,
|
|
0);
|
|
}
|
|
|
|
ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
|
|
ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen);
|
|
if (ies == NULL) {
|
|
wpabuf_free(wps_ie);
|
|
wpas_p2p_scan_res_join(wpa_s, NULL);
|
|
return;
|
|
}
|
|
wpabuf_put_buf(ies, wps_ie);
|
|
wpabuf_free(wps_ie);
|
|
|
|
bands = wpas_get_bands(wpa_s, freqs);
|
|
p2p_scan_ie(wpa_s->global->p2p, ies, NULL, bands);
|
|
|
|
params.p2p_probe = 1;
|
|
params.extra_ies = wpabuf_head(ies);
|
|
params.extra_ies_len = wpabuf_len(ies);
|
|
|
|
if (wpa_s->clear_driver_scan_cache) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"Request driver to clear scan cache due to local BSS flush");
|
|
params.only_new_results = 1;
|
|
}
|
|
|
|
/*
|
|
* Run a scan to update BSS table and start Provision Discovery once
|
|
* the new scan results become available.
|
|
*/
|
|
ret = wpa_drv_scan(wpa_s, ¶ms);
|
|
if (params.freqs != freqs)
|
|
os_free(params.freqs);
|
|
if (!ret) {
|
|
os_get_reltime(&wpa_s->scan_trigger_time);
|
|
wpa_s->scan_res_handler = wpas_p2p_scan_res_join;
|
|
wpa_s->own_scan_requested = 1;
|
|
wpa_s->clear_driver_scan_cache = 0;
|
|
}
|
|
|
|
wpabuf_free(ies);
|
|
|
|
if (ret) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Failed to start scan for join - "
|
|
"try again later");
|
|
eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
|
|
eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL);
|
|
wpas_p2p_check_join_scan_limit(wpa_s);
|
|
}
|
|
}
|
|
|
|
|
|
static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx)
|
|
{
|
|
struct wpa_supplicant *wpa_s = eloop_ctx;
|
|
wpas_p2p_join_scan_req(wpa_s, 0, NULL, 0);
|
|
}
|
|
|
|
|
|
static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
|
|
const u8 *dev_addr, enum p2p_wps_method wps_method,
|
|
int auto_join, int op_freq,
|
|
const u8 *ssid, size_t ssid_len)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
|
|
MACSTR " dev " MACSTR " op_freq=%d)%s",
|
|
MAC2STR(iface_addr), MAC2STR(dev_addr), op_freq,
|
|
auto_join ? " (auto_join)" : "");
|
|
if (ssid && ssid_len) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Group SSID specified: %s",
|
|
wpa_ssid_txt(ssid, ssid_len));
|
|
}
|
|
|
|
wpa_s->p2p_auto_pd = 0;
|
|
wpa_s->p2p_auto_join = !!auto_join;
|
|
os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, ETH_ALEN);
|
|
os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, ETH_ALEN);
|
|
wpa_s->pending_join_wps_method = wps_method;
|
|
|
|
/* Make sure we are not running find during connection establishment */
|
|
wpas_p2p_stop_find(wpa_s);
|
|
|
|
wpa_s->p2p_join_scan_count = 0;
|
|
wpas_p2p_join_scan_req(wpa_s, op_freq, ssid, ssid_len);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq,
|
|
const u8 *ssid, size_t ssid_len)
|
|
{
|
|
struct wpa_supplicant *group;
|
|
struct p2p_go_neg_results res;
|
|
struct wpa_bss *bss;
|
|
|
|
group = wpas_p2p_get_group_iface(wpa_s, 0, 0);
|
|
if (group == NULL)
|
|
return -1;
|
|
if (group != wpa_s) {
|
|
os_memcpy(group->p2p_pin, wpa_s->p2p_pin,
|
|
sizeof(group->p2p_pin));
|
|
group->p2p_wps_method = wpa_s->p2p_wps_method;
|
|
}
|
|
|
|
/*
|
|
* Need to mark the current interface for p2p_group_formation
|
|
* when a separate group interface is not used. This is needed
|
|
* to allow p2p_cancel stop a pending p2p_connect-join.
|
|
* wpas_p2p_init_group_interface() addresses this for the case
|
|
* where a separate group interface is used.
|
|
*/
|
|
if (group == wpa_s->parent)
|
|
wpa_s->global->p2p_group_formation = group;
|
|
|
|
group->p2p_in_provisioning = 1;
|
|
group->p2p_fallback_to_go_neg = wpa_s->p2p_fallback_to_go_neg;
|
|
|
|
os_memset(&res, 0, sizeof(res));
|
|
os_memcpy(res.peer_device_addr, wpa_s->pending_join_dev_addr, ETH_ALEN);
|
|
os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr,
|
|
ETH_ALEN);
|
|
res.wps_method = wpa_s->pending_join_wps_method;
|
|
if (freq && ssid && ssid_len) {
|
|
res.freq = freq;
|
|
res.ssid_len = ssid_len;
|
|
os_memcpy(res.ssid, ssid, ssid_len);
|
|
} else {
|
|
if (ssid && ssid_len) {
|
|
bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr,
|
|
ssid, ssid_len);
|
|
} else {
|
|
bss = wpa_bss_get_bssid_latest(
|
|
wpa_s, wpa_s->pending_join_iface_addr);
|
|
}
|
|
if (bss) {
|
|
res.freq = bss->freq;
|
|
res.ssid_len = bss->ssid_len;
|
|
os_memcpy(res.ssid, bss->ssid, bss->ssid_len);
|
|
wpa_printf(MSG_DEBUG, "P2P: Join target GO operating frequency from BSS table: %d MHz (SSID %s)",
|
|
bss->freq,
|
|
wpa_ssid_txt(bss->ssid, bss->ssid_len));
|
|
} else if (ssid && ssid_len) {
|
|
res.ssid_len = ssid_len;
|
|
os_memcpy(res.ssid, ssid, ssid_len);
|
|
wpa_printf(MSG_DEBUG, "P2P: Join target GO (SSID %s)",
|
|
wpa_ssid_txt(ssid, ssid_len));
|
|
}
|
|
}
|
|
|
|
if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel prior to "
|
|
"starting client");
|
|
wpa_drv_cancel_remain_on_channel(wpa_s);
|
|
wpa_s->off_channel_freq = 0;
|
|
wpa_s->roc_waiting_drv_freq = 0;
|
|
}
|
|
wpas_start_wps_enrollee(group, &res);
|
|
|
|
/*
|
|
* Allow a longer timeout for join-a-running-group than normal 15
|
|
* second group formation timeout since the GO may not have authorized
|
|
* our connection yet.
|
|
*/
|
|
eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
|
|
eloop_register_timeout(60, 0, wpas_p2p_group_formation_timeout,
|
|
wpa_s, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
|
|
int *force_freq, int *pref_freq, int go,
|
|
struct weighted_pcl *pref_freq_list,
|
|
unsigned int *num_pref_freq)
|
|
{
|
|
struct wpa_used_freq_data *freqs;
|
|
int res, best_freq, num_unused;
|
|
unsigned int freq_in_use = 0, num, i, max_pref_freq;
|
|
|
|
max_pref_freq = *num_pref_freq;
|
|
*num_pref_freq = 0;
|
|
|
|
freqs = os_calloc(wpa_s->num_multichan_concurrent,
|
|
sizeof(struct wpa_used_freq_data));
|
|
if (!freqs)
|
|
return -1;
|
|
|
|
num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
|
|
wpa_s->num_multichan_concurrent);
|
|
|
|
/*
|
|
* It is possible that the total number of used frequencies is bigger
|
|
* than the number of frequencies used for P2P, so get the system wide
|
|
* number of unused frequencies.
|
|
*/
|
|
num_unused = wpas_p2p_num_unused_channels(wpa_s);
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Setup freqs: freq=%d num_MCC=%d shared_freqs=%u num_unused=%d",
|
|
freq, wpa_s->num_multichan_concurrent, num, num_unused);
|
|
|
|
if (freq > 0) {
|
|
int ret;
|
|
if (go)
|
|
ret = p2p_supported_freq(wpa_s->global->p2p, freq);
|
|
else
|
|
ret = p2p_supported_freq_cli(wpa_s->global->p2p, freq);
|
|
if (!ret) {
|
|
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
|
|
ieee80211_is_dfs(freq, wpa_s->hw.modes,
|
|
wpa_s->hw.num_modes)) {
|
|
/*
|
|
* If freq is a DFS channel and DFS is offloaded
|
|
* to the driver, allow P2P GO to use it.
|
|
*/
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded to the driver",
|
|
freq);
|
|
} else {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: The forced channel (%u MHz) is not supported for P2P uses",
|
|
freq);
|
|
res = -3;
|
|
goto exit_free;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < num; i++) {
|
|
if (freqs[i].freq == freq)
|
|
freq_in_use = 1;
|
|
}
|
|
|
|
if (num_unused <= 0 && !freq_in_use) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group on %u MHz as there are no available channels",
|
|
freq);
|
|
res = -2;
|
|
goto exit_free;
|
|
}
|
|
wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
|
|
"requested channel (%u MHz)", freq);
|
|
*force_freq = freq;
|
|
goto exit_ok;
|
|
}
|
|
|
|
best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
|
|
|
|
if (!wpa_s->conf->num_p2p_pref_chan && *pref_freq == 0) {
|
|
enum wpa_driver_if_type iface_type;
|
|
|
|
if (go)
|
|
iface_type = WPA_IF_P2P_GO;
|
|
else
|
|
iface_type = WPA_IF_P2P_CLIENT;
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: best_freq=%d, go=%d",
|
|
best_freq, go);
|
|
|
|
res = wpa_drv_get_pref_freq_list(wpa_s, iface_type,
|
|
&max_pref_freq,
|
|
pref_freq_list);
|
|
if (!is_p2p_allow_6ghz(wpa_s->global->p2p))
|
|
max_pref_freq = p2p_remove_6ghz_channels(pref_freq_list,
|
|
max_pref_freq);
|
|
|
|
if (!res && max_pref_freq > 0) {
|
|
*num_pref_freq = max_pref_freq;
|
|
i = 0;
|
|
while (i < *num_pref_freq &&
|
|
(!p2p_supported_freq(wpa_s->global->p2p,
|
|
pref_freq_list[i].freq) ||
|
|
wpas_p2p_disallowed_freq(
|
|
wpa_s->global,
|
|
pref_freq_list[i].freq) ||
|
|
!p2p_pref_freq_allowed(&pref_freq_list[i],
|
|
go))) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: preferred_freq_list[%d]=%d is disallowed",
|
|
i, pref_freq_list[i].freq);
|
|
i++;
|
|
}
|
|
if (i != *num_pref_freq) {
|
|
best_freq = pref_freq_list[i].freq;
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Using preferred_freq_list[%d]=%d",
|
|
i, best_freq);
|
|
} else {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: All driver preferred frequencies are disallowed for P2P use");
|
|
*num_pref_freq = 0;
|
|
}
|
|
} else {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: No preferred frequency list available");
|
|
}
|
|
}
|
|
|
|
/* We have a candidate frequency to use */
|
|
if (best_freq > 0) {
|
|
if (*pref_freq == 0 && num_unused > 0) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Try to prefer a frequency (%u MHz) we are already using",
|
|
best_freq);
|
|
*pref_freq = best_freq;
|
|
} else {
|
|
wpa_printf(MSG_DEBUG, "P2P: Try to force us to use frequency (%u MHz) which is already in use",
|
|
best_freq);
|
|
*force_freq = best_freq;
|
|
}
|
|
} else if (num_unused > 0) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Current operating channels are not available for P2P. Try to use another channel");
|
|
*force_freq = 0;
|
|
} else {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: All channels are in use and none of them are P2P enabled. Cannot start P2P group");
|
|
res = -2;
|
|
goto exit_free;
|
|
}
|
|
|
|
exit_ok:
|
|
res = 0;
|
|
exit_free:
|
|
os_free(freqs);
|
|
return res;
|
|
}
|
|
|
|
|
|
static bool is_p2p_6ghz_supported(struct wpa_supplicant *wpa_s,
|
|
const u8 *peer_addr)
|
|
{
|
|
if (wpa_s->conf->p2p_6ghz_disable ||
|
|
!get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
|
|
HOSTAPD_MODE_IEEE80211A, true))
|
|
return false;
|
|
|
|
if (!p2p_wfd_enabled(wpa_s->global->p2p))
|
|
return false;
|
|
if (peer_addr && !p2p_peer_wfd_enabled(wpa_s->global->p2p, peer_addr))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static int wpas_p2p_check_6ghz(struct wpa_supplicant *wpa_s,
|
|
const u8 *peer_addr, bool allow_6ghz, int freq)
|
|
{
|
|
if (allow_6ghz && is_p2p_6ghz_supported(wpa_s, peer_addr)) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Allow connection on 6 GHz channels");
|
|
p2p_set_6ghz_dev_capab(wpa_s->global->p2p, true);
|
|
} else {
|
|
if (is_6ghz_freq(freq))
|
|
return -2;
|
|
p2p_set_6ghz_dev_capab(wpa_s->global->p2p, false);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* wpas_p2p_connect - Request P2P Group Formation to be started
|
|
* @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
|
|
* @peer_addr: Address of the peer P2P Device
|
|
* @pin: PIN to use during provisioning or %NULL to indicate PBC mode
|
|
* @persistent_group: Whether to create a persistent group
|
|
* @auto_join: Whether to select join vs. GO Negotiation automatically
|
|
* @join: Whether to join an existing group (as a client) instead of starting
|
|
* Group Owner negotiation; @peer_addr is BSSID in that case
|
|
* @auth: Whether to only authorize the connection instead of doing that and
|
|
* initiating Group Owner negotiation
|
|
* @go_intent: GO Intent or -1 to use default
|
|
* @freq: Frequency for the group or 0 for auto-selection
|
|
* @freq2: Center frequency of segment 1 for the GO operating in VHT 80P80 mode
|
|
* @persistent_id: Persistent group credentials to use for forcing GO
|
|
* parameters or -1 to generate new values (SSID/passphrase)
|
|
* @pd: Whether to send Provision Discovery prior to GO Negotiation as an
|
|
* interoperability workaround when initiating group formation
|
|
* @ht40: Start GO with 40 MHz channel width
|
|
* @vht: Start GO with VHT support
|
|
* @vht_chwidth: Channel width supported by GO operating with VHT support
|
|
* (CHANWIDTH_*).
|
|
* @group_ssid: Specific Group SSID for join or %NULL if not set
|
|
* @group_ssid_len: Length of @group_ssid in octets
|
|
* @allow_6ghz: Allow P2P connection on 6 GHz channels
|
|
* Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified
|
|
* failure, -2 on failure due to channel not currently available,
|
|
* -3 if forced channel is not supported
|
|
*/
|
|
int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
|
|
const char *pin, enum p2p_wps_method wps_method,
|
|
int persistent_group, int auto_join, int join, int auth,
|
|
int go_intent, int freq, unsigned int vht_center_freq2,
|
|
int persistent_id, int pd, int ht40, int vht,
|
|
unsigned int vht_chwidth, int he, int edmg,
|
|
const u8 *group_ssid, size_t group_ssid_len,
|
|
bool allow_6ghz)
|
|
{
|
|
int force_freq = 0, pref_freq = 0;
|
|
int ret = 0, res;
|
|
enum wpa_driver_if_type iftype;
|
|
const u8 *if_addr;
|
|
struct wpa_ssid *ssid = NULL;
|
|
struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS];
|
|
unsigned int size;
|
|
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return -1;
|
|
|
|
if (persistent_id >= 0) {
|
|
ssid = wpa_config_get_network(wpa_s->conf, persistent_id);
|
|
if (ssid == NULL || ssid->disabled != 2 ||
|
|
ssid->mode != WPAS_MODE_P2P_GO)
|
|
return -1;
|
|
}
|
|
|
|
if (wpas_p2p_check_6ghz(wpa_s, peer_addr, allow_6ghz, freq))
|
|
return -2;
|
|
|
|
os_free(wpa_s->global->add_psk);
|
|
wpa_s->global->add_psk = NULL;
|
|
|
|
wpa_s->global->p2p_fail_on_wps_complete = 0;
|
|
wpa_s->global->pending_p2ps_group = 0;
|
|
wpa_s->global->pending_p2ps_group_freq = 0;
|
|
wpa_s->p2ps_method_config_any = 0;
|
|
|
|
if (go_intent < 0)
|
|
go_intent = wpa_s->conf->p2p_go_intent;
|
|
|
|
if (!auth)
|
|
wpa_s->global->p2p_long_listen = 0;
|
|
|
|
wpa_s->p2p_wps_method = wps_method;
|
|
wpa_s->p2p_persistent_group = !!persistent_group;
|
|
wpa_s->p2p_persistent_id = persistent_id;
|
|
wpa_s->p2p_go_intent = go_intent;
|
|
wpa_s->p2p_connect_freq = freq;
|
|
wpa_s->p2p_fallback_to_go_neg = 0;
|
|
wpa_s->p2p_pd_before_go_neg = !!pd;
|
|
wpa_s->p2p_go_ht40 = !!ht40;
|
|
wpa_s->p2p_go_vht = !!vht;
|
|
wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2;
|
|
wpa_s->p2p_go_max_oper_chwidth = vht_chwidth;
|
|
wpa_s->p2p_go_he = !!he;
|
|
wpa_s->p2p_go_edmg = !!edmg;
|
|
|
|
if (pin)
|
|
os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
|
|
else if (wps_method == WPS_PIN_DISPLAY) {
|
|
if (wps_generate_pin((unsigned int *) &ret) < 0)
|
|
return -1;
|
|
res = os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin),
|
|
"%08d", ret);
|
|
if (os_snprintf_error(sizeof(wpa_s->p2p_pin), res))
|
|
wpa_s->p2p_pin[sizeof(wpa_s->p2p_pin) - 1] = '\0';
|
|
wpa_printf(MSG_DEBUG, "P2P: Randomly generated PIN: %s",
|
|
wpa_s->p2p_pin);
|
|
} else if (wps_method == WPS_P2PS) {
|
|
/* Force the P2Ps default PIN to be used */
|
|
os_strlcpy(wpa_s->p2p_pin, "12345670", sizeof(wpa_s->p2p_pin));
|
|
} else
|
|
wpa_s->p2p_pin[0] = '\0';
|
|
|
|
if (join || auto_join) {
|
|
u8 iface_addr[ETH_ALEN], dev_addr[ETH_ALEN];
|
|
if (auth) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Authorize invitation to "
|
|
"connect a running group from " MACSTR,
|
|
MAC2STR(peer_addr));
|
|
os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN);
|
|
return ret;
|
|
}
|
|
os_memcpy(dev_addr, peer_addr, ETH_ALEN);
|
|
if (p2p_get_interface_addr(wpa_s->global->p2p, peer_addr,
|
|
iface_addr) < 0) {
|
|
os_memcpy(iface_addr, peer_addr, ETH_ALEN);
|
|
p2p_get_dev_addr(wpa_s->global->p2p, peer_addr,
|
|
dev_addr);
|
|
}
|
|
if (auto_join) {
|
|
os_get_reltime(&wpa_s->p2p_auto_started);
|
|
wpa_printf(MSG_DEBUG, "P2P: Auto join started at "
|
|
"%ld.%06ld",
|
|
wpa_s->p2p_auto_started.sec,
|
|
wpa_s->p2p_auto_started.usec);
|
|
}
|
|
wpa_s->user_initiated_pd = 1;
|
|
if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method,
|
|
auto_join, freq,
|
|
group_ssid, group_ssid_len) < 0)
|
|
return -1;
|
|
return ret;
|
|
}
|
|
|
|
size = P2P_MAX_PREF_CHANNELS;
|
|
res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
|
|
go_intent == 15, pref_freq_list, &size);
|
|
if (res)
|
|
return res;
|
|
wpas_p2p_set_own_freq_preference(wpa_s,
|
|
force_freq ? force_freq : pref_freq);
|
|
|
|
p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size);
|
|
|
|
wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
|
|
|
|
if (wpa_s->create_p2p_iface) {
|
|
/* Prepare to add a new interface for the group */
|
|
iftype = WPA_IF_P2P_GROUP;
|
|
if (go_intent == 15)
|
|
iftype = WPA_IF_P2P_GO;
|
|
if (wpas_p2p_add_group_interface(wpa_s, iftype) < 0) {
|
|
wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new "
|
|
"interface for the group");
|
|
return -1;
|
|
}
|
|
|
|
if_addr = wpa_s->pending_interface_addr;
|
|
} else {
|
|
if (wpa_s->p2p_mgmt)
|
|
if_addr = wpa_s->parent->own_addr;
|
|
else
|
|
if_addr = wpa_s->own_addr;
|
|
os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
|
|
}
|
|
|
|
if (auth) {
|
|
if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
|
|
go_intent, if_addr,
|
|
force_freq, persistent_group, ssid,
|
|
pref_freq) < 0)
|
|
return -1;
|
|
return ret;
|
|
}
|
|
|
|
if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method,
|
|
go_intent, if_addr, force_freq,
|
|
persistent_group, ssid, pref_freq) < 0) {
|
|
if (wpa_s->create_p2p_iface)
|
|
wpas_p2p_remove_pending_group_interface(wpa_s);
|
|
return -1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* wpas_p2p_remain_on_channel_cb - Indication of remain-on-channel start
|
|
* @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
|
|
* @freq: Frequency of the channel in MHz
|
|
* @duration: Duration of the stay on the channel in milliseconds
|
|
*
|
|
* This callback is called when the driver indicates that it has started the
|
|
* requested remain-on-channel duration.
|
|
*/
|
|
void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
|
|
unsigned int freq, unsigned int duration)
|
|
{
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return;
|
|
wpa_printf(MSG_DEBUG, "P2P: remain-on-channel callback (off_channel_freq=%u pending_listen_freq=%d roc_waiting_drv_freq=%d freq=%u duration=%u)",
|
|
wpa_s->off_channel_freq, wpa_s->pending_listen_freq,
|
|
wpa_s->roc_waiting_drv_freq, freq, duration);
|
|
if (wpa_s->off_channel_freq &&
|
|
wpa_s->off_channel_freq == wpa_s->pending_listen_freq) {
|
|
p2p_listen_cb(wpa_s->global->p2p, wpa_s->pending_listen_freq,
|
|
wpa_s->pending_listen_duration);
|
|
wpa_s->pending_listen_freq = 0;
|
|
} else {
|
|
wpa_printf(MSG_DEBUG, "P2P: Ignore remain-on-channel callback (off_channel_freq=%u pending_listen_freq=%d freq=%u duration=%u)",
|
|
wpa_s->off_channel_freq, wpa_s->pending_listen_freq,
|
|
freq, duration);
|
|
}
|
|
}
|
|
|
|
|
|
int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s, unsigned int timeout)
|
|
{
|
|
/* Limit maximum Listen state time based on driver limitation. */
|
|
if (timeout > wpa_s->max_remain_on_chan)
|
|
timeout = wpa_s->max_remain_on_chan;
|
|
|
|
return p2p_listen(wpa_s->global->p2p, timeout);
|
|
}
|
|
|
|
|
|
/**
|
|
* wpas_p2p_cancel_remain_on_channel_cb - Remain-on-channel timeout
|
|
* @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
|
|
* @freq: Frequency of the channel in MHz
|
|
*
|
|
* This callback is called when the driver indicates that a remain-on-channel
|
|
* operation has been completed, i.e., the duration on the requested channel
|
|
* has timed out.
|
|
*/
|
|
void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
|
|
unsigned int freq)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel callback "
|
|
"(p2p_long_listen=%d ms pending_action_tx=%p)",
|
|
wpa_s->global->p2p_long_listen,
|
|
offchannel_pending_action_tx(wpa_s));
|
|
wpas_p2p_listen_work_done(wpa_s);
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return;
|
|
if (wpa_s->global->p2p_long_listen > 0)
|
|
wpa_s->global->p2p_long_listen -= wpa_s->max_remain_on_chan;
|
|
if (p2p_listen_end(wpa_s->global->p2p, freq) > 0)
|
|
return; /* P2P module started a new operation */
|
|
if (offchannel_pending_action_tx(wpa_s))
|
|
return;
|
|
if (wpa_s->global->p2p_long_listen > 0) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state");
|
|
wpas_p2p_listen_start(wpa_s, wpa_s->global->p2p_long_listen);
|
|
} else {
|
|
/*
|
|
* When listen duration is over, stop listen & update p2p_state
|
|
* to IDLE.
|
|
*/
|
|
p2p_stop_listen(wpa_s->global->p2p);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* wpas_p2p_group_remove - Remove a P2P group
|
|
* @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
|
|
* @ifname: Network interface name of the group interface or "*" to remove all
|
|
* groups
|
|
* Returns: 0 on success, -1 on failure
|
|
*
|
|
* This function is used to remove a P2P group. This can be used to disconnect
|
|
* from a group in which the local end is a P2P Client or to end a P2P Group in
|
|
* case the local end is the Group Owner. If a virtual network interface was
|
|
* created for this group, that interface will be removed. Otherwise, only the
|
|
* configured P2P group network will be removed from the interface.
|
|
*/
|
|
int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname)
|
|
{
|
|
struct wpa_global *global = wpa_s->global;
|
|
struct wpa_supplicant *calling_wpa_s = wpa_s;
|
|
|
|
if (os_strcmp(ifname, "*") == 0) {
|
|
struct wpa_supplicant *prev;
|
|
bool calling_wpa_s_group_removed = false;
|
|
|
|
wpa_s = global->ifaces;
|
|
while (wpa_s) {
|
|
prev = wpa_s;
|
|
wpa_s = wpa_s->next;
|
|
if (prev->p2p_group_interface !=
|
|
NOT_P2P_GROUP_INTERFACE ||
|
|
(prev->current_ssid &&
|
|
prev->current_ssid->p2p_group)) {
|
|
wpas_p2p_disconnect_safely(prev, calling_wpa_s);
|
|
if (prev == calling_wpa_s)
|
|
calling_wpa_s_group_removed = true;
|
|
}
|
|
}
|
|
|
|
if (!calling_wpa_s_group_removed &&
|
|
(calling_wpa_s->p2p_group_interface !=
|
|
NOT_P2P_GROUP_INTERFACE ||
|
|
(calling_wpa_s->current_ssid &&
|
|
calling_wpa_s->current_ssid->p2p_group))) {
|
|
wpa_printf(MSG_DEBUG, "Remove calling_wpa_s P2P group");
|
|
wpas_p2p_disconnect_safely(calling_wpa_s,
|
|
calling_wpa_s);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
|
|
if (os_strcmp(wpa_s->ifname, ifname) == 0)
|
|
break;
|
|
}
|
|
|
|
return wpas_p2p_disconnect_safely(wpa_s, calling_wpa_s);
|
|
}
|
|
|
|
|
|
static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
|
|
{
|
|
unsigned int r;
|
|
|
|
if (!wpa_s->conf->num_p2p_pref_chan && !freq) {
|
|
unsigned int i, size = P2P_MAX_PREF_CHANNELS;
|
|
struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS];
|
|
int res;
|
|
|
|
res = wpa_drv_get_pref_freq_list(wpa_s, WPA_IF_P2P_GO,
|
|
&size, pref_freq_list);
|
|
if (!is_p2p_allow_6ghz(wpa_s->global->p2p))
|
|
size = p2p_remove_6ghz_channels(pref_freq_list, size);
|
|
|
|
if (!res && size > 0) {
|
|
i = 0;
|
|
while (i < size &&
|
|
(!p2p_supported_freq(wpa_s->global->p2p,
|
|
pref_freq_list[i].freq) ||
|
|
wpas_p2p_disallowed_freq(
|
|
wpa_s->global,
|
|
pref_freq_list[i].freq) ||
|
|
!p2p_pref_freq_allowed(&pref_freq_list[i],
|
|
true))) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: preferred_freq_list[%d]=%d is disallowed",
|
|
i, pref_freq_list[i].freq);
|
|
i++;
|
|
}
|
|
if (i != size) {
|
|
freq = pref_freq_list[i].freq;
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Using preferred_freq_list[%d]=%d",
|
|
i, freq);
|
|
} else {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: All driver preferred frequencies are disallowed for P2P use");
|
|
}
|
|
} else {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: No preferred frequency list available");
|
|
}
|
|
}
|
|
|
|
if (freq == 2) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz "
|
|
"band");
|
|
if (wpa_s->best_24_freq > 0 &&
|
|
p2p_supported_freq_go(wpa_s->global->p2p,
|
|
wpa_s->best_24_freq)) {
|
|
freq = wpa_s->best_24_freq;
|
|
wpa_printf(MSG_DEBUG, "P2P: Use best 2.4 GHz band "
|
|
"channel: %d MHz", freq);
|
|
} else {
|
|
if (os_get_random((u8 *) &r, sizeof(r)) < 0)
|
|
return -1;
|
|
freq = 2412 + (r % 3) * 25;
|
|
wpa_printf(MSG_DEBUG, "P2P: Use random 2.4 GHz band "
|
|
"channel: %d MHz", freq);
|
|
}
|
|
}
|
|
|
|
if (freq == 5) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 5 GHz "
|
|
"band");
|
|
if (wpa_s->best_5_freq > 0 &&
|
|
p2p_supported_freq_go(wpa_s->global->p2p,
|
|
wpa_s->best_5_freq)) {
|
|
freq = wpa_s->best_5_freq;
|
|
wpa_printf(MSG_DEBUG, "P2P: Use best 5 GHz band "
|
|
"channel: %d MHz", freq);
|
|
} else {
|
|
const int freqs[] = {
|
|
/* operating class 115 */
|
|
5180, 5200, 5220, 5240,
|
|
/* operating class 124 */
|
|
5745, 5765, 5785, 5805,
|
|
};
|
|
unsigned int i, num_freqs = ARRAY_SIZE(freqs);
|
|
|
|
if (os_get_random((u8 *) &r, sizeof(r)) < 0)
|
|
return -1;
|
|
|
|
/*
|
|
* Most of the 5 GHz channels require DFS. Only
|
|
* operating classes 115 and 124 are available possibly
|
|
* without that requirement. Check these for
|
|
* availability starting from a randomly picked
|
|
* position.
|
|
*/
|
|
for (i = 0; i < num_freqs; i++, r++) {
|
|
freq = freqs[r % num_freqs];
|
|
if (p2p_supported_freq_go(wpa_s->global->p2p,
|
|
freq))
|
|
break;
|
|
}
|
|
|
|
if (i >= num_freqs) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Could not select "
|
|
"5 GHz channel for P2P group");
|
|
return -1;
|
|
}
|
|
wpa_printf(MSG_DEBUG, "P2P: Use random 5 GHz band "
|
|
"channel: %d MHz", freq);
|
|
}
|
|
}
|
|
|
|
if (freq > 0 && !p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
|
|
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
|
|
ieee80211_is_dfs(freq, wpa_s->hw.modes,
|
|
wpa_s->hw.num_modes)) {
|
|
/*
|
|
* If freq is a DFS channel and DFS is offloaded to the
|
|
* driver, allow P2P GO to use it.
|
|
*/
|
|
wpa_printf(MSG_DEBUG, "P2P: "
|
|
"%s: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded",
|
|
__func__, freq);
|
|
return freq;
|
|
}
|
|
wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO "
|
|
"(%u MHz) is not supported for P2P uses",
|
|
freq);
|
|
return -1;
|
|
}
|
|
|
|
return freq;
|
|
}
|
|
|
|
|
|
static int wpas_p2p_supported_freq_go(struct wpa_supplicant *wpa_s,
|
|
const struct p2p_channels *channels,
|
|
int freq)
|
|
{
|
|
if (!wpas_p2p_disallowed_freq(wpa_s->global, freq) &&
|
|
p2p_supported_freq_go(wpa_s->global->p2p, freq) &&
|
|
freq_included(wpa_s, channels, freq))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void wpas_p2p_select_go_freq_no_pref(struct wpa_supplicant *wpa_s,
|
|
struct p2p_go_neg_results *params,
|
|
const struct p2p_channels *channels)
|
|
{
|
|
unsigned int i, r;
|
|
|
|
/* try all channels in operating class 115 */
|
|
for (i = 0; i < 4; i++) {
|
|
params->freq = 5180 + i * 20;
|
|
if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
|
|
goto out;
|
|
}
|
|
|
|
/* try all channels in operating class 124 */
|
|
for (i = 0; i < 4; i++) {
|
|
params->freq = 5745 + i * 20;
|
|
if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
|
|
goto out;
|
|
}
|
|
|
|
/* try social channel class 180 channel 2 */
|
|
params->freq = 58320 + 1 * 2160;
|
|
if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
|
|
goto out;
|
|
|
|
/* try all channels in reg. class 180 */
|
|
for (i = 0; i < 4; i++) {
|
|
params->freq = 58320 + i * 2160;
|
|
if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
|
|
goto out;
|
|
}
|
|
|
|
/* try some random selection of the social channels */
|
|
if (os_get_random((u8 *) &r, sizeof(r)) < 0)
|
|
return;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
params->freq = 2412 + ((r + i) % 3) * 25;
|
|
if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
|
|
goto out;
|
|
}
|
|
|
|
/* try all other channels in operating class 81 */
|
|
for (i = 0; i < 11; i++) {
|
|
params->freq = 2412 + i * 5;
|
|
|
|
/* skip social channels; covered in the previous loop */
|
|
if (params->freq == 2412 ||
|
|
params->freq == 2437 ||
|
|
params->freq == 2462)
|
|
continue;
|
|
|
|
if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
|
|
goto out;
|
|
}
|
|
|
|
params->freq = 0;
|
|
wpa_printf(MSG_DEBUG, "P2P: No 2.4, 5, or 60 GHz channel allowed");
|
|
return;
|
|
out:
|
|
wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference known)",
|
|
params->freq);
|
|
}
|
|
|
|
|
|
static int wpas_same_band(int freq1, int freq2)
|
|
{
|
|
enum hostapd_hw_mode mode1, mode2;
|
|
u8 chan1, chan2;
|
|
|
|
mode1 = ieee80211_freq_to_chan(freq1, &chan1);
|
|
mode2 = ieee80211_freq_to_chan(freq2, &chan2);
|
|
if (mode1 == NUM_HOSTAPD_MODES)
|
|
return 0;
|
|
return mode1 == mode2;
|
|
}
|
|
|
|
|
|
static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
|
|
struct p2p_go_neg_results *params,
|
|
int freq, int vht_center_freq2, int ht40,
|
|
int vht, int max_oper_chwidth, int he,
|
|
int edmg,
|
|
const struct p2p_channels *channels)
|
|
{
|
|
struct wpa_used_freq_data *freqs;
|
|
unsigned int cand;
|
|
unsigned int num, i;
|
|
int ignore_no_freqs = 0;
|
|
int unused_channels = wpas_p2p_num_unused_channels(wpa_s) > 0;
|
|
|
|
os_memset(params, 0, sizeof(*params));
|
|
params->role_go = 1;
|
|
params->ht40 = ht40;
|
|
params->vht = vht;
|
|
params->he = he;
|
|
params->max_oper_chwidth = max_oper_chwidth;
|
|
params->vht_center_freq2 = vht_center_freq2;
|
|
params->edmg = edmg;
|
|
|
|
freqs = os_calloc(wpa_s->num_multichan_concurrent,
|
|
sizeof(struct wpa_used_freq_data));
|
|
if (!freqs)
|
|
return -1;
|
|
|
|
num = get_shared_radio_freqs_data(wpa_s, freqs,
|
|
wpa_s->num_multichan_concurrent);
|
|
|
|
if (wpa_s->current_ssid &&
|
|
wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO &&
|
|
wpa_s->wpa_state == WPA_COMPLETED) {
|
|
wpa_printf(MSG_DEBUG, "P2P: %s called for an active GO",
|
|
__func__);
|
|
|
|
/*
|
|
* If the frequency selection is done for an active P2P GO that
|
|
* is not sharing a frequency, allow to select a new frequency
|
|
* even if there are no unused frequencies as we are about to
|
|
* move the P2P GO so its frequency can be re-used.
|
|
*/
|
|
for (i = 0; i < num; i++) {
|
|
if (freqs[i].freq == wpa_s->current_ssid->frequency &&
|
|
freqs[i].flags == 0) {
|
|
ignore_no_freqs = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Try to use EDMG channel */
|
|
if (params->edmg) {
|
|
if (wpas_p2p_try_edmg_channel(wpa_s, params) == 0)
|
|
goto success;
|
|
params->edmg = 0;
|
|
}
|
|
|
|
/* try using the forced freq */
|
|
if (freq) {
|
|
if (wpas_p2p_disallowed_freq(wpa_s->global, freq) ||
|
|
!freq_included(wpa_s, channels, freq)) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Forced GO freq %d MHz disallowed",
|
|
freq);
|
|
goto fail;
|
|
}
|
|
if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
|
|
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
|
|
ieee80211_is_dfs(freq, wpa_s->hw.modes,
|
|
wpa_s->hw.num_modes)) {
|
|
/*
|
|
* If freq is a DFS channel and DFS is offloaded
|
|
* to the driver, allow P2P GO to use it.
|
|
*/
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: %s: The forced channel for GO (%u MHz) requires DFS and DFS is offloaded",
|
|
__func__, freq);
|
|
} else {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: The forced channel for GO (%u MHz) is not supported for P2P uses",
|
|
freq);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < num; i++) {
|
|
if (freqs[i].freq == freq) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: forced freq (%d MHz) is also shared",
|
|
freq);
|
|
params->freq = freq;
|
|
goto success;
|
|
}
|
|
}
|
|
|
|
if (!ignore_no_freqs && !unused_channels) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Cannot force GO on freq (%d MHz) as all the channels are in use",
|
|
freq);
|
|
goto fail;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: force GO freq (%d MHz) on a free channel",
|
|
freq);
|
|
params->freq = freq;
|
|
goto success;
|
|
}
|
|
|
|
/* consider using one of the shared frequencies */
|
|
if (num &&
|
|
(!wpa_s->conf->p2p_ignore_shared_freq || !unused_channels)) {
|
|
cand = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
|
|
if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Use shared freq (%d MHz) for GO",
|
|
cand);
|
|
params->freq = cand;
|
|
goto success;
|
|
}
|
|
|
|
/* try using one of the shared freqs */
|
|
for (i = 0; i < num; i++) {
|
|
if (wpas_p2p_supported_freq_go(wpa_s, channels,
|
|
freqs[i].freq)) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Use shared freq (%d MHz) for GO",
|
|
freqs[i].freq);
|
|
params->freq = freqs[i].freq;
|
|
goto success;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ignore_no_freqs && !unused_channels) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Cannot force GO on any of the channels we are already using");
|
|
goto fail;
|
|
}
|
|
|
|
/* try using the setting from the configuration file */
|
|
if (wpa_s->conf->p2p_oper_reg_class == 81 &&
|
|
wpa_s->conf->p2p_oper_channel >= 1 &&
|
|
wpa_s->conf->p2p_oper_channel <= 11 &&
|
|
wpas_p2p_supported_freq_go(
|
|
wpa_s, channels,
|
|
2407 + 5 * wpa_s->conf->p2p_oper_channel)) {
|
|
params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel;
|
|
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
|
|
"frequency %d MHz", params->freq);
|
|
goto success;
|
|
}
|
|
|
|
if ((wpa_s->conf->p2p_oper_reg_class == 115 ||
|
|
wpa_s->conf->p2p_oper_reg_class == 116 ||
|
|
wpa_s->conf->p2p_oper_reg_class == 117 ||
|
|
wpa_s->conf->p2p_oper_reg_class == 124 ||
|
|
wpa_s->conf->p2p_oper_reg_class == 125 ||
|
|
wpa_s->conf->p2p_oper_reg_class == 126 ||
|
|
wpa_s->conf->p2p_oper_reg_class == 127) &&
|
|
wpas_p2p_supported_freq_go(wpa_s, channels,
|
|
5000 +
|
|
5 * wpa_s->conf->p2p_oper_channel)) {
|
|
params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel;
|
|
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
|
|
"frequency %d MHz", params->freq);
|
|
goto success;
|
|
}
|
|
|
|
/* Try using best channels */
|
|
if (wpa_s->conf->p2p_oper_channel == 0 &&
|
|
wpa_s->best_overall_freq > 0 &&
|
|
wpas_p2p_supported_freq_go(wpa_s, channels,
|
|
wpa_s->best_overall_freq)) {
|
|
params->freq = wpa_s->best_overall_freq;
|
|
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall "
|
|
"channel %d MHz", params->freq);
|
|
goto success;
|
|
}
|
|
|
|
if (wpa_s->conf->p2p_oper_channel == 0 &&
|
|
wpa_s->best_24_freq > 0 &&
|
|
wpas_p2p_supported_freq_go(wpa_s, channels,
|
|
wpa_s->best_24_freq)) {
|
|
params->freq = wpa_s->best_24_freq;
|
|
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz "
|
|
"channel %d MHz", params->freq);
|
|
goto success;
|
|
}
|
|
|
|
if (wpa_s->conf->p2p_oper_channel == 0 &&
|
|
wpa_s->best_5_freq > 0 &&
|
|
wpas_p2p_supported_freq_go(wpa_s, channels,
|
|
wpa_s->best_5_freq)) {
|
|
params->freq = wpa_s->best_5_freq;
|
|
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz "
|
|
"channel %d MHz", params->freq);
|
|
goto success;
|
|
}
|
|
|
|
/* try using preferred channels */
|
|
cand = p2p_get_pref_freq(wpa_s->global->p2p, channels);
|
|
if (cand && wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
|
|
params->freq = cand;
|
|
wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred "
|
|
"channels", params->freq);
|
|
goto success;
|
|
}
|
|
|
|
/* Try using a channel that allows VHT to be used with 80 MHz */
|
|
if (wpa_s->hw.modes && wpa_s->p2p_group_common_freqs) {
|
|
for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
|
|
enum hostapd_hw_mode mode;
|
|
struct hostapd_hw_modes *hwmode;
|
|
u8 chan;
|
|
u8 op_class;
|
|
|
|
cand = wpa_s->p2p_group_common_freqs[i];
|
|
op_class = is_6ghz_freq(cand) ? 133 : 128;
|
|
mode = ieee80211_freq_to_chan(cand, &chan);
|
|
hwmode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
|
|
mode, is_6ghz_freq(cand));
|
|
if (!hwmode ||
|
|
wpas_p2p_verify_channel(wpa_s, hwmode, op_class,
|
|
chan, BW80) != ALLOWED)
|
|
continue;
|
|
if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
|
|
params->freq = cand;
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Use freq %d MHz common with the peer and allowing VHT80",
|
|
params->freq);
|
|
goto success;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Try using a channel that allows HT to be used with 40 MHz on the same
|
|
* band so that CSA can be used */
|
|
if (wpa_s->current_ssid && wpa_s->hw.modes &&
|
|
wpa_s->p2p_group_common_freqs) {
|
|
for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
|
|
enum hostapd_hw_mode mode;
|
|
struct hostapd_hw_modes *hwmode;
|
|
u8 chan, op_class;
|
|
bool is_6ghz, supported = false;
|
|
|
|
is_6ghz = is_6ghz_freq(cand);
|
|
cand = wpa_s->p2p_group_common_freqs[i];
|
|
mode = ieee80211_freq_to_chan(cand, &chan);
|
|
hwmode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
|
|
mode, is_6ghz);
|
|
if (!wpas_same_band(wpa_s->current_ssid->frequency,
|
|
cand) ||
|
|
!hwmode)
|
|
continue;
|
|
if (is_6ghz &&
|
|
wpas_p2p_verify_channel(wpa_s, hwmode, 132, chan,
|
|
BW40) == ALLOWED)
|
|
supported = true;
|
|
|
|
if (!is_6ghz &&
|
|
ieee80211_freq_to_channel_ext(
|
|
cand, -1, CONF_OPER_CHWIDTH_USE_HT,
|
|
&op_class, &chan) != NUM_HOSTAPD_MODES &&
|
|
wpas_p2p_verify_channel(
|
|
wpa_s, hwmode, op_class, chan,
|
|
BW40MINUS) == ALLOWED)
|
|
supported = true;
|
|
|
|
if (!supported && !is_6ghz &&
|
|
ieee80211_freq_to_channel_ext(
|
|
cand, 1, CONF_OPER_CHWIDTH_USE_HT,
|
|
&op_class, &chan) != NUM_HOSTAPD_MODES &&
|
|
wpas_p2p_verify_channel(
|
|
wpa_s, hwmode, op_class, chan,
|
|
BW40PLUS) == ALLOWED)
|
|
supported = true;
|
|
|
|
if (!supported)
|
|
continue;
|
|
|
|
if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
|
|
params->freq = cand;
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Use freq %d MHz common with the peer, allowing HT40, and maintaining same band",
|
|
params->freq);
|
|
goto success;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Try using one of the group common freqs on the same band so that CSA
|
|
* can be used */
|
|
if (wpa_s->current_ssid && wpa_s->p2p_group_common_freqs) {
|
|
for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
|
|
cand = wpa_s->p2p_group_common_freqs[i];
|
|
if (!wpas_same_band(wpa_s->current_ssid->frequency,
|
|
cand))
|
|
continue;
|
|
if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
|
|
params->freq = cand;
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Use freq %d MHz common with the peer and maintaining same band",
|
|
params->freq);
|
|
goto success;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Try using one of the group common freqs */
|
|
if (wpa_s->p2p_group_common_freqs) {
|
|
for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
|
|
cand = wpa_s->p2p_group_common_freqs[i];
|
|
if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
|
|
params->freq = cand;
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Use freq %d MHz common with the peer",
|
|
params->freq);
|
|
goto success;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* no preference, select some channel */
|
|
wpas_p2p_select_go_freq_no_pref(wpa_s, params, channels);
|
|
|
|
if (params->freq == 0) {
|
|
wpa_printf(MSG_DEBUG, "P2P: did not find a freq for GO use");
|
|
goto fail;
|
|
}
|
|
|
|
success:
|
|
os_free(freqs);
|
|
return 0;
|
|
fail:
|
|
os_free(freqs);
|
|
return -1;
|
|
}
|
|
|
|
|
|
static struct wpa_supplicant *
|
|
wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
|
|
int go)
|
|
{
|
|
struct wpa_supplicant *group_wpa_s;
|
|
|
|
if (!wpas_p2p_create_iface(wpa_s)) {
|
|
if (wpa_s->p2p_mgmt) {
|
|
/*
|
|
* We may be called on the p2p_dev interface which
|
|
* cannot be used for group operations, so always use
|
|
* the primary interface.
|
|
*/
|
|
wpa_s->parent->p2pdev = wpa_s;
|
|
wpa_s = wpa_s->parent;
|
|
}
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Use primary interface for group operations");
|
|
wpa_s->p2p_first_connection_timeout = 0;
|
|
if (wpa_s != wpa_s->p2pdev)
|
|
wpas_p2p_clone_config(wpa_s, wpa_s->p2pdev);
|
|
return wpa_s;
|
|
}
|
|
|
|
if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO :
|
|
WPA_IF_P2P_CLIENT) < 0) {
|
|
wpa_msg_global(wpa_s, MSG_ERROR,
|
|
"P2P: Failed to add group interface");
|
|
return NULL;
|
|
}
|
|
group_wpa_s = wpas_p2p_init_group_interface(wpa_s, go);
|
|
if (group_wpa_s == NULL) {
|
|
wpa_msg_global(wpa_s, MSG_ERROR,
|
|
"P2P: Failed to initialize group interface");
|
|
wpas_p2p_remove_pending_group_interface(wpa_s);
|
|
return NULL;
|
|
}
|
|
|
|
if (go && wpa_s->p2p_go_do_acs) {
|
|
group_wpa_s->p2p_go_do_acs = wpa_s->p2p_go_do_acs;
|
|
group_wpa_s->p2p_go_acs_band = wpa_s->p2p_go_acs_band;
|
|
wpa_s->p2p_go_do_acs = 0;
|
|
}
|
|
|
|
if (go && wpa_s->p2p_go_allow_dfs) {
|
|
group_wpa_s->p2p_go_allow_dfs = wpa_s->p2p_go_allow_dfs;
|
|
wpa_s->p2p_go_allow_dfs = 0;
|
|
}
|
|
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use separate group interface %s",
|
|
group_wpa_s->ifname);
|
|
group_wpa_s->p2p_first_connection_timeout = 0;
|
|
return group_wpa_s;
|
|
}
|
|
|
|
|
|
/**
|
|
* wpas_p2p_group_add - Add a new P2P group with local end as Group Owner
|
|
* @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
|
|
* @persistent_group: Whether to create a persistent group
|
|
* @freq: Frequency for the group or 0 to indicate no hardcoding
|
|
* @vht_center_freq2: segment_1 center frequency for GO operating in VHT 80P80
|
|
* @ht40: Start GO with 40 MHz channel width
|
|
* @vht: Start GO with VHT support
|
|
* @vht_chwidth: channel bandwidth for GO operating with VHT support
|
|
* @edmg: Start GO with EDMG support
|
|
* @allow_6ghz: Allow P2P group creation on a 6 GHz channel
|
|
* Returns: 0 on success, -1 on failure
|
|
*
|
|
* This function creates a new P2P group with the local end as the Group Owner,
|
|
* i.e., without using Group Owner Negotiation.
|
|
*/
|
|
int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
|
|
int freq, int vht_center_freq2, int ht40, int vht,
|
|
int max_oper_chwidth, int he, int edmg,
|
|
bool allow_6ghz)
|
|
{
|
|
struct p2p_go_neg_results params;
|
|
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return -1;
|
|
if (wpas_p2p_check_6ghz(wpa_s, NULL, allow_6ghz, freq))
|
|
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);
|
|
|
|
if (!wpa_s->p2p_go_do_acs) {
|
|
freq = wpas_p2p_select_go_freq(wpa_s, freq);
|
|
if (freq < 0)
|
|
return -1;
|
|
}
|
|
|
|
if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, vht_center_freq2,
|
|
ht40, vht, max_oper_chwidth, he, edmg,
|
|
NULL))
|
|
return -1;
|
|
|
|
p2p_go_params(wpa_s->global->p2p, ¶ms);
|
|
params.persistent_group = persistent_group;
|
|
|
|
wpa_s = wpas_p2p_get_group_iface(wpa_s, 0, 1);
|
|
if (wpa_s == NULL)
|
|
return -1;
|
|
wpas_start_wps_go(wpa_s, ¶ms, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
|
|
struct wpa_ssid *params, int addr_allocated,
|
|
int freq, int force_scan)
|
|
{
|
|
struct wpa_ssid *ssid;
|
|
int other_iface_found = 0;
|
|
struct wpa_supplicant *ifs;
|
|
|
|
wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0);
|
|
if (wpa_s == NULL)
|
|
return -1;
|
|
if (force_scan)
|
|
os_get_reltime(&wpa_s->scan_min_time);
|
|
wpa_s->p2p_last_4way_hs_fail = NULL;
|
|
|
|
wpa_supplicant_ap_deinit(wpa_s);
|
|
|
|
ssid = wpa_config_add_network(wpa_s->conf);
|
|
if (ssid == NULL)
|
|
return -1;
|
|
os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
|
|
wpa_config_set_network_defaults(ssid);
|
|
ssid->temporary = 1;
|
|
ssid->proto = WPA_PROTO_RSN;
|
|
ssid->pbss = params->pbss;
|
|
ssid->pairwise_cipher = params->pbss ? WPA_CIPHER_GCMP :
|
|
WPA_CIPHER_CCMP;
|
|
ssid->group_cipher = params->pbss ? WPA_CIPHER_GCMP : WPA_CIPHER_CCMP;
|
|
ssid->key_mgmt = WPA_KEY_MGMT_PSK;
|
|
ssid->ssid = os_malloc(params->ssid_len);
|
|
if (ssid->ssid == NULL) {
|
|
wpa_config_remove_network(wpa_s->conf, ssid->id);
|
|
return -1;
|
|
}
|
|
os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
|
|
ssid->ssid_len = params->ssid_len;
|
|
ssid->p2p_group = 1;
|
|
ssid->export_keys = 1;
|
|
if (params->psk_set) {
|
|
os_memcpy(ssid->psk, params->psk, 32);
|
|
ssid->psk_set = 1;
|
|
}
|
|
if (params->passphrase)
|
|
ssid->passphrase = os_strdup(params->passphrase);
|
|
|
|
wpa_s->show_group_started = 1;
|
|
wpa_s->p2p_in_invitation = 1;
|
|
wpa_s->p2p_invite_go_freq = freq;
|
|
wpa_s->p2p_go_group_formation_completed = 0;
|
|
wpa_s->global->p2p_group_formation = wpa_s;
|
|
|
|
/*
|
|
* Get latest scan results from driver in case cached scan results from
|
|
* interfaces on the same wiphy allow us to skip the next scan by fast
|
|
* associating. Also update the scan time to the most recent scan result
|
|
* fetch time on the same radio so it reflects the actual time the last
|
|
* scan result event occurred.
|
|
*/
|
|
wpa_supplicant_update_scan_results(wpa_s);
|
|
dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
|
|
radio_list) {
|
|
if (ifs == wpa_s)
|
|
continue;
|
|
if (!other_iface_found || os_reltime_before(&wpa_s->last_scan,
|
|
&ifs->last_scan)) {
|
|
other_iface_found = 1;
|
|
wpa_s->last_scan.sec = ifs->last_scan.sec;
|
|
wpa_s->last_scan.usec = ifs->last_scan.usec;
|
|
}
|
|
}
|
|
|
|
eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev,
|
|
NULL);
|
|
eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
|
|
wpas_p2p_group_formation_timeout,
|
|
wpa_s->p2pdev, NULL);
|
|
wpa_supplicant_select_network(wpa_s, ssid);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
|
|
struct wpa_ssid *ssid, int addr_allocated,
|
|
int force_freq, int neg_freq,
|
|
int vht_center_freq2, int ht40,
|
|
int vht, int max_oper_chwidth, int he,
|
|
int edmg,
|
|
const struct p2p_channels *channels,
|
|
int connection_timeout, int force_scan,
|
|
bool allow_6ghz)
|
|
{
|
|
struct p2p_go_neg_results params;
|
|
int go = 0, freq;
|
|
|
|
if (ssid->disabled != 2 || ssid->ssid == NULL)
|
|
return -1;
|
|
|
|
if (wpas_get_p2p_group(wpa_s, ssid->ssid, ssid->ssid_len, &go) &&
|
|
go == (ssid->mode == WPAS_MODE_P2P_GO)) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Requested persistent group is "
|
|
"already running");
|
|
if (go == 0 &&
|
|
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
|
|
wpa_s->p2pdev, NULL)) {
|
|
/*
|
|
* This can happen if Invitation Response frame was lost
|
|
* and the peer (GO of a persistent group) tries to
|
|
* invite us again. Reschedule the timeout to avoid
|
|
* terminating the wait for the connection too early
|
|
* since we now know that the peer is still trying to
|
|
* invite us instead of having already started the GO.
|
|
*/
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Reschedule group formation timeout since peer is still trying to invite us");
|
|
eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
|
|
wpas_p2p_group_formation_timeout,
|
|
wpa_s->p2pdev, NULL);
|
|
}
|
|
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);
|
|
|
|
wpa_s->p2p_fallback_to_go_neg = 0;
|
|
|
|
if (ssid->mode == WPAS_MODE_P2P_GO) {
|
|
if (force_freq > 0) {
|
|
freq = wpas_p2p_select_go_freq(wpa_s, force_freq);
|
|
if (freq < 0)
|
|
return -1;
|
|
} else {
|
|
freq = wpas_p2p_select_go_freq(wpa_s, neg_freq);
|
|
if (freq < 0 ||
|
|
(freq > 0 && !freq_included(wpa_s, channels, freq)))
|
|
freq = 0;
|
|
}
|
|
} else if (ssid->mode == WPAS_MODE_INFRA) {
|
|
freq = neg_freq;
|
|
if (freq <= 0 || !freq_included(wpa_s, channels, freq)) {
|
|
struct os_reltime now;
|
|
struct wpa_bss *bss =
|
|
wpa_bss_get_p2p_dev_addr(wpa_s, ssid->bssid);
|
|
|
|
os_get_reltime(&now);
|
|
if (bss &&
|
|
!os_reltime_expired(&now, &bss->last_update, 5) &&
|
|
freq_included(wpa_s, channels, bss->freq))
|
|
freq = bss->freq;
|
|
else
|
|
freq = 0;
|
|
}
|
|
|
|
return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq,
|
|
force_scan);
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, vht_center_freq2,
|
|
ht40, vht, max_oper_chwidth, he, edmg,
|
|
channels))
|
|
return -1;
|
|
|
|
params.role_go = 1;
|
|
params.psk_set = ssid->psk_set;
|
|
if (params.psk_set)
|
|
os_memcpy(params.psk, ssid->psk, sizeof(params.psk));
|
|
if (ssid->passphrase) {
|
|
if (os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) {
|
|
wpa_printf(MSG_ERROR, "P2P: Invalid passphrase in "
|
|
"persistent group");
|
|
return -1;
|
|
}
|
|
os_strlcpy(params.passphrase, ssid->passphrase,
|
|
sizeof(params.passphrase));
|
|
}
|
|
os_memcpy(params.ssid, ssid->ssid, ssid->ssid_len);
|
|
params.ssid_len = ssid->ssid_len;
|
|
params.persistent_group = 1;
|
|
|
|
wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 1);
|
|
if (wpa_s == NULL)
|
|
return -1;
|
|
|
|
p2p_channels_to_freqs(channels, params.freq_list, P2P_MAX_CHANNELS);
|
|
|
|
wpa_s->p2p_first_connection_timeout = connection_timeout;
|
|
wpas_start_wps_go(wpa_s, ¶ms, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void wpas_p2p_ie_update(void *ctx, struct wpabuf *beacon_ies,
|
|
struct wpabuf *proberesp_ies)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
if (wpa_s->ap_iface) {
|
|
struct hostapd_data *hapd = wpa_s->ap_iface->bss[0];
|
|
if (!(hapd->conf->p2p & P2P_GROUP_OWNER)) {
|
|
wpabuf_free(beacon_ies);
|
|
wpabuf_free(proberesp_ies);
|
|
return;
|
|
}
|
|
if (beacon_ies) {
|
|
wpabuf_free(hapd->p2p_beacon_ie);
|
|
hapd->p2p_beacon_ie = beacon_ies;
|
|
}
|
|
wpabuf_free(hapd->p2p_probe_resp_ie);
|
|
hapd->p2p_probe_resp_ie = proberesp_ies;
|
|
} else {
|
|
wpabuf_free(beacon_ies);
|
|
wpabuf_free(proberesp_ies);
|
|
}
|
|
wpa_supplicant_ap_update_beacon(wpa_s);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_idle_update(void *ctx, int idle)
|
|
{
|
|
struct wpa_supplicant *wpa_s = ctx;
|
|
if (!wpa_s->ap_iface)
|
|
return;
|
|
wpa_printf(MSG_DEBUG, "P2P: GO - group %sidle", idle ? "" : "not ");
|
|
if (idle) {
|
|
if (wpa_s->global->p2p_fail_on_wps_complete &&
|
|
wpa_s->p2p_in_provisioning) {
|
|
wpas_p2p_grpform_fail_after_wps(wpa_s);
|
|
return;
|
|
}
|
|
wpas_p2p_set_group_idle_timeout(wpa_s);
|
|
} else
|
|
eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
|
|
}
|
|
|
|
|
|
struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
|
|
struct wpa_ssid *ssid)
|
|
{
|
|
struct p2p_group *group;
|
|
struct p2p_group_config *cfg;
|
|
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL ||
|
|
!ssid->p2p_group)
|
|
return NULL;
|
|
|
|
cfg = os_zalloc(sizeof(*cfg));
|
|
if (cfg == NULL)
|
|
return NULL;
|
|
|
|
if (ssid->p2p_persistent_group && wpa_s->conf->persistent_reconnect)
|
|
cfg->persistent_group = 2;
|
|
else if (ssid->p2p_persistent_group)
|
|
cfg->persistent_group = 1;
|
|
os_memcpy(cfg->interface_addr, wpa_s->own_addr, ETH_ALEN);
|
|
if (wpa_s->max_stations &&
|
|
wpa_s->max_stations < wpa_s->conf->max_num_sta)
|
|
cfg->max_clients = wpa_s->max_stations;
|
|
else
|
|
cfg->max_clients = wpa_s->conf->max_num_sta;
|
|
os_memcpy(cfg->ssid, ssid->ssid, ssid->ssid_len);
|
|
cfg->ssid_len = ssid->ssid_len;
|
|
cfg->freq = ssid->frequency;
|
|
cfg->cb_ctx = wpa_s;
|
|
cfg->ie_update = wpas_p2p_ie_update;
|
|
cfg->idle_update = wpas_p2p_idle_update;
|
|
cfg->ip_addr_alloc = WPA_GET_BE32(wpa_s->p2pdev->conf->ip_addr_start)
|
|
!= 0;
|
|
|
|
group = p2p_group_init(wpa_s->global->p2p, cfg);
|
|
if (group == NULL)
|
|
os_free(cfg);
|
|
if (ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION)
|
|
p2p_group_notif_formation_done(group);
|
|
wpa_s->p2p_group = group;
|
|
return group;
|
|
}
|
|
|
|
|
|
void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
|
|
int registrar)
|
|
{
|
|
struct wpa_ssid *ssid = wpa_s->current_ssid;
|
|
|
|
if (!wpa_s->p2p_in_provisioning) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Ignore WPS success event - P2P "
|
|
"provisioning not in progress");
|
|
return;
|
|
}
|
|
|
|
if (ssid && ssid->mode == WPAS_MODE_INFRA) {
|
|
u8 go_dev_addr[ETH_ALEN];
|
|
os_memcpy(go_dev_addr, wpa_s->bssid, ETH_ALEN);
|
|
wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid,
|
|
ssid->ssid_len);
|
|
/* Clear any stored provisioning info */
|
|
p2p_clear_provisioning_info(wpa_s->global->p2p, go_dev_addr);
|
|
}
|
|
|
|
eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev,
|
|
NULL);
|
|
wpa_s->p2p_go_group_formation_completed = 1;
|
|
if (ssid && ssid->mode == WPAS_MODE_INFRA) {
|
|
/*
|
|
* Use a separate timeout for initial data connection to
|
|
* complete to allow the group to be removed automatically if
|
|
* something goes wrong in this step before the P2P group idle
|
|
* timeout mechanism is taken into use.
|
|
*/
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Re-start group formation timeout (%d seconds) as client for initial connection",
|
|
P2P_MAX_INITIAL_CONN_WAIT);
|
|
eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
|
|
wpas_p2p_group_formation_timeout,
|
|
wpa_s->p2pdev, NULL);
|
|
/* Complete group formation on successful data connection. */
|
|
wpa_s->p2p_go_group_formation_completed = 0;
|
|
} else if (ssid) {
|
|
/*
|
|
* Use a separate timeout for initial data connection to
|
|
* complete to allow the group to be removed automatically if
|
|
* the client does not complete data connection successfully.
|
|
*/
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Re-start group formation timeout (%d seconds) as GO for initial connection",
|
|
P2P_MAX_INITIAL_CONN_WAIT_GO);
|
|
eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT_GO, 0,
|
|
wpas_p2p_group_formation_timeout,
|
|
wpa_s->p2pdev, NULL);
|
|
/*
|
|
* Complete group formation on first successful data connection
|
|
*/
|
|
wpa_s->p2p_go_group_formation_completed = 0;
|
|
}
|
|
if (wpa_s->global->p2p)
|
|
p2p_wps_success_cb(wpa_s->global->p2p, peer_addr);
|
|
wpas_group_formation_completed(wpa_s, 1, 0);
|
|
}
|
|
|
|
|
|
void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
|
|
struct wps_event_fail *fail)
|
|
{
|
|
if (!wpa_s->p2p_in_provisioning) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Ignore WPS fail event - P2P "
|
|
"provisioning not in progress");
|
|
return;
|
|
}
|
|
|
|
if (wpa_s->go_params) {
|
|
p2p_clear_provisioning_info(
|
|
wpa_s->global->p2p,
|
|
wpa_s->go_params->peer_device_addr);
|
|
}
|
|
|
|
wpas_notify_p2p_wps_failed(wpa_s, fail);
|
|
|
|
if (wpa_s == wpa_s->global->p2p_group_formation) {
|
|
/*
|
|
* Allow some time for the failed WPS negotiation exchange to
|
|
* complete, but remove the group since group formation cannot
|
|
* succeed after provisioning failure.
|
|
*/
|
|
wpa_printf(MSG_DEBUG, "P2P: WPS step failed during group formation - reject connection from timeout");
|
|
wpa_s->global->p2p_fail_on_wps_complete = 1;
|
|
eloop_deplete_timeout(0, 50000,
|
|
wpas_p2p_group_formation_timeout,
|
|
wpa_s->p2pdev, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s)
|
|
{
|
|
if (!wpa_s->global->p2p_fail_on_wps_complete ||
|
|
!wpa_s->p2p_in_provisioning)
|
|
return 0;
|
|
|
|
wpas_p2p_grpform_fail_after_wps(wpa_s);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
|
|
const char *config_method,
|
|
enum wpas_p2p_prov_disc_use use,
|
|
struct p2ps_provision *p2ps_prov)
|
|
{
|
|
u16 config_methods;
|
|
|
|
wpa_s->global->pending_p2ps_group = 0;
|
|
wpa_s->global->pending_p2ps_group_freq = 0;
|
|
wpa_s->p2p_fallback_to_go_neg = 0;
|
|
wpa_s->pending_pd_use = NORMAL_PD;
|
|
if (p2ps_prov && use == WPAS_P2P_PD_FOR_ASP) {
|
|
p2ps_prov->conncap = p2ps_group_capability(
|
|
wpa_s, P2PS_SETUP_NONE, p2ps_prov->role,
|
|
&p2ps_prov->force_freq, &p2ps_prov->pref_freq);
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: %s conncap: %d - ASP parsed: %x %x %d %s",
|
|
__func__, p2ps_prov->conncap,
|
|
p2ps_prov->adv_id, p2ps_prov->conncap,
|
|
p2ps_prov->status, p2ps_prov->info);
|
|
|
|
config_methods = 0;
|
|
} else if (os_strncmp(config_method, "display", 7) == 0)
|
|
config_methods = WPS_CONFIG_DISPLAY;
|
|
else if (os_strncmp(config_method, "keypad", 6) == 0)
|
|
config_methods = WPS_CONFIG_KEYPAD;
|
|
else if (os_strncmp(config_method, "pbc", 3) == 0 ||
|
|
os_strncmp(config_method, "pushbutton", 10) == 0)
|
|
config_methods = WPS_CONFIG_PUSHBUTTON;
|
|
else {
|
|
wpa_printf(MSG_DEBUG, "P2P: Unknown config method");
|
|
os_free(p2ps_prov);
|
|
return -1;
|
|
}
|
|
|
|
if (use == WPAS_P2P_PD_AUTO) {
|
|
os_memcpy(wpa_s->pending_join_dev_addr, peer_addr, ETH_ALEN);
|
|
wpa_s->pending_pd_config_methods = config_methods;
|
|
wpa_s->p2p_auto_pd = 1;
|
|
wpa_s->p2p_auto_join = 0;
|
|
wpa_s->pending_pd_before_join = 0;
|
|
wpa_s->auto_pd_scan_retry = 0;
|
|
wpas_p2p_stop_find(wpa_s);
|
|
wpa_s->p2p_join_scan_count = 0;
|
|
os_get_reltime(&wpa_s->p2p_auto_started);
|
|
wpa_printf(MSG_DEBUG, "P2P: Auto PD started at %ld.%06ld",
|
|
wpa_s->p2p_auto_started.sec,
|
|
wpa_s->p2p_auto_started.usec);
|
|
wpas_p2p_join_scan(wpa_s, NULL);
|
|
return 0;
|
|
}
|
|
|
|
if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) {
|
|
os_free(p2ps_prov);
|
|
return -1;
|
|
}
|
|
|
|
return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr, p2ps_prov,
|
|
config_methods, use == WPAS_P2P_PD_FOR_JOIN,
|
|
0, 1);
|
|
}
|
|
|
|
|
|
int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
|
|
char *end)
|
|
{
|
|
return p2p_scan_result_text(ies, ies_len, buf, end);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
|
|
{
|
|
if (!offchannel_pending_action_tx(wpa_s))
|
|
return;
|
|
|
|
if (wpa_s->p2p_send_action_work) {
|
|
wpas_p2p_free_send_action_work(wpa_s);
|
|
eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
|
|
wpa_s, NULL);
|
|
offchannel_send_action_done(wpa_s);
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new "
|
|
"operation request");
|
|
offchannel_clear_pending_action_tx(wpa_s);
|
|
}
|
|
|
|
|
|
int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
|
|
enum p2p_discovery_type type,
|
|
unsigned int num_req_dev_types, const u8 *req_dev_types,
|
|
const u8 *dev_id, unsigned int search_delay,
|
|
u8 seek_cnt, const char **seek_string, int freq,
|
|
bool include_6ghz)
|
|
{
|
|
wpas_p2p_clear_pending_action_tx(wpa_s);
|
|
wpa_s->global->p2p_long_listen = 0;
|
|
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL ||
|
|
wpa_s->p2p_in_provisioning) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Reject p2p_find operation%s%s",
|
|
(wpa_s->global->p2p_disabled || !wpa_s->global->p2p) ?
|
|
" (P2P disabled)" : "",
|
|
wpa_s->p2p_in_provisioning ?
|
|
" (p2p_in_provisioning)" : "");
|
|
return -1;
|
|
}
|
|
|
|
wpa_supplicant_cancel_sched_scan(wpa_s);
|
|
|
|
return p2p_find(wpa_s->global->p2p, timeout, type,
|
|
num_req_dev_types, req_dev_types, dev_id,
|
|
search_delay, seek_cnt, seek_string, freq,
|
|
include_6ghz);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_scan_res_ignore_search(struct wpa_supplicant *wpa_s,
|
|
struct wpa_scan_results *scan_res)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "P2P: Ignore scan results");
|
|
|
|
if (wpa_s->p2p_scan_work) {
|
|
struct wpa_radio_work *work = wpa_s->p2p_scan_work;
|
|
wpa_s->p2p_scan_work = NULL;
|
|
radio_work_done(work);
|
|
}
|
|
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return;
|
|
|
|
/*
|
|
* Indicate that results have been processed so that the P2P module can
|
|
* continue pending tasks.
|
|
*/
|
|
wpas_p2p_scan_res_handled(wpa_s);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s)
|
|
{
|
|
wpas_p2p_clear_pending_action_tx(wpa_s);
|
|
wpa_s->global->p2p_long_listen = 0;
|
|
eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
|
|
eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
|
|
|
|
if (wpa_s->global->p2p)
|
|
p2p_stop_find(wpa_s->global->p2p);
|
|
|
|
if (wpa_s->scan_res_handler == wpas_p2p_scan_res_handler) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Do not consider the scan results after stop_find");
|
|
wpa_s->scan_res_handler = wpas_p2p_scan_res_ignore_search;
|
|
}
|
|
}
|
|
|
|
|
|
void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s)
|
|
{
|
|
wpas_p2p_stop_find_oper(wpa_s);
|
|
if (!wpa_s->global->pending_group_iface_for_p2ps)
|
|
wpas_p2p_remove_pending_group_interface(wpa_s);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx)
|
|
{
|
|
struct wpa_supplicant *wpa_s = eloop_ctx;
|
|
wpa_s->global->p2p_long_listen = 0;
|
|
}
|
|
|
|
|
|
int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout)
|
|
{
|
|
int res;
|
|
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return -1;
|
|
|
|
if (wpa_s->p2p_lo_started) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Cannot start P2P listen, it is offloaded");
|
|
return -1;
|
|
}
|
|
|
|
wpa_supplicant_cancel_sched_scan(wpa_s);
|
|
wpas_p2p_clear_pending_action_tx(wpa_s);
|
|
|
|
if (timeout == 0) {
|
|
/*
|
|
* This is a request for unlimited Listen state. However, at
|
|
* least for now, this is mapped to a Listen state for one
|
|
* hour.
|
|
*/
|
|
timeout = 3600;
|
|
}
|
|
eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
|
|
wpa_s->global->p2p_long_listen = 0;
|
|
|
|
/*
|
|
* Stop previous find/listen operation to avoid trying to request a new
|
|
* remain-on-channel operation while the driver is still running the
|
|
* previous one.
|
|
*/
|
|
if (wpa_s->global->p2p)
|
|
p2p_stop_find(wpa_s->global->p2p);
|
|
|
|
res = wpas_p2p_listen_start(wpa_s, timeout * 1000);
|
|
if (res == 0 && timeout * 1000 > wpa_s->max_remain_on_chan) {
|
|
wpa_s->global->p2p_long_listen = timeout * 1000;
|
|
eloop_register_timeout(timeout, 0,
|
|
wpas_p2p_long_listen_timeout,
|
|
wpa_s, NULL);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
|
|
u8 *buf, size_t len, int p2p_group)
|
|
{
|
|
struct wpabuf *p2p_ie;
|
|
int ret;
|
|
|
|
if (wpa_s->global->p2p_disabled)
|
|
return -1;
|
|
/*
|
|
* Advertize mandatory cross connection capability even on
|
|
* p2p_disabled=1 interface when associating with a P2P Manager WLAN AP.
|
|
*/
|
|
if (wpa_s->conf->p2p_disabled && p2p_group)
|
|
return -1;
|
|
if (wpa_s->global->p2p == NULL)
|
|
return -1;
|
|
if (bss == NULL)
|
|
return -1;
|
|
|
|
p2p_ie = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
|
|
ret = p2p_assoc_req_ie(wpa_s->global->p2p, bss->bssid, buf, len,
|
|
p2p_group, p2p_ie);
|
|
wpabuf_free(p2p_ie);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
|
|
const u8 *dst, const u8 *bssid,
|
|
const u8 *ie, size_t ie_len,
|
|
unsigned int rx_freq, int ssi_signal)
|
|
{
|
|
if (wpa_s->global->p2p_disabled)
|
|
return 0;
|
|
if (wpa_s->global->p2p == NULL)
|
|
return 0;
|
|
|
|
switch (p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid,
|
|
ie, ie_len, rx_freq, wpa_s->p2p_lo_started)) {
|
|
case P2P_PREQ_NOT_P2P:
|
|
wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len,
|
|
ssi_signal);
|
|
/* fall through */
|
|
case P2P_PREQ_MALFORMED:
|
|
case P2P_PREQ_NOT_LISTEN:
|
|
case P2P_PREQ_NOT_PROCESSED:
|
|
default: /* make gcc happy */
|
|
return 0;
|
|
case P2P_PREQ_PROCESSED:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
|
|
const u8 *sa, const u8 *bssid,
|
|
u8 category, const u8 *data, size_t len, int freq)
|
|
{
|
|
if (wpa_s->global->p2p_disabled)
|
|
return;
|
|
if (wpa_s->global->p2p == NULL)
|
|
return;
|
|
|
|
p2p_rx_action(wpa_s->global->p2p, da, sa, bssid, category, data, len,
|
|
freq);
|
|
}
|
|
|
|
|
|
void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies)
|
|
{
|
|
unsigned int bands;
|
|
|
|
if (wpa_s->global->p2p_disabled)
|
|
return;
|
|
if (wpa_s->global->p2p == NULL)
|
|
return;
|
|
|
|
bands = wpas_get_bands(wpa_s, NULL);
|
|
p2p_scan_ie(wpa_s->global->p2p, ies, NULL, bands);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s)
|
|
{
|
|
p2p_group_deinit(wpa_s->p2p_group);
|
|
wpa_s->p2p_group = NULL;
|
|
|
|
wpa_s->ap_configured_cb = NULL;
|
|
wpa_s->ap_configured_cb_ctx = NULL;
|
|
wpa_s->ap_configured_cb_data = NULL;
|
|
wpa_s->connect_without_scan = NULL;
|
|
}
|
|
|
|
|
|
int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr)
|
|
{
|
|
wpa_s->global->p2p_long_listen = 0;
|
|
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return -1;
|
|
|
|
return p2p_reject(wpa_s->global->p2p, addr);
|
|
}
|
|
|
|
|
|
/* Invite to reinvoke a persistent group */
|
|
int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
|
|
struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
|
|
int vht_center_freq2, int ht40, int vht, int max_chwidth,
|
|
int pref_freq, int he, int edmg, bool allow_6ghz)
|
|
{
|
|
enum p2p_invite_role role;
|
|
u8 *bssid = NULL;
|
|
int force_freq = 0;
|
|
int res;
|
|
int no_pref_freq_given = pref_freq == 0;
|
|
struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS];
|
|
unsigned int size;
|
|
|
|
if (wpas_p2p_check_6ghz(wpa_s, NULL, allow_6ghz, freq))
|
|
return -1;
|
|
|
|
wpa_s->global->p2p_invite_group = NULL;
|
|
if (peer_addr)
|
|
os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN);
|
|
else
|
|
os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
|
|
|
|
wpa_s->p2p_persistent_go_freq = freq;
|
|
wpa_s->p2p_go_ht40 = !!ht40;
|
|
wpa_s->p2p_go_vht = !!vht;
|
|
wpa_s->p2p_go_he = !!he;
|
|
wpa_s->p2p_go_max_oper_chwidth = max_chwidth;
|
|
wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2;
|
|
wpa_s->p2p_go_edmg = !!edmg;
|
|
if (ssid->mode == WPAS_MODE_P2P_GO) {
|
|
role = P2P_INVITE_ROLE_GO;
|
|
if (peer_addr == NULL) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Missing peer "
|
|
"address in invitation command");
|
|
return -1;
|
|
}
|
|
if (wpas_p2p_create_iface(wpa_s)) {
|
|
if (wpas_p2p_add_group_interface(wpa_s,
|
|
WPA_IF_P2P_GO) < 0) {
|
|
wpa_printf(MSG_ERROR, "P2P: Failed to "
|
|
"allocate a new interface for the "
|
|
"group");
|
|
return -1;
|
|
}
|
|
bssid = wpa_s->pending_interface_addr;
|
|
} else if (wpa_s->p2p_mgmt)
|
|
bssid = wpa_s->parent->own_addr;
|
|
else
|
|
bssid = wpa_s->own_addr;
|
|
} else {
|
|
role = P2P_INVITE_ROLE_CLIENT;
|
|
peer_addr = ssid->bssid;
|
|
}
|
|
wpa_s->pending_invite_ssid_id = ssid->id;
|
|
|
|
size = P2P_MAX_PREF_CHANNELS;
|
|
res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
|
|
role == P2P_INVITE_ROLE_GO,
|
|
pref_freq_list, &size);
|
|
if (res)
|
|
return res;
|
|
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return -1;
|
|
|
|
p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size);
|
|
|
|
if (wpa_s->parent->conf->p2p_ignore_shared_freq &&
|
|
no_pref_freq_given && pref_freq > 0 &&
|
|
wpa_s->num_multichan_concurrent > 1 &&
|
|
wpas_p2p_num_unused_channels(wpa_s) > 0) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Ignore own channel preference %d MHz for invitation due to p2p_ignore_shared_freq=1 configuration",
|
|
pref_freq);
|
|
pref_freq = 0;
|
|
}
|
|
|
|
/*
|
|
* Stop any find/listen operations before invitation and possibly
|
|
* connection establishment.
|
|
*/
|
|
wpas_p2p_stop_find_oper(wpa_s);
|
|
|
|
return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
|
|
ssid->ssid, ssid->ssid_len, force_freq, go_dev_addr,
|
|
1, pref_freq, -1);
|
|
}
|
|
|
|
|
|
/* Invite to join an active group */
|
|
int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
|
|
const u8 *peer_addr, const u8 *go_dev_addr,
|
|
bool allow_6ghz)
|
|
{
|
|
struct wpa_global *global = wpa_s->global;
|
|
enum p2p_invite_role role;
|
|
u8 *bssid = NULL;
|
|
struct wpa_ssid *ssid;
|
|
int persistent;
|
|
int freq = 0, force_freq = 0, pref_freq = 0;
|
|
int res;
|
|
struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS];
|
|
unsigned int size;
|
|
|
|
wpa_s->p2p_persistent_go_freq = 0;
|
|
wpa_s->p2p_go_ht40 = 0;
|
|
wpa_s->p2p_go_vht = 0;
|
|
wpa_s->p2p_go_vht_center_freq2 = 0;
|
|
wpa_s->p2p_go_max_oper_chwidth = 0;
|
|
wpa_s->p2p_go_edmg = 0;
|
|
|
|
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
|
|
if (os_strcmp(wpa_s->ifname, ifname) == 0)
|
|
break;
|
|
}
|
|
if (wpa_s == NULL) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Interface '%s' not found", ifname);
|
|
return -1;
|
|
}
|
|
|
|
ssid = wpa_s->current_ssid;
|
|
if (ssid == NULL) {
|
|
wpa_printf(MSG_DEBUG, "P2P: No current SSID to use for "
|
|
"invitation");
|
|
return -1;
|
|
}
|
|
|
|
wpa_s->global->p2p_invite_group = wpa_s;
|
|
persistent = ssid->p2p_persistent_group &&
|
|
wpas_p2p_get_persistent(wpa_s->p2pdev, peer_addr,
|
|
ssid->ssid, ssid->ssid_len);
|
|
|
|
if (ssid->mode == WPAS_MODE_P2P_GO) {
|
|
role = P2P_INVITE_ROLE_ACTIVE_GO;
|
|
bssid = wpa_s->own_addr;
|
|
if (go_dev_addr == NULL)
|
|
go_dev_addr = wpa_s->global->p2p_dev_addr;
|
|
freq = ssid->frequency;
|
|
} else {
|
|
role = P2P_INVITE_ROLE_CLIENT;
|
|
if (wpa_s->wpa_state < WPA_ASSOCIATED) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Not associated - cannot "
|
|
"invite to current group");
|
|
return -1;
|
|
}
|
|
bssid = wpa_s->bssid;
|
|
if (go_dev_addr == NULL &&
|
|
!is_zero_ether_addr(wpa_s->go_dev_addr))
|
|
go_dev_addr = wpa_s->go_dev_addr;
|
|
freq = wpa_s->current_bss ? wpa_s->current_bss->freq :
|
|
(int) wpa_s->assoc_freq;
|
|
}
|
|
wpa_s->p2pdev->pending_invite_ssid_id = -1;
|
|
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return -1;
|
|
if (wpas_p2p_check_6ghz(wpa_s, peer_addr, allow_6ghz, freq))
|
|
return -1;
|
|
|
|
size = P2P_MAX_PREF_CHANNELS;
|
|
res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
|
|
role == P2P_INVITE_ROLE_ACTIVE_GO,
|
|
pref_freq_list, &size);
|
|
if (res)
|
|
return res;
|
|
wpas_p2p_set_own_freq_preference(wpa_s, force_freq);
|
|
|
|
return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
|
|
ssid->ssid, ssid->ssid_len, force_freq,
|
|
go_dev_addr, persistent, pref_freq, -1);
|
|
}
|
|
|
|
|
|
void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
|
|
{
|
|
struct wpa_ssid *ssid = wpa_s->current_ssid;
|
|
u8 go_dev_addr[ETH_ALEN];
|
|
int persistent;
|
|
int freq;
|
|
u8 ip[3 * 4], *ip_ptr = NULL;
|
|
char ip_addr[100];
|
|
|
|
if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) {
|
|
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
|
|
wpa_s->p2pdev, NULL);
|
|
}
|
|
|
|
if (!wpa_s->show_group_started || !ssid)
|
|
return;
|
|
|
|
wpa_s->show_group_started = 0;
|
|
if (!wpa_s->p2p_go_group_formation_completed &&
|
|
wpa_s->global->p2p_group_formation == wpa_s) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Marking group formation completed on client on data connection");
|
|
wpa_s->p2p_go_group_formation_completed = 1;
|
|
wpa_s->global->p2p_group_formation = NULL;
|
|
wpa_s->p2p_in_provisioning = 0;
|
|
wpa_s->p2p_in_invitation = 0;
|
|
}
|
|
|
|
os_memset(go_dev_addr, 0, ETH_ALEN);
|
|
if (ssid->bssid_set)
|
|
os_memcpy(go_dev_addr, ssid->bssid, ETH_ALEN);
|
|
persistent = wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid,
|
|
ssid->ssid_len);
|
|
os_memcpy(wpa_s->go_dev_addr, go_dev_addr, ETH_ALEN);
|
|
|
|
if (wpa_s->global->p2p_group_formation == wpa_s)
|
|
wpa_s->global->p2p_group_formation = NULL;
|
|
|
|
freq = wpa_s->current_bss ? wpa_s->current_bss->freq :
|
|
(int) wpa_s->assoc_freq;
|
|
|
|
ip_addr[0] = '\0';
|
|
if (wpa_sm_get_p2p_ip_addr(wpa_s->wpa, ip) == 0) {
|
|
int res;
|
|
|
|
res = os_snprintf(ip_addr, sizeof(ip_addr),
|
|
" ip_addr=%u.%u.%u.%u "
|
|
"ip_mask=%u.%u.%u.%u go_ip_addr=%u.%u.%u.%u",
|
|
ip[0], ip[1], ip[2], ip[3],
|
|
ip[4], ip[5], ip[6], ip[7],
|
|
ip[8], ip[9], ip[10], ip[11]);
|
|
if (os_snprintf_error(sizeof(ip_addr), res))
|
|
ip_addr[0] = '\0';
|
|
ip_ptr = ip;
|
|
}
|
|
|
|
wpas_p2p_group_started(wpa_s, 0, ssid, freq,
|
|
ssid->passphrase == NULL && ssid->psk_set ?
|
|
ssid->psk : NULL,
|
|
ssid->passphrase, go_dev_addr, persistent,
|
|
ip_addr);
|
|
|
|
if (persistent)
|
|
wpas_p2p_store_persistent_group(wpa_s->p2pdev,
|
|
ssid, go_dev_addr);
|
|
|
|
wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 1, ip_ptr);
|
|
}
|
|
|
|
|
|
int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
|
|
u32 interval1, u32 duration2, u32 interval2)
|
|
{
|
|
int ret;
|
|
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return -1;
|
|
|
|
if (wpa_s->wpa_state < WPA_ASSOCIATED ||
|
|
wpa_s->current_ssid == NULL ||
|
|
wpa_s->current_ssid->mode != WPAS_MODE_INFRA)
|
|
return -1;
|
|
|
|
ret = p2p_presence_req(wpa_s->global->p2p, wpa_s->bssid,
|
|
wpa_s->own_addr, wpa_s->assoc_freq,
|
|
duration1, interval1, duration2, interval2);
|
|
if (ret == 0)
|
|
wpa_s->waiting_presence_resp = 1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
|
|
unsigned int interval)
|
|
{
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return -1;
|
|
|
|
return p2p_ext_listen(wpa_s->global->p2p, period, interval);
|
|
}
|
|
|
|
|
|
static int wpas_p2p_is_client(struct wpa_supplicant *wpa_s)
|
|
{
|
|
if (wpa_s->current_ssid == NULL) {
|
|
/*
|
|
* current_ssid can be cleared when P2P client interface gets
|
|
* disconnected, so assume this interface was used as P2P
|
|
* client.
|
|
*/
|
|
return 1;
|
|
}
|
|
return wpa_s->current_ssid->p2p_group &&
|
|
wpa_s->current_ssid->mode == WPAS_MODE_INFRA;
|
|
}
|
|
|
|
|
|
static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx)
|
|
{
|
|
struct wpa_supplicant *wpa_s = eloop_ctx;
|
|
|
|
if (wpa_s->conf->p2p_group_idle == 0 && !wpas_p2p_is_client(wpa_s)) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Ignore group idle timeout - "
|
|
"disabled");
|
|
return;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Group idle timeout reached - terminate "
|
|
"group");
|
|
wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_IDLE_TIMEOUT);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s)
|
|
{
|
|
int timeout;
|
|
|
|
if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
|
|
wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
|
|
|
|
if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group)
|
|
return;
|
|
|
|
timeout = wpa_s->conf->p2p_group_idle;
|
|
if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
|
|
(timeout == 0 || timeout > P2P_MAX_CLIENT_IDLE))
|
|
timeout = P2P_MAX_CLIENT_IDLE;
|
|
|
|
if (timeout == 0)
|
|
return;
|
|
|
|
if (timeout < 0) {
|
|
if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA)
|
|
timeout = 0; /* special client mode no-timeout */
|
|
else
|
|
return;
|
|
}
|
|
|
|
if (wpa_s->p2p_in_provisioning) {
|
|
/*
|
|
* Use the normal group formation timeout during the
|
|
* provisioning phase to avoid terminating this process too
|
|
* early due to group idle timeout.
|
|
*/
|
|
wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout "
|
|
"during provisioning");
|
|
return;
|
|
}
|
|
|
|
if (wpa_s->show_group_started) {
|
|
/*
|
|
* Use the normal group formation timeout between the end of
|
|
* the provisioning phase and completion of 4-way handshake to
|
|
* avoid terminating this process too early due to group idle
|
|
* timeout.
|
|
*/
|
|
wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout "
|
|
"while waiting for initial 4-way handshake to "
|
|
"complete");
|
|
return;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Set P2P group idle timeout to %u seconds",
|
|
timeout);
|
|
eloop_register_timeout(timeout, 0, wpas_p2p_group_idle_timeout,
|
|
wpa_s, NULL);
|
|
}
|
|
|
|
|
|
/* Returns 1 if the interface was removed */
|
|
int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
|
|
u16 reason_code, const u8 *ie, size_t ie_len,
|
|
int locally_generated)
|
|
{
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return 0;
|
|
|
|
if (!locally_generated)
|
|
p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie,
|
|
ie_len);
|
|
|
|
if (reason_code == WLAN_REASON_DEAUTH_LEAVING && !locally_generated &&
|
|
wpa_s->current_ssid &&
|
|
wpa_s->current_ssid->p2p_group &&
|
|
wpa_s->current_ssid->mode == WPAS_MODE_INFRA) {
|
|
wpa_printf(MSG_DEBUG, "P2P: GO indicated that the P2P Group "
|
|
"session is ending");
|
|
if (wpas_p2p_group_delete(wpa_s,
|
|
P2P_GROUP_REMOVAL_GO_ENDING_SESSION)
|
|
> 0)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
|
|
u16 reason_code, const u8 *ie, size_t ie_len,
|
|
int locally_generated)
|
|
{
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return;
|
|
|
|
if (!locally_generated)
|
|
p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie,
|
|
ie_len);
|
|
}
|
|
|
|
|
|
void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
|
|
{
|
|
struct p2p_data *p2p = wpa_s->global->p2p;
|
|
|
|
if (p2p == NULL)
|
|
return;
|
|
|
|
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE))
|
|
return;
|
|
|
|
if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_NAME)
|
|
p2p_set_dev_name(p2p, wpa_s->conf->device_name);
|
|
|
|
if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE)
|
|
p2p_set_pri_dev_type(p2p, wpa_s->conf->device_type);
|
|
|
|
if (wpa_s->wps &&
|
|
(wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS))
|
|
p2p_set_config_methods(p2p, wpa_s->wps->config_methods);
|
|
|
|
if (wpa_s->wps && (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID))
|
|
p2p_set_uuid(p2p, wpa_s->wps->uuid);
|
|
|
|
if (wpa_s->conf->changed_parameters & CFG_CHANGED_WPS_STRING) {
|
|
p2p_set_manufacturer(p2p, wpa_s->conf->manufacturer);
|
|
p2p_set_model_name(p2p, wpa_s->conf->model_name);
|
|
p2p_set_model_number(p2p, wpa_s->conf->model_number);
|
|
p2p_set_serial_number(p2p, wpa_s->conf->serial_number);
|
|
}
|
|
|
|
if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE)
|
|
p2p_set_sec_dev_types(p2p,
|
|
(void *) wpa_s->conf->sec_device_type,
|
|
wpa_s->conf->num_sec_device_types);
|
|
|
|
if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION) {
|
|
int i;
|
|
p2p_remove_wps_vendor_extensions(p2p);
|
|
for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) {
|
|
if (wpa_s->conf->wps_vendor_ext[i] == NULL)
|
|
continue;
|
|
p2p_add_wps_vendor_extension(
|
|
p2p, wpa_s->conf->wps_vendor_ext[i]);
|
|
}
|
|
}
|
|
|
|
if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) &&
|
|
wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
|
|
char country[3];
|
|
country[0] = wpa_s->conf->country[0];
|
|
country[1] = wpa_s->conf->country[1];
|
|
country[2] = 0x04;
|
|
p2p_set_country(p2p, country);
|
|
}
|
|
|
|
if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_SSID_POSTFIX) {
|
|
p2p_set_ssid_postfix(p2p, (u8 *) wpa_s->conf->p2p_ssid_postfix,
|
|
wpa_s->conf->p2p_ssid_postfix ?
|
|
os_strlen(wpa_s->conf->p2p_ssid_postfix) :
|
|
0);
|
|
}
|
|
|
|
if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_INTRA_BSS)
|
|
p2p_set_intra_bss_dist(p2p, wpa_s->conf->p2p_intra_bss);
|
|
|
|
if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_LISTEN_CHANNEL) {
|
|
u8 reg_class, channel;
|
|
int ret;
|
|
unsigned int r;
|
|
u8 channel_forced;
|
|
|
|
if (wpa_s->conf->p2p_listen_reg_class &&
|
|
wpa_s->conf->p2p_listen_channel) {
|
|
reg_class = wpa_s->conf->p2p_listen_reg_class;
|
|
channel = wpa_s->conf->p2p_listen_channel;
|
|
channel_forced = 1;
|
|
} else {
|
|
reg_class = 81;
|
|
/*
|
|
* Pick one of the social channels randomly as the
|
|
* listen channel.
|
|
*/
|
|
if (os_get_random((u8 *) &r, sizeof(r)) < 0)
|
|
channel = 1;
|
|
else
|
|
channel = 1 + (r % 3) * 5;
|
|
channel_forced = 0;
|
|
}
|
|
ret = p2p_set_listen_channel(p2p, reg_class, channel,
|
|
channel_forced);
|
|
if (ret)
|
|
wpa_printf(MSG_ERROR, "P2P: Own listen channel update "
|
|
"failed: %d", ret);
|
|
}
|
|
if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_OPER_CHANNEL) {
|
|
u8 op_reg_class, op_channel, cfg_op_channel;
|
|
int ret = 0;
|
|
unsigned int r;
|
|
if (wpa_s->conf->p2p_oper_reg_class &&
|
|
wpa_s->conf->p2p_oper_channel) {
|
|
op_reg_class = wpa_s->conf->p2p_oper_reg_class;
|
|
op_channel = wpa_s->conf->p2p_oper_channel;
|
|
cfg_op_channel = 1;
|
|
} else {
|
|
op_reg_class = 81;
|
|
/*
|
|
* Use random operation channel from (1, 6, 11)
|
|
*if no other preference is indicated.
|
|
*/
|
|
if (os_get_random((u8 *) &r, sizeof(r)) < 0)
|
|
op_channel = 1;
|
|
else
|
|
op_channel = 1 + (r % 3) * 5;
|
|
cfg_op_channel = 0;
|
|
}
|
|
ret = p2p_set_oper_channel(p2p, op_reg_class, op_channel,
|
|
cfg_op_channel);
|
|
if (ret)
|
|
wpa_printf(MSG_ERROR, "P2P: Own oper channel update "
|
|
"failed: %d", ret);
|
|
}
|
|
|
|
if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_PREF_CHAN) {
|
|
if (p2p_set_pref_chan(p2p, wpa_s->conf->num_p2p_pref_chan,
|
|
wpa_s->conf->p2p_pref_chan) < 0) {
|
|
wpa_printf(MSG_ERROR, "P2P: Preferred channel list "
|
|
"update failed");
|
|
}
|
|
|
|
if (p2p_set_no_go_freq(p2p, &wpa_s->conf->p2p_no_go_freq) < 0) {
|
|
wpa_printf(MSG_ERROR, "P2P: No GO channel list "
|
|
"update failed");
|
|
}
|
|
}
|
|
|
|
if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_PASSPHRASE_LEN)
|
|
p2p_set_passphrase_len(p2p, wpa_s->conf->p2p_passphrase_len);
|
|
}
|
|
|
|
|
|
int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start,
|
|
int duration)
|
|
{
|
|
if (!wpa_s->ap_iface)
|
|
return -1;
|
|
return hostapd_p2p_set_noa(wpa_s->ap_iface->bss[0], count, start,
|
|
duration);
|
|
}
|
|
|
|
|
|
int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled)
|
|
{
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return -1;
|
|
|
|
wpa_s->global->cross_connection = enabled;
|
|
p2p_set_cross_connect(wpa_s->global->p2p, enabled);
|
|
|
|
if (!enabled) {
|
|
struct wpa_supplicant *iface;
|
|
|
|
for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
|
|
{
|
|
if (iface->cross_connect_enabled == 0)
|
|
continue;
|
|
|
|
iface->cross_connect_enabled = 0;
|
|
iface->cross_connect_in_use = 0;
|
|
wpa_msg_global(iface->p2pdev, MSG_INFO,
|
|
P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
|
|
iface->ifname,
|
|
iface->cross_connect_uplink);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void wpas_p2p_enable_cross_connect(struct wpa_supplicant *uplink)
|
|
{
|
|
struct wpa_supplicant *iface;
|
|
|
|
if (!uplink->global->cross_connection)
|
|
return;
|
|
|
|
for (iface = uplink->global->ifaces; iface; iface = iface->next) {
|
|
if (!iface->cross_connect_enabled)
|
|
continue;
|
|
if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) !=
|
|
0)
|
|
continue;
|
|
if (iface->ap_iface == NULL)
|
|
continue;
|
|
if (iface->cross_connect_in_use)
|
|
continue;
|
|
|
|
iface->cross_connect_in_use = 1;
|
|
wpa_msg_global(iface->p2pdev, MSG_INFO,
|
|
P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s",
|
|
iface->ifname, iface->cross_connect_uplink);
|
|
}
|
|
}
|
|
|
|
|
|
static void wpas_p2p_disable_cross_connect(struct wpa_supplicant *uplink)
|
|
{
|
|
struct wpa_supplicant *iface;
|
|
|
|
for (iface = uplink->global->ifaces; iface; iface = iface->next) {
|
|
if (!iface->cross_connect_enabled)
|
|
continue;
|
|
if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) !=
|
|
0)
|
|
continue;
|
|
if (!iface->cross_connect_in_use)
|
|
continue;
|
|
|
|
wpa_msg_global(iface->p2pdev, MSG_INFO,
|
|
P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
|
|
iface->ifname, iface->cross_connect_uplink);
|
|
iface->cross_connect_in_use = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s)
|
|
{
|
|
if (wpa_s->ap_iface || wpa_s->current_ssid == NULL ||
|
|
wpa_s->current_ssid->mode != WPAS_MODE_INFRA ||
|
|
wpa_s->cross_connect_disallowed)
|
|
wpas_p2p_disable_cross_connect(wpa_s);
|
|
else
|
|
wpas_p2p_enable_cross_connect(wpa_s);
|
|
if (!wpa_s->ap_iface &&
|
|
eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
|
|
wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
|
|
}
|
|
|
|
|
|
void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s)
|
|
{
|
|
wpas_p2p_disable_cross_connect(wpa_s);
|
|
if (!wpa_s->ap_iface &&
|
|
!eloop_is_timeout_registered(wpas_p2p_group_idle_timeout,
|
|
wpa_s, NULL))
|
|
wpas_p2p_set_group_idle_timeout(wpa_s);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s)
|
|
{
|
|
struct wpa_supplicant *iface;
|
|
|
|
if (!wpa_s->global->cross_connection)
|
|
return;
|
|
|
|
for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
|
|
if (iface == wpa_s)
|
|
continue;
|
|
if (iface->drv_flags &
|
|
WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)
|
|
continue;
|
|
if ((iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) &&
|
|
iface != wpa_s->parent)
|
|
continue;
|
|
|
|
wpa_s->cross_connect_enabled = 1;
|
|
os_strlcpy(wpa_s->cross_connect_uplink, iface->ifname,
|
|
sizeof(wpa_s->cross_connect_uplink));
|
|
wpa_printf(MSG_DEBUG, "P2P: Enable cross connection from "
|
|
"%s to %s whenever uplink is available",
|
|
wpa_s->ifname, wpa_s->cross_connect_uplink);
|
|
|
|
if (iface->ap_iface || iface->current_ssid == NULL ||
|
|
iface->current_ssid->mode != WPAS_MODE_INFRA ||
|
|
iface->cross_connect_disallowed ||
|
|
iface->wpa_state != WPA_COMPLETED)
|
|
break;
|
|
|
|
wpa_s->cross_connect_in_use = 1;
|
|
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
|
|
P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s",
|
|
wpa_s->ifname, wpa_s->cross_connect_uplink);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s)
|
|
{
|
|
if (wpa_s->p2p_group_interface != P2P_GROUP_INTERFACE_CLIENT &&
|
|
!wpa_s->p2p_in_provisioning)
|
|
return 0; /* not P2P client operation */
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Terminate connection due to WPS PBC "
|
|
"session overlap");
|
|
if (wpa_s != wpa_s->p2pdev)
|
|
wpa_msg_ctrl(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_OVERLAP);
|
|
wpas_p2p_group_formation_failed(wpa_s, 0);
|
|
return 1;
|
|
}
|
|
|
|
|
|
void wpas_p2p_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx)
|
|
{
|
|
struct wpa_supplicant *wpa_s = eloop_ctx;
|
|
wpas_p2p_notif_pbc_overlap(wpa_s);
|
|
}
|
|
|
|
|
|
void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s,
|
|
enum wpas_p2p_channel_update_trig trig)
|
|
{
|
|
struct p2p_channels chan, cli_chan;
|
|
struct wpa_used_freq_data *freqs = NULL;
|
|
unsigned int num = wpa_s->num_multichan_concurrent;
|
|
|
|
if (wpa_s->global == NULL || wpa_s->global->p2p == NULL)
|
|
return;
|
|
|
|
freqs = os_calloc(num, sizeof(struct wpa_used_freq_data));
|
|
if (!freqs)
|
|
return;
|
|
|
|
num = get_shared_radio_freqs_data(wpa_s, freqs, num);
|
|
|
|
os_memset(&chan, 0, sizeof(chan));
|
|
os_memset(&cli_chan, 0, sizeof(cli_chan));
|
|
if (wpas_p2p_setup_channels(wpa_s, &chan, &cli_chan,
|
|
is_p2p_6ghz_disabled(wpa_s->global->p2p))) {
|
|
wpa_printf(MSG_ERROR, "P2P: Failed to update supported "
|
|
"channel list");
|
|
return;
|
|
}
|
|
|
|
p2p_update_channel_list(wpa_s->global->p2p, &chan, &cli_chan);
|
|
|
|
wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
|
|
|
|
/*
|
|
* The used frequencies map changed, so it is possible that a GO is
|
|
* using a channel that is no longer valid for P2P use. It is also
|
|
* possible that due to policy consideration, it would be preferable to
|
|
* move it to a frequency already used by other station interfaces.
|
|
*/
|
|
wpas_p2p_consider_moving_gos(wpa_s, freqs, num, trig);
|
|
|
|
os_free(freqs);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_scan_res_ignore(struct wpa_supplicant *wpa_s,
|
|
struct wpa_scan_results *scan_res)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "P2P: Ignore scan results");
|
|
}
|
|
|
|
|
|
int wpas_p2p_cancel(struct wpa_supplicant *wpa_s)
|
|
{
|
|
struct wpa_global *global = wpa_s->global;
|
|
int found = 0;
|
|
const u8 *peer;
|
|
|
|
if (global->p2p == NULL)
|
|
return -1;
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Request to cancel group formation");
|
|
|
|
if (wpa_s->pending_interface_name[0] &&
|
|
!is_zero_ether_addr(wpa_s->pending_interface_addr))
|
|
found = 1;
|
|
|
|
peer = p2p_get_go_neg_peer(global->p2p);
|
|
if (peer) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Unauthorize pending GO Neg peer "
|
|
MACSTR, MAC2STR(peer));
|
|
p2p_unauthorize(global->p2p, peer);
|
|
found = 1;
|
|
}
|
|
|
|
if (wpa_s->scan_res_handler == wpas_p2p_scan_res_join) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Stop pending scan for join");
|
|
wpa_s->scan_res_handler = wpas_p2p_scan_res_ignore;
|
|
found = 1;
|
|
}
|
|
|
|
if (wpa_s->pending_pd_before_join) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Stop pending PD before join");
|
|
wpa_s->pending_pd_before_join = 0;
|
|
found = 1;
|
|
}
|
|
|
|
wpas_p2p_stop_find(wpa_s);
|
|
|
|
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
|
|
if (wpa_s == global->p2p_group_formation &&
|
|
(wpa_s->p2p_in_provisioning ||
|
|
wpa_s->parent->pending_interface_type ==
|
|
WPA_IF_P2P_CLIENT)) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Interface %s in group "
|
|
"formation found - cancelling",
|
|
wpa_s->ifname);
|
|
found = 1;
|
|
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
|
|
wpa_s->p2pdev, NULL);
|
|
if (wpa_s->p2p_in_provisioning) {
|
|
wpas_group_formation_completed(wpa_s, 0, 0);
|
|
break;
|
|
}
|
|
wpas_p2p_group_delete(wpa_s,
|
|
P2P_GROUP_REMOVAL_REQUESTED);
|
|
break;
|
|
} else if (wpa_s->p2p_in_invitation) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Interface %s in invitation found - cancelling",
|
|
wpa_s->ifname);
|
|
found = 1;
|
|
wpas_p2p_group_formation_failed(wpa_s, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
wpa_printf(MSG_DEBUG, "P2P: No ongoing group formation found");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s)
|
|
{
|
|
if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group)
|
|
return;
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Remove group due to driver resource not "
|
|
"being available anymore");
|
|
wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_UNAVAILABLE);
|
|
}
|
|
|
|
|
|
void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
|
|
int freq_24, int freq_5, int freq_overall)
|
|
{
|
|
struct p2p_data *p2p = wpa_s->global->p2p;
|
|
if (p2p == NULL)
|
|
return;
|
|
p2p_set_best_channels(p2p, freq_24, freq_5, freq_overall);
|
|
}
|
|
|
|
|
|
int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr)
|
|
{
|
|
u8 peer[ETH_ALEN];
|
|
struct p2p_data *p2p = wpa_s->global->p2p;
|
|
|
|
if (p2p == NULL)
|
|
return -1;
|
|
|
|
if (hwaddr_aton(addr, peer))
|
|
return -1;
|
|
|
|
return p2p_unauthorize(p2p, peer);
|
|
}
|
|
|
|
|
|
/**
|
|
* wpas_p2p_disconnect - Disconnect from a P2P Group
|
|
* @wpa_s: Pointer to wpa_supplicant data
|
|
* Returns: 0 on success, -1 on failure
|
|
*
|
|
* This can be used to disconnect from a group in which the local end is a P2P
|
|
* Client or to end a P2P Group in case the local end is the Group Owner. If a
|
|
* virtual network interface was created for this group, that interface will be
|
|
* removed. Otherwise, only the configured P2P group network will be removed
|
|
* from the interface.
|
|
*/
|
|
int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s)
|
|
{
|
|
|
|
if (wpa_s == NULL)
|
|
return -1;
|
|
|
|
return wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_REQUESTED) < 0 ?
|
|
-1 : 0;
|
|
}
|
|
|
|
|
|
int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s)
|
|
{
|
|
int ret;
|
|
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return 0;
|
|
|
|
ret = p2p_in_progress(wpa_s->global->p2p);
|
|
if (ret == 0) {
|
|
/*
|
|
* Check whether there is an ongoing WPS provisioning step (or
|
|
* other parts of group formation) on another interface since
|
|
* p2p_in_progress() does not report this to avoid issues for
|
|
* scans during such provisioning step.
|
|
*/
|
|
if (wpa_s->global->p2p_group_formation &&
|
|
wpa_s->global->p2p_group_formation != wpa_s) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Another interface (%s) "
|
|
"in group formation",
|
|
wpa_s->global->p2p_group_formation->ifname);
|
|
ret = 1;
|
|
} else if (wpa_s->global->p2p_group_formation == wpa_s) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Skip Extended Listen timeout and allow scans on current interface for group formation");
|
|
ret = 2;
|
|
}
|
|
}
|
|
|
|
if (!ret && wpa_s->global->p2p_go_wait_client.sec) {
|
|
struct os_reltime now;
|
|
os_get_reltime(&now);
|
|
if (os_reltime_expired(&now, &wpa_s->global->p2p_go_wait_client,
|
|
P2P_MAX_INITIAL_CONN_WAIT_GO)) {
|
|
/* Wait for the first client has expired */
|
|
wpa_s->global->p2p_go_wait_client.sec = 0;
|
|
} else {
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Waiting for initial client connection during group formation");
|
|
ret = 1;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
|
|
struct wpa_ssid *ssid)
|
|
{
|
|
if (wpa_s->p2p_in_provisioning && ssid->p2p_group &&
|
|
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
|
|
wpa_s->p2pdev, NULL) > 0) {
|
|
/**
|
|
* Remove the network by scheduling the group formation
|
|
* timeout to happen immediately. The teardown code
|
|
* needs to be scheduled to run asynch later so that we
|
|
* don't delete data from under ourselves unexpectedly.
|
|
* Calling wpas_p2p_group_formation_timeout directly
|
|
* causes a series of crashes in WPS failure scenarios.
|
|
*/
|
|
wpa_printf(MSG_DEBUG, "P2P: Canceled group formation due to "
|
|
"P2P group network getting removed");
|
|
eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
|
|
wpa_s->p2pdev, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
|
|
const u8 *addr, const u8 *ssid,
|
|
size_t ssid_len)
|
|
{
|
|
struct wpa_ssid *s;
|
|
size_t i;
|
|
|
|
for (s = wpa_s->conf->ssid; s; s = s->next) {
|
|
if (s->disabled != 2)
|
|
continue;
|
|
if (ssid &&
|
|
(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)
|
|
continue;
|
|
for (i = 0; i < s->num_p2p_clients; i++) {
|
|
if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN,
|
|
addr, ETH_ALEN) == 0)
|
|
return s; /* peer is P2P client in persistent
|
|
* group */
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
|
|
const u8 *addr)
|
|
{
|
|
if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
|
|
wpa_s->p2pdev, NULL) > 0) {
|
|
/*
|
|
* This can happen if WPS provisioning step is not terminated
|
|
* cleanly (e.g., P2P Client does not send WSC_Done). Since the
|
|
* peer was able to connect, there is no need to time out group
|
|
* formation after this, though. In addition, this is used with
|
|
* the initial connection wait on the GO as a separate formation
|
|
* timeout and as such, expected to be hit after the initial WPS
|
|
* provisioning step.
|
|
*/
|
|
wpa_printf(MSG_DEBUG, "P2P: Canceled P2P group formation timeout on data connection");
|
|
|
|
if (!wpa_s->p2p_go_group_formation_completed &&
|
|
!wpa_s->group_formation_reported) {
|
|
/*
|
|
* GO has not yet notified group formation success since
|
|
* the WPS step was not completed cleanly. Do that
|
|
* notification now since the P2P Client was able to
|
|
* connect and as such, must have received the
|
|
* credential from the WPS step.
|
|
*/
|
|
if (wpa_s->global->p2p)
|
|
p2p_wps_success_cb(wpa_s->global->p2p, addr);
|
|
wpas_group_formation_completed(wpa_s, 1, 0);
|
|
}
|
|
}
|
|
if (!wpa_s->p2p_go_group_formation_completed) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Marking group formation completed on GO on first data connection");
|
|
wpa_s->p2p_go_group_formation_completed = 1;
|
|
wpa_s->global->p2p_group_formation = NULL;
|
|
wpa_s->p2p_in_provisioning = 0;
|
|
wpa_s->p2p_in_invitation = 0;
|
|
}
|
|
wpa_s->global->p2p_go_wait_client.sec = 0;
|
|
if (addr == NULL)
|
|
return;
|
|
wpas_p2p_add_persistent_group_client(wpa_s, addr);
|
|
}
|
|
|
|
|
|
static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
|
|
int group_added)
|
|
{
|
|
struct wpa_supplicant *group = wpa_s;
|
|
int ret = 0;
|
|
|
|
if (wpa_s->global->p2p_group_formation)
|
|
group = wpa_s->global->p2p_group_formation;
|
|
wpa_s = wpa_s->global->p2p_init_wpa_s;
|
|
offchannel_send_action_done(wpa_s);
|
|
if (group_added)
|
|
ret = wpas_p2p_group_delete(group, P2P_GROUP_REMOVAL_SILENT);
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Fall back to GO Negotiation");
|
|
wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin,
|
|
wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0,
|
|
0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq,
|
|
wpa_s->p2p_go_vht_center_freq2,
|
|
wpa_s->p2p_persistent_id,
|
|
wpa_s->p2p_pd_before_go_neg,
|
|
wpa_s->p2p_go_ht40,
|
|
wpa_s->p2p_go_vht,
|
|
wpa_s->p2p_go_max_oper_chwidth,
|
|
wpa_s->p2p_go_he,
|
|
wpa_s->p2p_go_edmg,
|
|
NULL, 0, is_p2p_allow_6ghz(wpa_s->global->p2p));
|
|
return ret;
|
|
}
|
|
|
|
|
|
int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s)
|
|
{
|
|
int res;
|
|
|
|
if (!wpa_s->p2p_fallback_to_go_neg ||
|
|
wpa_s->p2p_in_provisioning <= 5)
|
|
return 0;
|
|
|
|
if (wpas_p2p_peer_go(wpa_s, wpa_s->pending_join_dev_addr) > 0)
|
|
return 0; /* peer operating as a GO */
|
|
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: GO not found for p2p_connect-auto - "
|
|
"fallback to GO Negotiation");
|
|
wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG
|
|
"reason=GO-not-found");
|
|
res = wpas_p2p_fallback_to_go_neg(wpa_s, 1);
|
|
|
|
return res == 1 ? 2 : 1;
|
|
}
|
|
|
|
|
|
unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s)
|
|
{
|
|
struct wpa_supplicant *ifs;
|
|
|
|
if (wpa_s->wpa_state > WPA_SCANNING) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search delay due to "
|
|
"concurrent operation",
|
|
wpa_s->conf->p2p_search_delay);
|
|
return wpa_s->conf->p2p_search_delay;
|
|
}
|
|
|
|
dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
|
|
radio_list) {
|
|
if (ifs != wpa_s && ifs->wpa_state > WPA_SCANNING) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search "
|
|
"delay due to concurrent operation on "
|
|
"interface %s",
|
|
wpa_s->conf->p2p_search_delay,
|
|
ifs->ifname);
|
|
return wpa_s->conf->p2p_search_delay;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
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, *last;
|
|
|
|
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->p2pdev, 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 &&
|
|
(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->p2pdev, 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);
|
|
|
|
if (wpa_s->p2pdev->conf->update_config &&
|
|
wpa_config_write(wpa_s->p2pdev->confname, wpa_s->p2pdev->conf))
|
|
wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
|
|
}
|
|
|
|
|
|
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 && 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");
|
|
}
|
|
|
|
|
|
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;
|
|
bin_clear_free(rem, sizeof(*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;
|
|
struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s;
|
|
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove client " MACSTR, MAC2STR(peer));
|
|
|
|
/* Remove from any persistent group */
|
|
for (s = p2p_wpa_s->conf->ssid; s; s = s->next) {
|
|
if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO)
|
|
continue;
|
|
if (!iface_addr)
|
|
wpas_remove_persistent_peer(p2p_wpa_s, s, peer, 0);
|
|
wpas_p2p_remove_psk(p2p_wpa_s, 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);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx)
|
|
{
|
|
struct wpa_supplicant *wpa_s = eloop_ctx;
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - terminate group");
|
|
wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_FREQ_CONFLICT);
|
|
}
|
|
|
|
|
|
int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq,
|
|
struct wpa_ssid *ssid)
|
|
{
|
|
struct wpa_supplicant *iface;
|
|
|
|
for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
|
|
if (!iface->current_ssid ||
|
|
iface->current_ssid->frequency == freq ||
|
|
(iface->p2p_group_interface == NOT_P2P_GROUP_INTERFACE &&
|
|
!iface->current_ssid->p2p_group))
|
|
continue;
|
|
|
|
/* Remove the connection with least priority */
|
|
if (!wpas_is_p2p_prioritized(iface)) {
|
|
/* STA connection has priority over existing
|
|
* P2P connection, so remove the interface. */
|
|
wpa_printf(MSG_DEBUG, "P2P: Removing P2P connection due to single channel concurrent mode frequency conflict");
|
|
eloop_register_timeout(0, 0,
|
|
wpas_p2p_group_freq_conflict,
|
|
iface, NULL);
|
|
/* If connection in progress is P2P connection, do not
|
|
* proceed for the connection. */
|
|
if (wpa_s == iface)
|
|
return -1;
|
|
else
|
|
return 0;
|
|
} else {
|
|
/* P2P connection has priority, disable the STA network
|
|
*/
|
|
wpa_supplicant_disable_network(wpa_s->global->ifaces,
|
|
ssid);
|
|
wpa_msg(wpa_s->global->ifaces, MSG_INFO,
|
|
WPA_EVENT_FREQ_CONFLICT " id=%d", ssid->id);
|
|
os_memset(wpa_s->global->ifaces->pending_bssid, 0,
|
|
ETH_ALEN);
|
|
/* If P2P connection is in progress, continue
|
|
* connecting...*/
|
|
if (wpa_s == iface)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
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->p2pdev, 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->p2pdev, 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 CONFIG_WPS_NFC
|
|
|
|
static struct wpabuf * wpas_p2p_nfc_handover(int ndef, struct wpabuf *wsc,
|
|
struct wpabuf *p2p)
|
|
{
|
|
struct wpabuf *ret;
|
|
size_t wsc_len;
|
|
|
|
if (p2p == NULL) {
|
|
wpabuf_free(wsc);
|
|
wpa_printf(MSG_DEBUG, "P2P: No p2p buffer for handover");
|
|
return NULL;
|
|
}
|
|
|
|
wsc_len = wsc ? wpabuf_len(wsc) : 0;
|
|
ret = wpabuf_alloc(2 + wsc_len + 2 + wpabuf_len(p2p));
|
|
if (ret == NULL) {
|
|
wpabuf_free(wsc);
|
|
wpabuf_free(p2p);
|
|
return NULL;
|
|
}
|
|
|
|
wpabuf_put_be16(ret, wsc_len);
|
|
if (wsc)
|
|
wpabuf_put_buf(ret, wsc);
|
|
wpabuf_put_be16(ret, wpabuf_len(p2p));
|
|
wpabuf_put_buf(ret, p2p);
|
|
|
|
wpabuf_free(wsc);
|
|
wpabuf_free(p2p);
|
|
wpa_hexdump_buf(MSG_DEBUG,
|
|
"P2P: Generated NFC connection handover message", ret);
|
|
|
|
if (ndef && ret) {
|
|
struct wpabuf *tmp;
|
|
tmp = ndef_build_p2p(ret);
|
|
wpabuf_free(ret);
|
|
if (tmp == NULL) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Failed to NDEF encapsulate handover request");
|
|
return NULL;
|
|
}
|
|
ret = tmp;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int wpas_p2p_cli_freq(struct wpa_supplicant *wpa_s,
|
|
struct wpa_ssid **ssid, u8 *go_dev_addr)
|
|
{
|
|
struct wpa_supplicant *iface;
|
|
|
|
if (go_dev_addr)
|
|
os_memset(go_dev_addr, 0, ETH_ALEN);
|
|
if (ssid)
|
|
*ssid = NULL;
|
|
for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
|
|
if (iface->wpa_state < WPA_ASSOCIATING ||
|
|
iface->current_ssid == NULL || iface->assoc_freq == 0 ||
|
|
!iface->current_ssid->p2p_group ||
|
|
iface->current_ssid->mode != WPAS_MODE_INFRA)
|
|
continue;
|
|
if (ssid)
|
|
*ssid = iface->current_ssid;
|
|
if (go_dev_addr)
|
|
os_memcpy(go_dev_addr, iface->go_dev_addr, ETH_ALEN);
|
|
return iface->assoc_freq;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s,
|
|
int ndef)
|
|
{
|
|
struct wpabuf *wsc, *p2p;
|
|
struct wpa_ssid *ssid;
|
|
u8 go_dev_addr[ETH_ALEN];
|
|
int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr);
|
|
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) {
|
|
wpa_printf(MSG_DEBUG, "P2P: P2P disabled - cannot build handover request");
|
|
return NULL;
|
|
}
|
|
|
|
if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
|
|
wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
|
|
&wpa_s->conf->wps_nfc_dh_privkey) < 0) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No DH key available for handover request");
|
|
return NULL;
|
|
}
|
|
|
|
if (cli_freq == 0) {
|
|
wsc = wps_build_nfc_handover_req_p2p(
|
|
wpa_s->parent->wps, wpa_s->conf->wps_nfc_dh_pubkey);
|
|
} else
|
|
wsc = NULL;
|
|
p2p = p2p_build_nfc_handover_req(wpa_s->global->p2p, cli_freq,
|
|
go_dev_addr, ssid ? ssid->ssid : NULL,
|
|
ssid ? ssid->ssid_len : 0);
|
|
|
|
return wpas_p2p_nfc_handover(ndef, wsc, p2p);
|
|
}
|
|
|
|
|
|
struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s,
|
|
int ndef, int tag)
|
|
{
|
|
struct wpabuf *wsc, *p2p;
|
|
struct wpa_ssid *ssid;
|
|
u8 go_dev_addr[ETH_ALEN];
|
|
int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr);
|
|
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return NULL;
|
|
|
|
if (!tag && wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
|
|
wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
|
|
&wpa_s->conf->wps_nfc_dh_privkey) < 0)
|
|
return NULL;
|
|
|
|
if (cli_freq == 0) {
|
|
wsc = wps_build_nfc_handover_sel_p2p(
|
|
wpa_s->parent->wps,
|
|
tag ? wpa_s->conf->wps_nfc_dev_pw_id :
|
|
DEV_PW_NFC_CONNECTION_HANDOVER,
|
|
wpa_s->conf->wps_nfc_dh_pubkey,
|
|
tag ? wpa_s->conf->wps_nfc_dev_pw : NULL);
|
|
} else
|
|
wsc = NULL;
|
|
p2p = p2p_build_nfc_handover_sel(wpa_s->global->p2p, cli_freq,
|
|
go_dev_addr, ssid ? ssid->ssid : NULL,
|
|
ssid ? ssid->ssid_len : 0);
|
|
|
|
return wpas_p2p_nfc_handover(ndef, wsc, p2p);
|
|
}
|
|
|
|
|
|
static int wpas_p2p_nfc_join_group(struct wpa_supplicant *wpa_s,
|
|
struct p2p_nfc_params *params)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "P2P: Initiate join-group based on NFC "
|
|
"connection handover (freq=%d)",
|
|
params->go_freq);
|
|
|
|
if (params->go_freq && params->go_ssid_len) {
|
|
wpa_s->p2p_wps_method = WPS_NFC;
|
|
wpa_s->pending_join_wps_method = WPS_NFC;
|
|
os_memset(wpa_s->pending_join_iface_addr, 0, ETH_ALEN);
|
|
os_memcpy(wpa_s->pending_join_dev_addr, params->go_dev_addr,
|
|
ETH_ALEN);
|
|
return wpas_p2p_join_start(wpa_s, params->go_freq,
|
|
params->go_ssid,
|
|
params->go_ssid_len);
|
|
}
|
|
|
|
return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
|
|
WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent,
|
|
params->go_freq, wpa_s->p2p_go_vht_center_freq2,
|
|
-1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth,
|
|
wpa_s->p2p_go_he, wpa_s->p2p_go_edmg,
|
|
params->go_ssid_len ? params->go_ssid : NULL,
|
|
params->go_ssid_len, false);
|
|
}
|
|
|
|
|
|
static int wpas_p2p_nfc_auth_join(struct wpa_supplicant *wpa_s,
|
|
struct p2p_nfc_params *params, int tag)
|
|
{
|
|
int res, persistent;
|
|
struct wpa_ssid *ssid;
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Authorize join-group based on NFC "
|
|
"connection handover");
|
|
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
|
|
ssid = wpa_s->current_ssid;
|
|
if (ssid == NULL)
|
|
continue;
|
|
if (ssid->mode != WPAS_MODE_P2P_GO)
|
|
continue;
|
|
if (wpa_s->ap_iface == NULL)
|
|
continue;
|
|
break;
|
|
}
|
|
if (wpa_s == NULL) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Could not find GO interface");
|
|
return -1;
|
|
}
|
|
|
|
if (wpa_s->p2pdev->p2p_oob_dev_pw_id !=
|
|
DEV_PW_NFC_CONNECTION_HANDOVER &&
|
|
!wpa_s->p2pdev->p2p_oob_dev_pw) {
|
|
wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known");
|
|
return -1;
|
|
}
|
|
res = wpas_ap_wps_add_nfc_pw(
|
|
wpa_s, wpa_s->p2pdev->p2p_oob_dev_pw_id,
|
|
wpa_s->p2pdev->p2p_oob_dev_pw,
|
|
wpa_s->p2pdev->p2p_peer_oob_pk_hash_known ?
|
|
wpa_s->p2pdev->p2p_peer_oob_pubkey_hash : NULL);
|
|
if (res)
|
|
return res;
|
|
|
|
if (!tag) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Negotiated handover - wait for peer to join without invitation");
|
|
return 0;
|
|
}
|
|
|
|
if (!params->peer ||
|
|
!(params->peer->dev_capab & P2P_DEV_CAPAB_INVITATION_PROCEDURE))
|
|
return 0;
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Static handover - invite peer " MACSTR
|
|
" to join", MAC2STR(params->peer->p2p_device_addr));
|
|
|
|
wpa_s->global->p2p_invite_group = wpa_s;
|
|
persistent = ssid->p2p_persistent_group &&
|
|
wpas_p2p_get_persistent(wpa_s->p2pdev,
|
|
params->peer->p2p_device_addr,
|
|
ssid->ssid, ssid->ssid_len);
|
|
wpa_s->p2pdev->pending_invite_ssid_id = -1;
|
|
|
|
return p2p_invite(wpa_s->global->p2p, params->peer->p2p_device_addr,
|
|
P2P_INVITE_ROLE_ACTIVE_GO, wpa_s->own_addr,
|
|
ssid->ssid, ssid->ssid_len, ssid->frequency,
|
|
wpa_s->global->p2p_dev_addr, persistent, 0,
|
|
wpa_s->p2pdev->p2p_oob_dev_pw_id);
|
|
}
|
|
|
|
|
|
static int wpas_p2p_nfc_init_go_neg(struct wpa_supplicant *wpa_s,
|
|
struct p2p_nfc_params *params,
|
|
int forced_freq)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "P2P: Initiate GO Negotiation based on NFC "
|
|
"connection handover");
|
|
return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
|
|
WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent,
|
|
forced_freq, wpa_s->p2p_go_vht_center_freq2,
|
|
-1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth,
|
|
wpa_s->p2p_go_he, wpa_s->p2p_go_edmg,
|
|
NULL, 0, false);
|
|
}
|
|
|
|
|
|
static int wpas_p2p_nfc_resp_go_neg(struct wpa_supplicant *wpa_s,
|
|
struct p2p_nfc_params *params,
|
|
int forced_freq)
|
|
{
|
|
int res;
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Authorize GO Negotiation based on NFC "
|
|
"connection handover");
|
|
res = wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
|
|
WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent,
|
|
forced_freq, wpa_s->p2p_go_vht_center_freq2,
|
|
-1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth,
|
|
wpa_s->p2p_go_he, wpa_s->p2p_go_edmg,
|
|
NULL, 0, false);
|
|
if (res)
|
|
return res;
|
|
|
|
res = wpas_p2p_listen(wpa_s, 60);
|
|
if (res) {
|
|
p2p_unauthorize(wpa_s->global->p2p,
|
|
params->peer->p2p_device_addr);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
static int wpas_p2p_nfc_connection_handover(struct wpa_supplicant *wpa_s,
|
|
const struct wpabuf *data,
|
|
int sel, int tag, int forced_freq)
|
|
{
|
|
const u8 *pos, *end;
|
|
u16 len, id;
|
|
struct p2p_nfc_params params;
|
|
int res;
|
|
|
|
os_memset(¶ms, 0, sizeof(params));
|
|
params.sel = sel;
|
|
|
|
wpa_hexdump_buf(MSG_DEBUG, "P2P: Received NFC tag payload", data);
|
|
|
|
pos = wpabuf_head(data);
|
|
end = pos + wpabuf_len(data);
|
|
|
|
if (end - pos < 2) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of WSC "
|
|
"attributes");
|
|
return -1;
|
|
}
|
|
len = WPA_GET_BE16(pos);
|
|
pos += 2;
|
|
if (len > end - pos) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Not enough data for WSC "
|
|
"attributes");
|
|
return -1;
|
|
}
|
|
params.wsc_attr = pos;
|
|
params.wsc_len = len;
|
|
pos += len;
|
|
|
|
if (end - pos < 2) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of P2P "
|
|
"attributes");
|
|
return -1;
|
|
}
|
|
len = WPA_GET_BE16(pos);
|
|
pos += 2;
|
|
if (len > end - pos) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Not enough data for P2P "
|
|
"attributes");
|
|
return -1;
|
|
}
|
|
params.p2p_attr = pos;
|
|
params.p2p_len = len;
|
|
pos += len;
|
|
|
|
wpa_hexdump(MSG_DEBUG, "P2P: WSC attributes",
|
|
params.wsc_attr, params.wsc_len);
|
|
wpa_hexdump(MSG_DEBUG, "P2P: P2P attributes",
|
|
params.p2p_attr, params.p2p_len);
|
|
if (pos < end) {
|
|
wpa_hexdump(MSG_DEBUG,
|
|
"P2P: Ignored extra data after P2P attributes",
|
|
pos, end - pos);
|
|
}
|
|
|
|
res = p2p_process_nfc_connection_handover(wpa_s->global->p2p, ¶ms);
|
|
if (res)
|
|
return res;
|
|
|
|
if (params.next_step == NO_ACTION)
|
|
return 0;
|
|
|
|
if (params.next_step == BOTH_GO) {
|
|
wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_BOTH_GO "peer=" MACSTR,
|
|
MAC2STR(params.peer->p2p_device_addr));
|
|
return 0;
|
|
}
|
|
|
|
if (params.next_step == PEER_CLIENT) {
|
|
if (!is_zero_ether_addr(params.go_dev_addr)) {
|
|
wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT
|
|
"peer=" MACSTR " freq=%d go_dev_addr=" MACSTR
|
|
" ssid=\"%s\"",
|
|
MAC2STR(params.peer->p2p_device_addr),
|
|
params.go_freq,
|
|
MAC2STR(params.go_dev_addr),
|
|
wpa_ssid_txt(params.go_ssid,
|
|
params.go_ssid_len));
|
|
} else {
|
|
wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT
|
|
"peer=" MACSTR " freq=%d",
|
|
MAC2STR(params.peer->p2p_device_addr),
|
|
params.go_freq);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (wpas_p2p_cli_freq(wpa_s, NULL, NULL)) {
|
|
wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_WHILE_CLIENT "peer="
|
|
MACSTR, MAC2STR(params.peer->p2p_device_addr));
|
|
return 0;
|
|
}
|
|
|
|
wpabuf_free(wpa_s->p2p_oob_dev_pw);
|
|
wpa_s->p2p_oob_dev_pw = NULL;
|
|
|
|
if (params.oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
|
|
wpa_printf(MSG_DEBUG, "P2P: No peer OOB Dev Pw "
|
|
"received");
|
|
return -1;
|
|
}
|
|
|
|
id = WPA_GET_BE16(params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN);
|
|
wpa_printf(MSG_DEBUG, "P2P: Peer OOB Dev Pw %u", id);
|
|
wpa_hexdump(MSG_DEBUG, "P2P: Peer OOB Public Key hash",
|
|
params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN);
|
|
os_memcpy(wpa_s->p2p_peer_oob_pubkey_hash,
|
|
params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN);
|
|
wpa_s->p2p_peer_oob_pk_hash_known = 1;
|
|
|
|
if (tag) {
|
|
if (id < 0x10) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Static handover - invalid "
|
|
"peer OOB Device Password Id %u", id);
|
|
return -1;
|
|
}
|
|
wpa_printf(MSG_DEBUG, "P2P: Static handover - use peer OOB "
|
|
"Device Password Id %u", id);
|
|
wpa_hexdump_key(MSG_DEBUG, "P2P: Peer OOB Device Password",
|
|
params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2,
|
|
params.oob_dev_pw_len -
|
|
WPS_OOB_PUBKEY_HASH_LEN - 2);
|
|
wpa_s->p2p_oob_dev_pw_id = id;
|
|
wpa_s->p2p_oob_dev_pw = wpabuf_alloc_copy(
|
|
params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2,
|
|
params.oob_dev_pw_len -
|
|
WPS_OOB_PUBKEY_HASH_LEN - 2);
|
|
if (wpa_s->p2p_oob_dev_pw == NULL)
|
|
return -1;
|
|
|
|
if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
|
|
wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
|
|
&wpa_s->conf->wps_nfc_dh_privkey) < 0)
|
|
return -1;
|
|
} else {
|
|
wpa_printf(MSG_DEBUG, "P2P: Using abbreviated WPS handshake "
|
|
"without Device Password");
|
|
wpa_s->p2p_oob_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
|
|
}
|
|
|
|
switch (params.next_step) {
|
|
case NO_ACTION:
|
|
case BOTH_GO:
|
|
case PEER_CLIENT:
|
|
/* already covered above */
|
|
return 0;
|
|
case JOIN_GROUP:
|
|
return wpas_p2p_nfc_join_group(wpa_s, ¶ms);
|
|
case AUTH_JOIN:
|
|
return wpas_p2p_nfc_auth_join(wpa_s, ¶ms, tag);
|
|
case INIT_GO_NEG:
|
|
return wpas_p2p_nfc_init_go_neg(wpa_s, ¶ms, forced_freq);
|
|
case RESP_GO_NEG:
|
|
/* TODO: use own OOB Dev Pw */
|
|
return wpas_p2p_nfc_resp_go_neg(wpa_s, ¶ms, forced_freq);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
int wpas_p2p_nfc_tag_process(struct wpa_supplicant *wpa_s,
|
|
const struct wpabuf *data, int forced_freq)
|
|
{
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return -1;
|
|
|
|
return wpas_p2p_nfc_connection_handover(wpa_s, data, 1, 1, forced_freq);
|
|
}
|
|
|
|
|
|
int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init,
|
|
const struct wpabuf *req,
|
|
const struct wpabuf *sel, int forced_freq)
|
|
{
|
|
struct wpabuf *tmp;
|
|
int ret;
|
|
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return -1;
|
|
|
|
wpa_printf(MSG_DEBUG, "NFC: P2P connection handover reported");
|
|
|
|
wpa_hexdump_ascii(MSG_DEBUG, "NFC: Req",
|
|
wpabuf_head(req), wpabuf_len(req));
|
|
wpa_hexdump_ascii(MSG_DEBUG, "NFC: Sel",
|
|
wpabuf_head(sel), wpabuf_len(sel));
|
|
if (forced_freq)
|
|
wpa_printf(MSG_DEBUG, "NFC: Forced freq %d", forced_freq);
|
|
tmp = ndef_parse_p2p(init ? sel : req);
|
|
if (tmp == NULL) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Could not parse NDEF");
|
|
return -1;
|
|
}
|
|
|
|
ret = wpas_p2p_nfc_connection_handover(wpa_s, tmp, init, 0,
|
|
forced_freq);
|
|
wpabuf_free(tmp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled)
|
|
{
|
|
const u8 *if_addr;
|
|
int go_intent = wpa_s->conf->p2p_go_intent;
|
|
struct wpa_supplicant *iface;
|
|
|
|
if (wpa_s->global->p2p == NULL)
|
|
return -1;
|
|
|
|
if (!enabled) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Disable use of own NFC Tag");
|
|
for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
|
|
{
|
|
if (!iface->ap_iface)
|
|
continue;
|
|
hostapd_wps_nfc_token_disable(iface->ap_iface->bss[0]);
|
|
}
|
|
p2p_set_authorized_oob_dev_pw_id(wpa_s->global->p2p, 0,
|
|
0, NULL);
|
|
if (wpa_s->p2p_nfc_tag_enabled)
|
|
wpas_p2p_remove_pending_group_interface(wpa_s);
|
|
wpa_s->p2p_nfc_tag_enabled = 0;
|
|
return 0;
|
|
}
|
|
|
|
if (wpa_s->global->p2p_disabled)
|
|
return -1;
|
|
|
|
if (wpa_s->conf->wps_nfc_dh_pubkey == NULL ||
|
|
wpa_s->conf->wps_nfc_dh_privkey == NULL ||
|
|
wpa_s->conf->wps_nfc_dev_pw == NULL ||
|
|
wpa_s->conf->wps_nfc_dev_pw_id < 0x10) {
|
|
wpa_printf(MSG_DEBUG, "P2P: NFC password token not configured "
|
|
"to allow static handover cases");
|
|
return -1;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "P2P: Enable use of own NFC Tag");
|
|
|
|
wpa_s->p2p_oob_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
|
|
wpabuf_free(wpa_s->p2p_oob_dev_pw);
|
|
wpa_s->p2p_oob_dev_pw = wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw);
|
|
if (wpa_s->p2p_oob_dev_pw == NULL)
|
|
return -1;
|
|
wpa_s->p2p_peer_oob_pk_hash_known = 0;
|
|
|
|
if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO ||
|
|
wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT) {
|
|
/*
|
|
* P2P Group Interface present and the command came on group
|
|
* interface, so enable the token for the current interface.
|
|
*/
|
|
wpa_s->create_p2p_iface = 0;
|
|
} else {
|
|
wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
|
|
}
|
|
|
|
if (wpa_s->create_p2p_iface) {
|
|
enum wpa_driver_if_type iftype;
|
|
/* Prepare to add a new interface for the group */
|
|
iftype = WPA_IF_P2P_GROUP;
|
|
if (go_intent == 15)
|
|
iftype = WPA_IF_P2P_GO;
|
|
if (wpas_p2p_add_group_interface(wpa_s, iftype) < 0) {
|
|
wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new "
|
|
"interface for the group");
|
|
return -1;
|
|
}
|
|
|
|
if_addr = wpa_s->pending_interface_addr;
|
|
} else if (wpa_s->p2p_mgmt)
|
|
if_addr = wpa_s->parent->own_addr;
|
|
else
|
|
if_addr = wpa_s->own_addr;
|
|
|
|
wpa_s->p2p_nfc_tag_enabled = enabled;
|
|
|
|
for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
|
|
struct hostapd_data *hapd;
|
|
if (iface->ap_iface == NULL)
|
|
continue;
|
|
hapd = iface->ap_iface->bss[0];
|
|
wpabuf_free(hapd->conf->wps_nfc_dh_pubkey);
|
|
hapd->conf->wps_nfc_dh_pubkey =
|
|
wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
|
|
wpabuf_free(hapd->conf->wps_nfc_dh_privkey);
|
|
hapd->conf->wps_nfc_dh_privkey =
|
|
wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
|
|
wpabuf_free(hapd->conf->wps_nfc_dev_pw);
|
|
hapd->conf->wps_nfc_dev_pw =
|
|
wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw);
|
|
hapd->conf->wps_nfc_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
|
|
|
|
if (hostapd_wps_nfc_token_enable(iface->ap_iface->bss[0]) < 0) {
|
|
wpa_dbg(iface, MSG_DEBUG,
|
|
"P2P: Failed to enable NFC Tag for GO");
|
|
}
|
|
}
|
|
p2p_set_authorized_oob_dev_pw_id(
|
|
wpa_s->global->p2p, wpa_s->conf->wps_nfc_dev_pw_id, go_intent,
|
|
if_addr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* CONFIG_WPS_NFC */
|
|
|
|
|
|
static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s,
|
|
struct wpa_used_freq_data *freqs,
|
|
unsigned int num)
|
|
{
|
|
u8 curr_chan, cand, chan;
|
|
unsigned int i;
|
|
|
|
/*
|
|
* If possible, optimize the Listen channel to be a channel that is
|
|
* already used by one of the other interfaces.
|
|
*/
|
|
if (!wpa_s->conf->p2p_optimize_listen_chan)
|
|
return;
|
|
|
|
curr_chan = p2p_get_listen_channel(wpa_s->global->p2p);
|
|
for (i = 0, cand = 0; i < num; i++) {
|
|
ieee80211_freq_to_chan(freqs[i].freq, &chan);
|
|
if (curr_chan == chan) {
|
|
cand = 0;
|
|
break;
|
|
}
|
|
|
|
if (chan == 1 || chan == 6 || chan == 11)
|
|
cand = chan;
|
|
}
|
|
|
|
if (cand) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Update Listen channel to %u based on operating channel",
|
|
cand);
|
|
p2p_set_listen_channel(wpa_s->global->p2p, 81, cand, 0);
|
|
}
|
|
}
|
|
|
|
|
|
static int wpas_p2p_move_go_csa(struct wpa_supplicant *wpa_s)
|
|
{
|
|
struct hostapd_config *conf;
|
|
struct p2p_go_neg_results params;
|
|
struct csa_settings csa_settings;
|
|
struct wpa_ssid *current_ssid = wpa_s->current_ssid;
|
|
int old_freq = current_ssid->frequency;
|
|
int ret;
|
|
|
|
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "CSA is not enabled");
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* TODO: This function may not always work correctly. For example,
|
|
* when we have a running GO and a BSS on a DFS channel.
|
|
*/
|
|
if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, 0, 0, 0, 0,
|
|
NULL)) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P CSA: Failed to select new frequency for GO");
|
|
return -1;
|
|
}
|
|
|
|
if (current_ssid->frequency == params.freq) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P CSA: Selected same frequency - not moving GO");
|
|
return 0;
|
|
}
|
|
|
|
conf = hostapd_config_defaults();
|
|
if (!conf) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P CSA: Failed to allocate default config");
|
|
return -1;
|
|
}
|
|
|
|
current_ssid->frequency = params.freq;
|
|
if (wpa_supplicant_conf_ap_ht(wpa_s, current_ssid, conf)) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P CSA: Failed to create new GO config");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (conf->hw_mode != wpa_s->ap_iface->current_mode->mode &&
|
|
(wpa_s->ap_iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A ||
|
|
is_6ghz_freq(wpa_s->ap_iface->freq) ||
|
|
conf->hw_mode != HOSTAPD_MODE_IEEE80211G)) {
|
|
wpa_dbg(wpa_s, MSG_INFO,
|
|
"P2P CSA: CSA from hardware mode %d%s to %d is not supported",
|
|
wpa_s->ap_iface->current_mode->mode,
|
|
is_6ghz_freq(wpa_s->ap_iface->freq) ? " (6 GHz)" : "",
|
|
conf->hw_mode);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
os_memset(&csa_settings, 0, sizeof(csa_settings));
|
|
csa_settings.cs_count = P2P_GO_CSA_COUNT;
|
|
csa_settings.block_tx = P2P_GO_CSA_BLOCK_TX;
|
|
csa_settings.freq_params.freq = params.freq;
|
|
csa_settings.freq_params.sec_channel_offset = conf->secondary_channel;
|
|
csa_settings.freq_params.ht_enabled = conf->ieee80211n;
|
|
csa_settings.freq_params.bandwidth = conf->secondary_channel ? 40 : 20;
|
|
|
|
if (conf->ieee80211ac) {
|
|
int freq1 = 0, freq2 = 0;
|
|
u8 chan, opclass;
|
|
|
|
if (ieee80211_freq_to_channel_ext(params.freq,
|
|
conf->secondary_channel,
|
|
conf->vht_oper_chwidth,
|
|
&opclass, &chan) ==
|
|
NUM_HOSTAPD_MODES) {
|
|
wpa_printf(MSG_ERROR, "P2P CSA: Bad freq");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (conf->vht_oper_centr_freq_seg0_idx)
|
|
freq1 = ieee80211_chan_to_freq(
|
|
NULL, opclass,
|
|
conf->vht_oper_centr_freq_seg0_idx);
|
|
|
|
if (conf->vht_oper_centr_freq_seg1_idx)
|
|
freq2 = ieee80211_chan_to_freq(
|
|
NULL, opclass,
|
|
conf->vht_oper_centr_freq_seg1_idx);
|
|
|
|
if (freq1 < 0 || freq2 < 0) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P CSA: Selected invalid VHT center freqs");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
csa_settings.freq_params.vht_enabled = conf->ieee80211ac;
|
|
csa_settings.freq_params.center_freq1 = freq1;
|
|
csa_settings.freq_params.center_freq2 = freq2;
|
|
|
|
switch (conf->vht_oper_chwidth) {
|
|
case CONF_OPER_CHWIDTH_80MHZ:
|
|
case CONF_OPER_CHWIDTH_80P80MHZ:
|
|
csa_settings.freq_params.bandwidth = 80;
|
|
break;
|
|
case CONF_OPER_CHWIDTH_160MHZ:
|
|
csa_settings.freq_params.bandwidth = 160;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
ret = ap_switch_channel(wpa_s, &csa_settings);
|
|
out:
|
|
current_ssid->frequency = old_freq;
|
|
hostapd_config_free(conf);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static void wpas_p2p_move_go_no_csa(struct wpa_supplicant *wpa_s)
|
|
{
|
|
struct p2p_go_neg_results params;
|
|
struct wpa_ssid *current_ssid = wpa_s->current_ssid;
|
|
void (*ap_configured_cb)(void *ctx, void *data);
|
|
void *ap_configured_cb_ctx, *ap_configured_cb_data;
|
|
|
|
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
|
|
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz",
|
|
current_ssid->frequency);
|
|
|
|
/* Stop the AP functionality */
|
|
/* TODO: Should do this in a way that does not indicated to possible
|
|
* P2P Clients in the group that the group is terminated. */
|
|
/* If this action occurs before a group is started, the callback should
|
|
* be preserved, or GROUP-STARTED event would be lost. If this action
|
|
* occurs after a group is started, these pointers are all NULL and
|
|
* harmless. */
|
|
ap_configured_cb = wpa_s->ap_configured_cb;
|
|
ap_configured_cb_ctx = wpa_s->ap_configured_cb_ctx;
|
|
ap_configured_cb_data = wpa_s->ap_configured_cb_data;
|
|
wpa_supplicant_ap_deinit(wpa_s);
|
|
|
|
/* Reselect the GO frequency */
|
|
if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, 0, 0, 0, 0,
|
|
NULL)) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Failed to reselect freq");
|
|
wpas_p2p_group_delete(wpa_s,
|
|
P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL);
|
|
return;
|
|
}
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New freq selected for the GO (%u MHz)",
|
|
params.freq);
|
|
|
|
if (params.freq &&
|
|
!p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: Selected freq (%u MHz) is not valid for P2P",
|
|
params.freq);
|
|
wpas_p2p_group_delete(wpa_s,
|
|
P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL);
|
|
return;
|
|
}
|
|
|
|
/* Restore preserved callback parameters */
|
|
wpa_s->ap_configured_cb = ap_configured_cb;
|
|
wpa_s->ap_configured_cb_ctx = ap_configured_cb_ctx;
|
|
wpa_s->ap_configured_cb_data = ap_configured_cb_data;
|
|
|
|
/* Update the frequency */
|
|
current_ssid->frequency = params.freq;
|
|
wpa_s->connect_without_scan = current_ssid;
|
|
wpa_s->reassociate = 1;
|
|
wpa_s->disconnected = 0;
|
|
wpa_supplicant_req_scan(wpa_s, 0, 0);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx)
|
|
{
|
|
struct wpa_supplicant *wpa_s = eloop_ctx;
|
|
|
|
if (!wpa_s->ap_iface || !wpa_s->current_ssid)
|
|
return;
|
|
|
|
wpas_p2p_go_update_common_freqs(wpa_s);
|
|
|
|
/* Do not move GO in the middle of a CSA */
|
|
if (hostapd_csa_in_progress(wpa_s->ap_iface)) {
|
|
wpa_printf(MSG_DEBUG,
|
|
"P2P: CSA is in progress - not moving GO");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* First, try a channel switch flow. If it is not supported or fails,
|
|
* take down the GO and bring it up again.
|
|
*/
|
|
if (wpas_p2p_move_go_csa(wpa_s) < 0)
|
|
wpas_p2p_move_go_no_csa(wpa_s);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx)
|
|
{
|
|
struct wpa_supplicant *wpa_s = eloop_ctx;
|
|
struct wpa_used_freq_data *freqs = NULL;
|
|
unsigned int num = wpa_s->num_multichan_concurrent;
|
|
|
|
freqs = os_calloc(num, sizeof(struct wpa_used_freq_data));
|
|
if (!freqs)
|
|
return;
|
|
|
|
num = get_shared_radio_freqs_data(wpa_s, freqs, num);
|
|
|
|
/* Previous attempt to move a GO was not possible -- try again. */
|
|
wpas_p2p_consider_moving_gos(wpa_s, freqs, num,
|
|
WPAS_P2P_CHANNEL_UPDATE_ANY);
|
|
|
|
os_free(freqs);
|
|
}
|
|
|
|
|
|
/*
|
|
* Consider moving a GO from its currently used frequency:
|
|
* 1. It is possible that due to regulatory consideration the frequency
|
|
* can no longer be used and there is a need to evacuate the GO.
|
|
* 2. It is possible that due to MCC considerations, it would be preferable
|
|
* to move the GO to a channel that is currently used by some other
|
|
* station interface.
|
|
*
|
|
* In case a frequency that became invalid is once again valid, cancel a
|
|
* previously initiated GO frequency change.
|
|
*/
|
|
static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s,
|
|
struct wpa_used_freq_data *freqs,
|
|
unsigned int num)
|
|
{
|
|
unsigned int i, invalid_freq = 0, policy_move = 0, flags = 0;
|
|
unsigned int timeout;
|
|
int freq;
|
|
int dfs_offload;
|
|
|
|
wpas_p2p_go_update_common_freqs(wpa_s);
|
|
|
|
freq = wpa_s->current_ssid->frequency;
|
|
dfs_offload = (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
|
|
ieee80211_is_dfs(freq, wpa_s->hw.modes, wpa_s->hw.num_modes);
|
|
for (i = 0, invalid_freq = 0; i < num; i++) {
|
|
if (freqs[i].freq == freq) {
|
|
flags = freqs[i].flags;
|
|
|
|
/* The channel is invalid, must change it */
|
|
if (!p2p_supported_freq_go(wpa_s->global->p2p, freq) &&
|
|
!dfs_offload) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Freq=%d MHz no longer valid for GO",
|
|
freq);
|
|
invalid_freq = 1;
|
|
}
|
|
} else if (freqs[i].flags == 0) {
|
|
/* Freq is not used by any other station interface */
|
|
continue;
|
|
} else if (!p2p_supported_freq(wpa_s->global->p2p,
|
|
freqs[i].freq) && !dfs_offload) {
|
|
/* Freq is not valid for P2P use cases */
|
|
continue;
|
|
} else if (wpa_s->conf->p2p_go_freq_change_policy ==
|
|
P2P_GO_FREQ_MOVE_SCM) {
|
|
policy_move = 1;
|
|
} else if (wpa_s->conf->p2p_go_freq_change_policy ==
|
|
P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS &&
|
|
wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) {
|
|
policy_move = 1;
|
|
} else if ((wpa_s->conf->p2p_go_freq_change_policy ==
|
|
P2P_GO_FREQ_MOVE_SCM_ECSA) &&
|
|
wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) {
|
|
if (!p2p_get_group_num_members(wpa_s->p2p_group)) {
|
|
policy_move = 1;
|
|
} else if ((wpa_s->drv_flags &
|
|
WPA_DRIVER_FLAGS_AP_CSA) &&
|
|
wpas_p2p_go_clients_support_ecsa(wpa_s)) {
|
|
u8 chan;
|
|
|
|
/*
|
|
* We do not support CSA between bands, so move
|
|
* GO only within the same band.
|
|
*/
|
|
if (wpa_s->ap_iface->current_mode->mode ==
|
|
ieee80211_freq_to_chan(freqs[i].freq,
|
|
&chan))
|
|
policy_move = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: GO move: invalid_freq=%u, policy_move=%u, flags=0x%X",
|
|
invalid_freq, policy_move, flags);
|
|
|
|
/*
|
|
* The channel is valid, or we are going to have a policy move, so
|
|
* cancel timeout.
|
|
*/
|
|
if (!invalid_freq || policy_move) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Cancel a GO move from freq=%d MHz", freq);
|
|
eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
|
|
|
|
if (wpas_p2p_in_progress(wpa_s)) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: GO move: policy CS is not allowed - setting timeout to re-consider GO move");
|
|
eloop_cancel_timeout(wpas_p2p_reconsider_moving_go,
|
|
wpa_s, NULL);
|
|
eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0,
|
|
wpas_p2p_reconsider_moving_go,
|
|
wpa_s, NULL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!invalid_freq && (!policy_move || flags != 0)) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Not initiating a GO frequency change");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Do not consider moving GO if it is in the middle of a CSA. When the
|
|
* CSA is finished this flow should be retriggered.
|
|
*/
|
|
if (hostapd_csa_in_progress(wpa_s->ap_iface)) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Not initiating a GO frequency change - CSA is in progress");
|
|
return;
|
|
}
|
|
|
|
if (invalid_freq && !wpas_p2p_disallowed_freq(wpa_s->global, freq))
|
|
timeout = P2P_GO_FREQ_CHANGE_TIME;
|
|
else
|
|
timeout = 0;
|
|
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz in %d secs",
|
|
freq, timeout);
|
|
eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
|
|
eloop_register_timeout(timeout, 0, wpas_p2p_move_go, wpa_s, NULL);
|
|
}
|
|
|
|
|
|
static void wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s,
|
|
struct wpa_used_freq_data *freqs,
|
|
unsigned int num,
|
|
enum wpas_p2p_channel_update_trig trig)
|
|
{
|
|
struct wpa_supplicant *ifs;
|
|
|
|
eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, ELOOP_ALL_CTX,
|
|
NULL);
|
|
|
|
/*
|
|
* Travers all the radio interfaces, and for each GO interface, check
|
|
* if there is a need to move the GO from the frequency it is using,
|
|
* or in case the frequency is valid again, cancel the evacuation flow.
|
|
*/
|
|
dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
|
|
radio_list) {
|
|
if (ifs->current_ssid == NULL ||
|
|
ifs->current_ssid->mode != WPAS_MODE_P2P_GO)
|
|
continue;
|
|
|
|
/*
|
|
* The GO was just started or completed channel switch, no need
|
|
* to move it.
|
|
*/
|
|
if (wpa_s == ifs &&
|
|
(trig == WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE ||
|
|
trig == WPAS_P2P_CHANNEL_UPDATE_CS)) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: GO move - schedule re-consideration");
|
|
eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0,
|
|
wpas_p2p_reconsider_moving_go,
|
|
wpa_s, NULL);
|
|
continue;
|
|
}
|
|
|
|
wpas_p2p_consider_moving_one_go(ifs, freqs, num);
|
|
}
|
|
}
|
|
|
|
|
|
void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
|
|
{
|
|
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
|
|
return;
|
|
|
|
wpas_p2p_update_channel_list(wpa_s,
|
|
WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE);
|
|
}
|
|
|
|
|
|
void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s)
|
|
{
|
|
if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing "
|
|
"the management interface is being removed");
|
|
wpas_p2p_deinit_global(wpa_s->global);
|
|
}
|
|
}
|
|
|
|
|
|
void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s)
|
|
{
|
|
if (wpa_s->ap_iface->bss)
|
|
wpa_s->ap_iface->bss[0]->p2p_group = NULL;
|
|
wpas_p2p_group_deinit(wpa_s);
|
|
}
|
|
|
|
|
|
int wpas_p2p_lo_start(struct wpa_supplicant *wpa_s, unsigned int freq,
|
|
unsigned int period, unsigned int interval,
|
|
unsigned int count)
|
|
{
|
|
struct p2p_data *p2p = wpa_s->global->p2p;
|
|
u8 *device_types;
|
|
size_t dev_types_len;
|
|
struct wpabuf *buf;
|
|
int ret;
|
|
|
|
if (wpa_s->p2p_lo_started) {
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P Listen offload is already started");
|
|
return 0;
|
|
}
|
|
|
|
if (wpa_s->global->p2p == NULL ||
|
|
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD)) {
|
|
wpa_printf(MSG_DEBUG, "P2P: Listen offload not supported");
|
|
return -1;
|
|
}
|
|
|
|
if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
|
|
wpa_printf(MSG_ERROR, "P2P: Input channel not supported: %u",
|
|
freq);
|
|
return -1;
|
|
}
|
|
|
|
/* Get device type */
|
|
dev_types_len = (wpa_s->conf->num_sec_device_types + 1) *
|
|
WPS_DEV_TYPE_LEN;
|
|
device_types = os_malloc(dev_types_len);
|
|
if (!device_types)
|
|
return -1;
|
|
os_memcpy(device_types, wpa_s->conf->device_type, WPS_DEV_TYPE_LEN);
|
|
os_memcpy(&device_types[WPS_DEV_TYPE_LEN], wpa_s->conf->sec_device_type,
|
|
wpa_s->conf->num_sec_device_types * WPS_DEV_TYPE_LEN);
|
|
|
|
/* Get Probe Response IE(s) */
|
|
buf = p2p_build_probe_resp_template(p2p, freq);
|
|
if (!buf) {
|
|
os_free(device_types);
|
|
return -1;
|
|
}
|
|
|
|
ret = wpa_drv_p2p_lo_start(wpa_s, freq, period, interval, count,
|
|
device_types, dev_types_len,
|
|
wpabuf_mhead_u8(buf), wpabuf_len(buf));
|
|
if (ret < 0)
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Failed to start P2P listen offload");
|
|
|
|
os_free(device_types);
|
|
wpabuf_free(buf);
|
|
|
|
if (ret == 0) {
|
|
wpa_s->p2p_lo_started = 1;
|
|
|
|
/* Stop current P2P listen if any */
|
|
wpas_stop_listen(wpa_s);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s)
|
|
{
|
|
int ret;
|
|
|
|
if (!wpa_s->p2p_lo_started)
|
|
return 0;
|
|
|
|
ret = wpa_drv_p2p_lo_stop(wpa_s);
|
|
if (ret < 0)
|
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
|
"P2P: Failed to stop P2P listen offload");
|
|
|
|
wpa_s->p2p_lo_started = 0;
|
|
return ret;
|
|
}
|