hostapd/wpa_supplicant/ctrl_iface.c

14280 lines
348 KiB
C
Raw Normal View History

/*
* WPA Supplicant / Control interface (shared code for all backends)
* Copyright (c) 2004-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#ifdef CONFIG_TESTING_OPTIONS
#include <netinet/ip.h>
#endif /* CONFIG_TESTING_OPTIONS */
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/uuid.h"
#include "utils/module_tests.h"
#include "common/version.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
#ifdef CONFIG_DPP
#include "common/dpp.h"
#endif /* CONFIG_DPP */
#include "common/nan_de.h"
#include "common/ptksa_cache.h"
#include "crypto/tls.h"
#include "ap/hostapd.h"
#include "eap_peer/eap.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "rsn_supp/wpa.h"
#include "rsn_supp/preauth.h"
#include "rsn_supp/pmksa_cache.h"
#include "l2_packet/l2_packet.h"
#include "wps/wps.h"
#include "fst/fst.h"
#include "fst/fst_ctrl_iface.h"
#include "config.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "wps_supplicant.h"
#include "ibss_rsn.h"
#include "wpas_glue.h"
#include "ap.h"
#include "p2p_supplicant.h"
#include "p2p/p2p.h"
#include "hs20_supplicant.h"
#include "wifi_display.h"
#include "notify.h"
#include "bss.h"
#include "scan.h"
#include "ctrl_iface.h"
#include "interworking.h"
#include "bssid_ignore.h"
#include "autoscan.h"
#include "wnm_sta.h"
#include "offchannel.h"
#include "drivers/driver.h"
#include "mesh.h"
#include "dpp_supplicant.h"
#include "sme.h"
#include "nan_usd.h"
#ifdef __NetBSD__
#include <net/if_ether.h>
#elif !defined(__CYGWIN__) && !defined(CONFIG_NATIVE_WINDOWS)
#include <net/ethernet.h>
#endif
static int wpa_supplicant_global_iface_list(struct wpa_global *global,
char *buf, int len);
static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global,
const char *input,
char *buf, int len);
static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s,
char *val);
static int set_bssid_filter(struct wpa_supplicant *wpa_s, char *val)
{
char *pos;
u8 addr[ETH_ALEN], *filter = NULL, *n;
size_t count = 0;
pos = val;
while (pos) {
if (*pos == '\0')
break;
if (hwaddr_aton(pos, addr)) {
os_free(filter);
return -1;
}
n = os_realloc_array(filter, count + 1, ETH_ALEN);
if (n == NULL) {
os_free(filter);
return -1;
}
filter = n;
os_memcpy(filter + count * ETH_ALEN, addr, ETH_ALEN);
count++;
pos = os_strchr(pos, ' ');
if (pos)
pos++;
}
wpa_hexdump(MSG_DEBUG, "bssid_filter", filter, count * ETH_ALEN);
os_free(wpa_s->bssid_filter);
wpa_s->bssid_filter = filter;
wpa_s->bssid_filter_count = count;
return 0;
}
static int set_disallow_aps(struct wpa_supplicant *wpa_s, char *val)
{
char *pos;
u8 addr[ETH_ALEN], *bssid = NULL, *n;
struct wpa_ssid_value *ssid = NULL, *ns;
size_t count = 0, ssid_count = 0;
struct wpa_ssid *c;
/*
* disallow_list ::= <ssid_spec> | <bssid_spec> | <disallow_list> | ""
* SSID_SPEC ::= ssid <SSID_HEX>
* BSSID_SPEC ::= bssid <BSSID_HEX>
*/
pos = val;
while (pos) {
if (*pos == '\0')
break;
if (os_strncmp(pos, "bssid ", 6) == 0) {
int res;
pos += 6;
res = hwaddr_aton2(pos, addr);
if (res < 0) {
os_free(ssid);
os_free(bssid);
wpa_printf(MSG_DEBUG, "Invalid disallow_aps "
"BSSID value '%s'", pos);
return -1;
}
pos += res;
n = os_realloc_array(bssid, count + 1, ETH_ALEN);
if (n == NULL) {
os_free(ssid);
os_free(bssid);
return -1;
}
bssid = n;
os_memcpy(bssid + count * ETH_ALEN, addr, ETH_ALEN);
count++;
} else if (os_strncmp(pos, "ssid ", 5) == 0) {
char *end;
pos += 5;
end = pos;
while (*end) {
if (*end == '\0' || *end == ' ')
break;
end++;
}
ns = os_realloc_array(ssid, ssid_count + 1,
sizeof(struct wpa_ssid_value));
if (ns == NULL) {
os_free(ssid);
os_free(bssid);
return -1;
}
ssid = ns;
if ((end - pos) & 0x01 ||
end - pos > 2 * SSID_MAX_LEN ||
hexstr2bin(pos, ssid[ssid_count].ssid,
(end - pos) / 2) < 0) {
os_free(ssid);
os_free(bssid);
wpa_printf(MSG_DEBUG, "Invalid disallow_aps "
"SSID value '%s'", pos);
return -1;
}
ssid[ssid_count].ssid_len = (end - pos) / 2;
wpa_hexdump_ascii(MSG_DEBUG, "disallow_aps SSID",
ssid[ssid_count].ssid,
ssid[ssid_count].ssid_len);
ssid_count++;
pos = end;
} else {
wpa_printf(MSG_DEBUG, "Unexpected disallow_aps value "
"'%s'", pos);
os_free(ssid);
os_free(bssid);
return -1;
}
pos = os_strchr(pos, ' ');
if (pos)
pos++;
}
wpa_hexdump(MSG_DEBUG, "disallow_aps_bssid", bssid, count * ETH_ALEN);
os_free(wpa_s->disallow_aps_bssid);
wpa_s->disallow_aps_bssid = bssid;
wpa_s->disallow_aps_bssid_count = count;
wpa_printf(MSG_DEBUG, "disallow_aps_ssid_count %d", (int) ssid_count);
os_free(wpa_s->disallow_aps_ssid);
wpa_s->disallow_aps_ssid = ssid;
wpa_s->disallow_aps_ssid_count = ssid_count;
if (!wpa_s->current_ssid || wpa_s->wpa_state < WPA_AUTHENTICATING)
return 0;
c = wpa_s->current_ssid;
if (c->mode != WPAS_MODE_INFRA && c->mode != WPAS_MODE_IBSS)
return 0;
if (!disallowed_bssid(wpa_s, wpa_s->bssid) &&
!disallowed_ssid(wpa_s, c->ssid, c->ssid_len))
return 0;
wpa_printf(MSG_DEBUG, "Disconnect and try to find another network "
"because current AP was marked disallowed");
#ifdef CONFIG_SME
wpa_s->sme.prev_bssid_set = 0;
#endif /* CONFIG_SME */
wpa_s->reassociate = 1;
wpa_s->own_disconnect_req = 1;
wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
wpa_supplicant_req_scan(wpa_s, 0, 0);
return 0;
}
#ifndef CONFIG_NO_CONFIG_BLOBS
static int wpas_ctrl_set_blob(struct wpa_supplicant *wpa_s, char *pos)
{
char *name = pos;
struct wpa_config_blob *blob;
size_t len;
pos = os_strchr(pos, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
len = os_strlen(pos);
if (len & 1)
return -1;
wpa_printf(MSG_DEBUG, "CTRL: Set blob '%s'", name);
blob = os_zalloc(sizeof(*blob));
if (blob == NULL)
return -1;
blob->name = os_strdup(name);
blob->data = os_malloc(len / 2);
if (blob->name == NULL || blob->data == NULL) {
wpa_config_free_blob(blob);
return -1;
}
if (hexstr2bin(pos, blob->data, len / 2) < 0) {
wpa_printf(MSG_DEBUG, "CTRL: Invalid blob hex data");
wpa_config_free_blob(blob);
return -1;
}
blob->len = len / 2;
wpa_config_set_blob(wpa_s->conf, blob);
return 0;
}
#endif /* CONFIG_NO_CONFIG_BLOBS */
static int wpas_ctrl_pno(struct wpa_supplicant *wpa_s, char *cmd)
{
char *params;
char *pos;
int *freqs = NULL;
int ret;
if (atoi(cmd)) {
params = os_strchr(cmd, ' ');
os_free(wpa_s->manual_sched_scan_freqs);
if (params) {
params++;
pos = os_strstr(params, "freq=");
if (pos)
freqs = freq_range_to_channel_list(wpa_s,
pos + 5);
}
wpa_s->manual_sched_scan_freqs = freqs;
ret = wpas_start_pno(wpa_s);
} else {
ret = wpas_stop_pno(wpa_s);
}
return ret;
}
static int wpas_ctrl_set_band(struct wpa_supplicant *wpa_s, char *bands)
{
union wpa_event_data event;
u32 setband_mask = WPA_SETBAND_AUTO;
/*
* For example:
* SET setband 2G,6G
* SET setband 5G
* SET setband AUTO
*/
if (!os_strstr(bands, "AUTO")) {
if (os_strstr(bands, "5G"))
setband_mask |= WPA_SETBAND_5G;
if (os_strstr(bands, "6G"))
setband_mask |= WPA_SETBAND_6G;
if (os_strstr(bands, "2G"))
setband_mask |= WPA_SETBAND_2G;
if (setband_mask == WPA_SETBAND_AUTO)
return -1;
}
wpa_s->setband_mask = setband_mask;
if (wpa_drv_setband(wpa_s, wpa_s->setband_mask) == 0) {
os_memset(&event, 0, sizeof(event));
event.channel_list_changed.initiator = REGDOM_SET_BY_USER;
event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN;
wpa_supplicant_event(wpa_s, EVENT_CHANNEL_LIST_CHANGED, &event);
}
return 0;
}
static int wpas_ctrl_iface_set_lci(struct wpa_supplicant *wpa_s,
const char *cmd)
{
struct wpabuf *lci;
if (*cmd == '\0' || os_strcmp(cmd, "\"\"") == 0) {
wpabuf_free(wpa_s->lci);
wpa_s->lci = NULL;
return 0;
}
lci = wpabuf_parse_bin(cmd);
if (!lci)
return -1;
if (os_get_reltime(&wpa_s->lci_time)) {
wpabuf_free(lci);
return -1;
}
wpabuf_free(wpa_s->lci);
wpa_s->lci = lci;
return 0;
}
static int
wpas_ctrl_set_relative_rssi(struct wpa_supplicant *wpa_s, const char *cmd)
{
int relative_rssi;
if (os_strcmp(cmd, "disable") == 0) {
wpa_s->srp.relative_rssi_set = 0;
return 0;
}
relative_rssi = atoi(cmd);
if (relative_rssi < 0 || relative_rssi > 100)
return -1;
wpa_s->srp.relative_rssi = relative_rssi;
wpa_s->srp.relative_rssi_set = 1;
return 0;
}
static int wpas_ctrl_set_relative_band_adjust(struct wpa_supplicant *wpa_s,
const char *cmd)
{
char *pos;
int adjust_rssi;
/* <band>:adjust_value */
pos = os_strchr(cmd, ':');
if (!pos)
return -1;
pos++;
adjust_rssi = atoi(pos);
if (adjust_rssi < -100 || adjust_rssi > 100)
return -1;
if (os_strncmp(cmd, "2G", 2) == 0)
wpa_s->srp.relative_adjust_band = WPA_SETBAND_2G;
else if (os_strncmp(cmd, "5G", 2) == 0)
wpa_s->srp.relative_adjust_band = WPA_SETBAND_5G;
else
return -1;
wpa_s->srp.relative_adjust_rssi = adjust_rssi;
return 0;
}
static int wpas_ctrl_iface_set_ric_ies(struct wpa_supplicant *wpa_s,
const char *cmd)
{
struct wpabuf *ric_ies;
if (*cmd == '\0' || os_strcmp(cmd, "\"\"") == 0) {
wpabuf_free(wpa_s->ric_ies);
wpa_s->ric_ies = NULL;
return 0;
}
ric_ies = wpabuf_parse_bin(cmd);
if (!ric_ies)
return -1;
wpabuf_free(wpa_s->ric_ies);
wpa_s->ric_ies = ric_ies;
return 0;
}
#ifdef CONFIG_TESTING_OPTIONS
static int wpas_ctrl_iface_set_dso(struct wpa_supplicant *wpa_s,
const char *val)
{
u8 bssid[ETH_ALEN];
const char *pos = val;
struct driver_signal_override *dso = NULL, *tmp, parsed;
if (hwaddr_aton(pos, bssid))
return -1;
pos = os_strchr(pos, ' ');
dl_list_for_each(tmp, &wpa_s->drv_signal_override,
struct driver_signal_override, list) {
if (ether_addr_equal(bssid, tmp->bssid)) {
dso = tmp;
break;
}
}
if (!pos) {
/* Remove existing entry */
if (dso) {
dl_list_del(&dso->list);
os_free(dso);
}
return 0;
}
pos++;
/* Update an existing entry or add a new one */
os_memset(&parsed, 0, sizeof(parsed));
if (sscanf(pos, "%d %d %d %d %d",
&parsed.si_current_signal,
&parsed.si_avg_signal,
&parsed.si_avg_beacon_signal,
&parsed.si_current_noise,
&parsed.scan_level) != 5)
return -1;
if (!dso) {
dso = os_zalloc(sizeof(*dso));
if (!dso)
return -1;
os_memcpy(dso->bssid, bssid, ETH_ALEN);
dl_list_add(&wpa_s->drv_signal_override, &dso->list);
}
dso->si_current_signal = parsed.si_current_signal;
dso->si_avg_signal = parsed.si_avg_signal;
dso->si_avg_beacon_signal = parsed.si_avg_beacon_signal;
dso->si_current_noise = parsed.si_current_noise;
dso->scan_level = parsed.scan_level;
return 0;
}
#endif /* CONFIG_TESTING_OPTIONS */
static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
char *cmd)
{
char *value;
int ret = 0;
value = os_strchr(cmd, ' ');
if (value == NULL)
return -1;
*value++ = '\0';
wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value);
if (os_strcasecmp(cmd, "EAPOL::heldPeriod") == 0) {
eapol_sm_configure(wpa_s->eapol,
atoi(value), -1, -1, -1);
} else if (os_strcasecmp(cmd, "EAPOL::authPeriod") == 0) {
eapol_sm_configure(wpa_s->eapol,
-1, atoi(value), -1, -1);
} else if (os_strcasecmp(cmd, "EAPOL::startPeriod") == 0) {
eapol_sm_configure(wpa_s->eapol,
-1, -1, atoi(value), -1);
} else if (os_strcasecmp(cmd, "EAPOL::maxStart") == 0) {
eapol_sm_configure(wpa_s->eapol,
-1, -1, -1, atoi(value));
#ifdef CONFIG_TESTING_OPTIONS
} else if (os_strcasecmp(cmd, "EAPOL::portControl") == 0) {
if (os_strcmp(value, "Auto") == 0)
eapol_sm_notify_portControl(wpa_s->eapol, Auto);
else if (os_strcmp(value, "ForceUnauthorized") == 0)
eapol_sm_notify_portControl(wpa_s->eapol,
ForceUnauthorized);
else if (os_strcmp(value, "ForceAuthorized") == 0)
eapol_sm_notify_portControl(wpa_s->eapol,
ForceAuthorized);
else
ret = -1;
#endif /* CONFIG_TESTING_OPTIONS */
} else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKLifetime") == 0) {
if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME,
atoi(value))) {
ret = -1;
} else {
value[-1] = '=';
wpa_config_process_global(wpa_s->conf, cmd, -1);
}
} else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKReauthThreshold") ==
0) {
if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD,
atoi(value))) {
ret = -1;
} else {
value[-1] = '=';
wpa_config_process_global(wpa_s->conf, cmd, -1);
}
} else if (os_strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) {
if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT,
atoi(value))) {
ret = -1;
} else {
value[-1] = '=';
wpa_config_process_global(wpa_s->conf, cmd, -1);
}
} else if (os_strcasecmp(cmd, "wps_fragment_size") == 0) {
wpa_s->wps_fragment_size = atoi(value);
#ifdef CONFIG_WPS_TESTING
} else if (os_strcasecmp(cmd, "wps_version_number") == 0) {
long int val;
val = strtol(value, NULL, 0);
if (val < 0 || val > 0xff) {
ret = -1;
wpa_printf(MSG_DEBUG, "WPS: Invalid "
"wps_version_number %ld", val);
} else {
wps_version_number = val;
wpa_printf(MSG_DEBUG, "WPS: Testing - force WPS "
"version %u.%u",
(wps_version_number & 0xf0) >> 4,
wps_version_number & 0x0f);
}
} else if (os_strcasecmp(cmd, "wps_testing_stub_cred") == 0) {
wps_testing_stub_cred = atoi(value);
wpa_printf(MSG_DEBUG, "WPS: Testing - stub_cred=%d",
wps_testing_stub_cred);
} else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) {
wps_corrupt_pkhash = atoi(value);
wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d",
wps_corrupt_pkhash);
} else if (os_strcasecmp(cmd, "wps_force_auth_types") == 0) {
if (value[0] == '\0') {
wps_force_auth_types_in_use = 0;
} else {
wps_force_auth_types = strtol(value, NULL, 0);
wps_force_auth_types_in_use = 1;
}
} else if (os_strcasecmp(cmd, "wps_force_encr_types") == 0) {
if (value[0] == '\0') {
wps_force_encr_types_in_use = 0;
} else {
wps_force_encr_types = strtol(value, NULL, 0);
wps_force_encr_types_in_use = 1;
}
#endif /* CONFIG_WPS_TESTING */
} else if (os_strcasecmp(cmd, "ampdu") == 0) {
if (wpa_drv_ampdu(wpa_s, atoi(value)) < 0)
ret = -1;
#ifdef CONFIG_TDLS
#ifdef CONFIG_TDLS_TESTING
} else if (os_strcasecmp(cmd, "tdls_testing") == 0) {
tdls_testing = strtol(value, NULL, 0);
wpa_printf(MSG_DEBUG, "TDLS: tdls_testing=0x%x", tdls_testing);
#endif /* CONFIG_TDLS_TESTING */
} else if (os_strcasecmp(cmd, "tdls_disabled") == 0) {
int disabled = atoi(value);
wpa_printf(MSG_DEBUG, "TDLS: tdls_disabled=%d", disabled);
if (disabled) {
if (wpa_drv_tdls_oper(wpa_s, TDLS_DISABLE, NULL) < 0)
ret = -1;
} else if (wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL) < 0)
ret = -1;
wpa_tdls_enable(wpa_s->wpa, !disabled);
#endif /* CONFIG_TDLS */
} else if (os_strcasecmp(cmd, "pno") == 0) {
ret = wpas_ctrl_pno(wpa_s, value);
} else if (os_strcasecmp(cmd, "radio_disabled") == 0) {
int disabled = atoi(value);
if (wpa_drv_radio_disable(wpa_s, disabled) < 0)
ret = -1;
else if (disabled)
wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
} else if (os_strcasecmp(cmd, "uapsd") == 0) {
if (os_strcmp(value, "disable") == 0)
wpa_s->set_sta_uapsd = 0;
else {
int be, bk, vi, vo;
char *pos;
/* format: BE,BK,VI,VO;max SP Length */
be = atoi(value);
pos = os_strchr(value, ',');
if (pos == NULL)
return -1;
pos++;
bk = atoi(pos);
pos = os_strchr(pos, ',');
if (pos == NULL)
return -1;
pos++;
vi = atoi(pos);
pos = os_strchr(pos, ',');
if (pos == NULL)
return -1;
pos++;
vo = atoi(pos);
/* ignore max SP Length for now */
wpa_s->set_sta_uapsd = 1;
wpa_s->sta_uapsd = 0;
if (be)
wpa_s->sta_uapsd |= BIT(0);
if (bk)
wpa_s->sta_uapsd |= BIT(1);
if (vi)
wpa_s->sta_uapsd |= BIT(2);
if (vo)
wpa_s->sta_uapsd |= BIT(3);
}
} else if (os_strcasecmp(cmd, "ps") == 0) {
ret = wpa_drv_set_p2p_powersave(wpa_s, atoi(value), -1, -1);
#ifdef CONFIG_WIFI_DISPLAY
} else if (os_strcasecmp(cmd, "wifi_display") == 0) {
int enabled = !!atoi(value);
if (enabled && !wpa_s->global->p2p)
ret = -1;
else
wifi_display_enable(wpa_s->global, enabled);
#endif /* CONFIG_WIFI_DISPLAY */
} else if (os_strcasecmp(cmd, "bssid_filter") == 0) {
ret = set_bssid_filter(wpa_s, value);
} else if (os_strcasecmp(cmd, "disallow_aps") == 0) {
ret = set_disallow_aps(wpa_s, value);
} else if (os_strcasecmp(cmd, "no_keep_alive") == 0) {
wpa_s->no_keep_alive = !!atoi(value);
#ifdef CONFIG_DPP
} else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) {
os_free(wpa_s->dpp_configurator_params);
wpa_s->dpp_configurator_params = os_strdup(value);
#ifdef CONFIG_DPP2
dpp_controller_set_params(wpa_s->dpp, value);
#endif /* CONFIG_DPP2 */
} else if (os_strcasecmp(cmd, "dpp_init_max_tries") == 0) {
wpa_s->dpp_init_max_tries = atoi(value);
} else if (os_strcasecmp(cmd, "dpp_init_retry_time") == 0) {
wpa_s->dpp_init_retry_time = atoi(value);
} else if (os_strcasecmp(cmd, "dpp_resp_wait_time") == 0) {
wpa_s->dpp_resp_wait_time = atoi(value);
} else if (os_strcasecmp(cmd, "dpp_resp_max_tries") == 0) {
wpa_s->dpp_resp_max_tries = atoi(value);
} else if (os_strcasecmp(cmd, "dpp_resp_retry_time") == 0) {
wpa_s->dpp_resp_retry_time = atoi(value);
#ifdef CONFIG_TESTING_OPTIONS
} else if (os_strcasecmp(cmd, "dpp_pkex_own_mac_override") == 0) {
if (hwaddr_aton(value, dpp_pkex_own_mac_override))
ret = -1;
} else if (os_strcasecmp(cmd, "dpp_pkex_peer_mac_override") == 0) {
if (hwaddr_aton(value, dpp_pkex_peer_mac_override))
ret = -1;
} else if (os_strcasecmp(cmd, "dpp_pkex_ephemeral_key_override") == 0) {
size_t hex_len = os_strlen(value);
if (hex_len >
2 * sizeof(dpp_pkex_ephemeral_key_override))
ret = -1;
else if (hexstr2bin(value, dpp_pkex_ephemeral_key_override,
hex_len / 2))
ret = -1;
else
dpp_pkex_ephemeral_key_override_len = hex_len / 2;
} else if (os_strcasecmp(cmd, "dpp_protocol_key_override") == 0) {
size_t hex_len = os_strlen(value);
if (hex_len > 2 * sizeof(dpp_protocol_key_override))
ret = -1;
else if (hexstr2bin(value, dpp_protocol_key_override,
hex_len / 2))
ret = -1;
else
dpp_protocol_key_override_len = hex_len / 2;
} else if (os_strcasecmp(cmd, "dpp_nonce_override") == 0) {
size_t hex_len = os_strlen(value);
if (hex_len > 2 * sizeof(dpp_nonce_override))
ret = -1;
else if (hexstr2bin(value, dpp_nonce_override, hex_len / 2))
ret = -1;
else
dpp_nonce_override_len = hex_len / 2;
} else if (os_strcasecmp(cmd, "dpp_version_override") == 0) {
dpp_version_override = atoi(value);
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_DPP */
#ifdef CONFIG_TESTING_OPTIONS
} else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
wpa_s->ext_mgmt_frame_handling = !!atoi(value);
} else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
wpa_s->ext_eapol_frame_io = !!atoi(value);
#ifdef CONFIG_AP
if (wpa_s->ap_iface) {
wpa_s->ap_iface->bss[0]->ext_eapol_frame_io =
wpa_s->ext_eapol_frame_io;
}
#endif /* CONFIG_AP */
} else if (os_strcasecmp(cmd, "encrypt_eapol_m2") == 0) {
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_ENCRYPT_EAPOL_M2,
!!atoi(value));
} else if (os_strcasecmp(cmd, "encrypt_eapol_m4") == 0) {
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_ENCRYPT_EAPOL_M4,
!!atoi(value));
} else if (os_strcasecmp(cmd, "extra_roc_dur") == 0) {
wpa_s->extra_roc_dur = atoi(value);
} else if (os_strcasecmp(cmd, "test_failure") == 0) {
wpa_s->test_failure = atoi(value);
} else if (os_strcasecmp(cmd, "p2p_go_csa_on_inv") == 0) {
wpa_s->p2p_go_csa_on_inv = !!atoi(value);
} else if (os_strcasecmp(cmd, "ignore_auth_resp") == 0) {
wpa_s->ignore_auth_resp = !!atoi(value);
} else if (os_strcasecmp(cmd, "ignore_assoc_disallow") == 0) {
wpa_s->ignore_assoc_disallow = !!atoi(value);
wpa_drv_ignore_assoc_disallow(wpa_s,
wpa_s->ignore_assoc_disallow);
} else if (os_strcasecmp(cmd, "disable_sa_query") == 0) {
wpa_s->disable_sa_query = !!atoi(value);
} else if (os_strcasecmp(cmd, "ignore_sae_h2e_only") == 0) {
wpa_s->ignore_sae_h2e_only = !!atoi(value);
} else if (os_strcasecmp(cmd, "extra_sae_rejected_groups") == 0) {
char *pos;
os_free(wpa_s->extra_sae_rejected_groups);
wpa_s->extra_sae_rejected_groups = NULL;
pos = value;
while (pos && pos[0]) {
int group;
group = atoi(pos);
wpa_printf(MSG_DEBUG,
"TESTING: Extra rejection of SAE group %d",
group);
if (group)
int_array_add_unique(
&wpa_s->extra_sae_rejected_groups,
group);
pos = os_strchr(pos, ' ');
if (!pos)
break;
pos++;
}
} else if (os_strcasecmp(cmd, "ft_rsnxe_used") == 0) {
wpa_s->ft_rsnxe_used = atoi(value);
} else if (os_strcasecmp(cmd, "oci_freq_override_eapol") == 0) {
wpa_s->oci_freq_override_eapol = atoi(value);
} else if (os_strcasecmp(cmd, "oci_freq_override_saquery_req") == 0) {
wpa_s->oci_freq_override_saquery_req = atoi(value);
} else if (os_strcasecmp(cmd, "oci_freq_override_saquery_resp") == 0) {
wpa_s->oci_freq_override_saquery_resp = atoi(value);
} else if (os_strcasecmp(cmd, "oci_freq_override_eapol_g2") == 0) {
wpa_s->oci_freq_override_eapol_g2 = atoi(value);
/* Populate value to wpa_sm if already associated. */
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_EAPOL_G2,
wpa_s->oci_freq_override_eapol_g2);
} else if (os_strcasecmp(cmd, "oci_freq_override_ft_assoc") == 0) {
wpa_s->oci_freq_override_ft_assoc = atoi(value);
/* Populate value to wpa_sm if already associated. */
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_FT_ASSOC,
wpa_s->oci_freq_override_ft_assoc);
} else if (os_strcasecmp(cmd, "oci_freq_override_fils_assoc") == 0) {
wpa_s->oci_freq_override_fils_assoc = atoi(value);
} else if (os_strcasecmp(cmd, "oci_freq_override_wnm_sleep") == 0) {
wpa_s->oci_freq_override_wnm_sleep = atoi(value);
} else if (os_strcasecmp(cmd, "rsne_override_eapol") == 0) {
wpabuf_free(wpa_s->rsne_override_eapol);
if (os_strcmp(value, "NULL") == 0)
wpa_s->rsne_override_eapol = NULL;
else
wpa_s->rsne_override_eapol = wpabuf_parse_bin(value);
} else if (os_strcasecmp(cmd, "rsnxe_override_assoc") == 0) {
wpabuf_free(wpa_s->rsnxe_override_assoc);
if (os_strcmp(value, "NULL") == 0)
wpa_s->rsnxe_override_assoc = NULL;
else
wpa_s->rsnxe_override_assoc = wpabuf_parse_bin(value);
} else if (os_strcasecmp(cmd, "rsnxe_override_eapol") == 0) {
wpabuf_free(wpa_s->rsnxe_override_eapol);
if (os_strcmp(value, "NULL") == 0)
wpa_s->rsnxe_override_eapol = NULL;
else
wpa_s->rsnxe_override_eapol = wpabuf_parse_bin(value);
} else if (os_strcasecmp(cmd, "reject_btm_req_reason") == 0) {
wpa_s->reject_btm_req_reason = atoi(value);
} else if (os_strcasecmp(cmd, "get_pref_freq_list_override") == 0) {
os_free(wpa_s->get_pref_freq_list_override);
if (!value[0])
wpa_s->get_pref_freq_list_override = NULL;
else
wpa_s->get_pref_freq_list_override = os_strdup(value);
} else if (os_strcasecmp(cmd, "sae_commit_override") == 0) {
wpabuf_free(wpa_s->sae_commit_override);
if (value[0] == '\0')
wpa_s->sae_commit_override = NULL;
else
wpa_s->sae_commit_override = wpabuf_parse_bin(value);
} else if (os_strcasecmp(cmd, "driver_signal_override") == 0) {
ret = wpas_ctrl_iface_set_dso(wpa_s, value);
#ifndef CONFIG_NO_ROBUST_AV
} else if (os_strcasecmp(cmd, "disable_scs_support") == 0) {
wpa_s->disable_scs_support = !!atoi(value);
} else if (os_strcasecmp(cmd, "disable_mscs_support") == 0) {
wpa_s->disable_mscs_support = !!atoi(value);
#endif /* CONFIG_NO_ROBUST_AV */
} else if (os_strcasecmp(cmd, "disable_eapol_g2_tx") == 0) {
wpa_s->disable_eapol_g2_tx = !!atoi(value);
/* Populate value to wpa_sm if already associated. */
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DISABLE_EAPOL_G2_TX,
wpa_s->disable_eapol_g2_tx);
} else if (os_strcasecmp(cmd, "test_assoc_comeback_type") == 0) {
wpa_s->test_assoc_comeback_type = atoi(value);
#ifdef CONFIG_DPP
} else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) {
os_free(wpa_s->dpp_config_obj_override);
if (value[0] == '\0')
wpa_s->dpp_config_obj_override = NULL;
else
wpa_s->dpp_config_obj_override = os_strdup(value);
} else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) {
os_free(wpa_s->dpp_discovery_override);
if (value[0] == '\0')
wpa_s->dpp_discovery_override = NULL;
else
wpa_s->dpp_discovery_override = os_strdup(value);
} else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) {
os_free(wpa_s->dpp_groups_override);
if (value[0] == '\0')
wpa_s->dpp_groups_override = NULL;
else
wpa_s->dpp_groups_override = os_strdup(value);
} else if (os_strcasecmp(cmd,
"dpp_ignore_netaccesskey_mismatch") == 0) {
wpa_s->dpp_ignore_netaccesskey_mismatch = atoi(value);
} else if (os_strcasecmp(cmd, "dpp_discard_public_action") == 0) {
wpa_s->dpp_discard_public_action = atoi(value);
} else if (os_strcasecmp(cmd, "dpp_test") == 0) {
dpp_test = atoi(value);
#endif /* CONFIG_DPP */
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_FILS
} else if (os_strcasecmp(cmd, "disable_fils") == 0) {
wpa_s->disable_fils = !!atoi(value);
wpa_drv_disable_fils(wpa_s, wpa_s->disable_fils);
wpa_supplicant_set_default_scan_ies(wpa_s);
#endif /* CONFIG_FILS */
#ifndef CONFIG_NO_CONFIG_BLOBS
} else if (os_strcmp(cmd, "blob") == 0) {
ret = wpas_ctrl_set_blob(wpa_s, value);
#endif /* CONFIG_NO_CONFIG_BLOBS */
} else if (os_strcasecmp(cmd, "setband") == 0) {
ret = wpas_ctrl_set_band(wpa_s, value);
#ifdef CONFIG_MBO
} else if (os_strcasecmp(cmd, "non_pref_chan") == 0) {
ret = wpas_mbo_update_non_pref_chan(wpa_s, value);
if (ret == 0) {
value[-1] = '=';
wpa_config_process_global(wpa_s->conf, cmd, -1);
}
} else if (os_strcasecmp(cmd, "mbo_cell_capa") == 0) {
int val = atoi(value);
if (val < MBO_CELL_CAPA_AVAILABLE ||
val > MBO_CELL_CAPA_NOT_SUPPORTED)
return -1;
wpas_mbo_update_cell_capa(wpa_s, val);
} else if (os_strcasecmp(cmd, "oce") == 0) {
int val = atoi(value);
if (val < 0 || val > 3)
return -1;
wpa_s->conf->oce = val;
if (wpa_s->conf->oce) {
if ((wpa_s->conf->oce & OCE_STA) &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA))
wpa_s->enable_oce = OCE_STA;
if ((wpa_s->conf->oce & OCE_STA_CFON) &&
(wpa_s->drv_flags &
WPA_DRIVER_FLAGS_OCE_STA_CFON)) {
/* TODO: Need to add STA-CFON support */
wpa_printf(MSG_ERROR,
"OCE STA-CFON feature is not yet supported");
return -1;
}
} else {
wpa_s->enable_oce = 0;
}
wpa_supplicant_set_default_scan_ies(wpa_s);
#endif /* CONFIG_MBO */
} else if (os_strcasecmp(cmd, "lci") == 0) {
ret = wpas_ctrl_iface_set_lci(wpa_s, value);
} else if (os_strcasecmp(cmd, "tdls_trigger_control") == 0) {
ret = wpa_drv_set_tdls_mode(wpa_s, atoi(value));
} else if (os_strcasecmp(cmd, "relative_rssi") == 0) {
ret = wpas_ctrl_set_relative_rssi(wpa_s, value);
} else if (os_strcasecmp(cmd, "relative_band_adjust") == 0) {
ret = wpas_ctrl_set_relative_band_adjust(wpa_s, value);
} else if (os_strcasecmp(cmd, "ric_ies") == 0) {
ret = wpas_ctrl_iface_set_ric_ies(wpa_s, value);
} else if (os_strcasecmp(cmd, "roaming") == 0) {
ret = wpa_drv_roaming(wpa_s, atoi(value), NULL);
WNM: Collocated Interference Reporting Add support for negotiating WNM Collocated Interference Reporting. This allows hostapd to request associated STAs to report their collocated interference information and wpa_supplicant to process such request and reporting. The actual values (Collocated Interference Report Elements) are out of scope of hostapd and wpa_supplicant, i.e., external components are expected to generated and process these. For hostapd/AP, this mechanism is enabled by setting coloc_intf_reporting=1 in configuration. STAs are requested to perform reporting with "COLOC_INTF_REQ <addr> <Automatic Report Enabled> <Report Timeout>" control interface command. The received reports are indicated as control interface events "COLOC-INTF-REPORT <addr> <dialog token> <hexdump of report elements>". For wpa_supplicant/STA, this mechanism is enabled by setting coloc_intf_reporting=1 in configuration and setting Collocated Interference Report Elements as a hexdump with "SET coloc_intf_elems <hexdump>" control interface command. The hexdump can contain one or more Collocated Interference Report Elements (each including the information element header). For additional testing purposes, received requests are reported with "COLOC-INTF-REQ <dialog token> <automatic report enabled> <report timeout>" control interface events and unsolicited reports can be sent with "COLOC_INTF_REPORT <hexdump>". This commit adds support for reporting changes in the collocated interference (Automatic Report Enabled == 1 and partial 3), but not for periodic reports (2 and other part of 3). Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
2018-10-30 13:00:00 +01:00
#ifdef CONFIG_WNM
} else if (os_strcasecmp(cmd, "coloc_intf_elems") == 0) {
struct wpabuf *elems;
elems = wpabuf_parse_bin(value);
if (!elems)
return -1;
wnm_set_coloc_intf_elems(wpa_s, elems);
#endif /* CONFIG_WNM */
#ifndef CONFIG_NO_ROBUST_AV
} else if (os_strcasecmp(cmd, "enable_dscp_policy_capa") == 0) {
wpa_s->enable_dscp_policy_capa = !!atoi(value);
#endif /* CONFIG_NO_ROBUST_AV */
} else {
value[-1] = '=';
ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
if (ret == 0)
wpa_supplicant_update_config(wpa_s);
else if (ret == 1)
ret = 0;
}
return ret;
}
static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s,
char *cmd, char *buf, size_t buflen)
{
int res = -1;
wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd);
if (os_strcmp(cmd, "version") == 0) {
res = os_snprintf(buf, buflen, "%s", VERSION_STR);
} else if (os_strcasecmp(cmd, "max_command_len") == 0) {
res = os_snprintf(buf, buflen, "%u", CTRL_IFACE_MAX_LEN);
} else if (os_strcasecmp(cmd, "country") == 0) {
if (wpa_s->conf->country[0] && wpa_s->conf->country[1])
res = os_snprintf(buf, buflen, "%c%c",
wpa_s->conf->country[0],
wpa_s->conf->country[1]);
#ifdef CONFIG_WIFI_DISPLAY
} else if (os_strcasecmp(cmd, "wifi_display") == 0) {
int enabled;
if (wpa_s->global->p2p == NULL ||
wpa_s->global->p2p_disabled)
enabled = 0;
else
enabled = wpa_s->global->wifi_display;
res = os_snprintf(buf, buflen, "%d", enabled);
#endif /* CONFIG_WIFI_DISPLAY */
#ifdef CONFIG_TESTING_GET_GTK
} else if (os_strcmp(cmd, "gtk") == 0) {
if (wpa_s->last_gtk_len == 0)
return -1;
res = wpa_snprintf_hex(buf, buflen, wpa_s->last_gtk,
wpa_s->last_gtk_len);
return res;
#endif /* CONFIG_TESTING_GET_GTK */
} else if (os_strcmp(cmd, "tls_library") == 0) {
res = tls_get_library_version(buf, buflen);
#ifdef CONFIG_TESTING_OPTIONS
} else if (os_strcmp(cmd, "anonce") == 0) {
return wpa_snprintf_hex(buf, buflen,
wpa_sm_get_anonce(wpa_s->wpa),
WPA_NONCE_LEN);
} else if (os_strcasecmp(cmd, "last_tk_key_idx") == 0) {
res = os_snprintf(buf, buflen, "%d", wpa_s->last_tk_key_idx);
#endif /* CONFIG_TESTING_OPTIONS */
} else {
res = wpa_config_get_value(cmd, wpa_s->conf, buf, buflen);
}
if (os_snprintf_error(buflen, res))
return -1;
return res;
}
#ifdef IEEE8021X_EAPOL
static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s,
char *addr)
{
u8 bssid[ETH_ALEN];
struct wpa_ssid *ssid = wpa_s->current_ssid;
if (hwaddr_aton(addr, bssid)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH: invalid address "
"'%s'", addr);
return -1;
}
wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH " MACSTR, MAC2STR(bssid));
rsn_preauth_deinit(wpa_s->wpa);
if (rsn_preauth_init(wpa_s->wpa, bssid, ssid ? &ssid->eap : NULL))
return -1;
return 0;
}
#endif /* IEEE8021X_EAPOL */
#ifdef CONFIG_TDLS
static int wpa_supplicant_ctrl_iface_tdls_discover(
struct wpa_supplicant *wpa_s, char *addr)
{
u8 peer[ETH_ALEN];
int ret;
if (hwaddr_aton(addr, peer)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER: invalid "
"address '%s'", addr);
return -1;
}
wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER " MACSTR,
MAC2STR(peer));
if (wpa_tdls_is_external_setup(wpa_s->wpa))
ret = wpa_tdls_send_discovery_request(wpa_s->wpa, peer);
else
ret = wpa_drv_tdls_oper(wpa_s, TDLS_DISCOVERY_REQ, peer);
return ret;
}
static int wpa_supplicant_ctrl_iface_tdls_setup(
struct wpa_supplicant *wpa_s, char *addr)
{
u8 peer[ETH_ALEN];
int ret;
if (hwaddr_aton(addr, peer)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP: invalid "
"address '%s'", addr);
return -1;
}
wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP " MACSTR,
MAC2STR(peer));
if ((wpa_s->conf->tdls_external_control) &&
wpa_tdls_is_external_setup(wpa_s->wpa))
return wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer);
wpa_tdls_remove(wpa_s->wpa, peer);
if (wpa_tdls_is_external_setup(wpa_s->wpa))
ret = wpa_tdls_start(wpa_s->wpa, peer);
else
ret = wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer);
return ret;
}
static int wpa_supplicant_ctrl_iface_tdls_teardown(
struct wpa_supplicant *wpa_s, char *addr)
{
u8 peer[ETH_ALEN];
int ret;
if (os_strcmp(addr, "*") == 0) {
/* remove everyone */
wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN *");
wpa_tdls_teardown_peers(wpa_s->wpa);
return 0;
}
if (hwaddr_aton(addr, peer)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN: invalid "
"address '%s'", addr);
return -1;
}
wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN " MACSTR,
MAC2STR(peer));
if ((wpa_s->conf->tdls_external_control) &&
wpa_tdls_is_external_setup(wpa_s->wpa))
return wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer);
if (wpa_tdls_is_external_setup(wpa_s->wpa))
ret = wpa_tdls_teardown_link(
wpa_s->wpa, peer,
WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
else
ret = wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer);
return ret;
}
static int ctrl_iface_get_capability_tdls(
struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
{
int ret;
ret = os_snprintf(buf, buflen, "%s\n",
wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT ?
(wpa_s->drv_flags &
WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP ?
"EXTERNAL" : "INTERNAL") : "UNSUPPORTED");
if (os_snprintf_error(buflen, ret))
return -1;
return ret;
}
static int wpa_supplicant_ctrl_iface_tdls_chan_switch(
struct wpa_supplicant *wpa_s, char *cmd)
{
u8 peer[ETH_ALEN];
struct hostapd_freq_params freq_params;
u8 oper_class;
char *pos, *end;
if (!wpa_tdls_is_external_setup(wpa_s->wpa)) {
wpa_printf(MSG_INFO,
"tdls_chanswitch: Only supported with external setup");
return -1;
}
os_memset(&freq_params, 0, sizeof(freq_params));
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
oper_class = strtol(pos, &end, 10);
if (pos == end) {
wpa_printf(MSG_INFO,
"tdls_chanswitch: Invalid op class provided");
return -1;
}
pos = end;
freq_params.freq = atoi(pos);
if (freq_params.freq == 0) {
wpa_printf(MSG_INFO, "tdls_chanswitch: Invalid freq provided");
return -1;
}
#define SET_FREQ_SETTING(str) \
do { \
const char *pos2 = os_strstr(pos, " " #str "="); \
if (pos2) { \
pos2 += sizeof(" " #str "=") - 1; \
freq_params.str = atoi(pos2); \
} \
} while (0)
SET_FREQ_SETTING(center_freq1);
SET_FREQ_SETTING(center_freq2);
SET_FREQ_SETTING(bandwidth);
SET_FREQ_SETTING(sec_channel_offset);
#undef SET_FREQ_SETTING
freq_params.ht_enabled = !!os_strstr(pos, " ht");
freq_params.vht_enabled = !!os_strstr(pos, " vht");
if (hwaddr_aton(cmd, peer)) {
wpa_printf(MSG_DEBUG,
"CTRL_IFACE TDLS_CHAN_SWITCH: Invalid address '%s'",
cmd);
return -1;
}
wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CHAN_SWITCH " MACSTR
" OP CLASS %d FREQ %d CENTER1 %d CENTER2 %d BW %d SEC_OFFSET %d%s%s",
MAC2STR(peer), oper_class, freq_params.freq,
freq_params.center_freq1, freq_params.center_freq2,
freq_params.bandwidth, freq_params.sec_channel_offset,
freq_params.ht_enabled ? " HT" : "",
freq_params.vht_enabled ? " VHT" : "");
return wpa_tdls_enable_chan_switch(wpa_s->wpa, peer, oper_class,
&freq_params);
}
static int wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(
struct wpa_supplicant *wpa_s, char *cmd)
{
u8 peer[ETH_ALEN];
if (!wpa_tdls_is_external_setup(wpa_s->wpa)) {
wpa_printf(MSG_INFO,
"tdls_chanswitch: Only supported with external setup");
return -1;
}
if (hwaddr_aton(cmd, peer)) {
wpa_printf(MSG_DEBUG,
"CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH: Invalid address '%s'",
cmd);
return -1;
}
wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH " MACSTR,
MAC2STR(peer));
return wpa_tdls_disable_chan_switch(wpa_s->wpa, peer);
}
static int wpa_supplicant_ctrl_iface_tdls_link_status(
struct wpa_supplicant *wpa_s, const char *addr,
char *buf, size_t buflen)
{
u8 peer[ETH_ALEN];
const char *tdls_status;
int ret;
if (hwaddr_aton(addr, peer)) {
wpa_printf(MSG_DEBUG,
"CTRL_IFACE TDLS_LINK_STATUS: Invalid address '%s'",
addr);
return -1;
}
wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS " MACSTR,
MAC2STR(peer));
tdls_status = wpa_tdls_get_link_status(wpa_s->wpa, peer);
wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS: %s", tdls_status);
ret = os_snprintf(buf, buflen, "TDLS link status: %s\n", tdls_status);
if (os_snprintf_error(buflen, ret))
return -1;
return ret;
}
#endif /* CONFIG_TDLS */
#ifndef CONFIG_NO_WMM_AC
static int wmm_ac_ctrl_addts(struct wpa_supplicant *wpa_s, char *cmd)
{
char *token, *context = NULL;
struct wmm_ac_ts_setup_params params = {
.tsid = 0xff,
.direction = 0xff,
};
while ((token = str_token(cmd, " ", &context))) {
if (sscanf(token, "tsid=%i", &params.tsid) == 1 ||
sscanf(token, "up=%i", &params.user_priority) == 1 ||
sscanf(token, "nominal_msdu_size=%i",
&params.nominal_msdu_size) == 1 ||
sscanf(token, "mean_data_rate=%i",
&params.mean_data_rate) == 1 ||
sscanf(token, "min_phy_rate=%i",
&params.minimum_phy_rate) == 1 ||
sscanf(token, "sba=%i",
&params.surplus_bandwidth_allowance) == 1)
continue;
if (os_strcasecmp(token, "downlink") == 0) {
params.direction = WMM_TSPEC_DIRECTION_DOWNLINK;
} else if (os_strcasecmp(token, "uplink") == 0) {
params.direction = WMM_TSPEC_DIRECTION_UPLINK;
} else if (os_strcasecmp(token, "bidi") == 0) {
params.direction = WMM_TSPEC_DIRECTION_BI_DIRECTIONAL;
} else if (os_strcasecmp(token, "fixed_nominal_msdu") == 0) {
params.fixed_nominal_msdu = 1;
} else {
wpa_printf(MSG_DEBUG,
"CTRL: Invalid WMM_AC_ADDTS parameter: '%s'",
token);
return -1;
}
}
return wpas_wmm_ac_addts(wpa_s, &params);
}
static int wmm_ac_ctrl_delts(struct wpa_supplicant *wpa_s, char *cmd)
{
u8 tsid = atoi(cmd);
return wpas_wmm_ac_delts(wpa_s, tsid);
}
#endif /* CONFIG_NO_WMM_AC */
#ifdef CONFIG_IEEE80211R
static int wpa_supplicant_ctrl_iface_ft_ds(
struct wpa_supplicant *wpa_s, char *addr)
{
u8 target_ap[ETH_ALEN];
struct wpa_bss *bss;
const u8 *mdie;
bool force = os_strstr(addr, " force") != NULL;
if (hwaddr_aton(addr, target_ap)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS: invalid "
"address '%s'", addr);
return -1;
}
wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS " MACSTR, MAC2STR(target_ap));
bss = wpa_bss_get_bssid(wpa_s, target_ap);
if (bss)
mdie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
else
mdie = NULL;
return wpa_ft_start_over_ds(wpa_s->wpa, target_ap, mdie, force);
}
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_WPS
static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s,
char *cmd)
{
u8 bssid[ETH_ALEN], *_bssid = bssid;
#ifdef CONFIG_P2P
u8 p2p_dev_addr[ETH_ALEN];
#endif /* CONFIG_P2P */
#ifdef CONFIG_AP
u8 *_p2p_dev_addr = NULL;
#endif /* CONFIG_AP */
wpa_supplicant: Support Multi-AP backhaul STA onboarding with WPS The Wi-Fi Alliance Multi-AP Specification v1.0 allows onboarding of a backhaul STA through WPS. To enable this, the backhaul STA needs to add a Multi-AP IE to the WFA vendor extension element in the WSC M1 message that indicates it supports the Multi-AP backhaul STA role. The Registrar (if it support Multi-AP onboarding) will respond to that with a WSC M8 message that also contains the Multi-AP IE, and that contains the credentials for the backhaul SSID (which may be different from the SSID on which WPS is performed). Introduce a new parameter to wpas_wps_start_pbc() and allow it to be set via control interface's new multi_ap=1 parameter of WPS_PBC call. multi_ap_backhaul_sta is set to 1 in the automatically created SSID. Thus, if the AP does not support Multi-AP, association will fail and WPS will be terminated. Only wps_pbc is supported. This commit adds the multi_ap argument only to the control socket interface, not to the D-Bus interface. Since WPS associates with the fronthaul BSS instead of the backhaul BSS, we should not drop association if the AP announces fronthaul-only BSS. Still, we should only do that in the specific case of WPS. Therefore, add a check to multi_ap_process_assoc_resp() to allow association with a fronthaul-only BSS if and only if key_mgmt contains WPS. Signed-off-by: Davina Lu <ylu@quantenna.com> Signed-off-by: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com> Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be> Signed-off-by: Daniel Golle <daniel@makrotopia.org> Cc: Marianna Carrera <marianna.carrera.so@quantenna.com>
2019-02-12 15:35:25 +01:00
char *pos;
int multi_ap = 0;
wpa_supplicant: Support Multi-AP backhaul STA onboarding with WPS The Wi-Fi Alliance Multi-AP Specification v1.0 allows onboarding of a backhaul STA through WPS. To enable this, the backhaul STA needs to add a Multi-AP IE to the WFA vendor extension element in the WSC M1 message that indicates it supports the Multi-AP backhaul STA role. The Registrar (if it support Multi-AP onboarding) will respond to that with a WSC M8 message that also contains the Multi-AP IE, and that contains the credentials for the backhaul SSID (which may be different from the SSID on which WPS is performed). Introduce a new parameter to wpas_wps_start_pbc() and allow it to be set via control interface's new multi_ap=1 parameter of WPS_PBC call. multi_ap_backhaul_sta is set to 1 in the automatically created SSID. Thus, if the AP does not support Multi-AP, association will fail and WPS will be terminated. Only wps_pbc is supported. This commit adds the multi_ap argument only to the control socket interface, not to the D-Bus interface. Since WPS associates with the fronthaul BSS instead of the backhaul BSS, we should not drop association if the AP announces fronthaul-only BSS. Still, we should only do that in the specific case of WPS. Therefore, add a check to multi_ap_process_assoc_resp() to allow association with a fronthaul-only BSS if and only if key_mgmt contains WPS. Signed-off-by: Davina Lu <ylu@quantenna.com> Signed-off-by: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com> Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be> Signed-off-by: Daniel Golle <daniel@makrotopia.org> Cc: Marianna Carrera <marianna.carrera.so@quantenna.com>
2019-02-12 15:35:25 +01:00
if (!cmd || os_strcmp(cmd, "any") == 0 ||
os_strncmp(cmd, "any ", 4) == 0) {
_bssid = NULL;
#ifdef CONFIG_P2P
} else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) {
if (hwaddr_aton(cmd + 13, p2p_dev_addr)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid "
"P2P Device Address '%s'",
cmd + 13);
return -1;
}
_p2p_dev_addr = p2p_dev_addr;
#endif /* CONFIG_P2P */
wpa_supplicant: Support Multi-AP backhaul STA onboarding with WPS The Wi-Fi Alliance Multi-AP Specification v1.0 allows onboarding of a backhaul STA through WPS. To enable this, the backhaul STA needs to add a Multi-AP IE to the WFA vendor extension element in the WSC M1 message that indicates it supports the Multi-AP backhaul STA role. The Registrar (if it support Multi-AP onboarding) will respond to that with a WSC M8 message that also contains the Multi-AP IE, and that contains the credentials for the backhaul SSID (which may be different from the SSID on which WPS is performed). Introduce a new parameter to wpas_wps_start_pbc() and allow it to be set via control interface's new multi_ap=1 parameter of WPS_PBC call. multi_ap_backhaul_sta is set to 1 in the automatically created SSID. Thus, if the AP does not support Multi-AP, association will fail and WPS will be terminated. Only wps_pbc is supported. This commit adds the multi_ap argument only to the control socket interface, not to the D-Bus interface. Since WPS associates with the fronthaul BSS instead of the backhaul BSS, we should not drop association if the AP announces fronthaul-only BSS. Still, we should only do that in the specific case of WPS. Therefore, add a check to multi_ap_process_assoc_resp() to allow association with a fronthaul-only BSS if and only if key_mgmt contains WPS. Signed-off-by: Davina Lu <ylu@quantenna.com> Signed-off-by: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com> Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be> Signed-off-by: Daniel Golle <daniel@makrotopia.org> Cc: Marianna Carrera <marianna.carrera.so@quantenna.com>
2019-02-12 15:35:25 +01:00
} else if (os_strncmp(cmd, "multi_ap=", 9) == 0) {
_bssid = NULL;
multi_ap = atoi(cmd + 9);
} else if (hwaddr_aton(cmd, bssid)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid BSSID '%s'",
cmd);
return -1;
}
wpa_supplicant: Support Multi-AP backhaul STA onboarding with WPS The Wi-Fi Alliance Multi-AP Specification v1.0 allows onboarding of a backhaul STA through WPS. To enable this, the backhaul STA needs to add a Multi-AP IE to the WFA vendor extension element in the WSC M1 message that indicates it supports the Multi-AP backhaul STA role. The Registrar (if it support Multi-AP onboarding) will respond to that with a WSC M8 message that also contains the Multi-AP IE, and that contains the credentials for the backhaul SSID (which may be different from the SSID on which WPS is performed). Introduce a new parameter to wpas_wps_start_pbc() and allow it to be set via control interface's new multi_ap=1 parameter of WPS_PBC call. multi_ap_backhaul_sta is set to 1 in the automatically created SSID. Thus, if the AP does not support Multi-AP, association will fail and WPS will be terminated. Only wps_pbc is supported. This commit adds the multi_ap argument only to the control socket interface, not to the D-Bus interface. Since WPS associates with the fronthaul BSS instead of the backhaul BSS, we should not drop association if the AP announces fronthaul-only BSS. Still, we should only do that in the specific case of WPS. Therefore, add a check to multi_ap_process_assoc_resp() to allow association with a fronthaul-only BSS if and only if key_mgmt contains WPS. Signed-off-by: Davina Lu <ylu@quantenna.com> Signed-off-by: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com> Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be> Signed-off-by: Daniel Golle <daniel@makrotopia.org> Cc: Marianna Carrera <marianna.carrera.so@quantenna.com>
2019-02-12 15:35:25 +01:00
if (cmd) {
pos = os_strstr(cmd, " multi_ap=");
if (pos) {
pos += 10;
multi_ap = atoi(pos);
}
}
#ifdef CONFIG_AP
if (wpa_s->ap_iface)
return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid, _p2p_dev_addr);
#endif /* CONFIG_AP */
wpa_supplicant: Support Multi-AP backhaul STA onboarding with WPS The Wi-Fi Alliance Multi-AP Specification v1.0 allows onboarding of a backhaul STA through WPS. To enable this, the backhaul STA needs to add a Multi-AP IE to the WFA vendor extension element in the WSC M1 message that indicates it supports the Multi-AP backhaul STA role. The Registrar (if it support Multi-AP onboarding) will respond to that with a WSC M8 message that also contains the Multi-AP IE, and that contains the credentials for the backhaul SSID (which may be different from the SSID on which WPS is performed). Introduce a new parameter to wpas_wps_start_pbc() and allow it to be set via control interface's new multi_ap=1 parameter of WPS_PBC call. multi_ap_backhaul_sta is set to 1 in the automatically created SSID. Thus, if the AP does not support Multi-AP, association will fail and WPS will be terminated. Only wps_pbc is supported. This commit adds the multi_ap argument only to the control socket interface, not to the D-Bus interface. Since WPS associates with the fronthaul BSS instead of the backhaul BSS, we should not drop association if the AP announces fronthaul-only BSS. Still, we should only do that in the specific case of WPS. Therefore, add a check to multi_ap_process_assoc_resp() to allow association with a fronthaul-only BSS if and only if key_mgmt contains WPS. Signed-off-by: Davina Lu <ylu@quantenna.com> Signed-off-by: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com> Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be> Signed-off-by: Daniel Golle <daniel@makrotopia.org> Cc: Marianna Carrera <marianna.carrera.so@quantenna.com>
2019-02-12 15:35:25 +01:00
return wpas_wps_start_pbc(wpa_s, _bssid, 0, multi_ap);
}
static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s,
char *cmd, char *buf,
size_t buflen)
{
u8 bssid[ETH_ALEN], *_bssid = bssid;
char *pin;
int ret;
pin = os_strchr(cmd, ' ');
if (pin)
*pin++ = '\0';
if (os_strcmp(cmd, "any") == 0)
_bssid = NULL;
else if (os_strcmp(cmd, "get") == 0) {
if (wps_generate_pin((unsigned int *) &ret) < 0)
return -1;
goto done;
} else if (hwaddr_aton(cmd, bssid)) {
2008-12-20 20:51:34 +01:00
wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PIN: invalid BSSID '%s'",
cmd);
return -1;
}
#ifdef CONFIG_AP
if (wpa_s->ap_iface) {
int timeout = 0;
char *pos;
if (pin) {
pos = os_strchr(pin, ' ');
if (pos) {
*pos++ = '\0';
timeout = atoi(pos);
}
}
return wpa_supplicant_ap_wps_pin(wpa_s, _bssid, pin,
buf, buflen, timeout);
}
#endif /* CONFIG_AP */
if (pin) {
ret = wpas_wps_start_pin(wpa_s, _bssid, pin, 0,
DEV_PW_DEFAULT);
if (ret < 0)
return -1;
ret = os_snprintf(buf, buflen, "%s", pin);
if (os_snprintf_error(buflen, ret))
return -1;
return ret;
}
ret = wpas_wps_start_pin(wpa_s, _bssid, NULL, 0, DEV_PW_DEFAULT);
if (ret < 0)
return -1;
done:
/* Return the generated PIN */
ret = os_snprintf(buf, buflen, "%08d", ret);
if (os_snprintf_error(buflen, ret))
return -1;
return ret;
}
static int wpa_supplicant_ctrl_iface_wps_check_pin(
struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
{
char pin[9];
size_t len;
char *pos;
int ret;
wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN",
(u8 *) cmd, os_strlen(cmd));
for (pos = cmd, len = 0; *pos != '\0'; pos++) {
if (*pos < '0' || *pos > '9')
continue;
pin[len++] = *pos;
if (len == 9) {
wpa_printf(MSG_DEBUG, "WPS: Too long PIN");
return -1;
}
}
if (len != 4 && len != 8) {
wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len);
return -1;
}
pin[len] = '\0';
if (len == 8) {
unsigned int pin_val;
pin_val = atoi(pin);
if (!wps_pin_valid(pin_val)) {
wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit");
ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n");
if (os_snprintf_error(buflen, ret))
return -1;
return ret;
}
}
ret = os_snprintf(buf, buflen, "%s", pin);
if (os_snprintf_error(buflen, ret))
return -1;
return ret;
}
#ifdef CONFIG_WPS_NFC
static int wpa_supplicant_ctrl_iface_wps_nfc(struct wpa_supplicant *wpa_s,
char *cmd)
{
u8 bssid[ETH_ALEN], *_bssid = bssid;
if (cmd == NULL || cmd[0] == '\0')
_bssid = NULL;
else if (hwaddr_aton(cmd, bssid))
return -1;
return wpas_wps_start_nfc(wpa_s, NULL, _bssid, NULL, 0, 0, NULL, NULL,
0, 0);
}
static int wpa_supplicant_ctrl_iface_wps_nfc_config_token(
struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
{
int ndef;
struct wpabuf *buf;
int res;
char *pos;
pos = os_strchr(cmd, ' ');
if (pos)
*pos++ = '\0';
if (os_strcmp(cmd, "WPS") == 0)
ndef = 0;
else if (os_strcmp(cmd, "NDEF") == 0)
ndef = 1;
else
return -1;
buf = wpas_wps_nfc_config_token(wpa_s, ndef, pos);
if (buf == NULL)
return -1;
res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
wpabuf_len(buf));
reply[res++] = '\n';
reply[res] = '\0';
wpabuf_free(buf);
return res;
}
static int wpa_supplicant_ctrl_iface_wps_nfc_token(
struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
{
int ndef;
struct wpabuf *buf;
int res;
if (os_strcmp(cmd, "WPS") == 0)
ndef = 0;
else if (os_strcmp(cmd, "NDEF") == 0)
ndef = 1;
else
return -1;
buf = wpas_wps_nfc_token(wpa_s, ndef);
if (buf == NULL)
return -1;
res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
wpabuf_len(buf));
reply[res++] = '\n';
reply[res] = '\0';
wpabuf_free(buf);
return res;
}
static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read(
struct wpa_supplicant *wpa_s, char *pos)
{
size_t len;
struct wpabuf *buf;
int ret;
char *freq;
int forced_freq = 0;
freq = strstr(pos, " freq=");
if (freq) {
*freq = '\0';
freq += 6;
forced_freq = atoi(freq);
}
len = os_strlen(pos);
if (len & 0x01)
return -1;
len /= 2;
buf = wpabuf_alloc(len);
if (buf == NULL)
return -1;
if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) {
wpabuf_free(buf);
return -1;
}
ret = wpas_wps_nfc_tag_read(wpa_s, buf, forced_freq);
wpabuf_free(buf);
return ret;
}
static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s,
char *reply, size_t max_len,
int ndef)
{
struct wpabuf *buf;
int res;
buf = wpas_wps_nfc_handover_req(wpa_s, ndef);
if (buf == NULL)
return -1;
res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
wpabuf_len(buf));
reply[res++] = '\n';
reply[res] = '\0';
wpabuf_free(buf);
return res;
}
#ifdef CONFIG_P2P
static int wpas_ctrl_nfc_get_handover_req_p2p(struct wpa_supplicant *wpa_s,
char *reply, size_t max_len,
int ndef)
{
struct wpabuf *buf;
int res;
buf = wpas_p2p_nfc_handover_req(wpa_s, ndef);
if (buf == NULL) {
wpa_printf(MSG_DEBUG, "P2P: Could not generate NFC handover request");
return -1;
}
res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
wpabuf_len(buf));
reply[res++] = '\n';
reply[res] = '\0';
wpabuf_free(buf);
return res;
}
#endif /* CONFIG_P2P */
static int wpas_ctrl_nfc_get_handover_req(struct wpa_supplicant *wpa_s,
char *cmd, char *reply,
size_t max_len)
{
char *pos;
int ndef;
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
if (os_strcmp(cmd, "WPS") == 0)
ndef = 0;
else if (os_strcmp(cmd, "NDEF") == 0)
ndef = 1;
else
return -1;
if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) {
if (!ndef)
return -1;
return wpas_ctrl_nfc_get_handover_req_wps(
wpa_s, reply, max_len, ndef);
}
#ifdef CONFIG_P2P
if (os_strcmp(pos, "P2P-CR") == 0) {
return wpas_ctrl_nfc_get_handover_req_p2p(
wpa_s, reply, max_len, ndef);
}
#endif /* CONFIG_P2P */
return -1;
}
static int wpas_ctrl_nfc_get_handover_sel_wps(struct wpa_supplicant *wpa_s,
char *reply, size_t max_len,
int ndef, int cr, char *uuid)
{
struct wpabuf *buf;
int res;
buf = wpas_wps_nfc_handover_sel(wpa_s, ndef, cr, uuid);
if (buf == NULL)
return -1;
res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
wpabuf_len(buf));
reply[res++] = '\n';
reply[res] = '\0';
wpabuf_free(buf);
return res;
}
#ifdef CONFIG_P2P
static int wpas_ctrl_nfc_get_handover_sel_p2p(struct wpa_supplicant *wpa_s,
char *reply, size_t max_len,
int ndef, int tag)
{
struct wpabuf *buf;
int res;
buf = wpas_p2p_nfc_handover_sel(wpa_s, ndef, tag);
if (buf == NULL)
return -1;
res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
wpabuf_len(buf));
reply[res++] = '\n';
reply[res] = '\0';
wpabuf_free(buf);
return res;
}
#endif /* CONFIG_P2P */
static int wpas_ctrl_nfc_get_handover_sel(struct wpa_supplicant *wpa_s,
char *cmd, char *reply,
size_t max_len)
{
char *pos, *pos2;
int ndef;
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
if (os_strcmp(cmd, "WPS") == 0)
ndef = 0;
else if (os_strcmp(cmd, "NDEF") == 0)
ndef = 1;
else
return -1;
pos2 = os_strchr(pos, ' ');
if (pos2)
*pos2++ = '\0';
if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) {
if (!ndef)
return -1;
return wpas_ctrl_nfc_get_handover_sel_wps(
wpa_s, reply, max_len, ndef,
os_strcmp(pos, "WPS-CR") == 0, pos2);
}
#ifdef CONFIG_P2P
if (os_strcmp(pos, "P2P-CR") == 0) {
return wpas_ctrl_nfc_get_handover_sel_p2p(
wpa_s, reply, max_len, ndef, 0);
}
if (os_strcmp(pos, "P2P-CR-TAG") == 0) {
return wpas_ctrl_nfc_get_handover_sel_p2p(
wpa_s, reply, max_len, ndef, 1);
}
#endif /* CONFIG_P2P */
return -1;
}
static int wpas_ctrl_nfc_report_handover(struct wpa_supplicant *wpa_s,
char *cmd)
{
size_t len;
struct wpabuf *req, *sel;
int ret;
char *pos, *role, *type, *pos2;
#ifdef CONFIG_P2P
char *freq;
int forced_freq = 0;
freq = strstr(cmd, " freq=");
if (freq) {
*freq = '\0';
freq += 6;
forced_freq = atoi(freq);
}
#endif /* CONFIG_P2P */
role = cmd;
pos = os_strchr(role, ' ');
if (pos == NULL) {
wpa_printf(MSG_DEBUG, "NFC: Missing type in handover report");
return -1;
}
*pos++ = '\0';
type = pos;
pos = os_strchr(type, ' ');
if (pos == NULL) {
wpa_printf(MSG_DEBUG, "NFC: Missing request message in handover report");
return -1;
}
*pos++ = '\0';
pos2 = os_strchr(pos, ' ');
if (pos2 == NULL) {
wpa_printf(MSG_DEBUG, "NFC: Missing select message in handover report");
return -1;
}
*pos2++ = '\0';
len = os_strlen(pos);
if (len & 0x01) {
wpa_printf(MSG_DEBUG, "NFC: Invalid request message length in handover report");
return -1;
}
len /= 2;
req = wpabuf_alloc(len);
if (req == NULL) {
wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for request message");
return -1;
}
if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) {
wpa_printf(MSG_DEBUG, "NFC: Invalid request message hexdump in handover report");
wpabuf_free(req);
return -1;
}
len = os_strlen(pos2);
if (len & 0x01) {
wpa_printf(MSG_DEBUG, "NFC: Invalid select message length in handover report");
wpabuf_free(req);
return -1;
}
len /= 2;
sel = wpabuf_alloc(len);
if (sel == NULL) {
wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for select message");
wpabuf_free(req);
return -1;
}
if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) {
wpa_printf(MSG_DEBUG, "NFC: Invalid select message hexdump in handover report");
wpabuf_free(req);
wpabuf_free(sel);
return -1;
}
wpa_printf(MSG_DEBUG, "NFC: Connection handover reported - role=%s type=%s req_len=%d sel_len=%d",
role, type, (int) wpabuf_len(req), (int) wpabuf_len(sel));
if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "WPS") == 0) {
ret = wpas_wps_nfc_report_handover(wpa_s, req, sel);
#ifdef CONFIG_AP
} else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0)
{
ret = wpas_ap_wps_nfc_report_handover(wpa_s, req, sel);
if (ret < 0)
ret = wpas_er_wps_nfc_report_handover(wpa_s, req, sel);
#endif /* CONFIG_AP */
#ifdef CONFIG_P2P
} else if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "P2P") == 0)
{
ret = wpas_p2p_nfc_report_handover(wpa_s, 1, req, sel, 0);
} else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "P2P") == 0)
{
ret = wpas_p2p_nfc_report_handover(wpa_s, 0, req, sel,
forced_freq);
#endif /* CONFIG_P2P */
} else {
wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover "
"reported: role=%s type=%s", role, type);
ret = -1;
}
wpabuf_free(req);
wpabuf_free(sel);
if (ret)
wpa_printf(MSG_DEBUG, "NFC: Failed to process reported handover messages");
return ret;
}
#endif /* CONFIG_WPS_NFC */
static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
char *cmd)
{
u8 bssid[ETH_ALEN];
char *pin;
char *new_ssid;
char *new_auth;
char *new_encr;
char *new_key;
struct wps_new_ap_settings ap;
pin = os_strchr(cmd, ' ');
if (pin == NULL)
return -1;
*pin++ = '\0';
if (hwaddr_aton(cmd, bssid)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_REG: invalid BSSID '%s'",
cmd);
return -1;
}
new_ssid = os_strchr(pin, ' ');
if (new_ssid == NULL)
return wpas_wps_start_reg(wpa_s, bssid, pin, NULL);
*new_ssid++ = '\0';
new_auth = os_strchr(new_ssid, ' ');
if (new_auth == NULL)
return -1;
*new_auth++ = '\0';
new_encr = os_strchr(new_auth, ' ');
if (new_encr == NULL)
return -1;
*new_encr++ = '\0';
new_key = os_strchr(new_encr, ' ');
if (new_key == NULL)
return -1;
*new_key++ = '\0';
os_memset(&ap, 0, sizeof(ap));
ap.ssid_hex = new_ssid;
ap.auth = new_auth;
ap.encr = new_encr;
ap.key_hex = new_key;
return wpas_wps_start_reg(wpa_s, bssid, pin, &ap);
}
#ifdef CONFIG_AP
static int wpa_supplicant_ctrl_iface_wps_ap_pin(struct wpa_supplicant *wpa_s,
char *cmd, char *buf,
size_t buflen)
{
int timeout = 300;
char *pos;
const char *pin_txt;
if (!wpa_s->ap_iface)
return -1;
pos = os_strchr(cmd, ' ');
if (pos)
*pos++ = '\0';
if (os_strcmp(cmd, "disable") == 0) {
wpas_wps_ap_pin_disable(wpa_s);
return os_snprintf(buf, buflen, "OK\n");
}
if (os_strcmp(cmd, "random") == 0) {
if (pos)
timeout = atoi(pos);
pin_txt = wpas_wps_ap_pin_random(wpa_s, timeout);
if (pin_txt == NULL)
return -1;
return os_snprintf(buf, buflen, "%s", pin_txt);
}
if (os_strcmp(cmd, "get") == 0) {
pin_txt = wpas_wps_ap_pin_get(wpa_s);
if (pin_txt == NULL)
return -1;
return os_snprintf(buf, buflen, "%s", pin_txt);
}
if (os_strcmp(cmd, "set") == 0) {
char *pin;
if (pos == NULL)
return -1;
pin = pos;
pos = os_strchr(pos, ' ');
if (pos) {
*pos++ = '\0';
timeout = atoi(pos);
}
if (os_strlen(pin) > buflen)
return -1;
if (wpas_wps_ap_pin_set(wpa_s, pin, timeout) < 0)
return -1;
return os_snprintf(buf, buflen, "%s", pin);
}
return -1;
}
#endif /* CONFIG_AP */
#ifdef CONFIG_WPS_ER
static int wpa_supplicant_ctrl_iface_wps_er_pin(struct wpa_supplicant *wpa_s,
char *cmd)
{
char *uuid = cmd, *pin, *pos;
u8 addr_buf[ETH_ALEN], *addr = NULL;
pin = os_strchr(uuid, ' ');
if (pin == NULL)
return -1;
*pin++ = '\0';
pos = os_strchr(pin, ' ');
if (pos) {
*pos++ = '\0';
if (hwaddr_aton(pos, addr_buf) == 0)
addr = addr_buf;
}
return wpas_wps_er_add_pin(wpa_s, addr, uuid, pin);
}
static int wpa_supplicant_ctrl_iface_wps_er_learn(struct wpa_supplicant *wpa_s,
char *cmd)
{
char *uuid = cmd, *pin;
pin = os_strchr(uuid, ' ');
if (pin == NULL)
return -1;
*pin++ = '\0';
return wpas_wps_er_learn(wpa_s, uuid, pin);
}
static int wpa_supplicant_ctrl_iface_wps_er_set_config(
struct wpa_supplicant *wpa_s, char *cmd)
{
char *uuid = cmd, *id;
id = os_strchr(uuid, ' ');
if (id == NULL)
return -1;
*id++ = '\0';
return wpas_wps_er_set_config(wpa_s, uuid, atoi(id));
}
static int wpa_supplicant_ctrl_iface_wps_er_config(
struct wpa_supplicant *wpa_s, char *cmd)
{
char *pin;
char *new_ssid;
char *new_auth;
char *new_encr;
char *new_key;
struct wps_new_ap_settings ap;
pin = os_strchr(cmd, ' ');
if (pin == NULL)
return -1;
*pin++ = '\0';
new_ssid = os_strchr(pin, ' ');
if (new_ssid == NULL)
return -1;
*new_ssid++ = '\0';
new_auth = os_strchr(new_ssid, ' ');
if (new_auth == NULL)
return -1;
*new_auth++ = '\0';
new_encr = os_strchr(new_auth, ' ');
if (new_encr == NULL)
return -1;
*new_encr++ = '\0';
new_key = os_strchr(new_encr, ' ');
if (new_key == NULL)
return -1;
*new_key++ = '\0';
os_memset(&ap, 0, sizeof(ap));
ap.ssid_hex = new_ssid;
ap.auth = new_auth;
ap.encr = new_encr;
ap.key_hex = new_key;
return wpas_wps_er_config(wpa_s, cmd, pin, &ap);
}
#ifdef CONFIG_WPS_NFC
static int wpa_supplicant_ctrl_iface_wps_er_nfc_config_token(
struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
{
int ndef;
struct wpabuf *buf;
int res;
char *uuid;
uuid = os_strchr(cmd, ' ');
if (uuid == NULL)
return -1;
*uuid++ = '\0';
if (os_strcmp(cmd, "WPS") == 0)
ndef = 0;
else if (os_strcmp(cmd, "NDEF") == 0)
ndef = 1;
else
return -1;
buf = wpas_wps_er_nfc_config_token(wpa_s, ndef, uuid);
if (buf == NULL)
return -1;
res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
wpabuf_len(buf));
reply[res++] = '\n';
reply[res] = '\0';
wpabuf_free(buf);
return res;
}
#endif /* CONFIG_WPS_NFC */
#endif /* CONFIG_WPS_ER */
#endif /* CONFIG_WPS */
#ifdef CONFIG_IBSS_RSN
static int wpa_supplicant_ctrl_iface_ibss_rsn(
struct wpa_supplicant *wpa_s, char *addr)
{
u8 peer[ETH_ALEN];
if (hwaddr_aton(addr, peer)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE IBSS_RSN: invalid "
"address '%s'", addr);
return -1;
}
wpa_printf(MSG_DEBUG, "CTRL_IFACE IBSS_RSN " MACSTR,
MAC2STR(peer));
return ibss_rsn_start(wpa_s->ibss_rsn, peer);
}
#endif /* CONFIG_IBSS_RSN */
static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s,
char *rsp)
{
#ifdef IEEE8021X_EAPOL
char *pos, *id_pos;
int id;
struct wpa_ssid *ssid;
pos = os_strchr(rsp, '-');
if (pos == NULL)
return -1;
*pos++ = '\0';
id_pos = pos;
pos = os_strchr(pos, ':');
if (pos == NULL)
return -1;
*pos++ = '\0';
id = atoi(id_pos);
wpa_printf(MSG_DEBUG, "CTRL_IFACE: field=%s id=%d", rsp, id);
wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
(u8 *) pos, os_strlen(pos));
ssid = wpa_config_get_network(wpa_s->conf, id);
if (ssid == NULL) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
"to update", id);
return -1;
}
return wpa_supplicant_ctrl_iface_ctrl_rsp_handle(wpa_s, ssid, rsp,
pos);
#else /* IEEE8021X_EAPOL */
wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included");
return -1;
#endif /* IEEE8021X_EAPOL */
}
static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
const char *params,
char *buf, size_t buflen)
{
char *pos, *end, tmp[30];
int res, verbose, wps, ret;
#ifdef CONFIG_HS20
const u8 *hs20;
#endif /* CONFIG_HS20 */
const u8 *sess_id;
size_t sess_id_len;
if (os_strcmp(params, "-DRIVER") == 0)
return wpa_drv_status(wpa_s, buf, buflen);
verbose = os_strcmp(params, "-VERBOSE") == 0;
wps = os_strcmp(params, "-WPS") == 0;
pos = buf;
end = buf + buflen;
if (wpa_s->wpa_state >= WPA_ASSOCIATED) {
struct wpa_ssid *ssid = wpa_s->current_ssid;
ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n",
MAC2STR(wpa_s->bssid));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
ret = os_snprintf(pos, end - pos, "freq=%u\n",
wpa_s->assoc_freq);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
if (ssid) {
u8 *_ssid = ssid->ssid;
size_t ssid_len = ssid->ssid_len;
u8 ssid_buf[SSID_MAX_LEN];
if (ssid_len == 0) {
int _res = wpa_drv_get_ssid(wpa_s, ssid_buf);
if (_res < 0)
ssid_len = 0;
else
ssid_len = _res;
_ssid = ssid_buf;
}
ret = os_snprintf(pos, end - pos, "ssid=%s\nid=%d\n",
wpa_ssid_txt(_ssid, ssid_len),
ssid->id);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
if (wps && ssid->passphrase &&
wpa_key_mgmt_wpa_psk(ssid->key_mgmt) &&
(ssid->mode == WPAS_MODE_AP ||
ssid->mode == WPAS_MODE_P2P_GO)) {
ret = os_snprintf(pos, end - pos,
"passphrase=%s\n",
ssid->passphrase);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (ssid->id_str) {
ret = os_snprintf(pos, end - pos,
"id_str=%s\n",
ssid->id_str);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
switch (ssid->mode) {
case WPAS_MODE_INFRA:
ret = os_snprintf(pos, end - pos,
"mode=station\n");
break;
case WPAS_MODE_IBSS:
ret = os_snprintf(pos, end - pos,
"mode=IBSS\n");
break;
case WPAS_MODE_AP:
ret = os_snprintf(pos, end - pos,
"mode=AP\n");
break;
case WPAS_MODE_P2P_GO:
ret = os_snprintf(pos, end - pos,
"mode=P2P GO\n");
break;
case WPAS_MODE_P2P_GROUP_FORMATION:
ret = os_snprintf(pos, end - pos,
"mode=P2P GO - group "
"formation\n");
break;
case WPAS_MODE_MESH:
ret = os_snprintf(pos, end - pos,
"mode=mesh\n");
break;
default:
ret = 0;
break;
}
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (wpa_s->connection_set &&
(wpa_s->connection_ht || wpa_s->connection_vht ||
wpa_s->connection_he || wpa_s->connection_eht)) {
ret = os_snprintf(pos, end - pos,
"wifi_generation=%u\n",
wpa_s->connection_eht ? 7 :
(wpa_s->connection_he ? 6 :
(wpa_s->connection_vht ? 5 : 4)));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
2009-10-16 17:35:45 +02:00
#ifdef CONFIG_AP
if (wpa_s->ap_iface) {
pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos,
end - pos,
verbose);
} else
#endif /* CONFIG_AP */
pos += wpa_sm_get_status(wpa_s->wpa, pos, end - pos, verbose);
}
#ifdef CONFIG_SME
#ifdef CONFIG_SAE
if (wpa_s->wpa_state >= WPA_ASSOCIATED &&
#ifdef CONFIG_AP
!wpa_s->ap_iface &&
#endif /* CONFIG_AP */
wpa_s->sme.sae.state == SAE_ACCEPTED) {
ret = os_snprintf(pos, end - pos, "sae_group=%d\n"
"sae_h2e=%d\n"
"sae_pk=%d\n",
wpa_s->sme.sae.group,
wpa_s->sme.sae.h2e,
wpa_s->sme.sae.pk);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_SAE */
#endif /* CONFIG_SME */
ret = os_snprintf(pos, end - pos, "wpa_state=%s\n",
wpa_supplicant_state_txt(wpa_s->wpa_state));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
if (wpa_s->l2 &&
l2_packet_get_ip_addr(wpa_s->l2, tmp, sizeof(tmp)) >= 0) {
ret = os_snprintf(pos, end - pos, "ip_address=%s\n", tmp);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#ifdef CONFIG_P2P
if (wpa_s->global->p2p) {
ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR
"\n", MAC2STR(wpa_s->global->p2p_dev_addr));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_P2P */
ret = os_snprintf(pos, end - pos, "address=" MACSTR "\n",
MAC2STR(wpa_s->own_addr));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
if (wpa_s->valid_links) {
ret = os_snprintf(pos, end - pos, "ap_mld_addr=" MACSTR "\n",
MAC2STR(wpa_s->ap_mld_addr));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#ifdef CONFIG_HS20
if (wpa_s->current_bss &&
(hs20 = wpa_bss_get_vendor_ie(wpa_s->current_bss,
HS20_IE_VENDOR_TYPE)) &&
wpa_s->wpa_proto == WPA_PROTO_RSN &&
wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
int release = 1;
if (hs20[1] >= 5) {
u8 rel_num = (hs20[6] & 0xf0) >> 4;
release = rel_num + 1;
}
ret = os_snprintf(pos, end - pos, "hs20=%d\n", release);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (wpa_s->current_ssid) {
struct wpa_cred *cred;
char *type;
for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
size_t i;
if (wpa_s->current_ssid->parent_cred != cred)
continue;
if (cred->provisioning_sp) {
ret = os_snprintf(pos, end - pos,
"provisioning_sp=%s\n",
cred->provisioning_sp);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (!cred->domain)
goto no_domain;
i = 0;
if (wpa_s->current_bss && wpa_s->current_bss->anqp) {
struct wpabuf *names =
wpa_s->current_bss->anqp->domain_name;
for (i = 0; names && i < cred->num_domain; i++)
{
if (domain_name_list_contains(
names, cred->domain[i], 1))
break;
}
if (i == cred->num_domain)
i = 0; /* show first entry by default */
}
ret = os_snprintf(pos, end - pos, "home_sp=%s\n",
cred->domain[i]);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
no_domain:
if (wpa_s->current_bss == NULL ||
wpa_s->current_bss->anqp == NULL)
res = -1;
else
res = interworking_home_sp_cred(
wpa_s, cred,
wpa_s->current_bss->anqp->domain_name);
if (res > 0)
type = "home";
else if (res == 0)
type = "roaming";
else
type = "unknown";
ret = os_snprintf(pos, end - pos, "sp_type=%s\n", type);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
break;
}
}
#endif /* CONFIG_HS20 */
if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos,
verbose);
if (res >= 0)
pos += res;
}
#ifdef CONFIG_MACSEC
res = ieee802_1x_kay_get_status(wpa_s->kay, pos, end - pos);
if (res > 0)
pos += res;
#endif /* CONFIG_MACSEC */
sess_id = eapol_sm_get_session_id(wpa_s->eapol, &sess_id_len);
if (sess_id) {
char *start = pos;
ret = os_snprintf(pos, end - pos, "eap_session_id=");
if (os_snprintf_error(end - pos, ret))
return start - buf;
pos += ret;
ret = wpa_snprintf_hex(pos, end - pos, sess_id, sess_id_len);
if (ret <= 0)
return start - buf;
pos += ret;
ret = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, ret))
return start - buf;
pos += ret;
}
res = rsn_preauth_get_status(wpa_s->wpa, pos, end - pos, verbose);
if (res >= 0)
pos += res;
#ifdef CONFIG_WPS
{
char uuid_str[100];
uuid_bin2str(wpa_s->wps->uuid, uuid_str, sizeof(uuid_str));
ret = os_snprintf(pos, end - pos, "uuid=%s\n", uuid_str);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_WPS */
if (wpa_s->ieee80211ac) {
ret = os_snprintf(pos, end - pos, "ieee80211ac=1\n");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#ifdef ANDROID
/*
* Allow using the STATUS command with default behavior, say for debug,
* i.e., don't generate a "fake" CONNECTION and SUPPLICANT_STATE_CHANGE
* events with STATUS-NO_EVENTS.
*/
if (os_strcmp(params, "-NO_EVENTS")) {
wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE
"id=%d state=%d BSSID=" MACSTR " SSID=%s",
wpa_s->current_ssid ? wpa_s->current_ssid->id : -1,
wpa_s->wpa_state,
MAC2STR(wpa_s->bssid),
wpa_s->current_ssid && wpa_s->current_ssid->ssid ?
wpa_ssid_txt(wpa_s->current_ssid->ssid,
wpa_s->current_ssid->ssid_len) : "");
if (wpa_s->wpa_state == WPA_COMPLETED) {
struct wpa_ssid *ssid = wpa_s->current_ssid;
char mld_addr[50];
mld_addr[0] = '\0';
if (wpa_s->valid_links)
os_snprintf(mld_addr, sizeof(mld_addr),
" ap_mld_addr=" MACSTR,
MAC2STR(wpa_s->ap_mld_addr));
wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED
"- connection to " MACSTR
" completed %s [id=%d id_str=%s]%s",
MAC2STR(wpa_s->bssid), "(auth)",
ssid ? ssid->id : -1,
ssid && ssid->id_str ? ssid->id_str : "",
mld_addr);
}
}
#endif /* ANDROID */
return pos - buf;
}
static int wpa_supplicant_ctrl_iface_bssid(struct wpa_supplicant *wpa_s,
char *cmd)
{
char *pos;
int id;
struct wpa_ssid *ssid;
u8 bssid[ETH_ALEN];
/* cmd: "<network id> <BSSID>" */
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
id = atoi(cmd);
wpa_printf(MSG_DEBUG, "CTRL_IFACE: id=%d bssid='%s'", id, pos);
if (hwaddr_aton(pos, bssid)) {
wpa_printf(MSG_DEBUG ,"CTRL_IFACE: invalid BSSID '%s'", pos);
return -1;
}
ssid = wpa_config_get_network(wpa_s->conf, id);
if (ssid == NULL) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
"to update", id);
return -1;
}
os_memcpy(ssid->bssid, bssid, ETH_ALEN);
ssid->bssid_set = !is_zero_ether_addr(bssid);
return 0;
}
static int wpa_supplicant_ctrl_iface_bssid_ignore(struct wpa_supplicant *wpa_s,
char *cmd, char *buf,
size_t buflen)
{
u8 bssid[ETH_ALEN];
struct wpa_bssid_ignore *e;
char *pos, *end;
int ret;
/* cmd: "BSSID_IGNORE [<BSSID>]" */
if (*cmd == '\0') {
pos = buf;
end = buf + buflen;
e = wpa_s->bssid_ignore;
while (e) {
ret = os_snprintf(pos, end - pos, MACSTR "\n",
MAC2STR(e->bssid));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
e = e->next;
}
return pos - buf;
}
cmd++;
if (os_strncmp(cmd, "clear", 5) == 0) {
wpa_bssid_ignore_clear(wpa_s);
os_memcpy(buf, "OK\n", 3);
return 3;
}
wpa_printf(MSG_DEBUG, "CTRL_IFACE: BSSID_IGNORE bssid='%s'", cmd);
if (hwaddr_aton(cmd, bssid)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: invalid BSSID '%s'", cmd);
return -1;
}
/*
* Add the BSSID twice, so its count will be 2, causing it to be
* skipped when processing scan results.
*/
ret = wpa_bssid_ignore_add(wpa_s, bssid);
if (ret < 0)
return -1;
ret = wpa_bssid_ignore_add(wpa_s, bssid);
if (ret < 0)
return -1;
os_memcpy(buf, "OK\n", 3);
return 3;
}
static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s,
char *cmd, char *buf,
size_t buflen)
{
char *pos, *end, *stamp;
int ret;
/* cmd: "LOG_LEVEL [<level>]" */
if (*cmd == '\0') {
pos = buf;
end = buf + buflen;
ret = os_snprintf(pos, end - pos, "Current level: %s\n"
"Timestamp: %d\n",
debug_level_str(wpa_debug_level),
wpa_debug_timestamp);
if (os_snprintf_error(end - pos, ret))
ret = 0;
return ret;
}
while (*cmd == ' ')
cmd++;
stamp = os_strchr(cmd, ' ');
if (stamp) {
*stamp++ = '\0';
while (*stamp == ' ') {
stamp++;
}
}
if (os_strlen(cmd)) {
int level = str_to_debug_level(cmd);
if (level < 0)
return -1;
wpa_debug_level = level;
}
if (stamp && os_strlen(stamp))
wpa_debug_timestamp = atoi(stamp);
os_memcpy(buf, "OK\n", 3);
return 3;
}
static int wpa_supplicant_ctrl_iface_list_networks(
struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
{
char *pos, *end, *prev;
struct wpa_ssid *ssid;
int ret;
pos = buf;
end = buf + buflen;
ret = os_snprintf(pos, end - pos,
"network id / ssid / bssid / flags\n");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
ssid = wpa_s->conf->ssid;
/* skip over ssids until we find next one */
if (cmd != NULL && os_strncmp(cmd, "LAST_ID=", 8) == 0) {
int last_id = atoi(cmd + 8);
if (last_id != -1) {
while (ssid != NULL && ssid->id <= last_id) {
ssid = ssid->next;
}
}
}
while (ssid) {
prev = pos;
ret = os_snprintf(pos, end - pos, "%d\t%s",
ssid->id,
wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
if (os_snprintf_error(end - pos, ret))
return prev - buf;
pos += ret;
if (ssid->bssid_set) {
ret = os_snprintf(pos, end - pos, "\t" MACSTR,
MAC2STR(ssid->bssid));
} else {
ret = os_snprintf(pos, end - pos, "\tany");
}
if (os_snprintf_error(end - pos, ret))
return prev - buf;
pos += ret;
ret = os_snprintf(pos, end - pos, "\t%s%s%s%s",
ssid == wpa_s->current_ssid ?
"[CURRENT]" : "",
ssid->disabled ? "[DISABLED]" : "",
ssid->disabled_until.sec ?
"[TEMP-DISABLED]" : "",
ssid->disabled == 2 ? "[P2P-PERSISTENT]" :
"");
if (os_snprintf_error(end - pos, ret))
return prev - buf;
pos += ret;
ret = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, ret))
return prev - buf;
pos += ret;
ssid = ssid->next;
}
return pos - buf;
}
static char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher)
{
int ret;
ret = os_snprintf(pos, end - pos, "-");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
ret = wpa_write_ciphers(pos, end, cipher, "+");
if (ret < 0)
return pos;
pos += ret;
return pos;
}
static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto,
const u8 *ie, size_t ie_len)
{
struct wpa_ie_data data;
char *start;
int ret;
ret = os_snprintf(pos, end - pos, "[%s-", proto);
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
if (wpa_parse_wpa_ie(ie, ie_len, &data) < 0) {
ret = os_snprintf(pos, end - pos, "?]");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
return pos;
}
start = pos;
if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
ret = os_snprintf(pos, end - pos, "%sEAP",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
if (data.key_mgmt & WPA_KEY_MGMT_PSK) {
ret = os_snprintf(pos, end - pos, "%sPSK",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
if (data.key_mgmt & WPA_KEY_MGMT_WPA_NONE) {
ret = os_snprintf(pos, end - pos, "%sNone",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
if (data.key_mgmt & WPA_KEY_MGMT_SAE) {
ret = os_snprintf(pos, end - pos, "%sSAE",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
if (data.key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
ret = os_snprintf(pos, end - pos, "%sSAE-EXT-KEY",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
#ifdef CONFIG_IEEE80211R
if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
ret = os_snprintf(pos, end - pos, "%sFT/EAP",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) {
ret = os_snprintf(pos, end - pos, "%sFT/PSK",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE) {
ret = os_snprintf(pos, end - pos, "%sFT/SAE",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
ret = os_snprintf(pos, end - pos, "%sFT/SAE-EXT-KEY",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
#endif /* CONFIG_IEEE80211R */
if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
ret = os_snprintf(pos, end - pos, "%sEAP-SHA256",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
ret = os_snprintf(pos, end - pos, "%sPSK-SHA256",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
#ifdef CONFIG_SUITEB
if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
ret = os_snprintf(pos, end - pos, "%sEAP-SUITE-B",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
#endif /* CONFIG_SUITEB */
#ifdef CONFIG_SUITEB192
if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
ret = os_snprintf(pos, end - pos, "%sEAP-SUITE-B-192",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
#endif /* CONFIG_SUITEB192 */
#ifdef CONFIG_FILS
if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
ret = os_snprintf(pos, end - pos, "%sFILS-SHA256",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
ret = os_snprintf(pos, end - pos, "%sFILS-SHA384",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
#ifdef CONFIG_IEEE80211R
if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA256",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA384",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
#endif /* CONFIG_IEEE80211R */
#endif /* CONFIG_FILS */
#ifdef CONFIG_OWE
if (data.key_mgmt & WPA_KEY_MGMT_OWE) {
ret = os_snprintf(pos, end - pos, "%sOWE",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP
if (data.key_mgmt & WPA_KEY_MGMT_DPP) {
ret = os_snprintf(pos, end - pos, "%sDPP",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
#endif /* CONFIG_DPP */
if (data.key_mgmt & WPA_KEY_MGMT_OSEN) {
ret = os_snprintf(pos, end - pos, "%sOSEN",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
#ifdef CONFIG_SHA384
if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384) {
ret = os_snprintf(pos, end - pos, "%sEAP-SHA384",
pos == start ? "" : "+");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
#endif /* CONFIG_SHA384 */
pos = wpa_supplicant_cipher_txt(pos, end, data.pairwise_cipher);
if (data.capabilities & WPA_CAPABILITY_PREAUTH) {
ret = os_snprintf(pos, end - pos, "-preauth");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
}
ret = os_snprintf(pos, end - pos, "]");
if (os_snprintf_error(end - pos, ret))
return pos;
pos += ret;
return pos;
}
#ifdef CONFIG_WPS
static char * wpa_supplicant_wps_ie_txt_buf(struct wpa_supplicant *wpa_s,
char *pos, char *end,
struct wpabuf *wps_ie)
{
int ret;
const char *txt;
if (wps_ie == NULL)
return pos;
if (wps_is_selected_pbc_registrar(wps_ie))
txt = "[WPS-PBC]";
else if (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 0))
txt = "[WPS-AUTH]";
else if (wps_is_selected_pin_registrar(wps_ie))
txt = "[WPS-PIN]";
else
txt = "[WPS]";
ret = os_snprintf(pos, end - pos, "%s", txt);
if (!os_snprintf_error(end - pos, ret))
pos += ret;
wpabuf_free(wps_ie);
return pos;
}
#endif /* CONFIG_WPS */
static char * wpa_supplicant_wps_ie_txt(struct wpa_supplicant *wpa_s,
char *pos, char *end,
const struct wpa_bss *bss)
{
#ifdef CONFIG_WPS
struct wpabuf *wps_ie;
wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
return wpa_supplicant_wps_ie_txt_buf(wpa_s, pos, end, wps_ie);
#else /* CONFIG_WPS */
return pos;
#endif /* CONFIG_WPS */
}
/* Format one result on one text line into a buffer. */
static int wpa_supplicant_ctrl_iface_scan_result(
struct wpa_supplicant *wpa_s,
const struct wpa_bss *bss, char *buf, size_t buflen)
{
char *pos, *end;
int ret;
const u8 *ie, *ie2, *osen_ie, *p2p, *mesh, *owe, *rsnxe;
mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID);
p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
if (!p2p)
p2p = wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE);
if (p2p && bss->ssid_len == P2P_WILDCARD_SSID_LEN &&
os_memcmp(bss->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) ==
0)
return 0; /* Do not show P2P listen discovery results here */
pos = buf;
end = buf + buflen;
ret = os_snprintf(pos, end - pos, MACSTR "\t%d\t%d\t",
MAC2STR(bss->bssid), bss->freq, bss->level);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
if (ie)
pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]);
ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
if (ie2) {
pos = wpa_supplicant_ie_txt(pos, end, mesh ? "RSN" : "WPA2",
ie2, 2 + ie2[1]);
}
rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
if (ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SAE_H2E)) {
ret = os_snprintf(pos, end - pos, "[SAE-H2E]");
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SAE_PK)) {
ret = os_snprintf(pos, end - pos, "[SAE-PK]");
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
if (osen_ie)
pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
osen_ie, 2 + osen_ie[1]);
owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
if (owe) {
ret = os_snprintf(pos, end - pos,
ie2 ? "[OWE-TRANS]" : "[OWE-TRANS-OPEN]");
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
if (!ie && !ie2 && !osen_ie && (bss->caps & IEEE80211_CAP_PRIVACY)) {
ret = os_snprintf(pos, end - pos, "[WEP]");
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (mesh) {
ret = os_snprintf(pos, end - pos, "[MESH]");
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (bss_is_dmg(bss)) {
const char *s;
if (wpa_bss_get_ie_ext(bss, WLAN_EID_EXT_EDMG_OPERATION)) {
ret = os_snprintf(pos, end - pos, "[EDMG]");
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
ret = os_snprintf(pos, end - pos, "[DMG]");
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
switch (bss->caps & IEEE80211_CAP_DMG_MASK) {
case IEEE80211_CAP_DMG_IBSS:
s = "[IBSS]";
break;
case IEEE80211_CAP_DMG_AP:
s = "[ESS]";
break;
case IEEE80211_CAP_DMG_PBSS:
s = "[PBSS]";
break;
default:
s = "";
break;
}
ret = os_snprintf(pos, end - pos, "%s", s);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
} else {
if (bss->caps & IEEE80211_CAP_IBSS) {
ret = os_snprintf(pos, end - pos, "[IBSS]");
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (bss->caps & IEEE80211_CAP_ESS) {
ret = os_snprintf(pos, end - pos, "[ESS]");
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
}
if (p2p) {
ret = os_snprintf(pos, end - pos, "[P2P]");
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
#ifdef CONFIG_HS20
if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE) && ie2) {
ret = os_snprintf(pos, end - pos, "[HS20]");
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
#endif /* CONFIG_HS20 */
#ifdef CONFIG_FILS
if (wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION)) {
ret = os_snprintf(pos, end - pos, "[FILS]");
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
#endif /* CONFIG_FILS */
#ifdef CONFIG_FST
if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) {
ret = os_snprintf(pos, end - pos, "[FST]");
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
#endif /* CONFIG_FST */
if (wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_UTF_8_SSID)) {
ret = os_snprintf(pos, end - pos, "[UTF-8]");
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
ret = os_snprintf(pos, end - pos, "\t%s",
wpa_ssid_txt(bss->ssid, bss->ssid_len));
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
ret = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
return pos - buf;
}
static int wpa_supplicant_ctrl_iface_scan_results(
struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
{
char *pos, *end;
struct wpa_bss *bss;
int ret;
pos = buf;
end = buf + buflen;
ret = os_snprintf(pos, end - pos, "bssid / frequency / signal level / "
"flags / ssid\n");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
ret = wpa_supplicant_ctrl_iface_scan_result(wpa_s, bss, pos,
end - pos);
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
}
return pos - buf;
}
#ifdef CONFIG_MESH
static int wpa_supplicant_ctrl_iface_mesh_interface_add(
struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
{
char *pos, ifname[IFNAMSIZ + 1];
ifname[0] = '\0';
pos = os_strstr(cmd, "ifname=");
if (pos) {
pos += 7;
os_strlcpy(ifname, pos, sizeof(ifname));
}
if (wpas_mesh_add_interface(wpa_s, ifname, sizeof(ifname)) < 0)
return -1;
os_strlcpy(reply, ifname, max_len);
return os_strlen(ifname);
}
static int wpa_supplicant_ctrl_iface_mesh_group_add(
struct wpa_supplicant *wpa_s, char *cmd)
{
int id;
struct wpa_ssid *ssid;
id = atoi(cmd);
wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_ADD id=%d", id);
ssid = wpa_config_get_network(wpa_s->conf, id);
if (ssid == NULL) {
wpa_printf(MSG_DEBUG,
"CTRL_IFACE: Could not find network id=%d", id);
return -1;
}
if (ssid->mode != WPAS_MODE_MESH) {
wpa_printf(MSG_DEBUG,
"CTRL_IFACE: Cannot use MESH_GROUP_ADD on a non mesh network");
return -1;
}
if (ssid->key_mgmt != WPA_KEY_MGMT_NONE &&
ssid->key_mgmt != WPA_KEY_MGMT_SAE &&
ssid->key_mgmt != WPA_KEY_MGMT_SAE_EXT_KEY) {
wpa_printf(MSG_ERROR,
"CTRL_IFACE: key_mgmt for mesh network should be open or SAE");
return -1;
}
/*
* TODO: If necessary write our own group_add function,
* for now we can reuse select_network
*/
wpa_supplicant_select_network(wpa_s, ssid);
return 0;
}
static int wpa_supplicant_ctrl_iface_mesh_group_remove(
struct wpa_supplicant *wpa_s, char *cmd)
{
struct wpa_supplicant *orig;
struct wpa_global *global;
int found = 0;
wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s", cmd);
global = wpa_s->global;
orig = wpa_s;
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
if (os_strcmp(wpa_s->ifname, cmd) == 0) {
found = 1;
break;
}
}
if (!found) {
wpa_printf(MSG_ERROR,
"CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s not found",
cmd);
return -1;
}
if (wpa_s->mesh_if_created && wpa_s == orig) {
wpa_printf(MSG_ERROR,
"CTRL_IFACE: MESH_GROUP_REMOVE can't remove itself");
return -1;
}
wpa_s->reassociate = 0;
wpa_s->disconnected = 1;
wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_cancel_scan(wpa_s);
/*
* TODO: If necessary write our own group_remove function,
* for now we can reuse deauthenticate
*/
wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
if (wpa_s->mesh_if_created)
wpa_supplicant_remove_iface(global, wpa_s, 0);
return 0;
}
static int wpa_supplicant_ctrl_iface_mesh_peer_remove(
struct wpa_supplicant *wpa_s, char *cmd)
{
u8 addr[ETH_ALEN];
if (hwaddr_aton(cmd, addr) < 0)
return -1;
return wpas_mesh_peer_remove(wpa_s, addr);
}
static int wpa_supplicant_ctrl_iface_mesh_peer_add(
struct wpa_supplicant *wpa_s, char *cmd)
{
u8 addr[ETH_ALEN];
int duration;
char *pos;
pos = os_strstr(cmd, " duration=");
if (pos) {
*pos = '\0';
duration = atoi(pos + 10);
} else {
duration = -1;
}
if (hwaddr_aton(cmd, addr))
return -1;
return wpas_mesh_peer_add(wpa_s, addr, duration);
}
static int wpa_supplicant_ctrl_iface_mesh_link_probe(
struct wpa_supplicant *wpa_s, char *cmd)
{
struct ether_header *eth;
u8 addr[ETH_ALEN];
u8 *buf;
char *pos;
size_t payload_len = 0, len;
int ret = -1;
if (hwaddr_aton(cmd, addr))
return -1;
pos = os_strstr(cmd, " payload=");
if (pos) {
pos = pos + 9;
payload_len = os_strlen(pos);
if (payload_len & 1)
return -1;
payload_len /= 2;
}
len = ETH_HLEN + payload_len;
buf = os_malloc(len);
if (!buf)
return -1;
eth = (struct ether_header *) buf;
os_memcpy(eth->ether_dhost, addr, ETH_ALEN);
os_memcpy(eth->ether_shost, wpa_s->own_addr, ETH_ALEN);
eth->ether_type = htons(ETH_P_802_3);
if (payload_len && hexstr2bin(pos, buf + ETH_HLEN, payload_len) < 0)
goto fail;
ret = wpa_drv_mesh_link_probe(wpa_s, addr, buf, len);
fail:
os_free(buf);
return -ret;
}
#endif /* CONFIG_MESH */
static int wpa_supplicant_ctrl_iface_select_network(
struct wpa_supplicant *wpa_s, char *cmd)
{
int id;
struct wpa_ssid *ssid;
char *pos;
/* cmd: "<network id>" or "any" */
if (os_strncmp(cmd, "any", 3) == 0) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK any");
ssid = NULL;
} else {
id = atoi(cmd);
wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK id=%d", id);
ssid = wpa_config_get_network(wpa_s->conf, id);
if (ssid == NULL) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
"network id=%d", id);
return -1;
}
if (ssid->disabled == 2) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
"SELECT_NETWORK with persistent P2P group");
return -1;
}
}
pos = os_strstr(cmd, " freq=");
if (pos) {
int *freqs = freq_range_to_channel_list(wpa_s, pos + 6);
if (freqs) {
os_free(wpa_s->select_network_scan_freqs);
wpa_s->select_network_scan_freqs = freqs;
}
}
wpa_s->scan_min_time.sec = 0;
wpa_s->scan_min_time.usec = 0;
wpa_supplicant_select_network(wpa_s, ssid);
return 0;
}
static int wpa_supplicant_ctrl_iface_enable_network(
struct wpa_supplicant *wpa_s, char *cmd)
{
int id;
struct wpa_ssid *ssid;
/* cmd: "<network id>" or "all" */
if (os_strcmp(cmd, "all") == 0) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK all");
ssid = NULL;
} else {
id = atoi(cmd);
wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK id=%d", id);
ssid = wpa_config_get_network(wpa_s->conf, id);
if (ssid == NULL) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
"network id=%d", id);
return -1;
}
if (ssid->disabled == 2) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
"ENABLE_NETWORK with persistent P2P group");
return -1;
}
if (os_strstr(cmd, " no-connect")) {
ssid->disabled = 0;
return 0;
}
}
wpa_s->scan_min_time.sec = 0;
wpa_s->scan_min_time.usec = 0;
wpa_supplicant_enable_network(wpa_s, ssid);
return 0;
}
static int wpa_supplicant_ctrl_iface_disable_network(
struct wpa_supplicant *wpa_s, char *cmd)
{
int id;
struct wpa_ssid *ssid;
/* cmd: "<network id>" or "all" */
if (os_strcmp(cmd, "all") == 0) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK all");
ssid = NULL;
} else {
id = atoi(cmd);
wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK id=%d", id);
ssid = wpa_config_get_network(wpa_s->conf, id);
if (ssid == NULL) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
"network id=%d", id);
return -1;
}
if (ssid->disabled == 2) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
"DISABLE_NETWORK with persistent P2P "
"group");
return -1;
}
}
wpa_supplicant_disable_network(wpa_s, ssid);
return 0;
}
static int wpa_supplicant_ctrl_iface_add_network(
struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
{
struct wpa_ssid *ssid;
int ret;
wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_NETWORK");
ssid = wpa_supplicant_add_network(wpa_s);
if (ssid == NULL)
return -1;
ret = os_snprintf(buf, buflen, "%d\n", ssid->id);
if (os_snprintf_error(buflen, ret))
return -1;
return ret;
}
static int wpa_supplicant_ctrl_iface_remove_network(
struct wpa_supplicant *wpa_s, char *cmd)
{
int id;
int result;
/* cmd: "<network id>" or "all" */
if (os_strcmp(cmd, "all") == 0) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK all");
return wpa_supplicant_remove_all_networks(wpa_s);
}
id = atoi(cmd);
wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK id=%d", id);
result = wpa_supplicant_remove_network(wpa_s, id);
if (result == -1) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
"id=%d", id);
return -1;
}
if (result == -2) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Not able to remove the "
"network id=%d", id);
return -1;
}
return 0;
}
static int wpa_supplicant_ctrl_iface_update_network(
struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
char *name, char *value)
{
int ret;
ret = wpa_config_set(ssid, name, value, 0);
if (ret < 0) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network "
"variable '%s'", name);
return -1;
}
if (ret == 1)
return 0; /* No change to the previously configured value */
#ifdef CONFIG_BGSCAN
if (os_strcmp(name, "bgscan") == 0) {
/*
* Reset the bgscan parameters for the current network and
* return. There's no need to flush caches for bgscan parameter
* changes.
*/
if (wpa_s->current_ssid == ssid &&
wpa_s->wpa_state == WPA_COMPLETED)
wpa_supplicant_reset_bgscan(wpa_s);
return 0;
}
#endif /* CONFIG_BGSCAN */
if (os_strcmp(name, "bssid") != 0 &&
os_strncmp(name, "bssid_", 6) != 0 &&
os_strcmp(name, "scan_freq") != 0 &&
os_strcmp(name, "priority") != 0) {
wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
if (wpa_s->current_ssid == ssid ||
wpa_s->current_ssid == NULL) {
/*
* Invalidate the EAP session cache if anything in the
* current or previously used configuration changes.
*/
eapol_sm_invalidate_cached_session(wpa_s->eapol);
}
}
if ((os_strcmp(name, "psk") == 0 &&
value[0] == '"' && ssid->ssid_len) ||
(os_strcmp(name, "ssid") == 0 && ssid->passphrase))
wpa_config_update_psk(ssid);
else if (os_strcmp(name, "priority") == 0)
wpa_config_update_prio_list(wpa_s->conf);
return 0;
}
static int wpa_supplicant_ctrl_iface_set_network(
struct wpa_supplicant *wpa_s, char *cmd)
{
int id, ret, prev_bssid_set, prev_disabled;
struct wpa_ssid *ssid;
char *name, *value;
u8 prev_bssid[ETH_ALEN];
/* cmd: "<network id> <variable name> <value>" */
name = os_strchr(cmd, ' ');
if (name == NULL)
return -1;
*name++ = '\0';
value = os_strchr(name, ' ');
if (value == NULL)
return -1;
*value++ = '\0';
id = atoi(cmd);
wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_NETWORK id=%d name='%s'",
id, name);
wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
(u8 *) value, os_strlen(value));
ssid = wpa_config_get_network(wpa_s->conf, id);
if (ssid == NULL) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
"id=%d", id);
return -1;
}
prev_bssid_set = ssid->bssid_set;
prev_disabled = ssid->disabled;
os_memcpy(prev_bssid, ssid->bssid, ETH_ALEN);
ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid, name,
value);
if (ret == 0 &&
(ssid->bssid_set != prev_bssid_set ||
!ether_addr_equal(ssid->bssid, prev_bssid)))
wpas_notify_network_bssid_set_changed(wpa_s, ssid);
if (prev_disabled != ssid->disabled &&
(prev_disabled == 2 || ssid->disabled == 2))
wpas_notify_network_type_changed(wpa_s, ssid);
return ret;
}
static int wpa_supplicant_ctrl_iface_get_network(
struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
{
int id;
size_t res;
struct wpa_ssid *ssid;
char *name, *value;
/* cmd: "<network id> <variable name>" */
name = os_strchr(cmd, ' ');
if (name == NULL || buflen == 0)
return -1;
*name++ = '\0';
id = atoi(cmd);
wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: GET_NETWORK id=%d name='%s'",
id, name);
ssid = wpa_config_get_network(wpa_s->conf, id);
if (ssid == NULL) {
wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Could not find network "
"id=%d", id);
return -1;
}
value = wpa_config_get_no_key(ssid, name);
if (value == NULL) {
wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Failed to get network "
"variable '%s'", name);
return -1;
}
res = os_strlcpy(buf, value, buflen);
if (res >= buflen) {
os_free(value);
return -1;
}
os_free(value);
return res;
}
static int wpa_supplicant_ctrl_iface_dup_network(
struct wpa_supplicant *wpa_s, char *cmd,
struct wpa_supplicant *dst_wpa_s)
{
struct wpa_ssid *ssid_s, *ssid_d;
char *name, *id, *value;
int id_s, id_d, ret;
/* cmd: "<src network id> <dst network id> <variable name>" */
id = os_strchr(cmd, ' ');
if (id == NULL)
return -1;
*id++ = '\0';
name = os_strchr(id, ' ');
if (name == NULL)
return -1;
*name++ = '\0';
id_s = atoi(cmd);
id_d = atoi(id);
wpa_printf(MSG_DEBUG,
"CTRL_IFACE: DUP_NETWORK ifname=%s->%s id=%d->%d name='%s'",
wpa_s->ifname, dst_wpa_s->ifname, id_s, id_d, name);
ssid_s = wpa_config_get_network(wpa_s->conf, id_s);
if (ssid_s == NULL) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
"network id=%d", id_s);
return -1;
}
ssid_d = wpa_config_get_network(dst_wpa_s->conf, id_d);
if (ssid_d == NULL) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
"network id=%d", id_d);
return -1;
}
value = wpa_config_get(ssid_s, name);
if (value == NULL) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network "
"variable '%s'", name);
return -1;
}
ret = wpa_supplicant_ctrl_iface_update_network(dst_wpa_s, ssid_d, name,
value);
os_free(value);
return ret;
}
static int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen)
{
char *pos, *end;
struct wpa_cred *cred;
int ret;
pos = buf;
end = buf + buflen;
ret = os_snprintf(pos, end - pos,
"cred id / realm / username / domain / imsi\n");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
cred = wpa_s->conf->cred;
while (cred) {
ret = os_snprintf(pos, end - pos, "%d\t%s\t%s\t%s\t%s\n",
cred->id, cred->realm ? cred->realm : "",
cred->username ? cred->username : "",
cred->domain ? cred->domain[0] : "",
cred->imsi ? cred->imsi : "");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
cred = cred->next;
}
return pos - buf;
}
static int wpa_supplicant_ctrl_iface_add_cred(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen)
{
struct wpa_cred *cred;
int ret;
wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_CRED");
cred = wpa_config_add_cred(wpa_s->conf);
if (cred == NULL)
return -1;
wpa_msg(wpa_s, MSG_INFO, CRED_ADDED "%d", cred->id);
ret = os_snprintf(buf, buflen, "%d\n", cred->id);
if (os_snprintf_error(buflen, ret))
return -1;
return ret;
}
static int wpa_supplicant_ctrl_iface_remove_cred(struct wpa_supplicant *wpa_s,
char *cmd)
{
int id;
struct wpa_cred *cred, *prev;
/* cmd: "<cred id>", "all", "sp_fqdn=<FQDN>", or
* "provisioning_sp=<FQDN> */
if (os_strcmp(cmd, "all") == 0) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED all");
return wpas_remove_all_creds(wpa_s);
}
if (os_strncmp(cmd, "sp_fqdn=", 8) == 0) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED SP FQDN '%s'",
cmd + 8);
cred = wpa_s->conf->cred;
while (cred) {
prev = cred;
cred = cred->next;
if (prev->domain) {
size_t i;
for (i = 0; i < prev->num_domain; i++) {
if (os_strcmp(prev->domain[i], cmd + 8)
!= 0)
continue;
wpas_remove_cred(wpa_s, prev);
break;
}
}
}
return 0;
}
if (os_strncmp(cmd, "provisioning_sp=", 16) == 0) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED provisioning SP FQDN '%s'",
cmd + 16);
cred = wpa_s->conf->cred;
while (cred) {
prev = cred;
cred = cred->next;
if (prev->provisioning_sp &&
os_strcmp(prev->provisioning_sp, cmd + 16) == 0)
wpas_remove_cred(wpa_s, prev);
}
return 0;
}
id = atoi(cmd);
wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED id=%d", id);
cred = wpa_config_get_cred(wpa_s->conf, id);
return wpas_remove_cred(wpa_s, cred);
}
static int wpa_supplicant_ctrl_iface_set_cred(struct wpa_supplicant *wpa_s,
char *cmd)
{
int id;
struct wpa_cred *cred;
char *name, *value;
/* cmd: "<cred id> <variable name> <value>" */
name = os_strchr(cmd, ' ');
if (name == NULL)
return -1;
*name++ = '\0';
value = os_strchr(name, ' ');
if (value == NULL)
return -1;
*value++ = '\0';
id = atoi(cmd);
wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_CRED id=%d name='%s'",
id, name);
wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
(u8 *) value, os_strlen(value));
cred = wpa_config_get_cred(wpa_s->conf, id);
if (cred == NULL) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred id=%d",
id);
return -1;
}
if (wpa_config_set_cred(cred, name, value, 0) < 0) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set cred "
"variable '%s'", name);
return -1;
}
wpa_msg(wpa_s, MSG_INFO, CRED_MODIFIED "%d %s", cred->id, name);
return 0;
}
static int wpa_supplicant_ctrl_iface_get_cred(struct wpa_supplicant *wpa_s,
char *cmd, char *buf,
size_t buflen)
{
int id;
size_t res;
struct wpa_cred *cred;
char *name, *value;
/* cmd: "<cred id> <variable name>" */
name = os_strchr(cmd, ' ');
if (name == NULL)
return -1;
*name++ = '\0';
id = atoi(cmd);
wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CRED id=%d name='%s'",
id, name);
cred = wpa_config_get_cred(wpa_s->conf, id);
if (cred == NULL) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred id=%d",
id);
return -1;
}
value = wpa_config_get_cred_no_key(cred, name);
if (value == NULL) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get cred variable '%s'",
name);
return -1;
}
res = os_strlcpy(buf, value, buflen);
if (res >= buflen) {
os_free(value);
return -1;
}
os_free(value);
return res;
}
#ifndef CONFIG_NO_CONFIG_WRITE
static int wpa_supplicant_ctrl_iface_save_config(struct wpa_supplicant *wpa_s)
{
int ret;
if (!wpa_s->conf->update_config) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed "
"to update configuration (update_config=0)");
return -1;
}
ret = wpa_config_write(wpa_s->confname, wpa_s->conf);
if (ret) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to "
"update configuration");
} else {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration"
" updated");
}
return ret;
}
#endif /* CONFIG_NO_CONFIG_WRITE */
struct cipher_info {
unsigned int capa;
const char *name;
int group_only;
};
static const struct cipher_info ciphers[] = {
{ WPA_DRIVER_CAPA_ENC_CCMP_256, "CCMP-256", 0 },
{ WPA_DRIVER_CAPA_ENC_GCMP_256, "GCMP-256", 0 },
{ WPA_DRIVER_CAPA_ENC_CCMP, "CCMP", 0 },
{ WPA_DRIVER_CAPA_ENC_GCMP, "GCMP", 0 },
#ifndef CONFIG_NO_TKIP
{ WPA_DRIVER_CAPA_ENC_TKIP, "TKIP", 0 },
#endif /* CONFIG_NO_TKIP */
{ WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE, "NONE", 0 },
#ifdef CONFIG_WEP
{ WPA_DRIVER_CAPA_ENC_WEP104, "WEP104", 1 },
{ WPA_DRIVER_CAPA_ENC_WEP40, "WEP40", 1 }
#endif /* CONFIG_WEP */
};
static const struct cipher_info ciphers_group_mgmt[] = {
{ WPA_DRIVER_CAPA_ENC_BIP, "AES-128-CMAC", 1 },
{ WPA_DRIVER_CAPA_ENC_BIP_GMAC_128, "BIP-GMAC-128", 1 },
{ WPA_DRIVER_CAPA_ENC_BIP_GMAC_256, "BIP-GMAC-256", 1 },
{ WPA_DRIVER_CAPA_ENC_BIP_CMAC_256, "BIP-CMAC-256", 1 },
};
static int ctrl_iface_get_capability_pairwise(int res, bool strict,
struct wpa_driver_capa *capa,
char *buf, size_t buflen)
{
int ret;
char *pos, *end;
size_t len;
unsigned int i;
pos = buf;
end = pos + buflen;
if (res < 0) {
if (strict)
return 0;
#ifdef CONFIG_NO_TKIP
len = os_strlcpy(buf, "CCMP NONE", buflen);
#else /* CONFIG_NO_TKIP */
len = os_strlcpy(buf, "CCMP TKIP NONE", buflen);
#endif /* CONFIG_NO_TKIP */
if (len >= buflen)
return -1;
return len;
}
for (i = 0; i < ARRAY_SIZE(ciphers); i++) {
if (!ciphers[i].group_only && capa->enc & ciphers[i].capa) {
ret = os_snprintf(pos, end - pos, "%s%s",
pos == buf ? "" : " ",
ciphers[i].name);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
}
return pos - buf;
}
static int ctrl_iface_get_capability_group(int res, bool strict,
struct wpa_driver_capa *capa,
char *buf, size_t buflen)
{
int ret;
char *pos, *end;
size_t len;
unsigned int i;
pos = buf;
end = pos + buflen;
if (res < 0) {
if (strict)
return 0;
#ifdef CONFIG_WEP
#ifdef CONFIG_NO_TKIP
len = os_strlcpy(buf, "CCMP WEP104 WEP40", buflen);
#else /* CONFIG_NO_TKIP */
len = os_strlcpy(buf, "CCMP TKIP WEP104 WEP40", buflen);
#endif /* CONFIG_NO_TKIP */
#else /* CONFIG_WEP */
#ifdef CONFIG_NO_TKIP
len = os_strlcpy(buf, "CCMP", buflen);
#else /* CONFIG_NO_TKIP */
len = os_strlcpy(buf, "CCMP TKIP", buflen);
#endif /* CONFIG_NO_TKIP */
#endif /* CONFIG_WEP */
if (len >= buflen)
return -1;
return len;
}
for (i = 0; i < ARRAY_SIZE(ciphers); i++) {
if (capa->enc & ciphers[i].capa) {
ret = os_snprintf(pos, end - pos, "%s%s",
pos == buf ? "" : " ",
ciphers[i].name);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
}
return pos - buf;
}
static int ctrl_iface_get_capability_group_mgmt(int res, bool strict,
struct wpa_driver_capa *capa,
char *buf, size_t buflen)
{
int ret;
char *pos, *end;
unsigned int i;
pos = buf;
end = pos + buflen;
if (res < 0)
return 0;
for (i = 0; i < ARRAY_SIZE(ciphers_group_mgmt); i++) {
if (capa->enc & ciphers_group_mgmt[i].capa) {
ret = os_snprintf(pos, end - pos, "%s%s",
pos == buf ? "" : " ",
ciphers_group_mgmt[i].name);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
}
return pos - buf;
}
static int iftype_str_to_index(const char *iftype_str)
{
if (!iftype_str)
return WPA_IF_MAX;
if (os_strcmp(iftype_str, "STATION") == 0)
return WPA_IF_STATION;
if (os_strcmp(iftype_str, "AP_VLAN") == 0)
return WPA_IF_AP_VLAN;
if (os_strcmp(iftype_str, "AP") == 0)
return WPA_IF_AP_BSS;
if (os_strcmp(iftype_str, "P2P_GO") == 0)
return WPA_IF_P2P_GO;
if (os_strcmp(iftype_str, "P2P_CLIENT") == 0)
return WPA_IF_P2P_CLIENT;
if (os_strcmp(iftype_str, "P2P_DEVICE") == 0)
return WPA_IF_P2P_DEVICE;
if (os_strcmp(iftype_str, "MESH") == 0)
return WPA_IF_MESH;
if (os_strcmp(iftype_str, "IBSS") == 0)
return WPA_IF_IBSS;
if (os_strcmp(iftype_str, "NAN") == 0)
return WPA_IF_NAN;
return WPA_IF_MAX;
}
static int ctrl_iface_get_capability_key_mgmt(int res, bool strict,
struct wpa_driver_capa *capa,
const char *iftype_str,
char *buf, size_t buflen)
{
int ret;
unsigned int key_mgmt;
char *pos, *end;
size_t len;
pos = buf;
end = pos + buflen;
if (res < 0) {
if (strict)
return 0;
len = os_strlcpy(buf, "WPA-PSK WPA-EAP IEEE8021X WPA-NONE "
"NONE", buflen);
if (len >= buflen)
return -1;
return len;
}
if (iftype_str) {
enum wpa_driver_if_type iftype;
iftype = iftype_str_to_index(iftype_str);
if (iftype == WPA_IF_MAX)
return -1;
key_mgmt = capa->key_mgmt_iftype[iftype];
} else {
key_mgmt = capa->key_mgmt;
}
ret = os_snprintf(pos, end - pos, "NONE IEEE8021X");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
if (key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
ret = os_snprintf(pos, end - pos, " WPA-EAP");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
ret = os_snprintf(pos, end - pos, " WPA-PSK");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
ret = os_snprintf(pos, end - pos, " WPA-NONE");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK) {
ret = os_snprintf(pos, end - pos, " WAPI-PSK");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_TPK_HANDSHAKE) {
ret = os_snprintf(pos, end - pos, " TPK-HANDSHAKE");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_CCKM) {
ret = os_snprintf(pos, end - pos, " CCKM");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#ifdef CONFIG_SUITEB
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B) {
ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_SUITEB */
#ifdef CONFIG_SUITEB192
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192) {
ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B-192");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_SUITEB192 */
#ifdef CONFIG_OWE
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OWE) {
ret = os_snprintf(pos, end - pos, " OWE");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_DPP) {
ret = os_snprintf(pos, end - pos, " DPP");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_DPP */
#ifdef CONFIG_FILS
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256) {
ret = os_snprintf(pos, end - pos, " FILS-SHA256");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384) {
ret = os_snprintf(pos, end - pos, " FILS-SHA384");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#ifdef CONFIG_IEEE80211R
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256) {
ret = os_snprintf(pos, end - pos, " FT-FILS-SHA256");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384) {
ret = os_snprintf(pos, end - pos, " FT-FILS-SHA384");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_IEEE80211R */
#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211R
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) {
ret = os_snprintf(pos, end - pos, " FT-PSK");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) {
ret = os_snprintf(pos, end - pos, " FT-EAP");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#ifdef CONFIG_SAE
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE) {
ret = os_snprintf(pos, end - pos, " FT-SAE");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE_EXT_KEY) {
ret = os_snprintf(pos, end - pos, " FT-SAE-EXT-KEY");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_SHA384
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384) {
ret = os_snprintf(pos, end - pos, " FT-EAP-SHA384");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_SHA384 */
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_SAE
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE) {
ret = os_snprintf(pos, end - pos, " SAE");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY) {
ret = os_snprintf(pos, end - pos, " SAE-EXT-KEY");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_SHA256
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256) {
ret = os_snprintf(pos, end - pos, " WPA-EAP-SHA256");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256) {
ret = os_snprintf(pos, end - pos, " WPA-PSK-SHA256");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_SHA256 */
#ifdef CONFIG_HS20
if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OSEN) {
ret = os_snprintf(pos, end - pos, " OSEN");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_HS20 */
return pos - buf;
}
static int ctrl_iface_get_capability_proto(int res, bool strict,
struct wpa_driver_capa *capa,
char *buf, size_t buflen)
{
int ret;
char *pos, *end;
size_t len;
pos = buf;
end = pos + buflen;
if (res < 0) {
if (strict)
return 0;
len = os_strlcpy(buf, "RSN WPA", buflen);
if (len >= buflen)
return -1;
return len;
}
if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
ret = os_snprintf(pos, end - pos, "%sRSN",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) {
ret = os_snprintf(pos, end - pos, "%sWPA",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
return pos - buf;
}
static int ctrl_iface_get_capability_auth_alg(struct wpa_supplicant *wpa_s,
int res, bool strict,
struct wpa_driver_capa *capa,
char *buf, size_t buflen)
{
int ret;
char *pos, *end;
size_t len;
pos = buf;
end = pos + buflen;
if (res < 0) {
if (strict)
return 0;
len = os_strlcpy(buf, "OPEN SHARED LEAP", buflen);
if (len >= buflen)
return -1;
return len;
}
if (capa->auth & (WPA_DRIVER_AUTH_OPEN)) {
ret = os_snprintf(pos, end - pos, "%sOPEN",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (capa->auth & (WPA_DRIVER_AUTH_SHARED)) {
ret = os_snprintf(pos, end - pos, "%sSHARED",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (capa->auth & (WPA_DRIVER_AUTH_LEAP)) {
ret = os_snprintf(pos, end - pos, "%sLEAP",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#ifdef CONFIG_SAE
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) {
ret = os_snprintf(pos, end - pos, "%sSAE",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
if (wpa_is_fils_supported(wpa_s)) {
ret = os_snprintf(pos, end - pos, "%sFILS_SK_WITHOUT_PFS",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#ifdef CONFIG_FILS_SK_PFS
if (wpa_is_fils_sk_pfs_supported(wpa_s)) {
ret = os_snprintf(pos, end - pos, "%sFILS_SK_WITH_PFS",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_FILS_SK_PFS */
#endif /* CONFIG_FILS */
#ifdef CONFIG_PASN
ret = os_snprintf(pos, end - pos, "%sPASN",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
#endif /* CONFIG_PASN */
return pos - buf;
}
static int ctrl_iface_get_capability_modes(int res, bool strict,
struct wpa_driver_capa *capa,
char *buf, size_t buflen)
{
int ret;
char *pos, *end;
size_t len;
pos = buf;
end = pos + buflen;
if (res < 0) {
if (strict)
return 0;
len = os_strlcpy(buf, "IBSS AP", buflen);
if (len >= buflen)
return -1;
return len;
}
if (capa->flags & WPA_DRIVER_FLAGS_IBSS) {
ret = os_snprintf(pos, end - pos, "%sIBSS",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (capa->flags & WPA_DRIVER_FLAGS_AP) {
ret = os_snprintf(pos, end - pos, "%sAP",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#ifdef CONFIG_MESH
if (capa->flags & WPA_DRIVER_FLAGS_MESH) {
ret = os_snprintf(pos, end - pos, "%sMESH",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_MESH */
return pos - buf;
}
static int ctrl_iface_get_capability_channels(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen)
{
struct hostapd_channel_data *chnl;
int ret, i, j;
char *pos, *end, *hmode;
pos = buf;
end = pos + buflen;
for (j = 0; j < wpa_s->hw.num_modes; j++) {
switch (wpa_s->hw.modes[j].mode) {
case HOSTAPD_MODE_IEEE80211B:
hmode = "B";
break;
case HOSTAPD_MODE_IEEE80211G:
hmode = "G";
break;
case HOSTAPD_MODE_IEEE80211A:
hmode = "A";
break;
case HOSTAPD_MODE_IEEE80211AD:
hmode = "AD";
break;
default:
continue;
}
ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:", hmode);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
chnl = wpa_s->hw.modes[j].channels;
for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) {
if (chnl[i].flag & HOSTAPD_CHAN_DISABLED)
continue;
ret = os_snprintf(pos, end - pos, " %d", chnl[i].chan);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
ret = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
return pos - buf;
}
static int ctrl_iface_get_capability_freq(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen)
{
struct hostapd_channel_data *chnl;
int ret, i, j;
char *pos, *end, *hmode;
pos = buf;
end = pos + buflen;
for (j = 0; j < wpa_s->hw.num_modes; j++) {
switch (wpa_s->hw.modes[j].mode) {
case HOSTAPD_MODE_IEEE80211B:
hmode = "B";
break;
case HOSTAPD_MODE_IEEE80211G:
hmode = "G";
break;
case HOSTAPD_MODE_IEEE80211A:
hmode = "A";
break;
case HOSTAPD_MODE_IEEE80211AD:
hmode = "AD";
break;
default:
continue;
}
ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:\n",
hmode);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
chnl = wpa_s->hw.modes[j].channels;
for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) {
if (chnl[i].flag & HOSTAPD_CHAN_DISABLED)
continue;
ret = os_snprintf(pos, end - pos, " %d = %d MHz%s%s\n",
chnl[i].chan, chnl[i].freq,
chnl[i].flag & HOSTAPD_CHAN_NO_IR ?
" (NO_IR)" : "",
chnl[i].flag & HOSTAPD_CHAN_RADAR ?
" (DFS)" : "");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
ret = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
return pos - buf;
}
static int wpa_supplicant_ctrl_iface_get_capability(
struct wpa_supplicant *wpa_s, const char *_field, char *buf,
size_t buflen)
{
struct wpa_driver_capa capa;
int res;
char *next_param, *curr_param, *iftype = NULL;
bool strict = false;
char field[50];
size_t len;
/* Determine whether or not strict checking was requested */
len = os_strlcpy(field, _field, sizeof(field));
if (len >= sizeof(field))
return -1;
next_param = os_strchr(field, ' ');
while (next_param) {
*next_param++ = '\0';
curr_param = next_param;
next_param = os_strchr(next_param, ' ');
if (next_param)
*next_param = '\0';
if (os_strcmp(curr_param, "strict") == 0)
strict = true;
else if (os_strncmp(curr_param, "iftype=", 7) == 0)
iftype = curr_param + 7;
else
return -1;
}
wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s'%s%s%s",
field, iftype ? " iftype=" : "", iftype ? iftype : "",
strict ? " strict" : "");
if (os_strcmp(field, "eap") == 0) {
return eap_get_names(buf, buflen);
}
res = wpa_drv_get_capa(wpa_s, &capa);
if (os_strcmp(field, "pairwise") == 0)
return ctrl_iface_get_capability_pairwise(res, strict, &capa,
buf, buflen);
if (os_strcmp(field, "group") == 0)
return ctrl_iface_get_capability_group(res, strict, &capa,
buf, buflen);
if (os_strcmp(field, "group_mgmt") == 0)
return ctrl_iface_get_capability_group_mgmt(res, strict, &capa,
buf, buflen);
if (os_strcmp(field, "key_mgmt") == 0)
return ctrl_iface_get_capability_key_mgmt(res, strict, &capa,
iftype, buf, buflen);
if (os_strcmp(field, "proto") == 0)
return ctrl_iface_get_capability_proto(res, strict, &capa,
buf, buflen);
if (os_strcmp(field, "auth_alg") == 0)
return ctrl_iface_get_capability_auth_alg(wpa_s, res, strict,
&capa, buf, buflen);
if (os_strcmp(field, "modes") == 0)
return ctrl_iface_get_capability_modes(res, strict, &capa,
buf, buflen);
if (os_strcmp(field, "channels") == 0)
return ctrl_iface_get_capability_channels(wpa_s, buf, buflen);
if (os_strcmp(field, "freq") == 0)
return ctrl_iface_get_capability_freq(wpa_s, buf, buflen);
#ifdef CONFIG_TDLS
if (os_strcmp(field, "tdls") == 0)
return ctrl_iface_get_capability_tdls(wpa_s, buf, buflen);
#endif /* CONFIG_TDLS */
#ifdef CONFIG_ERP
if (os_strcmp(field, "erp") == 0) {
res = os_snprintf(buf, buflen, "ERP");
if (os_snprintf_error(buflen, res))
return -1;
return res;
}
#endif /* CONFIG_EPR */
#ifdef CONFIG_FIPS
if (os_strcmp(field, "fips") == 0) {
res = os_snprintf(buf, buflen, "FIPS");
if (os_snprintf_error(buflen, res))
return -1;
return res;
}
#endif /* CONFIG_FIPS */
#ifdef CONFIG_ACS
if (os_strcmp(field, "acs") == 0) {
res = os_snprintf(buf, buflen, "ACS");
if (os_snprintf_error(buflen, res))
return -1;
return res;
}
#endif /* CONFIG_ACS */
#ifdef CONFIG_FILS
if (os_strcmp(field, "fils") == 0) {
#ifdef CONFIG_FILS_SK_PFS
if (wpa_is_fils_supported(wpa_s) &&
wpa_is_fils_sk_pfs_supported(wpa_s)) {
res = os_snprintf(buf, buflen, "FILS FILS-SK-PFS");
if (os_snprintf_error(buflen, res))
return -1;
return res;
}
#endif /* CONFIG_FILS_SK_PFS */
if (wpa_is_fils_supported(wpa_s)) {
res = os_snprintf(buf, buflen, "FILS");
if (os_snprintf_error(buflen, res))
return -1;
return res;
}
}
#endif /* CONFIG_FILS */
if (os_strcmp(field, "multibss") == 0 && wpa_s->multi_bss_support) {
res = os_snprintf(buf, buflen, "MULTIBSS-STA");
if (os_snprintf_error(buflen, res))
return -1;
return res;
}
#ifdef CONFIG_DPP
if (os_strcmp(field, "dpp") == 0) {
#ifdef CONFIG_DPP3
res = os_snprintf(buf, buflen, "DPP=3");
#elif defined(CONFIG_DPP2)
res = os_snprintf(buf, buflen, "DPP=2");
#else /* CONFIG_DPP2 */
res = os_snprintf(buf, buflen, "DPP=1");
#endif /* CONFIG_DPP2 */
if (os_snprintf_error(buflen, res))
return -1;
return res;
}
#endif /* CONFIG_DPP */
#ifdef CONFIG_NAN_USD
if (os_strcmp(field, "nan") == 0) {
res = os_snprintf(buf, buflen, "USD");
if (os_snprintf_error(buflen, res))
return -1;
return res;
}
#endif /* CONFIG_NAN_USD */
#ifdef CONFIG_SAE
if (os_strcmp(field, "sae") == 0 &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) {
#ifdef CONFIG_SAE_PK
res = os_snprintf(buf, buflen, "H2E PK");
#else /* CONFIG_SAE_PK */
res = os_snprintf(buf, buflen, "H2E");
#endif /* CONFIG_SAE_PK */
if (os_snprintf_error(buflen, res))
return -1;
return res;
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_OCV
if (os_strcmp(field, "ocv") == 0) {
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) ||
(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_OCV))
res = os_snprintf(buf, buflen, "supported");
else
res = os_snprintf(buf, buflen, "not supported");
if (os_snprintf_error(buflen, res))
return -1;
return res;
}
#endif /* CONFIG_OCV */
if (os_strcmp(field, "beacon_prot") == 0) {
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION) ||
(wpa_s->drv_flags2 &
WPA_DRIVER_FLAGS2_BEACON_PROTECTION_CLIENT))
res = os_snprintf(buf, buflen, "supported");
else
res = os_snprintf(buf, buflen, "not supported");
if (os_snprintf_error(buflen, res))
return -1;
return res;
}
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
field);
return -1;
}
#ifdef CONFIG_INTERWORKING
static char * anqp_add_hex(char *pos, char *end, const char *title,
struct wpabuf *data)
{
char *start = pos;
size_t i;
int ret;
const u8 *d;
if (data == NULL)
return start;
ret = os_snprintf(pos, end - pos, "%s=", title);
if (os_snprintf_error(end - pos, ret))
return start;
pos += ret;
d = wpabuf_head_u8(data);
for (i = 0; i < wpabuf_len(data); i++) {
ret = os_snprintf(pos, end - pos, "%02x", *d++);
if (os_snprintf_error(end - pos, ret))
return start;
pos += ret;
}
ret = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, ret))
return start;
pos += ret;
return pos;
}
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_FILS
static int print_fils_indication(struct wpa_bss *bss, char *pos, char *end)
{
char *start = pos;
const u8 *ie, *ie_end;
u16 info, realms;
int ret;
ie = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
if (!ie)
return 0;
ie_end = ie + 2 + ie[1];
ie += 2;
if (ie_end - ie < 2)
return 0;
info = WPA_GET_LE16(ie);
ie += 2;
ret = os_snprintf(pos, end - pos, "fils_info=%04x\n", info);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
if (info & BIT(7)) {
/* Cache Identifier Included */
if (ie_end - ie < 2)
return 0;
ret = os_snprintf(pos, end - pos, "fils_cache_id=%02x%02x\n",
ie[0], ie[1]);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
ie += 2;
}
if (info & BIT(8)) {
/* HESSID Included */
if (ie_end - ie < ETH_ALEN)
return 0;
ret = os_snprintf(pos, end - pos, "fils_hessid=" MACSTR "\n",
MAC2STR(ie));
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
ie += ETH_ALEN;
}
realms = (info & (BIT(3) | BIT(4) | BIT(5))) >> 3;
if (realms) {
if (ie_end - ie < realms * 2)
return 0;
ret = os_snprintf(pos, end - pos, "fils_realms=");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
ret = wpa_snprintf_hex(pos, end - pos, ie, realms * 2);
if (ret <= 0)
return 0;
pos += ret;
ie += realms * 2;
ret = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
return pos - start;
}
#endif /* CONFIG_FILS */
static int print_rnr(struct wpa_bss *bss, char *pos, char *end)
{
char *start = pos;
const u8 *ie, *ie_end;
unsigned int n = 0;
int ret;
ie = wpa_bss_get_ie(bss, WLAN_EID_REDUCED_NEIGHBOR_REPORT);
if (!ie)
return 0;
ie_end = ie + 2 + ie[1];
ie += 2;
while (ie < ie_end) {
const struct ieee80211_neighbor_ap_info *info =
(const struct ieee80211_neighbor_ap_info *) ie;
const u8 *tbtt_start;
size_t left = ie_end - ie;
if (left < sizeof(struct ieee80211_neighbor_ap_info))
return 0;
left -= sizeof(struct ieee80211_neighbor_ap_info);
if (left < info->tbtt_info_len)
return 0;
ret = os_snprintf(pos, end - pos,
"ap_info[%u]: tbtt_info: hdr=0x%x, len=%u, op_c=%u, channel=%u, ",
n, *ie, info->tbtt_info_len,
info->op_class, info->channel);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
ie += sizeof(struct ieee80211_neighbor_ap_info);
tbtt_start = ie;
if (info->tbtt_info_len >= 1) {
ret = os_snprintf(pos, end - pos,
"tbtt_offset=%u, ", *ie);
if (os_snprintf_error(end - pos, ret))
return 0;
ie++;
pos += ret;
}
if (info->tbtt_info_len >= 7) {
ret = os_snprintf(pos, end - pos,
"bssid=" MACSTR ", ",
MAC2STR(ie));
if (os_snprintf_error(end - pos, ret))
return 0;
ie += ETH_ALEN;
pos += ret;
}
if (info->tbtt_info_len >= 11) {
ret = os_snprintf(pos, end - pos,
"short SSID=0x%x, ",
WPA_GET_LE32(ie));
if (os_snprintf_error(end - pos, ret))
return 0;
ie += 4;
pos += ret;
}
if (info->tbtt_info_len >= 12) {
ret = os_snprintf(pos, end - pos,
"bss_params=0x%x, ", *ie);
if (os_snprintf_error(end - pos, ret))
return 0;
ie++;
pos += ret;
}
if (info->tbtt_info_len >= 13) {
ret = os_snprintf(pos, end - pos,
"PSD=0x%x, ", *ie);
if (os_snprintf_error(end - pos, ret))
return 0;
ie++;
pos += ret;
}
if (info->tbtt_info_len >= 16) {
ret = os_snprintf(pos, end - pos,
"mld ID=%u, link ID=%u",
*ie, *(ie + 1) & 0xF);
if (os_snprintf_error(end - pos, ret))
return 0;
ie += 3;
pos += ret;
}
ie = tbtt_start + info->tbtt_info_len;
ret = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
n++;
}
return pos - start;
}
static int print_ml(struct wpa_bss *bss, char *pos, char *end)
{
const struct ieee80211_eht_ml *ml;
char *start = pos;
const u8 *ie, *ie_end;
u16 ml_control;
u8 common_info_length;
int ret;
ie = get_ml_ie(wpa_bss_ie_ptr(bss), bss->ie_len,
MULTI_LINK_CONTROL_TYPE_BASIC);
if (!ie)
return 0;
ie_end = ie + 2 + ie[1];
ie += 3;
ml = (const struct ieee80211_eht_ml *) ie;
/* control + common info length + MLD MAC Address */
if (ie_end - ie < 2 + 1 + ETH_ALEN)
return 0;
ml_control = le_to_host16(ml->ml_control);
common_info_length = *(ie + 2);
ret = os_snprintf(pos, end - pos,
"multi-link: control=0x%x, common info len=%u",
ml_control, common_info_length);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
ie += 2;
if (ie_end - ie < common_info_length)
return 0;
ie++;
common_info_length--;
if (common_info_length < ETH_ALEN)
return 0;
ret = os_snprintf(pos, end - pos, ", MLD addr=" MACSTR, MAC2STR(ie));
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
ie += ETH_ALEN;
common_info_length -= ETH_ALEN;
if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID) {
if (common_info_length < 1)
return 0;
ret = os_snprintf(pos, end - pos, ", link ID=%u", *ie & 0x0f);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
ie++;
common_info_length--;
}
if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT) {
if (common_info_length < 1)
return 0;
ret = os_snprintf(pos, end - pos,
", BSS change parameters=0x%x", *ie);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
ie++;
common_info_length--;
}
if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
if (common_info_length < 2)
return 0;
ret = os_snprintf(pos, end - pos, ", MSD Info=0x%x",
WPA_GET_LE16(ie));
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
ie += 2;
common_info_length -= 2;
}
if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
if (common_info_length < 2)
return 0;
ret = os_snprintf(pos, end - pos, ", EML capabilities=0x%x",
WPA_GET_LE16(ie));
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
ie += 2;
common_info_length -= 2;
}
if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA) {
if (common_info_length < 2)
return 0;
ret = os_snprintf(pos, end - pos, ", MLD capabilities=0x%x",
WPA_GET_LE16(ie));
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
ie += 2;
common_info_length -= 2;
}
if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID) {
if (common_info_length < 1)
return 0;
ret = os_snprintf(pos, end - pos, ", MLD ID=0x%x", *ie);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
ie += 1;
common_info_length--;
}
ret = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
return pos - start;
}
static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
unsigned long mask, char *buf, size_t buflen)
{
size_t i;
int ret;
char *pos, *end;
const u8 *ie, *ie2, *osen_ie, *mesh, *owe, *rsnxe;
pos = buf;
end = buf + buflen;
if (mask & WPA_BSS_MASK_ID) {
ret = os_snprintf(pos, end - pos, "id=%u\n", bss->id);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
if (mask & WPA_BSS_MASK_BSSID) {
ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n",
MAC2STR(bss->bssid));
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
if (mask & WPA_BSS_MASK_FREQ) {
ret = os_snprintf(pos, end - pos, "freq=%d\n", bss->freq);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
if (mask & WPA_BSS_MASK_BEACON_INT) {
ret = os_snprintf(pos, end - pos, "beacon_int=%d\n",
bss->beacon_int);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
if (mask & WPA_BSS_MASK_CAPABILITIES) {
ret = os_snprintf(pos, end - pos, "capabilities=0x%04x\n",
bss->caps);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
if (mask & WPA_BSS_MASK_QUAL) {
ret = os_snprintf(pos, end - pos, "qual=%d\n", bss->qual);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
if (mask & WPA_BSS_MASK_NOISE) {
ret = os_snprintf(pos, end - pos, "noise=%d\n", bss->noise);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
if (mask & WPA_BSS_MASK_LEVEL) {
ret = os_snprintf(pos, end - pos, "level=%d\n", bss->level);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
if (mask & WPA_BSS_MASK_TSF) {
ret = os_snprintf(pos, end - pos, "tsf=%016llu\n",
(unsigned long long) bss->tsf);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
if (mask & WPA_BSS_MASK_AGE) {
struct os_reltime now;
os_get_reltime(&now);
ret = os_snprintf(pos, end - pos, "age=%d\n",
(int) (now.sec - bss->last_update.sec));
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
if (mask & WPA_BSS_MASK_IE) {
ret = os_snprintf(pos, end - pos, "ie=");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
ie = wpa_bss_ie_ptr(bss);
for (i = 0; i < bss->ie_len; i++) {
ret = os_snprintf(pos, end - pos, "%02x", *ie++);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
ret = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
if (mask & WPA_BSS_MASK_FLAGS) {
ret = os_snprintf(pos, end - pos, "flags=");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID);
ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
if (ie)
pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie,
2 + ie[1]);
ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
if (ie2)
pos = wpa_supplicant_ie_txt(pos, end,
mesh ? "RSN" : "WPA2", ie2,
2 + ie2[1]);
rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
if (ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SAE_H2E)) {
ret = os_snprintf(pos, end - pos, "[SAE-H2E]");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
if (ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SAE_PK)) {
ret = os_snprintf(pos, end - pos, "[SAE-PK]");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
if (osen_ie)
pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
osen_ie, 2 + osen_ie[1]);
owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
if (owe) {
ret = os_snprintf(
pos, end - pos,
ie2 ? "[OWE-TRANS]" : "[OWE-TRANS-OPEN]");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
if (!ie && !ie2 && !osen_ie &&
(bss->caps & IEEE80211_CAP_PRIVACY)) {
ret = os_snprintf(pos, end - pos, "[WEP]");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
if (mesh) {
ret = os_snprintf(pos, end - pos, "[MESH]");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
if (bss_is_dmg(bss)) {
const char *s;
ret = os_snprintf(pos, end - pos, "[DMG]");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
switch (bss->caps & IEEE80211_CAP_DMG_MASK) {
case IEEE80211_CAP_DMG_IBSS:
s = "[IBSS]";
break;
case IEEE80211_CAP_DMG_AP:
s = "[ESS]";
break;
case IEEE80211_CAP_DMG_PBSS:
s = "[PBSS]";
break;
default:
s = "";
break;
}
ret = os_snprintf(pos, end - pos, "%s", s);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
} else {
if (bss->caps & IEEE80211_CAP_IBSS) {
ret = os_snprintf(pos, end - pos, "[IBSS]");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
if (bss->caps & IEEE80211_CAP_ESS) {
ret = os_snprintf(pos, end - pos, "[ESS]");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
}
if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
ret = os_snprintf(pos, end - pos, "[P2P]");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
#ifdef CONFIG_HS20
if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) {
ret = os_snprintf(pos, end - pos, "[HS20]");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
#endif /* CONFIG_HS20 */
#ifdef CONFIG_FILS
if (wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION)) {
ret = os_snprintf(pos, end - pos, "[FILS]");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
#endif /* CONFIG_FILS */
#ifdef CONFIG_FST
if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) {
ret = os_snprintf(pos, end - pos, "[FST]");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
#endif /* CONFIG_FST */
if (wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_UTF_8_SSID)) {
ret = os_snprintf(pos, end - pos, "[UTF-8]");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
ret = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
if (mask & WPA_BSS_MASK_SSID) {
ret = os_snprintf(pos, end - pos, "ssid=%s\n",
wpa_ssid_txt(bss->ssid, bss->ssid_len));
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
#ifdef CONFIG_WPS
if (mask & WPA_BSS_MASK_WPS_SCAN) {
ie = wpa_bss_ie_ptr(bss);
ret = wpas_wps_scan_result_text(ie, bss->ie_len, pos, end);
if (ret >= end - pos)
return 0;
if (ret > 0)
pos += ret;
}
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
if (mask & WPA_BSS_MASK_P2P_SCAN) {
ie = wpa_bss_ie_ptr(bss);
ret = wpas_p2p_scan_result_text(ie, bss->ie_len, pos, end);
if (ret >= end - pos)
return 0;
if (ret > 0)
pos += ret;
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_WIFI_DISPLAY
if (mask & WPA_BSS_MASK_WIFI_DISPLAY) {
struct wpabuf *wfd;
ie = wpa_bss_ie_ptr(bss);
wfd = ieee802_11_vendor_ie_concat(ie, bss->ie_len,
WFD_IE_VENDOR_TYPE);
if (wfd) {
ret = os_snprintf(pos, end - pos, "wfd_subelems=");
if (os_snprintf_error(end - pos, ret)) {
wpabuf_free(wfd);
return 0;
}
pos += ret;
pos += wpa_snprintf_hex(pos, end - pos,
wpabuf_head(wfd),
wpabuf_len(wfd));
wpabuf_free(wfd);
ret = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
}
#endif /* CONFIG_WIFI_DISPLAY */
#ifdef CONFIG_INTERWORKING
if ((mask & WPA_BSS_MASK_INTERNETW) && bss->anqp) {
struct wpa_bss_anqp *anqp = bss->anqp;
struct wpa_bss_anqp_elem *elem;
pos = anqp_add_hex(pos, end, "anqp_capability_list",
anqp->capability_list);
pos = anqp_add_hex(pos, end, "anqp_venue_name",
anqp->venue_name);
pos = anqp_add_hex(pos, end, "anqp_network_auth_type",
anqp->network_auth_type);
pos = anqp_add_hex(pos, end, "anqp_roaming_consortium",
anqp->roaming_consortium);
pos = anqp_add_hex(pos, end, "anqp_ip_addr_type_availability",
anqp->ip_addr_type_availability);
pos = anqp_add_hex(pos, end, "anqp_nai_realm",
anqp->nai_realm);
pos = anqp_add_hex(pos, end, "anqp_3gpp", anqp->anqp_3gpp);
pos = anqp_add_hex(pos, end, "anqp_domain_name",
anqp->domain_name);
pos = anqp_add_hex(pos, end, "anqp_fils_realm_info",
anqp->fils_realm_info);
#ifdef CONFIG_HS20
pos = anqp_add_hex(pos, end, "hs20_capability_list",
anqp->hs20_capability_list);
pos = anqp_add_hex(pos, end, "hs20_operator_friendly_name",
anqp->hs20_operator_friendly_name);
pos = anqp_add_hex(pos, end, "hs20_wan_metrics",
anqp->hs20_wan_metrics);
pos = anqp_add_hex(pos, end, "hs20_connection_capability",
anqp->hs20_connection_capability);
pos = anqp_add_hex(pos, end, "hs20_operating_class",
anqp->hs20_operating_class);
pos = anqp_add_hex(pos, end, "hs20_osu_providers_list",
anqp->hs20_osu_providers_list);
pos = anqp_add_hex(pos, end, "hs20_operator_icon_metadata",
anqp->hs20_operator_icon_metadata);
pos = anqp_add_hex(pos, end, "hs20_osu_providers_nai_list",
anqp->hs20_osu_providers_nai_list);
#endif /* CONFIG_HS20 */
dl_list_for_each(elem, &anqp->anqp_elems,
struct wpa_bss_anqp_elem, list) {
char title[20];
os_snprintf(title, sizeof(title), "anqp[%u]",
elem->infoid);
pos = anqp_add_hex(pos, end, title, elem->payload);
if (elem->protected_response) {
ret = os_snprintf(pos, end - pos,
"protected-anqp-info[%u]=1\n",
elem->infoid);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
}
}
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_MESH
if (mask & WPA_BSS_MASK_MESH_SCAN) {
ie = wpa_bss_ie_ptr(bss);
ret = wpas_mesh_scan_result_text(ie, bss->ie_len, pos, end);
if (ret >= end - pos)
return 0;
if (ret > 0)
pos += ret;
}
#endif /* CONFIG_MESH */
if (mask & WPA_BSS_MASK_SNR) {
ret = os_snprintf(pos, end - pos, "snr=%d\n", bss->snr);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
if (mask & WPA_BSS_MASK_EST_THROUGHPUT) {
ret = os_snprintf(pos, end - pos, "est_throughput=%d\n",
bss->est_throughput);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
#ifdef CONFIG_FST
if (mask & WPA_BSS_MASK_FST) {
ret = fst_ctrl_iface_mb_info(bss->bssid, pos, end - pos);
if (ret < 0 || ret >= end - pos)
return 0;
pos += ret;
}
#endif /* CONFIG_FST */
if (mask & WPA_BSS_MASK_UPDATE_IDX) {
ret = os_snprintf(pos, end - pos, "update_idx=%u\n",
bss->last_update_idx);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
if ((mask & WPA_BSS_MASK_BEACON_IE) && bss->beacon_ie_len) {
ret = os_snprintf(pos, end - pos, "beacon_ie=");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
ie = wpa_bss_ie_ptr(bss);
ie += bss->ie_len;
for (i = 0; i < bss->beacon_ie_len; i++) {
ret = os_snprintf(pos, end - pos, "%02x", *ie++);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
ret = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
#ifdef CONFIG_FILS
if (mask & WPA_BSS_MASK_FILS_INDICATION) {
ret = print_fils_indication(bss, pos, end);
pos += ret;
}
#endif /* CONFIG_FILS */
if (!is_zero_ether_addr(bss->mld_addr) &&
(mask & WPA_BSS_MASK_AP_MLD_ADDR)) {
ret = os_snprintf(pos, end - pos,
"ap_mld_addr=" MACSTR "\n",
MAC2STR(bss->mld_addr));
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
if (mask & WPA_BSS_MASK_RNR)
pos += print_rnr(bss, pos, end);
if (mask & WPA_BSS_MASK_ML)
pos += print_ml(bss, pos, end);
if (mask & WPA_BSS_MASK_DELIM) {
ret = os_snprintf(pos, end - pos, "====\n");
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
}
return pos - buf;
}
static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
const char *cmd, char *buf,
size_t buflen)
{
u8 bssid[ETH_ALEN];
size_t i;
struct wpa_bss *bss;
struct wpa_bss *bsslast = NULL;
struct dl_list *next;
int ret = 0;
int len;
char *ctmp, *end = buf + buflen;
unsigned long mask = WPA_BSS_MASK_ALL;
if (os_strncmp(cmd, "RANGE=", 6) == 0) {
if (os_strncmp(cmd + 6, "ALL", 3) == 0) {
bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss,
list_id);
bsslast = dl_list_last(&wpa_s->bss_id, struct wpa_bss,
list_id);
} else { /* N1-N2 */
unsigned int id1, id2;
if ((ctmp = os_strchr(cmd + 6, '-')) == NULL) {
wpa_printf(MSG_INFO, "Wrong BSS range "
"format");
return 0;
}
if (*(cmd + 6) == '-')
id1 = 0;
else
id1 = atoi(cmd + 6);
ctmp++;
if (*ctmp >= '0' && *ctmp <= '9')
id2 = atoi(ctmp);
else
id2 = (unsigned int) -1;
bss = wpa_bss_get_id_range(wpa_s, id1, id2);
if (id2 == (unsigned int) -1)
bsslast = dl_list_last(&wpa_s->bss_id,
struct wpa_bss,
list_id);
else {
bsslast = wpa_bss_get_id(wpa_s, id2);
if (bsslast == NULL && bss && id2 > id1) {
struct wpa_bss *tmp = bss;
for (;;) {
next = tmp->list_id.next;
if (next == &wpa_s->bss_id)
break;
tmp = dl_list_entry(
next, struct wpa_bss,
list_id);
if (tmp->id > id2)
break;
bsslast = tmp;
}
}
}
}
} else if (os_strncmp(cmd, "FIRST", 5) == 0)
bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss, list_id);
else if (os_strncmp(cmd, "LAST", 4) == 0)
bss = dl_list_last(&wpa_s->bss_id, struct wpa_bss, list_id);
else if (os_strncmp(cmd, "ID-", 3) == 0) {
i = atoi(cmd + 3);
bss = wpa_bss_get_id(wpa_s, i);
} else if (os_strncmp(cmd, "NEXT-", 5) == 0) {
i = atoi(cmd + 5);
bss = wpa_bss_get_id(wpa_s, i);
if (bss) {
next = bss->list_id.next;
if (next == &wpa_s->bss_id)
bss = NULL;
else
bss = dl_list_entry(next, struct wpa_bss,
list_id);
}
} else if (os_strncmp(cmd, "CURRENT", 7) == 0) {
bss = wpa_s->current_bss;
#ifdef CONFIG_P2P
} else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) {
if (hwaddr_aton(cmd + 13, bssid) == 0)
bss = wpa_bss_get_p2p_dev_addr(wpa_s, bssid);
else
bss = NULL;
#endif /* CONFIG_P2P */
} else if (hwaddr_aton(cmd, bssid) == 0)
bss = wpa_bss_get_bssid(wpa_s, bssid);
else {
struct wpa_bss *tmp;
i = atoi(cmd);
bss = NULL;
dl_list_for_each(tmp, &wpa_s->bss_id, struct wpa_bss, list_id)
{
if (i == 0) {
bss = tmp;
break;
}
i--;
}
}
if ((ctmp = os_strstr(cmd, "MASK=")) != NULL) {
mask = strtoul(ctmp + 5, NULL, 0x10);
if (mask == 0)
mask = WPA_BSS_MASK_ALL;
}
if (bss == NULL)
return 0;
if (bsslast == NULL)
bsslast = bss;
do {
len = print_bss_info(wpa_s, bss, mask, buf, buflen);
ret += len;
buf += len;
buflen -= len;
if (bss == bsslast) {
if ((mask & WPA_BSS_MASK_DELIM) && len &&
(bss == dl_list_last(&wpa_s->bss_id,
struct wpa_bss, list_id))) {
int res;
res = os_snprintf(buf - 5, end - buf + 5,
"####\n");
if (os_snprintf_error(end - buf + 5, res)) {
wpa_printf(MSG_DEBUG,
"Could not add end delim");
}
}
break;
}
next = bss->list_id.next;
if (next == &wpa_s->bss_id)
break;
bss = dl_list_entry(next, struct wpa_bss, list_id);
} while (bss && len);
return ret;
}
static int wpa_supplicant_ctrl_iface_ap_scan(
struct wpa_supplicant *wpa_s, char *cmd)
{
int ap_scan = atoi(cmd);
return wpa_supplicant_set_ap_scan(wpa_s, ap_scan);
}
static int wpa_supplicant_ctrl_iface_scan_interval(
struct wpa_supplicant *wpa_s, char *cmd)
{
int scan_int = atoi(cmd);
return wpa_supplicant_set_scan_interval(wpa_s, scan_int);
}
static int wpa_supplicant_ctrl_iface_bss_expire_age(
struct wpa_supplicant *wpa_s, char *cmd)
{
int expire_age = atoi(cmd);
return wpa_supplicant_set_bss_expiration_age(wpa_s, expire_age);
}
static int wpa_supplicant_ctrl_iface_bss_expire_count(
struct wpa_supplicant *wpa_s, char *cmd)
{
int expire_count = atoi(cmd);
return wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count);
}
static void wpa_supplicant_ctrl_iface_bss_flush(
struct wpa_supplicant *wpa_s, char *cmd)
{
int flush_age = atoi(cmd);
if (flush_age == 0)
wpa_bss_flush(wpa_s);
else
wpa_bss_flush_by_age(wpa_s, flush_age);
}
#ifdef CONFIG_TESTING_OPTIONS
static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
{
wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication");
/* MLME-DELETEKEYS.request */
wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL,
Introduce and add key_flag Add the new set_key() parameter "key_flag" to provide more specific description of what type of a key is being configured. This is needed to be able to add support for "Extended Key ID for Individually Addressed Frames" from IEEE Std 802.11-2016. In addition, this may be used to replace the set_tx boolean eventually once all the driver wrappers have moved to using the new key_flag. The following flag are defined: KEY_FLAG_MODIFY Set when an already installed key must be updated. So far the only use-case is changing RX/TX status of installed keys. Must not be set when deleting a key. KEY_FLAG_DEFAULT Set when the key is also a default key. Must not be set when deleting a key. (This is the replacement for set_tx.) KEY_FLAG_RX The key is valid for RX. Must not be set when deleting a key. KEY_FLAG_TX The key is valid for TX. Must not be set when deleting a key. KEY_FLAG_GROUP The key is a broadcast or group key. KEY_FLAG_PAIRWISE The key is a pairwise key. KEY_FLAG_PMK The key is a Pairwise Master Key (PMK). Predefined and needed flag combinations so far are: KEY_FLAG_GROUP_RX_TX WEP key not used as default key (yet). KEY_FLAG_GROUP_RX_TX_DEFAULT Default WEP or WPA-NONE key. KEY_FLAG_GROUP_RX GTK key valid for RX only. KEY_FLAG_GROUP_TX_DEFAULT GTK key valid for TX only, immediately taking over TX. KEY_FLAG_PAIRWISE_RX_TX Pairwise key immediately becoming the active pairwise key. KEY_FLAG_PAIRWISE_RX Pairwise key not yet valid for TX. (Only usable with Extended Key ID support.) KEY_FLAG_PAIRWISE_RX_TX_MODIFY Enable TX for a pairwise key installed with KEY_FLAG_PAIRWISE_RX. KEY_FLAG_RX_TX Not a valid standalone key type and can only used in combination with other flags to mark a key for RX/TX. This commit is not changing any functionality. It just adds the new key_flag to all hostapd/wpa_supplicant set_key() functions without using it, yet. Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
2020-01-04 23:10:04 +01:00
0, KEY_FLAG_GROUP);
wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL,
Introduce and add key_flag Add the new set_key() parameter "key_flag" to provide more specific description of what type of a key is being configured. This is needed to be able to add support for "Extended Key ID for Individually Addressed Frames" from IEEE Std 802.11-2016. In addition, this may be used to replace the set_tx boolean eventually once all the driver wrappers have moved to using the new key_flag. The following flag are defined: KEY_FLAG_MODIFY Set when an already installed key must be updated. So far the only use-case is changing RX/TX status of installed keys. Must not be set when deleting a key. KEY_FLAG_DEFAULT Set when the key is also a default key. Must not be set when deleting a key. (This is the replacement for set_tx.) KEY_FLAG_RX The key is valid for RX. Must not be set when deleting a key. KEY_FLAG_TX The key is valid for TX. Must not be set when deleting a key. KEY_FLAG_GROUP The key is a broadcast or group key. KEY_FLAG_PAIRWISE The key is a pairwise key. KEY_FLAG_PMK The key is a Pairwise Master Key (PMK). Predefined and needed flag combinations so far are: KEY_FLAG_GROUP_RX_TX WEP key not used as default key (yet). KEY_FLAG_GROUP_RX_TX_DEFAULT Default WEP or WPA-NONE key. KEY_FLAG_GROUP_RX GTK key valid for RX only. KEY_FLAG_GROUP_TX_DEFAULT GTK key valid for TX only, immediately taking over TX. KEY_FLAG_PAIRWISE_RX_TX Pairwise key immediately becoming the active pairwise key. KEY_FLAG_PAIRWISE_RX Pairwise key not yet valid for TX. (Only usable with Extended Key ID support.) KEY_FLAG_PAIRWISE_RX_TX_MODIFY Enable TX for a pairwise key installed with KEY_FLAG_PAIRWISE_RX. KEY_FLAG_RX_TX Not a valid standalone key type and can only used in combination with other flags to mark a key for RX/TX. This commit is not changing any functionality. It just adds the new key_flag to all hostapd/wpa_supplicant set_key() functions without using it, yet. Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
2020-01-04 23:10:04 +01:00
0, KEY_FLAG_GROUP);
wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL,
Introduce and add key_flag Add the new set_key() parameter "key_flag" to provide more specific description of what type of a key is being configured. This is needed to be able to add support for "Extended Key ID for Individually Addressed Frames" from IEEE Std 802.11-2016. In addition, this may be used to replace the set_tx boolean eventually once all the driver wrappers have moved to using the new key_flag. The following flag are defined: KEY_FLAG_MODIFY Set when an already installed key must be updated. So far the only use-case is changing RX/TX status of installed keys. Must not be set when deleting a key. KEY_FLAG_DEFAULT Set when the key is also a default key. Must not be set when deleting a key. (This is the replacement for set_tx.) KEY_FLAG_RX The key is valid for RX. Must not be set when deleting a key. KEY_FLAG_TX The key is valid for TX. Must not be set when deleting a key. KEY_FLAG_GROUP The key is a broadcast or group key. KEY_FLAG_PAIRWISE The key is a pairwise key. KEY_FLAG_PMK The key is a Pairwise Master Key (PMK). Predefined and needed flag combinations so far are: KEY_FLAG_GROUP_RX_TX WEP key not used as default key (yet). KEY_FLAG_GROUP_RX_TX_DEFAULT Default WEP or WPA-NONE key. KEY_FLAG_GROUP_RX GTK key valid for RX only. KEY_FLAG_GROUP_TX_DEFAULT GTK key valid for TX only, immediately taking over TX. KEY_FLAG_PAIRWISE_RX_TX Pairwise key immediately becoming the active pairwise key. KEY_FLAG_PAIRWISE_RX Pairwise key not yet valid for TX. (Only usable with Extended Key ID support.) KEY_FLAG_PAIRWISE_RX_TX_MODIFY Enable TX for a pairwise key installed with KEY_FLAG_PAIRWISE_RX. KEY_FLAG_RX_TX Not a valid standalone key type and can only used in combination with other flags to mark a key for RX/TX. This commit is not changing any functionality. It just adds the new key_flag to all hostapd/wpa_supplicant set_key() functions without using it, yet. Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
2020-01-04 23:10:04 +01:00
0, KEY_FLAG_GROUP);
wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL,
Introduce and add key_flag Add the new set_key() parameter "key_flag" to provide more specific description of what type of a key is being configured. This is needed to be able to add support for "Extended Key ID for Individually Addressed Frames" from IEEE Std 802.11-2016. In addition, this may be used to replace the set_tx boolean eventually once all the driver wrappers have moved to using the new key_flag. The following flag are defined: KEY_FLAG_MODIFY Set when an already installed key must be updated. So far the only use-case is changing RX/TX status of installed keys. Must not be set when deleting a key. KEY_FLAG_DEFAULT Set when the key is also a default key. Must not be set when deleting a key. (This is the replacement for set_tx.) KEY_FLAG_RX The key is valid for RX. Must not be set when deleting a key. KEY_FLAG_TX The key is valid for TX. Must not be set when deleting a key. KEY_FLAG_GROUP The key is a broadcast or group key. KEY_FLAG_PAIRWISE The key is a pairwise key. KEY_FLAG_PMK The key is a Pairwise Master Key (PMK). Predefined and needed flag combinations so far are: KEY_FLAG_GROUP_RX_TX WEP key not used as default key (yet). KEY_FLAG_GROUP_RX_TX_DEFAULT Default WEP or WPA-NONE key. KEY_FLAG_GROUP_RX GTK key valid for RX only. KEY_FLAG_GROUP_TX_DEFAULT GTK key valid for TX only, immediately taking over TX. KEY_FLAG_PAIRWISE_RX_TX Pairwise key immediately becoming the active pairwise key. KEY_FLAG_PAIRWISE_RX Pairwise key not yet valid for TX. (Only usable with Extended Key ID support.) KEY_FLAG_PAIRWISE_RX_TX_MODIFY Enable TX for a pairwise key installed with KEY_FLAG_PAIRWISE_RX. KEY_FLAG_RX_TX Not a valid standalone key type and can only used in combination with other flags to mark a key for RX/TX. This commit is not changing any functionality. It just adds the new key_flag to all hostapd/wpa_supplicant set_key() functions without using it, yet. Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
2020-01-04 23:10:04 +01:00
0, KEY_FLAG_GROUP);
wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL,
Introduce and add key_flag Add the new set_key() parameter "key_flag" to provide more specific description of what type of a key is being configured. This is needed to be able to add support for "Extended Key ID for Individually Addressed Frames" from IEEE Std 802.11-2016. In addition, this may be used to replace the set_tx boolean eventually once all the driver wrappers have moved to using the new key_flag. The following flag are defined: KEY_FLAG_MODIFY Set when an already installed key must be updated. So far the only use-case is changing RX/TX status of installed keys. Must not be set when deleting a key. KEY_FLAG_DEFAULT Set when the key is also a default key. Must not be set when deleting a key. (This is the replacement for set_tx.) KEY_FLAG_RX The key is valid for RX. Must not be set when deleting a key. KEY_FLAG_TX The key is valid for TX. Must not be set when deleting a key. KEY_FLAG_GROUP The key is a broadcast or group key. KEY_FLAG_PAIRWISE The key is a pairwise key. KEY_FLAG_PMK The key is a Pairwise Master Key (PMK). Predefined and needed flag combinations so far are: KEY_FLAG_GROUP_RX_TX WEP key not used as default key (yet). KEY_FLAG_GROUP_RX_TX_DEFAULT Default WEP or WPA-NONE key. KEY_FLAG_GROUP_RX GTK key valid for RX only. KEY_FLAG_GROUP_TX_DEFAULT GTK key valid for TX only, immediately taking over TX. KEY_FLAG_PAIRWISE_RX_TX Pairwise key immediately becoming the active pairwise key. KEY_FLAG_PAIRWISE_RX Pairwise key not yet valid for TX. (Only usable with Extended Key ID support.) KEY_FLAG_PAIRWISE_RX_TX_MODIFY Enable TX for a pairwise key installed with KEY_FLAG_PAIRWISE_RX. KEY_FLAG_RX_TX Not a valid standalone key type and can only used in combination with other flags to mark a key for RX/TX. This commit is not changing any functionality. It just adds the new key_flag to all hostapd/wpa_supplicant set_key() functions without using it, yet. Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
2020-01-04 23:10:04 +01:00
0, KEY_FLAG_GROUP);
wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL,
Introduce and add key_flag Add the new set_key() parameter "key_flag" to provide more specific description of what type of a key is being configured. This is needed to be able to add support for "Extended Key ID for Individually Addressed Frames" from IEEE Std 802.11-2016. In addition, this may be used to replace the set_tx boolean eventually once all the driver wrappers have moved to using the new key_flag. The following flag are defined: KEY_FLAG_MODIFY Set when an already installed key must be updated. So far the only use-case is changing RX/TX status of installed keys. Must not be set when deleting a key. KEY_FLAG_DEFAULT Set when the key is also a default key. Must not be set when deleting a key. (This is the replacement for set_tx.) KEY_FLAG_RX The key is valid for RX. Must not be set when deleting a key. KEY_FLAG_TX The key is valid for TX. Must not be set when deleting a key. KEY_FLAG_GROUP The key is a broadcast or group key. KEY_FLAG_PAIRWISE The key is a pairwise key. KEY_FLAG_PMK The key is a Pairwise Master Key (PMK). Predefined and needed flag combinations so far are: KEY_FLAG_GROUP_RX_TX WEP key not used as default key (yet). KEY_FLAG_GROUP_RX_TX_DEFAULT Default WEP or WPA-NONE key. KEY_FLAG_GROUP_RX GTK key valid for RX only. KEY_FLAG_GROUP_TX_DEFAULT GTK key valid for TX only, immediately taking over TX. KEY_FLAG_PAIRWISE_RX_TX Pairwise key immediately becoming the active pairwise key. KEY_FLAG_PAIRWISE_RX Pairwise key not yet valid for TX. (Only usable with Extended Key ID support.) KEY_FLAG_PAIRWISE_RX_TX_MODIFY Enable TX for a pairwise key installed with KEY_FLAG_PAIRWISE_RX. KEY_FLAG_RX_TX Not a valid standalone key type and can only used in combination with other flags to mark a key for RX/TX. This commit is not changing any functionality. It just adds the new key_flag to all hostapd/wpa_supplicant set_key() functions without using it, yet. Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
2020-01-04 23:10:04 +01:00
0, KEY_FLAG_GROUP);
wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0,
NULL, 0, KEY_FLAG_PAIRWISE);
if (wpa_sm_ext_key_id(wpa_s->wpa))
wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, wpa_s->bssid, 1, 0,
NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE);
/* MLME-SETPROTECTION.request(None) */
wpa_drv_mlme_setprotection(wpa_s, wpa_s->bssid,
MLME_SETPROTECTION_PROTECT_TYPE_NONE,
MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
wpa_sm_drop_sa(wpa_s->wpa);
}
#endif /* CONFIG_TESTING_OPTIONS */
static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s,
char *addr)
{
#ifdef CONFIG_NO_SCAN_PROCESSING
return -1;
#else /* CONFIG_NO_SCAN_PROCESSING */
u8 bssid[ETH_ALEN];
struct wpa_bss *bss;
struct wpa_ssid *ssid = wpa_s->current_ssid;
struct wpa_radio_work *already_connecting;
if (hwaddr_aton(addr, bssid)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: invalid "
"address '%s'", addr);
return -1;
}
wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM " MACSTR, MAC2STR(bssid));
if (!ssid) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: No network "
"configuration known for the target AP");
return -1;
}
bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
if (!bss) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: Target AP not found "
"from BSS table");
return -1;
}
/*
* TODO: Find best network configuration block from configuration to
* allow roaming to other networks
*/
already_connecting = radio_work_pending(wpa_s, "sme-connect");
wpa_s->reassociate = 1;
wpa_supplicant_connect(wpa_s, bss, ssid);
/*
* Indicate that an explicitly requested roam is in progress so scan
* results that come in before the 'sme-connect' radio work gets
* executed do not override the original connection attempt.
*/
if (!already_connecting && radio_work_pending(wpa_s, "sme-connect"))
wpa_s->roam_in_progress = true;
return 0;
#endif /* CONFIG_NO_SCAN_PROCESSING */
}
#ifdef CONFIG_P2P
static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
{
unsigned int timeout = atoi(cmd);
enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL;
u8 dev_id[ETH_ALEN], *_dev_id = NULL;
u8 dev_type[WPS_DEV_TYPE_LEN], *_dev_type = NULL;
char *pos;
unsigned int search_delay;
const char *_seek[P2P_MAX_QUERY_HASH + 1], **seek = NULL;
u8 seek_count = 0;
int freq = 0;
bool include_6ghz = false;
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
wpa_dbg(wpa_s, MSG_INFO,
"Reject P2P_FIND since interface is disabled");
return -1;
}
if (os_strstr(cmd, " include_6ghz"))
include_6ghz = true;
if (os_strstr(cmd, "type=social"))
type = P2P_FIND_ONLY_SOCIAL;
else if (os_strstr(cmd, "type=progressive"))
type = P2P_FIND_PROGRESSIVE;
pos = os_strstr(cmd, "dev_id=");
if (pos) {
pos += 7;
if (hwaddr_aton(pos, dev_id))
return -1;
_dev_id = dev_id;
}
pos = os_strstr(cmd, "dev_type=");
if (pos) {
pos += 9;
if (wps_dev_type_str2bin(pos, dev_type) < 0)
return -1;
_dev_type = dev_type;
}
pos = os_strstr(cmd, "delay=");
if (pos) {
pos += 6;
search_delay = atoi(pos);
} else
search_delay = wpas_p2p_search_delay(wpa_s);
pos = os_strstr(cmd, "freq=");
if (pos) {
pos += 5;
freq = atoi(pos);
if (freq <= 0)
return -1;
}
/* Must be searched for last, because it adds nul termination */
pos = os_strstr(cmd, " seek=");
if (pos)
pos += 6;
while (pos && seek_count < P2P_MAX_QUERY_HASH + 1) {
char *term;
_seek[seek_count++] = pos;
seek = _seek;
term = os_strchr(pos, ' ');
if (!term)
break;
*term = '\0';
pos = os_strstr(term + 1, "seek=");
if (pos)
pos += 5;
}
if (seek_count > P2P_MAX_QUERY_HASH) {
seek[0] = NULL;
seek_count = 1;
}
return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type,
_dev_id, search_delay, seek_count, seek, freq,
include_6ghz);
}
static int p2ps_ctrl_parse_cpt_priority(const char *pos, u8 *cpt)
{
const char *last = NULL;
const char *token;
long int token_len;
unsigned int i;
/* Expected predefined CPT names delimited by ':' */
for (i = 0; (token = cstr_token(pos, ": \t", &last)); i++) {
if (i >= P2PS_FEATURE_CAPAB_CPT_MAX) {
wpa_printf(MSG_ERROR,
"P2PS: CPT name list is too long, expected up to %d names",
P2PS_FEATURE_CAPAB_CPT_MAX);
cpt[0] = 0;
return -1;
}
token_len = last - token;
if (token_len == 3 &&
os_memcmp(token, "UDP", token_len) == 0) {
cpt[i] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
} else if (token_len == 3 &&
os_memcmp(token, "MAC", token_len) == 0) {
cpt[i] = P2PS_FEATURE_CAPAB_MAC_TRANSPORT;
} else {
wpa_printf(MSG_ERROR,
"P2PS: Unsupported CPT name '%s'", token);
cpt[0] = 0;
return -1;
}
if (isblank((unsigned char) *last)) {
i++;
break;
}
}
cpt[i] = 0;
return 0;
}
static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd)
{
struct p2ps_provision *p2ps_prov;
char *pos;
size_t info_len = 0;
char *info = NULL;
u8 role = P2PS_SETUP_NONE;
long long unsigned val;
int i;
pos = os_strstr(cmd, "info=");
if (pos) {
pos += 5;
info_len = os_strlen(pos);
if (info_len) {
info = os_malloc(info_len + 1);
if (info) {
info_len = utf8_unescape(pos, info_len,
info, info_len + 1);
} else
info_len = 0;
}
}
p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + info_len + 1);
if (p2ps_prov == NULL) {
os_free(info);
return NULL;
}
if (info) {
os_memcpy(p2ps_prov->info, info, info_len);
p2ps_prov->info[info_len] = '\0';
os_free(info);
}
pos = os_strstr(cmd, "status=");
if (pos)
p2ps_prov->status = atoi(pos + 7);
else
p2ps_prov->status = -1;
pos = os_strstr(cmd, "adv_id=");
if (!pos || sscanf(pos + 7, "%llx", &val) != 1 || val > 0xffffffffULL)
goto invalid_args;
p2ps_prov->adv_id = val;
pos = os_strstr(cmd, "method=");
if (pos)
p2ps_prov->method = strtol(pos + 7, NULL, 16);
else
p2ps_prov->method = 0;
pos = os_strstr(cmd, "session=");
if (!pos || sscanf(pos + 8, "%llx", &val) != 1 || val > 0xffffffffULL)
goto invalid_args;
p2ps_prov->session_id = val;
pos = os_strstr(cmd, "adv_mac=");
if (!pos || hwaddr_aton(pos + 8, p2ps_prov->adv_mac))
goto invalid_args;
pos = os_strstr(cmd, "session_mac=");
if (!pos || hwaddr_aton(pos + 12, p2ps_prov->session_mac))
goto invalid_args;
pos = os_strstr(cmd, "cpt=");
if (pos) {
if (p2ps_ctrl_parse_cpt_priority(pos + 4,
p2ps_prov->cpt_priority))
goto invalid_args;
} else {
p2ps_prov->cpt_priority[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
}
for (i = 0; p2ps_prov->cpt_priority[i]; i++)
p2ps_prov->cpt_mask |= p2ps_prov->cpt_priority[i];
/* force conncap with tstCap (no validity checks) */
pos = os_strstr(cmd, "tstCap=");
if (pos) {
role = strtol(pos + 7, NULL, 16);
} else {
pos = os_strstr(cmd, "role=");
if (pos) {
role = strtol(pos + 5, NULL, 16);
if (role != P2PS_SETUP_CLIENT &&
role != P2PS_SETUP_GROUP_OWNER)
role = P2PS_SETUP_NONE;
}
}
p2ps_prov->role = role;
return p2ps_prov;
invalid_args:
os_free(p2ps_prov);
return NULL;
}
static int p2p_ctrl_asp_provision_resp(struct wpa_supplicant *wpa_s, char *cmd)
{
u8 addr[ETH_ALEN];
struct p2ps_provision *p2ps_prov;
char *pos;
/* <addr> id=<adv_id> [role=<conncap>] [info=<infodata>] */
wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd);
if (hwaddr_aton(cmd, addr))
return -1;
pos = cmd + 17;
if (*pos != ' ')
return -1;
p2ps_prov = p2p_parse_asp_provision_cmd(pos);
if (!p2ps_prov)
return -1;
if (p2ps_prov->status < 0) {
os_free(p2ps_prov);
return -1;
}
return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
p2ps_prov);
}
static int p2p_ctrl_asp_provision(struct wpa_supplicant *wpa_s, char *cmd)
{
u8 addr[ETH_ALEN];
struct p2ps_provision *p2ps_prov;
char *pos;
/* <addr> id=<adv_id> adv_mac=<adv_mac> conncap=<conncap>
* session=<ses_id> mac=<ses_mac> [info=<infodata>]
*/
wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd);
if (hwaddr_aton(cmd, addr))
return -1;
pos = cmd + 17;
if (*pos != ' ')
return -1;
p2ps_prov = p2p_parse_asp_provision_cmd(pos);
if (!p2ps_prov)
return -1;
p2ps_prov->pd_seeker = 1;
return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
p2ps_prov);
}
static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
char *buf, size_t buflen)
{
u8 addr[ETH_ALEN];
char *pos, *pos2;
char *pin = NULL;
enum p2p_wps_method wps_method;
int new_pin;
int ret;
int persistent_group, persistent_id = -1;
int join;
int auth;
int automatic;
int go_intent = -1;
int freq = 0;
int pd;
int ht40, vht, max_oper_chwidth, chwidth = 0, freq2 = 0;
int edmg;
u8 _group_ssid[SSID_MAX_LEN], *group_ssid = NULL;
size_t group_ssid_len = 0;
int he;
bool allow_6ghz;
if (!wpa_s->global->p2p_init_wpa_s)
return -1;
if (wpa_s->global->p2p_init_wpa_s != wpa_s) {
wpa_dbg(wpa_s, MSG_DEBUG, "Direct P2P_CONNECT command to %s",
wpa_s->global->p2p_init_wpa_s->ifname);
wpa_s = wpa_s->global->p2p_init_wpa_s;
}
/* <addr> <"pbc" | "pin" | PIN> [label|display|keypad|p2ps]
* [persistent|persistent=<network id>]
* [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc]
* [ht40] [vht] [he] [edmg] [auto] [ssid=<hexdump>] */
if (hwaddr_aton(cmd, addr))
return -1;
pos = cmd + 17;
if (*pos != ' ')
return -1;
pos++;
persistent_group = os_strstr(pos, " persistent") != NULL;
pos2 = os_strstr(pos, " persistent=");
if (pos2) {
struct wpa_ssid *ssid;
persistent_id = atoi(pos2 + 12);
ssid = wpa_config_get_network(wpa_s->conf, persistent_id);
if (ssid == NULL || ssid->disabled != 2 ||
ssid->mode != WPAS_MODE_P2P_GO) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
"SSID id=%d for persistent P2P group (GO)",
persistent_id);
return -1;
}
}
join = os_strstr(pos, " join") != NULL;
allow_6ghz = os_strstr(pos, " allow_6ghz") != NULL;
auth = os_strstr(pos, " auth") != NULL;
automatic = os_strstr(pos, " auto") != NULL;
pd = os_strstr(pos, " provdisc") != NULL;
vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht;
ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
vht;
he = (os_strstr(cmd, " he") != NULL) || wpa_s->conf->p2p_go_he;
edmg = (os_strstr(cmd, " edmg") != NULL) || wpa_s->conf->p2p_go_edmg;
pos2 = os_strstr(pos, " go_intent=");
if (pos2) {
pos2 += 11;
go_intent = atoi(pos2);
if (go_intent < 0 || go_intent > 15)
return -1;
}
pos2 = os_strstr(pos, " freq=");
if (pos2) {
pos2 += 6;
freq = atoi(pos2);
if (freq <= 0)
return -1;
}
pos2 = os_strstr(pos, " freq2=");
if (pos2)
freq2 = atoi(pos2 + 7);
pos2 = os_strstr(pos, " max_oper_chwidth=");
if (pos2)
chwidth = atoi(pos2 + 18);
max_oper_chwidth = chwidth_freq2_to_ch_width(chwidth, freq2);
if (max_oper_chwidth < 0)
return -1;
if (allow_6ghz && chwidth == 40)
max_oper_chwidth = CONF_OPER_CHWIDTH_40MHZ_6GHZ;
pos2 = os_strstr(pos, " ssid=");
if (pos2) {
char *end;
pos2 += 6;
end = os_strchr(pos2, ' ');
if (!end)
group_ssid_len = os_strlen(pos2) / 2;
else
group_ssid_len = (end - pos2) / 2;
if (group_ssid_len == 0 || group_ssid_len > SSID_MAX_LEN ||
hexstr2bin(pos2, _group_ssid, group_ssid_len) < 0)
return -1;
group_ssid = _group_ssid;
}
if (os_strncmp(pos, "pin", 3) == 0) {
/* Request random PIN (to be displayed) and enable the PIN */
wps_method = WPS_PIN_DISPLAY;
} else if (os_strncmp(pos, "pbc", 3) == 0) {
wps_method = WPS_PBC;
} else if (os_strstr(pos, "p2ps") != NULL) {
wps_method = WPS_P2PS;
} else {
pin = pos;
pos = os_strchr(pin, ' ');
wps_method = WPS_PIN_KEYPAD;
if (pos) {
*pos++ = '\0';
if (os_strncmp(pos, "display", 7) == 0)
wps_method = WPS_PIN_DISPLAY;
}
if (!wps_pin_str_valid(pin)) {
os_memcpy(buf, "FAIL-INVALID-PIN\n", 17);
return 17;
}
}
new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
persistent_group, automatic, join,
auth, go_intent, freq, freq2, persistent_id,
pd, ht40, vht, max_oper_chwidth, he, edmg,
group_ssid, group_ssid_len, allow_6ghz);
if (new_pin == -2) {
os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25);
return 25;
}
if (new_pin == -3) {
os_memcpy(buf, "FAIL-CHANNEL-UNSUPPORTED\n", 25);
return 25;
}
if (new_pin < 0)
return -1;
if (wps_method == WPS_PIN_DISPLAY && pin == NULL) {
ret = os_snprintf(buf, buflen, "%08d", new_pin);
if (os_snprintf_error(buflen, ret))
return -1;
return ret;
}
os_memcpy(buf, "OK\n", 3);
return 3;
}
static int p2p_ctrl_listen(struct wpa_supplicant *wpa_s, char *cmd)
{
unsigned int timeout = atoi(cmd);
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
wpa_dbg(wpa_s, MSG_INFO,
"Reject P2P_LISTEN since interface is disabled");
return -1;
}
return wpas_p2p_listen(wpa_s, timeout);
}
static int p2p_ctrl_prov_disc(struct wpa_supplicant *wpa_s, char *cmd)
{
u8 addr[ETH_ALEN];
char *pos;
enum wpas_p2p_prov_disc_use use = WPAS_P2P_PD_FOR_GO_NEG;
/* <addr> <config method> [join|auto] */
if (hwaddr_aton(cmd, addr))
return -1;
pos = cmd + 17;
if (*pos != ' ')
return -1;
pos++;
if (os_strstr(pos, " join") != NULL)
use = WPAS_P2P_PD_FOR_JOIN;
else if (os_strstr(pos, " auto") != NULL)
use = WPAS_P2P_PD_AUTO;
return wpas_p2p_prov_disc(wpa_s, addr, pos, use, NULL);
}
static int p2p_get_passphrase(struct wpa_supplicant *wpa_s, char *buf,
size_t buflen)
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
ssid->passphrase == NULL)
return -1;
os_strlcpy(buf, ssid->passphrase, buflen);
return os_strlen(buf);
}
static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd,
char *buf, size_t buflen)
{
u64 ref;
int res;
u8 dst_buf[ETH_ALEN], *dst;
struct wpabuf *tlvs;
char *pos;
size_t len;
if (hwaddr_aton(cmd, dst_buf))
return -1;
dst = dst_buf;
if (dst[0] == 0 && dst[1] == 0 && dst[2] == 0 &&
dst[3] == 0 && dst[4] == 0 && dst[5] == 0)
dst = NULL;
pos = cmd + 17;
if (*pos != ' ')
return -1;
pos++;
if (os_strncmp(pos, "upnp ", 5) == 0) {
u8 version;
pos += 5;
if (hexstr2bin(pos, &version, 1) < 0)
return -1;
pos += 2;
if (*pos != ' ')
return -1;
pos++;
ref = wpas_p2p_sd_request_upnp(wpa_s, dst, version, pos);
#ifdef CONFIG_WIFI_DISPLAY
} else if (os_strncmp(pos, "wifi-display ", 13) == 0) {
ref = wpas_p2p_sd_request_wifi_display(wpa_s, dst, pos + 13);
#endif /* CONFIG_WIFI_DISPLAY */
} else if (os_strncmp(pos, "asp ", 4) == 0) {
char *svc_str;
char *svc_info = NULL;
u32 id;
pos += 4;
if (sscanf(pos, "%x", &id) != 1 || id > 0xff)
return -1;
pos = os_strchr(pos, ' ');
if (pos == NULL || pos[1] == '\0' || pos[1] == ' ')
return -1;
svc_str = pos + 1;
pos = os_strchr(svc_str, ' ');
if (pos)
*pos++ = '\0';
/* All remaining data is the svc_info string */
if (pos && pos[0] && pos[0] != ' ') {
len = os_strlen(pos);
/* Unescape in place */
len = utf8_unescape(pos, len, pos, len);
if (len > 0xff)
return -1;
svc_info = pos;
}
ref = wpas_p2p_sd_request_asp(wpa_s, dst, (u8) id,
svc_str, svc_info);
} else {
len = os_strlen(pos);
if (len & 1)
return -1;
len /= 2;
tlvs = wpabuf_alloc(len);
if (tlvs == NULL)
return -1;
if (hexstr2bin(pos, wpabuf_put(tlvs, len), len) < 0) {
wpabuf_free(tlvs);
return -1;
}
ref = wpas_p2p_sd_request(wpa_s, dst, tlvs);
wpabuf_free(tlvs);
}
if (ref == 0)
return -1;
res = os_snprintf(buf, buflen, "%llx", (long long unsigned) ref);
if (os_snprintf_error(buflen, res))
return -1;
return res;
}
static int p2p_ctrl_serv_disc_cancel_req(struct wpa_supplicant *wpa_s,
char *cmd)
{
long long unsigned val;
u64 req;
if (sscanf(cmd, "%llx", &val) != 1)
return -1;
req = val;
return wpas_p2p_sd_cancel_request(wpa_s, req);
}
static int p2p_ctrl_serv_disc_resp(struct wpa_supplicant *wpa_s, char *cmd)
{
int freq;
u8 dst[ETH_ALEN];
u8 dialog_token;
struct wpabuf *resp_tlvs;
char *pos, *pos2;
size_t len;
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
freq = atoi(cmd);
if (freq == 0)
return -1;
if (hwaddr_aton(pos, dst))
return -1;
pos += 17;
if (*pos != ' ')
return -1;
pos++;
pos2 = os_strchr(pos, ' ');
if (pos2 == NULL)
return -1;
*pos2++ = '\0';
dialog_token = atoi(pos);
len = os_strlen(pos2);
if (len & 1)
return -1;
len /= 2;
resp_tlvs = wpabuf_alloc(len);
if (resp_tlvs == NULL)
return -1;
if (hexstr2bin(pos2, wpabuf_put(resp_tlvs, len), len) < 0) {
wpabuf_free(resp_tlvs);
return -1;
}
wpas_p2p_sd_response(wpa_s, freq, dst, dialog_token, resp_tlvs);
wpabuf_free(resp_tlvs);
return 0;
}
static int p2p_ctrl_serv_disc_external(struct wpa_supplicant *wpa_s,
char *cmd)
{
if (os_strcmp(cmd, "0") && os_strcmp(cmd, "1"))
return -1;
wpa_s->p2p_sd_over_ctrl_iface = atoi(cmd);
return 0;
}
static int p2p_ctrl_service_add_bonjour(struct wpa_supplicant *wpa_s,
char *cmd)
{
char *pos;
size_t len;
struct wpabuf *query, *resp;
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
len = os_strlen(cmd);
if (len & 1)
return -1;
len /= 2;
query = wpabuf_alloc(len);
if (query == NULL)
return -1;
if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) {
wpabuf_free(query);
return -1;
}
len = os_strlen(pos);
if (len & 1) {
wpabuf_free(query);
return -1;
}
len /= 2;
resp = wpabuf_alloc(len);
if (resp == NULL) {
wpabuf_free(query);
return -1;
}
if (hexstr2bin(pos, wpabuf_put(resp, len), len) < 0) {
wpabuf_free(query);
wpabuf_free(resp);
return -1;
}
if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) {
wpabuf_free(query);
wpabuf_free(resp);
return -1;
}
return 0;
}
static int p2p_ctrl_service_add_upnp(struct wpa_supplicant *wpa_s, char *cmd)
{
char *pos;
u8 version;
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
if (hexstr2bin(cmd, &version, 1) < 0)
return -1;
return wpas_p2p_service_add_upnp(wpa_s, version, pos);
}
static int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s,
u8 replace, char *cmd)
{
char *pos;
char *adv_str;
u32 auto_accept, adv_id, svc_state, config_methods;
char *svc_info = NULL;
char *cpt_prio_str;
u8 cpt_prio[P2PS_FEATURE_CAPAB_CPT_MAX + 1];
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
/* Auto-Accept value is mandatory, and must be one of the
* single values (0, 1, 2, 4) */
auto_accept = atoi(cmd);
switch (auto_accept) {
case P2PS_SETUP_NONE: /* No auto-accept */
case P2PS_SETUP_NEW:
case P2PS_SETUP_CLIENT:
case P2PS_SETUP_GROUP_OWNER:
break;
default:
return -1;
}
/* Advertisement ID is mandatory */
cmd = pos;
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
/* Handle Adv_ID == 0 (wildcard "org.wi-fi.wfds") internally. */
if (sscanf(cmd, "%x", &adv_id) != 1 || adv_id == 0)
return -1;
/* Only allow replacements if exist, and adds if not */
if (wpas_p2p_service_p2ps_id_exists(wpa_s, adv_id)) {
if (!replace)
return -1;
} else {
if (replace)
return -1;
}
/* svc_state between 0 - 0xff is mandatory */
if (sscanf(pos, "%x", &svc_state) != 1 || svc_state > 0xff)
return -1;
pos = os_strchr(pos, ' ');
if (pos == NULL)
return -1;
/* config_methods is mandatory */
pos++;
if (sscanf(pos, "%x", &config_methods) != 1)
return -1;
if (!(config_methods &
(WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS)))
return -1;
pos = os_strchr(pos, ' ');
if (pos == NULL)
return -1;
pos++;
adv_str = pos;
/* Advertisement string is mandatory */
if (!pos[0] || pos[0] == ' ')
return -1;
/* Terminate svc string */
pos = os_strchr(pos, ' ');
if (pos != NULL)
*pos++ = '\0';
cpt_prio_str = (pos && pos[0]) ? os_strstr(pos, "cpt=") : NULL;
if (cpt_prio_str) {
pos = os_strchr(pos, ' ');
if (pos != NULL)
*pos++ = '\0';
if (p2ps_ctrl_parse_cpt_priority(cpt_prio_str + 4, cpt_prio))
return -1;
} else {
cpt_prio[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
cpt_prio[1] = 0;
}
/* Service and Response Information are optional */
if (pos && pos[0]) {
size_t len;
/* Note the bare ' included, which cannot exist legally
* in unescaped string. */
svc_info = os_strstr(pos, "svc_info='");
if (svc_info) {
svc_info += 9;
len = os_strlen(svc_info);
utf8_unescape(svc_info, len, svc_info, len);
}
}
return wpas_p2p_service_add_asp(wpa_s, auto_accept, adv_id, adv_str,
(u8) svc_state, (u16) config_methods,
svc_info, cpt_prio);
}
static int p2p_ctrl_service_add(struct wpa_supplicant *wpa_s, char *cmd)
{
char *pos;
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
if (os_strcmp(cmd, "bonjour") == 0)
return p2p_ctrl_service_add_bonjour(wpa_s, pos);
if (os_strcmp(cmd, "upnp") == 0)
return p2p_ctrl_service_add_upnp(wpa_s, pos);
if (os_strcmp(cmd, "asp") == 0)
return p2p_ctrl_service_add_asp(wpa_s, 0, pos);
wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
return -1;
}
static int p2p_ctrl_service_del_bonjour(struct wpa_supplicant *wpa_s,
char *cmd)
{
size_t len;
struct wpabuf *query;
int ret;
len = os_strlen(cmd);
if (len & 1)
return -1;
len /= 2;
query = wpabuf_alloc(len);
if (query == NULL)
return -1;
if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) {
wpabuf_free(query);
return -1;
}
ret = wpas_p2p_service_del_bonjour(wpa_s, query);
wpabuf_free(query);
return ret;
}
static int p2p_ctrl_service_del_upnp(struct wpa_supplicant *wpa_s, char *cmd)
{
char *pos;
u8 version;
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
if (hexstr2bin(cmd, &version, 1) < 0)
return -1;
return wpas_p2p_service_del_upnp(wpa_s, version, pos);
}
static int p2p_ctrl_service_del_asp(struct wpa_supplicant *wpa_s, char *cmd)
{
u32 adv_id;
if (os_strcmp(cmd, "all") == 0) {
wpas_p2p_service_flush_asp(wpa_s);
return 0;
}
if (sscanf(cmd, "%x", &adv_id) != 1)
return -1;
return wpas_p2p_service_del_asp(wpa_s, adv_id);
}
static int p2p_ctrl_service_del(struct wpa_supplicant *wpa_s, char *cmd)
{
char *pos;
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
if (os_strcmp(cmd, "bonjour") == 0)
return p2p_ctrl_service_del_bonjour(wpa_s, pos);
if (os_strcmp(cmd, "upnp") == 0)
return p2p_ctrl_service_del_upnp(wpa_s, pos);
if (os_strcmp(cmd, "asp") == 0)
return p2p_ctrl_service_del_asp(wpa_s, pos);
wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
return -1;
}
static int p2p_ctrl_service_replace(struct wpa_supplicant *wpa_s, char *cmd)
{
char *pos;
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
if (os_strcmp(cmd, "asp") == 0)
return p2p_ctrl_service_add_asp(wpa_s, 1, pos);
wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
return -1;
}
static int p2p_ctrl_reject(struct wpa_supplicant *wpa_s, char *cmd)
{
u8 addr[ETH_ALEN];
/* <addr> */
if (hwaddr_aton(cmd, addr))
return -1;
return wpas_p2p_reject(wpa_s, addr);
}
static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd)
{
char *pos;
int id;
struct wpa_ssid *ssid;
u8 *_peer = NULL, peer[ETH_ALEN];
int freq = 0, pref_freq = 0;
int ht40, vht, he, max_oper_chwidth, chwidth = 0, freq2 = 0;
int edmg;
bool allow_6ghz;
id = atoi(cmd);
pos = os_strstr(cmd, " peer=");
if (pos) {
pos += 6;
if (hwaddr_aton(pos, peer))
return -1;
_peer = peer;
}
ssid = wpa_config_get_network(wpa_s->conf, id);
if (ssid == NULL || ssid->disabled != 2) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
"for persistent P2P group",
id);
return -1;
}
pos = os_strstr(cmd, " freq=");
if (pos) {
pos += 6;
freq = atoi(pos);
if (freq <= 0)
return -1;
}
pos = os_strstr(cmd, " pref=");
if (pos) {
pos += 6;
pref_freq = atoi(pos);
if (pref_freq <= 0)
return -1;
}
vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht;
ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
vht;
he = (os_strstr(cmd, " he") != NULL) || wpa_s->conf->p2p_go_he;
edmg = (os_strstr(cmd, " edmg") != NULL) || wpa_s->conf->p2p_go_edmg;
pos = os_strstr(cmd, "freq2=");
if (pos)
freq2 = atoi(pos + 6);
pos = os_strstr(cmd, " max_oper_chwidth=");
if (pos)
chwidth = atoi(pos + 18);
max_oper_chwidth = chwidth_freq2_to_ch_width(chwidth, freq2);
if (max_oper_chwidth < 0)
return -1;
allow_6ghz = os_strstr(cmd, " allow_6ghz") != NULL;
if (allow_6ghz && chwidth == 40)
max_oper_chwidth = CONF_OPER_CHWIDTH_40MHZ_6GHZ;
return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, freq2, ht40, vht,
max_oper_chwidth, pref_freq, he, edmg,
allow_6ghz);
}
static int p2p_ctrl_invite_group(struct wpa_supplicant *wpa_s, char *cmd)
{
char *pos;
u8 peer[ETH_ALEN], go_dev_addr[ETH_ALEN], *go_dev = NULL;
bool allow_6ghz;
pos = os_strstr(cmd, " peer=");
if (!pos)
return -1;
*pos = '\0';
pos += 6;
if (hwaddr_aton(pos, peer)) {
wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'", pos);
return -1;
}
allow_6ghz = os_strstr(pos, " allow_6ghz") != NULL;
pos = os_strstr(pos, " go_dev_addr=");
if (pos) {
pos += 13;
if (hwaddr_aton(pos, go_dev_addr)) {
wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'",
pos);
return -1;
}
go_dev = go_dev_addr;
}
return wpas_p2p_invite_group(wpa_s, cmd, peer, go_dev, allow_6ghz);
}
static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd)
{
if (os_strncmp(cmd, "persistent=", 11) == 0)
return p2p_ctrl_invite_persistent(wpa_s, cmd + 11);
if (os_strncmp(cmd, "group=", 6) == 0)
return p2p_ctrl_invite_group(wpa_s, cmd + 6);
return -1;
}
static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
int id, int freq, int vht_center_freq2,
int ht40, int vht, int vht_chwidth,
int he, int edmg, bool allow_6ghz,
const u8 *go_bssid)
{
struct wpa_ssid *ssid;
ssid = wpa_config_get_network(wpa_s->conf, id);
if (ssid == NULL || ssid->disabled != 2) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
"for persistent P2P group",
id);
return -1;
}
return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0,
vht_center_freq2, ht40, vht,
vht_chwidth, he, edmg,
NULL, 0, 0, allow_6ghz, 0,
go_bssid);
}
static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
{
int freq = 0, persistent = 0, group_id = -1;
bool allow_6ghz = false;
int vht = wpa_s->conf->p2p_go_vht;
int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
int he = wpa_s->conf->p2p_go_he;
int edmg = wpa_s->conf->p2p_go_edmg;
int max_oper_chwidth, chwidth = 0, freq2 = 0;
char *token, *context = NULL;
u8 go_bssid_buf[ETH_ALEN], *go_bssid = NULL;
#ifdef CONFIG_ACS
int acs = 0;
#endif /* CONFIG_ACS */
while ((token = str_token(cmd, " ", &context))) {
if (sscanf(token, "freq2=%d", &freq2) == 1 ||
sscanf(token, "persistent=%d", &group_id) == 1 ||
sscanf(token, "max_oper_chwidth=%d", &chwidth) == 1) {
continue;
#ifdef CONFIG_ACS
} else if (os_strcmp(token, "freq=acs") == 0) {
acs = 1;
#endif /* CONFIG_ACS */
} else if (sscanf(token, "freq=%d", &freq) == 1) {
continue;
} else if (os_strcmp(token, "ht40") == 0) {
ht40 = 1;
} else if (os_strcmp(token, "vht") == 0) {
vht = 1;
ht40 = 1;
} else if (os_strcmp(token, "he") == 0) {
he = 1;
} else if (os_strcmp(token, "edmg") == 0) {
edmg = 1;
} else if (os_strcmp(token, "persistent") == 0) {
persistent = 1;
} else if (os_strcmp(token, "allow_6ghz") == 0) {
allow_6ghz = true;
} else if (os_strncmp(token, "go_bssid=", 9) == 0) {
if (hwaddr_aton(token + 9, go_bssid_buf))
return -1;
go_bssid = go_bssid_buf;
} else {
wpa_printf(MSG_DEBUG,
"CTRL: Invalid P2P_GROUP_ADD parameter: '%s'",
token);
return -1;
}
}
#ifdef CONFIG_ACS
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) &&
(acs || freq == 2 || freq == 5)) {
if (freq == 2 && wpa_s->best_24_freq <= 0) {
wpa_s->p2p_go_acs_band = HOSTAPD_MODE_IEEE80211G;
wpa_s->p2p_go_do_acs = 1;
freq = 0;
} else if (freq == 5 && wpa_s->best_5_freq <= 0) {
wpa_s->p2p_go_acs_band = HOSTAPD_MODE_IEEE80211A;
wpa_s->p2p_go_do_acs = 1;
freq = 0;
} else {
wpa_s->p2p_go_acs_band = HOSTAPD_MODE_IEEE80211ANY;
wpa_s->p2p_go_do_acs = 1;
}
} else {
wpa_s->p2p_go_do_acs = 0;
}
#endif /* CONFIG_ACS */
max_oper_chwidth = chwidth_freq2_to_ch_width(chwidth, freq2);
if (max_oper_chwidth < 0)
return -1;
if (allow_6ghz && chwidth == 40)
max_oper_chwidth = CONF_OPER_CHWIDTH_40MHZ_6GHZ;
/* Allow DFS to be used for Autonomous GO */
wpa_s->p2p_go_allow_dfs = !!(wpa_s->drv_flags &
WPA_DRIVER_FLAGS_DFS_OFFLOAD);
if (group_id >= 0)
return p2p_ctrl_group_add_persistent(wpa_s, group_id,
freq, freq2, ht40, vht,
max_oper_chwidth, he,
edmg, allow_6ghz,
go_bssid);
return wpas_p2p_group_add(wpa_s, persistent, freq, freq2, ht40, vht,
max_oper_chwidth, he, edmg, allow_6ghz);
}
static int p2p_ctrl_group_member(struct wpa_supplicant *wpa_s, const char *cmd,
char *buf, size_t buflen)
{
u8 dev_addr[ETH_ALEN];
struct wpa_ssid *ssid;
int res;
const u8 *iaddr;
ssid = wpa_s->current_ssid;
if (!wpa_s->global->p2p || !ssid || ssid->mode != WPAS_MODE_P2P_GO ||
hwaddr_aton(cmd, dev_addr))
return -1;
iaddr = p2p_group_get_client_interface_addr(wpa_s->p2p_group, dev_addr);
if (!iaddr)
return -1;
res = os_snprintf(buf, buflen, MACSTR, MAC2STR(iaddr));
if (os_snprintf_error(buflen, res))
return -1;
return res;
}
static int wpas_find_p2p_dev_addr_bss(struct wpa_global *global,
const u8 *p2p_dev_addr)
{
struct wpa_supplicant *wpa_s;
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
if (wpa_bss_get_p2p_dev_addr(wpa_s, p2p_dev_addr))
return 1;
}
return 0;
}
static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd,
char *buf, size_t buflen)
{
u8 addr[ETH_ALEN], *addr_ptr, group_capab;
int next, res;
const struct p2p_peer_info *info;
char *pos, *end;
char devtype[WPS_DEV_TYPE_BUFSIZE];
struct wpa_ssid *ssid;
size_t i;
if (!wpa_s->global->p2p)
return -1;
if (os_strcmp(cmd, "FIRST") == 0) {
addr_ptr = NULL;
next = 0;
} else if (os_strncmp(cmd, "NEXT-", 5) == 0) {
if (hwaddr_aton(cmd + 5, addr) < 0)
return -1;
addr_ptr = addr;
next = 1;
} else {
if (hwaddr_aton(cmd, addr) < 0)
return -1;
addr_ptr = addr;
next = 0;
}
info = p2p_get_peer_info(wpa_s->global->p2p, addr_ptr, next);
if (info == NULL)
return -1;
group_capab = info->group_capab;
if (group_capab &&
!wpas_find_p2p_dev_addr_bss(wpa_s->global, info->p2p_device_addr)) {
wpa_printf(MSG_DEBUG,
"P2P: Could not find any BSS with p2p_dev_addr "
MACSTR ", hence override group_capab from 0x%x to 0",
MAC2STR(info->p2p_device_addr), group_capab);
group_capab = 0;
}
pos = buf;
end = buf + buflen;
res = os_snprintf(pos, end - pos, MACSTR "\n"
"pri_dev_type=%s\n"
"device_name=%s\n"
"manufacturer=%s\n"
"model_name=%s\n"
"model_number=%s\n"
"serial_number=%s\n"
"config_methods=0x%x\n"
"dev_capab=0x%x\n"
"group_capab=0x%x\n"
"level=%d\n",
MAC2STR(info->p2p_device_addr),
wps_dev_type_bin2str(info->pri_dev_type,
devtype, sizeof(devtype)),
info->device_name,
info->manufacturer,
info->model_name,
info->model_number,
info->serial_number,
info->config_methods,
info->dev_capab,
group_capab,
info->level);
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
for (i = 0; i < info->wps_sec_dev_type_list_len / WPS_DEV_TYPE_LEN; i++)
{
const u8 *t;
t = &info->wps_sec_dev_type_list[i * WPS_DEV_TYPE_LEN];
res = os_snprintf(pos, end - pos, "sec_dev_type=%s\n",
wps_dev_type_bin2str(t, devtype,
sizeof(devtype)));
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
}
ssid = wpas_p2p_get_persistent(wpa_s, info->p2p_device_addr, NULL, 0);
if (ssid) {
res = os_snprintf(pos, end - pos, "persistent=%d\n", ssid->id);
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
}
res = p2p_get_peer_info_txt(info, pos, end - pos);
if (res < 0)
return pos - buf;
pos += res;
if (info->vendor_elems) {
res = os_snprintf(pos, end - pos, "vendor_elems=");
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
pos += wpa_snprintf_hex(pos, end - pos,
wpabuf_head(info->vendor_elems),
wpabuf_len(info->vendor_elems));
res = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
}
return pos - buf;
}
static int p2p_ctrl_disallow_freq(struct wpa_supplicant *wpa_s,
const char *param)
{
unsigned int i;
if (wpa_s->global->p2p == NULL)
return -1;
if (freq_range_list_parse(&wpa_s->global->p2p_disallow_freq, param) < 0)
return -1;
for (i = 0; i < wpa_s->global->p2p_disallow_freq.num; i++) {
struct wpa_freq_range *freq;
freq = &wpa_s->global->p2p_disallow_freq.range[i];
wpa_printf(MSG_DEBUG, "P2P: Disallowed frequency range %u-%u",
freq->min, freq->max);
}
wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DISALLOW);
return 0;
}
static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd)
{
char *param;
if (wpa_s->global->p2p == NULL)
return -1;
param = os_strchr(cmd, ' ');
if (param == NULL)
return -1;
*param++ = '\0';
if (os_strcmp(cmd, "discoverability") == 0) {
p2p_set_client_discoverability(wpa_s->global->p2p,
atoi(param));
return 0;
}
if (os_strcmp(cmd, "managed") == 0) {
p2p_set_managed_oper(wpa_s->global->p2p, atoi(param));
return 0;
}
if (os_strcmp(cmd, "listen_channel") == 0) {
char *pos;
u8 channel, op_class;
channel = atoi(param);
pos = os_strchr(param, ' ');
op_class = pos ? atoi(pos) : 81;
return p2p_set_listen_channel(wpa_s->global->p2p, op_class,
channel, 1);
}
if (os_strcmp(cmd, "ssid_postfix") == 0) {
return p2p_set_ssid_postfix(wpa_s->global->p2p, (u8 *) param,
os_strlen(param));
}
if (os_strcmp(cmd, "noa") == 0) {
char *pos;
int count, start, duration;
/* GO NoA parameters: count,start_offset(ms),duration(ms) */
count = atoi(param);
pos = os_strchr(param, ',');
if (pos == NULL)
return -1;
pos++;
start = atoi(pos);
pos = os_strchr(pos, ',');
if (pos == NULL)
return -1;
pos++;
duration = atoi(pos);
if (count < 0 || count > 255 || start < 0 || duration < 0)
return -1;
if (count == 0 && duration > 0)
return -1;
wpa_printf(MSG_DEBUG, "CTRL_IFACE: P2P_SET GO NoA: count=%d "
"start=%d duration=%d", count, start, duration);
return wpas_p2p_set_noa(wpa_s, count, start, duration);
}
if (os_strcmp(cmd, "ps") == 0)
return wpa_drv_set_p2p_powersave(wpa_s, atoi(param), -1, -1);
if (os_strcmp(cmd, "oppps") == 0)
return wpa_drv_set_p2p_powersave(wpa_s, -1, atoi(param), -1);
if (os_strcmp(cmd, "ctwindow") == 0)
return wpa_drv_set_p2p_powersave(wpa_s, -1, -1, atoi(param));
if (os_strcmp(cmd, "disabled") == 0) {
wpa_s->global->p2p_disabled = atoi(param);
wpa_printf(MSG_DEBUG, "P2P functionality %s",
wpa_s->global->p2p_disabled ?
"disabled" : "enabled");
if (wpa_s->global->p2p_disabled) {
wpas_p2p_stop_find(wpa_s);
os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
p2p_flush(wpa_s->global->p2p);
}
return 0;
}
if (os_strcmp(cmd, "conc_pref") == 0) {
if (os_strcmp(param, "sta") == 0)
wpa_s->global->conc_pref = WPA_CONC_PREF_STA;
else if (os_strcmp(param, "p2p") == 0)
wpa_s->global->conc_pref = WPA_CONC_PREF_P2P;
else {
wpa_printf(MSG_INFO, "Invalid conc_pref value");
return -1;
}
wpa_printf(MSG_DEBUG, "Single channel concurrency preference: "
"%s", param);
return 0;
}
if (os_strcmp(cmd, "force_long_sd") == 0) {
wpa_s->force_long_sd = atoi(param);
return 0;
}
if (os_strcmp(cmd, "peer_filter") == 0) {
u8 addr[ETH_ALEN];
if (hwaddr_aton(param, addr))
return -1;
p2p_set_peer_filter(wpa_s->global->p2p, addr);
return 0;
}
if (os_strcmp(cmd, "cross_connect") == 0)
return wpas_p2p_set_cross_connect(wpa_s, atoi(param));
if (os_strcmp(cmd, "go_apsd") == 0) {
if (os_strcmp(param, "disable") == 0)
wpa_s->set_ap_uapsd = 0;
else {
wpa_s->set_ap_uapsd = 1;
wpa_s->ap_uapsd = atoi(param);
}
return 0;
}
if (os_strcmp(cmd, "client_apsd") == 0) {
if (os_strcmp(param, "disable") == 0)
wpa_s->set_sta_uapsd = 0;
else {
int be, bk, vi, vo;
char *pos;
/* format: BE,BK,VI,VO;max SP Length */
be = atoi(param);
pos = os_strchr(param, ',');
if (pos == NULL)
return -1;
pos++;
bk = atoi(pos);
pos = os_strchr(pos, ',');
if (pos == NULL)
return -1;
pos++;
vi = atoi(pos);
pos = os_strchr(pos, ',');
if (pos == NULL)
return -1;
pos++;
vo = atoi(pos);
/* ignore max SP Length for now */
wpa_s->set_sta_uapsd = 1;
wpa_s->sta_uapsd = 0;
if (be)
wpa_s->sta_uapsd |= BIT(0);
if (bk)
wpa_s->sta_uapsd |= BIT(1);
if (vi)
wpa_s->sta_uapsd |= BIT(2);
if (vo)
wpa_s->sta_uapsd |= BIT(3);
}
return 0;
}
if (os_strcmp(cmd, "disallow_freq") == 0)
return p2p_ctrl_disallow_freq(wpa_s, param);
if (os_strcmp(cmd, "disc_int") == 0) {
int min_disc_int, max_disc_int, max_disc_tu;
char *pos;
pos = param;
min_disc_int = atoi(pos);
pos = os_strchr(pos, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
max_disc_int = atoi(pos);
pos = os_strchr(pos, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
max_disc_tu = atoi(pos);
return p2p_set_disc_int(wpa_s->global->p2p, min_disc_int,
max_disc_int, max_disc_tu);
}
if (os_strcmp(cmd, "per_sta_psk") == 0) {
wpa_s->global->p2p_per_sta_psk = !!atoi(param);
return 0;
}
#ifdef CONFIG_WPS_NFC
if (os_strcmp(cmd, "nfc_tag") == 0)
return wpas_p2p_nfc_tag_enabled(wpa_s, !!atoi(param));
#endif /* CONFIG_WPS_NFC */
if (os_strcmp(cmd, "disable_ip_addr_req") == 0) {
wpa_s->p2p_disable_ip_addr_req = !!atoi(param);
return 0;
}
if (os_strcmp(cmd, "override_pref_op_chan") == 0) {
int op_class, chan;
op_class = atoi(param);
param = os_strchr(param, ':');
if (!param)
return -1;
param++;
chan = atoi(param);
p2p_set_override_pref_op_chan(wpa_s->global->p2p, op_class,
chan);
return 0;
}
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'",
cmd);
return -1;
}
static void p2p_ctrl_flush(struct wpa_supplicant *wpa_s)
{
os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
wpa_s->force_long_sd = 0;
#ifdef CONFIG_TESTING_OPTIONS
os_free(wpa_s->get_pref_freq_list_override);
wpa_s->get_pref_freq_list_override = NULL;
#endif /* CONFIG_TESTING_OPTIONS */
wpas_p2p_stop_find(wpa_s);
wpa_s->parent->p2ps_method_config_any = 0;
if (wpa_s->global->p2p)
p2p_flush(wpa_s->global->p2p);
}
static int p2p_ctrl_presence_req(struct wpa_supplicant *wpa_s, char *cmd)
{
char *pos, *pos2;
unsigned int dur1 = 0, int1 = 0, dur2 = 0, int2 = 0;
if (cmd[0]) {
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
dur1 = atoi(cmd);
pos2 = os_strchr(pos, ' ');
if (pos2)
*pos2++ = '\0';
int1 = atoi(pos);
} else
pos2 = NULL;
if (pos2) {
pos = os_strchr(pos2, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
dur2 = atoi(pos2);
int2 = atoi(pos);
}
return wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2);
}
static int p2p_ctrl_ext_listen(struct wpa_supplicant *wpa_s, char *cmd)
{
char *pos;
unsigned int period = 0, interval = 0;
if (cmd[0]) {
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
period = atoi(cmd);
interval = atoi(pos);
}
return wpas_p2p_ext_listen(wpa_s, period, interval);
}
static int p2p_ctrl_remove_client(struct wpa_supplicant *wpa_s, const char *cmd)
{
const char *pos;
u8 peer[ETH_ALEN];
int iface_addr = 0;
pos = cmd;
if (os_strncmp(pos, "iface=", 6) == 0) {
iface_addr = 1;
pos += 6;
}
if (hwaddr_aton(pos, peer))
return -1;
wpas_p2p_remove_client(wpa_s, peer, iface_addr);
return 0;
}
static int p2p_ctrl_iface_p2p_lo_start(struct wpa_supplicant *wpa_s, char *cmd)
{
int freq = 0, period = 0, interval = 0, count = 0;
if (sscanf(cmd, "%d %d %d %d", &freq, &period, &interval, &count) != 4)
{
wpa_printf(MSG_DEBUG,
"CTRL: Invalid P2P LO Start parameter: '%s'", cmd);
return -1;
}
return wpas_p2p_lo_start(wpa_s, freq, period, interval, count);
}
#endif /* CONFIG_P2P */
static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s, char *val)
{
struct wpa_freq_range_list ranges;
int *freqs = NULL;
struct hostapd_hw_modes *mode;
u16 i;
if (wpa_s->hw.modes == NULL)
return NULL;
os_memset(&ranges, 0, sizeof(ranges));
if (freq_range_list_parse(&ranges, val) < 0)
return NULL;
for (i = 0; i < wpa_s->hw.num_modes; i++) {
int j;
mode = &wpa_s->hw.modes[i];
for (j = 0; j < mode->num_channels; j++) {
unsigned int freq;
if (mode->channels[j].flag & HOSTAPD_CHAN_DISABLED)
continue;
freq = mode->channels[j].freq;
if (!freq_range_list_includes(&ranges, freq))
continue;
int_array_add_unique(&freqs, freq);
}
}
os_free(ranges.range);
return freqs;
}
#ifdef CONFIG_INTERWORKING
static int ctrl_interworking_select(struct wpa_supplicant *wpa_s, char *param)
{
int auto_sel = 0;
int *freqs = NULL;
if (param) {
char *pos;
auto_sel = os_strstr(param, "auto") != NULL;
pos = os_strstr(param, "freq=");
if (pos) {
freqs = freq_range_to_channel_list(wpa_s, pos + 5);
if (freqs == NULL)
return -1;
}
}
return interworking_select(wpa_s, auto_sel, freqs);
}
static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst,
int only_add)
{
u8 bssid[ETH_ALEN];
struct wpa_bss *bss;
if (hwaddr_aton(dst, bssid)) {
wpa_printf(MSG_DEBUG, "Invalid BSSID '%s'", dst);
return -1;
}
bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
if (bss == NULL) {
wpa_printf(MSG_DEBUG, "Could not find BSS " MACSTR,
MAC2STR(bssid));
return -1;
}
if (bss->ssid_len == 0) {
int found = 0;
wpa_printf(MSG_DEBUG, "Selected BSS entry for " MACSTR
" does not have SSID information", MAC2STR(bssid));
dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss,
list) {
if (ether_addr_equal(bss->bssid, bssid) &&
bss->ssid_len > 0) {
found = 1;
break;
}
}
if (!found)
return -1;
wpa_printf(MSG_DEBUG,
"Found another matching BSS entry with SSID");
}
return interworking_connect(wpa_s, bss, only_add);
}
static int get_anqp(struct wpa_supplicant *wpa_s, char *dst)
{
u8 dst_addr[ETH_ALEN];
int used, freq = 0;
char *pos;
#define MAX_ANQP_INFO_ID 100
u16 id[MAX_ANQP_INFO_ID];
size_t num_id = 0;
u32 subtypes = 0;
u32 mbo_subtypes = 0;
used = hwaddr_aton2(dst, dst_addr);
if (used < 0)
return -1;
pos = dst + used;
if (*pos == ' ')
pos++;
if (os_strncmp(pos, "freq=", 5) == 0) {
freq = atoi(pos + 5);
pos = os_strchr(pos, ' ');
if (!pos)
return -1;
pos++;
}
while (num_id < MAX_ANQP_INFO_ID) {
if (os_strncmp(pos, "hs20:", 5) == 0) {
#ifdef CONFIG_HS20
int num = atoi(pos + 5);
if (num <= 0 || num > 31)
return -1;
subtypes |= BIT(num);
#else /* CONFIG_HS20 */
return -1;
#endif /* CONFIG_HS20 */
} else if (os_strncmp(pos, "mbo:", 4) == 0) {
#ifdef CONFIG_MBO
int num = atoi(pos + 4);
if (num <= 0 || num > MAX_MBO_ANQP_SUBTYPE)
return -1;
mbo_subtypes |= BIT(num);
#else /* CONFIG_MBO */
return -1;
#endif /* CONFIG_MBO */
} else {
id[num_id] = atoi(pos);
if (id[num_id])
num_id++;
}
pos = os_strchr(pos + 1, ',');
if (pos == NULL)
break;
pos++;
}
if (num_id == 0 && !subtypes && !mbo_subtypes)
return -1;
return anqp_send_req(wpa_s, dst_addr, freq, id, num_id, subtypes,
mbo_subtypes);
}
static int gas_request(struct wpa_supplicant *wpa_s, char *cmd)
{
u8 dst_addr[ETH_ALEN];
struct wpabuf *advproto, *query = NULL;
int used, ret = -1;
char *pos, *end;
size_t len;
used = hwaddr_aton2(cmd, dst_addr);
if (used < 0)
return -1;
pos = cmd + used;
while (*pos == ' ')
pos++;
/* Advertisement Protocol ID */
end = os_strchr(pos, ' ');
if (end)
len = end - pos;
else
len = os_strlen(pos);
if (len & 0x01)
return -1;
len /= 2;
if (len == 0)
return -1;
advproto = wpabuf_alloc(len);
if (advproto == NULL)
return -1;
if (hexstr2bin(pos, wpabuf_put(advproto, len), len) < 0)
goto fail;
if (end) {
/* Optional Query Request */
pos = end + 1;
while (*pos == ' ')
pos++;
len = os_strlen(pos);
if (len) {
if (len & 0x01)
goto fail;
len /= 2;
if (len == 0)
goto fail;
query = wpabuf_alloc(len);
if (query == NULL)
goto fail;
if (hexstr2bin(pos, wpabuf_put(query, len), len) < 0)
goto fail;
}
}
ret = gas_send_request(wpa_s, dst_addr, advproto, query);
fail:
wpabuf_free(advproto);
wpabuf_free(query);
return ret;
}
static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf,
size_t buflen)
{
u8 addr[ETH_ALEN];
int dialog_token;
int used;
char *pos;
size_t resp_len, start, requested_len;
struct wpabuf *resp;
int ret;
used = hwaddr_aton2(cmd, addr);
if (used < 0)
return -1;
pos = cmd + used;
while (*pos == ' ')
pos++;
dialog_token = atoi(pos);
if (wpa_s->last_gas_resp &&
ether_addr_equal(addr, wpa_s->last_gas_addr) &&
dialog_token == wpa_s->last_gas_dialog_token)
resp = wpa_s->last_gas_resp;
else if (wpa_s->prev_gas_resp &&
ether_addr_equal(addr, wpa_s->prev_gas_addr) &&
dialog_token == wpa_s->prev_gas_dialog_token)
resp = wpa_s->prev_gas_resp;
else
return -1;
resp_len = wpabuf_len(resp);
start = 0;
requested_len = resp_len;
pos = os_strchr(pos, ' ');
if (pos) {
start = atoi(pos);
if (start > resp_len)
return os_snprintf(buf, buflen, "FAIL-Invalid range");
pos = os_strchr(pos, ',');
if (pos == NULL)
return -1;
pos++;
requested_len = atoi(pos);
if (start + requested_len > resp_len)
return os_snprintf(buf, buflen, "FAIL-Invalid range");
}
if (requested_len * 2 + 1 > buflen)
return os_snprintf(buf, buflen, "FAIL-Too long response");
ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(resp) + start,
requested_len);
if (start + requested_len == resp_len) {
/*
* Free memory by dropping the response after it has been
* fetched.
*/
if (resp == wpa_s->prev_gas_resp) {
wpabuf_free(wpa_s->prev_gas_resp);
wpa_s->prev_gas_resp = NULL;
} else {
wpabuf_free(wpa_s->last_gas_resp);
wpa_s->last_gas_resp = NULL;
}
}
return ret;
}
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_HS20
static int get_hs20_anqp(struct wpa_supplicant *wpa_s, char *dst)
{
u8 dst_addr[ETH_ALEN];
int used;
char *pos;
u32 subtypes = 0;
used = hwaddr_aton2(dst, dst_addr);
if (used < 0)
return -1;
pos = dst + used;
if (*pos == ' ')
pos++;
for (;;) {
int num = atoi(pos);
if (num <= 0 || num > 31)
return -1;
subtypes |= BIT(num);
pos = os_strchr(pos + 1, ',');
if (pos == NULL)
break;
pos++;
}
if (subtypes == 0)
return -1;
return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0, 0);
}
static int hs20_nai_home_realm_list(struct wpa_supplicant *wpa_s,
const u8 *addr, const char *realm)
{
u8 *buf;
size_t rlen, len;
int ret;
rlen = os_strlen(realm);
len = 3 + rlen;
buf = os_malloc(len);
if (buf == NULL)
return -1;
buf[0] = 1; /* NAI Home Realm Count */
buf[1] = 0; /* Formatted in accordance with RFC 4282 */
buf[2] = rlen;
os_memcpy(buf + 3, realm, rlen);
ret = hs20_anqp_send_req(wpa_s, addr,
BIT(HS20_STYPE_NAI_HOME_REALM_QUERY),
buf, len, 0);
os_free(buf);
return ret;
}
static int hs20_get_nai_home_realm_list(struct wpa_supplicant *wpa_s,
char *dst)
{
struct wpa_cred *cred = wpa_s->conf->cred;
u8 dst_addr[ETH_ALEN];
int used;
u8 *buf;
size_t len;
int ret;
used = hwaddr_aton2(dst, dst_addr);
if (used < 0)
return -1;
while (dst[used] == ' ')
used++;
if (os_strncmp(dst + used, "realm=", 6) == 0)
return hs20_nai_home_realm_list(wpa_s, dst_addr,
dst + used + 6);
len = os_strlen(dst + used);
if (len == 0 && cred && cred->realm)
return hs20_nai_home_realm_list(wpa_s, dst_addr, cred->realm);
if (len & 1)
return -1;
len /= 2;
buf = os_malloc(len);
if (buf == NULL)
return -1;
if (hexstr2bin(dst + used, buf, len) < 0) {
os_free(buf);
return -1;
}
ret = hs20_anqp_send_req(wpa_s, dst_addr,
BIT(HS20_STYPE_NAI_HOME_REALM_QUERY),
buf, len, 0);
os_free(buf);
return ret;
}
static int get_hs20_icon(struct wpa_supplicant *wpa_s, char *cmd, char *reply,
int buflen)
{
u8 dst_addr[ETH_ALEN];
int used;
char *ctx = NULL, *icon, *poffset, *psize;
used = hwaddr_aton2(cmd, dst_addr);
if (used < 0)
return -1;
cmd += used;
icon = str_token(cmd, " ", &ctx);
poffset = str_token(cmd, " ", &ctx);
psize = str_token(cmd, " ", &ctx);
if (!icon || !poffset || !psize)
return -1;
wpa_s->fetch_osu_icon_in_progress = 0;
return hs20_get_icon(wpa_s, dst_addr, icon, atoi(poffset), atoi(psize),
reply, buflen);
}
static int del_hs20_icon(struct wpa_supplicant *wpa_s, char *cmd)
{
u8 dst_addr[ETH_ALEN];
int used;
char *icon;
if (!cmd[0])
return hs20_del_icon(wpa_s, NULL, NULL);
used = hwaddr_aton2(cmd, dst_addr);
if (used < 0)
return -1;
while (cmd[used] == ' ')
used++;
icon = cmd[used] ? &cmd[used] : NULL;
return hs20_del_icon(wpa_s, dst_addr, icon);
}
static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd, int inmem)
{
u8 dst_addr[ETH_ALEN];
int used;
char *icon;
used = hwaddr_aton2(cmd, dst_addr);
if (used < 0)
return -1;
while (cmd[used] == ' ')
used++;
icon = &cmd[used];
wpa_s->fetch_osu_icon_in_progress = 0;
return hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_ICON_REQUEST),
(u8 *) icon, os_strlen(icon), inmem);
}
#endif /* CONFIG_HS20 */
#ifdef CONFIG_AUTOSCAN
static int wpa_supplicant_ctrl_iface_autoscan(struct wpa_supplicant *wpa_s,
char *cmd)
{
enum wpa_states state = wpa_s->wpa_state;
char *new_params = NULL;
if (os_strlen(cmd) > 0) {
new_params = os_strdup(cmd);
if (new_params == NULL)
return -1;
}
os_free(wpa_s->conf->autoscan);
wpa_s->conf->autoscan = new_params;
if (wpa_s->conf->autoscan == NULL)
autoscan_deinit(wpa_s);
else if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
autoscan_init(wpa_s, 1);
else if (state == WPA_SCANNING)
wpa_supplicant_reinit_autoscan(wpa_s);
else
wpa_printf(MSG_DEBUG, "No autoscan update in state %s",
wpa_supplicant_state_txt(state));
return 0;
}
#endif /* CONFIG_AUTOSCAN */
#ifdef CONFIG_WNM
static int wpas_ctrl_iface_wnm_sleep(struct wpa_supplicant *wpa_s, char *cmd)
{
int enter;
int intval = 0;
char *pos;
int ret;
struct wpabuf *tfs_req = NULL;
if (os_strncmp(cmd, "enter", 5) == 0)
enter = 1;
else if (os_strncmp(cmd, "exit", 4) == 0)
enter = 0;
else
return -1;
pos = os_strstr(cmd, " interval=");
if (pos)
intval = atoi(pos + 10);
pos = os_strstr(cmd, " tfs_req=");
if (pos) {
char *end;
size_t len;
pos += 9;
end = os_strchr(pos, ' ');
if (end)
len = end - pos;
else
len = os_strlen(pos);
if (len & 1)
return -1;
len /= 2;
tfs_req = wpabuf_alloc(len);
if (tfs_req == NULL)
return -1;
if (hexstr2bin(pos, wpabuf_put(tfs_req, len), len) < 0) {
wpabuf_free(tfs_req);
return -1;
}
}
ret = ieee802_11_send_wnmsleep_req(wpa_s, enter ? WNM_SLEEP_MODE_ENTER :
WNM_SLEEP_MODE_EXIT, intval,
tfs_req);
wpabuf_free(tfs_req);
return ret;
}
static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd)
{
int query_reason, list = 0;
char *btm_candidates = NULL;
query_reason = atoi(cmd);
cmd = os_strchr(cmd, ' ');
if (cmd) {
if (os_strncmp(cmd, " list", 5) == 0)
list = 1;
else
btm_candidates = cmd;
}
wpa_printf(MSG_DEBUG,
"CTRL_IFACE: WNM_BSS_QUERY query_reason=%d%s",
query_reason, list ? " candidate list" : "");
return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason,
btm_candidates,
list);
}
WNM: Collocated Interference Reporting Add support for negotiating WNM Collocated Interference Reporting. This allows hostapd to request associated STAs to report their collocated interference information and wpa_supplicant to process such request and reporting. The actual values (Collocated Interference Report Elements) are out of scope of hostapd and wpa_supplicant, i.e., external components are expected to generated and process these. For hostapd/AP, this mechanism is enabled by setting coloc_intf_reporting=1 in configuration. STAs are requested to perform reporting with "COLOC_INTF_REQ <addr> <Automatic Report Enabled> <Report Timeout>" control interface command. The received reports are indicated as control interface events "COLOC-INTF-REPORT <addr> <dialog token> <hexdump of report elements>". For wpa_supplicant/STA, this mechanism is enabled by setting coloc_intf_reporting=1 in configuration and setting Collocated Interference Report Elements as a hexdump with "SET coloc_intf_elems <hexdump>" control interface command. The hexdump can contain one or more Collocated Interference Report Elements (each including the information element header). For additional testing purposes, received requests are reported with "COLOC-INTF-REQ <dialog token> <automatic report enabled> <report timeout>" control interface events and unsolicited reports can be sent with "COLOC_INTF_REPORT <hexdump>". This commit adds support for reporting changes in the collocated interference (Automatic Report Enabled == 1 and partial 3), but not for periodic reports (2 and other part of 3). Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
2018-10-30 13:00:00 +01:00
static int wpas_ctrl_iface_coloc_intf_report(struct wpa_supplicant *wpa_s,
char *cmd)
{
struct wpabuf *elems;
int ret;
elems = wpabuf_parse_bin(cmd);
if (!elems)
return -1;
ret = wnm_send_coloc_intf_report(wpa_s, 0, elems);
wpabuf_free(elems);
return ret;
}
#endif /* CONFIG_WNM */
static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf,
size_t buflen)
{
struct wpa_signal_info si;
int ret;
char *pos, *end;
ret = wpa_drv_signal_poll(wpa_s, &si);
if (ret)
return -1;
pos = buf;
end = buf + buflen;
ret = os_snprintf(pos, end - pos, "RSSI=%d\nLINKSPEED=%lu\n"
"NOISE=%d\nFREQUENCY=%u\n",
si.data.signal, si.data.current_tx_rate / 1000,
si.current_noise, si.frequency);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
if (si.chanwidth != CHAN_WIDTH_UNKNOWN) {
ret = os_snprintf(pos, end - pos, "WIDTH=%s\n",
channel_width_to_string(si.chanwidth));
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (si.center_frq1 > 0) {
ret = os_snprintf(pos, end - pos, "CENTER_FRQ1=%d\n",
si.center_frq1);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (si.center_frq2 > 0) {
ret = os_snprintf(pos, end - pos, "CENTER_FRQ2=%d\n",
si.center_frq2);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (si.data.avg_signal) {
ret = os_snprintf(pos, end - pos,
"AVG_RSSI=%d\n", si.data.avg_signal);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (si.data.avg_beacon_signal) {
ret = os_snprintf(pos, end - pos,
"AVG_BEACON_RSSI=%d\n",
si.data.avg_beacon_signal);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
return pos - buf;
}
static int wpas_ctrl_iface_signal_monitor(struct wpa_supplicant *wpa_s,
const char *cmd)
{
const char *pos;
int threshold = 0;
int hysteresis = 0;
if (wpa_s->bgscan && wpa_s->bgscan_priv) {
wpa_printf(MSG_DEBUG,
"Reject SIGNAL_MONITOR command - bgscan is active");
return -1;
}
pos = os_strstr(cmd, "THRESHOLD=");
if (pos)
threshold = atoi(pos + 10);
pos = os_strstr(cmd, "HYSTERESIS=");
if (pos)
hysteresis = atoi(pos + 11);
return wpa_drv_signal_monitor(wpa_s, threshold, hysteresis);
}
#ifdef CONFIG_TESTING_OPTIONS
int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s,
enum wpa_driver_if_type if_type,
unsigned int *num,
struct weighted_pcl *freq_list)
{
char *pos = wpa_s->get_pref_freq_list_override;
char *end;
unsigned int count = 0;
/* Override string format:
* <if_type1>:<freq1>,<freq2>,... <if_type2>:... */
while (pos) {
if (atoi(pos) == (int) if_type)
break;
pos = os_strchr(pos, ' ');
if (pos)
pos++;
}
if (!pos)
return -1;
pos = os_strchr(pos, ':');
if (!pos)
return -1;
pos++;
end = os_strchr(pos, ' ');
while (pos && (!end || pos < end) && count < *num) {
freq_list[count].freq = atoi(pos);
freq_list[count++].flag = WEIGHTED_PCL_GO | WEIGHTED_PCL_CLI;
pos = os_strchr(pos, ',');
if (pos)
pos++;
}
*num = count;
return 0;
}
#endif /* CONFIG_TESTING_OPTIONS */
static int wpas_ctrl_iface_get_pref_freq_list(
struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
{
unsigned int num = 100, i;
int ret;
enum wpa_driver_if_type iface_type;
char *pos, *end;
struct weighted_pcl freq_list[100];
pos = buf;
end = buf + buflen;
/* buf: "<interface_type>" */
if (os_strcmp(cmd, "STATION") == 0)
iface_type = WPA_IF_STATION;
else if (os_strcmp(cmd, "AP") == 0)
iface_type = WPA_IF_AP_BSS;
else if (os_strcmp(cmd, "P2P_GO") == 0)
iface_type = WPA_IF_P2P_GO;
else if (os_strcmp(cmd, "P2P_CLIENT") == 0)
iface_type = WPA_IF_P2P_CLIENT;
else if (os_strcmp(cmd, "IBSS") == 0)
iface_type = WPA_IF_IBSS;
else if (os_strcmp(cmd, "TDLS") == 0)
iface_type = WPA_IF_TDLS;
else
return -1;
wpa_printf(MSG_DEBUG,
"CTRL_IFACE: GET_PREF_FREQ_LIST iface_type=%d (%s)",
iface_type, cmd);
ret = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &num, freq_list);
if (ret)
return -1;
for (i = 0; i < num; i++) {
ret = os_snprintf(pos, end - pos, "%s%u",
i > 0 ? "," : "", freq_list[i].freq);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
return pos - buf;
}
static int wpas_ctrl_iface_driver_flags(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen)
{
int ret, i;
char *pos, *end;
ret = os_snprintf(buf, buflen, "%016llX:\n",
(long long unsigned) wpa_s->drv_flags);
if (os_snprintf_error(buflen, ret))
return -1;
pos = buf + ret;
end = buf + buflen;
for (i = 0; i < 64; i++) {
if (wpa_s->drv_flags & (1LLU << i)) {
ret = os_snprintf(pos, end - pos, "%s\n",
driver_flag_to_string(1LLU << i));
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
}
return pos - buf;
}
static int wpas_ctrl_iface_driver_flags2(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen)
{
int ret, i;
char *pos, *end;
ret = os_snprintf(buf, buflen, "%016llX:\n",
(long long unsigned) wpa_s->drv_flags2);
if (os_snprintf_error(buflen, ret))
return -1;
pos = buf + ret;
end = buf + buflen;
for (i = 0; i < 64; i++) {
if (wpa_s->drv_flags2 & (1LLU << i)) {
ret = os_snprintf(pos, end - pos, "%s\n",
driver_flag2_to_string(1LLU << i));
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
}
return pos - buf;
}
static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf,
size_t buflen)
{
struct hostap_sta_driver_data sta;
int ret;
ret = wpa_drv_pktcnt_poll(wpa_s, &sta);
if (ret)
return -1;
ret = os_snprintf(buf, buflen, "TXGOOD=%lu\nTXBAD=%lu\nRXGOOD=%lu\n",
sta.tx_packets, sta.tx_retry_failed, sta.rx_packets);
if (os_snprintf_error(buflen, ret))
return -1;
return ret;
}
#ifdef ANDROID
static int wpa_supplicant_driver_cmd(struct wpa_supplicant *wpa_s, char *cmd,
char *buf, size_t buflen)
{
int ret;
ret = wpa_drv_driver_cmd(wpa_s, cmd, buf, buflen);
if (ret == 0) {
if (os_strncasecmp(cmd, "COUNTRY", 7) == 0) {
struct p2p_data *p2p = wpa_s->global->p2p;
if (p2p) {
char country[3];
country[0] = cmd[8];
country[1] = cmd[9];
country[2] = 0x04;
p2p_set_country(p2p, country);
}
}
ret = os_snprintf(buf, buflen, "%s\n", "OK");
if (os_snprintf_error(buflen, ret))
ret = -1;
}
return ret;
}
#endif /* ANDROID */
static int wpa_supplicant_vendor_cmd(struct wpa_supplicant *wpa_s, char *cmd,
char *buf, size_t buflen)
{
int ret;
char *pos, *temp = NULL;
u8 *data = NULL;
unsigned int vendor_id, subcmd;
enum nested_attr nested_attr_flag = NESTED_ATTR_UNSPECIFIED;
struct wpabuf *reply;
size_t data_len = 0;
/**
* cmd: <vendor id> <subcommand id> [<hex formatted data>]
* [nested=<0|1>]
*/
vendor_id = strtoul(cmd, &pos, 16);
if (!isblank((unsigned char) *pos))
return -EINVAL;
subcmd = strtoul(pos, &pos, 10);
if (*pos != '\0') {
if (!isblank((unsigned char) *pos++))
return -EINVAL;
temp = os_strchr(pos, ' ');
data_len = temp ? (size_t) (temp - pos) : os_strlen(pos);
}
if (data_len) {
data_len /= 2;
data = os_malloc(data_len);
if (!data)
return -1;
if (hexstr2bin(pos, data, data_len)) {
wpa_printf(MSG_DEBUG,
"Vendor command: wrong parameter format");
os_free(data);
return -EINVAL;
}
}
pos = os_strstr(cmd, "nested=");
if (pos)
nested_attr_flag = atoi(pos + 7) ? NESTED_ATTR_USED :
NESTED_ATTR_NOT_USED;
reply = wpabuf_alloc((buflen - 1) / 2);
if (!reply) {
os_free(data);
return -1;
}
ret = wpa_drv_vendor_cmd(wpa_s, vendor_id, subcmd, data, data_len,
nested_attr_flag, reply);
if (ret == 0)
ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
wpabuf_len(reply));
wpabuf_free(reply);
os_free(data);
return ret;
}
static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
{
#ifdef CONFIG_P2P
struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s ?
wpa_s->global->p2p_init_wpa_s : wpa_s;
#endif /* CONFIG_P2P */
wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state");
if (wpas_abort_ongoing_scan(wpa_s) == 0)
wpa_s->ignore_post_flush_scan_res = 1;
if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
/*
* Avoid possible auto connect re-connection on getting
* disconnected due to state flush.
*/
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
}
#ifdef CONFIG_P2P
wpas_p2p_group_remove(p2p_wpa_s, "*");
wpas_p2p_cancel(p2p_wpa_s);
p2p_ctrl_flush(p2p_wpa_s);
wpas_p2p_service_flush(p2p_wpa_s);
p2p_wpa_s->global->p2p_disabled = 0;
p2p_wpa_s->global->p2p_per_sta_psk = 0;
p2p_wpa_s->conf->num_sec_device_types = 0;
p2p_wpa_s->p2p_disable_ip_addr_req = 0;
os_free(p2p_wpa_s->global->p2p_go_avoid_freq.range);
p2p_wpa_s->global->p2p_go_avoid_freq.range = NULL;
p2p_wpa_s->global->p2p_go_avoid_freq.num = 0;
p2p_wpa_s->global->pending_p2ps_group = 0;
p2p_wpa_s->global->pending_p2ps_group_freq = 0;
#endif /* CONFIG_P2P */
#ifdef CONFIG_WPS_TESTING
wps_version_number = 0x20;
wps_testing_stub_cred = 0;
wps_corrupt_pkhash = 0;
wps_force_auth_types_in_use = 0;
wps_force_encr_types_in_use = 0;
#endif /* CONFIG_WPS_TESTING */
#ifdef CONFIG_WPS
wpa_s->wps_fragment_size = 0;
wpas_wps_cancel(wpa_s);
wps_registrar_flush(wpa_s->wps->registrar);
#endif /* CONFIG_WPS */
wpa_s->after_wps = 0;
wpa_s->known_wps_freq = 0;
#ifdef CONFIG_DPP
wpas_dpp_deinit(wpa_s);
wpa_s->dpp_init_max_tries = 0;
wpa_s->dpp_init_retry_time = 0;
wpa_s->dpp_resp_wait_time = 0;
wpa_s->dpp_resp_max_tries = 0;
wpa_s->dpp_resp_retry_time = 0;
#ifdef CONFIG_DPP2
wpas_dpp_chirp_stop(wpa_s);
wpa_s->dpp_pfs_fallback = 0;
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_DPP3
{
int i;
for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
struct dpp_pb_info *info;
info = &wpa_s->dpp_pb[i];
info->rx_time.sec = 0;
info->rx_time.usec = 0;
}
}
#endif /* CONFIG_DPP3 */
#ifdef CONFIG_TESTING_OPTIONS
os_memset(dpp_pkex_own_mac_override, 0, ETH_ALEN);
os_memset(dpp_pkex_peer_mac_override, 0, ETH_ALEN);
dpp_pkex_ephemeral_key_override_len = 0;
dpp_protocol_key_override_len = 0;
dpp_nonce_override_len = 0;
#ifdef CONFIG_DPP3
dpp_version_override = 3;
#elif defined(CONFIG_DPP2)
dpp_version_override = 2;
#else /* CONFIG_DPP2 */
dpp_version_override = 1;
#endif /* CONFIG_DPP2 */
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_DPP */
#ifdef CONFIG_TDLS
#ifdef CONFIG_TDLS_TESTING
tdls_testing = 0;
#endif /* CONFIG_TDLS_TESTING */
wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL);
wpa_tdls_enable(wpa_s->wpa, 1);
#endif /* CONFIG_TDLS */
eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL);
wpa_supplicant_stop_countermeasures(wpa_s, NULL);
wpa_s->last_michael_mic_error.sec = 0;
wpa_s->no_keep_alive = 0;
wpa_s->own_disconnect_req = 0;
wpa_s->own_reconnect_req = 0;
wpa_s->deny_ptk0_rekey = 0;
os_free(wpa_s->disallow_aps_bssid);
wpa_s->disallow_aps_bssid = NULL;
wpa_s->disallow_aps_bssid_count = 0;
os_free(wpa_s->disallow_aps_ssid);
wpa_s->disallow_aps_ssid = NULL;
wpa_s->disallow_aps_ssid_count = 0;
wpa_s->set_sta_uapsd = 0;
wpa_s->sta_uapsd = 0;
wpa_supplicant: Track consecutive connection failures Within wpas_connection_failed(), the 'count' value of wpa_blacklist is erroneously used as a tally of the number times the device has failed to associate to a given BSSID without making a successful connection. This is not accurate because there are a variety of ways a BSS can be added to the blacklist beyond failed association such as interference or deauthentication. This 'count' is lost whenever the blacklist is cleared, so the wpa_supplicant stores an additional value 'extra_blacklist_count' which helps persist the 'count' through clears. These count values are used to determine how long to wait to rescan after a failed connection attempt. While this logic was already slightly wrong, it would have been completely broken by the upcoming change which adds time-based blacklisting functionality. With the upcoming change, 'count' values are not cleared on association, and thus do not necessarily even approximate the "consecutive connection failures" which they were being used for. This change seeks to remove this unnecessary overloading of the blacklist 'count' by directly tracking consecutive connection failures within the wpa_supplicant struct, independent of the blacklist. This new 'consecutive_conn_failures' is iterated with every connection failure and cleared when any successful connection is made. This change also removes the now unused 'extra_blacklist_count' value. Signed-off-by: Kevin Lund <kglund@google.com> Signed-off-by: Brian Norris <briannorris@chromium.org>
2020-06-11 23:11:15 +02:00
wpa_s->consecutive_conn_failures = 0;
wpa_drv_radio_disable(wpa_s, 0);
wpa_bssid_ignore_clear(wpa_s);
wpa_supplicant_ctrl_iface_remove_network(wpa_s, "all");
wpa_supplicant_ctrl_iface_remove_cred(wpa_s, "all");
wpa_config_flush_blobs(wpa_s->conf);
wpa_s->conf->auto_interworking = 0;
wpa_s->conf->okc = 0;
ptksa_cache_flush(wpa_s->ptksa, NULL, WPA_CIPHER_NONE);
wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
rsn_preauth_deinit(wpa_s->wpa);
wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, 43200);
wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, 70);
wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, 60);
eapol_sm_notify_logoff(wpa_s->eapol, false);
radio_remove_works(wpa_s, NULL, 1);
wpa_s->ext_work_in_progress = 0;
wpa_s->next_ssid = NULL;
wnm_btm_reset(wpa_s);
#ifdef CONFIG_INTERWORKING
#ifdef CONFIG_HS20
hs20_cancel_fetch_osu(wpa_s);
hs20_del_icon(wpa_s, NULL, NULL);
#endif /* CONFIG_HS20 */
#endif /* CONFIG_INTERWORKING */
wpa_s->ext_mgmt_frame_handling = 0;
wpa_s->ext_eapol_frame_io = 0;
#ifdef CONFIG_TESTING_OPTIONS
wpa_s->extra_roc_dur = 0;
wpa_s->test_failure = WPAS_TEST_FAILURE_NONE;
wpa_s->p2p_go_csa_on_inv = 0;
wpa_s->ignore_auth_resp = 0;
wpa_s->ignore_assoc_disallow = 0;
wpa_s->disable_sa_query = 0;
wpa_s->testing_resend_assoc = 0;
wpa_s->ignore_sae_h2e_only = 0;
wpa_s->ft_rsnxe_used = 0;
wpa_s->reject_btm_req_reason = 0;
wpa_sm_set_test_assoc_ie(wpa_s->wpa, NULL);
wpa_sm_set_test_eapol_m2_elems(wpa_s->wpa, NULL);
wpa_sm_set_test_eapol_m4_elems(wpa_s->wpa, NULL);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_ENCRYPT_EAPOL_M2, 0);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_ENCRYPT_EAPOL_M4, 0);
os_free(wpa_s->get_pref_freq_list_override);
wpa_s->get_pref_freq_list_override = NULL;
wpabuf_free(wpa_s->sae_commit_override);
wpa_s->sae_commit_override = NULL;
os_free(wpa_s->extra_sae_rejected_groups);
wpa_s->extra_sae_rejected_groups = NULL;
wpabuf_free(wpa_s->rsne_override_eapol);
wpa_s->rsne_override_eapol = NULL;
wpabuf_free(wpa_s->rsnxe_override_assoc);
wpa_s->rsnxe_override_assoc = NULL;
wpabuf_free(wpa_s->rsnxe_override_eapol);
wpa_s->rsnxe_override_eapol = NULL;
wpas_clear_driver_signal_override(wpa_s);
#ifndef CONFIG_NO_ROBUST_AV
wpa_s->disable_scs_support = 0;
wpa_s->disable_mscs_support = 0;
wpa_s->enable_dscp_policy_capa = 0;
#endif /* CONFIG_NO_ROBUST_AV */
wpa_s->oci_freq_override_eapol = 0;
wpa_s->oci_freq_override_saquery_req = 0;
wpa_s->oci_freq_override_saquery_resp = 0;
wpa_s->oci_freq_override_eapol_g2 = 0;
wpa_s->oci_freq_override_ft_assoc = 0;
wpa_s->oci_freq_override_fils_assoc = 0;
wpa_s->oci_freq_override_wnm_sleep = 0;
wpa_s->disable_eapol_g2_tx = 0;
wpa_s->test_assoc_comeback_type = -1;
#ifdef CONFIG_DPP
os_free(wpa_s->dpp_config_obj_override);
wpa_s->dpp_config_obj_override = NULL;
os_free(wpa_s->dpp_discovery_override);
wpa_s->dpp_discovery_override = NULL;
os_free(wpa_s->dpp_groups_override);
wpa_s->dpp_groups_override = NULL;
wpa_s->dpp_ignore_netaccesskey_mismatch = 0;
wpa_s->dpp_discard_public_action = 0;
dpp_test = DPP_TEST_DISABLED;
#endif /* CONFIG_DPP */
#endif /* CONFIG_TESTING_OPTIONS */
wpa_s->disconnected = 0;
os_free(wpa_s->next_scan_freqs);
wpa_s->next_scan_freqs = NULL;
os_memset(wpa_s->next_scan_bssid, 0, ETH_ALEN);
wpa_s->next_scan_bssid_wildcard_ssid = 0;
os_free(wpa_s->select_network_scan_freqs);
wpa_s->select_network_scan_freqs = NULL;
#ifndef CONFIG_NO_ROBUST_AV
os_memset(&wpa_s->robust_av, 0, sizeof(struct robust_av_data));
#endif /* CONFIG_NO_ROBUST_AV */
wpa_bss_flush(wpa_s);
if (!dl_list_empty(&wpa_s->bss)) {
wpa_printf(MSG_DEBUG,
"BSS table not empty after flush: %u entries, current_bss=%p bssid="
MACSTR " pending_bssid=" MACSTR,
dl_list_len(&wpa_s->bss), wpa_s->current_bss,
MAC2STR(wpa_s->bssid),
MAC2STR(wpa_s->pending_bssid));
}
eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
wpa_s->wnmsleep_used = 0;
#ifdef CONFIG_SME
wpa_s->sme.last_unprot_disconnect.sec = 0;
wpa_s->sme.auth_alg = 0;
#endif /* CONFIG_SME */
wpabuf_free(wpa_s->ric_ies);
wpa_s->ric_ies = NULL;
wpa_supplicant_update_channel_list(wpa_s, NULL);
free_bss_tmp_disallowed(wpa_s);
#ifndef CONFIG_NO_ROBUST_AV
os_memset(&wpa_s->robust_av, 0, sizeof(struct robust_av_data));
#endif /* CONFIG_NO_ROBUST_AV */
#ifdef CONFIG_PASN
wpas_pasn_auth_stop(wpa_s);
#endif /* CONFIG_PASN */
if (wpa_s->mac_addr_changed && wpa_s->conf->mac_addr == 0)
wpas_restore_permanent_mac_addr(wpa_s);
wpa_s->conf->ignore_old_scan_res = 0;
#ifdef CONFIG_NAN_USD
wpas_nan_usd_flush(wpa_s);
#endif /* CONFIG_NAN_USD */
}
static int wpas_ctrl_radio_work_show(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen)
{
struct wpa_radio_work *work;
char *pos, *end;
struct os_reltime now, diff;
pos = buf;
end = buf + buflen;
os_get_reltime(&now);
dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list)
{
int ret;
os_reltime_sub(&now, &work->time, &diff);
ret = os_snprintf(pos, end - pos, "%s@%s:%u:%u:%ld.%06ld\n",
work->type, work->wpa_s->ifname, work->freq,
work->started, diff.sec, diff.usec);
if (os_snprintf_error(end - pos, ret))
break;
pos += ret;
}
return pos - buf;
}
static void wpas_ctrl_radio_work_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_radio_work *work = eloop_ctx;
struct wpa_external_work *ework = work->ctx;
wpa_dbg(work->wpa_s, MSG_DEBUG,
"Timing out external radio work %u (%s)",
ework->id, work->type);
wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_TIMEOUT "%u", ework->id);
work->wpa_s->ext_work_in_progress = 0;
radio_work_done(work);
os_free(ework);
}
static void wpas_ctrl_radio_work_cb(struct wpa_radio_work *work, int deinit)
{
struct wpa_external_work *ework = work->ctx;
if (deinit) {
if (work->started)
eloop_cancel_timeout(wpas_ctrl_radio_work_timeout,
work, NULL);
/*
* work->type points to a buffer in ework, so need to replace
* that here with a fixed string to avoid use of freed memory
* in debug prints.
*/
work->type = "freed-ext-work";
work->ctx = NULL;
os_free(ework);
return;
}
wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting external radio work %u (%s)",
ework->id, ework->type);
wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_START "%u", ework->id);
work->wpa_s->ext_work_in_progress = 1;
if (!ework->timeout)
ework->timeout = 10;
eloop_register_timeout(ework->timeout, 0, wpas_ctrl_radio_work_timeout,
work, NULL);
}
static int wpas_ctrl_radio_work_add(struct wpa_supplicant *wpa_s, char *cmd,
char *buf, size_t buflen)
{
struct wpa_external_work *ework;
char *pos, *pos2;
size_t type_len;
int ret;
unsigned int freq = 0;
/* format: <name> [freq=<MHz>] [timeout=<seconds>] */
ework = os_zalloc(sizeof(*ework));
if (ework == NULL)
return -1;
pos = os_strchr(cmd, ' ');
if (pos) {
type_len = pos - cmd;
pos++;
pos2 = os_strstr(pos, "freq=");
if (pos2)
freq = atoi(pos2 + 5);
pos2 = os_strstr(pos, "timeout=");
if (pos2)
ework->timeout = atoi(pos2 + 8);
} else {
type_len = os_strlen(cmd);
}
if (4 + type_len >= sizeof(ework->type))
type_len = sizeof(ework->type) - 4 - 1;
os_strlcpy(ework->type, "ext:", sizeof(ework->type));
os_memcpy(ework->type + 4, cmd, type_len);
ework->type[4 + type_len] = '\0';
wpa_s->ext_work_id++;
if (wpa_s->ext_work_id == 0)
wpa_s->ext_work_id++;
ework->id = wpa_s->ext_work_id;
if (radio_add_work(wpa_s, freq, ework->type, 0, wpas_ctrl_radio_work_cb,
ework) < 0) {
os_free(ework);
return -1;
}
ret = os_snprintf(buf, buflen, "%u", ework->id);
if (os_snprintf_error(buflen, ret))
return -1;
return ret;
}
static int wpas_ctrl_radio_work_done(struct wpa_supplicant *wpa_s, char *cmd)
{
struct wpa_radio_work *work;
unsigned int id = atoi(cmd);
dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list)
{
struct wpa_external_work *ework;
if (os_strncmp(work->type, "ext:", 4) != 0)
continue;
ework = work->ctx;
if (id && ework->id != id)
continue;
wpa_dbg(wpa_s, MSG_DEBUG,
"Completed external radio work %u (%s)",
ework->id, ework->type);
eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, work, NULL);
wpa_s->ext_work_in_progress = 0;
radio_work_done(work);
os_free(ework);
return 3; /* "OK\n" */
}
return -1;
}
static int wpas_ctrl_radio_work(struct wpa_supplicant *wpa_s, char *cmd,
char *buf, size_t buflen)
{
if (os_strcmp(cmd, "show") == 0)
return wpas_ctrl_radio_work_show(wpa_s, buf, buflen);
if (os_strncmp(cmd, "add ", 4) == 0)
return wpas_ctrl_radio_work_add(wpa_s, cmd + 4, buf, buflen);
if (os_strncmp(cmd, "done ", 5) == 0)
return wpas_ctrl_radio_work_done(wpa_s, cmd + 4);
return -1;
}
void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s)
{
struct wpa_radio_work *work, *tmp;
if (!wpa_s || !wpa_s->radio)
return;
dl_list_for_each_safe(work, tmp, &wpa_s->radio->work,
struct wpa_radio_work, list) {
struct wpa_external_work *ework;
if (os_strncmp(work->type, "ext:", 4) != 0)
continue;
ework = work->ctx;
wpa_dbg(wpa_s, MSG_DEBUG,
"Flushing%s external radio work %u (%s)",
work->started ? " started" : "", ework->id,
ework->type);
if (work->started)
eloop_cancel_timeout(wpas_ctrl_radio_work_timeout,
work, NULL);
radio_work_done(work);
os_free(ework);
}
}
static void wpas_ctrl_eapol_response(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
eapol_sm_notify_ctrl_response(wpa_s->eapol);
}
static int scan_id_list_parse(struct wpa_supplicant *wpa_s, const char *value,
unsigned int *scan_id_count, int scan_id[])
{
const char *pos = value;
while (pos) {
if (*pos == ' ' || *pos == '\0')
break;
if (*scan_id_count == MAX_SCAN_ID)
return -1;
scan_id[(*scan_id_count)++] = atoi(pos);
pos = os_strchr(pos, ',');
if (pos)
pos++;
}
return 0;
}
static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params,
char *reply, int reply_size, int *reply_len)
{
char *pos;
unsigned int manual_scan_passive = 0;
unsigned int manual_scan_use_id = 0;
unsigned int manual_scan_only_new = 0;
unsigned int scan_only = 0;
unsigned int scan_id_count = 0;
unsigned int manual_non_coloc_6ghz = 0;
int scan_id[MAX_SCAN_ID];
void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
int *manual_scan_freqs = NULL;
struct wpa_ssid_value *ssid = NULL, *ns;
unsigned int ssid_count = 0;
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
*reply_len = -1;
return;
}
if (radio_work_pending(wpa_s, "scan")) {
wpa_printf(MSG_DEBUG,
"Pending scan scheduled - reject new request");
*reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
return;
}
#ifdef CONFIG_INTERWORKING
if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) {
wpa_printf(MSG_DEBUG,
"Interworking select in progress - reject new scan");
*reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
return;
}
#endif /* CONFIG_INTERWORKING */
if (params) {
if (os_strncasecmp(params, "TYPE=ONLY", 9) == 0)
scan_only = 1;
pos = os_strstr(params, "freq=");
if (pos) {
manual_scan_freqs = freq_range_to_channel_list(wpa_s,
pos + 5);
if (manual_scan_freqs == NULL) {
*reply_len = -1;
goto done;
}
}
pos = os_strstr(params, "passive=");
if (pos)
manual_scan_passive = !!atoi(pos + 8);
pos = os_strstr(params, "use_id=");
if (pos)
manual_scan_use_id = atoi(pos + 7);
pos = os_strstr(params, "only_new=1");
if (pos)
manual_scan_only_new = 1;
pos = os_strstr(params, "scan_id=");
if (pos && scan_id_list_parse(wpa_s, pos + 8, &scan_id_count,
scan_id) < 0) {
*reply_len = -1;
goto done;
}
pos = os_strstr(params, "bssid=");
if (pos) {
u8 bssid[ETH_ALEN];
pos += 6;
if (hwaddr_aton(pos, bssid)) {
wpa_printf(MSG_ERROR, "Invalid BSSID %s", pos);
*reply_len = -1;
goto done;
}
os_memcpy(wpa_s->next_scan_bssid, bssid, ETH_ALEN);
wpa_s->next_scan_bssid_wildcard_ssid =
os_strstr(params, "wildcard_ssid=1") != NULL;
}
pos = os_strstr(params, "non_coloc_6ghz=");
if (pos)
manual_non_coloc_6ghz = !!atoi(pos + 15);
pos = params;
while (pos && *pos != '\0') {
if (os_strncmp(pos, "ssid ", 5) == 0) {
char *end;
pos += 5;
end = pos;
while (*end) {
if (*end == '\0' || *end == ' ')
break;
end++;
}
ns = os_realloc_array(
ssid, ssid_count + 1,
sizeof(struct wpa_ssid_value));
if (ns == NULL) {
*reply_len = -1;
goto done;
}
ssid = ns;
if ((end - pos) & 0x01 ||
end - pos > 2 * SSID_MAX_LEN ||
hexstr2bin(pos, ssid[ssid_count].ssid,
(end - pos) / 2) < 0) {
wpa_printf(MSG_DEBUG,
"Invalid SSID value '%s'",
pos);
*reply_len = -1;
goto done;
}
ssid[ssid_count].ssid_len = (end - pos) / 2;
wpa_hexdump_ascii(MSG_DEBUG, "scan SSID",
ssid[ssid_count].ssid,
ssid[ssid_count].ssid_len);
ssid_count++;
pos = end;
}
pos = os_strchr(pos, ' ');
if (pos)
pos++;
}
}
wpa_s->num_ssids_from_scan_req = ssid_count;
os_free(wpa_s->ssids_from_scan_req);
if (ssid_count) {
wpa_s->ssids_from_scan_req = ssid;
ssid = NULL;
} else {
wpa_s->ssids_from_scan_req = NULL;
}
if (scan_only)
scan_res_handler = scan_only_handler;
else if (wpa_s->scan_res_handler == scan_only_handler)
scan_res_handler = NULL;
else
scan_res_handler = wpa_s->scan_res_handler;
if (!wpa_s->sched_scanning && !wpa_s->scanning &&
((wpa_s->wpa_state <= WPA_SCANNING) ||
(wpa_s->wpa_state == WPA_COMPLETED))) {
wpa_s->manual_scan_passive = manual_scan_passive;
wpa_s->manual_scan_use_id = manual_scan_use_id;
wpa_s->manual_scan_only_new = manual_scan_only_new;
wpa_s->scan_id_count = scan_id_count;
wpa_s->manual_non_coloc_6ghz = manual_non_coloc_6ghz;
os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int));
wpa_s->scan_res_handler = scan_res_handler;
os_free(wpa_s->manual_scan_freqs);
wpa_s->manual_scan_freqs = manual_scan_freqs;
manual_scan_freqs = NULL;
wpa_s->normal_scans = 0;
wpa_s->scan_req = MANUAL_SCAN_REQ;
wpa_s->after_wps = 0;
wpa_s->known_wps_freq = 0;
wpa_supplicant_req_scan(wpa_s, 0, 0);
if (wpa_s->manual_scan_use_id) {
wpa_s->manual_scan_id++;
wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u",
wpa_s->manual_scan_id);
*reply_len = os_snprintf(reply, reply_size, "%u\n",
wpa_s->manual_scan_id);
}
} else if (wpa_s->sched_scanning) {
wpa_s->manual_scan_passive = manual_scan_passive;
wpa_s->manual_scan_use_id = manual_scan_use_id;
wpa_s->manual_scan_only_new = manual_scan_only_new;
wpa_s->scan_id_count = scan_id_count;
wpa_s->manual_non_coloc_6ghz = manual_non_coloc_6ghz;
os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int));
wpa_s->scan_res_handler = scan_res_handler;
os_free(wpa_s->manual_scan_freqs);
wpa_s->manual_scan_freqs = manual_scan_freqs;
manual_scan_freqs = NULL;
wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to allow requested full scan to proceed");
wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_s->scan_req = MANUAL_SCAN_REQ;
wpa_supplicant_req_scan(wpa_s, 0, 0);
if (wpa_s->manual_scan_use_id) {
wpa_s->manual_scan_id++;
*reply_len = os_snprintf(reply, reply_size, "%u\n",
wpa_s->manual_scan_id);
wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u",
wpa_s->manual_scan_id);
}
} else {
wpa_printf(MSG_DEBUG, "Ongoing scan action - reject new request");
*reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
}
done:
os_free(manual_scan_freqs);
os_free(ssid);
}
#ifdef CONFIG_TESTING_OPTIONS
static void wpas_ctrl_iface_mgmt_tx_cb(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)
{
wpa_msg(wpa_s, MSG_INFO, "MGMT-TX-STATUS freq=%u dst=" MACSTR
" src=" MACSTR " bssid=" MACSTR " result=%s",
freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
result == OFFCHANNEL_SEND_ACTION_SUCCESS ?
"SUCCESS" : (result == OFFCHANNEL_SEND_ACTION_NO_ACK ?
"NO_ACK" : "FAILED"));
}
static int wpas_ctrl_iface_mgmt_tx(struct wpa_supplicant *wpa_s, char *cmd)
{
char *pos, *param;
size_t len;
u8 *buf, da[ETH_ALEN], bssid[ETH_ALEN];
int res, used;
int freq = 0, no_cck = 0, wait_time = 0;
/* <DA> <BSSID> [freq=<MHz>] [wait_time=<ms>] [no_cck=1]
* <action=Action frame payload> */
wpa_printf(MSG_DEBUG, "External MGMT TX: %s", cmd);
pos = cmd;
used = hwaddr_aton2(pos, da);
if (used < 0)
return -1;
pos += used;
while (*pos == ' ')
pos++;
used = hwaddr_aton2(pos, bssid);
if (used < 0)
return -1;
pos += used;
param = os_strstr(pos, " freq=");
if (param) {
param += 6;
freq = atoi(param);
}
param = os_strstr(pos, " no_cck=");
if (param) {
param += 8;
no_cck = atoi(param);
}
param = os_strstr(pos, " wait_time=");
if (param) {
param += 11;
wait_time = atoi(param);
}
param = os_strstr(pos, " action=");
if (param == NULL)
return -1;
param += 8;
len = os_strlen(param);
if (len & 1)
return -1;
len /= 2;
buf = os_malloc(len);
if (buf == NULL)
return -1;
if (hexstr2bin(param, buf, len) < 0) {
os_free(buf);
return -1;
}
res = offchannel_send_action(wpa_s, freq, da, wpa_s->own_addr, bssid,
buf, len, wait_time,
wpas_ctrl_iface_mgmt_tx_cb, no_cck);
os_free(buf);
return res;
}
static void wpas_ctrl_iface_mgmt_tx_done(struct wpa_supplicant *wpa_s)
{
wpa_printf(MSG_DEBUG, "External MGMT TX - done waiting");
offchannel_send_action_done(wpa_s);
}
static int wpas_ctrl_iface_mgmt_rx_process(struct wpa_supplicant *wpa_s,
char *cmd)
{
char *pos, *param;
size_t len;
u8 *buf;
int freq = 0, datarate = 0, ssi_signal = 0;
union wpa_event_data event;
if (!wpa_s->ext_mgmt_frame_handling)
return -1;
/* freq=<MHz> datarate=<val> ssi_signal=<val> frame=<frame hexdump> */
wpa_printf(MSG_DEBUG, "External MGMT RX process: %s", cmd);
pos = cmd;
param = os_strstr(pos, "freq=");
if (param) {
param += 5;
freq = atoi(param);
}
param = os_strstr(pos, " datarate=");
if (param) {
param += 10;
datarate = atoi(param);
}
param = os_strstr(pos, " ssi_signal=");
if (param) {
param += 12;
ssi_signal = atoi(param);
}
param = os_strstr(pos, " frame=");
if (param == NULL)
return -1;
param += 7;
len = os_strlen(param);
if (len & 1)
return -1;
len /= 2;
buf = os_malloc(len);
if (buf == NULL)
return -1;
if (hexstr2bin(param, buf, len) < 0) {
os_free(buf);
return -1;
}
os_memset(&event, 0, sizeof(event));
event.rx_mgmt.freq = freq;
event.rx_mgmt.frame = buf;
event.rx_mgmt.frame_len = len;
event.rx_mgmt.ssi_signal = ssi_signal;
event.rx_mgmt.datarate = datarate;
wpa_s->ext_mgmt_frame_handling = 0;
wpa_supplicant_event(wpa_s, EVENT_RX_MGMT, &event);
wpa_s->ext_mgmt_frame_handling = 1;
os_free(buf);
return 0;
}
static int wpas_ctrl_iface_driver_scan_res(struct wpa_supplicant *wpa_s,
char *param)
{
struct wpa_scan_res *res;
struct os_reltime now;
char *pos, *end;
int ret = -1;
if (!param)
return -1;
if (os_strcmp(param, "START") == 0) {
wpa_bss_update_start(wpa_s);
return 0;
}
if (os_strcmp(param, "END") == 0) {
wpa_bss_update_end(wpa_s, NULL, 1);
return 0;
}
if (os_strncmp(param, "BSS ", 4) != 0)
return -1;
param += 3;
res = os_zalloc(sizeof(*res) + os_strlen(param) / 2);
if (!res)
return -1;
pos = os_strstr(param, " flags=");
if (pos)
res->flags = strtol(pos + 7, NULL, 16);
pos = os_strstr(param, " bssid=");
if (pos && hwaddr_aton(pos + 7, res->bssid))
goto fail;
pos = os_strstr(param, " freq=");
if (pos)
res->freq = atoi(pos + 6);
pos = os_strstr(param, " beacon_int=");
if (pos)
res->beacon_int = atoi(pos + 12);
pos = os_strstr(param, " caps=");
if (pos)
res->caps = strtol(pos + 6, NULL, 16);
pos = os_strstr(param, " qual=");
if (pos)
res->qual = atoi(pos + 6);
pos = os_strstr(param, " noise=");
if (pos)
res->noise = atoi(pos + 7);
pos = os_strstr(param, " level=");
if (pos)
res->level = atoi(pos + 7);
pos = os_strstr(param, " tsf=");
if (pos)
res->tsf = strtoll(pos + 5, NULL, 16);
pos = os_strstr(param, " age=");
if (pos)
res->age = atoi(pos + 5);
pos = os_strstr(param, " est_throughput=");
if (pos)
res->est_throughput = atoi(pos + 16);
pos = os_strstr(param, " snr=");
if (pos)
res->snr = atoi(pos + 5);
pos = os_strstr(param, " parent_tsf=");
if (pos)
res->parent_tsf = strtoll(pos + 7, NULL, 16);
pos = os_strstr(param, " tsf_bssid=");
if (pos && hwaddr_aton(pos + 11, res->tsf_bssid))
goto fail;
pos = os_strstr(param, " ie=");
if (pos) {
pos += 4;
end = os_strchr(pos, ' ');
if (!end)
end = pos + os_strlen(pos);
res->ie_len = (end - pos) / 2;
if (hexstr2bin(pos, (u8 *) (res + 1), res->ie_len))
goto fail;
}
pos = os_strstr(param, " beacon_ie=");
if (pos) {
pos += 11;
end = os_strchr(pos, ' ');
if (!end)
end = pos + os_strlen(pos);
res->beacon_ie_len = (end - pos) / 2;
if (hexstr2bin(pos, ((u8 *) (res + 1)) + res->ie_len,
res->beacon_ie_len))
goto fail;
}
os_get_reltime(&now);
wpa_bss_update_scan_res(wpa_s, res, &now);
ret = 0;
fail:
os_free(res);
return ret;
}
static int wpas_ctrl_iface_driver_event_assoc(struct wpa_supplicant *wpa_s,
char *param)
{
union wpa_event_data event;
struct assoc_info *ai;
char *ctx = NULL;
int ret = -1;
struct wpabuf *req_ies = NULL;
struct wpabuf *resp_ies = NULL;
struct wpabuf *resp_frame = NULL;
struct wpabuf *beacon_ies = NULL;
struct wpabuf *key_replay_ctr = NULL;
struct wpabuf *ptk_kck = NULL;
struct wpabuf *ptk_kek = NULL;
struct wpabuf *fils_pmk = NULL;
char *str, *pos;
u8 addr[ETH_ALEN];
u8 fils_pmkid[PMKID_LEN];
os_memset(&event, 0, sizeof(event));
ai = &event.assoc_info;
while ((str = str_token(param, " ", &ctx))) {
pos = os_strchr(str, '=');
if (!pos)
goto fail;
*pos++ = '\0';
if (os_strcmp(str, "reassoc") == 0) {
ai->reassoc = atoi(pos);
} else if (os_strcmp(str, "req_ies") == 0) {
wpabuf_free(req_ies);
req_ies = wpabuf_parse_bin(pos);
if (!req_ies)
goto fail;
ai->req_ies = wpabuf_head(req_ies);
ai->req_ies_len = wpabuf_len(req_ies);
} else if (os_strcmp(str, "resp_ies") == 0) {
wpabuf_free(resp_ies);
resp_ies = wpabuf_parse_bin(pos);
if (!resp_ies)
goto fail;
ai->resp_ies = wpabuf_head(resp_ies);
ai->resp_ies_len = wpabuf_len(resp_ies);
} else if (os_strcmp(str, "resp_frame") == 0) {
wpabuf_free(resp_frame);
resp_frame = wpabuf_parse_bin(pos);
if (!resp_frame)
goto fail;
ai->resp_frame = wpabuf_head(resp_frame);
ai->resp_frame_len = wpabuf_len(resp_frame);
} else if (os_strcmp(str, "beacon_ies") == 0) {
wpabuf_free(beacon_ies);
beacon_ies = wpabuf_parse_bin(pos);
if (!beacon_ies)
goto fail;
ai->beacon_ies = wpabuf_head(beacon_ies);
ai->beacon_ies_len = wpabuf_len(beacon_ies);
} else if (os_strcmp(str, "freq") == 0) {
ai->freq = atoi(pos);
} else if (os_strcmp(str, "wmm::info_bitmap") == 0) {
ai->wmm_params.info_bitmap = atoi(pos);
} else if (os_strcmp(str, "wmm::uapsd_queues") == 0) {
ai->wmm_params.uapsd_queues = atoi(pos);
} else if (os_strcmp(str, "addr") == 0) {
if (hwaddr_aton(pos, addr))
goto fail;
ai->addr = addr;
} else if (os_strcmp(str, "authorized") == 0) {
ai->authorized = atoi(pos);
} else if (os_strcmp(str, "key_replay_ctr") == 0) {
wpabuf_free(key_replay_ctr);
key_replay_ctr = wpabuf_parse_bin(pos);
if (!key_replay_ctr)
goto fail;
ai->key_replay_ctr = wpabuf_head(key_replay_ctr);
ai->key_replay_ctr_len = wpabuf_len(key_replay_ctr);
} else if (os_strcmp(str, "ptk_kck") == 0) {
wpabuf_free(ptk_kck);
ptk_kck = wpabuf_parse_bin(pos);
if (!ptk_kck)
goto fail;
ai->ptk_kck = wpabuf_head(ptk_kck);
ai->ptk_kck_len = wpabuf_len(ptk_kck);
} else if (os_strcmp(str, "ptk_kek") == 0) {
wpabuf_free(ptk_kek);
ptk_kek = wpabuf_parse_bin(pos);
if (!ptk_kek)
goto fail;
ai->ptk_kek = wpabuf_head(ptk_kek);
ai->ptk_kek_len = wpabuf_len(ptk_kek);
} else if (os_strcmp(str, "subnet_status") == 0) {
ai->subnet_status = atoi(pos);
} else if (os_strcmp(str, "fils_erp_next_seq_num") == 0) {
ai->fils_erp_next_seq_num = atoi(pos);
} else if (os_strcmp(str, "fils_pmk") == 0) {
wpabuf_free(fils_pmk);
fils_pmk = wpabuf_parse_bin(pos);
if (!fils_pmk)
goto fail;
ai->fils_pmk = wpabuf_head(fils_pmk);
ai->fils_pmk_len = wpabuf_len(fils_pmk);
} else if (os_strcmp(str, "fils_pmkid") == 0) {
if (hexstr2bin(pos, fils_pmkid, PMKID_LEN) < 0)
goto fail;
ai->fils_pmkid = fils_pmkid;
} else {
goto fail;
}
}
wpa_supplicant_event(wpa_s, EVENT_ASSOC, &event);
ret = 0;
fail:
wpabuf_free(req_ies);
wpabuf_free(resp_ies);
wpabuf_free(resp_frame);
wpabuf_free(beacon_ies);
wpabuf_free(key_replay_ctr);
wpabuf_free(ptk_kck);
wpabuf_free(ptk_kek);
wpabuf_free(fils_pmk);
return ret;
}
static int wpas_ctrl_iface_driver_event(struct wpa_supplicant *wpa_s, char *cmd)
{
char *pos, *param;
union wpa_event_data event;
enum wpa_event_type ev;
/* <event name> [parameters..] */
wpa_dbg(wpa_s, MSG_DEBUG, "Testing - external driver event: %s", cmd);
pos = cmd;
param = os_strchr(pos, ' ');
if (param)
*param++ = '\0';
os_memset(&event, 0, sizeof(event));
if (os_strcmp(cmd, "INTERFACE_ENABLED") == 0) {
ev = EVENT_INTERFACE_ENABLED;
} else if (os_strcmp(cmd, "INTERFACE_DISABLED") == 0) {
ev = EVENT_INTERFACE_DISABLED;
} else if (os_strcmp(cmd, "AVOID_FREQUENCIES") == 0) {
ev = EVENT_AVOID_FREQUENCIES;
if (param == NULL)
param = "";
if (freq_range_list_parse(&event.freq_range, param) < 0)
return -1;
wpa_supplicant_event(wpa_s, ev, &event);
os_free(event.freq_range.range);
return 0;
} else if (os_strcmp(cmd, "SCAN_RES") == 0) {
return wpas_ctrl_iface_driver_scan_res(wpa_s, param);
} else if (os_strcmp(cmd, "ASSOC") == 0) {
return wpas_ctrl_iface_driver_event_assoc(wpa_s, param);
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "Testing - unknown driver event: %s",
cmd);
return -1;
}
wpa_supplicant_event(wpa_s, ev, &event);
return 0;
}
static int wpas_ctrl_iface_eapol_rx(struct wpa_supplicant *wpa_s, char *cmd)
{
char *pos;
u8 src[ETH_ALEN], *buf;
int used;
size_t len;
wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd);
pos = cmd;
used = hwaddr_aton2(pos, src);
if (used < 0)
return -1;
pos += used;
while (*pos == ' ')
pos++;
len = os_strlen(pos);
if (len & 1)
return -1;
len /= 2;
buf = os_malloc(len);
if (buf == NULL)
return -1;
if (hexstr2bin(pos, buf, len) < 0) {
os_free(buf);
return -1;
}
wpa_supplicant_rx_eapol(wpa_s, src, buf, len, FRAME_ENCRYPTION_UNKNOWN);
os_free(buf);
return 0;
}
static int wpas_ctrl_iface_eapol_tx(struct wpa_supplicant *wpa_s, char *cmd)
{
char *pos;
u8 dst[ETH_ALEN], *buf;
int used, ret;
size_t len;
unsigned int prev;
wpa_printf(MSG_DEBUG, "External EAPOL TX: %s", cmd);
pos = cmd;
used = hwaddr_aton2(pos, dst);
if (used < 0)
return -1;
pos += used;
while (*pos == ' ')
pos++;
len = os_strlen(pos);
if (len & 1)
return -1;
len /= 2;
buf = os_malloc(len);
if (!buf || hexstr2bin(pos, buf, len) < 0) {
os_free(buf);
return -1;
}
prev = wpa_s->ext_eapol_frame_io;
wpa_s->ext_eapol_frame_io = 0;
ret = wpa_ether_send(wpa_s, dst, ETH_P_EAPOL, buf, len);
wpa_s->ext_eapol_frame_io = prev;
os_free(buf);
return ret;
}
static u16 ipv4_hdr_checksum(const void *buf, size_t len)
{
size_t i;
u32 sum = 0;
const u16 *pos = buf;
for (i = 0; i < len / 2; i++)
sum += *pos++;
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
return sum ^ 0xffff;
}
#define HWSIM_PACKETLEN 1500
#define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header))
static void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf,
size_t len)
{
struct wpa_supplicant *wpa_s = ctx;
const struct ether_header *eth;
struct ip ip;
const u8 *pos;
unsigned int i;
char extra[30];
if (len < sizeof(*eth) + sizeof(ip) || len > HWSIM_PACKETLEN) {
wpa_printf(MSG_DEBUG,
"test data: RX - ignore unexpected length %d",
(int) len);
return;
}
eth = (const struct ether_header *) buf;
os_memcpy(&ip, eth + 1, sizeof(ip));
pos = &buf[sizeof(*eth) + sizeof(ip)];
if (ip.ip_hl != 5 || ip.ip_v != 4 || ntohs(ip.ip_len) > HWSIM_IP_LEN) {
wpa_printf(MSG_DEBUG,
"test data: RX - ignore unexpected IP header");
return;
}
for (i = 0; i < ntohs(ip.ip_len) - sizeof(ip); i++) {
if (*pos != (u8) i) {
wpa_printf(MSG_DEBUG,
"test data: RX - ignore mismatching payload");
return;
}
pos++;
}
extra[0] = '\0';
if (ntohs(ip.ip_len) != HWSIM_IP_LEN)
os_snprintf(extra, sizeof(extra), " len=%d", ntohs(ip.ip_len));
wpa_msg(wpa_s, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR "%s",
MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost), extra);
}
static int wpas_ctrl_iface_data_test_config(struct wpa_supplicant *wpa_s,
char *cmd)
{
int enabled = atoi(cmd);
char *pos;
const char *ifname;
if (!enabled) {
if (wpa_s->l2_test) {
l2_packet_deinit(wpa_s->l2_test);
wpa_s->l2_test = NULL;
wpa_dbg(wpa_s, MSG_DEBUG, "test data: Disabled");
}
return 0;
}
if (wpa_s->l2_test)
return 0;
pos = os_strstr(cmd, " ifname=");
if (pos)
ifname = pos + 8;
else
ifname = wpa_s->ifname;
wpa_s->l2_test = l2_packet_init(ifname, wpa_s->own_addr,
ETHERTYPE_IP, wpas_data_test_rx,
wpa_s, 1);
if (wpa_s->l2_test == NULL)
return -1;
wpa_dbg(wpa_s, MSG_DEBUG, "test data: Enabled");
return 0;
}
static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd)
{
u8 dst[ETH_ALEN], src[ETH_ALEN];
char *pos, *pos2;
int used;
long int val;
u8 tos;
u8 buf[2 + HWSIM_PACKETLEN];
struct ether_header *eth;
struct ip *ip;
u8 *dpos;
unsigned int i;
size_t send_len = HWSIM_IP_LEN;
if (wpa_s->l2_test == NULL)
return -1;
/* format: <dst> <src> <tos> [len=<length>] */
pos = cmd;
used = hwaddr_aton2(pos, dst);
if (used < 0)
return -1;
pos += used;
while (*pos == ' ')
pos++;
used = hwaddr_aton2(pos, src);
if (used < 0)
return -1;
pos += used;
val = strtol(pos, &pos2, 0);
if (val < 0 || val > 0xff)
return -1;
tos = val;
pos = os_strstr(pos2, " len=");
if (pos) {
i = atoi(pos + 5);
if (i < sizeof(*ip) || i > HWSIM_IP_LEN)
return -1;
send_len = i;
}
eth = (struct ether_header *) &buf[2];
os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
os_memcpy(eth->ether_shost, src, ETH_ALEN);
eth->ether_type = htons(ETHERTYPE_IP);
ip = (struct ip *) (eth + 1);
os_memset(ip, 0, sizeof(*ip));
ip->ip_hl = 5;
ip->ip_v = 4;
ip->ip_ttl = 64;
ip->ip_tos = tos;
ip->ip_len = htons(send_len);
ip->ip_p = 1;
ip->ip_src.s_addr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1);
ip->ip_dst.s_addr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2);
ip->ip_sum = ipv4_hdr_checksum(ip, sizeof(*ip));
dpos = (u8 *) (ip + 1);
for (i = 0; i < send_len - sizeof(*ip); i++)
*dpos++ = i;
if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, &buf[2],
sizeof(struct ether_header) + send_len) < 0)
return -1;
wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX dst=" MACSTR " src=" MACSTR
" tos=0x%x", MAC2STR(dst), MAC2STR(src), tos);
return 0;
}
static int wpas_ctrl_iface_data_test_frame(struct wpa_supplicant *wpa_s,
char *cmd)
{
u8 *buf;
struct ether_header *eth;
struct l2_packet_data *l2 = NULL;
size_t len;
u16 ethertype;
int res = -1;
len = os_strlen(cmd);
if (len & 1 || len < ETH_HLEN * 2)
return -1;
len /= 2;
buf = os_malloc(len);
if (buf == NULL)
return -1;
if (hexstr2bin(cmd, buf, len) < 0)
goto done;
eth = (struct ether_header *) buf;
ethertype = ntohs(eth->ether_type);
l2 = l2_packet_init(wpa_s->ifname, wpa_s->own_addr, ethertype,
wpas_data_test_rx, wpa_s, 1);
if (l2 == NULL)
goto done;
res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len);
wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX frame res=%d", res);
done:
if (l2)
l2_packet_deinit(l2);
os_free(buf);
return res < 0 ? -1 : 0;
}
static void wpas_ctrl_event_test_cb(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
int i, count = (intptr_t) timeout_ctx;
wpa_printf(MSG_DEBUG, "TEST: Send %d control interface event messages",
count);
for (i = 0; i < count; i++) {
wpa_msg_ctrl(wpa_s, MSG_INFO, "TEST-EVENT-MESSAGE %d/%d",
i + 1, count);
}
}
static int wpas_ctrl_event_test(struct wpa_supplicant *wpa_s, const char *cmd)
{
int count;
count = atoi(cmd);
if (count <= 0)
return -1;
return eloop_register_timeout(0, 0, wpas_ctrl_event_test_cb, wpa_s,
(void *) (intptr_t) count);
}
static int wpas_get_hex_buf(const char *val, struct wpabuf **ret)
{
struct wpabuf *buf;
size_t len;
len = os_strlen(val);
if (len & 1)
return -1;
len /= 2;
if (len == 0) {
buf = NULL;
} else {
buf = wpabuf_alloc(len);
if (!buf)
return -1;
if (hexstr2bin(val, wpabuf_put(buf, len), len) < 0) {
wpabuf_free(buf);
return -1;
}
}
*ret = buf;
return 0;
}
static int wpas_ctrl_test_assoc_ie(struct wpa_supplicant *wpa_s,
const char *cmd)
{
struct wpabuf *buf;
if (wpas_get_hex_buf(cmd, &buf) < 0)
return -1;
wpa_sm_set_test_assoc_ie(wpa_s->wpa, buf);
return 0;
}
static int wpas_ctrl_test_eapol_m2_elems(struct wpa_supplicant *wpa_s,
const char *cmd)
{
struct wpabuf *buf;
if (wpas_get_hex_buf(cmd, &buf) < 0)
return -1;
wpa_sm_set_test_eapol_m2_elems(wpa_s->wpa, buf);
return 0;
}
static int wpas_ctrl_test_eapol_m4_elems(struct wpa_supplicant *wpa_s,
const char *cmd)
{
struct wpabuf *buf;
if (wpas_get_hex_buf(cmd, &buf) < 0)
return -1;
wpa_sm_set_test_eapol_m4_elems(wpa_s->wpa, buf);
return 0;
}
static int wpas_ctrl_reset_pn(struct wpa_supplicant *wpa_s)
{
u8 zero[WPA_TK_MAX_LEN];
if (wpa_s->last_tk_alg == WPA_ALG_NONE)
return -1;
wpa_printf(MSG_INFO, "TESTING: Reset PN");
os_memset(zero, 0, sizeof(zero));
/* First, use a zero key to avoid any possible duplicate key avoidance
* in the driver. */
if (wpa_drv_set_key(wpa_s, -1, wpa_s->last_tk_alg, wpa_s->last_tk_addr,
wpa_s->last_tk_key_idx, 1, zero, 6,
Introduce and add key_flag Add the new set_key() parameter "key_flag" to provide more specific description of what type of a key is being configured. This is needed to be able to add support for "Extended Key ID for Individually Addressed Frames" from IEEE Std 802.11-2016. In addition, this may be used to replace the set_tx boolean eventually once all the driver wrappers have moved to using the new key_flag. The following flag are defined: KEY_FLAG_MODIFY Set when an already installed key must be updated. So far the only use-case is changing RX/TX status of installed keys. Must not be set when deleting a key. KEY_FLAG_DEFAULT Set when the key is also a default key. Must not be set when deleting a key. (This is the replacement for set_tx.) KEY_FLAG_RX The key is valid for RX. Must not be set when deleting a key. KEY_FLAG_TX The key is valid for TX. Must not be set when deleting a key. KEY_FLAG_GROUP The key is a broadcast or group key. KEY_FLAG_PAIRWISE The key is a pairwise key. KEY_FLAG_PMK The key is a Pairwise Master Key (PMK). Predefined and needed flag combinations so far are: KEY_FLAG_GROUP_RX_TX WEP key not used as default key (yet). KEY_FLAG_GROUP_RX_TX_DEFAULT Default WEP or WPA-NONE key. KEY_FLAG_GROUP_RX GTK key valid for RX only. KEY_FLAG_GROUP_TX_DEFAULT GTK key valid for TX only, immediately taking over TX. KEY_FLAG_PAIRWISE_RX_TX Pairwise key immediately becoming the active pairwise key. KEY_FLAG_PAIRWISE_RX Pairwise key not yet valid for TX. (Only usable with Extended Key ID support.) KEY_FLAG_PAIRWISE_RX_TX_MODIFY Enable TX for a pairwise key installed with KEY_FLAG_PAIRWISE_RX. KEY_FLAG_RX_TX Not a valid standalone key type and can only used in combination with other flags to mark a key for RX/TX. This commit is not changing any functionality. It just adds the new key_flag to all hostapd/wpa_supplicant set_key() functions without using it, yet. Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
2020-01-04 23:10:04 +01:00
zero, wpa_s->last_tk_len,
KEY_FLAG_PAIRWISE_RX_TX) < 0)
return -1;
/* Set the previously configured key to reset its TSC/RSC */
return wpa_drv_set_key(wpa_s, -1, wpa_s->last_tk_alg,
wpa_s->last_tk_addr,
wpa_s->last_tk_key_idx, 1, zero, 6,
Introduce and add key_flag Add the new set_key() parameter "key_flag" to provide more specific description of what type of a key is being configured. This is needed to be able to add support for "Extended Key ID for Individually Addressed Frames" from IEEE Std 802.11-2016. In addition, this may be used to replace the set_tx boolean eventually once all the driver wrappers have moved to using the new key_flag. The following flag are defined: KEY_FLAG_MODIFY Set when an already installed key must be updated. So far the only use-case is changing RX/TX status of installed keys. Must not be set when deleting a key. KEY_FLAG_DEFAULT Set when the key is also a default key. Must not be set when deleting a key. (This is the replacement for set_tx.) KEY_FLAG_RX The key is valid for RX. Must not be set when deleting a key. KEY_FLAG_TX The key is valid for TX. Must not be set when deleting a key. KEY_FLAG_GROUP The key is a broadcast or group key. KEY_FLAG_PAIRWISE The key is a pairwise key. KEY_FLAG_PMK The key is a Pairwise Master Key (PMK). Predefined and needed flag combinations so far are: KEY_FLAG_GROUP_RX_TX WEP key not used as default key (yet). KEY_FLAG_GROUP_RX_TX_DEFAULT Default WEP or WPA-NONE key. KEY_FLAG_GROUP_RX GTK key valid for RX only. KEY_FLAG_GROUP_TX_DEFAULT GTK key valid for TX only, immediately taking over TX. KEY_FLAG_PAIRWISE_RX_TX Pairwise key immediately becoming the active pairwise key. KEY_FLAG_PAIRWISE_RX Pairwise key not yet valid for TX. (Only usable with Extended Key ID support.) KEY_FLAG_PAIRWISE_RX_TX_MODIFY Enable TX for a pairwise key installed with KEY_FLAG_PAIRWISE_RX. KEY_FLAG_RX_TX Not a valid standalone key type and can only used in combination with other flags to mark a key for RX/TX. This commit is not changing any functionality. It just adds the new key_flag to all hostapd/wpa_supplicant set_key() functions without using it, yet. Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
2020-01-04 23:10:04 +01:00
wpa_s->last_tk, wpa_s->last_tk_len,
KEY_FLAG_PAIRWISE_RX_TX);
}
static int wpas_ctrl_key_request(struct wpa_supplicant *wpa_s, const char *cmd)
{
const char *pos = cmd;
int error, pairwise;
error = atoi(pos);
pos = os_strchr(pos, ' ');
if (!pos)
return -1;
pairwise = atoi(pos);
wpa_sm_key_request(wpa_s->wpa, error, pairwise);
return 0;
}
static int wpas_ctrl_resend_assoc(struct wpa_supplicant *wpa_s)
{
#ifdef CONFIG_SME
struct wpa_driver_associate_params params;
int ret;
os_memset(&params, 0, sizeof(params));
params.bssid = wpa_s->bssid;
params.ssid = wpa_s->sme.ssid;
params.ssid_len = wpa_s->sme.ssid_len;
params.freq.freq = wpa_s->sme.freq;
if (wpa_s->last_assoc_req_wpa_ie) {
params.wpa_ie = wpabuf_head(wpa_s->last_assoc_req_wpa_ie);
params.wpa_ie_len = wpabuf_len(wpa_s->last_assoc_req_wpa_ie);
}
params.pairwise_suite = wpa_s->pairwise_cipher;
params.group_suite = wpa_s->group_cipher;
params.mgmt_group_suite = wpa_s->mgmt_group_cipher;
params.key_mgmt_suite = wpa_s->key_mgmt;
params.wpa_proto = wpa_s->wpa_proto;
params.mgmt_frame_protection = wpa_s->sme.mfp;
params.rrm_used = wpa_s->rrm.rrm_used;
if (wpa_s->sme.prev_bssid_set)
params.prev_bssid = wpa_s->sme.prev_bssid;
wpa_printf(MSG_INFO, "TESTING: Resend association request");
ret = wpa_drv_associate(wpa_s, &params);
wpa_s->testing_resend_assoc = 1;
return ret;
#else /* CONFIG_SME */
return -1;
#endif /* CONFIG_SME */
}
static int wpas_ctrl_iface_send_twt_setup(struct wpa_supplicant *wpa_s,
const char *cmd)
{
u8 dtok = 1;
int exponent = 10;
int mantissa = 8192;
u8 min_twt = 255;
unsigned long long twt = 0;
bool requestor = true;
int setup_cmd = 0;
bool trigger = true;
bool implicit = true;
bool flow_type = true;
int flow_id = 0;
bool protection = false;
u8 twt_channel = 0;
u8 control = BIT(4); /* Control field (IEEE Std 802.11ax-2021,
* Figure 9-687 - Control field format):
* B4 = TWT Information Frame Disabled */
const char *tok_s;
tok_s = os_strstr(cmd, " dialog=");
if (tok_s)
dtok = atoi(tok_s + os_strlen(" dialog="));
tok_s = os_strstr(cmd, " exponent=");
if (tok_s)
exponent = atoi(tok_s + os_strlen(" exponent="));
tok_s = os_strstr(cmd, " mantissa=");
if (tok_s)
mantissa = atoi(tok_s + os_strlen(" mantissa="));
tok_s = os_strstr(cmd, " min_twt=");
if (tok_s)
min_twt = atoi(tok_s + os_strlen(" min_twt="));
tok_s = os_strstr(cmd, " setup_cmd=");
if (tok_s)
setup_cmd = atoi(tok_s + os_strlen(" setup_cmd="));
tok_s = os_strstr(cmd, " twt=");
if (tok_s &&
sscanf(tok_s + os_strlen(" twt="), "%llu", &twt) != 1)
return -1;
tok_s = os_strstr(cmd, " requestor=");
if (tok_s)
requestor = atoi(tok_s + os_strlen(" requestor="));
tok_s = os_strstr(cmd, " trigger=");
if (tok_s)
trigger = atoi(tok_s + os_strlen(" trigger="));
tok_s = os_strstr(cmd, " implicit=");
if (tok_s)
implicit = atoi(tok_s + os_strlen(" implicit="));
tok_s = os_strstr(cmd, " flow_type=");
if (tok_s)
flow_type = atoi(tok_s + os_strlen(" flow_type="));
tok_s = os_strstr(cmd, " flow_id=");
if (tok_s)
flow_id = atoi(tok_s + os_strlen(" flow_id="));
tok_s = os_strstr(cmd, " protection=");
if (tok_s)
protection = atoi(tok_s + os_strlen(" protection="));
tok_s = os_strstr(cmd, " twt_channel=");
if (tok_s)
twt_channel = atoi(tok_s + os_strlen(" twt_channel="));
tok_s = os_strstr(cmd, " control=");
if (tok_s)
control = atoi(tok_s + os_strlen(" control="));
return wpas_twt_send_setup(wpa_s, dtok, exponent, mantissa, min_twt,
setup_cmd, twt, requestor, trigger, implicit,
flow_type, flow_id, protection, twt_channel,
control);
}
static int wpas_ctrl_iface_send_twt_teardown(struct wpa_supplicant *wpa_s,
const char *cmd)
{
u8 flags = 0x1;
const char *tok_s;
tok_s = os_strstr(cmd, " flags=");
if (tok_s)
flags = atoi(tok_s + os_strlen(" flags="));
return wpas_twt_send_teardown(wpa_s, flags);
}
#endif /* CONFIG_TESTING_OPTIONS */
static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd)
{
char *pos = cmd;
int frame;
size_t len;
struct wpabuf *buf;
struct ieee802_11_elems elems;
frame = atoi(pos);
if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
return -1;
wpa_s = wpas_vendor_elem(wpa_s, frame);
pos = os_strchr(pos, ' ');
if (pos == NULL)
return -1;
pos++;
len = os_strlen(pos);
if (len == 0)
return 0;
if (len & 1)
return -1;
len /= 2;
buf = wpabuf_alloc(len);
if (buf == NULL)
return -1;
if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) {
wpabuf_free(buf);
return -1;
}
if (ieee802_11_parse_elems(wpabuf_head_u8(buf), len, &elems, 0) ==
ParseFailed) {
wpabuf_free(buf);
return -1;
}
if (wpa_s->vendor_elem[frame] == NULL) {
wpa_s->vendor_elem[frame] = buf;
goto update_ies;
}
if (wpabuf_resize(&wpa_s->vendor_elem[frame], len) < 0) {
wpabuf_free(buf);
return -1;
}
wpabuf_put_buf(wpa_s->vendor_elem[frame], buf);
wpabuf_free(buf);
update_ies:
wpas_vendor_elem_update(wpa_s);
if (frame == VENDOR_ELEM_PROBE_REQ ||
frame == VENDOR_ELEM_PROBE_REQ_P2P)
wpa_supplicant_set_default_scan_ies(wpa_s);
return 0;
}
static int wpas_ctrl_vendor_elem_get(struct wpa_supplicant *wpa_s, char *cmd,
char *buf, size_t buflen)
{
int frame = atoi(cmd);
if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
return -1;
wpa_s = wpas_vendor_elem(wpa_s, frame);
if (wpa_s->vendor_elem[frame] == NULL)
return 0;
return wpa_snprintf_hex(buf, buflen,
wpabuf_head_u8(wpa_s->vendor_elem[frame]),
wpabuf_len(wpa_s->vendor_elem[frame]));
}
static int wpas_ctrl_vendor_elem_remove(struct wpa_supplicant *wpa_s, char *cmd)
{
char *pos = cmd;
int frame;
size_t len;
u8 *buf;
struct ieee802_11_elems elems;
int res;
frame = atoi(pos);
if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
return -1;
wpa_s = wpas_vendor_elem(wpa_s, frame);
pos = os_strchr(pos, ' ');
if (pos == NULL)
return -1;
pos++;
if (*pos == '*') {
wpabuf_free(wpa_s->vendor_elem[frame]);
wpa_s->vendor_elem[frame] = NULL;
wpas_vendor_elem_update(wpa_s);
return 0;
}
if (wpa_s->vendor_elem[frame] == NULL)
return -1;
len = os_strlen(pos);
if (len == 0)
return 0;
if (len & 1)
return -1;
len /= 2;
buf = os_malloc(len);
if (buf == NULL)
return -1;
if (hexstr2bin(pos, buf, len) < 0) {
os_free(buf);
return -1;
}
if (ieee802_11_parse_elems(buf, len, &elems, 0) == ParseFailed) {
os_free(buf);
return -1;
}
res = wpas_vendor_elem_remove(wpa_s, frame, buf, len);
os_free(buf);
return res;
}
#ifndef CONFIG_NO_RRM
static void wpas_ctrl_neighbor_rep_cb(void *ctx, struct wpabuf *neighbor_rep)
{
struct wpa_supplicant *wpa_s = ctx;
size_t len;
const u8 *data;
/*
* Neighbor Report element (IEEE P802.11-REVmc/D5.0)
* BSSID[6]
* BSSID Information[4]
* Operating Class[1]
* Channel Number[1]
* PHY Type[1]
* Optional Subelements[variable]
*/
#define NR_IE_MIN_LEN (ETH_ALEN + 4 + 1 + 1 + 1)
if (!neighbor_rep || wpabuf_len(neighbor_rep) == 0) {
wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_FAILED);
goto out;
}
data = wpabuf_head_u8(neighbor_rep);
len = wpabuf_len(neighbor_rep);
while (len >= 2 + NR_IE_MIN_LEN) {
const u8 *nr;
char lci[256 * 2 + 1];
char civic[256 * 2 + 1];
u8 nr_len = data[1];
const u8 *pos = data, *end;
if (pos[0] != WLAN_EID_NEIGHBOR_REPORT ||
nr_len < NR_IE_MIN_LEN) {
wpa_dbg(wpa_s, MSG_DEBUG,
"CTRL: Invalid Neighbor Report element: id=%u len=%u",
data[0], nr_len);
goto out;
}
if (2U + nr_len > len) {
wpa_dbg(wpa_s, MSG_DEBUG,
"CTRL: Invalid Neighbor Report element: id=%u len=%zu nr_len=%u",
data[0], len, nr_len);
goto out;
}
pos += 2;
end = pos + nr_len;
nr = pos;
pos += NR_IE_MIN_LEN;
lci[0] = '\0';
civic[0] = '\0';
while (end - pos > 2) {
u8 s_id, s_len;
s_id = *pos++;
s_len = *pos++;
if (s_len > end - pos)
goto out;
if (s_id == WLAN_EID_MEASURE_REPORT && s_len > 3) {
/* Measurement Token[1] */
/* Measurement Report Mode[1] */
/* Measurement Type[1] */
/* Measurement Report[variable] */
switch (pos[2]) {
case MEASURE_TYPE_LCI:
if (lci[0])
break;
wpa_snprintf_hex(lci, sizeof(lci),
pos, s_len);
break;
case MEASURE_TYPE_LOCATION_CIVIC:
if (civic[0])
break;
wpa_snprintf_hex(civic, sizeof(civic),
pos, s_len);
break;
}
}
pos += s_len;
}
wpa_msg(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_RXED
"bssid=" MACSTR
" info=0x%x op_class=%u chan=%u phy_type=%u%s%s%s%s",
MAC2STR(nr), WPA_GET_LE32(nr + ETH_ALEN),
nr[ETH_ALEN + 4], nr[ETH_ALEN + 5],
nr[ETH_ALEN + 6],
lci[0] ? " lci=" : "", lci,
civic[0] ? " civic=" : "", civic);
data = end;
len -= 2 + nr_len;
}
out:
wpabuf_free(neighbor_rep);
}
static int wpas_ctrl_iface_send_neighbor_rep(struct wpa_supplicant *wpa_s,
char *cmd)
{
struct wpa_ssid_value ssid, *ssid_p = NULL;
int ret, lci = 0, civic = 0;
char *ssid_s;
ssid_s = os_strstr(cmd, "ssid=");
if (ssid_s) {
if (ssid_parse(ssid_s + 5, &ssid)) {
wpa_msg(wpa_s, MSG_INFO,
"CTRL: Send Neighbor Report: bad SSID");
return -1;
}
ssid_p = &ssid;
/*
* Move cmd after the SSID text that may include "lci" or
* "civic".
*/
cmd = os_strchr(ssid_s + 6, ssid_s[5] == '"' ? '"' : ' ');
if (cmd)
cmd++;
}
if (cmd && os_strstr(cmd, "lci"))
lci = 1;
if (cmd && os_strstr(cmd, "civic"))
civic = 1;
ret = wpas_rrm_send_neighbor_rep_request(wpa_s, ssid_p, lci, civic,
wpas_ctrl_neighbor_rep_cb,
wpa_s);
return ret;
}
#endif /* CONFIG_NO_RRM */
static int wpas_ctrl_iface_erp_flush(struct wpa_supplicant *wpa_s)
{
eapol_sm_erp_flush(wpa_s->eapol);
return 0;
}
static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s,
char *cmd)
{
char *token, *context = NULL;
unsigned int enable = ~0, type = 0;
u8 _addr[ETH_ALEN], _mask[ETH_ALEN];
u8 *addr = NULL, *mask = NULL;
while ((token = str_token(cmd, " ", &context))) {
if (os_strcasecmp(token, "scan") == 0) {
type |= MAC_ADDR_RAND_SCAN;
} else if (os_strcasecmp(token, "sched") == 0) {
type |= MAC_ADDR_RAND_SCHED_SCAN;
} else if (os_strcasecmp(token, "pno") == 0) {
type |= MAC_ADDR_RAND_PNO;
} else if (os_strcasecmp(token, "all") == 0) {
type = wpa_s->mac_addr_rand_supported;
} else if (os_strncasecmp(token, "enable=", 7) == 0) {
enable = atoi(token + 7);
} else if (os_strncasecmp(token, "addr=", 5) == 0) {
addr = _addr;
if (hwaddr_aton(token + 5, addr)) {
wpa_printf(MSG_INFO,
"CTRL: Invalid MAC address: %s",
token);
return -1;
}
} else if (os_strncasecmp(token, "mask=", 5) == 0) {
mask = _mask;
if (hwaddr_aton(token + 5, mask)) {
wpa_printf(MSG_INFO,
"CTRL: Invalid MAC address mask: %s",
token);
return -1;
}
} else {
wpa_printf(MSG_INFO,
"CTRL: Invalid MAC_RAND_SCAN parameter: %s",
token);
return -1;
}
}
if (!type) {
wpa_printf(MSG_INFO, "CTRL: MAC_RAND_SCAN no type specified");
return -1;
}
if (enable > 1) {
wpa_printf(MSG_INFO,
"CTRL: MAC_RAND_SCAN enable=<0/1> not specified");
return -1;
}
if (!enable)
return wpas_disable_mac_addr_randomization(wpa_s, type);
return wpas_enable_mac_addr_randomization(wpa_s, type, addr, mask);
}
static int wpas_ctrl_iface_pmksa(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen)
{
size_t reply_len;
reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, buf, buflen);
#ifdef CONFIG_AP
reply_len += wpas_ap_pmksa_cache_list(wpa_s, &buf[reply_len],
buflen - reply_len);
#endif /* CONFIG_AP */
return reply_len;
}
static void wpas_ctrl_iface_pmksa_flush(struct wpa_supplicant *wpa_s)
{
ptksa_cache_flush(wpa_s->ptksa, NULL, WPA_CIPHER_NONE);
wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
#ifdef CONFIG_AP
wpas_ap_pmksa_cache_flush(wpa_s);
#endif /* CONFIG_AP */
}
External persistent storage for PMKSA cache entries This adds new wpa_supplicant control interface commands PMKSA_GET and PMKSA_ADD that can be used to store PMKSA cache entries in an external persistent storage when terminating a wpa_supplicant process and then restore those entries when starting a new process. The previously added PMKSA-CACHE-ADDED/REMOVED events can be used to help in synchronizing the external storage with the memory-only volatile storage within wpa_supplicant. "PMKSA_GET <network_id>" fetches all stored PMKSA cache entries bound to a specific network profile. The network_id of the current profile is available with the STATUS command (id=<network_id). In addition, the network_id is included in the PMKSA-CACHE-ADDED/REMOVED events. The output of the PMKSA_GET command uses the following format: <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> For example: 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30240 43200 1 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30240 43200 1 0 The PMKSA_GET command uses the following format: <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> (i.e., "PMKSA_ADD <network_id> " prefix followed by a line of PMKSA_GET output data; however, the reauth_time and expiration values need to be updated by decrementing them by number of seconds between the PMKSA_GET and PMKSA_ADD commands) For example: PMKSA_ADD 0 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30140 43100 1 0 PMKSA_ADD 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30140 43100 1 0 This functionality is disabled be default and can be enabled with CONFIG_PMKSA_CACHE_EXTERNAL=y build configuration option. It should be noted that this allows any process that has access to the wpa_supplicant control interface to use PMKSA_ADD command to fetch keying material (PMK), so this is for environments in which the control interface access is restricted. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
2016-12-12 22:47:04 +01:00
#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
static int wpas_ctrl_iface_pmksa_get(struct wpa_supplicant *wpa_s,
const char *cmd, char *buf, size_t buflen)
{
struct rsn_pmksa_cache_entry *entry;
struct wpa_ssid *ssid;
char *pos, *pos2, *end;
int ret;
struct os_reltime now;
ssid = wpa_config_get_network(wpa_s->conf, atoi(cmd));
if (!ssid)
return -1;
pos = buf;
end = buf + buflen;
os_get_reltime(&now);
/*
* Entry format:
* <BSSID> <PMKID> <PMK> <reauth_time in seconds>
* <expiration in seconds> <akmp> <opportunistic>
* [FILS Cache Identifier]
External persistent storage for PMKSA cache entries This adds new wpa_supplicant control interface commands PMKSA_GET and PMKSA_ADD that can be used to store PMKSA cache entries in an external persistent storage when terminating a wpa_supplicant process and then restore those entries when starting a new process. The previously added PMKSA-CACHE-ADDED/REMOVED events can be used to help in synchronizing the external storage with the memory-only volatile storage within wpa_supplicant. "PMKSA_GET <network_id>" fetches all stored PMKSA cache entries bound to a specific network profile. The network_id of the current profile is available with the STATUS command (id=<network_id). In addition, the network_id is included in the PMKSA-CACHE-ADDED/REMOVED events. The output of the PMKSA_GET command uses the following format: <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> For example: 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30240 43200 1 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30240 43200 1 0 The PMKSA_GET command uses the following format: <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> (i.e., "PMKSA_ADD <network_id> " prefix followed by a line of PMKSA_GET output data; however, the reauth_time and expiration values need to be updated by decrementing them by number of seconds between the PMKSA_GET and PMKSA_ADD commands) For example: PMKSA_ADD 0 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30140 43100 1 0 PMKSA_ADD 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30140 43100 1 0 This functionality is disabled be default and can be enabled with CONFIG_PMKSA_CACHE_EXTERNAL=y build configuration option. It should be noted that this allows any process that has access to the wpa_supplicant control interface to use PMKSA_ADD command to fetch keying material (PMK), so this is for environments in which the control interface access is restricted. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
2016-12-12 22:47:04 +01:00
*/
for (entry = wpa_sm_pmksa_cache_head(wpa_s->wpa); entry;
entry = entry->next) {
if (entry->network_ctx != ssid)
continue;
pos2 = pos;
ret = os_snprintf(pos2, end - pos2, MACSTR " ",
MAC2STR(entry->aa));
if (os_snprintf_error(end - pos2, ret))
break;
pos2 += ret;
pos2 += wpa_snprintf_hex(pos2, end - pos2, entry->pmkid,
PMKID_LEN);
ret = os_snprintf(pos2, end - pos2, " ");
if (os_snprintf_error(end - pos2, ret))
break;
pos2 += ret;
pos2 += wpa_snprintf_hex(pos2, end - pos2, entry->pmk,
entry->pmk_len);
ret = os_snprintf(pos2, end - pos2, " %d %d %d %d",
(int) (entry->reauth_time - now.sec),
(int) (entry->expiration - now.sec),
entry->akmp,
entry->opportunistic);
if (os_snprintf_error(end - pos2, ret))
break;
pos2 += ret;
if (entry->fils_cache_id_set) {
ret = os_snprintf(pos2, end - pos2, " %02x%02x",
entry->fils_cache_id[0],
entry->fils_cache_id[1]);
if (os_snprintf_error(end - pos2, ret))
break;
pos2 += ret;
}
External persistent storage for PMKSA cache entries This adds new wpa_supplicant control interface commands PMKSA_GET and PMKSA_ADD that can be used to store PMKSA cache entries in an external persistent storage when terminating a wpa_supplicant process and then restore those entries when starting a new process. The previously added PMKSA-CACHE-ADDED/REMOVED events can be used to help in synchronizing the external storage with the memory-only volatile storage within wpa_supplicant. "PMKSA_GET <network_id>" fetches all stored PMKSA cache entries bound to a specific network profile. The network_id of the current profile is available with the STATUS command (id=<network_id). In addition, the network_id is included in the PMKSA-CACHE-ADDED/REMOVED events. The output of the PMKSA_GET command uses the following format: <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> For example: 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30240 43200 1 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30240 43200 1 0 The PMKSA_GET command uses the following format: <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> (i.e., "PMKSA_ADD <network_id> " prefix followed by a line of PMKSA_GET output data; however, the reauth_time and expiration values need to be updated by decrementing them by number of seconds between the PMKSA_GET and PMKSA_ADD commands) For example: PMKSA_ADD 0 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30140 43100 1 0 PMKSA_ADD 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30140 43100 1 0 This functionality is disabled be default and can be enabled with CONFIG_PMKSA_CACHE_EXTERNAL=y build configuration option. It should be noted that this allows any process that has access to the wpa_supplicant control interface to use PMKSA_ADD command to fetch keying material (PMK), so this is for environments in which the control interface access is restricted. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
2016-12-12 22:47:04 +01:00
ret = os_snprintf(pos2, end - pos2, "\n");
if (os_snprintf_error(end - pos2, ret))
break;
pos2 += ret;
pos = pos2;
}
return pos - buf;
}
static int wpas_ctrl_iface_pmksa_add(struct wpa_supplicant *wpa_s,
char *cmd)
{
struct rsn_pmksa_cache_entry *entry;
struct wpa_ssid *ssid;
char *pos, *pos2;
int ret = -1;
struct os_reltime now;
int reauth_time = 0, expiration = 0, i;
External persistent storage for PMKSA cache entries This adds new wpa_supplicant control interface commands PMKSA_GET and PMKSA_ADD that can be used to store PMKSA cache entries in an external persistent storage when terminating a wpa_supplicant process and then restore those entries when starting a new process. The previously added PMKSA-CACHE-ADDED/REMOVED events can be used to help in synchronizing the external storage with the memory-only volatile storage within wpa_supplicant. "PMKSA_GET <network_id>" fetches all stored PMKSA cache entries bound to a specific network profile. The network_id of the current profile is available with the STATUS command (id=<network_id). In addition, the network_id is included in the PMKSA-CACHE-ADDED/REMOVED events. The output of the PMKSA_GET command uses the following format: <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> For example: 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30240 43200 1 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30240 43200 1 0 The PMKSA_GET command uses the following format: <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> (i.e., "PMKSA_ADD <network_id> " prefix followed by a line of PMKSA_GET output data; however, the reauth_time and expiration values need to be updated by decrementing them by number of seconds between the PMKSA_GET and PMKSA_ADD commands) For example: PMKSA_ADD 0 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30140 43100 1 0 PMKSA_ADD 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30140 43100 1 0 This functionality is disabled be default and can be enabled with CONFIG_PMKSA_CACHE_EXTERNAL=y build configuration option. It should be noted that this allows any process that has access to the wpa_supplicant control interface to use PMKSA_ADD command to fetch keying material (PMK), so this is for environments in which the control interface access is restricted. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
2016-12-12 22:47:04 +01:00
/*
* Entry format:
* <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds>
* <expiration in seconds> <akmp> <opportunistic>
* [FILS Cache Identifier]
External persistent storage for PMKSA cache entries This adds new wpa_supplicant control interface commands PMKSA_GET and PMKSA_ADD that can be used to store PMKSA cache entries in an external persistent storage when terminating a wpa_supplicant process and then restore those entries when starting a new process. The previously added PMKSA-CACHE-ADDED/REMOVED events can be used to help in synchronizing the external storage with the memory-only volatile storage within wpa_supplicant. "PMKSA_GET <network_id>" fetches all stored PMKSA cache entries bound to a specific network profile. The network_id of the current profile is available with the STATUS command (id=<network_id). In addition, the network_id is included in the PMKSA-CACHE-ADDED/REMOVED events. The output of the PMKSA_GET command uses the following format: <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> For example: 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30240 43200 1 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30240 43200 1 0 The PMKSA_GET command uses the following format: <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> (i.e., "PMKSA_ADD <network_id> " prefix followed by a line of PMKSA_GET output data; however, the reauth_time and expiration values need to be updated by decrementing them by number of seconds between the PMKSA_GET and PMKSA_ADD commands) For example: PMKSA_ADD 0 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30140 43100 1 0 PMKSA_ADD 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30140 43100 1 0 This functionality is disabled be default and can be enabled with CONFIG_PMKSA_CACHE_EXTERNAL=y build configuration option. It should be noted that this allows any process that has access to the wpa_supplicant control interface to use PMKSA_ADD command to fetch keying material (PMK), so this is for environments in which the control interface access is restricted. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
2016-12-12 22:47:04 +01:00
*/
ssid = wpa_config_get_network(wpa_s->conf, atoi(cmd));
if (!ssid)
return -1;
pos = os_strchr(cmd, ' ');
if (!pos)
return -1;
pos++;
entry = os_zalloc(sizeof(*entry));
if (!entry)
return -1;
if (hwaddr_aton(pos, entry->aa))
goto fail;
pos = os_strchr(pos, ' ');
if (!pos)
goto fail;
pos++;
if (hexstr2bin(pos, entry->pmkid, PMKID_LEN) < 0)
goto fail;
pos = os_strchr(pos, ' ');
if (!pos)
goto fail;
pos++;
pos2 = os_strchr(pos, ' ');
if (!pos2)
goto fail;
entry->pmk_len = (pos2 - pos) / 2;
if (entry->pmk_len < PMK_LEN || entry->pmk_len > PMK_LEN_MAX ||
hexstr2bin(pos, entry->pmk, entry->pmk_len) < 0)
goto fail;
pos = os_strchr(pos, ' ');
if (!pos)
goto fail;
pos++;
if (sscanf(pos, "%d %d %d %d", &reauth_time, &expiration,
&entry->akmp, &entry->opportunistic) != 4)
goto fail;
if (reauth_time > expiration)
goto fail;
for (i = 0; i < 4; i++) {
pos = os_strchr(pos, ' ');
if (!pos) {
if (i < 3)
goto fail;
break;
}
pos++;
}
if (pos) {
if (hexstr2bin(pos, entry->fils_cache_id,
FILS_CACHE_ID_LEN) < 0)
goto fail;
entry->fils_cache_id_set = 1;
}
External persistent storage for PMKSA cache entries This adds new wpa_supplicant control interface commands PMKSA_GET and PMKSA_ADD that can be used to store PMKSA cache entries in an external persistent storage when terminating a wpa_supplicant process and then restore those entries when starting a new process. The previously added PMKSA-CACHE-ADDED/REMOVED events can be used to help in synchronizing the external storage with the memory-only volatile storage within wpa_supplicant. "PMKSA_GET <network_id>" fetches all stored PMKSA cache entries bound to a specific network profile. The network_id of the current profile is available with the STATUS command (id=<network_id). In addition, the network_id is included in the PMKSA-CACHE-ADDED/REMOVED events. The output of the PMKSA_GET command uses the following format: <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> For example: 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30240 43200 1 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30240 43200 1 0 The PMKSA_GET command uses the following format: <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> (i.e., "PMKSA_ADD <network_id> " prefix followed by a line of PMKSA_GET output data; however, the reauth_time and expiration values need to be updated by decrementing them by number of seconds between the PMKSA_GET and PMKSA_ADD commands) For example: PMKSA_ADD 0 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30140 43100 1 0 PMKSA_ADD 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30140 43100 1 0 This functionality is disabled be default and can be enabled with CONFIG_PMKSA_CACHE_EXTERNAL=y build configuration option. It should be noted that this allows any process that has access to the wpa_supplicant control interface to use PMKSA_ADD command to fetch keying material (PMK), so this is for environments in which the control interface access is restricted. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
2016-12-12 22:47:04 +01:00
os_get_reltime(&now);
entry->expiration = now.sec + expiration;
entry->reauth_time = now.sec + reauth_time;
entry->network_ctx = ssid;
os_memcpy(entry->spa, wpa_s->own_addr, ETH_ALEN);
External persistent storage for PMKSA cache entries This adds new wpa_supplicant control interface commands PMKSA_GET and PMKSA_ADD that can be used to store PMKSA cache entries in an external persistent storage when terminating a wpa_supplicant process and then restore those entries when starting a new process. The previously added PMKSA-CACHE-ADDED/REMOVED events can be used to help in synchronizing the external storage with the memory-only volatile storage within wpa_supplicant. "PMKSA_GET <network_id>" fetches all stored PMKSA cache entries bound to a specific network profile. The network_id of the current profile is available with the STATUS command (id=<network_id). In addition, the network_id is included in the PMKSA-CACHE-ADDED/REMOVED events. The output of the PMKSA_GET command uses the following format: <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> For example: 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30240 43200 1 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30240 43200 1 0 The PMKSA_GET command uses the following format: <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> (i.e., "PMKSA_ADD <network_id> " prefix followed by a line of PMKSA_GET output data; however, the reauth_time and expiration values need to be updated by decrementing them by number of seconds between the PMKSA_GET and PMKSA_ADD commands) For example: PMKSA_ADD 0 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30140 43100 1 0 PMKSA_ADD 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30140 43100 1 0 This functionality is disabled be default and can be enabled with CONFIG_PMKSA_CACHE_EXTERNAL=y build configuration option. It should be noted that this allows any process that has access to the wpa_supplicant control interface to use PMKSA_ADD command to fetch keying material (PMK), so this is for environments in which the control interface access is restricted. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
2016-12-12 22:47:04 +01:00
entry->external = true;
External persistent storage for PMKSA cache entries This adds new wpa_supplicant control interface commands PMKSA_GET and PMKSA_ADD that can be used to store PMKSA cache entries in an external persistent storage when terminating a wpa_supplicant process and then restore those entries when starting a new process. The previously added PMKSA-CACHE-ADDED/REMOVED events can be used to help in synchronizing the external storage with the memory-only volatile storage within wpa_supplicant. "PMKSA_GET <network_id>" fetches all stored PMKSA cache entries bound to a specific network profile. The network_id of the current profile is available with the STATUS command (id=<network_id). In addition, the network_id is included in the PMKSA-CACHE-ADDED/REMOVED events. The output of the PMKSA_GET command uses the following format: <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> For example: 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30240 43200 1 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30240 43200 1 0 The PMKSA_GET command uses the following format: <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> (i.e., "PMKSA_ADD <network_id> " prefix followed by a line of PMKSA_GET output data; however, the reauth_time and expiration values need to be updated by decrementing them by number of seconds between the PMKSA_GET and PMKSA_ADD commands) For example: PMKSA_ADD 0 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30140 43100 1 0 PMKSA_ADD 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30140 43100 1 0 This functionality is disabled be default and can be enabled with CONFIG_PMKSA_CACHE_EXTERNAL=y build configuration option. It should be noted that this allows any process that has access to the wpa_supplicant control interface to use PMKSA_ADD command to fetch keying material (PMK), so this is for environments in which the control interface access is restricted. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
2016-12-12 22:47:04 +01:00
wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry);
entry = NULL;
ret = 0;
fail:
os_free(entry);
return ret;
}
#ifdef CONFIG_MESH
static int wpas_ctrl_iface_mesh_pmksa_get(struct wpa_supplicant *wpa_s,
const char *cmd, char *buf,
size_t buflen)
{
u8 spa[ETH_ALEN];
if (!wpa_s->ifmsh)
return -1;
if (os_strcasecmp(cmd, "any") == 0)
return wpas_ap_pmksa_cache_list_mesh(wpa_s, NULL, buf, buflen);
if (hwaddr_aton(cmd, spa))
return -1;
return wpas_ap_pmksa_cache_list_mesh(wpa_s, spa, buf, buflen);
}
static int wpas_ctrl_iface_mesh_pmksa_add(struct wpa_supplicant *wpa_s,
char *cmd)
{
/*
* We do not check mesh interface existence because PMKSA should be
* stored before wpa_s->ifmsh creation to suppress commit message
* creation.
*/
return wpas_ap_pmksa_cache_add_external(wpa_s, cmd);
}
#endif /* CONFIG_MESH */
External persistent storage for PMKSA cache entries This adds new wpa_supplicant control interface commands PMKSA_GET and PMKSA_ADD that can be used to store PMKSA cache entries in an external persistent storage when terminating a wpa_supplicant process and then restore those entries when starting a new process. The previously added PMKSA-CACHE-ADDED/REMOVED events can be used to help in synchronizing the external storage with the memory-only volatile storage within wpa_supplicant. "PMKSA_GET <network_id>" fetches all stored PMKSA cache entries bound to a specific network profile. The network_id of the current profile is available with the STATUS command (id=<network_id). In addition, the network_id is included in the PMKSA-CACHE-ADDED/REMOVED events. The output of the PMKSA_GET command uses the following format: <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> For example: 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30240 43200 1 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30240 43200 1 0 The PMKSA_GET command uses the following format: <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> (i.e., "PMKSA_ADD <network_id> " prefix followed by a line of PMKSA_GET output data; however, the reauth_time and expiration values need to be updated by decrementing them by number of seconds between the PMKSA_GET and PMKSA_ADD commands) For example: PMKSA_ADD 0 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30140 43100 1 0 PMKSA_ADD 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30140 43100 1 0 This functionality is disabled be default and can be enabled with CONFIG_PMKSA_CACHE_EXTERNAL=y build configuration option. It should be noted that this allows any process that has access to the wpa_supplicant control interface to use PMKSA_ADD command to fetch keying material (PMK), so this is for environments in which the control interface access is restricted. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
2016-12-12 22:47:04 +01:00
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
#ifdef CONFIG_FILS
static int wpas_ctrl_iface_fils_hlp_req_add(struct wpa_supplicant *wpa_s,
const char *cmd)
{
struct fils_hlp_req *req;
const char *pos;
/* format: <dst> <packet starting from ethertype> */
req = os_zalloc(sizeof(*req));
if (!req)
return -1;
if (hwaddr_aton(cmd, req->dst))
goto fail;
pos = os_strchr(cmd, ' ');
if (!pos)
goto fail;
pos++;
req->pkt = wpabuf_parse_bin(pos);
if (!req->pkt)
goto fail;
dl_list_add_tail(&wpa_s->fils_hlp_req, &req->list);
return 0;
fail:
wpabuf_free(req->pkt);
os_free(req);
return -1;
}
#endif /* CONFIG_FILS */
int wpas_ctrl_cmd_debug_level(const char *cmd)
{
if (os_strcmp(cmd, "PING") == 0 ||
os_strncmp(cmd, "BSS ", 4) == 0 ||
os_strncmp(cmd, "GET_NETWORK ", 12) == 0 ||
os_strncmp(cmd, "STATUS", 6) == 0 ||
os_strncmp(cmd, "STA ", 4) == 0 ||
os_strncmp(cmd, "STA-", 4) == 0)
return MSG_EXCESSIVE;
return MSG_DEBUG;
}
#ifndef CONFIG_NO_ROBUST_AV
static int wpas_ctrl_iface_configure_mscs(struct wpa_supplicant *wpa_s,
const char *cmd)
{
size_t frame_classifier_len;
const char *pos, *end;
struct robust_av_data *robust_av = &wpa_s->robust_av;
int val;
/*
* format:
* <add|remove|change> [up_bitmap=<hex byte>] [up_limit=<integer>]
* [stream_timeout=<in TUs>] [frame_classifier=<hex bytes>]
*/
os_memset(robust_av, 0, sizeof(struct robust_av_data));
if (os_strncmp(cmd, "add ", 4) == 0) {
robust_av->request_type = SCS_REQ_ADD;
} else if (os_strcmp(cmd, "remove") == 0) {
robust_av->request_type = SCS_REQ_REMOVE;
robust_av->valid_config = false;
return wpas_send_mscs_req(wpa_s);
} else if (os_strncmp(cmd, "change ", 7) == 0) {
robust_av->request_type = SCS_REQ_CHANGE;
} else {
return -1;
}
pos = os_strstr(cmd, "up_bitmap=");
if (!pos)
return -1;
val = hex2byte(pos + 10);
if (val < 0)
return -1;
robust_av->up_bitmap = val;
pos = os_strstr(cmd, "up_limit=");
if (!pos)
return -1;
robust_av->up_limit = atoi(pos + 9);
pos = os_strstr(cmd, "stream_timeout=");
if (!pos)
return -1;
robust_av->stream_timeout = atoi(pos + 15);
if (robust_av->stream_timeout == 0)
return -1;
pos = os_strstr(cmd, "frame_classifier=");
if (!pos)
return -1;
pos += 17;
end = os_strchr(pos, ' ');
if (!end)
end = pos + os_strlen(pos);
frame_classifier_len = (end - pos) / 2;
if (frame_classifier_len > sizeof(robust_av->frame_classifier) ||
hexstr2bin(pos, robust_av->frame_classifier, frame_classifier_len))
return -1;
robust_av->frame_classifier_len = frame_classifier_len;
robust_av->valid_config = true;
return wpas_send_mscs_req(wpa_s);
}
#endif /* CONFIG_NO_ROBUST_AV */
#ifdef CONFIG_PASN
static int wpas_ctrl_iface_pasn_start(struct wpa_supplicant *wpa_s, char *cmd)
{
char *token, *context = NULL;
u8 bssid[ETH_ALEN];
int akmp = -1, cipher = -1, got_bssid = 0;
u16 group = 0xFFFF;
u8 *comeback = NULL;
size_t comeback_len = 0;
int id = 0, ret = -1;
/*
* Entry format: bssid=<BSSID> akmp=<AKMP> cipher=<CIPHER> group=<group>
* [comeback=<hexdump>]
*/
while ((token = str_token(cmd, " ", &context))) {
if (os_strncmp(token, "bssid=", 6) == 0) {
if (hwaddr_aton(token + 6, bssid))
goto out;
got_bssid = 1;
} else if (os_strcmp(token, "akmp=PASN") == 0) {
akmp = WPA_KEY_MGMT_PASN;
#ifdef CONFIG_IEEE80211R
} else if (os_strcmp(token, "akmp=FT-PSK") == 0) {
akmp = WPA_KEY_MGMT_FT_PSK;
} else if (os_strcmp(token, "akmp=FT-EAP-SHA384") == 0) {
akmp = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
} else if (os_strcmp(token, "akmp=FT-EAP") == 0) {
akmp = WPA_KEY_MGMT_FT_IEEE8021X;
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_SAE
} else if (os_strcmp(token, "akmp=SAE") == 0) {
akmp = WPA_KEY_MGMT_SAE;
} else if (os_strcmp(token, "akmp=SAE-EXT-KEY") == 0) {
akmp = WPA_KEY_MGMT_SAE_EXT_KEY;
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
} else if (os_strcmp(token, "akmp=FILS-SHA256") == 0) {
akmp = WPA_KEY_MGMT_FILS_SHA256;
} else if (os_strcmp(token, "akmp=FILS-SHA384") == 0) {
akmp = WPA_KEY_MGMT_FILS_SHA384;
#endif /* CONFIG_FILS */
} else if (os_strcmp(token, "cipher=CCMP-256") == 0) {
cipher = WPA_CIPHER_CCMP_256;
} else if (os_strcmp(token, "cipher=GCMP-256") == 0) {
cipher = WPA_CIPHER_GCMP_256;
} else if (os_strcmp(token, "cipher=CCMP") == 0) {
cipher = WPA_CIPHER_CCMP;
} else if (os_strcmp(token, "cipher=GCMP") == 0) {
cipher = WPA_CIPHER_GCMP;
} else if (os_strncmp(token, "group=", 6) == 0) {
group = atoi(token + 6);
} else if (os_strncmp(token, "nid=", 4) == 0) {
id = atoi(token + 4);
} else if (os_strncmp(token, "comeback=", 9) == 0) {
comeback_len = os_strlen(token + 9);
if (comeback || !comeback_len || comeback_len % 2)
goto out;
comeback_len /= 2;
comeback = os_malloc(comeback_len);
if (!comeback ||
hexstr2bin(token + 9, comeback, comeback_len))
goto out;
} else {
wpa_printf(MSG_DEBUG,
"CTRL: PASN Invalid parameter: '%s'",
token);
goto out;
}
}
if (!got_bssid || akmp == -1 || cipher == -1 || group == 0xFFFF) {
wpa_printf(MSG_DEBUG,"CTRL: PASN missing parameter");
goto out;
}
ret = wpas_pasn_auth_start(wpa_s, wpa_s->own_addr, bssid, akmp, cipher,
group, id, comeback, comeback_len);
out:
os_free(comeback);
return ret;
}
static int wpas_ctrl_iface_pasn_deauthenticate(struct wpa_supplicant *wpa_s,
const char *cmd)
{
u8 bssid[ETH_ALEN];
if (os_strncmp(cmd, "bssid=", 6) != 0 || hwaddr_aton(cmd + 6, bssid)) {
wpa_printf(MSG_DEBUG,
"CTRL: PASN_DEAUTH without valid BSSID");
return -1;
}
return wpas_pasn_deauthenticate(wpa_s, wpa_s->own_addr, bssid);
}
#ifdef CONFIG_TESTING_OPTIONS
static int wpas_ctrl_iface_pasn_driver(struct wpa_supplicant *wpa_s,
const char *cmd)
{
union wpa_event_data event;
const char *pos = cmd;
u8 addr[ETH_ALEN];
os_memset(&event, 0, sizeof(event));
if (os_strncmp(pos, "auth ", 5) == 0)
event.pasn_auth.action = PASN_ACTION_AUTH;
else if (os_strncmp(pos, "del ", 4) == 0)
event.pasn_auth.action =
PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT;
else
return -1;
pos = os_strchr(pos, ' ');
if (!pos)
return -1;
pos++;
while (hwaddr_aton(pos, addr) == 0) {
struct pasn_peer *peer;
if (event.pasn_auth.num_peers == WPAS_MAX_PASN_PEERS)
return -1;
peer = &event.pasn_auth.peer[event.pasn_auth.num_peers];
os_memcpy(peer->own_addr, wpa_s->own_addr, ETH_ALEN);
os_memcpy(peer->peer_addr, addr, ETH_ALEN);
event.pasn_auth.num_peers++;
pos = os_strchr(pos, ' ');
if (!pos)
break;
pos++;
}
wpa_supplicant_event(wpa_s, EVENT_PASN_AUTH, &event);
return 0;
}
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_PASN */
#ifndef CONFIG_NO_ROBUST_AV
static int set_type4_frame_classifier(const char *cmd,
struct type4_params *param)
{
const char *pos, *end;
u8 classifier_mask = 0;
int ret;
char addr[INET6_ADDRSTRLEN];
size_t alen;
if (os_strstr(cmd, "ip_version=ipv4")) {
param->ip_version = IPV4;
} else if (os_strstr(cmd, "ip_version=ipv6")) {
param->ip_version = IPV6;
} else {
wpa_printf(MSG_ERROR, "IP version missing/invalid");
return -1;
}
classifier_mask |= BIT(0);
pos = os_strstr(cmd, "src_ip=");
if (pos) {
pos += 7;
end = os_strchr(pos, ' ');
if (!end)
end = pos + os_strlen(pos);
alen = end - pos;
if (alen >= INET6_ADDRSTRLEN)
return -1;
os_memcpy(addr, pos, alen);
addr[alen] = '\0';
if (param->ip_version == IPV4)
ret = inet_pton(AF_INET, addr,
&param->ip_params.v4.src_ip);
else
ret = inet_pton(AF_INET6, addr,
&param->ip_params.v6.src_ip);
if (ret != 1) {
wpa_printf(MSG_ERROR,
"Error converting src IP address to binary ret=%d",
ret);
return -1;
}
classifier_mask |= BIT(1);
}
pos = os_strstr(cmd, "dst_ip=");
if (pos) {
pos += 7;
end = os_strchr(pos, ' ');
if (!end)
end = pos + os_strlen(pos);
alen = end - pos;
if (alen >= INET6_ADDRSTRLEN)
return -1;
os_memcpy(addr, pos, alen);
addr[alen] = '\0';
if (param->ip_version == IPV4)
ret = inet_pton(AF_INET, addr,
&param->ip_params.v4.dst_ip);
else
ret = inet_pton(AF_INET6, addr,
&param->ip_params.v6.dst_ip);
if (ret != 1) {
wpa_printf(MSG_ERROR,
"Error converting dst IP address to binary ret=%d",
ret);
return -1;
}
classifier_mask |= BIT(2);
}
pos = os_strstr(cmd, "src_port=");
if (pos && atoi(pos + 9) > 0) {
if (param->ip_version == IPV4)
param->ip_params.v4.src_port = atoi(pos + 9);
else
param->ip_params.v6.src_port = atoi(pos + 9);
classifier_mask |= BIT(3);
}
pos = os_strstr(cmd, "dst_port=");
if (pos && atoi(pos + 9) > 0) {
if (param->ip_version == IPV4)
param->ip_params.v4.dst_port = atoi(pos + 9);
else
param->ip_params.v6.dst_port = atoi(pos + 9);
classifier_mask |= BIT(4);
}
pos = os_strstr(cmd, "dscp=");
if (pos && atoi(pos + 5) > 0) {
if (param->ip_version == IPV4)
param->ip_params.v4.dscp = atoi(pos + 5);
else
param->ip_params.v6.dscp = atoi(pos + 5);
classifier_mask |= BIT(5);
}
if (param->ip_version == IPV4) {
pos = os_strstr(cmd, "protocol=");
if (pos) {
if (os_strstr(pos, "udp")) {
param->ip_params.v4.protocol = 17;
} else if (os_strstr(pos, "tcp")) {
param->ip_params.v4.protocol = 6;
} else if (os_strstr(pos, "esp")) {
param->ip_params.v4.protocol = 50;
} else {
wpa_printf(MSG_ERROR, "Invalid protocol");
return -1;
}
classifier_mask |= BIT(6);
}
} else {
pos = os_strstr(cmd, "next_header=");
if (pos) {
if (os_strstr(pos, "udp")) {
param->ip_params.v6.next_header = 17;
} else if (os_strstr(pos, "tcp")) {
param->ip_params.v6.next_header = 6;
} else if (os_strstr(pos, "esp")) {
param->ip_params.v6.next_header = 50;
} else {
wpa_printf(MSG_ERROR, "Invalid next header");
return -1;
}
classifier_mask |= BIT(6);
}
pos = os_strstr(cmd, "flow_label=");
if (pos) {
pos += 11;
end = os_strchr(pos, ' ');
if (!end)
end = pos + os_strlen(pos);
if (end - pos != 6 ||
hexstr2bin(pos, param->ip_params.v6.flow_label,
3) ||
param->ip_params.v6.flow_label[0] > 0x0F) {
wpa_printf(MSG_ERROR, "Invalid flow label");
return -1;
}
classifier_mask |= BIT(7);
}
}
param->classifier_mask = classifier_mask;
return 0;
}
static int set_type10_frame_classifier(const char *cmd,
struct type10_params *param)
{
const char *pos, *end;
size_t filter_len;
pos = os_strstr(cmd, "prot_instance=");
if (!pos) {
wpa_printf(MSG_ERROR, "Protocol instance missing");
return -1;
}
param->prot_instance = atoi(pos + 14);
pos = os_strstr(cmd, "prot_number=");
if (!pos) {
wpa_printf(MSG_ERROR, "Protocol number missing");
return -1;
}
if (os_strstr(pos, "udp")) {
param->prot_number = 17;
} else if (os_strstr(pos, "tcp")) {
param->prot_number = 6;
} else if (os_strstr(pos, "esp")) {
param->prot_number = 50;
} else {
wpa_printf(MSG_ERROR, "Invalid protocol number");
return -1;
}
pos = os_strstr(cmd, "filter_value=");
if (!pos) {
wpa_printf(MSG_ERROR,
"Classifier parameter filter_value missing");
return -1;
}
pos += 13;
end = os_strchr(pos, ' ');
if (!end)
end = pos + os_strlen(pos);
filter_len = (end - pos) / 2;
param->filter_value = os_malloc(filter_len);
if (!param->filter_value)
return -1;
if (hexstr2bin(pos, param->filter_value, filter_len)) {
wpa_printf(MSG_ERROR, "Invalid filter_value %s", pos);
goto free;
}
pos = os_strstr(cmd, "filter_mask=");
if (!pos) {
wpa_printf(MSG_ERROR,
"Classifier parameter filter_mask missing");
goto free;
}
pos += 12;
end = os_strchr(pos, ' ');
if (!end)
end = pos + os_strlen(pos);
if (filter_len != (size_t) (end - pos) / 2) {
wpa_printf(MSG_ERROR,
"Filter mask length mismatch expected=%zu received=%zu",
filter_len, (size_t) (end - pos) / 2);
goto free;
}
param->filter_mask = os_malloc(filter_len);
if (!param->filter_mask)
goto free;
if (hexstr2bin(pos, param->filter_mask, filter_len)) {
wpa_printf(MSG_ERROR, "Invalid filter mask %s", pos);
os_free(param->filter_mask);
param->filter_mask = NULL;
goto free;
}
param->filter_len = filter_len;
return 0;
free:
os_free(param->filter_value);
param->filter_value = NULL;
return -1;
}
static int scs_parse_type4(struct tclas_element *elem, const char *pos)
{
struct type4_params type4_param = { 0 };
if (set_type4_frame_classifier(pos, &type4_param) == -1) {
wpa_printf(MSG_ERROR, "Failed to set frame_classifier 4");
return -1;
}
os_memcpy(&elem->frame_classifier.type4_param,
&type4_param, sizeof(struct type4_params));
return 0;
}
static int scs_parse_type10(struct tclas_element *elem, const char *pos)
{
struct type10_params type10_param = { 0 };
if (set_type10_frame_classifier(pos, &type10_param) == -1) {
wpa_printf(MSG_ERROR, "Failed to set frame_classifier 10");
return -1;
}
os_memcpy(&elem->frame_classifier.type10_param,
&type10_param, sizeof(struct type10_params));
return 0;
}
static int wpas_ctrl_iface_configure_scs(struct wpa_supplicant *wpa_s,
char *cmd)
{
char *pos1, *pos;
struct scs_robust_av_data *scs_data = &wpa_s->scs_robust_av_req;
struct scs_desc_elem desc_elem = { 0 };
int val;
unsigned int num_scs_desc = 0;
if (wpa_s->ongoing_scs_req) {
wpa_printf(MSG_ERROR, "%s: SCS Request already in queue",
__func__);
return -1;
}
/**
* format:
* [scs_id=<decimal number>] <add|remove|change> [scs_up=<0-7>]
* [classifier_type=<4|10>]
* [classifier params based on classifier type]
* [tclas_processing=<0|1>]
* [qos_characteristics] <up/down/direct> [min_si=<decimal number>]
* [max_si=<decimal number>] [min_data_rate=<decimal number>]
* [delay_bound=<decimal number>] [max_msdu=<decimal number>]
* [service_start_time=<decimal number>]
* [service_start_time_link_id=<decimal number>]
* [mean_data_rate=<decimal number>] [burst_size=<decimal number>]
* [msdu_lifetime=<decimal number>]
* [msdu_delivery_info=<decimal number>] [medium_time=<decimal number>]
* [scs_id=<decimal number>] ...
*/
pos1 = os_strstr(cmd, "scs_id=");
if (!pos1) {
wpa_printf(MSG_ERROR, "SCSID not present");
return -1;
}
free_up_scs_desc(scs_data);
while (pos1) {
struct scs_desc_elem *n1;
struct active_scs_elem *active_scs_desc;
char *next_scs_desc, *pos2;
unsigned int num_tclas_elem = 0;
bool scsid_active = false, tclas_present = false;
struct qos_characteristics *qos_elem = &desc_elem.qos_char_elem;
desc_elem.scs_id = atoi(pos1 + 7);
pos1 += 7;
next_scs_desc = os_strstr(pos1, "scs_id=");
if (next_scs_desc) {
char temp[20];
os_snprintf(temp, sizeof(temp), "scs_id=%d ",
desc_elem.scs_id);
if (os_strstr(next_scs_desc, temp)) {
wpa_printf(MSG_ERROR,
"Multiple SCS descriptors configured with same SCSID(=%d)",
desc_elem.scs_id);
goto free_scs_desc;
}
pos1[next_scs_desc - pos1 - 1] = '\0';
}
dl_list_for_each(active_scs_desc, &wpa_s->active_scs_ids,
struct active_scs_elem, list) {
if (desc_elem.scs_id == active_scs_desc->scs_id) {
scsid_active = true;
break;
}
}
if (os_strstr(pos1, "add ")) {
desc_elem.request_type = SCS_REQ_ADD;
if (scsid_active) {
wpa_printf(MSG_ERROR, "SCSID %d already active",
desc_elem.scs_id);
return -1;
}
} else if (os_strstr(pos1, "remove")) {
desc_elem.request_type = SCS_REQ_REMOVE;
if (!scsid_active) {
wpa_printf(MSG_ERROR, "SCSID %d not active",
desc_elem.scs_id);
return -1;
}
goto scs_desc_end;
} else if (os_strstr(pos1, "change ")) {
desc_elem.request_type = SCS_REQ_CHANGE;
if (!scsid_active) {
wpa_printf(MSG_ERROR, "SCSID %d not active",
desc_elem.scs_id);
return -1;
}
} else {
wpa_printf(MSG_ERROR, "SCS Request type invalid");
goto free_scs_desc;
}
pos1 = os_strstr(pos1, "scs_up=");
if (!pos1) {
wpa_printf(MSG_ERROR,
"Intra-Access user priority not present");
goto free_scs_desc;
}
val = atoi(pos1 + 7);
if (val < 0 || val > 7) {
wpa_printf(MSG_ERROR,
"Intra-Access user priority invalid %d",
val);
goto free_scs_desc;
}
desc_elem.intra_access_priority = val;
desc_elem.scs_up_avail = true;
pos = os_strstr(pos1, "classifier_type=");
if (!pos) {
wpa_printf(MSG_ERROR, "classifier type empty");
goto qos_characteristics;
}
tclas_present = true;
while (pos) {
struct tclas_element elem = { 0 }, *n;
char *next_tclas_elem;
val = atoi(pos + 16);
if (val != 4 && val != 10) {
wpa_printf(MSG_ERROR,
"classifier type invalid %d", val);
goto free_scs_desc;
}
elem.classifier_type = val;
pos += 16;
next_tclas_elem = os_strstr(pos, "classifier_type=");
if (next_tclas_elem) {
pos1 = next_tclas_elem;
pos[next_tclas_elem - pos - 1] = '\0';
}
switch (val) {
case 4:
if (scs_parse_type4(&elem, pos) < 0)
goto free_scs_desc;
break;
case 10:
if (scs_parse_type10(&elem, pos) < 0)
goto free_scs_desc;
break;
}
n = os_realloc(desc_elem.tclas_elems,
(num_tclas_elem + 1) * sizeof(elem));
if (!n)
goto free_scs_desc;
desc_elem.tclas_elems = n;
os_memcpy((u8 *) desc_elem.tclas_elems +
num_tclas_elem * sizeof(elem),
&elem, sizeof(elem));
num_tclas_elem++;
desc_elem.num_tclas_elem = num_tclas_elem;
pos = next_tclas_elem;
}
if (desc_elem.num_tclas_elem > 1) {
pos1 = os_strstr(pos1, "tclas_processing=");
if (!pos1) {
wpa_printf(MSG_ERROR, "tclas_processing empty");
goto free_scs_desc;
}
val = atoi(pos1 + 17);
if (val != 0 && val != 1) {
wpa_printf(MSG_ERROR,
"tclas_processing invalid");
goto free_scs_desc;
}
desc_elem.tclas_processing = val;
}
qos_characteristics:
pos1 = os_strstr(pos1, "qos_characteristics");
if (!pos1 && !tclas_present)
goto free_scs_desc;
if (!pos1)
goto scs_desc_end;
qos_elem->available = true;
if (os_strstr(pos1, "up ")) {
qos_elem->direction = SCS_DIRECTION_UP;
if (tclas_present) {
wpa_printf(MSG_ERROR,
"TCLAS with direction:UP not allowed");
goto free_scs_desc;
}
} else if (os_strstr(pos1, "down ")) {
qos_elem->direction = SCS_DIRECTION_DOWN;
} else if (os_strstr(pos1, "direct ")) {
qos_elem->direction = SCS_DIRECTION_DIRECT;
}
pos1 = os_strstr(pos1, "min_si=");
if (!pos1) {
wpa_printf(MSG_ERROR, "Min SI is required");
goto free_scs_desc;
}
qos_elem->min_si = atoi(pos1 + 7);
pos1 = os_strstr(pos1, "max_si=");
if (!pos1) {
wpa_printf(MSG_ERROR, "Max SI is required");
goto free_scs_desc;
}
qos_elem->max_si = atoi(pos1 + 7);
if (qos_elem->min_si && qos_elem->max_si &&
qos_elem->max_si < qos_elem->min_si) {
wpa_printf(MSG_ERROR, "Invalid Max SI");
goto free_scs_desc;
}
pos1 = os_strstr(pos1, "min_data_rate=");
if (!pos1) {
wpa_printf(MSG_ERROR, "Min data rate is required");
goto free_scs_desc;
}
qos_elem->min_data_rate = atoi(pos1 + 14);
pos1 = os_strstr(pos1, "delay_bound=");
if (!pos1) {
wpa_printf(MSG_ERROR, "Delay Bound is required");
goto free_scs_desc;
}
qos_elem->delay_bound = atoi(pos1 + 12);
if (qos_elem->min_data_rate >= BIT(24) ||
qos_elem->delay_bound >= BIT(24)) {
wpa_printf(MSG_ERROR,
"Invalid min_data_rate or delay_bound");
goto free_scs_desc;
}
pos2 = os_strstr(pos1, "max_msdu=");
if (pos2) {
qos_elem->max_msdu_size = atoi(pos2 + 9);
qos_elem->mask |= SCS_QOS_BIT_MAX_MSDU_SIZE;
}
pos2 = os_strstr(pos1, "service_start_time=");
if (pos2) {
qos_elem->service_start_time = atoi(pos2 + 19);
qos_elem->mask |= SCS_QOS_BIT_SERVICE_START_TIME;
}
pos2 = os_strstr(pos1, "service_start_time_link_id=");
if (pos2) {
qos_elem->service_start_time_link_id = atoi(pos2 + 27);
qos_elem->mask |= SCS_QOS_BIT_SERVICE_START_TIME_LINKID;
}
pos2 = os_strstr(pos1, "mean_data_rate=");
if (pos2) {
qos_elem->mean_data_rate = atoi(pos2 + 15);
qos_elem->mask |= SCS_QOS_BIT_MEAN_DATA_RATE;
}
pos2 = os_strstr(pos1, "burst_size=");
if (pos2) {
qos_elem->burst_size = atoi(pos2 + 11);
qos_elem->mask |=
SCS_QOS_BIT_DELAYED_BOUNDED_BURST_SIZE;
}
pos2 = os_strstr(pos1, "msdu_lifetime=");
if (pos2) {
qos_elem->msdu_lifetime = atoi(pos2 + 14);
qos_elem->mask |= SCS_QOS_BIT_MSDU_LIFETIME;
}
pos2 = os_strstr(pos1, "msdu_delivery_info=");
if (pos2) {
qos_elem->msdu_delivery_info = atoi(pos2 + 19);
qos_elem->mask |= SCS_QOS_BIT_MSDU_DELIVERY_INFO;
}
pos2 = os_strstr(pos1, "medium_time=");
if (pos2) {
qos_elem->medium_time = atoi(pos2 + 12);
qos_elem->mask |= SCS_QOS_BIT_MEDIUM_TIME;
}
scs_desc_end:
n1 = os_realloc(scs_data->scs_desc_elems, (num_scs_desc + 1) *
sizeof(struct scs_desc_elem));
if (!n1)
goto free_scs_desc;
scs_data->scs_desc_elems = n1;
os_memcpy((u8 *) scs_data->scs_desc_elems + num_scs_desc *
sizeof(desc_elem), &desc_elem, sizeof(desc_elem));
num_scs_desc++;
scs_data->num_scs_desc = num_scs_desc;
pos1 = next_scs_desc;
os_memset(&desc_elem, 0, sizeof(desc_elem));
}
return wpas_send_scs_req(wpa_s);
free_scs_desc:
free_up_tclas_elem(&desc_elem);
free_up_scs_desc(scs_data);
return -1;
}
static int wpas_ctrl_iface_send_dscp_resp(struct wpa_supplicant *wpa_s,
const char *cmd)
{
char *pos;
struct dscp_policy_status *policy = NULL, *n;
int num_policies = 0, ret = -1;
struct dscp_resp_data resp_data;
/*
* format:
* <[reset]>/<[solicited] [policy_id=1 status=0...]> [more]
*/
os_memset(&resp_data, 0, sizeof(resp_data));
resp_data.more = os_strstr(cmd, "more") != NULL;
if (os_strstr(cmd, "reset")) {
resp_data.reset = true;
resp_data.solicited = false;
goto send_resp;
}
resp_data.solicited = os_strstr(cmd, "solicited") != NULL;
pos = os_strstr(cmd, "policy_id=");
while (pos) {
n = os_realloc(policy, (num_policies + 1) * sizeof(*policy));
if (!n)
goto fail;
policy = n;
pos += 10;
policy[num_policies].id = atoi(pos);
if (policy[num_policies].id == 0) {
wpa_printf(MSG_ERROR, "DSCP: Invalid policy id");
goto fail;
}
pos = os_strstr(pos, "status=");
if (!pos) {
wpa_printf(MSG_ERROR,
"DSCP: Status is not found for a policy");
goto fail;
}
pos += 7;
policy[num_policies].status = atoi(pos);
num_policies++;
pos = os_strstr(pos, "policy_id");
}
resp_data.policy = policy;
resp_data.num_policies = num_policies;
send_resp:
ret = wpas_send_dscp_response(wpa_s, &resp_data);
if (ret)
wpa_printf(MSG_ERROR, "DSCP: Failed to send DSCP response");
fail:
os_free(policy);
return ret;
}
static int wpas_ctrl_iface_send_dscp_query(struct wpa_supplicant *wpa_s,
const char *cmd)
{
char *pos;
/*
* format:
* Wildcard DSCP query
* <wildcard>
*
* DSCP query with a domain name attribute:
* [domain_name=<string>]
*/
if (os_strstr(cmd, "wildcard")) {
wpa_printf(MSG_DEBUG, "QM: Send wildcard DSCP policy query");
return wpas_send_dscp_query(wpa_s, NULL, 0);
}
pos = os_strstr(cmd, "domain_name=");
if (!pos || !os_strlen(pos + 12)) {
wpa_printf(MSG_ERROR, "QM: Domain name not preset");
return -1;
}
return wpas_send_dscp_query(wpa_s, pos + 12, os_strlen(pos + 12));
}
#endif /* CONFIG_NO_ROBUST_AV */
static int wpas_ctrl_iface_mlo_signal_poll(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen)
{
int ret, i;
char *pos, *end;
struct wpa_mlo_signal_info mlo_si;
if (!wpa_s->valid_links)
return -1;
ret = wpa_drv_mlo_signal_poll(wpa_s, &mlo_si);
if (ret)
return -1;
pos = buf;
end = buf + buflen;
for_each_link(mlo_si.valid_links, i) {
ret = os_snprintf(pos, end - pos,
"LINK_ID=%d\nRSSI=%d\nLINKSPEED=%lu\n"
"NOISE=%d\nFREQUENCY=%u\n",
i, mlo_si.links[i].data.signal,
mlo_si.links[i].data.current_tx_rate / 1000,
mlo_si.links[i].current_noise,
mlo_si.links[i].frequency);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
if (mlo_si.links[i].chanwidth != CHAN_WIDTH_UNKNOWN) {
ret = os_snprintf(pos, end - pos, "WIDTH=%s\n",
channel_width_to_string(
mlo_si.links[i].chanwidth));
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (mlo_si.links[i].center_frq1 > 0) {
ret = os_snprintf(pos, end - pos, "CENTER_FRQ1=%d\n",
mlo_si.links[i].center_frq1);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (mlo_si.links[i].center_frq2 > 0) {
ret = os_snprintf(pos, end - pos, "CENTER_FRQ2=%d\n",
mlo_si.links[i].center_frq2);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (mlo_si.links[i].data.avg_signal) {
ret = os_snprintf(pos, end - pos,
"AVG_RSSI=%d\n",
mlo_si.links[i].data.avg_signal);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (mlo_si.links[i].data.avg_beacon_signal) {
ret = os_snprintf(
pos, end - pos, "AVG_BEACON_RSSI=%d\n",
mlo_si.links[i].data.avg_beacon_signal);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
}
return pos - buf;
}
static int wpas_ctrl_iface_mlo_status(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen)
{
int ret, i;
char *pos, *end;
if (!wpa_s->valid_links)
return -1;
pos = buf;
end = buf + buflen;
for_each_link(wpa_s->valid_links, i) {
ret = os_snprintf(pos, end - pos, "link_id=%d\nfreq=%u\n"
"ap_link_addr=" MACSTR
"\nsta_link_addr=" MACSTR "\n",
i, wpa_s->links[i].freq,
MAC2STR(wpa_s->links[i].bssid),
MAC2STR(wpa_s->links[i].addr));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
return pos - buf;
}
#ifdef CONFIG_TESTING_OPTIONS
static int wpas_ctrl_ml_probe(struct wpa_supplicant *wpa_s, char *cmd)
{
char *token, *context = NULL;
u8 bssid[ETH_ALEN];
int mld_id = -1, link_id = -1;
struct wpa_bss *bss;
int *freqs;
os_memset(bssid, 0, sizeof(bssid));
while ((token = str_token(cmd, " ", &context))) {
if (os_strncmp(token, "bssid=", 6) == 0) {
if (hwaddr_aton(token + 6, bssid))
return -1;
} else if (os_strncmp(token, "mld_id=", 7) == 0) {
mld_id = atoi(token + 7);
} else if (os_strncmp(token, "link_id=", 8) == 0) {
link_id = atoi(token + 8);
}
}
if (mld_id < 0 || is_zero_ether_addr(bssid)) {
wpa_printf(MSG_DEBUG,
"MLD: Failed parsing ML probe request arguments");
return -1;
}
bss = wpa_bss_get_bssid(wpa_s, bssid);
if (!bss) {
wpa_printf(MSG_DEBUG,
"MLD: Unknown BSS for " MACSTR, MAC2STR(bssid));
return -1;
}
if (wpa_s->sched_scanning || wpa_s->scanning ||
(wpa_s->wpa_state > WPA_SCANNING &&
wpa_s->wpa_state != WPA_COMPLETED)) {
wpa_printf(MSG_DEBUG,
"MLO: Ongoing scan: Reject ML probe request");
return -1;
}
freqs = os_malloc(sizeof(int) * 2);
if (!freqs)
return -1;
freqs[0] = bss->freq;
freqs[1] = 0;
wpa_s->manual_scan_passive = 0;
wpa_s->manual_scan_use_id = 0;
wpa_s->manual_scan_only_new = 0;
wpa_s->scan_id_count = 0;
wpa_s->scan_res_handler = scan_only_handler;
os_free(wpa_s->manual_scan_freqs);
wpa_s->manual_scan_freqs = freqs;
os_memcpy(wpa_s->ml_probe_bssid, bssid, ETH_ALEN);
wpa_s->ml_probe_mld_id = mld_id;
if (link_id >= 0)
wpa_s->ml_probe_links = BIT(link_id);
wpa_s->normal_scans = 0;
wpa_s->scan_req = MANUAL_SCAN_REQ;
wpa_s->after_wps = 0;
wpa_s->known_wps_freq = 0;
wpa_supplicant_req_scan(wpa_s, 0, 0);
return 0;
}
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_NAN_USD
static int wpas_ctrl_nan_publish(struct wpa_supplicant *wpa_s, char *cmd,
char *buf, size_t buflen)
{
char *token, *context = NULL;
int publish_id;
struct nan_publish_params params;
const char *service_name = NULL;
struct wpabuf *ssi = NULL;
int ret = -1;
enum nan_service_protocol_type srv_proto_type = 0;
int *freq_list = NULL;
os_memset(&params, 0, sizeof(params));
/* USD shall use both solicited and unsolicited transmissions */
params.unsolicited = true;
params.solicited = true;
/* USD shall require FSD without GAS */
params.fsd = true;
params.freq = NAN_USD_DEFAULT_FREQ;
while ((token = str_token(cmd, " ", &context))) {
if (os_strncmp(token, "service_name=", 13) == 0) {
service_name = token + 13;
continue;
}
if (os_strncmp(token, "ttl=", 4) == 0) {
params.ttl = atoi(token + 4);
continue;
}
if (os_strncmp(token, "freq=", 5) == 0) {
params.freq = atoi(token + 5);
continue;
}
if (os_strncmp(token, "freq_list=", 10) == 0) {
char *pos = token + 10;
if (os_strcmp(pos, "all") == 0) {
os_free(freq_list);
freq_list = wpas_nan_usd_all_freqs(wpa_s);
params.freq_list = freq_list;
continue;
}
while (pos && pos[0]) {
int_array_add_unique(&freq_list, atoi(pos));
pos = os_strchr(pos, ',');
if (pos)
pos++;
}
params.freq_list = freq_list;
continue;
}
if (os_strncmp(token, "srv_proto_type=", 15) == 0) {
srv_proto_type = atoi(token + 15);
continue;
}
if (os_strncmp(token, "ssi=", 4) == 0) {
if (ssi)
goto fail;
ssi = wpabuf_parse_bin(token + 4);
if (!ssi)
goto fail;
continue;
}
if (os_strcmp(token, "solicited=0") == 0) {
params.solicited = false;
continue;
}
if (os_strcmp(token, "unsolicited=0") == 0) {
params.unsolicited = false;
continue;
}
if (os_strcmp(token, "fsd=0") == 0) {
params.fsd = false;
continue;
}
wpa_printf(MSG_INFO, "CTRL: Invalid NAN_PUBLISH parameter: %s",
token);
goto fail;
}
publish_id = wpas_nan_usd_publish(wpa_s, service_name, srv_proto_type,
ssi, &params);
if (publish_id > 0)
ret = os_snprintf(buf, buflen, "%d", publish_id);
fail:
wpabuf_free(ssi);
os_free(freq_list);
return ret;
}
static int wpas_ctrl_nan_cancel_publish(struct wpa_supplicant *wpa_s,
char *cmd)
{
char *token, *context = NULL;
int publish_id = 0;
while ((token = str_token(cmd, " ", &context))) {
if (sscanf(token, "publish_id=%i", &publish_id) == 1)
continue;
wpa_printf(MSG_INFO,
"CTRL: Invalid NAN_CANCEL_PUBLISH parameter: %s",
token);
return -1;
}
if (publish_id <= 0) {
wpa_printf(MSG_INFO,
"CTRL: Invalid or missing NAN_CANCEL_PUBLISH publish_id");
return -1;
}
wpas_nan_usd_cancel_publish(wpa_s, publish_id);
return 0;
}
static int wpas_ctrl_nan_update_publish(struct wpa_supplicant *wpa_s,
char *cmd)
{
char *token, *context = NULL;
int publish_id = 0;
struct wpabuf *ssi = NULL;
int ret = -1;
while ((token = str_token(cmd, " ", &context))) {
if (sscanf(token, "publish_id=%i", &publish_id) == 1)
continue;
if (os_strncmp(token, "ssi=", 4) == 0) {
if (ssi)
goto fail;
ssi = wpabuf_parse_bin(token + 4);
if (!ssi)
goto fail;
continue;
}
wpa_printf(MSG_INFO,
"CTRL: Invalid NAN_UPDATE_PUBLISH parameter: %s",
token);
goto fail;
}
if (publish_id <= 0) {
wpa_printf(MSG_INFO,
"CTRL: Invalid or missing NAN_UPDATE_PUBLISH publish_id");
goto fail;
}
ret = wpas_nan_usd_update_publish(wpa_s, publish_id, ssi);
fail:
wpabuf_free(ssi);
return ret;
}
static int wpas_ctrl_nan_subscribe(struct wpa_supplicant *wpa_s, char *cmd,
char *buf, size_t buflen)
{
char *token, *context = NULL;
int subscribe_id;
struct nan_subscribe_params params;
const char *service_name = NULL;
struct wpabuf *ssi = NULL;
int ret = -1;
enum nan_service_protocol_type srv_proto_type = 0;
os_memset(&params, 0, sizeof(params));
params.freq = NAN_USD_DEFAULT_FREQ;
while ((token = str_token(cmd, " ", &context))) {
if (os_strncmp(token, "service_name=", 13) == 0) {
service_name = token + 13;
continue;
}
if (os_strcmp(token, "active=1") == 0) {
params.active = true;
continue;
}
if (os_strncmp(token, "ttl=", 4) == 0) {
params.ttl = atoi(token + 4);
continue;
}
if (os_strncmp(token, "freq=", 5) == 0) {
params.freq = atoi(token + 5);
continue;
}
if (os_strncmp(token, "srv_proto_type=", 15) == 0) {
srv_proto_type = atoi(token + 15);
continue;
}
if (os_strncmp(token, "ssi=", 4) == 0) {
if (ssi)
goto fail;
ssi = wpabuf_parse_bin(token + 4);
if (!ssi)
goto fail;
continue;
}
wpa_printf(MSG_INFO,
"CTRL: Invalid NAN_SUBSCRIBE parameter: %s",
token);
goto fail;
}
subscribe_id = wpas_nan_usd_subscribe(wpa_s, service_name,
srv_proto_type, ssi,
&params);
if (subscribe_id > 0)
ret = os_snprintf(buf, buflen, "%d", subscribe_id);
fail:
wpabuf_free(ssi);
return ret;
}
static int wpas_ctrl_nan_cancel_subscribe(struct wpa_supplicant *wpa_s,
char *cmd)
{
char *token, *context = NULL;
int subscribe_id = 0;
while ((token = str_token(cmd, " ", &context))) {
if (sscanf(token, "subscribe_id=%i", &subscribe_id) == 1)
continue;
wpa_printf(MSG_INFO,
"CTRL: Invalid NAN_CANCEL_SUBSCRIBE parameter: %s",
token);
return -1;
}
if (subscribe_id <= 0) {
wpa_printf(MSG_INFO,
"CTRL: Invalid or missing NAN_CANCEL_SUBSCRIBE subscribe_id");
return -1;
}
wpas_nan_usd_cancel_subscribe(wpa_s, subscribe_id);
return 0;
}
static int wpas_ctrl_nan_transmit(struct wpa_supplicant *wpa_s, char *cmd)
{
char *token, *context = NULL;
int handle = 0;
int req_instance_id = 0;
struct wpabuf *ssi = NULL;
u8 peer_addr[ETH_ALEN];
int ret = -1;
os_memset(peer_addr, 0, ETH_ALEN);
while ((token = str_token(cmd, " ", &context))) {
if (sscanf(token, "handle=%i", &handle) == 1)
continue;
if (sscanf(token, "req_instance_id=%i", &req_instance_id) == 1)
continue;
if (os_strncmp(token, "address=", 8) == 0) {
if (hwaddr_aton(token + 8, peer_addr) < 0)
return -1;
continue;
}
if (os_strncmp(token, "ssi=", 4) == 0) {
if (ssi)
goto fail;
ssi = wpabuf_parse_bin(token + 4);
if (!ssi)
goto fail;
continue;
}
wpa_printf(MSG_INFO,
"CTRL: Invalid NAN_TRANSMIT parameter: %s",
token);
goto fail;
}
if (handle <= 0) {
wpa_printf(MSG_INFO,
"CTRL: Invalid or missing NAN_TRANSMIT handle");
goto fail;
}
if (is_zero_ether_addr(peer_addr)) {
wpa_printf(MSG_INFO,
"CTRL: Invalid or missing NAN_TRANSMIT address");
goto fail;
}
ret = wpas_nan_usd_transmit(wpa_s, handle, ssi, NULL, peer_addr,
req_instance_id);
fail:
wpabuf_free(ssi);
return ret;
}
#endif /* CONFIG_NAN_USD */
char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
char *buf, size_t *resp_len)
{
char *reply;
const int reply_size = 4096;
int reply_len;
if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ||
os_strncmp(buf, "SET_NETWORK ", 12) == 0 ||
os_strncmp(buf, "PMKSA_ADD ", 10) == 0 ||
os_strncmp(buf, "MESH_PMKSA_ADD ", 15) == 0) {
if (wpa_debug_show_keys)
wpa_dbg(wpa_s, MSG_DEBUG,
"Control interface command '%s'", buf);
else
wpa_dbg(wpa_s, MSG_DEBUG,
"Control interface command '%s [REMOVED]'",
os_strncmp(buf, WPA_CTRL_RSP,
os_strlen(WPA_CTRL_RSP)) == 0 ?
WPA_CTRL_RSP :
(os_strncmp(buf, "SET_NETWORK ", 12) == 0 ?
"SET_NETWORK" : "key-add"));
} else if (os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 ||
os_strncmp(buf, "NFC_REPORT_HANDOVER", 19) == 0) {
wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
(const u8 *) buf, os_strlen(buf));
} else {
int level = wpas_ctrl_cmd_debug_level(buf);
wpa_dbg(wpa_s, level, "Control interface command '%s'", buf);
}
reply = os_malloc(reply_size);
if (reply == NULL) {
*resp_len = 1;
return NULL;
}
os_memcpy(reply, "OK\n", 3);
reply_len = 3;
if (os_strcmp(buf, "PING") == 0) {
os_memcpy(reply, "PONG\n", 5);
reply_len = 5;
} else if (os_strcmp(buf, "IFNAME") == 0) {
reply_len = os_strlen(wpa_s->ifname);
os_memcpy(reply, wpa_s->ifname, reply_len);
} else if (os_strncmp(buf, "RELOG", 5) == 0) {
if (wpa_debug_reopen_file() < 0)
reply_len = -1;
} else if (os_strncmp(buf, "NOTE ", 5) == 0) {
wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
} else if (os_strcmp(buf, "MIB") == 0) {
reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
if (reply_len >= 0) {
reply_len += eapol_sm_get_mib(wpa_s->eapol,
reply + reply_len,
reply_size - reply_len);
#ifdef CONFIG_MACSEC
reply_len += ieee802_1x_kay_get_mib(
wpa_s->kay, reply + reply_len,
reply_size - reply_len);
#endif /* CONFIG_MACSEC */
}
} else if (os_strncmp(buf, "STATUS", 6) == 0) {
reply_len = wpa_supplicant_ctrl_iface_status(
wpa_s, buf + 6, reply, reply_size);
} else if (os_strcmp(buf, "PMKSA") == 0) {
reply_len = wpas_ctrl_iface_pmksa(wpa_s, reply, reply_size);
} else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
wpas_ctrl_iface_pmksa_flush(wpa_s);
External persistent storage for PMKSA cache entries This adds new wpa_supplicant control interface commands PMKSA_GET and PMKSA_ADD that can be used to store PMKSA cache entries in an external persistent storage when terminating a wpa_supplicant process and then restore those entries when starting a new process. The previously added PMKSA-CACHE-ADDED/REMOVED events can be used to help in synchronizing the external storage with the memory-only volatile storage within wpa_supplicant. "PMKSA_GET <network_id>" fetches all stored PMKSA cache entries bound to a specific network profile. The network_id of the current profile is available with the STATUS command (id=<network_id). In addition, the network_id is included in the PMKSA-CACHE-ADDED/REMOVED events. The output of the PMKSA_GET command uses the following format: <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> For example: 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30240 43200 1 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30240 43200 1 0 The PMKSA_GET command uses the following format: <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> (i.e., "PMKSA_ADD <network_id> " prefix followed by a line of PMKSA_GET output data; however, the reauth_time and expiration values need to be updated by decrementing them by number of seconds between the PMKSA_GET and PMKSA_ADD commands) For example: PMKSA_ADD 0 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30140 43100 1 0 PMKSA_ADD 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30140 43100 1 0 This functionality is disabled be default and can be enabled with CONFIG_PMKSA_CACHE_EXTERNAL=y build configuration option. It should be noted that this allows any process that has access to the wpa_supplicant control interface to use PMKSA_ADD command to fetch keying material (PMK), so this is for environments in which the control interface access is restricted. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
2016-12-12 22:47:04 +01:00
#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
} else if (os_strncmp(buf, "PMKSA_GET ", 10) == 0) {
reply_len = wpas_ctrl_iface_pmksa_get(wpa_s, buf + 10,
reply, reply_size);
} else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) {
if (wpas_ctrl_iface_pmksa_add(wpa_s, buf + 10) < 0)
reply_len = -1;
#ifdef CONFIG_MESH
} else if (os_strncmp(buf, "MESH_PMKSA_GET ", 15) == 0) {
reply_len = wpas_ctrl_iface_mesh_pmksa_get(wpa_s, buf + 15,
reply, reply_size);
} else if (os_strncmp(buf, "MESH_PMKSA_ADD ", 15) == 0) {
if (wpas_ctrl_iface_mesh_pmksa_add(wpa_s, buf + 15) < 0)
reply_len = -1;
#endif /* CONFIG_MESH */
External persistent storage for PMKSA cache entries This adds new wpa_supplicant control interface commands PMKSA_GET and PMKSA_ADD that can be used to store PMKSA cache entries in an external persistent storage when terminating a wpa_supplicant process and then restore those entries when starting a new process. The previously added PMKSA-CACHE-ADDED/REMOVED events can be used to help in synchronizing the external storage with the memory-only volatile storage within wpa_supplicant. "PMKSA_GET <network_id>" fetches all stored PMKSA cache entries bound to a specific network profile. The network_id of the current profile is available with the STATUS command (id=<network_id). In addition, the network_id is included in the PMKSA-CACHE-ADDED/REMOVED events. The output of the PMKSA_GET command uses the following format: <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> For example: 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30240 43200 1 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30240 43200 1 0 The PMKSA_GET command uses the following format: <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> (i.e., "PMKSA_ADD <network_id> " prefix followed by a line of PMKSA_GET output data; however, the reauth_time and expiration values need to be updated by decrementing them by number of seconds between the PMKSA_GET and PMKSA_ADD commands) For example: PMKSA_ADD 0 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30140 43100 1 0 PMKSA_ADD 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30140 43100 1 0 This functionality is disabled be default and can be enabled with CONFIG_PMKSA_CACHE_EXTERNAL=y build configuration option. It should be noted that this allows any process that has access to the wpa_supplicant control interface to use PMKSA_ADD command to fetch keying material (PMK), so this is for environments in which the control interface access is restricted. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
2016-12-12 22:47:04 +01:00
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
} else if (os_strncmp(buf, "SET ", 4) == 0) {
if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4))
reply_len = -1;
} else if (os_strncmp(buf, "DUMP", 4) == 0) {
reply_len = wpa_config_dump_values(wpa_s->conf,
reply, reply_size);
} else if (os_strncmp(buf, "GET ", 4) == 0) {
reply_len = wpa_supplicant_ctrl_iface_get(wpa_s, buf + 4,
reply, reply_size);
} else if (os_strcmp(buf, "LOGON") == 0) {
eapol_sm_notify_logoff(wpa_s->eapol, false);
} else if (os_strcmp(buf, "LOGOFF") == 0) {
eapol_sm_notify_logoff(wpa_s->eapol, true);
} else if (os_strcmp(buf, "REASSOCIATE") == 0) {
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
reply_len = -1;
else
wpas_request_connection(wpa_s);
} else if (os_strcmp(buf, "REATTACH") == 0) {
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED ||
!wpa_s->current_ssid)
reply_len = -1;
else {
wpa_s->reattach = 1;
wpas_request_connection(wpa_s);
}
} else if (os_strcmp(buf, "RECONNECT") == 0) {
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
reply_len = -1;
else if (wpa_s->disconnected)
wpas_request_connection(wpa_s);
#ifdef IEEE8021X_EAPOL
} else if (os_strncmp(buf, "PREAUTH ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8))
reply_len = -1;
#endif /* IEEE8021X_EAPOL */
#ifdef CONFIG_IEEE80211R
} else if (os_strncmp(buf, "FT_DS ", 6) == 0) {
if (wpa_supplicant_ctrl_iface_ft_ds(wpa_s, buf + 6))
reply_len = -1;
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_WPS
} else if (os_strcmp(buf, "WPS_PBC") == 0) {
int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, NULL);
if (res == -2) {
os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
reply_len = 17;
} else if (res)
reply_len = -1;
} else if (os_strncmp(buf, "WPS_PBC ", 8) == 0) {
int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, buf + 8);
if (res == -2) {
os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
reply_len = 17;
} else if (res)
reply_len = -1;
} else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
reply_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8,
reply,
reply_size);
} else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) {
reply_len = wpa_supplicant_ctrl_iface_wps_check_pin(
wpa_s, buf + 14, reply, reply_size);
} else if (os_strcmp(buf, "WPS_CANCEL") == 0) {
if (wpas_wps_cancel(wpa_s))
reply_len = -1;
#ifdef CONFIG_WPS_NFC
} else if (os_strcmp(buf, "WPS_NFC") == 0) {
if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, NULL))
reply_len = -1;
} else if (os_strncmp(buf, "WPS_NFC ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, buf + 8))
reply_len = -1;
} else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) {
reply_len = wpa_supplicant_ctrl_iface_wps_nfc_config_token(
wpa_s, buf + 21, reply, reply_size);
} else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) {
reply_len = wpa_supplicant_ctrl_iface_wps_nfc_token(
wpa_s, buf + 14, reply, reply_size);
} else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) {
if (wpa_supplicant_ctrl_iface_wps_nfc_tag_read(wpa_s,
buf + 17))
reply_len = -1;
} else if (os_strncmp(buf, "NFC_GET_HANDOVER_REQ ", 21) == 0) {
reply_len = wpas_ctrl_nfc_get_handover_req(
wpa_s, buf + 21, reply, reply_size);
} else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) {
reply_len = wpas_ctrl_nfc_get_handover_sel(
wpa_s, buf + 21, reply, reply_size);
} else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) {
if (wpas_ctrl_nfc_report_handover(wpa_s, buf + 20))
reply_len = -1;
#endif /* CONFIG_WPS_NFC */
} else if (os_strncmp(buf, "WPS_REG ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8))
reply_len = -1;
#ifdef CONFIG_AP
} else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) {
reply_len = wpa_supplicant_ctrl_iface_wps_ap_pin(
wpa_s, buf + 11, reply, reply_size);
#endif /* CONFIG_AP */
#ifdef CONFIG_WPS_ER
} else if (os_strcmp(buf, "WPS_ER_START") == 0) {
if (wpas_wps_er_start(wpa_s, NULL))
reply_len = -1;
} else if (os_strncmp(buf, "WPS_ER_START ", 13) == 0) {
if (wpas_wps_er_start(wpa_s, buf + 13))
reply_len = -1;
} else if (os_strcmp(buf, "WPS_ER_STOP") == 0) {
wpas_wps_er_stop(wpa_s);
} else if (os_strncmp(buf, "WPS_ER_PIN ", 11) == 0) {
if (wpa_supplicant_ctrl_iface_wps_er_pin(wpa_s, buf + 11))
reply_len = -1;
} else if (os_strncmp(buf, "WPS_ER_PBC ", 11) == 0) {
int ret = wpas_wps_er_pbc(wpa_s, buf + 11);
if (ret == -2) {
os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
reply_len = 17;
} else if (ret == -3) {
os_memcpy(reply, "FAIL-UNKNOWN-UUID\n", 18);
reply_len = 18;
} else if (ret == -4) {
os_memcpy(reply, "FAIL-NO-AP-SETTINGS\n", 20);
reply_len = 20;
} else if (ret)
reply_len = -1;
} else if (os_strncmp(buf, "WPS_ER_LEARN ", 13) == 0) {
if (wpa_supplicant_ctrl_iface_wps_er_learn(wpa_s, buf + 13))
reply_len = -1;
} else if (os_strncmp(buf, "WPS_ER_SET_CONFIG ", 18) == 0) {
if (wpa_supplicant_ctrl_iface_wps_er_set_config(wpa_s,
buf + 18))
reply_len = -1;
} else if (os_strncmp(buf, "WPS_ER_CONFIG ", 14) == 0) {
if (wpa_supplicant_ctrl_iface_wps_er_config(wpa_s, buf + 14))
reply_len = -1;
#ifdef CONFIG_WPS_NFC
} else if (os_strncmp(buf, "WPS_ER_NFC_CONFIG_TOKEN ", 24) == 0) {
reply_len = wpa_supplicant_ctrl_iface_wps_er_nfc_config_token(
wpa_s, buf + 24, reply, reply_size);
#endif /* CONFIG_WPS_NFC */
#endif /* CONFIG_WPS_ER */
#endif /* CONFIG_WPS */
#ifdef CONFIG_IBSS_RSN
} else if (os_strncmp(buf, "IBSS_RSN ", 9) == 0) {
if (wpa_supplicant_ctrl_iface_ibss_rsn(wpa_s, buf + 9))
reply_len = -1;
#endif /* CONFIG_IBSS_RSN */
#ifdef CONFIG_MESH
} else if (os_strncmp(buf, "MESH_INTERFACE_ADD ", 19) == 0) {
reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add(
wpa_s, buf + 19, reply, reply_size);
} else if (os_strcmp(buf, "MESH_INTERFACE_ADD") == 0) {
reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add(
wpa_s, "", reply, reply_size);
} else if (os_strncmp(buf, "MESH_GROUP_ADD ", 15) == 0) {
if (wpa_supplicant_ctrl_iface_mesh_group_add(wpa_s, buf + 15))
reply_len = -1;
} else if (os_strncmp(buf, "MESH_GROUP_REMOVE ", 18) == 0) {
if (wpa_supplicant_ctrl_iface_mesh_group_remove(wpa_s,
buf + 18))
reply_len = -1;
} else if (os_strncmp(buf, "MESH_PEER_REMOVE ", 17) == 0) {
if (wpa_supplicant_ctrl_iface_mesh_peer_remove(wpa_s, buf + 17))
reply_len = -1;
} else if (os_strncmp(buf, "MESH_PEER_ADD ", 14) == 0) {
if (wpa_supplicant_ctrl_iface_mesh_peer_add(wpa_s, buf + 14))
reply_len = -1;
} else if (os_strncmp(buf, "MESH_LINK_PROBE ", 16) == 0) {
if (wpa_supplicant_ctrl_iface_mesh_link_probe(wpa_s, buf + 16))
reply_len = -1;
#endif /* CONFIG_MESH */
#ifdef CONFIG_P2P
} else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) {
if (p2p_ctrl_find(wpa_s, buf + 8))
reply_len = -1;
} else if (os_strcmp(buf, "P2P_FIND") == 0) {
if (p2p_ctrl_find(wpa_s, ""))
reply_len = -1;
} else if (os_strcmp(buf, "P2P_STOP_FIND") == 0) {
wpas_p2p_stop_find(wpa_s);
} else if (os_strncmp(buf, "P2P_ASP_PROVISION ", 18) == 0) {
if (p2p_ctrl_asp_provision(wpa_s, buf + 18))
reply_len = -1;
} else if (os_strncmp(buf, "P2P_ASP_PROVISION_RESP ", 23) == 0) {
if (p2p_ctrl_asp_provision_resp(wpa_s, buf + 23))
reply_len = -1;
} else if (os_strncmp(buf, "P2P_CONNECT ", 12) == 0) {
reply_len = p2p_ctrl_connect(wpa_s, buf + 12, reply,
reply_size);
} else if (os_strncmp(buf, "P2P_LISTEN ", 11) == 0) {
if (p2p_ctrl_listen(wpa_s, buf + 11))
reply_len = -1;
} else if (os_strcmp(buf, "P2P_LISTEN") == 0) {
if (p2p_ctrl_listen(wpa_s, ""))
reply_len = -1;
} else if (os_strncmp(buf, "P2P_GROUP_REMOVE ", 17) == 0) {
if (wpas_p2p_group_remove(wpa_s, buf + 17))
reply_len = -1;
} else if (os_strcmp(buf, "P2P_GROUP_ADD") == 0) {
if (p2p_ctrl_group_add(wpa_s, ""))
reply_len = -1;
} else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) {
if (p2p_ctrl_group_add(wpa_s, buf + 14))
reply_len = -1;
} else if (os_strncmp(buf, "P2P_GROUP_MEMBER ", 17) == 0) {
reply_len = p2p_ctrl_group_member(wpa_s, buf + 17, reply,
reply_size);
} else if (os_strncmp(buf, "P2P_PROV_DISC ", 14) == 0) {
if (p2p_ctrl_prov_disc(wpa_s, buf + 14))
reply_len = -1;
} else if (os_strcmp(buf, "P2P_GET_PASSPHRASE") == 0) {
reply_len = p2p_get_passphrase(wpa_s, reply, reply_size);
} else if (os_strncmp(buf, "P2P_SERV_DISC_REQ ", 18) == 0) {
reply_len = p2p_ctrl_serv_disc_req(wpa_s, buf + 18, reply,
reply_size);
} else if (os_strncmp(buf, "P2P_SERV_DISC_CANCEL_REQ ", 25) == 0) {
if (p2p_ctrl_serv_disc_cancel_req(wpa_s, buf + 25) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "P2P_SERV_DISC_RESP ", 19) == 0) {
if (p2p_ctrl_serv_disc_resp(wpa_s, buf + 19) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "P2P_SERVICE_UPDATE") == 0) {
wpas_p2p_sd_service_update(wpa_s);
} else if (os_strncmp(buf, "P2P_SERV_DISC_EXTERNAL ", 23) == 0) {
if (p2p_ctrl_serv_disc_external(wpa_s, buf + 23) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "P2P_SERVICE_FLUSH") == 0) {
wpas_p2p_service_flush(wpa_s);
} else if (os_strncmp(buf, "P2P_SERVICE_ADD ", 16) == 0) {
if (p2p_ctrl_service_add(wpa_s, buf + 16) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "P2P_SERVICE_DEL ", 16) == 0) {
if (p2p_ctrl_service_del(wpa_s, buf + 16) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "P2P_SERVICE_REP ", 16) == 0) {
if (p2p_ctrl_service_replace(wpa_s, buf + 16) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "P2P_REJECT ", 11) == 0) {
if (p2p_ctrl_reject(wpa_s, buf + 11) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "P2P_INVITE ", 11) == 0) {
if (p2p_ctrl_invite(wpa_s, buf + 11) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "P2P_PEER ", 9) == 0) {
reply_len = p2p_ctrl_peer(wpa_s, buf + 9, reply,
reply_size);
} else if (os_strncmp(buf, "P2P_SET ", 8) == 0) {
if (p2p_ctrl_set(wpa_s, buf + 8) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "P2P_FLUSH") == 0) {
p2p_ctrl_flush(wpa_s);
} else if (os_strncmp(buf, "P2P_UNAUTHORIZE ", 16) == 0) {
if (wpas_p2p_unauthorize(wpa_s, buf + 16) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "P2P_CANCEL") == 0) {
if (wpas_p2p_cancel(wpa_s))
reply_len = -1;
} else if (os_strncmp(buf, "P2P_PRESENCE_REQ ", 17) == 0) {
if (p2p_ctrl_presence_req(wpa_s, buf + 17) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "P2P_PRESENCE_REQ") == 0) {
if (p2p_ctrl_presence_req(wpa_s, "") < 0)
reply_len = -1;
} else if (os_strncmp(buf, "P2P_EXT_LISTEN ", 15) == 0) {
if (p2p_ctrl_ext_listen(wpa_s, buf + 15) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "P2P_EXT_LISTEN") == 0) {
if (p2p_ctrl_ext_listen(wpa_s, "") < 0)
reply_len = -1;
} else if (os_strncmp(buf, "P2P_REMOVE_CLIENT ", 18) == 0) {
if (p2p_ctrl_remove_client(wpa_s, buf + 18) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "P2P_LO_START ", 13) == 0) {
if (p2p_ctrl_iface_p2p_lo_start(wpa_s, buf + 13))
reply_len = -1;
} else if (os_strcmp(buf, "P2P_LO_STOP") == 0) {
if (wpas_p2p_lo_stop(wpa_s))
reply_len = -1;
#endif /* CONFIG_P2P */
#ifdef CONFIG_WIFI_DISPLAY
} else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) {
if (wifi_display_subelem_set(wpa_s->global, buf + 16) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0) {
reply_len = wifi_display_subelem_get(wpa_s->global, buf + 16,
reply, reply_size);
#endif /* CONFIG_WIFI_DISPLAY */
#ifdef CONFIG_INTERWORKING
} else if (os_strcmp(buf, "FETCH_ANQP") == 0) {
if (interworking_fetch_anqp(wpa_s) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "STOP_FETCH_ANQP") == 0) {
interworking_stop_fetch_anqp(wpa_s);
} else if (os_strcmp(buf, "INTERWORKING_SELECT") == 0) {
if (ctrl_interworking_select(wpa_s, NULL) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "INTERWORKING_SELECT ", 20) == 0) {
if (ctrl_interworking_select(wpa_s, buf + 20) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "INTERWORKING_CONNECT ", 21) == 0) {
if (ctrl_interworking_connect(wpa_s, buf + 21, 0) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "INTERWORKING_ADD_NETWORK ", 25) == 0) {
int id;
id = ctrl_interworking_connect(wpa_s, buf + 25, 1);
if (id < 0)
reply_len = -1;
else {
reply_len = os_snprintf(reply, reply_size, "%d\n", id);
if (os_snprintf_error(reply_size, reply_len))
reply_len = -1;
}
} else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) {
if (get_anqp(wpa_s, buf + 9) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "GAS_REQUEST ", 12) == 0) {
if (gas_request(wpa_s, buf + 12) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "GAS_RESPONSE_GET ", 17) == 0) {
reply_len = gas_response_get(wpa_s, buf + 17, reply,
reply_size);
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_HS20
} else if (os_strncmp(buf, "HS20_ANQP_GET ", 14) == 0) {
if (get_hs20_anqp(wpa_s, buf + 14) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "HS20_GET_NAI_HOME_REALM_LIST ", 29) == 0) {
if (hs20_get_nai_home_realm_list(wpa_s, buf + 29) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "HS20_ICON_REQUEST ", 18) == 0) {
if (hs20_icon_request(wpa_s, buf + 18, 0) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "REQ_HS20_ICON ", 14) == 0) {
if (hs20_icon_request(wpa_s, buf + 14, 1) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "GET_HS20_ICON ", 14) == 0) {
reply_len = get_hs20_icon(wpa_s, buf + 14, reply, reply_size);
} else if (os_strncmp(buf, "DEL_HS20_ICON ", 14) == 0) {
if (del_hs20_icon(wpa_s, buf + 14) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "FETCH_OSU") == 0) {
if (hs20_fetch_osu(wpa_s, 0) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "FETCH_OSU no-scan") == 0) {
if (hs20_fetch_osu(wpa_s, 1) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "CANCEL_FETCH_OSU") == 0) {
hs20_cancel_fetch_osu(wpa_s);
#endif /* CONFIG_HS20 */
} else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0)
{
if (wpa_supplicant_ctrl_iface_ctrl_rsp(
wpa_s, buf + os_strlen(WPA_CTRL_RSP)))
reply_len = -1;
else {
/*
* Notify response from timeout to allow the control
* interface response to be sent first.
*/
eloop_register_timeout(0, 0, wpas_ctrl_eapol_response,
wpa_s, NULL);
}
} else if (os_strcmp(buf, "RECONFIGURE") == 0) {
if (wpa_supplicant_reload_configuration(wpa_s))
reply_len = -1;
} else if (os_strcmp(buf, "TERMINATE") == 0) {
wpa_supplicant_terminate_proc(wpa_s->global);
} else if (os_strncmp(buf, "BSSID ", 6) == 0) {
if (wpa_supplicant_ctrl_iface_bssid(wpa_s, buf + 6))
reply_len = -1;
} else if (os_strncmp(buf, "BSSID_IGNORE", 12) == 0) {
reply_len = wpa_supplicant_ctrl_iface_bssid_ignore(
wpa_s, buf + 12, reply, reply_size);
} else if (os_strncmp(buf, "BLACKLIST", 9) == 0) {
/* deprecated backwards compatibility alias for BSSID_IGNORE */
reply_len = wpa_supplicant_ctrl_iface_bssid_ignore(
wpa_s, buf + 9, reply, reply_size);
} else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) {
reply_len = wpa_supplicant_ctrl_iface_log_level(
wpa_s, buf + 9, reply, reply_size);
} else if (os_strncmp(buf, "LIST_NETWORKS ", 14) == 0) {
reply_len = wpa_supplicant_ctrl_iface_list_networks(
wpa_s, buf + 14, reply, reply_size);
} else if (os_strcmp(buf, "LIST_NETWORKS") == 0) {
reply_len = wpa_supplicant_ctrl_iface_list_networks(
wpa_s, NULL, reply, reply_size);
} else if (os_strcmp(buf, "DISCONNECT") == 0) {
wpas_request_disconnection(wpa_s);
} else if (os_strcmp(buf, "SCAN") == 0) {
wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, &reply_len);
} else if (os_strncmp(buf, "SCAN ", 5) == 0) {
wpas_ctrl_scan(wpa_s, buf + 5, reply, reply_size, &reply_len);
} else if (os_strcmp(buf, "SCAN_RESULTS") == 0) {
reply_len = wpa_supplicant_ctrl_iface_scan_results(
wpa_s, reply, reply_size);
} else if (os_strcmp(buf, "ABORT_SCAN") == 0) {
if (wpas_abort_ongoing_scan(wpa_s) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "SELECT_NETWORK ", 15) == 0) {
if (wpa_supplicant_ctrl_iface_select_network(wpa_s, buf + 15))
reply_len = -1;
} else if (os_strncmp(buf, "ENABLE_NETWORK ", 15) == 0) {
if (wpa_supplicant_ctrl_iface_enable_network(wpa_s, buf + 15))
reply_len = -1;
} else if (os_strncmp(buf, "DISABLE_NETWORK ", 16) == 0) {
if (wpa_supplicant_ctrl_iface_disable_network(wpa_s, buf + 16))
reply_len = -1;
} else if (os_strcmp(buf, "ADD_NETWORK") == 0) {
reply_len = wpa_supplicant_ctrl_iface_add_network(
wpa_s, reply, reply_size);
} else if (os_strncmp(buf, "REMOVE_NETWORK ", 15) == 0) {
if (wpa_supplicant_ctrl_iface_remove_network(wpa_s, buf + 15))
reply_len = -1;
} else if (os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
if (wpa_supplicant_ctrl_iface_set_network(wpa_s, buf + 12))
reply_len = -1;
} else if (os_strncmp(buf, "GET_NETWORK ", 12) == 0) {
reply_len = wpa_supplicant_ctrl_iface_get_network(
wpa_s, buf + 12, reply, reply_size);
} else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12,
wpa_s))
reply_len = -1;
} else if (os_strcmp(buf, "LIST_CREDS") == 0) {
reply_len = wpa_supplicant_ctrl_iface_list_creds(
wpa_s, reply, reply_size);
} else if (os_strcmp(buf, "ADD_CRED") == 0) {
reply_len = wpa_supplicant_ctrl_iface_add_cred(
wpa_s, reply, reply_size);
} else if (os_strncmp(buf, "REMOVE_CRED ", 12) == 0) {
if (wpa_supplicant_ctrl_iface_remove_cred(wpa_s, buf + 12))
reply_len = -1;
} else if (os_strncmp(buf, "SET_CRED ", 9) == 0) {
if (wpa_supplicant_ctrl_iface_set_cred(wpa_s, buf + 9))
reply_len = -1;
} else if (os_strncmp(buf, "GET_CRED ", 9) == 0) {
reply_len = wpa_supplicant_ctrl_iface_get_cred(wpa_s, buf + 9,
reply,
reply_size);
#ifndef CONFIG_NO_CONFIG_WRITE
} else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
if (wpa_supplicant_ctrl_iface_save_config(wpa_s))
reply_len = -1;
#endif /* CONFIG_NO_CONFIG_WRITE */
} else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) {
reply_len = wpa_supplicant_ctrl_iface_get_capability(
wpa_s, buf + 15, reply, reply_size);
} else if (os_strncmp(buf, "AP_SCAN ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_ap_scan(wpa_s, buf + 8))
reply_len = -1;
} else if (os_strncmp(buf, "SCAN_INTERVAL ", 14) == 0) {
if (wpa_supplicant_ctrl_iface_scan_interval(wpa_s, buf + 14))
reply_len = -1;
} else if (os_strcmp(buf, "INTERFACE_LIST") == 0) {
reply_len = wpa_supplicant_global_iface_list(
wpa_s->global, reply, reply_size);
} else if (os_strncmp(buf, "INTERFACES", 10) == 0) {
reply_len = wpa_supplicant_global_iface_interfaces(
wpa_s->global, buf + 10, reply, reply_size);
} else if (os_strncmp(buf, "BSS ", 4) == 0) {
reply_len = wpa_supplicant_ctrl_iface_bss(
wpa_s, buf + 4, reply, reply_size);
#ifdef CONFIG_AP
} else if (os_strcmp(buf, "STA-FIRST") == 0) {
reply_len = ap_ctrl_iface_sta_first(wpa_s, reply, reply_size);
} else if (os_strncmp(buf, "STA ", 4) == 0) {
reply_len = ap_ctrl_iface_sta(wpa_s, buf + 4, reply,
reply_size);
} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply,
reply_size);
} else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
if (ap_ctrl_iface_sta_deauthenticate(wpa_s, buf + 15))
reply_len = -1;
} else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13))
reply_len = -1;
} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
if (ap_ctrl_iface_chanswitch(wpa_s, buf + 12))
reply_len = -1;
} else if (os_strcmp(buf, "STOP_AP") == 0) {
if (wpas_ap_stop_ap(wpa_s))
reply_len = -1;
} else if (os_strcmp(buf, "UPDATE_BEACON") == 0) {
if (wpas_ap_update_beacon(wpa_s))
reply_len = -1;
} else if (os_strncmp(buf, "ACCEPT_ACL ", 11) == 0) {
if (os_strncmp(buf + 11, "ADD_MAC ", 8) == 0) {
if (ap_ctrl_iface_acl_add_mac(wpa_s,
DENY_UNLESS_ACCEPTED,
buf + 19) ||
ap_ctrl_iface_set_acl(wpa_s))
reply_len = -1;
} else if (os_strncmp((buf + 11), "DEL_MAC ", 8) == 0) {
if (ap_ctrl_iface_acl_del_mac(wpa_s,
DENY_UNLESS_ACCEPTED,
buf + 19) ||
ap_ctrl_iface_set_acl(wpa_s) ||
ap_ctrl_iface_disassoc_accept_mac(wpa_s))
reply_len = -1;
} else if (os_strcmp(buf + 11, "SHOW") == 0) {
reply_len = ap_ctrl_iface_acl_show_mac(
wpa_s, DENY_UNLESS_ACCEPTED,
reply, reply_size);
} else if (os_strcmp(buf + 11, "CLEAR") == 0) {
ap_ctrl_iface_acl_clear_list(wpa_s,
DENY_UNLESS_ACCEPTED);
if (ap_ctrl_iface_set_acl(wpa_s) ||
ap_ctrl_iface_disassoc_accept_mac(wpa_s))
reply_len = -1;
} else {
reply_len = -1;
}
} else if (os_strncmp(buf, "DENY_ACL ", 9) == 0) {
if (os_strncmp(buf + 9, "ADD_MAC ", 8) == 0) {
if (ap_ctrl_iface_acl_add_mac(wpa_s,
ACCEPT_UNLESS_DENIED,
buf + 17) ||
ap_ctrl_iface_set_acl(wpa_s) ||
ap_ctrl_iface_disassoc_deny_mac(wpa_s))
reply_len = -1;
} else if (os_strncmp(buf + 9, "DEL_MAC ", 8) == 0) {
if (ap_ctrl_iface_acl_del_mac(wpa_s,
ACCEPT_UNLESS_DENIED,
buf + 17) ||
ap_ctrl_iface_set_acl(wpa_s))
reply_len = -1;
} else if (os_strcmp(buf + 9, "SHOW") == 0) {
reply_len = ap_ctrl_iface_acl_show_mac(
wpa_s, ACCEPT_UNLESS_DENIED, reply, reply_size);
} else if (os_strcmp(buf + 9, "CLEAR") == 0) {
ap_ctrl_iface_acl_clear_list(wpa_s,
ACCEPT_UNLESS_DENIED);
if (ap_ctrl_iface_set_acl(wpa_s))
reply_len = -1;
} else {
reply_len = -1;
}
#endif /* CONFIG_AP */
} else if (os_strcmp(buf, "SUSPEND") == 0) {
wpas_notify_suspend(wpa_s->global);
} else if (os_strcmp(buf, "RESUME") == 0) {
wpas_notify_resume(wpa_s->global);
#ifdef CONFIG_TESTING_OPTIONS
} else if (os_strcmp(buf, "DROP_SA") == 0) {
wpa_supplicant_ctrl_iface_drop_sa(wpa_s);
#endif /* CONFIG_TESTING_OPTIONS */
} else if (os_strncmp(buf, "ROAM ", 5) == 0) {
if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5))
reply_len = -1;
} else if (os_strncmp(buf, "STA_AUTOCONNECT ", 16) == 0) {
wpa_s->auto_reconnect_disabled = atoi(buf + 16) == 0;
} else if (os_strncmp(buf, "BSS_EXPIRE_AGE ", 15) == 0) {
if (wpa_supplicant_ctrl_iface_bss_expire_age(wpa_s, buf + 15))
reply_len = -1;
} else if (os_strncmp(buf, "BSS_EXPIRE_COUNT ", 17) == 0) {
if (wpa_supplicant_ctrl_iface_bss_expire_count(wpa_s,
buf + 17))
reply_len = -1;
} else if (os_strncmp(buf, "BSS_FLUSH ", 10) == 0) {
wpa_supplicant_ctrl_iface_bss_flush(wpa_s, buf + 10);
#ifdef CONFIG_TDLS
} else if (os_strncmp(buf, "TDLS_DISCOVER ", 14) == 0) {
if (wpa_supplicant_ctrl_iface_tdls_discover(wpa_s, buf + 14))
reply_len = -1;
} else if (os_strncmp(buf, "TDLS_SETUP ", 11) == 0) {
if (wpa_supplicant_ctrl_iface_tdls_setup(wpa_s, buf + 11))
reply_len = -1;
} else if (os_strncmp(buf, "TDLS_TEARDOWN ", 14) == 0) {
if (wpa_supplicant_ctrl_iface_tdls_teardown(wpa_s, buf + 14))
reply_len = -1;
} else if (os_strncmp(buf, "TDLS_CHAN_SWITCH ", 17) == 0) {
if (wpa_supplicant_ctrl_iface_tdls_chan_switch(wpa_s,
buf + 17))
reply_len = -1;
} else if (os_strncmp(buf, "TDLS_CANCEL_CHAN_SWITCH ", 24) == 0) {
if (wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(wpa_s,
buf + 24))
reply_len = -1;
} else if (os_strncmp(buf, "TDLS_LINK_STATUS ", 17) == 0) {
reply_len = wpa_supplicant_ctrl_iface_tdls_link_status(
wpa_s, buf + 17, reply, reply_size);
#endif /* CONFIG_TDLS */
#ifndef CONFIG_NO_WMM_AC
} else if (os_strcmp(buf, "WMM_AC_STATUS") == 0) {
reply_len = wpas_wmm_ac_status(wpa_s, reply, reply_size);
} else if (os_strncmp(buf, "WMM_AC_ADDTS ", 13) == 0) {
if (wmm_ac_ctrl_addts(wpa_s, buf + 13))
reply_len = -1;
} else if (os_strncmp(buf, "WMM_AC_DELTS ", 13) == 0) {
if (wmm_ac_ctrl_delts(wpa_s, buf + 13))
reply_len = -1;
#endif /* CONFIG_NO_WMM_AC */
} else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) {
reply_len = wpa_supplicant_signal_poll(wpa_s, reply,
reply_size);
} else if (os_strncmp(buf, "SIGNAL_MONITOR", 14) == 0) {
if (wpas_ctrl_iface_signal_monitor(wpa_s, buf + 14))
reply_len = -1;
} else if (os_strncmp(buf, "PKTCNT_POLL", 11) == 0) {
reply_len = wpa_supplicant_pktcnt_poll(wpa_s, reply,
reply_size);
#ifdef CONFIG_AUTOSCAN
} else if (os_strncmp(buf, "AUTOSCAN ", 9) == 0) {
if (wpa_supplicant_ctrl_iface_autoscan(wpa_s, buf + 9))
reply_len = -1;
#endif /* CONFIG_AUTOSCAN */
} else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) {
reply_len = wpas_ctrl_iface_driver_flags(wpa_s, reply,
reply_size);
} else if (os_strcmp(buf, "DRIVER_FLAGS2") == 0) {
reply_len = wpas_ctrl_iface_driver_flags2(wpa_s, reply,
reply_size);
#ifdef ANDROID
} else if (os_strncmp(buf, "DRIVER ", 7) == 0) {
reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, reply,
reply_size);
#endif /* ANDROID */
} else if (os_strncmp(buf, "VENDOR ", 7) == 0) {
reply_len = wpa_supplicant_vendor_cmd(wpa_s, buf + 7, reply,
reply_size);
} else if (os_strcmp(buf, "REAUTHENTICATE") == 0) {
pmksa_cache_clear_current(wpa_s->wpa);
eapol_sm_request_reauth(wpa_s->eapol);
#ifdef CONFIG_WNM
} else if (os_strncmp(buf, "WNM_SLEEP ", 10) == 0) {
if (wpas_ctrl_iface_wnm_sleep(wpa_s, buf + 10))
reply_len = -1;
} else if (os_strncmp(buf, "WNM_BSS_QUERY ", 14) == 0) {
if (wpas_ctrl_iface_wnm_bss_query(wpa_s, buf + 14))
reply_len = -1;
WNM: Collocated Interference Reporting Add support for negotiating WNM Collocated Interference Reporting. This allows hostapd to request associated STAs to report their collocated interference information and wpa_supplicant to process such request and reporting. The actual values (Collocated Interference Report Elements) are out of scope of hostapd and wpa_supplicant, i.e., external components are expected to generated and process these. For hostapd/AP, this mechanism is enabled by setting coloc_intf_reporting=1 in configuration. STAs are requested to perform reporting with "COLOC_INTF_REQ <addr> <Automatic Report Enabled> <Report Timeout>" control interface command. The received reports are indicated as control interface events "COLOC-INTF-REPORT <addr> <dialog token> <hexdump of report elements>". For wpa_supplicant/STA, this mechanism is enabled by setting coloc_intf_reporting=1 in configuration and setting Collocated Interference Report Elements as a hexdump with "SET coloc_intf_elems <hexdump>" control interface command. The hexdump can contain one or more Collocated Interference Report Elements (each including the information element header). For additional testing purposes, received requests are reported with "COLOC-INTF-REQ <dialog token> <automatic report enabled> <report timeout>" control interface events and unsolicited reports can be sent with "COLOC_INTF_REPORT <hexdump>". This commit adds support for reporting changes in the collocated interference (Automatic Report Enabled == 1 and partial 3), but not for periodic reports (2 and other part of 3). Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
2018-10-30 13:00:00 +01:00
} else if (os_strncmp(buf, "COLOC_INTF_REPORT ", 18) == 0) {
if (wpas_ctrl_iface_coloc_intf_report(wpa_s, buf + 18))
reply_len = -1;
#endif /* CONFIG_WNM */
#ifdef CONFIG_WNM_AP
} else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
if (ap_ctrl_iface_disassoc_imminent(wpa_s, buf + 18))
reply_len = -1;
} else if (os_strncmp(buf, "ESS_DISASSOC ", 13) == 0) {
if (ap_ctrl_iface_ess_disassoc(wpa_s, buf + 13))
reply_len = -1;
} else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
if (ap_ctrl_iface_bss_tm_req(wpa_s, buf + 11))
reply_len = -1;
#endif /* CONFIG_WNM_AP */
} else if (os_strcmp(buf, "FLUSH") == 0) {
wpa_supplicant_ctrl_iface_flush(wpa_s);
} else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) {
reply_len = wpas_ctrl_radio_work(wpa_s, buf + 11, reply,
reply_size);
#ifdef CONFIG_TESTING_OPTIONS
} else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) {
if (wpas_ctrl_iface_mgmt_tx(wpa_s, buf + 8) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "MGMT_TX_DONE") == 0) {
wpas_ctrl_iface_mgmt_tx_done(wpa_s);
} else if (os_strncmp(buf, "MGMT_RX_PROCESS ", 16) == 0) {
if (wpas_ctrl_iface_mgmt_rx_process(wpa_s, buf + 16) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DRIVER_EVENT ", 13) == 0) {
if (wpas_ctrl_iface_driver_event(wpa_s, buf + 13) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) {
if (wpas_ctrl_iface_eapol_rx(wpa_s, buf + 9) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "EAPOL_TX ", 9) == 0) {
if (wpas_ctrl_iface_eapol_tx(wpa_s, buf + 9) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DATA_TEST_CONFIG ", 17) == 0) {
if (wpas_ctrl_iface_data_test_config(wpa_s, buf + 17) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DATA_TEST_TX ", 13) == 0) {
if (wpas_ctrl_iface_data_test_tx(wpa_s, buf + 13) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) {
if (wpas_ctrl_iface_data_test_frame(wpa_s, buf + 16) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) {
if (testing_set_fail_pattern(true, buf + 16) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
reply_len = testing_get_fail_pattern(true, reply, reply_size);
} else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
if (testing_set_fail_pattern(false, buf + 10) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "GET_FAIL") == 0) {
reply_len = testing_get_fail_pattern(false, reply, reply_size);
} else if (os_strncmp(buf, "EVENT_TEST ", 11) == 0) {
if (wpas_ctrl_event_test(wpa_s, buf + 11) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "TEST_ASSOC_IE ", 14) == 0) {
if (wpas_ctrl_test_assoc_ie(wpa_s, buf + 14) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "TEST_EAPOL_M2_ELEMS ", 20) == 0) {
if (wpas_ctrl_test_eapol_m2_elems(wpa_s, buf + 20) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "TEST_EAPOL_M4_ELEMS ", 20) == 0) {
if (wpas_ctrl_test_eapol_m4_elems(wpa_s, buf + 20) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "RESET_PN") == 0) {
if (wpas_ctrl_reset_pn(wpa_s) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "KEY_REQUEST ", 12) == 0) {
if (wpas_ctrl_key_request(wpa_s, buf + 12) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "RESEND_ASSOC") == 0) {
if (wpas_ctrl_resend_assoc(wpa_s) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "UNPROT_DEAUTH") == 0) {
sme_event_unprot_disconnect(
wpa_s, wpa_s->bssid, NULL,
WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
} else if (os_strncmp(buf, "TWT_SETUP ", 10) == 0) {
if (wpas_ctrl_iface_send_twt_setup(wpa_s, buf + 9))
reply_len = -1;
} else if (os_strcmp(buf, "TWT_SETUP") == 0) {
if (wpas_ctrl_iface_send_twt_setup(wpa_s, ""))
reply_len = -1;
} else if (os_strncmp(buf, "TWT_TEARDOWN ", 13) == 0) {
if (wpas_ctrl_iface_send_twt_teardown(wpa_s, buf + 12))
reply_len = -1;
} else if (os_strcmp(buf, "TWT_TEARDOWN") == 0) {
if (wpas_ctrl_iface_send_twt_teardown(wpa_s, ""))
reply_len = -1;
} else if (os_strncmp(buf, "ML_PROBE_REQ ", 13) == 0) {
if (wpas_ctrl_ml_probe(wpa_s, buf + 13))
reply_len = -1;
#endif /* CONFIG_TESTING_OPTIONS */
} else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) {
if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "VENDOR_ELEM_GET ", 16) == 0) {
reply_len = wpas_ctrl_vendor_elem_get(wpa_s, buf + 16, reply,
reply_size);
} else if (os_strncmp(buf, "VENDOR_ELEM_REMOVE ", 19) == 0) {
if (wpas_ctrl_vendor_elem_remove(wpa_s, buf + 19) < 0)
reply_len = -1;
#ifndef CONFIG_NO_RRM
} else if (os_strncmp(buf, "NEIGHBOR_REP_REQUEST", 20) == 0) {
if (wpas_ctrl_iface_send_neighbor_rep(wpa_s, buf + 20))
reply_len = -1;
#endif /* CONFIG_NO_RRM */
} else if (os_strcmp(buf, "ERP_FLUSH") == 0) {
wpas_ctrl_iface_erp_flush(wpa_s);
} else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) {
if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14))
reply_len = -1;
} else if (os_strncmp(buf, "GET_PREF_FREQ_LIST ", 19) == 0) {
reply_len = wpas_ctrl_iface_get_pref_freq_list(
wpa_s, buf + 19, reply, reply_size);
#ifdef CONFIG_FILS
} else if (os_strncmp(buf, "FILS_HLP_REQ_ADD ", 17) == 0) {
if (wpas_ctrl_iface_fils_hlp_req_add(wpa_s, buf + 17))
reply_len = -1;
} else if (os_strcmp(buf, "FILS_HLP_REQ_FLUSH") == 0) {
wpas_flush_fils_hlp_req(wpa_s);
#endif /* CONFIG_FILS */
#ifdef CONFIG_DPP
} else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) {
int res;
res = wpas_dpp_qr_code(wpa_s, buf + 12);
if (res < 0) {
reply_len = -1;
} else {
reply_len = os_snprintf(reply, reply_size, "%d", res);
if (os_snprintf_error(reply_size, reply_len))
reply_len = -1;
}
} else if (os_strncmp(buf, "DPP_NFC_URI ", 12) == 0) {
int res;
res = wpas_dpp_nfc_uri(wpa_s, buf + 12);
if (res < 0) {
reply_len = -1;
} else {
reply_len = os_snprintf(reply, reply_size, "%d", res);
if (os_snprintf_error(reply_size, reply_len))
reply_len = -1;
}
} else if (os_strncmp(buf, "DPP_NFC_HANDOVER_REQ ", 21) == 0) {
int res;
res = wpas_dpp_nfc_handover_req(wpa_s, buf + 20);
if (res < 0) {
reply_len = -1;
} else {
reply_len = os_snprintf(reply, reply_size, "%d", res);
if (os_snprintf_error(reply_size, reply_len))
reply_len = -1;
}
} else if (os_strncmp(buf, "DPP_NFC_HANDOVER_SEL ", 21) == 0) {
int res;
res = wpas_dpp_nfc_handover_sel(wpa_s, buf + 20);
if (res < 0) {
reply_len = -1;
} else {
reply_len = os_snprintf(reply, reply_size, "%d", res);
if (os_snprintf_error(reply_size, reply_len))
reply_len = -1;
}
} else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) {
int res;
res = dpp_bootstrap_gen(wpa_s->dpp, buf + 18);
if (res < 0) {
reply_len = -1;
} else {
reply_len = os_snprintf(reply, reply_size, "%d", res);
if (os_snprintf_error(reply_size, reply_len))
reply_len = -1;
}
} else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) {
if (dpp_bootstrap_remove(wpa_s->dpp, buf + 21) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) {
const char *uri;
uri = dpp_bootstrap_get_uri(wpa_s->dpp, atoi(buf + 22));
if (!uri) {
reply_len = -1;
} else {
reply_len = os_snprintf(reply, reply_size, "%s", uri);
if (os_snprintf_error(reply_size, reply_len))
reply_len = -1;
}
} else if (os_strncmp(buf, "DPP_BOOTSTRAP_INFO ", 19) == 0) {
reply_len = dpp_bootstrap_info(wpa_s->dpp, atoi(buf + 19),
reply, reply_size);
} else if (os_strncmp(buf, "DPP_BOOTSTRAP_SET ", 18) == 0) {
if (dpp_bootstrap_set(wpa_s->dpp, atoi(buf + 18),
os_strchr(buf + 18, ' ')) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) {
if (wpas_dpp_auth_init(wpa_s, buf + 13) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DPP_LISTEN ", 11) == 0) {
if (wpas_dpp_listen(wpa_s, buf + 11) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "DPP_STOP_LISTEN") == 0) {
wpas_dpp_stop(wpa_s);
wpas_dpp_listen_stop(wpa_s);
} else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) {
int res;
res = dpp_configurator_add(wpa_s->dpp, buf + 20);
if (res < 0) {
reply_len = -1;
} else {
reply_len = os_snprintf(reply, reply_size, "%d", res);
if (os_snprintf_error(reply_size, reply_len))
reply_len = -1;
}
} else if (os_strncmp(buf, "DPP_CONFIGURATOR_SET ", 21) == 0) {
if (dpp_configurator_set(wpa_s->dpp, buf + 20) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) {
if (dpp_configurator_remove(wpa_s->dpp, buf + 24) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DPP_CONFIGURATOR_SIGN ", 22) == 0) {
if (wpas_dpp_configurator_sign(wpa_s, buf + 21) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DPP_CONFIGURATOR_GET_KEY ", 25) == 0) {
reply_len = dpp_configurator_get_key_id(wpa_s->dpp,
atoi(buf + 25),
reply, reply_size);
} else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) {
int res;
res = wpas_dpp_pkex_add(wpa_s, buf + 12);
if (res < 0) {
reply_len = -1;
} else {
reply_len = os_snprintf(reply, reply_size, "%d", res);
if (os_snprintf_error(reply_size, reply_len))
reply_len = -1;
}
} else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) {
if (wpas_dpp_pkex_remove(wpa_s, buf + 16) < 0)
reply_len = -1;
DPP: Allow Configurator parameters to be provided during config exchange This provides an alternative mechanism for upper layer components to control configuration parameters to be used by the local Configurator. Instead of the previously used design where the Configurator parameters had to be provided before initiating the DPP Authentication exchange, the new alternative approach allows the DPP Authentication exchange to be started before any Configurator parameters have been determined and wpa_supplicant will then request the parameters once the DPP Configuration Request has been received from the Enrollee. This allows the Config Request information to be used at upper layers to determine how the Enrollee should be configured. For example for an Initiator: CTRL: DPP_QR_CODE <URI from Responder/Enrollee> CTRL: DPP_AUTH_INIT peer=1 conf=query <3>DPP-CONF-NEEDED peer=1 src=02:00:00:00:00:00 net_role=sta name="Test" opclass=81,82,83,84,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130 mud_url=N/A (upper layer processing; potentially including user interaction) CTRL: DPP_CONF_SET peer=1 conf=sta-sae ssid=736165 pass=70617373776f7264 <3>DPP-CONF-SENT For example for a Responder: CTRL: SET dpp_configurator_params conf=query CTRL: DPP_LISTEN 2412 role=configurator <3>DPP-CONF-NEEDED peer=2 src=02:00:00:00:01:00 net_role=sta name="Test" opclass=81,82,83,84,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130 mud_url=N/A (upper layer processing; potentially including user interaction) CTRL: DPP_CONF_SET peer=2 conf=sta-sae ssid=736165 pass=70617373776f7264 <3>DPP-CONF-SENT For example for an Initiator that can act both as a Configurator and an Enrollee in a case where the Initiator becomes the Enrollee: CTRL: DPP_AUTH_INIT peer=1 role=either conf=query <3>DPP-CONF-RECEIVED Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>
2022-02-02 15:52:01 +01:00
} else if (os_strncmp(buf, "DPP_CONF_SET ", 13) == 0) {
if (wpas_dpp_conf_set(wpa_s, buf + 12) < 0)
reply_len = -1;
#ifdef CONFIG_DPP2
} else if (os_strncmp(buf, "DPP_CONTROLLER_START ", 21) == 0) {
if (wpas_dpp_controller_start(wpa_s, buf + 20) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "DPP_CONTROLLER_START") == 0) {
if (wpas_dpp_controller_start(wpa_s, NULL) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "DPP_CONTROLLER_STOP") == 0) {
dpp_controller_stop(wpa_s->dpp);
} else if (os_strncmp(buf, "DPP_CHIRP ", 10) == 0) {
if (wpas_dpp_chirp(wpa_s, buf + 9) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "DPP_STOP_CHIRP") == 0) {
wpas_dpp_chirp_stop(wpa_s);
} else if (os_strncmp(buf, "DPP_RECONFIG ", 13) == 0) {
if (wpas_dpp_reconfig(wpa_s, buf + 13) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DPP_CA_SET ", 11) == 0) {
if (wpas_dpp_ca_set(wpa_s, buf + 10) < 0)
reply_len = -1;
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_DPP3
} else if (os_strcmp(buf, "DPP_PUSH_BUTTON") == 0) {
if (wpas_dpp_push_button(wpa_s, NULL) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DPP_PUSH_BUTTON ", 16) == 0) {
if (wpas_dpp_push_button(wpa_s, buf + 15) < 0)
reply_len = -1;
#endif /* CONFIG_DPP3 */
#endif /* CONFIG_DPP */
#ifdef CONFIG_NAN_USD
} else if (os_strncmp(buf, "NAN_PUBLISH ", 12) == 0) {
reply_len = wpas_ctrl_nan_publish(wpa_s, buf + 12, reply,
reply_size);
} else if (os_strncmp(buf, "NAN_CANCEL_PUBLISH ", 19) == 0) {
if (wpas_ctrl_nan_cancel_publish(wpa_s, buf + 19) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "NAN_UPDATE_PUBLISH ", 19) == 0) {
if (wpas_ctrl_nan_update_publish(wpa_s, buf + 19) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "NAN_SUBSCRIBE ", 14) == 0) {
reply_len = wpas_ctrl_nan_subscribe(wpa_s, buf + 14, reply,
reply_size);
} else if (os_strncmp(buf, "NAN_CANCEL_SUBSCRIBE ", 21) == 0) {
if (wpas_ctrl_nan_cancel_subscribe(wpa_s, buf + 21) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "NAN_TRANSMIT ", 13) == 0) {
if (wpas_ctrl_nan_transmit(wpa_s, buf + 13) < 0)
reply_len = -1;
#endif /* CONFIG_NAN_USD */
#ifdef CONFIG_PASN
} else if (os_strncmp(buf, "PASN_START ", 11) == 0) {
if (wpas_ctrl_iface_pasn_start(wpa_s, buf + 11) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "PASN_STOP") == 0) {
wpas_pasn_auth_stop(wpa_s);
} else if (os_strcmp(buf, "PTKSA_CACHE_LIST") == 0) {
reply_len = ptksa_cache_list(wpa_s->ptksa, reply, reply_size);
} else if (os_strncmp(buf, "PASN_DEAUTH ", 12) == 0) {
if (wpas_ctrl_iface_pasn_deauthenticate(wpa_s, buf + 12) < 0)
reply_len = -1;
#ifdef CONFIG_TESTING_OPTIONS
} else if (os_strncmp(buf, "PASN_DRIVER ", 12) == 0) {
if (wpas_ctrl_iface_pasn_driver(wpa_s, buf + 12) < 0)
reply_len = -1;
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_PASN */
#ifndef CONFIG_NO_ROBUST_AV
} else if (os_strncmp(buf, "MSCS ", 5) == 0) {
if (wpas_ctrl_iface_configure_mscs(wpa_s, buf + 5))
reply_len = -1;
} else if (os_strncmp(buf, "SCS ", 4) == 0) {
if (wpas_ctrl_iface_configure_scs(wpa_s, buf + 4))
reply_len = -1;
} else if (os_strncmp(buf, "DSCP_RESP ", 10) == 0) {
if (wpas_ctrl_iface_send_dscp_resp(wpa_s, buf + 10))
reply_len = -1;
} else if (os_strncmp(buf, "DSCP_QUERY ", 11) == 0) {
if (wpas_ctrl_iface_send_dscp_query(wpa_s, buf + 11))
reply_len = -1;
#endif /* CONFIG_NO_ROBUST_AV */
} else if (os_strcmp(buf, "MLO_STATUS") == 0) {
reply_len = wpas_ctrl_iface_mlo_status(wpa_s, reply,
reply_size);
} else if (os_strcmp(buf, "MLO_SIGNAL_POLL") == 0) {
reply_len = wpas_ctrl_iface_mlo_signal_poll(wpa_s, reply,
reply_size);
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
}
if (reply_len < 0) {
os_memcpy(reply, "FAIL\n", 5);
reply_len = 5;
}
*resp_len = reply_len;
return reply;
}
static int wpa_supplicant_global_iface_add(struct wpa_global *global,
char *cmd)
{
struct wpa_interface iface;
char *pos, *extra;
struct wpa_supplicant *wpa_s;
unsigned int create_iface = 0;
u8 mac_addr[ETH_ALEN];
enum wpa_driver_if_type type = WPA_IF_STATION;
/*
* <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB<driver_param>
* TAB<bridge_ifname>[TAB<create>[TAB<interface_type>]]
*/
wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd);
os_memset(&iface, 0, sizeof(iface));
do {
iface.ifname = pos = cmd;
pos = os_strchr(pos, '\t');
if (pos)
*pos++ = '\0';
if (iface.ifname[0] == '\0')
return -1;
if (pos == NULL)
break;
iface.confname = pos;
pos = os_strchr(pos, '\t');
if (pos)
*pos++ = '\0';
if (iface.confname[0] == '\0')
iface.confname = NULL;
if (pos == NULL)
break;
iface.driver = pos;
pos = os_strchr(pos, '\t');
if (pos)
*pos++ = '\0';
if (iface.driver[0] == '\0')
iface.driver = NULL;
if (pos == NULL)
break;
iface.ctrl_interface = pos;
pos = os_strchr(pos, '\t');
if (pos)
*pos++ = '\0';
if (iface.ctrl_interface[0] == '\0')
iface.ctrl_interface = NULL;
if (pos == NULL)
break;
iface.driver_param = pos;
pos = os_strchr(pos, '\t');
if (pos)
*pos++ = '\0';
if (iface.driver_param[0] == '\0')
iface.driver_param = NULL;
if (pos == NULL)
break;
iface.bridge_ifname = pos;
pos = os_strchr(pos, '\t');
if (pos)
*pos++ = '\0';
if (iface.bridge_ifname[0] == '\0')
iface.bridge_ifname = NULL;
if (pos == NULL)
break;
extra = pos;
pos = os_strchr(pos, '\t');
if (pos)
*pos++ = '\0';
if (!extra[0])
break;
if (os_strcmp(extra, "create") == 0) {
create_iface = 1;
if (!pos)
break;
if (os_strcmp(pos, "sta") == 0) {
type = WPA_IF_STATION;
} else if (os_strcmp(pos, "ap") == 0) {
type = WPA_IF_AP_BSS;
} else {
wpa_printf(MSG_DEBUG,
"INTERFACE_ADD unsupported interface type: '%s'",
pos);
return -1;
}
} else {
wpa_printf(MSG_DEBUG,
"INTERFACE_ADD unsupported extra parameter: '%s'",
extra);
return -1;
}
} while (0);
if (create_iface) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE creating interface '%s'",
iface.ifname);
if (!global->ifaces)
return -1;
if (wpa_drv_if_add(global->ifaces, type, iface.ifname,
NULL, NULL, NULL, mac_addr, NULL) < 0) {
wpa_printf(MSG_ERROR,
"CTRL_IFACE interface creation failed");
return -1;
}
wpa_printf(MSG_DEBUG,
"CTRL_IFACE interface '%s' created with MAC addr: "
MACSTR, iface.ifname, MAC2STR(mac_addr));
}
if (wpa_supplicant_get_iface(global, iface.ifname))
goto fail;
wpa_s = wpa_supplicant_add_iface(global, &iface, NULL);
if (!wpa_s)
goto fail;
wpa_s->added_vif = create_iface;
return 0;
fail:
if (create_iface) {
/* wpa_supplicant does not create multi-BSS AP, so collapse to
* WPA_IF_STATION to avoid unwanted clean up in the driver. */
wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, iface.ifname);
}
return -1;
}
static int wpa_supplicant_global_iface_remove(struct wpa_global *global,
char *cmd)
{
struct wpa_supplicant *wpa_s;
int ret;
unsigned int delete_iface;
wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_REMOVE '%s'", cmd);
wpa_s = wpa_supplicant_get_iface(global, cmd);
if (wpa_s == NULL)
return -1;
delete_iface = wpa_s->added_vif;
ret = wpa_supplicant_remove_iface(global, wpa_s, 0);
if (!ret && delete_iface) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE deleting the interface '%s'",
cmd);
/* wpa_supplicant does not create multi-BSS AP, so collapse to
* WPA_IF_STATION to avoid unwanted clean up in the driver. */
ret = wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, cmd);
}
return ret;
}
static void wpa_free_iface_info(struct wpa_interface_info *iface)
{
struct wpa_interface_info *prev;
while (iface) {
prev = iface;
iface = iface->next;
os_free(prev->ifname);
os_free(prev->desc);
os_free(prev);
}
}
static int wpa_supplicant_global_iface_list(struct wpa_global *global,
char *buf, int len)
{
int i, res;
struct wpa_interface_info *iface = NULL, *last = NULL, *tmp;
char *pos, *end;
for (i = 0; wpa_drivers[i]; i++) {
const struct wpa_driver_ops *drv = wpa_drivers[i];
if (drv->get_interfaces == NULL)
continue;
tmp = drv->get_interfaces(global->drv_priv[i]);
if (tmp == NULL)
continue;
if (last == NULL)
iface = last = tmp;
else
last->next = tmp;
while (last->next)
last = last->next;
}
pos = buf;
end = buf + len;
for (tmp = iface; tmp; tmp = tmp->next) {
res = os_snprintf(pos, end - pos, "%s\t%s\t%s\n",
tmp->drv_name, tmp->ifname,
tmp->desc ? tmp->desc : "");
if (os_snprintf_error(end - pos, res)) {
*pos = '\0';
break;
}
pos += res;
}
wpa_free_iface_info(iface);
return pos - buf;
}
static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global,
const char *input,
char *buf, int len)
{
int res;
char *pos, *end;
struct wpa_supplicant *wpa_s;
int show_ctrl = 0;
if (input)
show_ctrl = !!os_strstr(input, "ctrl");
wpa_s = global->ifaces;
pos = buf;
end = buf + len;
while (wpa_s) {
if (show_ctrl)
res = os_snprintf(pos, end - pos, "%s ctrl_iface=%s\n",
wpa_s->ifname,
wpa_s->conf->ctrl_interface ?
wpa_s->conf->ctrl_interface : "N/A");
else
res = os_snprintf(pos, end - pos, "%s\n",
wpa_s->ifname);
if (os_snprintf_error(end - pos, res)) {
*pos = '\0';
break;
}
pos += res;
wpa_s = wpa_s->next;
}
return pos - buf;
}
static char * wpas_global_ctrl_iface_ifname(struct wpa_global *global,
const char *ifname,
char *cmd, size_t *resp_len)
{
struct wpa_supplicant *wpa_s;
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
if (os_strcmp(ifname, wpa_s->ifname) == 0)
break;
}
if (wpa_s == NULL) {
char *resp = os_strdup("FAIL-NO-IFNAME-MATCH\n");
if (resp)
*resp_len = os_strlen(resp);
else
*resp_len = 1;
return resp;
}
return wpa_supplicant_ctrl_iface_process(wpa_s, cmd, resp_len);
}
static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global,
char *buf, size_t *resp_len)
{
#ifdef CONFIG_P2P
static const char * cmd[] = {
"LIST_NETWORKS",
"P2P_FIND",
"P2P_STOP_FIND",
"P2P_LISTEN",
"P2P_GROUP_ADD",
"P2P_GET_PASSPHRASE",
"P2P_SERVICE_UPDATE",
"P2P_SERVICE_FLUSH",
"P2P_FLUSH",
"P2P_CANCEL",
"P2P_PRESENCE_REQ",
"P2P_EXT_LISTEN",
#ifdef CONFIG_AP
"STA-FIRST",
#endif /* CONFIG_AP */
NULL
};
static const char * prefix[] = {
#ifdef ANDROID
"DRIVER ",
#endif /* ANDROID */
"GET_CAPABILITY ",
"GET_NETWORK ",
"REMOVE_NETWORK ",
"P2P_FIND ",
"P2P_CONNECT ",
"P2P_LISTEN ",
"P2P_GROUP_REMOVE ",
"P2P_GROUP_ADD ",
"P2P_GROUP_MEMBER ",
"P2P_PROV_DISC ",
"P2P_SERV_DISC_REQ ",
"P2P_SERV_DISC_CANCEL_REQ ",
"P2P_SERV_DISC_RESP ",
"P2P_SERV_DISC_EXTERNAL ",
"P2P_SERVICE_ADD ",
"P2P_SERVICE_DEL ",
"P2P_SERVICE_REP ",
"P2P_REJECT ",
"P2P_INVITE ",
"P2P_PEER ",
"P2P_SET ",
"P2P_UNAUTHORIZE ",
"P2P_PRESENCE_REQ ",
"P2P_EXT_LISTEN ",
"P2P_REMOVE_CLIENT ",
"WPS_NFC_TOKEN ",
"WPS_NFC_TAG_READ ",
"NFC_GET_HANDOVER_SEL ",
"NFC_GET_HANDOVER_REQ ",
"NFC_REPORT_HANDOVER ",
"P2P_ASP_PROVISION ",
"P2P_ASP_PROVISION_RESP ",
#ifdef CONFIG_AP
"STA ",
"STA-NEXT ",
#endif /* CONFIG_AP */
NULL
};
int found = 0;
int i;
if (global->p2p_init_wpa_s == NULL)
return NULL;
for (i = 0; !found && cmd[i]; i++) {
if (os_strcmp(buf, cmd[i]) == 0)
found = 1;
}
for (i = 0; !found && prefix[i]; i++) {
if (os_strncmp(buf, prefix[i], os_strlen(prefix[i])) == 0)
found = 1;
}
if (found)
return wpa_supplicant_ctrl_iface_process(global->p2p_init_wpa_s,
buf, resp_len);
#endif /* CONFIG_P2P */
return NULL;
}
static char * wpas_global_ctrl_iface_redir_wfd(struct wpa_global *global,
char *buf, size_t *resp_len)
{
#ifdef CONFIG_WIFI_DISPLAY
if (global->p2p_init_wpa_s == NULL)
return NULL;
if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0 ||
os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0)
return wpa_supplicant_ctrl_iface_process(global->p2p_init_wpa_s,
buf, resp_len);
#endif /* CONFIG_WIFI_DISPLAY */
return NULL;
}
static char * wpas_global_ctrl_iface_redir(struct wpa_global *global,
char *buf, size_t *resp_len)
{
char *ret;
ret = wpas_global_ctrl_iface_redir_p2p(global, buf, resp_len);
if (ret)
return ret;
ret = wpas_global_ctrl_iface_redir_wfd(global, buf, resp_len);
if (ret)
return ret;
return NULL;
}
static int wpas_global_ctrl_iface_set(struct wpa_global *global, char *cmd)
{
char *value;
value = os_strchr(cmd, ' ');
if (value == NULL)
return -1;
*value++ = '\0';
wpa_printf(MSG_DEBUG, "GLOBAL_CTRL_IFACE SET '%s'='%s'", cmd, value);
#ifdef CONFIG_WIFI_DISPLAY
if (os_strcasecmp(cmd, "wifi_display") == 0) {
wifi_display_enable(global, !!atoi(value));
return 0;
}
#endif /* CONFIG_WIFI_DISPLAY */
/* Restore cmd to its original value to allow redirection */
value[-1] = ' ';
return -1;
}
static int wpas_global_ctrl_iface_dup_network(struct wpa_global *global,
char *cmd)
{
struct wpa_supplicant *wpa_s[2]; /* src, dst */
char *p;
unsigned int i;
/* cmd: "<src ifname> <dst ifname> <src network id> <dst network id>
* <variable name> */
for (i = 0; i < ARRAY_SIZE(wpa_s) ; i++) {
p = os_strchr(cmd, ' ');
if (p == NULL)
return -1;
*p = '\0';
wpa_s[i] = global->ifaces;
for (; wpa_s[i]; wpa_s[i] = wpa_s[i]->next) {
if (os_strcmp(cmd, wpa_s[i]->ifname) == 0)
break;
}
if (!wpa_s[i]) {
wpa_printf(MSG_DEBUG,
"CTRL_IFACE: Could not find iface=%s", cmd);
return -1;
}
cmd = p + 1;
}
return wpa_supplicant_ctrl_iface_dup_network(wpa_s[0], cmd, wpa_s[1]);
}
#ifndef CONFIG_NO_CONFIG_WRITE
static int wpas_global_ctrl_iface_save_config(struct wpa_global *global)
{
int ret = 0, saved = 0;
struct wpa_supplicant *wpa_s;
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
if (!wpa_s->conf->update_config) {
wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed to update configuration (update_config=0)");
continue;
}
if (wpa_config_write(wpa_s->confname, wpa_s->conf)) {
wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to update configuration");
ret = 1;
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration updated");
saved++;
}
}
if (!saved && !ret) {
wpa_dbg(wpa_s, MSG_DEBUG,
"CTRL_IFACE: SAVE_CONFIG - No configuration files could be updated");
ret = 1;
}
return ret;
}
#endif /* CONFIG_NO_CONFIG_WRITE */
static int wpas_global_ctrl_iface_status(struct wpa_global *global,
char *buf, size_t buflen)
{
char *pos, *end;
int ret;
struct wpa_supplicant *wpa_s;
pos = buf;
end = buf + buflen;
#ifdef CONFIG_P2P
if (global->p2p && !global->p2p_disabled) {
ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR
"\n"
"p2p_state=%s\n",
MAC2STR(global->p2p_dev_addr),
p2p_get_state_txt(global->p2p));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
} else if (global->p2p) {
ret = os_snprintf(pos, end - pos, "p2p_state=DISABLED\n");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_WIFI_DISPLAY
ret = os_snprintf(pos, end - pos, "wifi_display=%d\n",
!!global->wifi_display);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
#endif /* CONFIG_WIFI_DISPLAY */
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
ret = os_snprintf(pos, end - pos, "ifname=%s\n"
"address=" MACSTR "\n",
wpa_s->ifname, MAC2STR(wpa_s->own_addr));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
return pos - buf;
}
#ifdef CONFIG_FST
static int wpas_global_ctrl_iface_fst_attach(struct wpa_global *global,
char *cmd, char *buf,
size_t reply_size)
{
char ifname[IFNAMSIZ + 1];
struct fst_iface_cfg cfg;
struct wpa_supplicant *wpa_s;
struct fst_wpa_obj iface_obj;
if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) {
wpa_s = wpa_supplicant_get_iface(global, ifname);
if (wpa_s) {
if (wpa_s->fst) {
wpa_printf(MSG_INFO, "FST: Already attached");
return -1;
}
fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj);
wpa_s->fst = fst_attach(ifname, wpa_s->own_addr,
&iface_obj, &cfg);
if (wpa_s->fst)
return os_snprintf(buf, reply_size, "OK\n");
}
}
return -1;
}
static int wpas_global_ctrl_iface_fst_detach(struct wpa_global *global,
char *cmd, char *buf,
size_t reply_size)
{
char ifname[IFNAMSIZ + 1];
struct wpa_supplicant *wpa_s;
if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) {
wpa_s = wpa_supplicant_get_iface(global, ifname);
if (wpa_s) {
if (!fst_iface_detach(ifname)) {
wpa_s->fst = NULL;
return os_snprintf(buf, reply_size, "OK\n");
}
}
}
return -1;
}
#endif /* CONFIG_FST */
char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
char *buf, size_t *resp_len)
{
char *reply;
const int reply_size = 2048;
int reply_len;
int level = MSG_DEBUG;
if (os_strncmp(buf, "IFNAME=", 7) == 0) {
char *pos = os_strchr(buf + 7, ' ');
if (pos) {
*pos++ = '\0';
return wpas_global_ctrl_iface_ifname(global,
buf + 7, pos,
resp_len);
}
}
reply = wpas_global_ctrl_iface_redir(global, buf, resp_len);
if (reply)
return reply;
if (os_strcmp(buf, "PING") == 0)
level = MSG_EXCESSIVE;
wpa_hexdump_ascii(level, "RX global ctrl_iface",
(const u8 *) buf, os_strlen(buf));
reply = os_malloc(reply_size);
if (reply == NULL) {
*resp_len = 1;
return NULL;
}
os_memcpy(reply, "OK\n", 3);
reply_len = 3;
if (os_strcmp(buf, "PING") == 0) {
os_memcpy(reply, "PONG\n", 5);
reply_len = 5;
} else if (os_strncmp(buf, "INTERFACE_ADD ", 14) == 0) {
if (wpa_supplicant_global_iface_add(global, buf + 14))
reply_len = -1;
} else if (os_strncmp(buf, "INTERFACE_REMOVE ", 17) == 0) {
if (wpa_supplicant_global_iface_remove(global, buf + 17))
reply_len = -1;
} else if (os_strcmp(buf, "INTERFACE_LIST") == 0) {
reply_len = wpa_supplicant_global_iface_list(
global, reply, reply_size);
} else if (os_strncmp(buf, "INTERFACES", 10) == 0) {
reply_len = wpa_supplicant_global_iface_interfaces(
global, buf + 10, reply, reply_size);
#ifdef CONFIG_FST
} else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) {
reply_len = wpas_global_ctrl_iface_fst_attach(global, buf + 11,
reply,
reply_size);
} else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) {
reply_len = wpas_global_ctrl_iface_fst_detach(global, buf + 11,
reply,
reply_size);
} else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) {
reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size);
#endif /* CONFIG_FST */
} else if (os_strcmp(buf, "TERMINATE") == 0) {
wpa_supplicant_terminate_proc(global);
} else if (os_strcmp(buf, "SUSPEND") == 0) {
wpas_notify_suspend(global);
} else if (os_strcmp(buf, "RESUME") == 0) {
wpas_notify_resume(global);
} else if (os_strncmp(buf, "SET ", 4) == 0) {
if (wpas_global_ctrl_iface_set(global, buf + 4)) {
#ifdef CONFIG_P2P
if (global->p2p_init_wpa_s) {
os_free(reply);
/* Check if P2P redirection would work for this
* command. */
return wpa_supplicant_ctrl_iface_process(
global->p2p_init_wpa_s,
buf, resp_len);
}
#endif /* CONFIG_P2P */
reply_len = -1;
}
} else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
if (wpas_global_ctrl_iface_dup_network(global, buf + 12))
reply_len = -1;
#ifndef CONFIG_NO_CONFIG_WRITE
} else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
if (wpas_global_ctrl_iface_save_config(global))
reply_len = -1;
#endif /* CONFIG_NO_CONFIG_WRITE */
} else if (os_strcmp(buf, "STATUS") == 0) {
reply_len = wpas_global_ctrl_iface_status(global, reply,
reply_size);
#ifdef CONFIG_MODULE_TESTS
} else if (os_strcmp(buf, "MODULE_TESTS") == 0) {
if (wpas_module_tests() < 0)
reply_len = -1;
#endif /* CONFIG_MODULE_TESTS */
} else if (os_strncmp(buf, "RELOG", 5) == 0) {
if (wpa_debug_reopen_file() < 0)
reply_len = -1;
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
}
if (reply_len < 0) {
os_memcpy(reply, "FAIL\n", 5);
reply_len = 5;
}
*resp_len = reply_len;
return reply;
}